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

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

Issue 2605253002: [DevTools] Add grow mode to ListControl. (Closed)
Patch Set: addressed review comments 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
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 19 matching lines...) Expand all
30 /** 30 /**
31 * @param {?T} from 31 * @param {?T} from
32 * @param {?T} to 32 * @param {?T} to
33 * @param {?Element} fromElement 33 * @param {?Element} fromElement
34 * @param {?Element} toElement 34 * @param {?Element} toElement
35 */ 35 */
36 selectedItemChanged(from, to, fromElement, toElement) {}, 36 selectedItemChanged(from, to, fromElement, toElement) {},
37 }; 37 };
38 38
39 /** @enum {symbol} */ 39 /** @enum {symbol} */
40 UI.ListHeightMode = { 40 UI.ListMode = {
41 Fixed: Symbol('UI.ListHeightMode.Fixed'), 41 Grow: Symbol('UI.ListMode.Grow'),
42 Measured: Symbol('UI.ListHeightMode.Measured'), 42 ViewportFixedItems: Symbol('UI.ListMode.ViewportFixedItems'),
43 Variable: Symbol('UI.ListHeightMode.Variable') 43 ViewportFixedItemsMeasured: Symbol('UI.ListMode.ViewportFixedItemsMeasured'),
44 ViewportVariableItems: Symbol('UI.ListMode.ViewportVariableItems')
44 }; 45 };
45 46
46 /** 47 /**
47 * @template T 48 * @template T
48 */ 49 */
49 UI.ListControl = class { 50 UI.ListControl = class {
50 /** 51 /**
51 * @param {!UI.ListDelegate<T>} delegate 52 * @param {!UI.ListDelegate<T>} delegate
53 * @param {!UI.ListMode=} mode
52 */ 54 */
53 constructor(delegate) { 55 constructor(delegate, mode) {
54 this.element = createElement('div'); 56 this.element = createElement('div');
55 this.element.style.overflow = 'auto'; 57 this.element.style.overflow = 'auto';
56 this._topElement = this.element.createChild('div'); 58 this._topElement = this.element.createChild('div');
57 this._bottomElement = this.element.createChild('div'); 59 this._bottomElement = this.element.createChild('div');
58 this._firstIndex = 0; 60 this._firstIndex = 0;
59 this._lastIndex = 0; 61 this._lastIndex = 0;
60 this._renderedHeight = 0; 62 this._renderedHeight = 0;
61 this._topHeight = 0; 63 this._topHeight = 0;
62 this._bottomHeight = 0; 64 this._bottomHeight = 0;
63 this._clearViewport();
64 65
65 /** @type {!Array<T>} */ 66 /** @type {!Array<T>} */
66 this._items = []; 67 this._items = [];
67 /** @type {!Map<T, !Element>} */ 68 /** @type {!Map<T, !Element>} */
68 this._itemToElement = new Map(); 69 this._itemToElement = new Map();
69 this._selectedIndex = -1; 70 this._selectedIndex = -1;
70 71
71 this._boundKeyDown = event => { 72 this._boundKeyDown = event => {
72 if (this.onKeyDown(event)) 73 if (this.onKeyDown(event))
73 event.consume(true); 74 event.consume(true);
74 }; 75 };
75 this._boundClick = event => { 76 this._boundClick = event => {
76 if (this.onClick(event)) 77 if (this.onClick(event))
77 event.consume(true); 78 event.consume(true);
78 }; 79 };
80 this._boundScroll = event => {
81 this._updateViewport(this.element.scrollTop, this.element.offsetHeight);
82 };
79 83
80 this._delegate = delegate; 84 this._delegate = delegate;
81 this._heightMode = UI.ListHeightMode.Measured; 85 this._mode = mode || UI.ListMode.ViewportFixedItemsMeasured;
82 this._fixedHeight = 0; 86 this._fixedHeight = 0;
83 this._variableOffsets = new Int32Array(0); 87 this._variableOffsets = new Int32Array(0);
84 88 this._clearContents();
85 this.element.addEventListener('scroll', this._onScroll.bind(this), false); 89 if (this._mode !== UI.ListMode.Grow)
90 this.element.addEventListener('scroll', this._boundScroll, false);
86 } 91 }
87 92
88 /** 93 /**
89 * @param {!UI.ListHeightMode} mode
90 */
91 setHeightMode(mode) {
92 this._heightMode = mode;
93 this._fixedHeight = 0;
94 if (this._items.length) {
95 this._itemToElement.clear();
96 this._invalidate(0, this._items.length, this._items.length);
97 }
98 }
99
100 /**
101 * @param {boolean} handleInput 94 * @param {boolean} handleInput
102 */ 95 */
103 setHandleInput(handleInput) { 96 setHandleInput(handleInput) {
104 if (handleInput) { 97 if (handleInput) {
105 this.element.addEventListener('keydown', this._boundKeyDown, false); 98 this.element.addEventListener('keydown', this._boundKeyDown, false);
106 this.element.addEventListener('click', this._boundClick, false); 99 this.element.addEventListener('click', this._boundClick, false);
107 } else { 100 } else {
108 this.element.removeEventListener('keydown', this._boundKeyDown, false); 101 this.element.removeEventListener('keydown', this._boundKeyDown, false);
109 this.element.removeEventListener('click', this._boundClick, false); 102 this.element.removeEventListener('click', this._boundClick, false);
110 } 103 }
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
174 // Splice may fail with too many arguments. 167 // Splice may fail with too many arguments.
175 var before = this._items.slice(0, from); 168 var before = this._items.slice(0, from);
176 var after = this._items.slice(to); 169 var after = this._items.slice(to);
177 this._items = [].concat(before, items, after); 170 this._items = [].concat(before, items, after);
178 } 171 }
179 this._invalidate(from, to, items.length); 172 this._invalidate(from, to, items.length);
180 173
181 if (this._selectedIndex >= to) { 174 if (this._selectedIndex >= to) {
182 this._selectedIndex += items.length - (to - from); 175 this._selectedIndex += items.length - (to - from);
183 } else if (this._selectedIndex >= from) { 176 } else if (this._selectedIndex >= from) {
184 var index = this._findClosestSelectable(from + items.length, +1, 0, false) ; 177 var index = this._findFirstSelectable(from + items.length, +1, false);
185 if (index === -1) 178 if (index === -1)
186 index = this._findClosestSelectable(from - 1, -1, 0, false); 179 index = this._findFirstSelectable(from - 1, -1, false);
187 this._select(index, oldSelectedItem, oldSelectedElement); 180 this._select(index, oldSelectedItem, oldSelectedElement);
188 } 181 }
189 } 182 }
190 183
191 /** 184 /**
192 * @param {!Array<T>} items 185 * @param {!Array<T>} items
193 */ 186 */
194 replaceAllItems(items) { 187 replaceAllItems(items) {
195 this.replaceItemsInRange(0, this._items.length, items); 188 this.replaceItemsInRange(0, this._items.length, items);
196 } 189 }
197 190
198 /** 191 /**
199 * @param {number} from 192 * @param {number} from
200 * @param {number} to 193 * @param {number} to
201 */ 194 */
202 invalidateRange(from, to) { 195 invalidateRange(from, to) {
203 this._invalidate(from, to, to - from); 196 this._invalidate(from, to, to - from);
204 } 197 }
205 198
206 viewportResized() { 199 viewportResized() {
207 // TODO(dgozman): try to keep the visible scrollTop the same 200 if (this._mode === UI.ListMode.Grow)
208 // when invalidating after firstIndex but before first visible element. 201 return;
202 // TODO(dgozman): try to keep visible scrollTop the same.
209 var scrollTop = this.element.scrollTop; 203 var scrollTop = this.element.scrollTop;
210 var viewportHeight = this.element.offsetHeight; 204 var viewportHeight = this.element.offsetHeight;
211 this._clearViewport(); 205 this._clearViewport();
212 this._updateViewport(Number.constrain(scrollTop, 0, this._totalHeight() - vi ewportHeight), viewportHeight); 206 this._updateViewport(Number.constrain(scrollTop, 0, this._totalHeight() - vi ewportHeight), viewportHeight);
213 } 207 }
214 208
209 fixedHeightChanged() {
210 if (this._mode !== UI.ListMode.ViewportFixedItemsMeasured && this._mode !== UI.ListMode.ViewportFixedItems)
211 throw 'Only supported in fixed height items modes';
212 this._fixedHeight = 0;
213 if (this._items.length) {
214 this._itemToElement.clear();
215 this._invalidate(0, this._items.length, this._items.length);
216 }
217 }
218
215 /** 219 /**
216 * @param {number} index 220 * @param {number} index
217 */ 221 */
218 scrollItemAtIndexIntoView(index) { 222 scrollItemAtIndexIntoView(index) {
223 if (this._mode === UI.ListMode.Grow) {
224 this._elementAtIndex(index).scrollIntoViewIfNeeded(false);
225 return;
226 }
219 var top = this._offsetAtIndex(index); 227 var top = this._offsetAtIndex(index);
220 var bottom = this._offsetAtIndex(index + 1); 228 var bottom = this._offsetAtIndex(index + 1);
221 var scrollTop = this.element.scrollTop; 229 var scrollTop = this.element.scrollTop;
222 var viewportHeight = this.element.offsetHeight; 230 var viewportHeight = this.element.offsetHeight;
223 if (top < scrollTop) 231 if (top < scrollTop)
224 this._updateViewport(top, viewportHeight); 232 this._updateViewport(top, viewportHeight);
225 else if (bottom > scrollTop + viewportHeight) 233 else if (bottom > scrollTop + viewportHeight)
226 this._updateViewport(bottom - viewportHeight, viewportHeight); 234 this._updateViewport(bottom - viewportHeight, viewportHeight);
227 } 235 }
228 236
(...skipping 24 matching lines...) Expand all
253 } 261 }
254 262
255 /** 263 /**
256 * @param {!Event} event 264 * @param {!Event} event
257 * @return {boolean} 265 * @return {boolean}
258 */ 266 */
259 onKeyDown(event) { 267 onKeyDown(event) {
260 var index = -1; 268 var index = -1;
261 switch (event.key) { 269 switch (event.key) {
262 case 'ArrowUp': 270 case 'ArrowUp':
263 index = this._selectedIndex === -1 ? this._findClosestSelectable(this._i tems.length - 1, -1, 0, true) : 271 index = this._selectedIndex === -1 ? this._items.length - 1 : this._sele ctedIndex - 1;
264 this._findClosestSelectable(this._s electedIndex, -1, 1, true); 272 index = this._findFirstSelectable(index, -1, true);
265 break; 273 break;
266 case 'ArrowDown': 274 case 'ArrowDown':
267 index = this._selectedIndex === -1 ? this._findClosestSelectable(0, +1, 0, true) : 275 index = this._selectedIndex === -1 ? 0 : this._selectedIndex + 1;
268 this._findClosestSelectable(this._s electedIndex, +1, 1, true); 276 index = this._findFirstSelectable(index, +1, true);
269 break; 277 break;
270 case 'PageUp': 278 case 'PageUp':
279 if (this._mode === UI.ListMode.Grow)
280 return false;
271 index = this._selectedIndex === -1 ? this._items.length - 1 : this._sele ctedIndex; 281 index = this._selectedIndex === -1 ? this._items.length - 1 : this._sele ctedIndex;
272 // Compensate for zoom rounding errors with -1. 282 index = this._findPageSelectable(index, -1);
273 index = this._findClosestSelectable(index, -1, this.element.offsetHeight - 1, false);
274 break; 283 break;
275 case 'PageDown': 284 case 'PageDown':
285 if (this._mode === UI.ListMode.Grow)
286 return false;
276 index = this._selectedIndex === -1 ? 0 : this._selectedIndex; 287 index = this._selectedIndex === -1 ? 0 : this._selectedIndex;
277 // Compensate for zoom rounding errors with -1. 288 index = this._findPageSelectable(index, +1);
278 index = this._findClosestSelectable(index, +1, this.element.offsetHeight - 1, false);
279 break; 289 break;
280 default: 290 default:
281 return false; 291 return false;
282 } 292 }
283 if (index !== -1) { 293 if (index !== -1) {
284 this.scrollItemAtIndexIntoView(index); 294 this.scrollItemAtIndexIntoView(index);
285 this._select(index); 295 this._select(index);
286 return true; 296 return true;
287 } 297 }
288 return false; 298 return false;
289 } 299 }
290 300
291 /** 301 /**
292 * @param {!Event} event 302 * @param {!Event} event
293 * @return {boolean} 303 * @return {boolean}
294 */ 304 */
295 onClick(event) { 305 onClick(event) {
296 var node = event.target; 306 var node = event.target;
297 while (node && node.parentNodeOrShadowHost() !== this.element) 307 while (node && node.parentNodeOrShadowHost() !== this.element)
298 node = node.parentNodeOrShadowHost(); 308 node = node.parentNodeOrShadowHost();
299 if (!node || node.nodeType !== Node.ELEMENT_NODE) 309 if (!node)
300 return false; 310 return false;
301 var offset = /** @type {!Element} */ (node).getBoundingClientRect().top; 311 var index = this._items.findIndex(item => this._itemToElement.get(item) === node);
302 offset -= this.element.getBoundingClientRect().top;
303 var index = this._indexAtOffset(offset + this.element.scrollTop);
304 if (index === -1 || !this._delegate.isItemSelectable(this._items[index])) 312 if (index === -1 || !this._delegate.isItemSelectable(this._items[index]))
305 return false; 313 return false;
306 this._select(index); 314 this._select(index);
307 return true; 315 return true;
308 } 316 }
309 317
310 /** 318 /**
311 * @return {number} 319 * @return {number}
312 */ 320 */
313 _totalHeight() { 321 _totalHeight() {
314 return this._offsetAtIndex(this._items.length); 322 return this._offsetAtIndex(this._items.length);
315 } 323 }
316 324
317 /** 325 /**
318 * @param {number} offset 326 * @param {number} offset
319 * @return {number} 327 * @return {number}
320 */ 328 */
321 _indexAtOffset(offset) { 329 _indexAtOffset(offset) {
330 if (this._mode === UI.ListMode.Grow)
331 throw 'There should be no offset conversions in grow mode';
322 if (!this._items.length || offset < 0) 332 if (!this._items.length || offset < 0)
323 return 0; 333 return 0;
324 if (this._heightMode === UI.ListHeightMode.Variable) { 334 if (this._mode === UI.ListMode.ViewportVariableItems) {
325 return Math.min( 335 return Math.min(
326 this._items.length - 1, this._variableOffsets.lowerBound(offset, undef ined, 0, this._items.length)); 336 this._items.length - 1, this._variableOffsets.lowerBound(offset, undef ined, 0, this._items.length));
327 } 337 }
328 if (!this._fixedHeight) 338 if (!this._fixedHeight)
329 this._measureHeight(); 339 this._measureHeight();
330 return Math.min(this._items.length - 1, Math.floor(offset / this._fixedHeigh t)); 340 return Math.min(this._items.length - 1, Math.floor(offset / this._fixedHeigh t));
331 } 341 }
332 342
333 /** 343 /**
334 * @param {number} index 344 * @param {number} index
335 * @return {!Element} 345 * @return {!Element}
336 */ 346 */
337 _elementAtIndex(index) { 347 _elementAtIndex(index) {
338 var item = this._items[index]; 348 var item = this._items[index];
339 var element = this._itemToElement.get(item); 349 var element = this._itemToElement.get(item);
340 if (!element) { 350 if (!element) {
341 element = this._delegate.createElementForItem(item); 351 element = this._delegate.createElementForItem(item);
342 this._itemToElement.set(item, element); 352 this._itemToElement.set(item, element);
343 } 353 }
344 return element; 354 return element;
345 } 355 }
346 356
347 /** 357 /**
348 * @param {number} index 358 * @param {number} index
349 * @return {number} 359 * @return {number}
350 */ 360 */
351 _offsetAtIndex(index) { 361 _offsetAtIndex(index) {
362 if (this._mode === UI.ListMode.Grow)
363 throw 'There should be no offset conversions in grow mode';
352 if (!this._items.length) 364 if (!this._items.length)
353 return 0; 365 return 0;
354 if (this._heightMode === UI.ListHeightMode.Variable) 366 if (this._mode === UI.ListMode.ViewportVariableItems)
355 return this._variableOffsets[index]; 367 return this._variableOffsets[index];
356 if (!this._fixedHeight) 368 if (!this._fixedHeight)
357 this._measureHeight(); 369 this._measureHeight();
358 return index * this._fixedHeight; 370 return index * this._fixedHeight;
359 } 371 }
360 372
361 _measureHeight() { 373 _measureHeight() {
362 if (this._heightMode === UI.ListHeightMode.Measured) 374 if (this._mode === UI.ListMode.ViewportFixedItemsMeasured)
363 this._fixedHeight = UI.measurePreferredSize(this._elementAtIndex(0), this. element).height; 375 this._fixedHeight = UI.measurePreferredSize(this._elementAtIndex(0), this. element).height;
364 else 376 else
365 this._fixedHeight = this._delegate.heightForItem(this._items[0]); 377 this._fixedHeight = this._delegate.heightForItem(this._items[0]);
366 } 378 }
367 379
368 /** 380 /**
369 * @param {number} index 381 * @param {number} index
370 * @param {?T=} oldItem 382 * @param {?T=} oldItem
371 * @param {?Element=} oldElement 383 * @param {?Element=} oldElement
372 */ 384 */
373 _select(index, oldItem, oldElement) { 385 _select(index, oldItem, oldElement) {
374 if (oldItem === undefined) 386 if (oldItem === undefined)
375 oldItem = this._selectedIndex !== -1 ? this._items[this._selectedIndex] : null; 387 oldItem = this._selectedIndex !== -1 ? this._items[this._selectedIndex] : null;
376 if (oldElement === undefined) 388 if (oldElement === undefined)
377 oldElement = this._itemToElement.get(oldItem) || null; 389 oldElement = this._itemToElement.get(oldItem) || null;
378 this._selectedIndex = index; 390 this._selectedIndex = index;
379 var newItem = this._selectedIndex !== -1 ? this._items[this._selectedIndex] : null; 391 var newItem = this._selectedIndex !== -1 ? this._items[this._selectedIndex] : null;
380 var newElement = this._itemToElement.get(newItem) || null; 392 var newElement = this._itemToElement.get(newItem) || null;
381 this._delegate.selectedItemChanged(oldItem, newItem, /** @type {?Element} */ (oldElement), newElement); 393 this._delegate.selectedItemChanged(oldItem, newItem, /** @type {?Element} */ (oldElement), newElement);
382 } 394 }
383 395
384 /** 396 /**
385 * @param {number} index 397 * @param {number} index
386 * @param {number} direction 398 * @param {number} direction
387 * @param {number} minSkippedHeight
388 * @param {boolean} canWrap 399 * @param {boolean} canWrap
389 * @return {number} 400 * @return {number}
390 */ 401 */
391 _findClosestSelectable(index, direction, minSkippedHeight, canWrap) { 402 _findFirstSelectable(index, direction, canWrap) {
392 var length = this._items.length; 403 var length = this._items.length;
393 if (!length) 404 if (!length)
394 return -1; 405 return -1;
395 406 for (var step = 0; step <= length; step++) {
396 var lastSelectable = -1;
397 var start = -1;
398 var startOffset = this._offsetAtIndex(index);
399 while (true) {
400 if (index < 0 || index >= length) { 407 if (index < 0 || index >= length) {
401 if (!canWrap) 408 if (!canWrap)
402 return lastSelectable; 409 return -1;
403 index = (index + length) % length; 410 index = (index + length) % length;
404 } 411 }
412 if (this._delegate.isItemSelectable(this._items[index]))
413 return index;
414 index += direction;
415 }
416 return -1;
417 }
405 418
406 // Handle full wrap-around. 419 /**
407 if (index === start) 420 * @param {number} index
408 return lastSelectable; 421 * @param {number} direction
409 if (start === -1) { 422 * @return {number}
410 start = index; 423 */
411 startOffset = this._offsetAtIndex(index); 424 _findPageSelectable(index, direction) {
412 } 425 var lastSelectable = -1;
413 426 var startOffset = this._offsetAtIndex(index);
427 // Compensate for zoom rounding errors with -1.
428 var viewportHeight = this.element.offsetHeight - 1;
429 while (index >= 0 && index < this._items.length) {
414 if (this._delegate.isItemSelectable(this._items[index])) { 430 if (this._delegate.isItemSelectable(this._items[index])) {
415 if (Math.abs(this._offsetAtIndex(index) - startOffset) >= minSkippedHeig ht) 431 if (Math.abs(this._offsetAtIndex(index) - startOffset) >= viewportHeight )
416 return index; 432 return index;
417 lastSelectable = index; 433 lastSelectable = index;
418 } 434 }
419
420 index += direction; 435 index += direction;
421 } 436 }
437 return lastSelectable;
422 } 438 }
423 439
424 /** 440 /**
425 * @param {number} length 441 * @param {number} length
426 * @param {number} copyTo 442 * @param {number} copyTo
427 */ 443 */
428 _reallocateVariableOffsets(length, copyTo) { 444 _reallocateVariableOffsets(length, copyTo) {
429 if (this._variableOffsets.length < length) { 445 if (this._variableOffsets.length < length) {
430 var variableOffsets = new Int32Array(Math.max(length, this._variableOffset s.length * 2)); 446 var variableOffsets = new Int32Array(Math.max(length, this._variableOffset s.length * 2));
431 variableOffsets.set(this._variableOffsets.slice(0, copyTo), 0); 447 variableOffsets.set(this._variableOffsets.slice(0, copyTo), 0);
432 this._variableOffsets = variableOffsets; 448 this._variableOffsets = variableOffsets;
433 } else if (this._variableOffsets.length >= 2 * length) { 449 } else if (this._variableOffsets.length >= 2 * length) {
434 var variableOffsets = new Int32Array(length); 450 var variableOffsets = new Int32Array(length);
435 variableOffsets.set(this._variableOffsets.slice(0, copyTo), 0); 451 variableOffsets.set(this._variableOffsets.slice(0, copyTo), 0);
436 this._variableOffsets = variableOffsets; 452 this._variableOffsets = variableOffsets;
437 } 453 }
438 } 454 }
439 455
440 /** 456 /**
441 * @param {number} from 457 * @param {number} from
442 * @param {number} to 458 * @param {number} to
443 * @param {number} inserted 459 * @param {number} inserted
444 */ 460 */
445 _invalidate(from, to, inserted) { 461 _invalidate(from, to, inserted) {
446 if (this._heightMode === UI.ListHeightMode.Variable) { 462 if (this._mode === UI.ListMode.Grow) {
463 this._invalidateGrowMode(from, to - from, inserted);
464 return;
465 }
466
467 if (this._mode === UI.ListMode.ViewportVariableItems) {
447 this._reallocateVariableOffsets(this._items.length + 1, from + 1); 468 this._reallocateVariableOffsets(this._items.length + 1, from + 1);
448 for (var i = from + 1; i <= this._items.length; i++) 469 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]); 470 this._variableOffsets[i] = this._variableOffsets[i - 1] + this._delegate .heightForItem(this._items[i - 1]);
450 } 471 }
451 472
452 var viewportHeight = this.element.offsetHeight; 473 var viewportHeight = this.element.offsetHeight;
453 var totalHeight = this._totalHeight(); 474 var totalHeight = this._totalHeight();
454 var scrollTop = this.element.scrollTop; 475 var scrollTop = this.element.scrollTop;
455 476
456 if (this._renderedHeight < viewportHeight || totalHeight < viewportHeight) { 477 if (this._renderedHeight < viewportHeight || totalHeight < viewportHeight) {
(...skipping 16 matching lines...) Expand all
473 } 494 }
474 495
475 if (from >= this._lastIndex) { 496 if (from >= this._lastIndex) {
476 var bottomHeight = this._bottomHeight + heightDelta; 497 var bottomHeight = this._bottomHeight + heightDelta;
477 this._bottomElement.style.height = bottomHeight + 'px'; 498 this._bottomElement.style.height = bottomHeight + 'px';
478 this._bottomHeight = bottomHeight; 499 this._bottomHeight = bottomHeight;
479 this._renderedHeight = totalHeight; 500 this._renderedHeight = totalHeight;
480 return; 501 return;
481 } 502 }
482 503
483 // TODO(dgozman): try to keep the visible scrollTop the same 504 // TODO(dgozman): try to keep visible scrollTop the same
484 // when invalidating after firstIndex but before first visible element. 505 // when invalidating after firstIndex but before first visible element.
485 this._clearViewport(); 506 this._clearViewport();
486 this._updateViewport(Number.constrain(scrollTop, 0, totalHeight - viewportHe ight), viewportHeight); 507 this._updateViewport(Number.constrain(scrollTop, 0, totalHeight - viewportHe ight), viewportHeight);
487 } 508 }
488 509
510 /**
511 * @param {number} start
512 * @param {number} remove
513 * @param {number} add
514 */
515 _invalidateGrowMode(start, remove, add) {
516 var startElement = this._topElement;
517 for (var index = 0; index < start; index++)
518 startElement = startElement.nextElementSibling;
519 while (remove--)
520 startElement.nextElementSibling.remove();
521 while (add--)
522 this.element.insertBefore(this._elementAtIndex(start + add), startElement. nextElementSibling);
523 }
524
489 _clearViewport() { 525 _clearViewport() {
526 if (this._mode === UI.ListMode.Grow)
527 throw 'There should be no viewport updates in grow mode';
490 this._firstIndex = 0; 528 this._firstIndex = 0;
491 this._lastIndex = 0; 529 this._lastIndex = 0;
492 this._renderedHeight = 0; 530 this._renderedHeight = 0;
493 this._topHeight = 0; 531 this._topHeight = 0;
494 this._bottomHeight = 0; 532 this._bottomHeight = 0;
533 this._clearContents();
534 }
535
536 _clearContents() {
537 // Note: this method should not force layout. Be careful.
495 this._topElement.style.height = '0'; 538 this._topElement.style.height = '0';
496 this._bottomElement.style.height = '0'; 539 this._bottomElement.style.height = '0';
497 this.element.removeChildren(); 540 this.element.removeChildren();
498 this.element.appendChild(this._topElement); 541 this.element.appendChild(this._topElement);
499 this.element.appendChild(this._bottomElement); 542 this.element.appendChild(this._bottomElement);
500 } 543 }
501 544
502 _onScroll() {
503 this._updateViewport(this.element.scrollTop, this.element.offsetHeight);
504 }
505
506 /** 545 /**
507 * @param {number} scrollTop 546 * @param {number} scrollTop
508 * @param {number} viewportHeight 547 * @param {number} viewportHeight
509 */ 548 */
510 _updateViewport(scrollTop, viewportHeight) { 549 _updateViewport(scrollTop, viewportHeight) {
511 // Note: this method should not force layout. Be careful. 550 // Note: this method should not force layout. Be careful.
551 if (this._mode === UI.ListMode.Grow)
552 throw 'There should be no viewport updates in grow mode';
512 553
513 var totalHeight = this._totalHeight(); 554 var totalHeight = this._totalHeight();
514 if (!totalHeight) { 555 if (!totalHeight) {
515 this._firstIndex = 0; 556 this._firstIndex = 0;
516 this._lastIndex = 0; 557 this._lastIndex = 0;
517 this._topHeight = 0; 558 this._topHeight = 0;
518 this._bottomHeight = 0; 559 this._bottomHeight = 0;
519 this._renderedHeight = 0; 560 this._renderedHeight = 0;
520 this._topElement.style.height = '0'; 561 this._topElement.style.height = '0';
521 this._bottomElement.style.height = '0'; 562 this._bottomElement.style.height = '0';
(...skipping 26 matching lines...) Expand all
548 this._firstIndex = firstIndex; 589 this._firstIndex = firstIndex;
549 this._lastIndex = lastIndex; 590 this._lastIndex = lastIndex;
550 this._topHeight = this._offsetAtIndex(firstIndex); 591 this._topHeight = this._offsetAtIndex(firstIndex);
551 this._topElement.style.height = this._topHeight + 'px'; 592 this._topElement.style.height = this._topHeight + 'px';
552 this._bottomHeight = totalHeight - this._offsetAtIndex(lastIndex); 593 this._bottomHeight = totalHeight - this._offsetAtIndex(lastIndex);
553 this._bottomElement.style.height = this._bottomHeight + 'px'; 594 this._bottomElement.style.height = this._bottomHeight + 'px';
554 this._renderedHeight = totalHeight; 595 this._renderedHeight = totalHeight;
555 this.element.scrollTop = scrollTop; 596 this.element.scrollTop = scrollTop;
556 } 597 }
557 }; 598 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698