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

Side by Side Diff: third_party/WebKit/Source/devtools/front_end/quick_open/FilteredListWidget.js

Issue 2679483002: DevTools: Create extensible QuickOpen control (Closed)
Patch Set: delegates again Created 3 years, 9 months 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 unified diff | Download patch
OLDNEW
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
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
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
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
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
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
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
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 {};
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698