Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(535)

Unified Diff: third_party/WebKit/Source/devtools/front_end/ui/SuggestBox.js

Issue 2592433003: [DevTools] Replace ViewportControl with ListControl. (Closed)
Patch Set: tests Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/WebKit/Source/devtools/front_end/ui/SuggestBox.js
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/SuggestBox.js b/third_party/WebKit/Source/devtools/front_end/ui/SuggestBox.js
index 1b20de2e0162c3f1caf51a5295798dbd816e4dad..2492af997336363ffa703874f4cb32294c14892a 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui/SuggestBox.js
+++ b/third_party/WebKit/Source/devtools/front_end/ui/SuggestBox.js
@@ -46,8 +46,8 @@ UI.SuggestBoxDelegate.prototype = {
};
/**
- * @implements {UI.ViewportControl.Provider}
* @unrestricted
+ * @implements {UI.ListDelegate}
*/
UI.SuggestBox = class {
/**
@@ -57,32 +57,26 @@ UI.SuggestBox = class {
*/
constructor(suggestBoxDelegate, maxItemsHeight, captureEnter) {
this._suggestBoxDelegate = suggestBoxDelegate;
- this._length = 0;
- this._selectedIndex = -1;
- this._selectedElement = null;
this._maxItemsHeight = maxItemsHeight;
this._maybeHideBound = this._maybeHide.bind(this);
this._container = createElementWithClass('div', 'suggest-box-container');
- this._viewport = new UI.ViewportControl(this);
- this._element = this._viewport.element;
+ this._rowHeight = 17;
+ /** @type {!UI.ListControl<!UI.SuggestBox.Suggestion>} */
+ this._list = new UI.ListControl(this);
+ this._element = this._list.element;
this._element.classList.add('suggest-box');
this._container.appendChild(this._element);
this._element.addEventListener('mousedown', this._onBoxMouseDown.bind(this), true);
this._detailsPopup = this._container.createChild('div', 'suggest-box details-popup monospace');
this._detailsPopup.classList.add('hidden');
this._asyncDetailsCallback = null;
- /** @type {!Map<number, !Promise<{detail: string, description: string}>>} */
+ /** @type {!Map<!UI.SuggestBox.Suggestion, !Promise<{detail: string, description: string}>>} */
this._asyncDetailsPromises = new Map();
this._userInteracted = false;
this._captureEnter = captureEnter;
- /** @type {!Array<!Element>} */
- this._elementList = [];
- this._rowHeight = 17;
this._viewportWidth = '100vw';
this._hasVerticalScroll = false;
this._userEnteredText = '';
- /** @type {!UI.SuggestBox.Suggestions} */
- this._items = [];
}
/**
@@ -96,17 +90,18 @@ UI.SuggestBox = class {
* @param {!AnchorBox} anchorBox
*/
setPosition(anchorBox) {
- this._updateBoxPosition(anchorBox);
+ this._updateBoxPosition(anchorBox, this._list.length());
}
/**
* @param {!AnchorBox} anchorBox
+ * @param {number} length
*/
- _updateBoxPosition(anchorBox) {
+ _updateBoxPosition(anchorBox, length) {
console.assert(this._overlay);
- if (this._lastAnchorBox && this._lastAnchorBox.equals(anchorBox) && this._lastItemCount === this.itemCount())
+ if (this._lastAnchorBox && this._lastAnchorBox.equals(anchorBox) && this._lastItemCount === length)
return;
- this._lastItemCount = this.itemCount();
+ this._lastItemCount = length;
this._lastAnchorBox = anchorBox;
// Position relative to main DevTools element.
@@ -128,24 +123,28 @@ UI.SuggestBox = class {
var maxHeight = Math.min(
Math.max(underHeight, aboveHeight) - spacer,
this._maxItemsHeight ? this._maxItemsHeight * this._rowHeight : Infinity);
- var height = this._rowHeight * this._items.length;
+ var height = this._rowHeight * length;
this._hasVerticalScroll = height > maxHeight;
this._element.style.height = Math.min(maxHeight, height) + 'px';
}
- _updateWidth() {
+ /**
+ * @param {!UI.SuggestBox.Suggestions} items
+ */
+ _updateWidth(items) {
if (this._hasVerticalScroll) {
this._element.style.width = '100vw';
return;
}
+ if (!items.length)
+ return;
// If there are no scrollbars, set the width to the width of the largest row.
- var maxIndex = 0;
- for (var i = 0; i < this._items.length; i++) {
- if (this._items[i].title.length > this._items[maxIndex].title.length)
- maxIndex = i;
+ var maxItem = items[0];
+ for (var i = 1; i < items.length; i++) {
+ if (items[i].title.length > maxItem.title.length)
+ maxItem = items[i];
}
- var element = /** @type {!Element} */ (this.itemElement(maxIndex));
- this._element.style.width = UI.measurePreferredSize(element, this._element).width + 'px';
+ this._element.style.width = UI.measurePreferredSize(this.createElementForItem(maxItem), this._element).width + 'px';
}
/**
@@ -175,10 +174,8 @@ UI.SuggestBox = class {
this._bodyElement.addEventListener('mousedown', this._maybeHideBound, true);
this._overlay = new UI.SuggestBox.Overlay();
this._overlay.setContentElement(this._container);
- var measuringElement = this._createItemElement('1', '12');
- this._viewport.element.appendChild(measuringElement);
- this._rowHeight = measuringElement.getBoundingClientRect().height;
- measuringElement.remove();
+ this._rowHeight =
+ UI.measurePreferredSize(this.createElementForItem({title: '1', subtitle: '12'}), this._element).height;
}
hide() {
@@ -191,8 +188,6 @@ UI.SuggestBox = class {
this._container.remove();
this._overlay.dispose();
delete this._overlay;
- delete this._selectedElement;
- this._selectedIndex = -1;
delete this._lastAnchorBox;
}
@@ -210,10 +205,10 @@ UI.SuggestBox = class {
return true;
}
- if (!this.visible() || !this._selectedElement)
+ if (!this.visible() || !this._list.selectedItem())
return false;
- var suggestion = this._selectedElement.__fullValue;
+ var suggestion = this._list.selectedItem().title;
if (!suggestion)
return false;
@@ -236,57 +231,21 @@ UI.SuggestBox = class {
}
/**
- * @param {number} shift
- * @param {boolean=} isCircular
- * @return {boolean} is changed
- */
- _selectClosest(shift, isCircular) {
- if (!this._length)
- return false;
-
- this._userInteracted = true;
-
- if (this._selectedIndex === -1 && shift < 0)
- shift += 1;
-
- var index = this._selectedIndex + shift;
-
- if (isCircular)
- index = (this._length + index) % this._length;
- else
- index = Number.constrain(index, 0, this._length - 1);
-
- this._selectItem(index);
- return true;
- }
-
- /**
- * @param {!Event} event
- */
- _onItemMouseDown(event) {
- this._selectedElement = event.currentTarget;
- this.acceptSuggestion();
- event.consume(true);
- }
-
- /**
- * @param {string} query
- * @param {string} title
- * @param {string=} subtitle
- * @param {string=} iconType
- * @param {boolean=} isSecondary
+ * @override
+ * @param {!UI.SuggestBox.Suggestion} item
* @return {!Element}
*/
- _createItemElement(query, title, subtitle, iconType, isSecondary) {
+ createElementForItem(item) {
+ var query = this._userEnteredText;
var element = createElementWithClass('div', 'suggest-box-content-item source-code');
- if (iconType) {
- var icon = UI.Icon.create(iconType, 'suggestion-icon');
+ if (item.iconType) {
+ var icon = UI.Icon.create(item.iconType, 'suggestion-icon');
element.appendChild(icon);
}
- if (isSecondary)
+ if (item.isSecondary)
element.classList.add('secondary');
element.tabIndex = -1;
- var displayText = title.trimEnd(50 + query.length);
+ var displayText = item.title.trimEnd(50 + query.length);
var titleElement = element.createChild('span', 'suggestion-title');
var index = displayText.toLowerCase().indexOf(query.toLowerCase());
@@ -296,41 +255,75 @@ UI.SuggestBox = class {
titleElement.createChild('span', 'query').textContent = displayText.substring(index, index + query.length);
titleElement.createChild('span').textContent = displayText.substring(index > -1 ? index + query.length : 0);
titleElement.createChild('span', 'spacer');
- if (subtitle) {
+ if (item.subtitle) {
var subtitleElement = element.createChild('span', 'suggestion-subtitle');
- subtitleElement.textContent = subtitle.trimEnd(15);
+ subtitleElement.textContent = item.subtitle.trimEnd(15);
}
- element.__fullValue = title;
element.addEventListener('mousedown', this._onItemMouseDown.bind(this), false);
return element;
}
/**
- * @param {!UI.SuggestBox.Suggestions} items
- * @param {string} userEnteredText
- * @param {function(number): !Promise<{detail:string, description:string}>=} asyncDetails
+ * @override
+ * @param {!UI.SuggestBox.Suggestion} item
+ * @return {number}
+ */
+ heightForItem(item) {
+ return this._rowHeight;
+ }
+
+ /**
+ * @override
+ * @param {!UI.SuggestBox.Suggestion} item
+ * @return {boolean}
*/
- _updateItems(items, userEnteredText, asyncDetails) {
- this._length = items.length;
- this._asyncDetailsPromises.clear();
- this._asyncDetailsCallback = asyncDetails;
- this._elementList = [];
- delete this._selectedElement;
-
- this._userEnteredText = userEnteredText;
- this._items = items;
+ isItemSelectable(item) {
+ return true;
}
/**
- * @param {number} index
+ * @override
+ * @param {?UI.SuggestBox.Suggestion} from
+ * @param {?UI.SuggestBox.Suggestion} to
+ * @param {?Element} fromElement
+ * @param {?Element} toElement
+ */
+ selectedItemChanged(from, to, fromElement, toElement) {
+ if (fromElement)
+ fromElement.classList.remove('selected', 'force-white-icons');
+ if (toElement)
+ toElement.classList.add('selected', 'force-white-icons');
+ if (!to)
+ return;
+ this._detailsPopup.classList.add('hidden');
+ this._asyncDetails(to).then(details => {
+ if (this._list.selectedItem() === to)
+ this._showDetailsPopup(details);
+ });
+ this._applySuggestion(true);
+ }
+
+ /**
+ * @param {!Event} event
+ */
+ _onItemMouseDown(event) {
+ if (!this._list.onClick(event))
+ return;
+ this._userInteracted = true;
+ this.acceptSuggestion();
+ event.consume(true);
+ }
+
+ /**
+ * @param {!UI.SuggestBox.Suggestion} item
* @return {!Promise<?{detail: string, description: string}>}
*/
- _asyncDetails(index) {
+ _asyncDetails(item) {
if (!this._asyncDetailsCallback)
return Promise.resolve(/** @type {?{description: string, detail: string}} */ (null));
- if (!this._asyncDetailsPromises.has(index))
- this._asyncDetailsPromises.set(index, this._asyncDetailsCallback(index));
- return /** @type {!Promise<?{detail: string, description: string}>} */ (this._asyncDetailsPromises.get(index));
+ if (!this._asyncDetailsPromises.has(item))
+ this._asyncDetailsPromises.set(item, this._asyncDetailsCallback(item));
+ return /** @type {!Promise<?{detail: string, description: string}>} */ (this._asyncDetailsPromises.get(item));
}
/**
@@ -346,39 +339,6 @@ UI.SuggestBox = class {
}
/**
- * @param {number} index
- */
- _selectItem(index) {
- if (this._selectedElement) {
- this._selectedElement.classList.remove('selected');
- this._selectedElement.classList.remove('force-white-icons');
- }
-
- this._selectedIndex = index;
- if (index < 0)
- return;
-
- this._selectedElement = this.itemElement(index);
- this._selectedElement.classList.add('selected');
- this._selectedElement.classList.add('force-white-icons');
- this._detailsPopup.classList.add('hidden');
- var elem = this._selectedElement;
- this._asyncDetails(index).then(showDetails.bind(this), function() {});
-
- this._viewport.scrollItemIntoView(index);
- this._applySuggestion(true);
-
- /**
- * @param {?{detail: string, description: string}} details
- * @this {UI.SuggestBox}
- */
- function showDetails(details) {
- if (elem === this._selectedElement)
- this._showDetailsPopup(details);
- }
- }
-
- /**
* @param {!UI.SuggestBox.Suggestions} completions
* @param {boolean} canShowForSingleItem
* @param {string} userEnteredText
@@ -398,15 +358,6 @@ UI.SuggestBox = class {
return canShowForSingleItem && completions[0].title !== userEnteredText;
}
- _ensureRowCountPerViewport() {
- if (this._rowCountPerViewport)
- return;
- if (!this._items.length)
- return;
-
- this._rowCountPerViewport = Math.floor(this._element.getBoundingClientRect().height / this._rowHeight);
- }
-
/**
* @param {!AnchorBox} anchorBox
* @param {!UI.SuggestBox.Suggestions} completions
@@ -424,11 +375,20 @@ UI.SuggestBox = class {
asyncDetails) {
delete this._onlyCompletion;
if (this._canShowBox(completions, canShowForSingleItem, userEnteredText)) {
- this._updateItems(completions, userEnteredText, asyncDetails);
+ this._asyncDetailsPromises.clear();
+ if (asyncDetails)
+ this._asyncDetailsCallback = item => asyncDetails(completions.indexOf(item));
+ else
+ this._asyncDetailsCallback = null;
+ this._userEnteredText = userEnteredText;
+
this._show();
- this._updateBoxPosition(anchorBox);
- this._updateWidth();
- this._viewport.refresh();
+ this._updateBoxPosition(anchorBox, completions.length);
+ this._updateWidth(completions);
+
+ this._list.setHeightMode(UI.ListHeightMode.Fixed);
+ this._list.replaceAllItems(completions);
+
var highestPriorityItem = -1;
if (selectHighestPriority) {
var highestPriority = -Infinity;
@@ -440,8 +400,7 @@ UI.SuggestBox = class {
}
}
}
- this._selectItem(highestPriorityItem);
- delete this._rowCountPerViewport;
+ this._list.selectItemAtIndex(highestPriorityItem, true);
} else {
if (completions.length === 1) {
this._onlyCompletion = completions[0].title;
@@ -456,100 +415,38 @@ UI.SuggestBox = class {
* @return {boolean}
*/
keyPressed(event) {
- switch (event.key) {
- case 'ArrowUp':
- return this.upKeyPressed();
- case 'ArrowDown':
- return this.downKeyPressed();
- case 'PageUp':
- return this.pageUpKeyPressed();
- case 'PageDown':
- return this.pageDownKeyPressed();
- case 'Enter':
- return this.enterKeyPressed();
+ if (this._list.onKeyDown(event)) {
+ this._userInteracted = true;
+ return true;
}
+ if (event.key === 'Enter')
+ return this.enterKeyPressed();
return false;
}
/**
* @return {boolean}
*/
- upKeyPressed() {
- return this._selectClosest(-1, true);
- }
-
- /**
- * @return {boolean}
- */
- downKeyPressed() {
- return this._selectClosest(1, true);
- }
-
- /**
- * @return {boolean}
- */
- pageUpKeyPressed() {
- this._ensureRowCountPerViewport();
- return this._selectClosest(-this._rowCountPerViewport, false);
- }
-
- /**
- * @return {boolean}
- */
- pageDownKeyPressed() {
- this._ensureRowCountPerViewport();
- return this._selectClosest(this._rowCountPerViewport, false);
- }
-
- /**
- * @return {boolean}
- */
enterKeyPressed() {
if (!this._userInteracted && this._captureEnter)
return false;
- var hasSelectedItem = !!this._selectedElement || this._onlyCompletion;
+ var hasSelectedItem = !!this._list.selectedItem() || this._onlyCompletion;
this.acceptSuggestion();
// Report the event as non-handled if there is no selected item,
// to commit the input or handle it otherwise.
return hasSelectedItem;
}
-
- /**
- * @override
- * @param {number} index
- * @return {number}
- */
- fastItemHeight(index) {
- return this._rowHeight;
- }
-
- /**
- * @override
- * @return {number}
- */
- itemCount() {
- return this._items.length;
- }
-
- /**
- * @override
- * @param {number} index
- * @return {?Element}
- */
- itemElement(index) {
- if (!this._elementList[index]) {
- this._elementList[index] = this._createItemElement(
- this._userEnteredText, this._items[index].title, this._items[index].subtitle, this._items[index].iconType,
- this._items[index].isSecondary);
- }
- return this._elementList[index];
- }
};
/**
- * @typedef {!Array.<{title: string, subtitle: (string|undefined), iconType: (string|undefined), priority: (number|undefined), isSecondary: (boolean|undefined)}>}
+ * @typedef {!{title: string, subtitle: (string|undefined), iconType: (string|undefined), priority: (number|undefined), isSecondary: (boolean|undefined)}}
+ */
+UI.SuggestBox.Suggestion;
+
+/**
+ * @typedef {!Array<!UI.SuggestBox.Suggestion>}
*/
UI.SuggestBox.Suggestions;

Powered by Google App Engine
This is Rietveld 408576698