| 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 DragBehavior, |
| 21 LayoutBehavior, | 21 LayoutBehavior, |
| 22 ], | 22 ], |
| 23 | 23 |
| 24 properties: { | 24 properties: { |
| 25 /** | 25 /** |
| 26 * Array of displays. | 26 * Array of displays. |
| 27 * @type {!Array<!chrome.system.display.DisplayUnitInfo>} | 27 * @type {!Array<!chrome.system.display.DisplayUnitInfo>} |
| 28 */ | 28 */ |
| 29 displays: Array, | 29 displays: Array, |
| 30 | 30 |
| 31 /** @type {!chrome.system.display.DisplayUnitInfo|undefined} */ | 31 /** @type {!chrome.system.display.DisplayUnitInfo|undefined} */ |
| 32 selectedDisplay: Object, | 32 selectedDisplay: Object, |
| 33 |
| 34 /** |
| 35 * The ratio of the display area div (in px) to DisplayUnitInfo.bounds. |
| 36 * @type {number} |
| 37 */ |
| 38 visualScale: 1, |
| 39 }, |
| 40 |
| 41 /** @private {!{left: number, top: number}} */ |
| 42 visualOffset_: {left: 0, top: 0}, |
| 43 |
| 44 /** @override */ |
| 45 detached: function() { |
| 46 this.initializeDrag(false); |
| 47 }, |
| 33 | 48 |
| 34 /** | 49 /** |
| 35 * The ratio of the display area div (in px) to DisplayUnitInfo.bounds. | 50 * Called explicitly when |this.displays| and their associated |
| 36 * @type {number} | 51 * |this.layouts| |
| 52 * have been fetched from chrome. |
| 53 * @param {!Array<!chrome.system.display.DisplayUnitInfo>} displays |
| 54 * @param {!Array<!chrome.system.display.DisplayLayout>} layouts |
| 37 */ | 55 */ |
| 38 visualScale: 1, | 56 updateDisplays: function(displays, layouts) { |
| 39 }, | 57 this.displays = displays; |
| 58 this.layouts = layouts; |
| 40 | 59 |
| 41 /** @private {!{left: number, top: number}} */ | 60 this.initializeDisplayLayout(displays, layouts); |
| 42 visualOffset_: {left: 0, top: 0}, | |
| 43 | 61 |
| 44 /** @override */ | 62 var self = this; |
| 45 detached: function() { this.initializeDrag(false); }, | 63 var retry = 100; // ms |
| 64 function tryCalcVisualScale() { |
| 65 if (!self.calculateVisualScale_()) |
| 66 setTimeout(tryCalcVisualScale, retry); |
| 67 } |
| 68 tryCalcVisualScale(); |
| 46 | 69 |
| 47 /** | 70 this.initializeDrag( |
| 48 * Called explicitly when |this.displays| and their associated |this.layouts| | 71 !this.mirroring, this.$.displayArea, this.onDrag_.bind(this)); |
| 49 * have been fetched from chrome. | 72 }, |
| 50 * @param {!Array<!chrome.system.display.DisplayUnitInfo>} displays | |
| 51 * @param {!Array<!chrome.system.display.DisplayLayout>} layouts | |
| 52 */ | |
| 53 updateDisplays: function(displays, layouts) { | |
| 54 this.displays = displays; | |
| 55 this.layouts = layouts; | |
| 56 | 73 |
| 57 this.initializeDisplayLayout(displays, layouts); | 74 /** |
| 58 | |
| 59 var self = this; | |
| 60 var retry = 100; // ms | |
| 61 function tryCalcVisualScale() { | |
| 62 if (!self.calculateVisualScale_()) | |
| 63 setTimeout(tryCalcVisualScale, retry); | |
| 64 } | |
| 65 tryCalcVisualScale(); | |
| 66 | |
| 67 this.initializeDrag( | |
| 68 !this.mirroring, this.$.displayArea, this.onDrag_.bind(this)); | |
| 69 }, | |
| 70 | |
| 71 /** | |
| 72 * Calculates the visual offset and scale for the display area | 75 * Calculates the visual offset and scale for the display area |
| 73 * (i.e. the ratio of the display area div size to the area required to | 76 * (i.e. the ratio of the display area div size to the area required to |
| 74 * contain the DisplayUnitInfo bounding boxes). | 77 * contain the DisplayUnitInfo bounding boxes). |
| 75 * @return {boolean} Whether the calculation was successful. | 78 * @return {boolean} Whether the calculation was successful. |
| 76 * @private | 79 * @private |
| 77 */ | 80 */ |
| 78 calculateVisualScale_: function() { | 81 calculateVisualScale_: function() { |
| 79 var displayAreaDiv = this.$.displayArea; | 82 var displayAreaDiv = this.$.displayArea; |
| 80 if (!displayAreaDiv || !displayAreaDiv.offsetWidth || !this.displays || | 83 if (!displayAreaDiv || !displayAreaDiv.offsetWidth || !this.displays || |
| 81 !this.displays.length) { | 84 !this.displays.length) { |
| 82 return false; | 85 return false; |
| 83 } | 86 } |
| 84 | 87 |
| 85 var display = this.displays[0]; | 88 var display = this.displays[0]; |
| 86 var bounds = this.getCalculatedDisplayBounds(display.id); | 89 var bounds = this.getCalculatedDisplayBounds(display.id); |
| 87 var boundsBoundingBox = { | 90 var boundsBoundingBox = { |
| 88 left: bounds.left, | 91 left: bounds.left, |
| 89 right: bounds.left + bounds.width, | 92 right: bounds.left + bounds.width, |
| 90 top: bounds.top, | 93 top: bounds.top, |
| 91 bottom: bounds.top + bounds.height, | 94 bottom: bounds.top + bounds.height, |
| 92 }; | 95 }; |
| 93 var maxWidth = bounds.width; | 96 var maxWidth = bounds.width; |
| 94 var maxHeight = bounds.height; | 97 var maxHeight = bounds.height; |
| 95 for (let i = 1; i < this.displays.length; ++i) { | 98 for (let i = 1; i < this.displays.length; ++i) { |
| 96 display = this.displays[i]; | 99 display = this.displays[i]; |
| 97 bounds = this.getCalculatedDisplayBounds(display.id); | 100 bounds = this.getCalculatedDisplayBounds(display.id); |
| 98 boundsBoundingBox.left = Math.min(boundsBoundingBox.left, bounds.left); | 101 boundsBoundingBox.left = Math.min(boundsBoundingBox.left, bounds.left); |
| 99 boundsBoundingBox.right = | 102 boundsBoundingBox.right = |
| 100 Math.max(boundsBoundingBox.right, bounds.left + bounds.width); | 103 Math.max(boundsBoundingBox.right, bounds.left + bounds.width); |
| 101 boundsBoundingBox.top = Math.min(boundsBoundingBox.top, bounds.top); | 104 boundsBoundingBox.top = Math.min(boundsBoundingBox.top, bounds.top); |
| 102 boundsBoundingBox.bottom = | 105 boundsBoundingBox.bottom = |
| 103 Math.max(boundsBoundingBox.bottom, bounds.top + bounds.height); | 106 Math.max(boundsBoundingBox.bottom, bounds.top + bounds.height); |
| 104 maxWidth = Math.max(maxWidth, bounds.width); | 107 maxWidth = Math.max(maxWidth, bounds.width); |
| 105 maxHeight = Math.max(maxHeight, bounds.height); | 108 maxHeight = Math.max(maxHeight, bounds.height); |
| 106 } | 109 } |
| 107 | 110 |
| 108 // Create a margin around the bounding box equal to the size of the | 111 // Create a margin around the bounding box equal to the size of the |
| 109 // largest displays. | 112 // largest displays. |
| 110 var boundsWidth = boundsBoundingBox.right - boundsBoundingBox.left; | 113 var boundsWidth = boundsBoundingBox.right - boundsBoundingBox.left; |
| 111 var boundsHeight = boundsBoundingBox.bottom - boundsBoundingBox.top; | 114 var boundsHeight = boundsBoundingBox.bottom - boundsBoundingBox.top; |
| 112 | 115 |
| 113 // Calculate the scale. | 116 // Calculate the scale. |
| 114 var horizontalScale = | 117 var horizontalScale = |
| 115 displayAreaDiv.offsetWidth / (boundsWidth + maxWidth * 2); | 118 displayAreaDiv.offsetWidth / (boundsWidth + maxWidth * 2); |
| 116 var verticalScale = | 119 var verticalScale = |
| 117 displayAreaDiv.offsetHeight / (boundsHeight + maxHeight * 2); | 120 displayAreaDiv.offsetHeight / (boundsHeight + maxHeight * 2); |
| 118 var scale = Math.min(horizontalScale, verticalScale); | 121 var scale = Math.min(horizontalScale, verticalScale); |
| 119 | 122 |
| 120 // Calculate the offset. | 123 // Calculate the offset. |
| 121 this.visualOffset_.left = | 124 this.visualOffset_.left = |
| 122 ((displayAreaDiv.offsetWidth - (boundsWidth * scale)) / 2) - | 125 ((displayAreaDiv.offsetWidth - (boundsWidth * scale)) / 2) - |
| 123 boundsBoundingBox.left * scale; | 126 boundsBoundingBox.left * scale; |
| 124 this.visualOffset_.top = | 127 this.visualOffset_.top = |
| 125 ((displayAreaDiv.offsetHeight - (boundsHeight * scale)) / 2) - | 128 ((displayAreaDiv.offsetHeight - (boundsHeight * scale)) / 2) - |
| 126 boundsBoundingBox.top * scale; | 129 boundsBoundingBox.top * scale; |
| 127 | 130 |
| 128 // Update the scale which will trigger calls to getDivStyle_. | 131 // Update the scale which will trigger calls to getDivStyle_. |
| 129 this.visualScale = Math.max(MIN_VISUAL_SCALE, scale); | 132 this.visualScale = Math.max(MIN_VISUAL_SCALE, scale); |
| 130 | 133 |
| 131 return true; | 134 return true; |
| 132 }, | 135 }, |
| 133 | 136 |
| 134 /** | 137 /** |
| 135 * @param {string} id | 138 * @param {string} id |
| 136 * @param {!chrome.system.display.Bounds} displayBounds | 139 * @param {!chrome.system.display.Bounds} displayBounds |
| 137 * @param {number} visualScale | 140 * @param {number} visualScale |
| 138 * @param {boolean=} opt_mirrored | 141 * @param {boolean=} opt_mirrored |
| 139 * @return {string} The style string for the div. | 142 * @return {string} The style string for the div. |
| 140 * @private | 143 * @private |
| 141 */ | 144 */ |
| 142 getDivStyle_: function(id, displayBounds, visualScale, opt_mirrored) { | 145 getDivStyle_: function(id, displayBounds, visualScale, opt_mirrored) { |
| 143 // This matches the size of the box-shadow or border in CSS. | 146 // This matches the size of the box-shadow or border in CSS. |
| 144 /** @const {number} */ var BORDER = 1; | 147 /** @const {number} */ var BORDER = 1; |
| 145 /** @const {number} */ var MARGIN = 4; | 148 /** @const {number} */ var MARGIN = 4; |
| 146 /** @const {number} */ var OFFSET = opt_mirrored ? -4 : 0; | 149 /** @const {number} */ var OFFSET = opt_mirrored ? -4 : 0; |
| 147 /** @const {number} */ var PADDING = 3; | 150 /** @const {number} */ var PADDING = 3; |
| 148 var bounds = this.getCalculatedDisplayBounds(id, true /* notest */); | 151 var bounds = this.getCalculatedDisplayBounds(id, true /* notest */); |
| 149 if (!bounds) | 152 if (!bounds) |
| 150 return ''; | 153 return ''; |
| 151 var height = Math.round(bounds.height * this.visualScale) - BORDER * 2 - | 154 var height = Math.round(bounds.height * this.visualScale) - BORDER * 2 - |
| 152 MARGIN * 2 - PADDING * 2; | 155 MARGIN * 2 - PADDING * 2; |
| 153 var width = Math.round(bounds.width * this.visualScale) - BORDER * 2 - | 156 var width = Math.round(bounds.width * this.visualScale) - BORDER * 2 - |
| 154 MARGIN * 2 - PADDING * 2; | 157 MARGIN * 2 - PADDING * 2; |
| 155 var left = OFFSET + | 158 var left = OFFSET + |
| 156 Math.round(this.visualOffset_.left + (bounds.left * this.visualScale)); | 159 Math.round( |
| 157 var top = OFFSET + | 160 this.visualOffset_.left + (bounds.left * this.visualScale)); |
| 158 Math.round(this.visualOffset_.top + (bounds.top * this.visualScale)); | 161 var top = OFFSET + |
| 159 return `height: ${height}px; width: ${width}px;` + | 162 Math.round(this.visualOffset_.top + (bounds.top * this.visualScale)); |
| 160 ` left: ${left}px; top: ${top}px`; | 163 return `height: ${height}px; width: ${width}px;` + |
| 161 }, | 164 ` left: ${left}px; top: ${top}px`; |
| 165 }, |
| 162 | 166 |
| 163 /** | 167 /** |
| 164 * @param {string} id | 168 * @param {string} id |
| 165 * @param {!chrome.system.display.Bounds} displayBounds | 169 * @param {!chrome.system.display.Bounds} displayBounds |
| 166 * @param {number} visualScale | 170 * @param {number} visualScale |
| 167 * @return {string} The style string for the mirror div. | 171 * @return {string} The style string for the mirror div. |
| 168 * @private | 172 * @private |
| 169 */ | 173 */ |
| 170 getMirrorDivStyle_: function(id, displayBounds, visualScale) { | 174 getMirrorDivStyle_: function(id, displayBounds, visualScale) { |
| 171 return this.getDivStyle_(id, displayBounds, visualScale, true); | 175 return this.getDivStyle_(id, displayBounds, visualScale, true); |
| 172 }, | 176 }, |
| 173 | 177 |
| 174 /** | 178 /** |
| 175 * @param {!chrome.system.display.DisplayUnitInfo} display | 179 * @param {!chrome.system.display.DisplayUnitInfo} display |
| 176 * @param {!chrome.system.display.DisplayUnitInfo} selectedDisplay | 180 * @param {!chrome.system.display.DisplayUnitInfo} selectedDisplay |
| 177 * @return {boolean} | 181 * @return {boolean} |
| 178 * @private | 182 * @private |
| 179 */ | 183 */ |
| 180 isSelected_: function(display, selectedDisplay) { | 184 isSelected_: function(display, selectedDisplay) { |
| 181 return display.id == selectedDisplay.id; | 185 return display.id == selectedDisplay.id; |
| 182 }, | 186 }, |
| 183 | 187 |
| 184 /** | 188 /** |
| 185 * @param {!{model: !{item: !chrome.system.display.DisplayUnitInfo}, | 189 * @param {!{model: !{item: !chrome.system.display.DisplayUnitInfo}, |
| 186 * target: !HTMLDivElement}} e | 190 * target: !HTMLDivElement}} e |
| 187 * @private | 191 * @private |
| 188 */ | 192 */ |
| 189 onSelectDisplayTap_: function(e) { | 193 onSelectDisplayTap_: function(e) { |
| 190 this.fire('select-display', e.model.item.id); | 194 this.fire('select-display', e.model.item.id); |
| 191 // Force active in case the selected display was clicked. | 195 // Force active in case the selected display was clicked. |
| 192 // TODO(dpapad): Ask @stevenjb, why are we setting 'active' on a div? | 196 // TODO(dpapad): Ask @stevenjb, why are we setting 'active' on a div? |
| 193 e.target.active = true; | 197 e.target.active = true; |
| 194 }, | 198 }, |
| 195 | 199 |
| 196 /** | 200 /** |
| 197 * @param {string} id | 201 * @param {string} id |
| 198 * @param {?DragPosition} amount | 202 * @param {?DragPosition} amount |
| 199 */ | 203 */ |
| 200 onDrag_: function(id, amount) { | 204 onDrag_: function(id, amount) { |
| 201 id = id.substr(1); // Skip prefix | 205 id = id.substr(1); // Skip prefix |
| 202 | 206 |
| 203 var newBounds; | 207 var newBounds; |
| 204 if (!amount) { | 208 if (!amount) { |
| 205 this.finishUpdateDisplayBounds(id); | 209 this.finishUpdateDisplayBounds(id); |
| 206 newBounds = this.getCalculatedDisplayBounds(id); | 210 newBounds = this.getCalculatedDisplayBounds(id); |
| 207 } else { | 211 } else { |
| 208 // Make sure the dragged display is also selected. | 212 // Make sure the dragged display is also selected. |
| 209 if (id != this.selectedDisplay.id) | 213 if (id != this.selectedDisplay.id) |
| 210 this.fire('select-display', id); | 214 this.fire('select-display', id); |
| 211 | 215 |
| 212 var calculatedBounds = this.getCalculatedDisplayBounds(id); | 216 var calculatedBounds = this.getCalculatedDisplayBounds(id); |
| 213 newBounds = | 217 newBounds = |
| 214 /** @type {chrome.system.display.Bounds} */ ( | 218 /** @type {chrome.system.display.Bounds} */ ( |
| 215 Object.assign({}, calculatedBounds)); | 219 Object.assign({}, calculatedBounds)); |
| 216 newBounds.left += Math.round(amount.x / this.visualScale); | 220 newBounds.left += Math.round(amount.x / this.visualScale); |
| 217 newBounds.top += Math.round(amount.y / this.visualScale); | 221 newBounds.top += Math.round(amount.y / this.visualScale); |
| 218 | 222 |
| 219 if (this.displays.length >= 2) | 223 if (this.displays.length >= 2) |
| 220 newBounds = this.updateDisplayBounds(id, newBounds); | 224 newBounds = this.updateDisplayBounds(id, newBounds); |
| 221 } | 225 } |
| 222 var left = | 226 var left = this.visualOffset_.left + |
| 223 this.visualOffset_.left + Math.round(newBounds.left * this.visualScale); | 227 Math.round(newBounds.left * this.visualScale); |
| 224 var top = | 228 var top = |
| 225 this.visualOffset_.top + Math.round(newBounds.top * this.visualScale); | 229 this.visualOffset_.top + Math.round(newBounds.top * this.visualScale); |
| 226 var div = this.$$('#_' + id); | 230 var div = this.$$('#_' + id); |
| 227 div.style.left = '' + left + 'px'; | 231 div.style.left = '' + left + 'px'; |
| 228 div.style.top = '' + top + 'px'; | 232 div.style.top = '' + top + 'px'; |
| 229 }, | 233 }, |
| 230 | 234 |
| 231 }); | 235 }); |
| 232 | 236 |
| 233 })(); | 237 })(); |
| OLD | NEW |