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.Delegate} delegate | 12 * @param {?QuickOpen.FilteredListWidget.Delegate} delegate |
13 * @param {!Array<string>=} promptHistory | 13 * @param {!Array<string>=} promptHistory |
14 */ | 14 */ |
15 constructor(delegate, promptHistory) { | 15 constructor(delegate, promptHistory) { |
16 super(true); | 16 super(true); |
17 this._promptHistory = promptHistory || []; | 17 this._promptHistory = promptHistory || []; |
18 this._renderAsTwoRows = delegate.renderAsTwoRows(); | |
19 | 18 |
20 this.contentElement.classList.add('filtered-list-widget'); | 19 this.contentElement.classList.add('filtered-list-widget'); |
21 this.contentElement.addEventListener('keydown', this._onKeyDown.bind(this), true); | 20 this.contentElement.addEventListener('keydown', this._onKeyDown.bind(this), true); |
22 this.registerRequiredCSS('quick_open/filteredListWidget.css'); | 21 this.registerRequiredCSS('quick_open/filteredListWidget.css'); |
23 | 22 |
24 this._promptElement = this.contentElement.createChild('div', 'filtered-list- widget-input'); | 23 this._promptElement = this.contentElement.createChild('div', 'filtered-list- widget-input'); |
25 this._promptElement.setAttribute('spellcheck', 'false'); | 24 this._promptElement.setAttribute('spellcheck', 'false'); |
26 this._promptElement.setAttribute('contenteditable', 'plaintext-only'); | 25 this._promptElement.setAttribute('contenteditable', 'plaintext-only'); |
27 this._prompt = new UI.TextPrompt(); | 26 this._prompt = new UI.TextPrompt(); |
28 this._prompt.initialize(() => Promise.resolve([])); | 27 this._prompt.initialize(() => Promise.resolve([])); |
(...skipping 10 matching lines...) Expand all Loading... | |
39 this._list = new UI.ListControl(this, UI.ListMode.EqualHeightItems); | 38 this._list = new UI.ListControl(this, UI.ListMode.EqualHeightItems); |
40 this._itemElementsContainer = this._list.element; | 39 this._itemElementsContainer = this._list.element; |
41 this._itemElementsContainer.classList.add('container'); | 40 this._itemElementsContainer.classList.add('container'); |
42 this._bottomElementsContainer.appendChild(this._itemElementsContainer); | 41 this._bottomElementsContainer.appendChild(this._itemElementsContainer); |
43 | 42 |
44 this._notFoundElement = this._bottomElementsContainer.createChild('div', 'no t-found-text'); | 43 this._notFoundElement = this._bottomElementsContainer.createChild('div', 'no t-found-text'); |
45 this._notFoundElement.classList.add('hidden'); | 44 this._notFoundElement.classList.add('hidden'); |
46 | 45 |
47 this.setDefaultFocusedElement(this._promptElement); | 46 this.setDefaultFocusedElement(this._promptElement); |
48 | 47 |
49 this._delegate = delegate; | 48 this._prefix = ''; |
50 this._delegate.setRefreshCallback(this._itemsLoaded.bind(this)); | 49 this._delegate = null; |
pfeldman
2017/03/06 18:37:18
Could you extract swappable delegate-related chang
einbinder
2017/03/08 22:39:56
Done.
| |
50 this.setDelegate(delegate); | |
51 this._itemsLoaded(); | 51 this._itemsLoaded(); |
52 this._updateShowMatchingItems(); | |
53 } | 52 } |
54 | 53 |
55 /** | 54 /** |
56 * @param {string} query | 55 * @param {string} query |
57 * @return {!RegExp} | 56 * @return {!RegExp} |
58 */ | 57 */ |
59 static filterRegex(query) { | 58 static filterRegex(query) { |
60 const toEscape = String.regexSpecialCharacters(); | 59 const toEscape = String.regexSpecialCharacters(); |
61 var regexString = ''; | 60 var regexString = ''; |
62 for (var i = 0; i < query.length; ++i) { | 61 for (var i = 0; i < query.length; ++i) { |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
111 return false; | 110 return false; |
112 } | 111 } |
113 | 112 |
114 showAsDialog() { | 113 showAsDialog() { |
115 this._dialog = new UI.Dialog(); | 114 this._dialog = new UI.Dialog(); |
116 this._dialog.setMaxContentSize(new UI.Size(504, 340)); | 115 this._dialog.setMaxContentSize(new UI.Size(504, 340)); |
117 this._dialog.setSizeBehavior(UI.GlassPane.SizeBehavior.SetMaxHeight); | 116 this._dialog.setSizeBehavior(UI.GlassPane.SizeBehavior.SetMaxHeight); |
118 this._dialog.setContentPosition(null, 22); | 117 this._dialog.setContentPosition(null, 22); |
119 this.show(this._dialog.contentElement); | 118 this.show(this._dialog.contentElement); |
120 this._dialog.show(); | 119 this._dialog.show(); |
121 this._updateShowMatchingItems(); | |
122 } | 120 } |
123 | 121 |
124 /** | 122 /** |
123 * @param {?QuickOpen.FilteredListWidget.Delegate} delegate | |
124 */ | |
125 setDelegate(delegate) { | |
126 if (this._delegate === delegate) | |
127 return; | |
128 this._delegate = delegate; | |
129 if (!delegate) | |
130 return; | |
131 this._delegate.setRefreshCallback(this._itemsLoaded.bind(this)); | |
132 this._delegate.queryChanged(this._cleanValue()); | |
133 this._itemsLoaded(); | |
134 } | |
135 | |
136 /** | |
137 * @param {string} prefix | |
138 */ | |
139 setPrefix(prefix) { | |
140 this._prefix = prefix; | |
141 } | |
142 | |
143 /** | |
125 * @return {string} | 144 * @return {string} |
126 */ | 145 */ |
127 _value() { | 146 _value() { |
128 return this._prompt.text().trim(); | 147 return this._prompt.text().trim(); |
129 } | 148 } |
130 | 149 |
150 _cleanValue() { | |
151 return this._value().substring(this._prefix.length); | |
152 } | |
153 | |
131 /** | 154 /** |
132 * @override | 155 * @override |
133 */ | 156 */ |
134 wasShown() { | 157 wasShown() { |
135 this._list.invalidateItemHeight(); | 158 this._list.invalidateItemHeight(); |
136 } | 159 } |
137 | 160 |
138 /** | 161 /** |
139 * @override | 162 * @override |
140 */ | 163 */ |
141 willHide() { | 164 willHide() { |
142 this._delegate.dispose(); | 165 this.emit(new QuickOpen.FilteredListWidget.DisposeEvent()); |
166 if (this._delegate) | |
167 this._delegate.dispose(); | |
143 if (this._filterTimer) | 168 if (this._filterTimer) |
144 clearTimeout(this._filterTimer); | 169 clearTimeout(this._filterTimer); |
145 } | 170 } |
146 | 171 |
147 /** | 172 /** |
148 * @param {!Event} event | 173 * @param {!Event} event |
149 */ | 174 */ |
150 _onEnter(event) { | 175 _onEnter(event) { |
151 event.preventDefault(); | 176 event.preventDefault(); |
152 if (!this._delegate.itemCount()) | 177 var selectedIndexInDelegate = this._delegate.itemCount() ? this._list.select edItem() : null; |
153 return; | |
154 var selectedIndexInDelegate = this._shouldShowMatchingItems() ? this._list.s electedItem() : null; | |
155 | 178 |
156 // Detach dialog before allowing delegate to override focus. | 179 // Detach dialog before allowing delegate to override focus. |
157 if (this._dialog) | 180 if (this._dialog) |
158 this._dialog.hide(); | 181 this._dialog.hide(); |
159 this._selectItemWithQuery(selectedIndexInDelegate, this._value()); | 182 this._selectItem(selectedIndexInDelegate); |
160 } | 183 } |
161 | 184 |
162 _itemsLoaded() { | 185 _itemsLoaded() { |
163 if (this._loadTimeout) | 186 if (this._loadTimeout) |
164 return; | 187 return; |
165 this._loadTimeout = setTimeout(this._updateAfterItemsLoaded.bind(this), 0); | 188 this._loadTimeout = setTimeout(this._updateAfterItemsLoaded.bind(this), 0); |
166 } | 189 } |
167 | 190 |
168 _updateAfterItemsLoaded() { | 191 _updateAfterItemsLoaded() { |
169 delete this._loadTimeout; | 192 delete this._loadTimeout; |
170 this._filterItems(); | 193 this._filterItems(); |
171 } | 194 } |
172 | 195 |
173 /** | 196 /** |
174 * @override | 197 * @override |
175 * @param {number} item | 198 * @param {number} item |
176 * @return {!Element} | 199 * @return {!Element} |
177 */ | 200 */ |
178 createElementForItem(item) { | 201 createElementForItem(item) { |
179 var itemElement = createElement('div'); | 202 var itemElement = createElement('div'); |
180 itemElement.className = 'filtered-list-widget-item ' + (this._renderAsTwoRow s ? 'two-rows' : 'one-row'); | 203 itemElement.className = 'filtered-list-widget-item ' + (this._delegate.rende rAsTwoRows() ? 'two-rows' : 'one-row'); |
181 var titleElement = itemElement.createChild('div', 'filtered-list-widget-titl e'); | 204 var titleElement = itemElement.createChild('div', 'filtered-list-widget-titl e'); |
182 var subtitleElement = itemElement.createChild('div', 'filtered-list-widget-s ubtitle'); | 205 var subtitleElement = itemElement.createChild('div', 'filtered-list-widget-s ubtitle'); |
183 subtitleElement.textContent = '\u200B'; | 206 subtitleElement.textContent = '\u200B'; |
184 this._delegate.renderItem(item, this._value(), titleElement, subtitleElement ); | 207 this._delegate.renderItem(item, this._cleanValue(), titleElement, subtitleEl ement); |
185 itemElement.addEventListener('click', event => { | 208 itemElement.addEventListener('click', event => { |
186 event.consume(true); | 209 event.consume(true); |
187 // Detach dialog before allowing delegate to override focus. | 210 // Detach dialog before allowing delegate to override focus. |
188 if (this._dialog) | 211 if (this._dialog) |
189 this._dialog.hide(); | 212 this._dialog.hide(); |
190 this._selectItemWithQuery(item, this._value()); | 213 this._selectItem(item); |
191 }, false); | 214 }, false); |
192 return itemElement; | 215 return itemElement; |
193 } | 216 } |
194 | 217 |
195 /** | 218 /** |
196 * @override | 219 * @override |
197 * @param {number} item | 220 * @param {number} item |
198 * @return {number} | 221 * @return {number} |
199 */ | 222 */ |
200 heightForItem(item) { | 223 heightForItem(item) { |
(...skipping 22 matching lines...) Expand all Loading... | |
223 fromElement.classList.remove('selected'); | 246 fromElement.classList.remove('selected'); |
224 if (toElement) | 247 if (toElement) |
225 toElement.classList.add('selected'); | 248 toElement.classList.add('selected'); |
226 } | 249 } |
227 | 250 |
228 /** | 251 /** |
229 * @param {string} query | 252 * @param {string} query |
230 */ | 253 */ |
231 setQuery(query) { | 254 setQuery(query) { |
232 this._prompt.setText(query); | 255 this._prompt.setText(query); |
256 this._queryChanged(); | |
233 this._prompt.autoCompleteSoon(true); | 257 this._prompt.autoCompleteSoon(true); |
234 this._scheduleFilter(); | 258 this._scheduleFilter(); |
235 this._updateShowMatchingItems(); | |
236 } | 259 } |
237 | 260 |
238 _tabKeyPressed() { | 261 _tabKeyPressed() { |
239 var userEnteredText = this._prompt.text(); | 262 var userEnteredText = this._prompt.text(); |
240 var completion; | 263 var completion; |
241 for (var i = this._promptHistory.length - 1; i >= 0; i--) { | 264 for (var i = this._promptHistory.length - 1; i >= 0; i--) { |
242 if (this._promptHistory[i] !== userEnteredText && this._promptHistory[i].s tartsWith(userEnteredText)) { | 265 if (this._promptHistory[i] !== userEnteredText && this._promptHistory[i].s tartsWith(userEnteredText)) { |
243 completion = this._promptHistory[i]; | 266 completion = this._promptHistory[i]; |
244 break; | 267 break; |
245 } | 268 } |
(...skipping 16 matching lines...) Expand all Loading... | |
262 delete this._scoringTimer; | 285 delete this._scoringTimer; |
263 | 286 |
264 if (this._refreshListWithCurrentResult) | 287 if (this._refreshListWithCurrentResult) |
265 this._refreshListWithCurrentResult(); | 288 this._refreshListWithCurrentResult(); |
266 } | 289 } |
267 | 290 |
268 this._progressBarElement.style.transform = 'scaleX(0)'; | 291 this._progressBarElement.style.transform = 'scaleX(0)'; |
269 this._progressBarElement.classList.remove('filtered-widget-progress-fade'); | 292 this._progressBarElement.classList.remove('filtered-widget-progress-fade'); |
270 this._progressBarElement.classList.remove('hidden'); | 293 this._progressBarElement.classList.remove('hidden'); |
271 | 294 |
272 var query = this._delegate.rewriteQuery(this._value()); | 295 var query = this._delegate.rewriteQuery(this._cleanValue()); |
273 this._query = query; | 296 this._query = query; |
274 | 297 |
275 var filterRegex = query ? QuickOpen.FilteredListWidget.filterRegex(query) : null; | 298 var filterRegex = query ? QuickOpen.FilteredListWidget.filterRegex(query) : null; |
276 | 299 |
277 var filteredItems = []; | 300 var filteredItems = []; |
278 | 301 |
279 var bestScores = []; | 302 var bestScores = []; |
280 var bestItems = []; | 303 var bestItems = []; |
281 var bestItemsToCollect = 100; | 304 var bestItemsToCollect = 100; |
282 var minBestScore = 0; | 305 var minBestScore = 0; |
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
368 this._itemsFilteredForTest(); | 391 this._itemsFilteredForTest(); |
369 } | 392 } |
370 | 393 |
371 /** | 394 /** |
372 * @param {boolean} hasItems | 395 * @param {boolean} hasItems |
373 */ | 396 */ |
374 _updateNotFoundMessage(hasItems) { | 397 _updateNotFoundMessage(hasItems) { |
375 this._list.element.classList.toggle('hidden', !hasItems); | 398 this._list.element.classList.toggle('hidden', !hasItems); |
376 this._notFoundElement.classList.toggle('hidden', hasItems); | 399 this._notFoundElement.classList.toggle('hidden', hasItems); |
377 if (!hasItems) | 400 if (!hasItems) |
378 this._notFoundElement.textContent = this._delegate.notFoundText(); | 401 this._notFoundElement.textContent = this._delegate.notFoundText(this._clea nValue()); |
402 } | |
403 | |
404 _onInput() { | |
405 this._queryChanged(); | |
406 this._scheduleFilter(); | |
407 } | |
408 | |
409 _queryChanged() { | |
410 this.emit(new QuickOpen.FilteredListWidget.QueryChangedEvent(this._value())) ; | |
411 this._delegate.queryChanged(this._cleanValue()); | |
379 } | 412 } |
380 | 413 |
381 /** | 414 /** |
382 * @return {boolean} | |
383 */ | |
384 _shouldShowMatchingItems() { | |
385 return this._delegate.shouldShowMatchingItems(this._value()); | |
386 } | |
387 | |
388 _onInput() { | |
389 this._updateShowMatchingItems(); | |
390 this._scheduleFilter(); | |
391 } | |
392 | |
393 _updateShowMatchingItems() { | |
394 var shouldShowMatchingItems = this._shouldShowMatchingItems(); | |
395 this._bottomElementsContainer.classList.toggle('hidden', !shouldShowMatching Items); | |
396 } | |
397 | |
398 /** | |
399 * @param {!Event} event | 415 * @param {!Event} event |
400 */ | 416 */ |
401 _onKeyDown(event) { | 417 _onKeyDown(event) { |
402 var handled = false; | 418 var handled = false; |
403 switch (event.key) { | 419 switch (event.key) { |
404 case 'Enter': | 420 case 'Enter': |
405 this._onEnter(event); | 421 this._onEnter(event); |
406 return; | 422 return; |
407 case 'Tab': | 423 case 'Tab': |
408 this._tabKeyPressed(); | 424 this._tabKeyPressed(); |
(...skipping 16 matching lines...) Expand all Loading... | |
425 } | 441 } |
426 | 442 |
427 _scheduleFilter() { | 443 _scheduleFilter() { |
428 if (this._filterTimer) | 444 if (this._filterTimer) |
429 return; | 445 return; |
430 this._filterTimer = setTimeout(this._filterItems.bind(this), 0); | 446 this._filterTimer = setTimeout(this._filterItems.bind(this), 0); |
431 } | 447 } |
432 | 448 |
433 /** | 449 /** |
434 * @param {?number} itemIndex | 450 * @param {?number} itemIndex |
435 * @param {string} promptValue | |
436 */ | 451 */ |
437 _selectItemWithQuery(itemIndex, promptValue) { | 452 _selectItem(itemIndex) { |
einbinder
2017/03/03 23:07:14
This was always called with promptValue = this._va
| |
438 this._promptHistory.push(promptValue); | 453 this._promptHistory.push(this._value()); |
439 if (this._promptHistory.length > 100) | 454 if (this._promptHistory.length > 100) |
440 this._promptHistory.shift(); | 455 this._promptHistory.shift(); |
441 this._delegate.selectItem(itemIndex, promptValue); | 456 this._delegate.selectItem(itemIndex, this._cleanValue()); |
442 } | 457 } |
443 }; | 458 }; |
444 | 459 |
445 | 460 |
446 /** | 461 /** |
447 * @unrestricted | 462 * @unrestricted |
448 */ | 463 */ |
449 QuickOpen.FilteredListWidget.Delegate = class { | 464 QuickOpen.FilteredListWidget.Delegate = class { |
450 /** | 465 /** |
451 * @param {function():void} refreshCallback | 466 * @param {function():void} refreshCallback |
452 */ | 467 */ |
453 setRefreshCallback(refreshCallback) { | 468 setRefreshCallback(refreshCallback) { |
454 this._refreshCallback = refreshCallback; | 469 this._refreshCallback = refreshCallback; |
455 } | 470 } |
456 | 471 |
457 /** | 472 /** |
458 * @param {string} query | |
459 * @return {boolean} | |
460 */ | |
461 shouldShowMatchingItems(query) { | |
462 return true; | |
463 } | |
464 | |
465 /** | |
466 * @return {number} | 473 * @return {number} |
467 */ | 474 */ |
468 itemCount() { | 475 itemCount() { |
469 return 0; | 476 return 0; |
470 } | 477 } |
471 | 478 |
472 /** | 479 /** |
473 * @param {number} itemIndex | 480 * @param {number} itemIndex |
474 * @return {string} | 481 * @return {string} |
475 */ | 482 */ |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
515 | 522 |
516 /** | 523 /** |
517 * @param {string} query | 524 * @param {string} query |
518 * @return {string} | 525 * @return {string} |
519 */ | 526 */ |
520 rewriteQuery(query) { | 527 rewriteQuery(query) { |
521 return query; | 528 return query; |
522 } | 529 } |
523 | 530 |
524 /** | 531 /** |
532 * @param {string} query | |
533 */ | |
534 queryChanged(query) { | |
535 } | |
536 | |
537 /** | |
538 * @param {string} query | |
525 * @return {string} | 539 * @return {string} |
526 */ | 540 */ |
527 notFoundText() { | 541 notFoundText(query) { |
528 return Common.UIString('No results found'); | 542 return Common.UIString('No results found'); |
529 } | 543 } |
530 | 544 |
531 dispose() { | 545 dispose() { |
532 } | 546 } |
533 }; | 547 }; |
548 | |
549 /** @implements {Common.Emittable} */ | |
550 QuickOpen.FilteredListWidget.QueryChangedEvent = class { | |
551 /** | |
552 * @param {string} query | |
553 */ | |
554 constructor(query) { | |
555 this.query = query; | |
556 } | |
557 }; | |
558 | |
559 /** @implements {Common.Emittable} */ | |
560 QuickOpen.FilteredListWidget.DisposeEvent = class {}; | |
OLD | NEW |