| 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 |