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

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

Powered by Google App Engine
This is Rietveld 408576698