Chromium Code Reviews| 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 |