OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 * Copyright (c) 2012 The Chromium Authors. All rights reserved. |
3 * Use of this source code is governed by a BSD-style license that can be | 3 * Use of this source code is governed by a BSD-style license that can be |
4 * found in the LICENSE file. | 4 * found in the LICENSE file. |
5 */ | 5 */ |
6 /** | 6 /** |
7 * @unrestricted | 7 * @unrestricted |
8 * @implements {UI.ListDelegate} | 8 * @implements {UI.ListDelegate} |
9 */ | 9 */ |
10 QuickOpen.FilteredListWidget = class extends UI.VBox { | 10 QuickOpen.FilteredListWidget = class extends UI.VBox { |
11 /** | 11 /** |
12 * @param {?QuickOpen.FilteredListWidget.Provider} provider | 12 * @param {?QuickOpen.FilteredListWidget.Provider} provider |
13 * @param {!Array<string>=} promptHistory | 13 * @param {!Array<string>=} promptHistory |
14 * @param {function(string)=} queryChangedCallback | 14 * @param {function(string)=} queryChangedCallback |
15 */ | 15 */ |
16 constructor(provider, promptHistory, queryChangedCallback) { | 16 constructor(provider, promptHistory, queryChangedCallback) { |
17 super(true); | 17 super(true); |
18 this._promptHistory = promptHistory || []; | 18 this._promptHistory = promptHistory || []; |
19 | 19 |
20 this.contentElement.classList.add('filtered-list-widget'); | 20 this.contentElement.classList.add('filtered-list-widget'); |
21 this.contentElement.addEventListener('keydown', this._onKeyDown.bind(this), true); | 21 this.contentElement.addEventListener('keydown', this._onKeyDown.bind(this), true); |
22 this.registerRequiredCSS('quick_open/filteredListWidget.css'); | 22 this.registerRequiredCSS('quick_open/filteredListWidget.css'); |
23 | 23 |
24 this._promptElement = this.contentElement.createChild('div', 'filtered-list- widget-input'); | 24 var promptContainer = this.contentElement.createChild('div', 'filtered-list- widget-input-container'); |
25 this._iconContainer = promptContainer.createChild('div'); | |
26 var container = promptContainer.createChild('div', 'filtered-list-widget-inp ut'); | |
27 this._promptElement = container.createChild('div'); | |
25 this._promptElement.setAttribute('spellcheck', 'false'); | 28 this._promptElement.setAttribute('spellcheck', 'false'); |
26 this._promptElement.setAttribute('contenteditable', 'plaintext-only'); | 29 this._promptElement.setAttribute('contenteditable', 'plaintext-only'); |
27 this._prompt = new UI.TextPrompt(); | 30 this._prompt = new UI.TextPrompt(); |
28 this._prompt.initialize(() => Promise.resolve([])); | 31 this._prompt.initialize(() => Promise.resolve([])); |
29 var promptProxy = this._prompt.attach(this._promptElement); | 32 var promptProxy = this._prompt.attach(this._promptElement); |
30 promptProxy.addEventListener('input', this._onInput.bind(this), false); | 33 promptProxy.addEventListener('input', this._onInput.bind(this), false); |
31 promptProxy.classList.add('filtered-list-widget-prompt-element'); | 34 promptProxy.classList.add('filtered-list-widget-prompt-element'); |
32 | 35 |
33 this._bottomElementsContainer = this.contentElement.createChild('div', 'vbox '); | 36 this._bottomElementsContainer = this.contentElement.createChild('div', 'vbox '); |
34 this._progressElement = this._bottomElementsContainer.createChild('div', 'fi ltered-list-widget-progress'); | 37 this._progressElement = this._bottomElementsContainer.createChild('div', 'fi ltered-list-widget-progress'); |
35 this._progressBarElement = this._progressElement.createChild('div', 'filtere d-list-widget-progress-bar'); | 38 this._progressBarElement = this._progressElement.createChild('div', 'filtere d-list-widget-progress-bar'); |
36 | 39 |
37 /** @type {!UI.ListControl<number>} */ | 40 /** @type {!UI.ListControl<number>} */ |
38 this._list = new UI.ListControl(this, UI.ListMode.EqualHeightItems); | 41 this._list = new UI.ListControl(this, UI.ListMode.EqualHeightItems); |
39 this._itemElementsContainer = this._list.element; | 42 this._itemElementsContainer = this._list.element; |
43 this._itemElementsContainer.addEventListener('mousemove', event => this._upd ateHover(event)); | |
44 this._itemElementsContainer.addEventListener('mouseout', () => this._hover(n ull)); | |
40 this._itemElementsContainer.classList.add('container'); | 45 this._itemElementsContainer.classList.add('container'); |
41 this._bottomElementsContainer.appendChild(this._itemElementsContainer); | 46 this._bottomElementsContainer.appendChild(this._itemElementsContainer); |
42 this._itemElementsContainer.addEventListener('click', this._onClick.bind(thi s), false); | 47 this._itemElementsContainer.addEventListener('click', this._onClick.bind(thi s), false); |
43 | 48 |
44 this._notFoundElement = this._bottomElementsContainer.createChild('div', 'no t-found-text'); | 49 this._notFoundElement = this._bottomElementsContainer.createChild('div', 'no t-found-text'); |
45 this._notFoundElement.classList.add('hidden'); | 50 this._notFoundElement.classList.add('hidden'); |
46 | 51 |
47 this.setDefaultFocusedElement(this._promptElement); | 52 this.setDefaultFocusedElement(this._promptElement); |
48 | 53 |
49 this._prefix = ''; | 54 this._prefix = ''; |
50 this._provider = provider; | 55 this._provider = provider; |
51 this._queryChangedCallback = queryChangedCallback; | 56 this._queryChangedCallback = queryChangedCallback; |
57 | |
58 /** @type {?number} */ | |
59 this._initialSelectedItem = null; | |
60 /** @type {?Element} */ | |
61 this._hoveredListElement = null; | |
52 } | 62 } |
53 | 63 |
54 /** | 64 /** |
55 * @param {string} query | 65 * @param {string} query |
56 * @return {!RegExp} | 66 * @return {!RegExp} |
57 */ | 67 */ |
58 static filterRegex(query) { | 68 static filterRegex(query) { |
59 const toEscape = String.regexSpecialCharacters(); | 69 const toEscape = String.regexSpecialCharacters(); |
60 var regexString = ''; | 70 var regexString = ''; |
61 for (var i = 0; i < query.length; ++i) { | 71 for (var i = 0; i < query.length; ++i) { |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
110 return false; | 120 return false; |
111 } | 121 } |
112 | 122 |
113 /** | 123 /** |
114 * @param {string} placeholder | 124 * @param {string} placeholder |
115 */ | 125 */ |
116 setPlaceholder(placeholder) { | 126 setPlaceholder(placeholder) { |
117 this._prompt.setPlaceholder(placeholder); | 127 this._prompt.setPlaceholder(placeholder); |
118 } | 128 } |
119 | 129 |
120 showAsDialog() { | 130 /** |
131 * @param {!AnchorBox=} anchorBox | |
132 * @param {!UI.GlassPane.AnchorBehavior=} anchorBehavior | |
133 */ | |
134 showAsDialog(anchorBox, anchorBehavior) { | |
121 this._dialog = new UI.Dialog(); | 135 this._dialog = new UI.Dialog(); |
136 if (anchorBox) | |
137 this._dialog.setContentAnchorBox(anchorBox); | |
138 else | |
139 this._dialog.setContentPosition(null, 22); | |
122 this._dialog.setMaxContentSize(new UI.Size(504, 340)); | 140 this._dialog.setMaxContentSize(new UI.Size(504, 340)); |
123 this._dialog.setSizeBehavior(UI.GlassPane.SizeBehavior.SetExactWidthMaxHeigh t); | 141 this._dialog.setSizeBehavior(UI.GlassPane.SizeBehavior.SetExactWidthMaxHeigh t); |
124 this._dialog.setContentPosition(null, 22); | 142 if (anchorBehavior) |
143 this._dialog.setAnchorBehavior(anchorBehavior); | |
125 this.show(this._dialog.contentElement); | 144 this.show(this._dialog.contentElement); |
126 this._dialog.show(); | 145 this._dialog.show(); |
127 } | 146 } |
128 | 147 |
129 /** | 148 /** |
130 * @param {string} prefix | 149 * @param {string} prefix |
131 */ | 150 */ |
132 setPrefix(prefix) { | 151 setPrefix(prefix) { |
133 this._prefix = prefix; | 152 this._prefix = prefix; |
134 } | 153 } |
135 | 154 |
136 /** | 155 /** |
137 * @param {?QuickOpen.FilteredListWidget.Provider} provider | 156 * @param {?QuickOpen.FilteredListWidget.Provider} provider |
138 */ | 157 */ |
139 setProvider(provider) { | 158 setProvider(provider) { |
140 if (provider === this._provider) | 159 if (provider === this._provider) |
141 return; | 160 return; |
142 | 161 |
143 if (this._provider) | 162 if (this._provider) |
144 this._provider.detach(); | 163 this._provider.detach(); |
145 this._clearTimers(); | 164 this._clearTimers(); |
146 | 165 |
147 this._provider = provider; | 166 this._provider = provider; |
148 if (this.isShowing()) | 167 if (this.isShowing()) |
149 this._attachProvider(); | 168 this._attachProvider(); |
150 } | 169 } |
151 | 170 |
171 /** | |
172 * @param {?string} icon | |
173 */ | |
174 setInputIcon(icon) { | |
175 this._iconContainer.removeChildren(); | |
176 if (icon) | |
177 this._iconContainer.appendChild(UI.Icon.create(icon, 'filtered-list-input- icon')); | |
178 } | |
179 | |
152 _attachProvider() { | 180 _attachProvider() { |
153 this._list.replaceAllItems([]); | 181 this._list.replaceAllItems([]); |
154 this._list.invalidateItemHeight(); | 182 this._list.invalidateItemHeight(); |
155 if (this._provider) { | 183 if (this._provider) { |
156 this._provider.setRefreshCallback(this._itemsLoaded.bind(this, this._provi der)); | 184 this._provider.setRefreshCallback(this._itemsLoaded.bind(this, this._provi der)); |
157 this._provider.attach(); | 185 this._provider.attach(); |
158 } | 186 } |
159 this._itemsLoaded(this._provider); | 187 this._itemsLoaded(this._provider); |
160 } | 188 } |
161 | 189 |
162 /** | 190 /** |
163 * @return {string} | 191 * @return {string} |
164 */ | 192 */ |
165 _value() { | 193 _value() { |
166 return this._prompt.text().trim(); | 194 return this._prompt.text().trim(); |
167 } | 195 } |
168 | 196 |
169 _cleanValue() { | 197 _cleanValue() { |
170 return this._value().substring(this._prefix.length); | 198 return this._value().substring(this._prefix.length); |
171 } | 199 } |
172 | 200 |
173 /** | 201 /** |
202 * @param {!Event} event | |
203 */ | |
204 _updateHover(event) { | |
205 var item = this._list.itemForNode(/** @type {?Node} */ (event.target)); | |
206 this._hover(item); | |
207 } | |
208 | |
209 /** | |
174 * @override | 210 * @override |
175 */ | 211 */ |
176 wasShown() { | 212 wasShown() { |
177 this._attachProvider(); | 213 this._attachProvider(); |
178 } | 214 } |
179 | 215 |
180 /** | 216 /** |
181 * @override | 217 * @override |
182 */ | 218 */ |
183 willHide() { | 219 willHide() { |
220 this._hover(null); | |
184 if (this._provider) | 221 if (this._provider) |
185 this._provider.detach(); | 222 this._provider.detach(); |
186 this._clearTimers(); | 223 this._clearTimers(); |
187 } | 224 } |
188 | 225 |
189 _clearTimers() { | 226 _clearTimers() { |
190 clearTimeout(this._filterTimer); | 227 clearTimeout(this._filterTimer); |
191 clearTimeout(this._scoringTimer); | 228 clearTimeout(this._scoringTimer); |
192 clearTimeout(this._loadTimeout); | 229 clearTimeout(this._loadTimeout); |
193 delete this._filterTimer; | 230 delete this._filterTimer; |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
263 * @param {?number} from | 300 * @param {?number} from |
264 * @param {?number} to | 301 * @param {?number} to |
265 * @param {?Element} fromElement | 302 * @param {?Element} fromElement |
266 * @param {?Element} toElement | 303 * @param {?Element} toElement |
267 */ | 304 */ |
268 selectedItemChanged(from, to, fromElement, toElement) { | 305 selectedItemChanged(from, to, fromElement, toElement) { |
269 if (fromElement) | 306 if (fromElement) |
270 fromElement.classList.remove('selected'); | 307 fromElement.classList.remove('selected'); |
271 if (toElement) | 308 if (toElement) |
272 toElement.classList.add('selected'); | 309 toElement.classList.add('selected'); |
310 this._hover(to); | |
dgozman
2017/05/03 17:32:58
I don't think it's a good idea to mix up mouse acc
eostroukhov
2017/05/03 22:01:37
In the newer version it is now called "highlight"
| |
273 } | 311 } |
274 | 312 |
275 /** | 313 /** |
314 * @param {number} item | |
315 */ | |
316 setInitialSelection(item) { | |
317 if (this._list.length() > 0) | |
318 this._list.selectItem(item, true); | |
319 else | |
320 this._initialSelectedItem = item; | |
321 } | |
322 | |
323 /** | |
276 * @param {!Event} event | 324 * @param {!Event} event |
277 */ | 325 */ |
278 _onClick(event) { | 326 _onClick(event) { |
279 var item = this._list.itemForNode(/** @type {?Node} */ (event.target)); | 327 var item = this._list.itemForNode(/** @type {?Node} */ (event.target)); |
280 if (item === null) | 328 if (item === null) |
281 return; | 329 return; |
282 | 330 |
283 event.consume(true); | 331 event.consume(true); |
284 this._selectItem(item); | 332 this._selectItem(item); |
285 if (this._dialog) | 333 if (this._dialog) |
(...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
427 * @param {!Array<number>} bestItems | 475 * @param {!Array<number>} bestItems |
428 * @param {!Array<number>} overflowItems | 476 * @param {!Array<number>} overflowItems |
429 * @param {!Array<number>} filteredItems | 477 * @param {!Array<number>} filteredItems |
430 */ | 478 */ |
431 _refreshList(bestItems, overflowItems, filteredItems) { | 479 _refreshList(bestItems, overflowItems, filteredItems) { |
432 delete this._refreshListWithCurrentResult; | 480 delete this._refreshListWithCurrentResult; |
433 filteredItems = [].concat(bestItems, overflowItems, filteredItems); | 481 filteredItems = [].concat(bestItems, overflowItems, filteredItems); |
434 this._updateNotFoundMessage(!!filteredItems.length); | 482 this._updateNotFoundMessage(!!filteredItems.length); |
435 var oldHeight = this._list.element.offsetHeight; | 483 var oldHeight = this._list.element.offsetHeight; |
436 this._list.replaceAllItems(filteredItems); | 484 this._list.replaceAllItems(filteredItems); |
437 if (filteredItems.length) | 485 if (filteredItems.length) { |
438 this._list.selectItem(filteredItems[0]); | 486 var selection = filteredItems[0]; |
487 if (this._initialSelectedItem !== null && filteredItems.includes(this._ini tialSelectedItem)) | |
488 selection = this._initialSelectedItem; | |
489 this._initialSelectedItem = null; | |
490 this._list.selectItem(selection, true); | |
491 } | |
439 if (this._list.element.offsetHeight !== oldHeight) | 492 if (this._list.element.offsetHeight !== oldHeight) |
440 this._list.viewportResized(); | 493 this._list.viewportResized(); |
441 this._itemsFilteredForTest(); | 494 this._itemsFilteredForTest(); |
442 } | 495 } |
443 | 496 |
444 /** | 497 /** |
445 * @param {boolean} hasItems | 498 * @param {boolean} hasItems |
446 */ | 499 */ |
447 _updateNotFoundMessage(hasItems) { | 500 _updateNotFoundMessage(hasItems) { |
448 this._list.element.classList.toggle('hidden', !hasItems); | 501 this._list.element.classList.toggle('hidden', !hasItems); |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
500 | 553 |
501 /** | 554 /** |
502 * @param {?number} itemIndex | 555 * @param {?number} itemIndex |
503 */ | 556 */ |
504 _selectItem(itemIndex) { | 557 _selectItem(itemIndex) { |
505 this._promptHistory.push(this._value()); | 558 this._promptHistory.push(this._value()); |
506 if (this._promptHistory.length > 100) | 559 if (this._promptHistory.length > 100) |
507 this._promptHistory.shift(); | 560 this._promptHistory.shift(); |
508 this._provider.selectItem(itemIndex, this._cleanValue()); | 561 this._provider.selectItem(itemIndex, this._cleanValue()); |
509 } | 562 } |
563 | |
564 /** | |
565 * @param {?number} item | |
566 */ | |
567 _hover(item) { | |
568 var element = item === null ? null : this._list.elementForItem(item); | |
569 if (element === this._hoveredListElement) | |
570 return; | |
571 if (this._hoveredListElement) | |
572 this._hoveredListElement.classList.remove('hovered'); | |
dgozman
2017/05/03 17:32:58
Let's do this with :hover css class, and remove Li
eostroukhov
2017/05/03 22:01:38
I was not sure if it was ok to use pseudoclasses :
| |
573 if (element) | |
574 element.classList.add('hovered'); | |
575 this._hoveredListElement = element; | |
576 if (this._provider) | |
577 this._provider.hoverItem(item); | |
578 } | |
510 }; | 579 }; |
511 | 580 |
512 | 581 |
513 /** | 582 /** |
514 * @unrestricted | 583 * @unrestricted |
515 */ | 584 */ |
516 QuickOpen.FilteredListWidget.Provider = class { | 585 QuickOpen.FilteredListWidget.Provider = class { |
517 /** | 586 /** |
518 * @param {function():void} refreshCallback | 587 * @param {function():void} refreshCallback |
519 */ | 588 */ |
(...skipping 22 matching lines...) Expand all Loading... | |
542 /** | 611 /** |
543 * @param {number} itemIndex | 612 * @param {number} itemIndex |
544 * @param {string} query | 613 * @param {string} query |
545 * @return {number} | 614 * @return {number} |
546 */ | 615 */ |
547 itemScoreAt(itemIndex, query) { | 616 itemScoreAt(itemIndex, query) { |
548 return 1; | 617 return 1; |
549 } | 618 } |
550 | 619 |
551 /** | 620 /** |
621 * @param {?number} itemIndex | |
622 */ | |
623 hoverItem(itemIndex) { | |
624 } | |
625 | |
626 /** | |
552 * @param {number} itemIndex | 627 * @param {number} itemIndex |
553 * @param {string} query | 628 * @param {string} query |
554 * @param {!Element} titleElement | 629 * @param {!Element} titleElement |
555 * @param {!Element} subtitleElement | 630 * @param {!Element} subtitleElement |
556 */ | 631 */ |
557 renderItem(itemIndex, query, titleElement, subtitleElement) { | 632 renderItem(itemIndex, query, titleElement, subtitleElement) { |
558 } | 633 } |
559 | 634 |
560 /** | 635 /** |
561 * @return {boolean} | 636 * @return {boolean} |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
593 * @param {string} query | 668 * @param {string} query |
594 * @return {string} | 669 * @return {string} |
595 */ | 670 */ |
596 notFoundText(query) { | 671 notFoundText(query) { |
597 return Common.UIString('No results found'); | 672 return Common.UIString('No results found'); |
598 } | 673 } |
599 | 674 |
600 detach() { | 675 detach() { |
601 } | 676 } |
602 }; | 677 }; |
OLD | NEW |