Chromium Code Reviews| Index: third_party/WebKit/Source/devtools/front_end/ui/ListControl.js |
| diff --git a/third_party/WebKit/Source/devtools/front_end/ui/ListControl.js b/third_party/WebKit/Source/devtools/front_end/ui/ListControl.js |
| index b80e963eb6bbed2184d7808d8e2b65f4124f0fe8..828420ea13e656c273030ce0263c3d9c0e2dd885 100644 |
| --- a/third_party/WebKit/Source/devtools/front_end/ui/ListControl.js |
| +++ b/third_party/WebKit/Source/devtools/front_end/ui/ListControl.js |
| @@ -16,6 +16,8 @@ UI.ListDelegate.prototype = { |
| createElementForItem(item) {}, |
| /** |
| + * This method is not called in NonViewport mode. |
| + * Return zero to make list measure the item (only works in SameHeight mode). |
| * @param {T} item |
| * @return {number} |
| */ |
| @@ -34,14 +36,20 @@ UI.ListDelegate.prototype = { |
| * @param {?Element} toElement |
| */ |
| selectedItemChanged(from, to, fromElement, toElement) {}, |
| + |
| + /** |
| + * @param {T} item |
| + * @param {!Element} element |
| + * @param {!Event} event |
| + */ |
| + itemElementClicked(item, element, event) {}, |
|
pfeldman
2017/01/04 02:07:50
It does not help much in case you decide to listen
|
| }; |
| /** @enum {symbol} */ |
| UI.ListMode = { |
| - Grow: Symbol('UI.ListMode.Grow'), |
| - ViewportFixedItems: Symbol('UI.ListMode.ViewportFixedItems'), |
| - ViewportFixedItemsMeasured: Symbol('UI.ListMode.ViewportFixedItemsMeasured'), |
| - ViewportVariableItems: Symbol('UI.ListMode.ViewportVariableItems') |
| + NonViewport: Symbol('UI.ListMode.NonViewport'), |
| + SameHeightItems: Symbol('UI.ListMode.SameHeightItems'), |
|
pfeldman
2017/01/04 02:07:50
Equal/Various
|
| + DifferentHeightItems: Symbol('UI.ListMode.DifferentHeightItems') |
| }; |
| /** |
| @@ -69,41 +77,21 @@ UI.ListControl = class { |
| this._itemToElement = new Map(); |
| this._selectedIndex = -1; |
| - this._boundKeyDown = event => { |
| - if (this.onKeyDown(event)) |
| - event.consume(true); |
| - }; |
| - this._boundClick = event => { |
| - if (this.onClick(event)) |
| - event.consume(true); |
| - }; |
| + this.element.addEventListener('click', this._onClick.bind(this), false); |
| this._boundScroll = event => { |
| this._updateViewport(this.element.scrollTop, this.element.offsetHeight); |
| }; |
| this._delegate = delegate; |
| - this._mode = mode || UI.ListMode.ViewportFixedItemsMeasured; |
| + this._mode = mode || UI.ListMode.SameHeightItems; |
| this._fixedHeight = 0; |
| this._variableOffsets = new Int32Array(0); |
| this._clearContents(); |
| - if (this._mode !== UI.ListMode.Grow) |
| + if (this._mode !== UI.ListMode.NonViewport) |
| this.element.addEventListener('scroll', this._boundScroll, false); |
| } |
| /** |
| - * @param {boolean} handleInput |
| - */ |
| - setHandleInput(handleInput) { |
| - if (handleInput) { |
| - this.element.addEventListener('keydown', this._boundKeyDown, false); |
| - this.element.addEventListener('click', this._boundClick, false); |
| - } else { |
| - this.element.removeEventListener('keydown', this._boundKeyDown, false); |
| - this.element.removeEventListener('click', this._boundClick, false); |
| - } |
| - } |
| - |
| - /** |
| * @return {number} |
| */ |
| length() { |
| @@ -197,7 +185,7 @@ UI.ListControl = class { |
| } |
| viewportResized() { |
| - if (this._mode === UI.ListMode.Grow) |
| + if (this._mode === UI.ListMode.NonViewport) |
| return; |
| // TODO(dgozman): try to keep visible scrollTop the same. |
| var scrollTop = this.element.scrollTop; |
| @@ -206,9 +194,9 @@ UI.ListControl = class { |
| this._updateViewport(Number.constrain(scrollTop, 0, this._totalHeight() - viewportHeight), viewportHeight); |
| } |
| - fixedHeightChanged() { |
| - if (this._mode !== UI.ListMode.ViewportFixedItemsMeasured && this._mode !== UI.ListMode.ViewportFixedItems) |
| - throw 'Only supported in fixed height items modes'; |
| + invalidateSameHeight() { |
|
pfeldman
2017/01/04 02:07:50
invalidateItemsHeight
|
| + if (this._mode !== UI.ListMode.SameHeightItems) |
| + throw 'Only supported in same height items mode'; |
| this._fixedHeight = 0; |
| if (this._items.length) { |
| this._itemToElement.clear(); |
| @@ -217,81 +205,105 @@ UI.ListControl = class { |
| } |
| /** |
| - * @param {number} index |
| + * @param {T} item |
| + * @param {boolean=} center |
| */ |
| - scrollItemAtIndexIntoView(index) { |
| - if (this._mode === UI.ListMode.Grow) { |
| - this._elementAtIndex(index).scrollIntoViewIfNeeded(false); |
| - return; |
| - } |
| - var top = this._offsetAtIndex(index); |
| - var bottom = this._offsetAtIndex(index + 1); |
| - var scrollTop = this.element.scrollTop; |
| - var viewportHeight = this.element.offsetHeight; |
| - if (top < scrollTop) |
| - this._updateViewport(top, viewportHeight); |
| - else if (bottom > scrollTop + viewportHeight) |
| - this._updateViewport(bottom - viewportHeight, viewportHeight); |
| + scrollItemIntoView(item, center) { |
| + var index = this._items.indexOf(item); |
| + if (index === -1) |
| + throw 'Attempt to scroll onto missing item'; |
| + this._scrollIntoView(index, center); |
| } |
| /** |
| - * @param {number} index |
| - * @param {boolean=} scrollIntoView |
| + * @return {?T} |
| + */ |
| + selectedItem() { |
| + return this._selectedIndex === -1 ? null : this._items[this._selectedIndex]; |
| + } |
| + |
| + /** |
| + * @param {?T} item |
| + * @param {boolean=} center |
| */ |
| - selectItemAtIndex(index, scrollIntoView) { |
| - if (index !== -1 && !this._delegate.isItemSelectable(this._items[index])) |
| - throw 'Attempt to select non-selectable item'; |
| + selectItem(item, center) { |
| + var index = -1; |
| + if (item !== null) { |
| + index = this._items.indexOf(item); |
| + if (index === -1) |
| + throw 'Attempt to select missing item'; |
| + if (!this._delegate.isItemSelectable(item)) |
| + throw 'Attempt to select non-selectable item'; |
| + } |
| this._select(index); |
| - if (index !== -1 && !!scrollIntoView) |
| - this.scrollItemAtIndexIntoView(index); |
| + if (index !== -1) |
| + this._scrollIntoView(index, center); |
| } |
| /** |
| - * @return {number} |
| + * @param {boolean=} canWrap |
| + * @param {boolean=} center |
| + * @return {boolean} |
| */ |
| - selectedIndex() { |
| - return this._selectedIndex; |
| + selectPreviousItem(canWrap, center) { |
| + if (this._selectedIndex === -1 && !canWrap) |
| + return false; |
| + var index = this._selectedIndex === -1 ? this._items.length - 1 : this._selectedIndex - 1; |
| + index = this._findFirstSelectable(index, -1, !!canWrap); |
| + if (index !== -1) { |
| + this._scrollIntoView(index, center); |
| + this._select(index); |
| + return true; |
| + } |
| + return false; |
| } |
| /** |
| - * @return {?T} |
| + * @param {boolean=} canWrap |
| + * @param {boolean=} center |
| + * @return {boolean} |
| */ |
| - selectedItem() { |
| - return this._selectedIndex === -1 ? null : this._items[this._selectedIndex]; |
| + selectNextItem(canWrap, center) { |
| + if (this._selectedIndex === -1 && !canWrap) |
| + return false; |
| + var index = this._selectedIndex === -1 ? 0 : this._selectedIndex + 1; |
| + index = this._findFirstSelectable(index, +1, !!canWrap); |
| + if (index !== -1) { |
| + this._scrollIntoView(index, center); |
| + this._select(index); |
| + return true; |
| + } |
| + return false; |
| } |
| /** |
| - * @param {!Event} event |
| + * @param {boolean=} center |
| * @return {boolean} |
| */ |
| - onKeyDown(event) { |
| - var index = -1; |
| - switch (event.key) { |
| - case 'ArrowUp': |
| - index = this._selectedIndex === -1 ? this._items.length - 1 : this._selectedIndex - 1; |
| - index = this._findFirstSelectable(index, -1, true); |
| - break; |
| - case 'ArrowDown': |
| - index = this._selectedIndex === -1 ? 0 : this._selectedIndex + 1; |
| - index = this._findFirstSelectable(index, +1, true); |
| - break; |
| - case 'PageUp': |
| - if (this._mode === UI.ListMode.Grow) |
| - return false; |
| - index = this._selectedIndex === -1 ? this._items.length - 1 : this._selectedIndex; |
| - index = this._findPageSelectable(index, -1); |
| - break; |
| - case 'PageDown': |
| - if (this._mode === UI.ListMode.Grow) |
| - return false; |
| - index = this._selectedIndex === -1 ? 0 : this._selectedIndex; |
| - index = this._findPageSelectable(index, +1); |
| - break; |
| - default: |
| - return false; |
| + selectItemPreviousPage(center) { |
| + if (this._mode === UI.ListMode.NonViewport) |
| + return false; |
| + var index = this._selectedIndex === -1 ? this._items.length - 1 : this._selectedIndex; |
| + index = this._findPageSelectable(index, -1); |
| + if (index !== -1) { |
| + this._scrollIntoView(index, center); |
| + this._select(index); |
| + return true; |
| } |
| + return false; |
| + } |
| + |
| + /** |
| + * @param {boolean=} center |
| + * @return {boolean} |
| + */ |
| + selectItemNextPage(center) { |
| + if (this._mode === UI.ListMode.NonViewport) |
| + return false; |
| + var index = this._selectedIndex === -1 ? 0 : this._selectedIndex; |
| + index = this._findPageSelectable(index, +1); |
| if (index !== -1) { |
| - this.scrollItemAtIndexIntoView(index); |
| + this._scrollIntoView(index, center); |
| this._select(index); |
| return true; |
| } |
| @@ -299,20 +311,44 @@ UI.ListControl = class { |
| } |
| /** |
| + * @param {number} index |
| + * @param {boolean=} center |
| + */ |
| + _scrollIntoView(index, center) { |
| + if (this._mode === UI.ListMode.NonViewport) { |
| + this._elementAtIndex(index).scrollIntoViewIfNeeded(!!center); |
| + return; |
| + } |
| + |
| + var top = this._offsetAtIndex(index); |
| + var bottom = this._offsetAtIndex(index + 1); |
| + var viewportHeight = this.element.offsetHeight; |
| + if (center) { |
| + var scrollTo = (top + bottom) / 2 - viewportHeight / 2; |
| + this._updateViewport(Number.constrain(scrollTo, 0, this._totalHeight() - viewportHeight), viewportHeight); |
| + return; |
| + } |
| + |
| + var scrollTop = this.element.scrollTop; |
| + if (top < scrollTop) |
| + this._updateViewport(top, viewportHeight); |
| + else if (bottom > scrollTop + viewportHeight) |
| + this._updateViewport(bottom - viewportHeight, viewportHeight); |
| + } |
| + |
| + /** |
| * @param {!Event} event |
| - * @return {boolean} |
| */ |
| - onClick(event) { |
| + _onClick(event) { |
| var node = event.target; |
| while (node && node.parentNodeOrShadowHost() !== this.element) |
| node = node.parentNodeOrShadowHost(); |
| if (!node) |
| - return false; |
| - var index = this._items.findIndex(item => this._itemToElement.get(item) === node); |
| - if (index === -1 || !this._delegate.isItemSelectable(this._items[index])) |
| - return false; |
| - this._select(index); |
| - return true; |
| + return; |
| + var element = /** @type {!Element} */ (node); |
| + var index = this._items.findIndex(item => this._itemToElement.get(item) === element); |
| + if (index !== -1) |
| + this._delegate.itemElementClicked(this._items[index], element, event); |
| } |
| /** |
| @@ -327,11 +363,11 @@ UI.ListControl = class { |
| * @return {number} |
| */ |
| _indexAtOffset(offset) { |
| - if (this._mode === UI.ListMode.Grow) |
| - throw 'There should be no offset conversions in grow mode'; |
| + if (this._mode === UI.ListMode.NonViewport) |
| + throw 'There should be no offset conversions in non-viewport mode'; |
| if (!this._items.length || offset < 0) |
| return 0; |
| - if (this._mode === UI.ListMode.ViewportVariableItems) { |
| + if (this._mode === UI.ListMode.DifferentHeightItems) { |
| return Math.min( |
| this._items.length - 1, this._variableOffsets.lowerBound(offset, undefined, 0, this._items.length)); |
| } |
| @@ -359,11 +395,11 @@ UI.ListControl = class { |
| * @return {number} |
| */ |
| _offsetAtIndex(index) { |
| - if (this._mode === UI.ListMode.Grow) |
| - throw 'There should be no offset conversions in grow mode'; |
| + if (this._mode === UI.ListMode.NonViewport) |
| + throw 'There should be no offset conversions in non-viewport mode'; |
| if (!this._items.length) |
| return 0; |
| - if (this._mode === UI.ListMode.ViewportVariableItems) |
| + if (this._mode === UI.ListMode.DifferentHeightItems) |
| return this._variableOffsets[index]; |
| if (!this._fixedHeight) |
| this._measureHeight(); |
| @@ -371,10 +407,9 @@ UI.ListControl = class { |
| } |
| _measureHeight() { |
| - if (this._mode === UI.ListMode.ViewportFixedItemsMeasured) |
| + this._fixedHeight = this._delegate.heightForItem(this._items[0]); |
| + if (!this._fixedHeight) |
| this._fixedHeight = UI.measurePreferredSize(this._elementAtIndex(0), this.element).height; |
| - else |
| - this._fixedHeight = this._delegate.heightForItem(this._items[0]); |
| } |
| /** |
| @@ -459,12 +494,12 @@ UI.ListControl = class { |
| * @param {number} inserted |
| */ |
| _invalidate(from, to, inserted) { |
| - if (this._mode === UI.ListMode.Grow) { |
| - this._invalidateGrowMode(from, to - from, inserted); |
| + if (this._mode === UI.ListMode.NonViewport) { |
| + this._invalidateNonViewportMode(from, to - from, inserted); |
| return; |
| } |
| - if (this._mode === UI.ListMode.ViewportVariableItems) { |
| + if (this._mode === UI.ListMode.DifferentHeightItems) { |
| this._reallocateVariableOffsets(this._items.length + 1, from + 1); |
| for (var i = from + 1; i <= this._items.length; i++) |
| this._variableOffsets[i] = this._variableOffsets[i - 1] + this._delegate.heightForItem(this._items[i - 1]); |
| @@ -512,7 +547,7 @@ UI.ListControl = class { |
| * @param {number} remove |
| * @param {number} add |
| */ |
| - _invalidateGrowMode(start, remove, add) { |
| + _invalidateNonViewportMode(start, remove, add) { |
| var startElement = this._topElement; |
| for (var index = 0; index < start; index++) |
| startElement = startElement.nextElementSibling; |
| @@ -523,8 +558,8 @@ UI.ListControl = class { |
| } |
| _clearViewport() { |
| - if (this._mode === UI.ListMode.Grow) |
| - throw 'There should be no viewport updates in grow mode'; |
| + if (this._mode === UI.ListMode.NonViewport) |
| + throw 'There should be no viewport updates in non-viewport mode'; |
| this._firstIndex = 0; |
| this._lastIndex = 0; |
| this._renderedHeight = 0; |
| @@ -548,8 +583,8 @@ UI.ListControl = class { |
| */ |
| _updateViewport(scrollTop, viewportHeight) { |
| // Note: this method should not force layout. Be careful. |
| - if (this._mode === UI.ListMode.Grow) |
| - throw 'There should be no viewport updates in grow mode'; |
| + if (this._mode === UI.ListMode.NonViewport) |
| + throw 'There should be no viewport updates in non-viewport mode'; |
| var totalHeight = this._totalHeight(); |
| if (!totalHeight) { |