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

Side by Side Diff: third_party/WebKit/Source/devtools/front_end/ui/ListControl.js

Issue 2604013003: [DevTools] Support variable height in ListControl. (Closed)
Patch Set: Created 3 years, 11 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
« no previous file with comments | « third_party/WebKit/Source/devtools/front_end/platform/utilities.js ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 /** 5 /**
6 * @template T 6 * @template T
7 * @interface 7 * @interface
8 */ 8 */
9 UI.ListDelegate = function() {}; 9 UI.ListDelegate = function() {};
10 10
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
52 */ 52 */
53 constructor(delegate) { 53 constructor(delegate) {
54 this.element = createElement('div'); 54 this.element = createElement('div');
55 this.element.style.overflow = 'auto'; 55 this.element.style.overflow = 'auto';
56 this._topElement = this.element.createChild('div'); 56 this._topElement = this.element.createChild('div');
57 this._bottomElement = this.element.createChild('div'); 57 this._bottomElement = this.element.createChild('div');
58 this._firstIndex = 0; 58 this._firstIndex = 0;
59 this._lastIndex = 0; 59 this._lastIndex = 0;
60 this._renderedHeight = 0; 60 this._renderedHeight = 0;
61 this._topHeight = 0; 61 this._topHeight = 0;
62 this._topElement.style.height = '0';
63 this._bottomHeight = 0; 62 this._bottomHeight = 0;
64 this._bottomElement.style.height = '0'; 63 this._clearViewport();
65 64
66 /** @type {!Array<T>} */ 65 /** @type {!Array<T>} */
67 this._items = []; 66 this._items = [];
68 /** @type {!Map<T, !Element>} */ 67 /** @type {!Map<T, !Element>} */
69 this._itemToElement = new Map(); 68 this._itemToElement = new Map();
70 this._selectedIndex = -1; 69 this._selectedIndex = -1;
71 70
72 this._boundKeyDown = event => { 71 this._boundKeyDown = event => {
73 if (this.onKeyDown(event)) 72 if (this.onKeyDown(event))
74 event.consume(true); 73 event.consume(true);
75 }; 74 };
76 this._boundClick = event => { 75 this._boundClick = event => {
77 if (this.onClick(event)) 76 if (this.onClick(event))
78 event.consume(true); 77 event.consume(true);
79 }; 78 };
80 79
81 this._delegate = delegate; 80 this._delegate = delegate;
82 this._heightMode = UI.ListHeightMode.Measured; 81 this._heightMode = UI.ListHeightMode.Measured;
83 this._fixedHeight = 0; 82 this._fixedHeight = 0;
83 this._variableOffsets = new Int32Array(0);
84 84
85 this.element.addEventListener('scroll', this._onScroll.bind(this), false); 85 this.element.addEventListener('scroll', this._onScroll.bind(this), false);
86 } 86 }
87 87
88 /** 88 /**
89 * @param {!UI.ListHeightMode} mode 89 * @param {!UI.ListHeightMode} mode
90 */ 90 */
91 setHeightMode(mode) { 91 setHeightMode(mode) {
92 if (mode === UI.ListHeightMode.Variable)
93 throw 'Variable height is not supported (yet)';
94 this._heightMode = mode; 92 this._heightMode = mode;
95 this._fixedHeight = 0; 93 this._fixedHeight = 0;
96 if (this._items.length) { 94 if (this._items.length) {
97 this._itemToElement.clear(); 95 this._itemToElement.clear();
98 this._refresh(); 96 this._invalidate(0, this._items.length, this._items.length);
99 } 97 }
100 } 98 }
101 99
102 /** 100 /**
103 * @param {boolean} handleInput 101 * @param {boolean} handleInput
104 */ 102 */
105 setHandleInput(handleInput) { 103 setHandleInput(handleInput) {
106 if (handleInput) { 104 if (handleInput) {
107 this.element.addEventListener('keydown', this._boundKeyDown, false); 105 this.element.addEventListener('keydown', this._boundKeyDown, false);
108 this.element.addEventListener('click', this._boundClick, false); 106 this.element.addEventListener('click', this._boundClick, false);
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after
199 197
200 /** 198 /**
201 * @param {number} from 199 * @param {number} from
202 * @param {number} to 200 * @param {number} to
203 */ 201 */
204 invalidateRange(from, to) { 202 invalidateRange(from, to) {
205 this._invalidate(from, to, to - from); 203 this._invalidate(from, to, to - from);
206 } 204 }
207 205
208 viewportResized() { 206 viewportResized() {
209 this._refresh(); 207 // TODO(dgozman): try to keep the visible scrollTop the same
208 // when invalidating after firstIndex but before first visible element.
209 var scrollTop = this.element.scrollTop;
210 var viewportHeight = this.element.offsetHeight;
211 this._clearViewport();
212 this._updateViewport(Number.constrain(scrollTop, 0, this._totalHeight() - vi ewportHeight), viewportHeight);
210 } 213 }
211 214
212 /** 215 /**
213 * @param {number} index 216 * @param {number} index
214 */ 217 */
215 scrollItemAtIndexIntoView(index) { 218 scrollItemAtIndexIntoView(index) {
216 var top = this._offsetAtIndex(index); 219 var top = this._offsetAtIndex(index);
217 var bottom = this._offsetAtIndex(index + 1); 220 var bottom = this._offsetAtIndex(index + 1);
218 var scrollTop = this.element.scrollTop; 221 var scrollTop = this.element.scrollTop;
219 var height = this.element.offsetHeight; 222 var viewportHeight = this.element.offsetHeight;
220 if (top < scrollTop) 223 if (top < scrollTop)
221 this._update(top, height); 224 this._updateViewport(top, viewportHeight);
222 else if (bottom > scrollTop + height) 225 else if (bottom > scrollTop + viewportHeight)
223 this._update(bottom - height, height); 226 this._updateViewport(bottom - viewportHeight, viewportHeight);
224 } 227 }
225 228
226 /** 229 /**
227 * @param {number} index 230 * @param {number} index
228 * @param {boolean=} scrollIntoView 231 * @param {boolean=} scrollIntoView
229 */ 232 */
230 selectItemAtIndex(index, scrollIntoView) { 233 selectItemAtIndex(index, scrollIntoView) {
231 if (index !== -1 && !this._delegate.isItemSelectable(this._items[index])) 234 if (index !== -1 && !this._delegate.isItemSelectable(this._items[index]))
232 throw 'Attempt to select non-selectable item'; 235 throw 'Attempt to select non-selectable item';
233 this._select(index); 236 this._select(index);
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
311 return this._offsetAtIndex(this._items.length); 314 return this._offsetAtIndex(this._items.length);
312 } 315 }
313 316
314 /** 317 /**
315 * @param {number} offset 318 * @param {number} offset
316 * @return {number} 319 * @return {number}
317 */ 320 */
318 _indexAtOffset(offset) { 321 _indexAtOffset(offset) {
319 if (!this._items.length || offset < 0) 322 if (!this._items.length || offset < 0)
320 return 0; 323 return 0;
321 if (this._heightMode === UI.ListHeightMode.Variable) 324 if (this._heightMode === UI.ListHeightMode.Variable) {
322 throw 'Variable height is not supported (yet)'; 325 return Math.min(
326 this._items.length - 1, this._variableOffsets.lowerBound(offset, undef ined, 0, this._items.length));
327 }
323 if (!this._fixedHeight) 328 if (!this._fixedHeight)
324 this._measureHeight(); 329 this._measureHeight();
325 return Math.min(this._items.length - 1, Math.floor(offset / this._fixedHeigh t)); 330 return Math.min(this._items.length - 1, Math.floor(offset / this._fixedHeigh t));
326 } 331 }
327 332
328 /** 333 /**
329 * @param {number} index 334 * @param {number} index
330 * @return {!Element} 335 * @return {!Element}
331 */ 336 */
332 _elementAtIndex(index) { 337 _elementAtIndex(index) {
333 var item = this._items[index]; 338 var item = this._items[index];
334 var element = this._itemToElement.get(item); 339 var element = this._itemToElement.get(item);
335 if (!element) { 340 if (!element) {
336 element = this._delegate.createElementForItem(item); 341 element = this._delegate.createElementForItem(item);
337 this._itemToElement.set(item, element); 342 this._itemToElement.set(item, element);
338 } 343 }
339 return element; 344 return element;
340 } 345 }
341 346
342 /** 347 /**
343 * @param {number} index 348 * @param {number} index
344 * @return {number} 349 * @return {number}
345 */ 350 */
346 _offsetAtIndex(index) { 351 _offsetAtIndex(index) {
347 if (!this._items.length) 352 if (!this._items.length)
348 return 0; 353 return 0;
349 if (this._heightMode === UI.ListHeightMode.Variable) 354 if (this._heightMode === UI.ListHeightMode.Variable)
350 throw 'Variable height is not supported (yet)'; 355 return this._variableOffsets[index];
351 if (!this._fixedHeight) 356 if (!this._fixedHeight)
352 this._measureHeight(); 357 this._measureHeight();
353 return index * this._fixedHeight; 358 return index * this._fixedHeight;
354 } 359 }
355 360
356 _measureHeight() { 361 _measureHeight() {
357 if (this._heightMode === UI.ListHeightMode.Measured) 362 if (this._heightMode === UI.ListHeightMode.Measured)
358 this._fixedHeight = UI.measurePreferredSize(this._elementAtIndex(0), this. element).height; 363 this._fixedHeight = UI.measurePreferredSize(this._elementAtIndex(0), this. element).height;
359 else 364 else
360 this._fixedHeight = this._delegate.heightForItem(this._items[0]); 365 this._fixedHeight = this._delegate.heightForItem(this._items[0]);
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
410 if (Math.abs(this._offsetAtIndex(index) - startOffset) >= minSkippedHeig ht) 415 if (Math.abs(this._offsetAtIndex(index) - startOffset) >= minSkippedHeig ht)
411 return index; 416 return index;
412 lastSelectable = index; 417 lastSelectable = index;
413 } 418 }
414 419
415 index += direction; 420 index += direction;
416 } 421 }
417 } 422 }
418 423
419 /** 424 /**
425 * @param {number} length
426 * @param {number} copyTo
427 */
428 _reallocateVariableOffsets(length, copyTo) {
429 if (this._variableOffsets.length < length) {
430 var variableOffsets = new Int32Array(Math.max(length, this._variableOffset s.length * 2));
431 variableOffsets.set(this._variableOffsets.slice(0, copyTo), 0);
432 this._variableOffsets = variableOffsets;
433 } else if (this._variableOffsets.length >= 2 * length) {
434 var variableOffsets = new Int32Array(length);
435 variableOffsets.set(this._variableOffsets.slice(0, copyTo), 0);
436 this._variableOffsets = variableOffsets;
437 }
438 }
439
440 /**
420 * @param {number} from 441 * @param {number} from
421 * @param {number} to 442 * @param {number} to
422 * @param {number} inserted 443 * @param {number} inserted
423 */ 444 */
424 _invalidate(from, to, inserted) { 445 _invalidate(from, to, inserted) {
446 if (this._heightMode === UI.ListHeightMode.Variable) {
447 this._reallocateVariableOffsets(this._items.length + 1, from + 1);
448 for (var i = from + 1; i <= this._items.length; i++)
449 this._variableOffsets[i] = this._variableOffsets[i - 1] + this._delegate .heightForItem(this._items[i - 1]);
450 }
451
425 var viewportHeight = this.element.offsetHeight; 452 var viewportHeight = this.element.offsetHeight;
426 var totalHeight = this._totalHeight(); 453 var totalHeight = this._totalHeight();
454 var scrollTop = this.element.scrollTop;
455
427 if (this._renderedHeight < viewportHeight || totalHeight < viewportHeight) { 456 if (this._renderedHeight < viewportHeight || totalHeight < viewportHeight) {
428 this._refresh(); 457 this._clearViewport();
458 this._updateViewport(Number.constrain(scrollTop, 0, totalHeight - viewport Height), viewportHeight);
429 return; 459 return;
430 } 460 }
431 461
432 var scrollTop = this.element.scrollTop;
433 var heightDelta = totalHeight - this._renderedHeight; 462 var heightDelta = totalHeight - this._renderedHeight;
434 if (to <= this._firstIndex) { 463 if (to <= this._firstIndex) {
435 var topHeight = this._topHeight + heightDelta; 464 var topHeight = this._topHeight + heightDelta;
436 this._topElement.style.height = topHeight + 'px'; 465 this._topElement.style.height = topHeight + 'px';
437 this.element.scrollTop = scrollTop + heightDelta; 466 this.element.scrollTop = scrollTop + heightDelta;
438 this._topHeight = topHeight; 467 this._topHeight = topHeight;
439 this._renderedHeight = totalHeight; 468 this._renderedHeight = totalHeight;
440 var indexDelta = inserted - (to - from); 469 var indexDelta = inserted - (to - from);
441 this._firstIndex += indexDelta; 470 this._firstIndex += indexDelta;
442 this._lastIndex += indexDelta; 471 this._lastIndex += indexDelta;
443 return; 472 return;
444 } 473 }
445 474
446 if (from >= this._lastIndex) { 475 if (from >= this._lastIndex) {
447 var bottomHeight = this._bottomHeight + heightDelta; 476 var bottomHeight = this._bottomHeight + heightDelta;
448 this._bottomElement.style.height = bottomHeight + 'px'; 477 this._bottomElement.style.height = bottomHeight + 'px';
449 this._bottomHeight = bottomHeight; 478 this._bottomHeight = bottomHeight;
450 this._renderedHeight = totalHeight; 479 this._renderedHeight = totalHeight;
451 return; 480 return;
452 } 481 }
453 482
454 // TODO(dgozman): try to keep the visible scrollTop the same 483 // TODO(dgozman): try to keep the visible scrollTop the same
455 // when invalidating after firstIndex but before first visible element. 484 // when invalidating after firstIndex but before first visible element.
456 this._refresh(); 485 this._clearViewport();
486 this._updateViewport(Number.constrain(scrollTop, 0, totalHeight - viewportHe ight), viewportHeight);
457 } 487 }
458 488
459 _refresh() { 489 _clearViewport() {
460 var viewportHeight = this.element.offsetHeight;
461 var scrollTop = Number.constrain(this.element.scrollTop, 0, this._totalHeigh t() - viewportHeight);
462 this._firstIndex = 0; 490 this._firstIndex = 0;
463 this._lastIndex = 0; 491 this._lastIndex = 0;
464 this._renderedHeight = 0; 492 this._renderedHeight = 0;
465 this._topHeight = 0; 493 this._topHeight = 0;
466 this._bottomHeight = 0; 494 this._bottomHeight = 0;
495 this._topElement.style.height = '0';
496 this._bottomElement.style.height = '0';
467 this.element.removeChildren(); 497 this.element.removeChildren();
468 this.element.appendChild(this._topElement); 498 this.element.appendChild(this._topElement);
469 this.element.appendChild(this._bottomElement); 499 this.element.appendChild(this._bottomElement);
470 this._update(scrollTop, viewportHeight);
471 } 500 }
472 501
473 _onScroll() { 502 _onScroll() {
474 this._update(this.element.scrollTop, this.element.offsetHeight); 503 this._updateViewport(this.element.scrollTop, this.element.offsetHeight);
475 } 504 }
476 505
477 /** 506 /**
478 * @param {number} scrollTop 507 * @param {number} scrollTop
479 * @param {number} viewportHeight 508 * @param {number} viewportHeight
480 */ 509 */
481 _update(scrollTop, viewportHeight) { 510 _updateViewport(scrollTop, viewportHeight) {
482 // Note: this method should not force layout. Be careful. 511 // Note: this method should not force layout. Be careful.
483 512
484 var totalHeight = this._totalHeight(); 513 var totalHeight = this._totalHeight();
485 if (!totalHeight) { 514 if (!totalHeight) {
486 this._firstIndex = 0; 515 this._firstIndex = 0;
487 this._lastIndex = 0; 516 this._lastIndex = 0;
488 this._topHeight = 0; 517 this._topHeight = 0;
489 this._bottomHeight = 0; 518 this._bottomHeight = 0;
490 this._renderedHeight = 0; 519 this._renderedHeight = 0;
491 this._topElement.style.height = '0'; 520 this._topElement.style.height = '0';
(...skipping 27 matching lines...) Expand all
519 this._firstIndex = firstIndex; 548 this._firstIndex = firstIndex;
520 this._lastIndex = lastIndex; 549 this._lastIndex = lastIndex;
521 this._topHeight = this._offsetAtIndex(firstIndex); 550 this._topHeight = this._offsetAtIndex(firstIndex);
522 this._topElement.style.height = this._topHeight + 'px'; 551 this._topElement.style.height = this._topHeight + 'px';
523 this._bottomHeight = totalHeight - this._offsetAtIndex(lastIndex); 552 this._bottomHeight = totalHeight - this._offsetAtIndex(lastIndex);
524 this._bottomElement.style.height = this._bottomHeight + 'px'; 553 this._bottomElement.style.height = this._bottomHeight + 'px';
525 this._renderedHeight = totalHeight; 554 this._renderedHeight = totalHeight;
526 this.element.scrollTop = scrollTop; 555 this.element.scrollTop = scrollTop;
527 } 556 }
528 }; 557 };
OLDNEW
« no previous file with comments | « third_party/WebKit/Source/devtools/front_end/platform/utilities.js ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698