| 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; |
| 193 this._arrowElement.classList.remove('arrow-none', 'arrow-top', 'arrow-bott
om', 'arrow-left', 'arrow-right'); |
| 174 | 194 |
| 175 if (behavior === UI.GlassPane.AnchorBehavior.PreferTop || behavior === UI.
GlassPane.AnchorBehavior.PreferBottom) { | 195 if (behavior === UI.GlassPane.AnchorBehavior.PreferTop || behavior === UI.
GlassPane.AnchorBehavior.PreferBottom) { |
| 176 var top = anchorBox.y - gutterSize; | 196 var top = anchorBox.y - 2 * gutterSize; |
| 177 var bottom = containerHeight - anchorBox.y - anchorBox.height - gutterSi
ze; | 197 var bottom = containerHeight - anchorBox.y - anchorBox.height - 2 * gutt
erSize; |
| 178 if (behavior === UI.GlassPane.AnchorBehavior.PreferTop && top < height &
& bottom > top) | 198 if (behavior === UI.GlassPane.AnchorBehavior.PreferTop && top < height &
& bottom > top) |
| 179 behavior = UI.GlassPane.AnchorBehavior.PreferBottom; | 199 behavior = UI.GlassPane.AnchorBehavior.PreferBottom; |
| 180 if (behavior === UI.GlassPane.AnchorBehavior.PreferBottom && bottom < he
ight && top > bottom) | 200 if (behavior === UI.GlassPane.AnchorBehavior.PreferBottom && bottom < he
ight && top > bottom) |
| 181 behavior = UI.GlassPane.AnchorBehavior.PreferTop; | 201 behavior = UI.GlassPane.AnchorBehavior.PreferTop; |
| 182 | 202 |
| 203 var arrowY; |
| 204 if (behavior === UI.GlassPane.AnchorBehavior.PreferTop) { |
| 205 positionY = Math.max(gutterSize, anchorBox.y - height - gutterSize); |
| 206 var spaceTop = anchorBox.y - positionY - gutterSize; |
| 207 if (this._sizeBehavior === UI.GlassPane.SizeBehavior.MeasureContent) { |
| 208 if (height < measuredHeight) |
| 209 width += scrollbarSize; |
| 210 if (height > spaceTop) |
| 211 this._arrowElement.classList.add('arrow-none'); |
| 212 } else { |
| 213 height = Math.min(height, spaceTop); |
| 214 } |
| 215 this._arrowElement.setIconType('mediumicon-arrow-bottom'); |
| 216 this._arrowElement.classList.add('arrow-bottom'); |
| 217 arrowY = anchorBox.y - gutterSize; |
| 218 } else { |
| 219 positionY = anchorBox.y + anchorBox.height + gutterSize; |
| 220 var spaceBottom = containerHeight - positionY - gutterSize; |
| 221 if (this._sizeBehavior === UI.GlassPane.SizeBehavior.MeasureContent) { |
| 222 if (height < measuredHeight) |
| 223 width += scrollbarSize; |
| 224 if (height > spaceBottom) { |
| 225 this._arrowElement.classList.add('arrow-none'); |
| 226 positionY = containerHeight - gutterSize - height; |
| 227 } |
| 228 } else { |
| 229 height = Math.min(height, spaceBottom); |
| 230 } |
| 231 this._arrowElement.setIconType('mediumicon-arrow-top'); |
| 232 this._arrowElement.classList.add('arrow-top'); |
| 233 arrowY = anchorBox.y + anchorBox.height + gutterSize; |
| 234 } |
| 235 |
| 183 positionX = Math.max(gutterSize, Math.min(anchorBox.x, containerWidth -
width - gutterSize)); | 236 positionX = Math.max(gutterSize, Math.min(anchorBox.x, containerWidth -
width - gutterSize)); |
| 184 width = Math.min(width, containerWidth - positionX - gutterSize); | 237 width = Math.min(width, containerWidth - positionX - gutterSize); |
| 185 if (behavior === UI.GlassPane.AnchorBehavior.PreferTop) { | 238 if (2 * arrowSize >= width) { |
| 186 positionY = Math.max(gutterSize, anchorBox.y - height); | 239 this._arrowElement.classList.add('arrow-none'); |
| 187 height = Math.min(height, anchorBox.y - positionY); | |
| 188 } else { | 240 } else { |
| 189 positionY = anchorBox.y + anchorBox.height; | 241 var arrowX = anchorBox.x + Math.min(50, Math.floor(anchorBox.width / 2
)); |
| 190 height = Math.min(height, containerHeight - positionY - gutterSize); | 242 arrowX = Number.constrain(arrowX, positionX + arrowSize, positionX + w
idth - arrowSize); |
| 243 this._arrowElement.positionAt(arrowX, arrowY, container); |
| 191 } | 244 } |
| 192 } else { | 245 } else { |
| 193 var left = anchorBox.x - gutterSize; | 246 var left = anchorBox.x - 2 * gutterSize; |
| 194 var right = containerWidth - anchorBox.x - anchorBox.width - gutterSize; | 247 var right = containerWidth - anchorBox.x - anchorBox.width - 2 * gutterS
ize; |
| 195 if (behavior === UI.GlassPane.AnchorBehavior.PreferLeft && left < width
&& right > left) | 248 if (behavior === UI.GlassPane.AnchorBehavior.PreferLeft && left < width
&& right > left) |
| 196 behavior = UI.GlassPane.AnchorBehavior.PreferRight; | 249 behavior = UI.GlassPane.AnchorBehavior.PreferRight; |
| 197 if (behavior === UI.GlassPane.AnchorBehavior.PreferRight && right < widt
h && left > right) | 250 if (behavior === UI.GlassPane.AnchorBehavior.PreferRight && right < widt
h && left > right) |
| 198 behavior = UI.GlassPane.AnchorBehavior.PreferLeft; | 251 behavior = UI.GlassPane.AnchorBehavior.PreferLeft; |
| 199 | 252 |
| 253 var arrowX; |
| 254 if (behavior === UI.GlassPane.AnchorBehavior.PreferLeft) { |
| 255 positionX = Math.max(gutterSize, anchorBox.x - width - gutterSize); |
| 256 var spaceLeft = anchorBox.x - positionX - gutterSize; |
| 257 if (this._sizeBehavior === UI.GlassPane.SizeBehavior.MeasureContent) { |
| 258 if (width < measuredWidth) |
| 259 height += scrollbarSize; |
| 260 if (width > spaceLeft) |
| 261 this._arrowElement.classList.add('arrow-none'); |
| 262 } else { |
| 263 width = Math.min(width, spaceLeft); |
| 264 } |
| 265 this._arrowElement.setIconType('mediumicon-arrow-right'); |
| 266 this._arrowElement.classList.add('arrow-right'); |
| 267 arrowX = anchorBox.x - gutterSize; |
| 268 } else { |
| 269 positionX = anchorBox.x + anchorBox.width + gutterSize; |
| 270 var spaceRight = containerWidth - positionX - gutterSize; |
| 271 if (this._sizeBehavior === UI.GlassPane.SizeBehavior.MeasureContent) { |
| 272 if (width < measuredWidth) |
| 273 height += scrollbarSize; |
| 274 if (width > spaceRight) { |
| 275 this._arrowElement.classList.add('arrow-none'); |
| 276 positionX = containerWidth - gutterSize - width; |
| 277 } |
| 278 } else { |
| 279 width = Math.min(width, spaceRight); |
| 280 } |
| 281 this._arrowElement.setIconType('mediumicon-arrow-left'); |
| 282 this._arrowElement.classList.add('arrow-left'); |
| 283 arrowX = anchorBox.x + anchorBox.width + gutterSize; |
| 284 } |
| 285 |
| 200 positionY = Math.max(gutterSize, Math.min(anchorBox.y, containerHeight -
height - gutterSize)); | 286 positionY = Math.max(gutterSize, Math.min(anchorBox.y, containerHeight -
height - gutterSize)); |
| 201 height = Math.min(height, containerHeight - positionY - gutterSize); | 287 height = Math.min(height, containerHeight - positionY - gutterSize); |
| 202 if (behavior === UI.GlassPane.AnchorBehavior.PreferLeft) { | 288 if (2 * arrowSize >= height) { |
| 203 positionX = Math.max(gutterSize, anchorBox.x - width); | 289 this._arrowElement.classList.add('arrow-none'); |
| 204 width = Math.min(width, anchorBox.x - positionX); | |
| 205 } else { | 290 } else { |
| 206 positionX = anchorBox.x + anchorBox.width; | 291 var arrowY = anchorBox.y + Math.min(50, Math.floor(anchorBox.height /
2)); |
| 207 width = Math.min(width, containerWidth - positionX - gutterSize); | 292 arrowY = Number.constrain(arrowY, positionY + arrowSize, positionY + h
eight - arrowSize); |
| 293 this._arrowElement.positionAt(arrowX, arrowY, container); |
| 208 } | 294 } |
| 209 } | 295 } |
| 210 } else { | 296 } else { |
| 211 positionX = this._positionX !== null ? this._positionX : (containerWidth -
width) / 2; | 297 positionX = this._positionX !== null ? this._positionX : (containerWidth -
width) / 2; |
| 212 positionY = this._positionY !== null ? this._positionY : (containerHeight
- height) / 2; | 298 positionY = this._positionY !== null ? this._positionY : (containerHeight
- height) / 2; |
| 213 width = Math.min(width, containerWidth - positionX - gutterSize); | 299 width = Math.min(width, containerWidth - positionX - gutterSize); |
| 214 height = Math.min(height, containerHeight - positionY - gutterSize); | 300 height = Math.min(height, containerHeight - positionY - gutterSize); |
| 301 this._arrowElement.classList.add('arrow-none'); |
| 215 } | 302 } |
| 216 | 303 |
| 217 this.contentElement.style.width = width + 'px'; | 304 if (this._sizeBehavior === UI.GlassPane.SizeBehavior.SetMaxSize) |
| 218 if (this._sizeBehavior === UI.GlassPane.SizeBehavior.SetMaxHeight) | 305 this.contentElement.style.maxWidth = width + 'px'; |
| 306 else |
| 307 this.contentElement.style.width = width + 'px'; |
| 308 |
| 309 if (this._sizeBehavior === UI.GlassPane.SizeBehavior.SetMaxSize || |
| 310 this._sizeBehavior === UI.GlassPane.SizeBehavior.SetExactWidthMaxHeight) |
| 219 this.contentElement.style.maxHeight = height + 'px'; | 311 this.contentElement.style.maxHeight = height + 'px'; |
| 220 else | 312 else |
| 221 this.contentElement.style.height = height + 'px'; | 313 this.contentElement.style.height = height + 'px'; |
| 314 |
| 315 |
| 222 this.contentElement.positionAt(positionX, positionY, container); | 316 this.contentElement.positionAt(positionX, positionY, container); |
| 223 this._widget.doResize(); | 317 this._widget.doResize(); |
| 224 } | 318 } |
| 225 | 319 |
| 226 /** | 320 /** |
| 227 * @protected | 321 * @protected |
| 228 * @return {!UI.Widget} | 322 * @return {!UI.Widget} |
| 229 */ | 323 */ |
| 230 widget() { | 324 widget() { |
| 231 return this._widget; | 325 return this._widget; |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 265 PreferTop: Symbol('PreferTop'), | 359 PreferTop: Symbol('PreferTop'), |
| 266 PreferBottom: Symbol('PreferBottom'), | 360 PreferBottom: Symbol('PreferBottom'), |
| 267 PreferLeft: Symbol('PreferLeft'), | 361 PreferLeft: Symbol('PreferLeft'), |
| 268 PreferRight: Symbol('PreferRight'), | 362 PreferRight: Symbol('PreferRight'), |
| 269 }; | 363 }; |
| 270 | 364 |
| 271 /** | 365 /** |
| 272 * @enum {symbol} | 366 * @enum {symbol} |
| 273 */ | 367 */ |
| 274 UI.GlassPane.SizeBehavior = { | 368 UI.GlassPane.SizeBehavior = { |
| 275 SetHeight: Symbol('SetHeight'), | 369 SetExactSize: Symbol('SetExactSize'), |
| 276 SetMaxHeight: Symbol('SetMaxHeight'), | 370 SetMaxSize: Symbol('SetMaxSize'), |
| 371 SetExactWidthMaxHeight: Symbol('SetExactWidthMaxHeight'), |
| 277 MeasureContent: Symbol('MeasureContent') | 372 MeasureContent: Symbol('MeasureContent') |
| 278 }; | 373 }; |
| 279 | 374 |
| 280 /** @type {!Map<!Document, !Element>} */ | 375 /** @type {!Map<!Document, !Element>} */ |
| 281 UI.GlassPane._containers = new Map(); | 376 UI.GlassPane._containers = new Map(); |
| 282 /** @type {!Set<!UI.GlassPane>} */ | 377 /** @type {!Set<!UI.GlassPane>} */ |
| 283 UI.GlassPane._panes = new Set(); | 378 UI.GlassPane._panes = new Set(); |
| OLD | NEW |