OLD | NEW |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 /** | 5 /** |
6 * @fileoverview | 6 * @fileoverview |
7 * 'display-layout' presents a visual representation of the layout of one or | 7 * 'display-layout' presents a visual representation of the layout of one or |
8 * more displays and allows them to be arranged. | 8 * more displays and allows them to be arranged. |
9 */ | 9 */ |
10 | 10 |
11 (function() { | 11 (function() { |
12 | 12 |
13 /** @const {number} */ var MIN_VISUAL_SCALE = .01; | 13 /** @const {number} */ var MIN_VISUAL_SCALE = .01; |
14 | 14 |
15 Polymer({ | 15 Polymer({ |
16 is: 'display-layout', | 16 is: 'display-layout', |
17 | 17 |
18 behaviors: [ | 18 behaviors: [ |
19 Polymer.IronResizableBehavior, | 19 Polymer.IronResizableBehavior, |
| 20 DragBehavior, |
20 ], | 21 ], |
21 | 22 |
22 properties: { | 23 properties: { |
23 /** | 24 /** |
24 * Array of displays. | 25 * Array of displays. |
25 * @type {!Array<!chrome.system.display.DisplayUnitInfo>} | 26 * @type {!Array<!chrome.system.display.DisplayUnitInfo>} |
26 */ | 27 */ |
27 displays: Array, | 28 displays: Array, |
28 | 29 |
29 /** | 30 /** |
30 * Array of display layouts. | 31 * Array of display layouts. |
31 * @type {!Array<!chrome.system.display.DisplayLayout>} | 32 * @type {!Array<!chrome.system.display.DisplayLayout>} |
32 */ | 33 */ |
33 layouts: Array, | 34 layouts: Array, |
34 | 35 |
| 36 /** |
| 37 * Whether or not mirroring is enabled. |
| 38 * @type {boolean} |
| 39 */ |
| 40 mirroring: false, |
| 41 |
35 /** @type {!chrome.system.display.DisplayUnitInfo|undefined} */ | 42 /** @type {!chrome.system.display.DisplayUnitInfo|undefined} */ |
36 selectedDisplay: Object, | 43 selectedDisplay: Object, |
37 | 44 |
38 /** | 45 /** |
39 * The ratio of the display area div (in px) to DisplayUnitInfo.bounds. | 46 * The ratio of the display area div (in px) to DisplayUnitInfo.bounds. |
40 * @type {number} | 47 * @type {number} |
41 */ | 48 */ |
42 visualScale: 1, | 49 visualScale: 1, |
43 }, | 50 }, |
44 | 51 |
45 /** @private {!Object<chrome.system.display.DisplayUnitInfo>} */ | 52 /** @private {!Object<chrome.system.display.Bounds>} */ |
46 displayMap_: {}, | 53 displayBoundsMap_: {}, |
47 | 54 |
48 /** @private {!Object<chrome.system.display.DisplayLayout>} */ | 55 /** @private {!Object<chrome.system.display.DisplayLayout>} */ |
49 layoutMap_: {}, | 56 layoutMap_: {}, |
50 | 57 |
51 /** @private {!Object<chrome.system.display.Bounds>} */ | 58 /** |
52 boundsMap_: {}, | 59 * The calculated bounds used for generating the div bounds. |
| 60 * @private {!Object<chrome.system.display.Bounds>} |
| 61 */ |
| 62 calculatedBoundsMap_: {}, |
53 | 63 |
54 /** @private {!{left: number, top: number}} */ | 64 /** @private {!{left: number, top: number}} */ |
55 visualOffset_: {left: 0, top: 0}, | 65 visualOffset_: {left: 0, top: 0}, |
56 | 66 |
57 /** @override */ | 67 /** @override */ |
58 attached: function() { | 68 attached: function() { |
59 // TODO(stevenjb): Remove retry once fixed: | 69 // TODO(stevenjb): Remove retry once fixed: |
60 // https://github.com/Polymer/polymer/issues/3629 | 70 // https://github.com/Polymer/polymer/issues/3629 |
61 var self = this; | 71 var self = this; |
62 var retry = 100; // ms | 72 var retry = 100; // ms |
63 function tryCalcVisualScale() { | 73 function tryCalcVisualScale() { |
64 if (!self.calculateVisualScale_()) | 74 if (!self.calculateVisualScale_()) |
65 setTimeout(tryCalcVisualScale, retry); | 75 setTimeout(tryCalcVisualScale, retry); |
66 } | 76 } |
67 tryCalcVisualScale(); | 77 tryCalcVisualScale(); |
68 }, | 78 }, |
69 | 79 |
| 80 /** @override */ |
| 81 detached: function() { |
| 82 this.initializeDrag(false); |
| 83 }, |
| 84 |
70 /** | 85 /** |
71 * Called explicitly when |this.displays| and their associated |this.layouts| | 86 * Called explicitly when |this.displays| and their associated |this.layouts| |
72 * have been fetched from chrome. | 87 * have been fetched from chrome. |
73 * @param {!Array<!chrome.system.display.DisplayUnitInfo>} displays | 88 * @param {!Array<!chrome.system.display.DisplayUnitInfo>} displays |
74 * @param {!Array<!chrome.system.display.DisplayLayout>} layouts | 89 * @param {!Array<!chrome.system.display.DisplayLayout>} layouts |
75 */ | 90 */ |
76 updateDisplays: function(displays, layouts) { | 91 updateDisplays: function(displays, layouts) { |
77 this.displays = displays; | 92 this.displays = displays; |
78 this.layouts = layouts; | 93 this.layouts = layouts; |
79 | 94 |
80 this.displayMap_ = {}; | 95 this.mirroring = displays.length > 0 && !!displays[0].mirroringSourceId; |
| 96 |
| 97 this.displayBoundsMap_ = {}; |
81 for (let display of this.displays) | 98 for (let display of this.displays) |
82 this.displayMap_[display.id] = display; | 99 this.displayBoundsMap_[display.id] = display.bounds; |
83 | 100 |
84 this.layoutMap_ = {}; | 101 this.layoutMap_ = {}; |
85 for (let layout of this.layouts) | 102 for (let layout of this.layouts) |
86 this.layoutMap_[layout.id] = layout; | 103 this.layoutMap_[layout.id] = layout; |
87 | 104 |
88 this.boundsMap_ = {}; | 105 this.calculatedBoundsMap_ = {}; |
89 for (let display of this.displays) | 106 for (let display of this.displays) |
90 this.calcDisplayBounds_(display); | 107 this.calculateBounds_(display.id, display.bounds); |
91 | 108 |
92 this.calculateVisualScale_(); | 109 this.calculateVisualScale_(); |
| 110 |
| 111 this.initializeDrag( |
| 112 !this.mirroring, this.$.displayArea, this.onDrag_.bind(this)); |
93 }, | 113 }, |
94 | 114 |
95 /** | 115 /** |
96 * Calculates the visual offset and scale for the display area | 116 * Calculates the visual offset and scale for the display area |
97 * (i.e. the ratio of the display area div size to the area required to | 117 * (i.e. the ratio of the display area div size to the area required to |
98 * contain the DisplayUnitInfo bounding boxes). | 118 * contain the DisplayUnitInfo bounding boxes). |
99 * @return {boolean} Whether the calculation was successful. | 119 * @return {boolean} Whether the calculation was successful. |
100 * @private | 120 * @private |
101 */ | 121 */ |
102 calculateVisualScale_() { | 122 calculateVisualScale_() { |
103 var displayAreaDiv = this.$.displayArea; | 123 var displayAreaDiv = this.$.displayArea; |
104 if (!displayAreaDiv || !displayAreaDiv.offsetWidth || !this.displays || | 124 if (!displayAreaDiv || !displayAreaDiv.offsetWidth || !this.displays || |
105 !this.displays.length) { | 125 !this.displays.length) { |
106 return false; | 126 return false; |
107 } | 127 } |
108 | 128 |
109 var display = this.displays[0]; | 129 var display = this.displays[0]; |
110 var bounds = this.boundsMap_[display.id]; | 130 var bounds = this.calculatedBoundsMap_[display.id]; |
111 var displayInfoBoundingBox = { | 131 var displayInfoBoundingBox = { |
112 left: bounds.left, | 132 left: bounds.left, |
113 right: bounds.left + bounds.width, | 133 right: bounds.left + bounds.width, |
114 top: bounds.top, | 134 top: bounds.top, |
115 bottom: bounds.top + bounds.height, | 135 bottom: bounds.top + bounds.height, |
116 }; | 136 }; |
117 var maxWidth = bounds.width; | 137 var maxWidth = bounds.width; |
118 var maxHeight = bounds.height; | 138 var maxHeight = bounds.height; |
119 for (let i = 1; i < this.displays.length; ++i) { | 139 for (let i = 1; i < this.displays.length; ++i) { |
120 display = this.displays[i]; | 140 display = this.displays[i]; |
121 bounds = this.boundsMap_[display.id]; | 141 bounds = this.calculatedBoundsMap_[display.id]; |
122 displayInfoBoundingBox.left = | 142 displayInfoBoundingBox.left = |
123 Math.min(displayInfoBoundingBox.left, bounds.left); | 143 Math.min(displayInfoBoundingBox.left, bounds.left); |
124 displayInfoBoundingBox.right = | 144 displayInfoBoundingBox.right = |
125 Math.max(displayInfoBoundingBox.right, bounds.left + bounds.width); | 145 Math.max(displayInfoBoundingBox.right, bounds.left + bounds.width); |
126 displayInfoBoundingBox.top = | 146 displayInfoBoundingBox.top = |
127 Math.min(displayInfoBoundingBox.top, bounds.top); | 147 Math.min(displayInfoBoundingBox.top, bounds.top); |
128 displayInfoBoundingBox.bottom = | 148 displayInfoBoundingBox.bottom = |
129 Math.max(displayInfoBoundingBox.bottom, bounds.top + bounds.height); | 149 Math.max(displayInfoBoundingBox.bottom, bounds.top + bounds.height); |
130 maxWidth = Math.max(maxWidth, bounds.width); | 150 maxWidth = Math.max(maxWidth, bounds.width); |
131 maxHeight = Math.max(maxHeight, bounds.height); | 151 maxHeight = Math.max(maxHeight, bounds.height); |
(...skipping 15 matching lines...) Expand all Loading... |
147 this.visualOffset_.left = (-displayInfoBoundingBox.left + maxWidth) * scale; | 167 this.visualOffset_.left = (-displayInfoBoundingBox.left + maxWidth) * scale; |
148 this.visualOffset_.top = (-displayInfoBoundingBox.top + maxHeight) * scale; | 168 this.visualOffset_.top = (-displayInfoBoundingBox.top + maxHeight) * scale; |
149 | 169 |
150 // Update the scale which will trigger calls to getDivStyle_. | 170 // Update the scale which will trigger calls to getDivStyle_. |
151 this.visualScale = Math.max(MIN_VISUAL_SCALE, scale); | 171 this.visualScale = Math.max(MIN_VISUAL_SCALE, scale); |
152 | 172 |
153 return true; | 173 return true; |
154 }, | 174 }, |
155 | 175 |
156 /** | 176 /** |
157 * @param {!chrome.system.display.DisplayUnitInfo} display | 177 * @param {string} id |
| 178 * @param {!chrome.system.display.Bounds} displayBounds |
158 * @param {number} visualScale | 179 * @param {number} visualScale |
159 * @return {string} The style string for the div. | 180 * @return {string} The style string for the div. |
160 * @private | 181 * @private |
161 */ | 182 */ |
162 getDivStyle_: function(display, visualScale) { | 183 getDivStyle_: function(id, displayBounds, visualScale) { |
163 // This matches the size of the box-shadow or border in CSS. | 184 // This matches the size of the box-shadow or border in CSS. |
164 /** @const {number} */ var BORDER = 2; | 185 /** @const {number} */ var BORDER = 2; |
165 var bounds = this.boundsMap_[display.id]; | 186 var bounds = this.calculatedBoundsMap_[id]; |
166 var height = Math.round(bounds.height * this.visualScale) - BORDER * 2; | 187 var height = Math.round(bounds.height * this.visualScale) - BORDER * 2; |
167 var width = Math.round(bounds.width * this.visualScale) - BORDER * 2; | 188 var width = Math.round(bounds.width * this.visualScale) - BORDER * 2; |
168 var left = | 189 var left = |
169 Math.round(this.visualOffset_.left + (bounds.left * this.visualScale)); | 190 Math.round(this.visualOffset_.left + (bounds.left * this.visualScale)); |
170 var top = | 191 var top = |
171 Math.round(this.visualOffset_.top + (bounds.top * this.visualScale)); | 192 Math.round(this.visualOffset_.top + (bounds.top * this.visualScale)); |
172 return `height: ${height}px; width: ${width}px;` + | 193 return `height: ${height}px; width: ${width}px;` + |
173 ` left: ${left}px; top: ${top}px`; | 194 ` left: ${left}px; top: ${top}px`; |
174 }, | 195 }, |
175 | 196 |
(...skipping 13 matching lines...) Expand all Loading... |
189 */ | 210 */ |
190 onSelectDisplayTap_: function(e) { | 211 onSelectDisplayTap_: function(e) { |
191 this.fire('select-display', e.model.index); | 212 this.fire('select-display', e.model.index); |
192 }, | 213 }, |
193 | 214 |
194 /** | 215 /** |
195 * Recursively calculate the bounds of a display relative to its parents. | 216 * Recursively calculate the bounds of a display relative to its parents. |
196 * Caches the display bounds so that parent bounds are only calculated once. | 217 * Caches the display bounds so that parent bounds are only calculated once. |
197 * TODO(stevenjb): Move this function and the maps it requires to a separate | 218 * TODO(stevenjb): Move this function and the maps it requires to a separate |
198 * behavior which will include snapping and collisions. | 219 * behavior which will include snapping and collisions. |
199 * @param {!chrome.system.display.DisplayUnitInfo} display | 220 * @param {string} id |
| 221 * @param {!chrome.system.display.Bounds} bounds |
200 * @private | 222 * @private |
201 */ | 223 */ |
202 calcDisplayBounds_: function(display) { | 224 calculateBounds_: function(id, bounds) { |
203 if (display.id in this.boundsMap_) | 225 if (id in this.calculatedBoundsMap_) |
204 return; // Already calculated (i.e. a parent of a previous display) | 226 return; // Already calculated (i.e. a parent of a previous display) |
205 var left, top; | 227 var left, top; |
206 if (display.isPrimary) { | 228 var layout = this.layoutMap_[id]; |
207 left = -display.bounds.width / 2; | 229 if (!layout || !layout.parentId) { |
208 top = -display.bounds.height / 2; | 230 left = -bounds.width / 2; |
| 231 top = -bounds.height / 2; |
209 } else { | 232 } else { |
210 var layout = this.layoutMap_[display.id]; | 233 var parentDisplayBounds = this.displayBoundsMap_[layout.parentId]; |
211 assert(layout.parentId); | |
212 var parentDisplay = this.displayMap_[layout.parentId]; | |
213 var parentBounds; | 234 var parentBounds; |
214 if (!(parentDisplay.id in this.boundsMap_)) | 235 if (!(layout.parentId in this.calculatedBoundsMap_)) |
215 this.calcDisplayBounds_(parentDisplay); | 236 this.calculateBounds_(layout.parentId, parentDisplayBounds); |
216 parentBounds = this.boundsMap_[parentDisplay.id]; | 237 parentBounds = this.calculatedBoundsMap_[layout.parentId]; |
217 left = parentBounds.left; | 238 left = parentBounds.left; |
218 top = parentBounds.top; | 239 top = parentBounds.top; |
219 switch (layout.position) { | 240 switch (layout.position) { |
220 case chrome.system.display.LayoutPosition.TOP: | 241 case chrome.system.display.LayoutPosition.TOP: |
221 top -= display.bounds.height; | 242 top -= bounds.height; |
222 break; | 243 break; |
223 case chrome.system.display.LayoutPosition.RIGHT: | 244 case chrome.system.display.LayoutPosition.RIGHT: |
224 left += parentBounds.width; | 245 left += parentBounds.width; |
225 break; | 246 break; |
226 case chrome.system.display.LayoutPosition.BOTTOM: | 247 case chrome.system.display.LayoutPosition.BOTTOM: |
227 top += parentBounds.height; | 248 top += parentBounds.height; |
228 break; | 249 break; |
229 case chrome.system.display.LayoutPosition.LEFT: | 250 case chrome.system.display.LayoutPosition.LEFT: |
230 left -= display.bounds.height; | 251 left -= bounds.height; |
231 break; | 252 break; |
232 } | 253 } |
233 } | 254 } |
234 var result = { | 255 var result = { |
235 left: left, | 256 left: left, |
236 top: top, | 257 top: top, |
237 width: display.bounds.width, | 258 width: bounds.width, |
238 height: display.bounds.height | 259 height: bounds.height |
239 }; | 260 }; |
240 this.boundsMap_[display.id] = result; | 261 this.calculatedBoundsMap_[id] = result; |
241 } | 262 }, |
| 263 |
| 264 /** |
| 265 * @param {string} id |
| 266 * @param {?DragPosition} amount |
| 267 */ |
| 268 onDrag_(id, amount) { |
| 269 id = id.substr(1); // Skip prefix |
| 270 |
| 271 var newBounds; |
| 272 if (!amount) { |
| 273 // TODO(stevenjb): Resolve layout and send update. |
| 274 newBounds = this.calculatedBoundsMap_[id]; |
| 275 } else { |
| 276 // Make sure the dragged display is also selected. |
| 277 if (id != this.selectedDisplay.id) |
| 278 this.fire('select-display', id); |
| 279 |
| 280 var calculatedBounds = this.calculatedBoundsMap_[id]; |
| 281 newBounds = |
| 282 /** @type {chrome.system.display.Bounds} */ ( |
| 283 Object.assign({}, calculatedBounds)); |
| 284 newBounds.left += Math.round(amount.x / this.visualScale); |
| 285 newBounds.top += Math.round(amount.y / this.visualScale); |
| 286 // TODO(stevenjb): Update layout. |
| 287 } |
| 288 var left = |
| 289 this.visualOffset_.left + Math.round(newBounds.left * this.visualScale); |
| 290 var top = |
| 291 this.visualOffset_.top + Math.round(newBounds.top * this.visualScale); |
| 292 var div = this.$$('#_' + id); |
| 293 div.style.left = '' + left + 'px'; |
| 294 div.style.top = '' + top + 'px'; |
| 295 }, |
| 296 |
242 }); | 297 }); |
243 | 298 |
244 })(); | 299 })(); |
OLD | NEW |