OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 /** | |
6 * @fileoverview | |
7 * 'display-layout' presents a visual representation of the layout of one or | |
8 * more displays and allows them to be arranged. | |
9 */ | |
10 | |
11 (function() { | |
12 | |
13 /** @const {number} */ var MIN_VISUAL_SCALE = .01; | |
14 | |
15 Polymer({ | |
16 is: 'display-layout', | |
17 | |
18 behaviors: [ | |
19 Polymer.IronResizableBehavior, | |
20 ], | |
21 | |
22 properties: { | |
23 /** | |
24 * Array of displays. | |
25 * @type {!Array<!chrome.system.display.DisplayUnitInfo>} | |
26 */ | |
27 displays: Array, | |
28 | |
29 /** | |
30 * Array of display layouts. | |
31 * @type {!Array<!chrome.system.display.DisplayLayout>} | |
32 */ | |
33 layouts: Array, | |
34 | |
35 /** @type {!chrome.system.display.DisplayUnitInfo|undefined} */ | |
36 selectedDisplay: Object, | |
37 | |
38 /** | |
39 * The ratio of the display area div (in px) to DisplayUnitInfo.bounds. | |
40 * @type {number} | |
41 */ | |
42 visualScale: 1, | |
43 }, | |
44 | |
45 observers: ['displaysChanged_(displays, layouts)'], | |
46 | |
47 /** @private {!Object<chrome.system.display.DisplayUnitInfo>} */ | |
48 displayMap_: {}, | |
49 | |
50 /** @private {!Object<chrome.system.display.DisplayLayout>} */ | |
51 layoutMap_: {}, | |
52 | |
53 /** @private {!Object<chrome.system.display.Bounds>} */ | |
54 boundsMap_: {}, | |
55 | |
56 /** @private {!{left: number, top: number}} */ | |
57 visualOffset_: {left: 0, top: 0}, | |
58 | |
59 /** @override */ | |
60 attached: function() { | |
61 // TODO(stevenjb): Remove retry once fixed: | |
62 // https://github.com/Polymer/polymer/issues/3629 | |
63 var self = this; | |
64 var retry = 100; // ms | |
65 function tryCalcVisualScale() { | |
66 if (!self.calculateVisualScale_()) | |
67 setTimeout(tryCalcVisualScale, retry); | |
68 } | |
69 tryCalcVisualScale(); | |
70 }, | |
71 | |
72 /** @private */ | |
73 displaysChanged_: function(displays, layouts) { | |
74 this.displayMap_ = {}; | |
75 for (let display of this.displays) | |
76 this.displayMap_[display.id] = display; | |
77 | |
78 this.layoutMap_ = {}; | |
79 for (let layout of this.layouts) | |
80 this.layoutMap_[layout.id] = layout; | |
81 | |
82 this.boundsMap_ = {}; | |
83 for (let display of this.displays) | |
84 this.calcDisplayBounds_(display); | |
85 | |
86 this.calculateVisualScale_(); | |
87 }, | |
88 | |
89 /** | |
90 * Calculates the visual offset and scale for the display area | |
91 * (i.e. the ratio of the display area div size to the area required to | |
92 * contain the DisplayUnitInfo bounding boxes). | |
93 * @return {boolean} Whether the calculation was successful. | |
94 * @private | |
95 */ | |
96 calculateVisualScale_() { | |
97 var displayAreaDiv = this.$.displayArea; | |
98 if (!displayAreaDiv || !displayAreaDiv.offsetWidth || !this.displays) | |
99 return false; | |
100 | |
101 var maxWidth = 0; | |
102 var maxHeight = 0; | |
103 var displayInfoBoundingBox = | |
104 {left: Number.MAX_VALUE, right: 0, top: Number.MAX_VALUE, bottom: 0}; | |
michaelpg
2016/06/23 04:29:35
Normally the origin (0, 0) is the top-left, and th
stevenjb
2016/06/23 19:11:01
The intention is that these will be immediately ov
michaelpg
2016/06/24 20:25:10
If you use Array.prototype.reduce you wouldn't hav
stevenjb
2016/06/24 20:59:51
Interesting. I can see how that would be useful, b
| |
105 | |
106 for (let display of this.displays) { | |
107 var bounds = this.boundsMap_[display.id]; | |
108 displayInfoBoundingBox.left = | |
109 Math.min(displayInfoBoundingBox.left, bounds.left); | |
110 displayInfoBoundingBox.right = | |
111 Math.max(displayInfoBoundingBox.right, bounds.left + bounds.width); | |
112 displayInfoBoundingBox.top = | |
113 Math.min(displayInfoBoundingBox.top, bounds.top); | |
114 displayInfoBoundingBox.bottom = | |
115 Math.max(displayInfoBoundingBox.bottom, bounds.top + bounds.height); | |
116 maxWidth = Math.max(maxWidth, bounds.width); | |
117 maxHeight = Math.max(maxHeight, bounds.height); | |
118 } | |
119 | |
120 // Create a margin around the bounding box equal to the size of the | |
121 // largest display. | |
michaelpg
2016/06/23 04:29:34
display(s) -- e.g., a landscape display and a port
stevenjb
2016/06/23 19:11:01
Done.
| |
122 var displayInfoBoundsWidth = | |
123 displayInfoBoundingBox.right - displayInfoBoundingBox.left + | |
124 maxWidth * 2; | |
125 var displayInfoBoundsHeight = | |
126 displayInfoBoundingBox.bottom - displayInfoBoundingBox.top + | |
127 maxHeight * 2; | |
128 | |
129 // Calculate the scale. | |
130 var horizontalScale = displayAreaDiv.offsetWidth / displayInfoBoundsWidth; | |
131 var verticalScale = displayAreaDiv.offsetHeight / displayInfoBoundsHeight; | |
132 var scale = Math.min(horizontalScale, verticalScale); | |
133 | |
134 // Calculate the offset. | |
135 this.visualOffset_.left = (-displayInfoBoundingBox.left + maxWidth) * scale; | |
136 this.visualOffset_.top = (-displayInfoBoundingBox.top + maxHeight) * scale; | |
137 | |
138 // Update the scale which will trigger calls to getDivStyle_. | |
139 this.visualScale = Math.max(MIN_VISUAL_SCALE, scale); | |
140 | |
141 return true; | |
142 }, | |
143 | |
144 /** | |
145 * @param {!chrome.system.display.DisplayUnitInfo} display | |
146 * @param {number} visualScale | |
147 * @return {string} The style string for the div. | |
148 * @private | |
149 */ | |
150 getDivStyle_: function(display, visualScale) { | |
151 /** @const {number} */ var BORDER = 2; | |
michaelpg
2016/06/23 04:29:35
why 2, when the border with on .display is 1?
stevenjb
2016/06/23 19:11:01
It has a box shadow of width 2. Added a comment.
| |
152 var bounds = this.boundsMap_[display.id]; | |
153 var height = Math.round(bounds.height * this.visualScale) - BORDER * 2; | |
michaelpg
2016/06/23 04:29:35
I think using box-sizing: border-box on these divs
stevenjb
2016/06/23 19:11:01
Alas, your newfangled CSS property doesn't appear
| |
154 var width = Math.round(bounds.width * this.visualScale) - BORDER * 2; | |
155 var left = | |
156 this.visualOffset_.left + Math.round(bounds.left * this.visualScale); | |
michaelpg
2016/06/23 04:29:34
why round the 2nd term but not the offset?
stevenjb
2016/06/23 19:11:01
Good catch. Fixed.
| |
157 var top = | |
158 this.visualOffset_.top + Math.round(bounds.top * this.visualScale); | |
159 var style = 'height:' + height + 'px;' + 'width:' + width + 'px;' + | |
michaelpg
2016/06/23 04:29:35
i think this is a great use case for es6 template
stevenjb
2016/06/23 19:11:01
Cute. Closure is fine with it. Done.
| |
160 'left:' + left + 'px;' + 'top:' + top + 'px;'; | |
161 return style; | |
162 }, | |
163 | |
164 /** | |
165 * @param {!chrome.system.display.DisplayUnitInfo} display | |
166 * @param {!chrome.system.display.DisplayUnitInfo} selectedDisplay | |
167 * @return {boolean} | |
168 * @private | |
169 */ | |
170 isSelected_: function(display, selectedDisplay) { | |
171 return display.id == selectedDisplay.id; | |
172 }, | |
173 | |
174 /** | |
175 * @param {!{model: !{index: number}, target: !PaperButtonElement}} e | |
michaelpg
2016/06/23 04:29:35
meh, remove unused target property info
stevenjb
2016/06/23 19:11:01
Done.
| |
176 * @private | |
177 */ | |
178 onSelectDisplayTap_: function(e) { | |
179 this.fire('select-display', e.model.index); | |
180 }, | |
181 | |
182 /** | |
183 * Recursively calculate the bounds of a display relative to its parents. | |
184 * Caches the display bounds so that parent bounds are only calculated once. | |
185 * TODO(stevenjb): Move this function and the maps it requires to a separate | |
186 * behavior which will include snapping and collisions. | |
187 * @param {!chrome.system.display.DisplayUnitInfo} display | |
188 * @return {!chrome.system.display.Bounds} | |
189 * @private | |
190 */ | |
191 calcDisplayBounds_: function(display) { | |
192 var left, top; | |
193 if (display.isPrimary) { | |
194 left = -display.bounds.width / 2; | |
195 top = -display.bounds.height / 2; | |
196 } else { | |
197 var layout = this.layoutMap_[display.id]; | |
198 var parentDisplay = this.displayMap_[layout.parentId]; | |
199 var parentBounds; | |
200 if (parentDisplay.id in this.boundsMap_) | |
201 parentBounds = this.boundsMap_[parentDisplay.id]; | |
202 else | |
203 parentBounds = this.calcDisplayBounds_(parentDisplay); | |
204 left = parentBounds.left; | |
205 top = parentBounds.top; | |
206 switch (layout.position) { | |
207 case chrome.system.display.LayoutPosition.TOP: | |
208 top -= display.bounds.height; | |
209 break; | |
210 case chrome.system.display.LayoutPosition.RIGHT: | |
211 left += parentBounds.width; | |
212 break; | |
213 case chrome.system.display.LayoutPosition.BOTTOM: | |
214 top += parentBounds.height; | |
215 break; | |
216 case chrome.system.display.LayoutPosition.LEFT: | |
217 left -= display.bounds.height; | |
218 break; | |
219 } | |
220 } | |
221 var result = { | |
222 left: left, | |
223 top: top, | |
224 width: display.bounds.width, | |
225 height: display.bounds.height | |
226 }; | |
227 this.boundsMap_[display.id] = result; | |
228 return result; | |
229 } | |
230 }); | |
231 | |
232 })(); | |
OLD | NEW |