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

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

Issue 2916743002: [DevTools] Introduce Common.List used as a backend for list controls (Closed)
Patch Set: addressed comments Created 3 years, 6 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 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
43 NonViewport: Symbol('UI.ListMode.NonViewport'), 43 NonViewport: Symbol('UI.ListMode.NonViewport'),
44 EqualHeightItems: Symbol('UI.ListMode.EqualHeightItems'), 44 EqualHeightItems: Symbol('UI.ListMode.EqualHeightItems'),
45 VariousHeightItems: Symbol('UI.ListMode.VariousHeightItems') 45 VariousHeightItems: Symbol('UI.ListMode.VariousHeightItems')
46 }; 46 };
47 47
48 /** 48 /**
49 * @template T 49 * @template T
50 */ 50 */
51 UI.ListControl = class { 51 UI.ListControl = class {
52 /** 52 /**
53 * @param {!UI.ListModel<T>} model
53 * @param {!UI.ListDelegate<T>} delegate 54 * @param {!UI.ListDelegate<T>} delegate
54 * @param {!UI.ListMode=} mode 55 * @param {!UI.ListMode=} mode
55 */ 56 */
56 constructor(delegate, mode) { 57 constructor(model, delegate, mode) {
57 this.element = createElement('div'); 58 this.element = createElement('div');
58 this.element.style.overflowY = 'auto'; 59 this.element.style.overflowY = 'auto';
59 this._topElement = this.element.createChild('div'); 60 this._topElement = this.element.createChild('div');
60 this._bottomElement = this.element.createChild('div'); 61 this._bottomElement = this.element.createChild('div');
61 this._firstIndex = 0; 62 this._firstIndex = 0;
62 this._lastIndex = 0; 63 this._lastIndex = 0;
63 this._renderedHeight = 0; 64 this._renderedHeight = 0;
64 this._topHeight = 0; 65 this._topHeight = 0;
65 this._bottomHeight = 0; 66 this._bottomHeight = 0;
66 67
67 /** @type {!Array<T>} */ 68 this._model = model;
68 this._items = []; 69 this._model.addEventListener(UI.ListModel.Events.ItemsReplaced, this._replac edItemsInRange, this);
69 /** @type {!Map<T, !Element>} */ 70 /** @type {!Map<T, !Element>} */
70 this._itemToElement = new Map(); 71 this._itemToElement = new Map();
71 this._selectedIndex = -1; 72 this._selectedIndex = -1;
73 /** @type {?T} */
74 this._selectedItem = null;
72 75
73 this.element.tabIndex = -1; 76 this.element.tabIndex = -1;
74 this.element.addEventListener('click', this._onClick.bind(this), false); 77 this.element.addEventListener('click', this._onClick.bind(this), false);
75 this.element.addEventListener('keydown', this._onKeyDown.bind(this), false); 78 this.element.addEventListener('keydown', this._onKeyDown.bind(this), false);
76 79
77 this._delegate = delegate; 80 this._delegate = delegate;
78 this._mode = mode || UI.ListMode.EqualHeightItems; 81 this._mode = mode || UI.ListMode.EqualHeightItems;
79 this._fixedHeight = 0; 82 this._fixedHeight = 0;
80 this._variableOffsets = new Int32Array(0); 83 this._variableOffsets = new Int32Array(0);
81 this._clearContents(); 84 this._clearContents();
82 85
83 if (this._mode !== UI.ListMode.NonViewport) { 86 if (this._mode !== UI.ListMode.NonViewport) {
84 this.element.addEventListener('scroll', () => { 87 this.element.addEventListener('scroll', () => {
85 this._updateViewport(this.element.scrollTop, this.element.offsetHeight); 88 this._updateViewport(this.element.scrollTop, this.element.offsetHeight);
86 }, false); 89 }, false);
87 } 90 }
88 } 91 }
89 92
90 /** 93 /**
91 * @return {number} 94 * @param {!UI.ListModel<T>} model
92 */ 95 */
93 length() { 96 setModel(model) {
94 return this._items.length; 97 this._itemToElement.clear();
98 var length = this._model.length();
99 this._model.removeEventListener(UI.ListModel.Events.ItemsReplaced, this._rep lacedItemsInRange, this);
100 this._model = model;
101 this._model.addEventListener(UI.ListModel.Events.ItemsReplaced, this._replac edItemsInRange, this);
102 this.invalidateRange(0, length);
95 } 103 }
96 104
97 /** 105 /**
98 * @param {number} index 106 * @param {!Common.Event} event
99 * @return {T}
100 */ 107 */
101 itemAtIndex(index) { 108 _replacedItemsInRange(event) {
102 return this._items[index]; 109 var data = /** @type {{index: number, removed: !Array<T>, inserted: number}} */ (event.data);
103 } 110 var from = data.index;
111 var to = from + data.removed.length;
104 112
105 /** 113 var oldSelectedItem = this._selectedItem;
106 * @param {T} item
107 */
108 pushItem(item) {
109 this.replaceItemsInRange(this._items.length, this._items.length, [item]);
110 }
111
112 /**
113 * @return {T}
114 */
115 popItem() {
116 return this.removeItemAtIndex(this._items.length - 1);
117 }
118
119 /**
120 * @param {number} index
121 * @param {T} item
122 */
123 insertItemAtIndex(index, item) {
124 this.replaceItemsInRange(index, index, [item]);
125 }
126
127 /**
128 * @param {T} item
129 * @param {function(T, T):number} comparator
130 */
131 insertItemWithComparator(item, comparator) {
132 var index = this._items.lowerBound(item, comparator);
133 this.insertItemAtIndex(index, item);
134 }
135
136 /**
137 * @param {T} item
138 * @return {number}
139 */
140 indexOfItem(item) {
141 return this._items.indexOf(item);
142 }
143
144 /**
145 * @param {number} index
146 * @return {T}
147 */
148 removeItemAtIndex(index) {
149 var result = this._items[index];
150 this.replaceItemsInRange(index, index + 1, []);
151 return result;
152 }
153
154 /**
155 * @param {T} item
156 */
157 removeItem(item) {
158 var index = this._items.indexOf(item);
159 if (index === -1) {
160 console.error('Attempt to remove non-existing item');
161 return;
162 }
163 this.removeItemAtIndex(index);
164 }
165
166 /**
167 * @param {number} from
168 * @param {number} to
169 * @param {!Array<T>} items
170 */
171 replaceItemsInRange(from, to, items) {
172 var oldSelectedItem = this._selectedIndex !== -1 ? this._items[this._selecte dIndex] : null;
173 var oldSelectedElement = oldSelectedItem ? (this._itemToElement.get(oldSelec tedItem) || null) : null; 114 var oldSelectedElement = oldSelectedItem ? (this._itemToElement.get(oldSelec tedItem) || null) : null;
174 115 for (var i = 0; i < data.removed.length; i++)
175 for (var i = from; i < to; i++) 116 this._itemToElement.delete(data.removed[i]);
176 this._itemToElement.delete(this._items[i]); 117 this._invalidate(from, to, data.inserted);
177 if (items.length < 10000) {
178 this._items.splice.bind(this._items, from, to - from).apply(null, items);
179 } else {
180 // Splice may fail with too many arguments.
181 var before = this._items.slice(0, from);
182 var after = this._items.slice(to);
183 this._items = [].concat(before, items, after);
184 }
185 this._invalidate(from, to, items.length);
186 118
187 if (this._selectedIndex >= to) { 119 if (this._selectedIndex >= to) {
188 this._selectedIndex += items.length - (to - from); 120 this._selectedIndex += data.inserted - (to - from);
121 this._selectedItem = this._model.itemAtIndex(this._selectedIndex);
189 } else if (this._selectedIndex >= from) { 122 } else if (this._selectedIndex >= from) {
190 var index = this._findFirstSelectable(from + items.length, +1, false); 123 var index = this._findFirstSelectable(from + data.inserted, +1, false);
191 if (index === -1) 124 if (index === -1)
192 index = this._findFirstSelectable(from - 1, -1, false); 125 index = this._findFirstSelectable(from - 1, -1, false);
193 this._select(index, oldSelectedItem, oldSelectedElement); 126 this._select(index, oldSelectedItem, oldSelectedElement);
194 } 127 }
195 } 128 }
196 129
197 /** 130 /**
198 * @param {!Array<T>} items 131 * @param {T} item
199 */ 132 */
200 replaceAllItems(items) { 133 refreshItem(item) {
201 this.replaceItemsInRange(0, this._items.length, items); 134 var index = this._model.indexOfItem(item);
202 } 135 if (index === -1) {
203 136 console.error('Item to refresh is not present');
204 refreshAllItems() { 137 return;
205 this.refreshItemsInRange(0, this._items.length); 138 }
206 } 139 this._itemToElement.delete(item);
207 140 this.invalidateRange(index, index + 1);
208 /**
209 * @param {number} from
210 * @param {number} to
211 */
212 refreshItemsInRange(from, to) {
213 for (var i = from; i < to; i++)
214 this._itemToElement.delete(this._items[i]);
215 this.invalidateRange(from, to);
216 if (this._selectedIndex !== -1) 141 if (this._selectedIndex !== -1)
217 this._select(this._selectedIndex, null, null); 142 this._select(this._selectedIndex, null, null);
218 } 143 }
219 144
220 /** 145 /**
221 * @param {number} from 146 * @param {number} from
222 * @param {number} to 147 * @param {number} to
223 */ 148 */
224 invalidateRange(from, to) { 149 invalidateRange(from, to) {
225 this._invalidate(from, to, to - from); 150 this._invalidate(from, to, to - from);
226 } 151 }
227 152
228 viewportResized() { 153 viewportResized() {
229 if (this._mode === UI.ListMode.NonViewport) 154 if (this._mode === UI.ListMode.NonViewport)
230 return; 155 return;
231 // TODO(dgozman): try to keep visible scrollTop the same. 156 // TODO(dgozman): try to keep visible scrollTop the same.
232 var scrollTop = this.element.scrollTop; 157 var scrollTop = this.element.scrollTop;
233 var viewportHeight = this.element.offsetHeight; 158 var viewportHeight = this.element.offsetHeight;
234 this._clearViewport(); 159 this._clearViewport();
235 this._updateViewport(Number.constrain(scrollTop, 0, this._totalHeight() - vi ewportHeight), viewportHeight); 160 this._updateViewport(Number.constrain(scrollTop, 0, this._totalHeight() - vi ewportHeight), viewportHeight);
236 } 161 }
237 162
238 invalidateItemHeight() { 163 invalidateItemHeight() {
239 if (this._mode !== UI.ListMode.EqualHeightItems) { 164 if (this._mode !== UI.ListMode.EqualHeightItems) {
240 console.error('Only supported in equal height items mode'); 165 console.error('Only supported in equal height items mode');
241 return; 166 return;
242 } 167 }
243 this._fixedHeight = 0; 168 this._fixedHeight = 0;
244 if (this._items.length) { 169 if (this._model.length()) {
245 this._itemToElement.clear(); 170 this._itemToElement.clear();
246 this._invalidate(0, this._items.length, this._items.length); 171 this._invalidate(0, this._model.length(), this._model.length());
247 } 172 }
248 } 173 }
249 174
250 /** 175 /**
251 * @param {?Node} node 176 * @param {?Node} node
252 * @return {?T} 177 * @return {?T}
253 */ 178 */
254 itemForNode(node) { 179 itemForNode(node) {
255 while (node && node.parentNodeOrShadowHost() !== this.element) 180 while (node && node.parentNodeOrShadowHost() !== this.element)
256 node = node.parentNodeOrShadowHost(); 181 node = node.parentNodeOrShadowHost();
257 if (!node) 182 if (!node)
258 return null; 183 return null;
259 var element = /** @type {!Element} */ (node); 184 var element = /** @type {!Element} */ (node);
260 var index = this._items.findIndex(item => this._itemToElement.get(item) === element); 185 var index = this._model.findIndex(item => this._itemToElement.get(item) === element);
261 return index !== -1 ? this._items[index] : null; 186 return index !== -1 ? this._model.itemAtIndex(index) : null;
262 } 187 }
263 188
264 /** 189 /**
265 * @param {T} item 190 * @param {T} item
266 * @param {boolean=} center 191 * @param {boolean=} center
267 */ 192 */
268 scrollItemIntoView(item, center) { 193 scrollItemIntoView(item, center) {
269 var index = this._items.indexOf(item); 194 var index = this._model.indexOfItem(item);
270 if (index === -1) { 195 if (index === -1) {
271 console.error('Attempt to scroll onto missing item'); 196 console.error('Attempt to scroll onto missing item');
272 return; 197 return;
273 } 198 }
274 this._scrollIntoView(index, center); 199 this._scrollIntoView(index, center);
275 } 200 }
276 201
277 /** 202 /**
278 * @return {?T} 203 * @return {?T}
279 */ 204 */
280 selectedItem() { 205 selectedItem() {
281 return this._selectedIndex === -1 ? null : this._items[this._selectedIndex]; 206 return this._selectedItem;
282 } 207 }
283 208
284 /** 209 /**
285 * @return {number} 210 * @return {number}
286 */ 211 */
287 selectedIndex() { 212 selectedIndex() {
288 return this._selectedIndex; 213 return this._selectedIndex;
289 } 214 }
290 215
291 /** 216 /**
292 * @param {?T} item 217 * @param {?T} item
293 * @param {boolean=} center 218 * @param {boolean=} center
294 * @param {boolean=} dontScroll 219 * @param {boolean=} dontScroll
295 */ 220 */
296 selectItem(item, center, dontScroll) { 221 selectItem(item, center, dontScroll) {
297 var index = -1; 222 var index = -1;
298 if (item !== null) { 223 if (item !== null) {
299 index = this._items.indexOf(item); 224 index = this._model.indexOfItem(item);
300 if (index === -1) { 225 if (index === -1) {
301 console.error('Attempt to select missing item'); 226 console.error('Attempt to select missing item');
302 return; 227 return;
303 } 228 }
304 if (!this._delegate.isItemSelectable(item)) { 229 if (!this._delegate.isItemSelectable(item)) {
305 console.error('Attempt to select non-selectable item'); 230 console.error('Attempt to select non-selectable item');
306 return; 231 return;
307 } 232 }
308 } 233 }
309 if (this._selectedIndex !== index) 234 if (this._selectedIndex !== index)
310 this._select(index); 235 this._select(index);
311 if (index !== -1 && !dontScroll) 236 if (index !== -1 && !dontScroll)
312 this._scrollIntoView(index, center); 237 this._scrollIntoView(index, center);
313 } 238 }
314 239
315 /** 240 /**
316 * @param {boolean=} canWrap 241 * @param {boolean=} canWrap
317 * @param {boolean=} center 242 * @param {boolean=} center
318 * @return {boolean} 243 * @return {boolean}
319 */ 244 */
320 selectPreviousItem(canWrap, center) { 245 selectPreviousItem(canWrap, center) {
321 if (this._selectedIndex === -1 && !canWrap) 246 if (this._selectedIndex === -1 && !canWrap)
322 return false; 247 return false;
323 var index = this._selectedIndex === -1 ? this._items.length - 1 : this._sele ctedIndex - 1; 248 var index = this._selectedIndex === -1 ? this._model.length() - 1 : this._se lectedIndex - 1;
324 index = this._findFirstSelectable(index, -1, !!canWrap); 249 index = this._findFirstSelectable(index, -1, !!canWrap);
325 if (index !== -1) { 250 if (index !== -1) {
326 this._scrollIntoView(index, center); 251 this._scrollIntoView(index, center);
327 this._select(index); 252 this._select(index);
328 return true; 253 return true;
329 } 254 }
330 return false; 255 return false;
331 } 256 }
332 257
333 /** 258 /**
(...skipping 14 matching lines...) Expand all
348 return false; 273 return false;
349 } 274 }
350 275
351 /** 276 /**
352 * @param {boolean=} center 277 * @param {boolean=} center
353 * @return {boolean} 278 * @return {boolean}
354 */ 279 */
355 selectItemPreviousPage(center) { 280 selectItemPreviousPage(center) {
356 if (this._mode === UI.ListMode.NonViewport) 281 if (this._mode === UI.ListMode.NonViewport)
357 return false; 282 return false;
358 var index = this._selectedIndex === -1 ? this._items.length - 1 : this._sele ctedIndex; 283 var index = this._selectedIndex === -1 ? this._model.length() - 1 : this._se lectedIndex;
359 index = this._findPageSelectable(index, -1); 284 index = this._findPageSelectable(index, -1);
360 if (index !== -1) { 285 if (index !== -1) {
361 this._scrollIntoView(index, center); 286 this._scrollIntoView(index, center);
362 this._select(index); 287 this._select(index);
363 return true; 288 return true;
364 } 289 }
365 return false; 290 return false;
366 } 291 }
367 292
368 /** 293 /**
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after
437 break; 362 break;
438 } 363 }
439 if (selected) 364 if (selected)
440 event.consume(); 365 event.consume();
441 } 366 }
442 367
443 /** 368 /**
444 * @return {number} 369 * @return {number}
445 */ 370 */
446 _totalHeight() { 371 _totalHeight() {
447 return this._offsetAtIndex(this._items.length); 372 return this._offsetAtIndex(this._model.length());
448 } 373 }
449 374
450 /** 375 /**
451 * @param {number} offset 376 * @param {number} offset
452 * @return {number} 377 * @return {number}
453 */ 378 */
454 _indexAtOffset(offset) { 379 _indexAtOffset(offset) {
455 if (this._mode === UI.ListMode.NonViewport) 380 if (this._mode === UI.ListMode.NonViewport)
456 throw 'There should be no offset conversions in non-viewport mode'; 381 throw 'There should be no offset conversions in non-viewport mode';
457 if (!this._items.length || offset < 0) 382 if (!this._model.length() || offset < 0)
458 return 0; 383 return 0;
459 if (this._mode === UI.ListMode.VariousHeightItems) { 384 if (this._mode === UI.ListMode.VariousHeightItems) {
460 return Math.min( 385 return Math.min(
461 this._items.length - 1, this._variableOffsets.lowerBound(offset, undef ined, 0, this._items.length)); 386 this._model.length() - 1, this._variableOffsets.lowerBound(offset, und efined, 0, this._model.length()));
462 } 387 }
463 if (!this._fixedHeight) 388 if (!this._fixedHeight)
464 this._measureHeight(); 389 this._measureHeight();
465 return Math.min(this._items.length - 1, Math.floor(offset / this._fixedHeigh t)); 390 return Math.min(this._model.length() - 1, Math.floor(offset / this._fixedHei ght));
466 } 391 }
467 392
468 /** 393 /**
469 * @param {number} index 394 * @param {number} index
470 * @return {!Element} 395 * @return {!Element}
471 */ 396 */
472 _elementAtIndex(index) { 397 _elementAtIndex(index) {
473 var item = this._items[index]; 398 var item = this._model.itemAtIndex(index);
474 var element = this._itemToElement.get(item); 399 var element = this._itemToElement.get(item);
475 if (!element) { 400 if (!element) {
476 element = this._delegate.createElementForItem(item); 401 element = this._delegate.createElementForItem(item);
477 this._itemToElement.set(item, element); 402 this._itemToElement.set(item, element);
478 } 403 }
479 return element; 404 return element;
480 } 405 }
481 406
482 /** 407 /**
483 * @param {number} index 408 * @param {number} index
484 * @return {number} 409 * @return {number}
485 */ 410 */
486 _offsetAtIndex(index) { 411 _offsetAtIndex(index) {
487 if (this._mode === UI.ListMode.NonViewport) 412 if (this._mode === UI.ListMode.NonViewport)
488 throw 'There should be no offset conversions in non-viewport mode'; 413 throw 'There should be no offset conversions in non-viewport mode';
489 if (!this._items.length) 414 if (!this._model.length())
490 return 0; 415 return 0;
491 if (this._mode === UI.ListMode.VariousHeightItems) 416 if (this._mode === UI.ListMode.VariousHeightItems)
492 return this._variableOffsets[index]; 417 return this._variableOffsets[index];
493 if (!this._fixedHeight) 418 if (!this._fixedHeight)
494 this._measureHeight(); 419 this._measureHeight();
495 return index * this._fixedHeight; 420 return index * this._fixedHeight;
496 } 421 }
497 422
498 _measureHeight() { 423 _measureHeight() {
499 this._fixedHeight = this._delegate.heightForItem(this._items[0]); 424 this._fixedHeight = this._delegate.heightForItem(this._model.itemAtIndex(0)) ;
500 if (!this._fixedHeight) 425 if (!this._fixedHeight)
501 this._fixedHeight = UI.measurePreferredSize(this._elementAtIndex(0), this. element).height; 426 this._fixedHeight = UI.measurePreferredSize(this._elementAtIndex(0), this. element).height;
502 } 427 }
503 428
504 /** 429 /**
505 * @param {number} index 430 * @param {number} index
506 * @param {?T=} oldItem 431 * @param {?T=} oldItem
507 * @param {?Element=} oldElement 432 * @param {?Element=} oldElement
508 */ 433 */
509 _select(index, oldItem, oldElement) { 434 _select(index, oldItem, oldElement) {
510 if (oldItem === undefined) 435 if (oldItem === undefined)
511 oldItem = this._selectedIndex !== -1 ? this._items[this._selectedIndex] : null; 436 oldItem = this._selectedItem;
512 if (oldElement === undefined) 437 if (oldElement === undefined)
513 oldElement = this._itemToElement.get(oldItem) || null; 438 oldElement = this._itemToElement.get(oldItem) || null;
514 this._selectedIndex = index; 439 this._selectedIndex = index;
515 var newItem = this._selectedIndex !== -1 ? this._items[this._selectedIndex] : null; 440 this._selectedItem = index === -1 ? null : this._model.itemAtIndex(index);
441 var newItem = this._selectedItem;
516 var newElement = this._selectedIndex !== -1 ? this._elementAtIndex(index) : null; 442 var newElement = this._selectedIndex !== -1 ? this._elementAtIndex(index) : null;
517 this._delegate.selectedItemChanged(oldItem, newItem, /** @type {?Element} */ (oldElement), newElement); 443 this._delegate.selectedItemChanged(oldItem, newItem, /** @type {?Element} */ (oldElement), newElement);
518 } 444 }
519 445
520 /** 446 /**
521 * @param {number} index 447 * @param {number} index
522 * @param {number} direction 448 * @param {number} direction
523 * @param {boolean} canWrap 449 * @param {boolean} canWrap
524 * @return {number} 450 * @return {number}
525 */ 451 */
526 _findFirstSelectable(index, direction, canWrap) { 452 _findFirstSelectable(index, direction, canWrap) {
527 var length = this._items.length; 453 var length = this._model.length();
528 if (!length) 454 if (!length)
529 return -1; 455 return -1;
530 for (var step = 0; step <= length; step++) { 456 for (var step = 0; step <= length; step++) {
531 if (index < 0 || index >= length) { 457 if (index < 0 || index >= length) {
532 if (!canWrap) 458 if (!canWrap)
533 return -1; 459 return -1;
534 index = (index + length) % length; 460 index = (index + length) % length;
535 } 461 }
536 if (this._delegate.isItemSelectable(this._items[index])) 462 if (this._delegate.isItemSelectable(this._model.itemAtIndex(index)))
537 return index; 463 return index;
538 index += direction; 464 index += direction;
539 } 465 }
540 return -1; 466 return -1;
541 } 467 }
542 468
543 /** 469 /**
544 * @param {number} index 470 * @param {number} index
545 * @param {number} direction 471 * @param {number} direction
546 * @return {number} 472 * @return {number}
547 */ 473 */
548 _findPageSelectable(index, direction) { 474 _findPageSelectable(index, direction) {
549 var lastSelectable = -1; 475 var lastSelectable = -1;
550 var startOffset = this._offsetAtIndex(index); 476 var startOffset = this._offsetAtIndex(index);
551 // Compensate for zoom rounding errors with -1. 477 // Compensate for zoom rounding errors with -1.
552 var viewportHeight = this.element.offsetHeight - 1; 478 var viewportHeight = this.element.offsetHeight - 1;
553 while (index >= 0 && index < this._items.length) { 479 while (index >= 0 && index < this._model.length()) {
554 if (this._delegate.isItemSelectable(this._items[index])) { 480 if (this._delegate.isItemSelectable(this._model.itemAtIndex(index))) {
555 if (Math.abs(this._offsetAtIndex(index) - startOffset) >= viewportHeight ) 481 if (Math.abs(this._offsetAtIndex(index) - startOffset) >= viewportHeight )
556 return index; 482 return index;
557 lastSelectable = index; 483 lastSelectable = index;
558 } 484 }
559 index += direction; 485 index += direction;
560 } 486 }
561 return lastSelectable; 487 return lastSelectable;
562 } 488 }
563 489
564 /** 490 /**
(...skipping 17 matching lines...) Expand all
582 * @param {number} to 508 * @param {number} to
583 * @param {number} inserted 509 * @param {number} inserted
584 */ 510 */
585 _invalidate(from, to, inserted) { 511 _invalidate(from, to, inserted) {
586 if (this._mode === UI.ListMode.NonViewport) { 512 if (this._mode === UI.ListMode.NonViewport) {
587 this._invalidateNonViewportMode(from, to - from, inserted); 513 this._invalidateNonViewportMode(from, to - from, inserted);
588 return; 514 return;
589 } 515 }
590 516
591 if (this._mode === UI.ListMode.VariousHeightItems) { 517 if (this._mode === UI.ListMode.VariousHeightItems) {
592 this._reallocateVariableOffsets(this._items.length + 1, from + 1); 518 this._reallocateVariableOffsets(this._model.length() + 1, from + 1);
593 for (var i = from + 1; i <= this._items.length; i++) 519 for (var i = from + 1; i <= this._model.length(); i++) {
594 this._variableOffsets[i] = this._variableOffsets[i - 1] + this._delegate .heightForItem(this._items[i - 1]); 520 this._variableOffsets[i] =
521 this._variableOffsets[i - 1] + this._delegate.heightForItem(this._mo del.itemAtIndex(i - 1));
522 }
595 } 523 }
596 524
597 var viewportHeight = this.element.offsetHeight; 525 var viewportHeight = this.element.offsetHeight;
598 var totalHeight = this._totalHeight(); 526 var totalHeight = this._totalHeight();
599 var scrollTop = this.element.scrollTop; 527 var scrollTop = this.element.scrollTop;
600 528
601 if (this._renderedHeight < viewportHeight || totalHeight < viewportHeight) { 529 if (this._renderedHeight < viewportHeight || totalHeight < viewportHeight) {
602 this._clearViewport(); 530 this._clearViewport();
603 this._updateViewport(Number.constrain(scrollTop, 0, totalHeight - viewport Height), viewportHeight); 531 this._updateViewport(Number.constrain(scrollTop, 0, totalHeight - viewport Height), viewportHeight);
604 return; 532 return;
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after
716 this._firstIndex = firstIndex; 644 this._firstIndex = firstIndex;
717 this._lastIndex = lastIndex; 645 this._lastIndex = lastIndex;
718 this._topHeight = this._offsetAtIndex(firstIndex); 646 this._topHeight = this._offsetAtIndex(firstIndex);
719 this._topElement.style.height = this._topHeight + 'px'; 647 this._topElement.style.height = this._topHeight + 'px';
720 this._bottomHeight = totalHeight - this._offsetAtIndex(lastIndex); 648 this._bottomHeight = totalHeight - this._offsetAtIndex(lastIndex);
721 this._bottomElement.style.height = this._bottomHeight + 'px'; 649 this._bottomElement.style.height = this._bottomHeight + 'px';
722 this._renderedHeight = totalHeight; 650 this._renderedHeight = totalHeight;
723 this.element.scrollTop = scrollTop; 651 this.element.scrollTop = scrollTop;
724 } 652 }
725 }; 653 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698