Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 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 UI.GlassPane = class { | 5 UI.GlassPane = class { |
| 6 constructor() { | 6 constructor() { |
| 7 this._widget = new UI.Widget(true); | 7 this._widget = new UI.Widget(true); |
| 8 this._widget.markAsRoot(); | 8 this._widget.markAsRoot(); |
| 9 this.element = this._widget.element; | 9 this.element = this._widget.element; |
| 10 this.contentElement = this._widget.contentElement; | 10 this.contentElement = this._widget.contentElement; |
| 11 this._arrowElement = UI.Icon.create('', 'arrow hidden'); | |
| 12 this.element.shadowRoot.appendChild(this._arrowElement); | |
| 11 | 13 |
| 12 this.registerRequiredCSS('ui/glassPane.css'); | 14 this.registerRequiredCSS('ui/glassPane.css'); |
| 13 this.element.classList.add('no-pointer-events'); | 15 this.element.classList.add('no-pointer-events'); |
| 14 this._onMouseDownBound = this._onMouseDown.bind(this); | 16 this._onMouseDownBound = this._onMouseDown.bind(this); |
| 15 /** @type {?function(!Event)} */ | 17 /** @type {?function(!Event)} */ |
| 16 this._onClickOutsideCallback = null; | 18 this._onClickOutsideCallback = null; |
| 17 /** @type {?UI.Size} */ | 19 /** @type {?UI.Size} */ |
| 18 this._maxSize = null; | 20 this._maxSize = null; |
| 19 /** @type {?number} */ | 21 /** @type {?number} */ |
| 20 this._positionX = null; | 22 this._positionX = null; |
| 21 /** @type {?number} */ | 23 /** @type {?number} */ |
| 22 this._positionY = null; | 24 this._positionY = null; |
| 23 /** @type {?AnchorBox} */ | 25 /** @type {?AnchorBox} */ |
| 24 this._anchorBox = null; | 26 this._anchorBox = null; |
| 25 this._anchorBehavior = UI.GlassPane.AnchorBehavior.PreferTop; | 27 this._anchorBehavior = UI.GlassPane.AnchorBehavior.PreferTop; |
| 26 this._sizeBehavior = UI.GlassPane.SizeBehavior.SetHeight; | 28 this._sizeBehavior = UI.GlassPane.SizeBehavior.SetExactSize; |
| 29 this._showArrow = false; | |
| 27 } | 30 } |
| 28 | 31 |
| 29 /** | 32 /** |
| 30 * @return {boolean} | 33 * @return {boolean} |
| 31 */ | 34 */ |
| 32 isShowing() { | 35 isShowing() { |
| 33 return this._widget.isShowing(); | 36 return this._widget.isShowing(); |
| 34 } | 37 } |
| 35 | 38 |
| 36 /** | 39 /** |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 98 } | 101 } |
| 99 | 102 |
| 100 /** | 103 /** |
| 101 * @param {!UI.GlassPane.AnchorBehavior} behavior | 104 * @param {!UI.GlassPane.AnchorBehavior} behavior |
| 102 */ | 105 */ |
| 103 setAnchorBehavior(behavior) { | 106 setAnchorBehavior(behavior) { |
| 104 this._anchorBehavior = behavior; | 107 this._anchorBehavior = behavior; |
| 105 } | 108 } |
| 106 | 109 |
| 107 /** | 110 /** |
| 111 * @param {boolean} showArrow | |
| 112 */ | |
| 113 setShowArrow(showArrow) { | |
| 114 this._showArrow = showArrow; | |
| 115 this._arrowElement.classList.toggle('hidden', !showArrow); | |
| 116 } | |
| 117 | |
| 118 /** | |
| 108 * @param {!Document} document | 119 * @param {!Document} document |
| 109 */ | 120 */ |
| 110 show(document) { | 121 show(document) { |
| 111 if (this.isShowing()) | 122 if (this.isShowing()) |
| 112 return; | 123 return; |
| 113 // Deliberately starts with 3000 to hide other z-indexed elements below. | 124 // Deliberately starts with 3000 to hide other z-indexed elements below. |
| 114 this.element.style.zIndex = 3000 + 1000 * UI.GlassPane._panes.size; | 125 this.element.style.zIndex = 3000 + 1000 * UI.GlassPane._panes.size; |
| 115 document.body.addEventListener('mousedown', this._onMouseDownBound, true); | 126 document.body.addEventListener('mousedown', this._onMouseDownBound, true); |
| 116 this._widget.show(document.body); | 127 this._widget.show(document.body); |
| 117 UI.GlassPane._panes.add(this); | 128 UI.GlassPane._panes.add(this); |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 134 return; | 145 return; |
| 135 if (this.contentElement.isSelfOrAncestor(/** @type {?Node} */ (event.deepEle mentFromPoint()))) | 146 if (this.contentElement.isSelfOrAncestor(/** @type {?Node} */ (event.deepEle mentFromPoint()))) |
| 136 return; | 147 return; |
| 137 this._onClickOutsideCallback.call(null, event); | 148 this._onClickOutsideCallback.call(null, event); |
| 138 } | 149 } |
| 139 | 150 |
| 140 _positionContent() { | 151 _positionContent() { |
| 141 if (!this.isShowing()) | 152 if (!this.isShowing()) |
| 142 return; | 153 return; |
| 143 | 154 |
| 144 var gutterSize = 5; | 155 var gutterSize = this._showArrow ? 6 : 3; |
| 156 var scrollbarSize = 14; | |
| 157 var arrowSize = 10; | |
| 158 | |
| 145 var container = UI.GlassPane._containers.get(/** @type {!Document} */ (this. element.ownerDocument)); | 159 var container = UI.GlassPane._containers.get(/** @type {!Document} */ (this. element.ownerDocument)); |
| 146 if (this._sizeBehavior === UI.GlassPane.SizeBehavior.MeasureContent) { | 160 if (this._sizeBehavior === UI.GlassPane.SizeBehavior.MeasureContent) { |
| 147 this.contentElement.positionAt(0, 0); | 161 this.contentElement.positionAt(0, 0); |
| 148 this.contentElement.style.width = ''; | 162 this.contentElement.style.width = ''; |
| 163 this.contentElement.style.maxWidth = ''; | |
| 149 this.contentElement.style.height = ''; | 164 this.contentElement.style.height = ''; |
| 150 this.contentElement.style.maxHeight = ''; | 165 this.contentElement.style.maxHeight = ''; |
| 151 } | 166 } |
| 152 | 167 |
| 153 var containerWidth = container.offsetWidth; | 168 var containerWidth = container.offsetWidth; |
| 154 var containerHeight = container.offsetHeight; | 169 var containerHeight = container.offsetHeight; |
| 155 | 170 |
| 156 var width = containerWidth - gutterSize * 2; | 171 var width = containerWidth - gutterSize * 2; |
| 157 var height = containerHeight - gutterSize * 2; | 172 var height = containerHeight - gutterSize * 2; |
| 158 var positionX = gutterSize; | 173 var positionX = gutterSize; |
| 159 var positionY = gutterSize; | 174 var positionY = gutterSize; |
| 160 | 175 |
| 161 if (this._maxSize) { | 176 if (this._maxSize) { |
| 162 width = Math.min(width, this._maxSize.width); | 177 width = Math.min(width, this._maxSize.width); |
| 163 height = Math.min(height, this._maxSize.height); | 178 height = Math.min(height, this._maxSize.height); |
| 164 } | 179 } |
| 165 | 180 |
| 181 var measuredWidth = 0; | |
| 182 var measuredHeight = 0; | |
| 166 if (this._sizeBehavior === UI.GlassPane.SizeBehavior.MeasureContent) { | 183 if (this._sizeBehavior === UI.GlassPane.SizeBehavior.MeasureContent) { |
| 167 width = Math.min(width, this.contentElement.offsetWidth); | 184 measuredWidth = this.contentElement.offsetWidth; |
| 168 height = Math.min(height, this.contentElement.offsetHeight); | 185 measuredHeight = this.contentElement.offsetHeight; |
| 186 width = Math.min(width, measuredWidth); | |
| 187 height = Math.min(height, measuredHeight); | |
| 169 } | 188 } |
| 170 | 189 |
| 171 if (this._anchorBox) { | 190 if (this._anchorBox) { |
| 172 var anchorBox = this._anchorBox.relativeToElement(container); | 191 var anchorBox = this._anchorBox.relativeToElement(container); |
| 173 var behavior = this._anchorBehavior; | 192 var behavior = this._anchorBehavior; |
| 174 | 193 |
| 194 this._arrowElement.classList.remove('arrow-none'); | |
|
alph
2017/03/07 02:39:10
nit: remove accepts multiple arguments
dgozman
2017/03/07 21:33:50
Done.
| |
| 195 this._arrowElement.classList.remove('arrow-top'); | |
| 196 this._arrowElement.classList.remove('arrow-bottom'); | |
| 197 this._arrowElement.classList.remove('arrow-left'); | |
| 198 this._arrowElement.classList.remove('arrow-right'); | |
| 199 | |
| 175 if (behavior === UI.GlassPane.AnchorBehavior.PreferTop || behavior === UI. GlassPane.AnchorBehavior.PreferBottom) { | 200 if (behavior === UI.GlassPane.AnchorBehavior.PreferTop || behavior === UI. GlassPane.AnchorBehavior.PreferBottom) { |
| 176 var top = anchorBox.y - gutterSize; | 201 var top = anchorBox.y - 2 * gutterSize; |
| 177 var bottom = containerHeight - anchorBox.y - anchorBox.height - gutterSi ze; | 202 var bottom = containerHeight - anchorBox.y - anchorBox.height - 2 * gutt erSize; |
| 178 if (behavior === UI.GlassPane.AnchorBehavior.PreferTop && top < height & & bottom > top) | 203 if (behavior === UI.GlassPane.AnchorBehavior.PreferTop && top < height & & bottom > top) |
| 179 behavior = UI.GlassPane.AnchorBehavior.PreferBottom; | 204 behavior = UI.GlassPane.AnchorBehavior.PreferBottom; |
| 180 if (behavior === UI.GlassPane.AnchorBehavior.PreferBottom && bottom < he ight && top > bottom) | 205 if (behavior === UI.GlassPane.AnchorBehavior.PreferBottom && bottom < he ight && top > bottom) |
| 181 behavior = UI.GlassPane.AnchorBehavior.PreferTop; | 206 behavior = UI.GlassPane.AnchorBehavior.PreferTop; |
| 182 | 207 |
| 208 var arrowY; | |
| 209 if (behavior === UI.GlassPane.AnchorBehavior.PreferTop) { | |
| 210 positionY = Math.max(gutterSize, anchorBox.y - height - gutterSize); | |
| 211 var spaceTop = anchorBox.y - positionY - gutterSize; | |
| 212 if (this._sizeBehavior === UI.GlassPane.SizeBehavior.MeasureContent) { | |
| 213 if (height < measuredHeight) | |
| 214 width += scrollbarSize; | |
| 215 if (height > spaceTop) | |
| 216 this._arrowElement.classList.add('arrow-none'); | |
| 217 } else { | |
| 218 height = Math.min(height, spaceTop); | |
| 219 } | |
| 220 this._arrowElement.setIconType('mediumicon-arrow-bottom'); | |
| 221 this._arrowElement.classList.add('arrow-bottom'); | |
| 222 arrowY = anchorBox.y - gutterSize; | |
| 223 } else { | |
| 224 positionY = anchorBox.y + anchorBox.height + gutterSize; | |
| 225 var spaceBottom = containerHeight - positionY - gutterSize; | |
| 226 if (this._sizeBehavior === UI.GlassPane.SizeBehavior.MeasureContent) { | |
| 227 if (height < measuredHeight) | |
| 228 width += scrollbarSize; | |
| 229 if (height > spaceBottom) { | |
| 230 this._arrowElement.classList.add('arrow-none'); | |
| 231 positionY = containerHeight - gutterSize - height; | |
| 232 } | |
| 233 } else { | |
| 234 height = Math.min(height, spaceBottom); | |
| 235 } | |
| 236 this._arrowElement.setIconType('mediumicon-arrow-top'); | |
| 237 this._arrowElement.classList.add('arrow-top'); | |
| 238 arrowY = anchorBox.y + anchorBox.height + gutterSize; | |
| 239 } | |
| 240 | |
| 183 positionX = Math.max(gutterSize, Math.min(anchorBox.x, containerWidth - width - gutterSize)); | 241 positionX = Math.max(gutterSize, Math.min(anchorBox.x, containerWidth - width - gutterSize)); |
| 184 width = Math.min(width, containerWidth - positionX - gutterSize); | 242 width = Math.min(width, containerWidth - positionX - gutterSize); |
| 185 if (behavior === UI.GlassPane.AnchorBehavior.PreferTop) { | 243 if (2 * arrowSize >= width) { |
| 186 positionY = Math.max(gutterSize, anchorBox.y - height); | 244 this._arrowElement.classList.add('arrow-none'); |
| 187 height = Math.min(height, anchorBox.y - positionY); | |
| 188 } else { | 245 } else { |
| 189 positionY = anchorBox.y + anchorBox.height; | 246 var arrowX = anchorBox.x + Math.min(50, Math.floor(anchorBox.width / 2 )); |
| 190 height = Math.min(height, containerHeight - positionY - gutterSize); | 247 arrowX = Number.constrain(arrowX, positionX + arrowSize, positionX + w idth - arrowSize); |
| 248 this._arrowElement.positionAt(arrowX, arrowY, container); | |
| 191 } | 249 } |
| 192 } else { | 250 } else { |
| 193 var left = anchorBox.x - gutterSize; | 251 var left = anchorBox.x - 2 * gutterSize; |
| 194 var right = containerWidth - anchorBox.x - anchorBox.width - gutterSize; | 252 var right = containerWidth - anchorBox.x - anchorBox.width - 2 * gutterS ize; |
| 195 if (behavior === UI.GlassPane.AnchorBehavior.PreferLeft && left < width && right > left) | 253 if (behavior === UI.GlassPane.AnchorBehavior.PreferLeft && left < width && right > left) |
| 196 behavior = UI.GlassPane.AnchorBehavior.PreferRight; | 254 behavior = UI.GlassPane.AnchorBehavior.PreferRight; |
| 197 if (behavior === UI.GlassPane.AnchorBehavior.PreferRight && right < widt h && left > right) | 255 if (behavior === UI.GlassPane.AnchorBehavior.PreferRight && right < widt h && left > right) |
| 198 behavior = UI.GlassPane.AnchorBehavior.PreferLeft; | 256 behavior = UI.GlassPane.AnchorBehavior.PreferLeft; |
| 199 | 257 |
| 258 var arrowX; | |
| 259 if (behavior === UI.GlassPane.AnchorBehavior.PreferLeft) { | |
| 260 positionX = Math.max(gutterSize, anchorBox.x - width - gutterSize); | |
| 261 var spaceLeft = anchorBox.x - positionX - gutterSize; | |
| 262 if (this._sizeBehavior === UI.GlassPane.SizeBehavior.MeasureContent) { | |
| 263 if (width < measuredWidth) | |
| 264 height += scrollbarSize; | |
| 265 if (width > spaceLeft) | |
| 266 this._arrowElement.classList.add('arrow-none'); | |
| 267 } else { | |
| 268 width = Math.min(width, spaceLeft); | |
| 269 } | |
| 270 this._arrowElement.setIconType('mediumicon-arrow-right'); | |
| 271 this._arrowElement.classList.add('arrow-right'); | |
| 272 arrowX = anchorBox.x - gutterSize; | |
| 273 } else { | |
| 274 positionX = anchorBox.x + anchorBox.width + gutterSize; | |
| 275 var spaceRight = containerWidth - positionX - gutterSize; | |
| 276 if (this._sizeBehavior === UI.GlassPane.SizeBehavior.MeasureContent) { | |
| 277 if (width < measuredWidth) | |
| 278 height += scrollbarSize; | |
| 279 if (width > spaceRight) { | |
| 280 this._arrowElement.classList.add('arrow-none'); | |
| 281 positionX = containerWidth - gutterSize - width; | |
| 282 } | |
| 283 } else { | |
| 284 width = Math.min(width, spaceRight); | |
| 285 } | |
| 286 this._arrowElement.setIconType('mediumicon-arrow-left'); | |
| 287 this._arrowElement.classList.add('arrow-left'); | |
| 288 arrowX = anchorBox.x + anchorBox.width + gutterSize; | |
| 289 } | |
| 290 | |
| 200 positionY = Math.max(gutterSize, Math.min(anchorBox.y, containerHeight - height - gutterSize)); | 291 positionY = Math.max(gutterSize, Math.min(anchorBox.y, containerHeight - height - gutterSize)); |
| 201 height = Math.min(height, containerHeight - positionY - gutterSize); | 292 height = Math.min(height, containerHeight - positionY - gutterSize); |
| 202 if (behavior === UI.GlassPane.AnchorBehavior.PreferLeft) { | 293 if (2 * arrowSize >= height) { |
| 203 positionX = Math.max(gutterSize, anchorBox.x - width); | 294 this._arrowElement.classList.add('arrow-none'); |
| 204 width = Math.min(width, anchorBox.x - positionX); | |
| 205 } else { | 295 } else { |
| 206 positionX = anchorBox.x + anchorBox.width; | 296 var arrowY = anchorBox.y + Math.min(50, Math.floor(anchorBox.height / 2)); |
| 207 width = Math.min(width, containerWidth - positionX - gutterSize); | 297 arrowY = Number.constrain(arrowY, positionY + arrowSize, positionY + h eight - arrowSize); |
| 298 this._arrowElement.positionAt(arrowX, arrowY, container); | |
| 208 } | 299 } |
| 209 } | 300 } |
| 210 } else { | 301 } else { |
| 211 positionX = this._positionX !== null ? this._positionX : (containerWidth - width) / 2; | 302 positionX = this._positionX !== null ? this._positionX : (containerWidth - width) / 2; |
| 212 positionY = this._positionY !== null ? this._positionY : (containerHeight - height) / 2; | 303 positionY = this._positionY !== null ? this._positionY : (containerHeight - height) / 2; |
| 213 width = Math.min(width, containerWidth - positionX - gutterSize); | 304 width = Math.min(width, containerWidth - positionX - gutterSize); |
| 214 height = Math.min(height, containerHeight - positionY - gutterSize); | 305 height = Math.min(height, containerHeight - positionY - gutterSize); |
| 306 this._arrowElement.classList.add('arrow-none'); | |
| 215 } | 307 } |
| 216 | 308 |
| 217 this.contentElement.style.width = width + 'px'; | 309 if (this._sizeBehavior === UI.GlassPane.SizeBehavior.SetMaxSize) |
| 218 if (this._sizeBehavior === UI.GlassPane.SizeBehavior.SetMaxHeight) | 310 this.contentElement.style.maxWidth = width + 'px'; |
| 311 else | |
| 312 this.contentElement.style.width = width + 'px'; | |
| 313 | |
| 314 if (this._sizeBehavior === UI.GlassPane.SizeBehavior.SetMaxSize || | |
| 315 this._sizeBehavior === UI.GlassPane.SizeBehavior.SetExactWidthMaxHeight) | |
| 219 this.contentElement.style.maxHeight = height + 'px'; | 316 this.contentElement.style.maxHeight = height + 'px'; |
| 220 else | 317 else |
| 221 this.contentElement.style.height = height + 'px'; | 318 this.contentElement.style.height = height + 'px'; |
| 319 | |
| 320 | |
| 222 this.contentElement.positionAt(positionX, positionY, container); | 321 this.contentElement.positionAt(positionX, positionY, container); |
| 223 this._widget.doResize(); | 322 this._widget.doResize(); |
| 224 } | 323 } |
| 225 | 324 |
| 226 /** | 325 /** |
| 227 * @protected | 326 * @protected |
| 228 * @return {!UI.Widget} | 327 * @return {!UI.Widget} |
| 229 */ | 328 */ |
| 230 widget() { | 329 widget() { |
| 231 return this._widget; | 330 return this._widget; |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 265 PreferTop: Symbol('PreferTop'), | 364 PreferTop: Symbol('PreferTop'), |
| 266 PreferBottom: Symbol('PreferBottom'), | 365 PreferBottom: Symbol('PreferBottom'), |
| 267 PreferLeft: Symbol('PreferLeft'), | 366 PreferLeft: Symbol('PreferLeft'), |
| 268 PreferRight: Symbol('PreferRight'), | 367 PreferRight: Symbol('PreferRight'), |
| 269 }; | 368 }; |
| 270 | 369 |
| 271 /** | 370 /** |
| 272 * @enum {symbol} | 371 * @enum {symbol} |
| 273 */ | 372 */ |
| 274 UI.GlassPane.SizeBehavior = { | 373 UI.GlassPane.SizeBehavior = { |
| 275 SetHeight: Symbol('SetHeight'), | 374 SetExactSize: Symbol('SetExactSize'), |
| 276 SetMaxHeight: Symbol('SetMaxHeight'), | 375 SetMaxSize: Symbol('SetMaxSize'), |
| 376 SetExactWidthMaxHeight: Symbol('SetExactWidthMaxHeight'), | |
| 277 MeasureContent: Symbol('MeasureContent') | 377 MeasureContent: Symbol('MeasureContent') |
| 278 }; | 378 }; |
| 279 | 379 |
| 280 /** @type {!Map<!Document, !Element>} */ | 380 /** @type {!Map<!Document, !Element>} */ |
| 281 UI.GlassPane._containers = new Map(); | 381 UI.GlassPane._containers = new Map(); |
| 282 /** @type {!Set<!UI.GlassPane>} */ | 382 /** @type {!Set<!UI.GlassPane>} */ |
| 283 UI.GlassPane._panes = new Set(); | 383 UI.GlassPane._panes = new Set(); |
| OLD | NEW |