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

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

Issue 2592433003: [DevTools] Replace ViewportControl with ListControl. (Closed)
Patch Set: partial Created 3 years, 12 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 * @unrestricted 5 * @unrestricted
6 * @template T
6 */ 7 */
7 UI.ViewportControl = class { 8 UI.ViewportControl = class {
8 /** 9 constructor() {
9 * @param {!UI.ViewportControl.Provider} provider
10 */
11 constructor(provider) {
12 this.element = createElement('div'); 10 this.element = createElement('div');
13 this.element.style.overflow = 'auto'; 11 this.element.style.overflow = 'auto';
14 this._innerElement = this.element.createChild('div'); 12 this._topElement = this.element.createChild('div');
15 this._innerElement.style.height = '0px'; 13 this._bottomElement = this.element.createChild('div');
16 this._innerElement.style.position = 'relative'; 14 this._firstIndex = 0;
17 this._innerElement.style.overflow = 'hidden'; 15 this._lastIndex = 0;
18 16 this._fixedHeight = true;
19 this._provider = provider; 17 this._measuredHeight = 0;
20 this.element.addEventListener('scroll', this._update.bind(this), false); 18 /** @type {?function(T):!Element} */
21 this._itemCount = 0; 19 this._renderer = null;
22 this._indexSymbol = Symbol('UI.ViewportControl._indexSymbol'); 20 /** @type {!Array<T>} */
23 } 21 this._items = [];
24 22 this._elementSymbol = Symbol('UI.ViewportControl.element');
25 refresh() { 23 this.element.addEventListener('scroll', this._onScroll.bind(this), false);
26 this._itemCount = this._provider.itemCount(); 24 this._update(0);
27 this._innerElement.removeChildren(); 25 }
28 26
29 var height = 0; 27 setVariableHeights() {
30 this._cumulativeHeights = new Int32Array(this._itemCount); 28 // TODO(dgozman): support variable height.
31 for (var i = 0; i < this._itemCount; ++i) { 29 throw 'Not supported';
32 height += this._provider.fastItemHeight(i); 30 }
33 this._cumulativeHeights[i] = height; 31
34 } 32 /**
35 this._innerElement.style.height = height + 'px'; 33 * @param {number} elementHeight
36 34 */
37 this._update(); 35 setFixedHeights(elementHeight) {
38 } 36 this._fixedHeight = true;
39 37 this._measuredHeight = elementHeight;
40 _update() { 38 }
41 if (!this._cumulativeHeights) { 39
42 this.refresh(); 40 // !!!!! Move this to clients.
41 setFixedMeasuredHeights() {
42 this._fixedHeight = true;
43 this._measuredHeight = 0;
44 }
45
46 /**
47 * @param {?function(T):!Element} renderer
48 */
49 setRenderer(renderer) {
50 this._renderer = renderer;
51 for (var i = 0; i < this._items; i++)
52 this._items[i][this._elementSymbol] = null;
53 this._refresh();
54 }
55
56 slowRefresh() {
57 this._refresh();
58 }
59
60 /**
61 * @return {number}
62 */
63 measuredElementHeight() {
64 return this._elementHeight();
65 }
66
67 /**
68 * @return {number}
69 */
70 elementsPerViewport() {
71 if (!this._fixedHeight)
72 throw 'Only supported in fixed-height scenario';
73 var elementHeight = this._elementHeight();
74 return elementHeight ? Math.floor(this.element.offsetHeight / elementHeight) : 0;
75 }
76
77 /**
78 * @param {number} index
79 * @return {!Element}
80 */
81 elementAtIndex(index) {
82 return this._elementAtIndex(index);
83 }
84
85 /**
86 * @return {number}
87 */
88 length() {
89 return this._items.length;
90 }
91
92 /**
93 * @param {number} index
94 * @return {T}
95 */
96 itemAtIndex(index) {
97 return this._items[index];
98 }
99
100 /**
101 * @param {T} item
102 */
103 pushItem(item) {
104 var totalHeight = this._totalHeight();
105 this._items.push(item);
106 this._invalidate(totalHeight, this._items.length - 1, this._items.length - 1 );
107 }
108
109 /**
110 * @return {T}
111 */
112 popItem() {
113 var totalHeight = this._totalHeight();
114 var result = this._items.pop();
115 result[this._elementSymbol] = null;
116 this._invalidate(totalHeight, this._items.length - 1, this._items.length);
117 return result;
118 }
119
120 /**
121 * @param {number} index
122 * @param {T} item
123 */
124 insertItemAtIndex(index, item) {
125 var totalHeight = this._totalHeight();
126 this._items.splice(index, 0, item);
127 this._invalidate(totalHeight, index, index);
128 }
129
130 /**
131 * @param {number} index
132 * @return {T}
133 */
134 removeItemAtIndex(index) {
135 var totalHeight = this._totalHeight();
136 var result = this._items[index];
137 this._items.splice(index, 1);
138 result[this._elementSymbol] = null;
139 this._invalidate(totalHeight, index, index + 1);
140 return result;
141 }
142
143 /**
144 * @param {number} from
145 * @param {number} to
146 * @param {!Array<T>} items
147 */
148 replaceItemsInRange(from, to, items) {
149 var totalHeight = this._totalHeight();
150 for (var i = from; i < to; i++)
151 this._items[i][this._elementSymbol] = null;
152 this._items.splice.bind(this._items, from, to - from).apply(null, items);
153 this._invalidate(totalHeight, from, to);
154 }
155
156 /**
157 * @param {!Array<T>} items
158 */
159 replaceAllItems(items) {
160 var totalHeight = this._totalHeight();
161 var length = this._items.length;
162 for (var i = 0; i < this._items; i++)
163 this._items[i][this._elementSymbol] = null;
164 this._items = items;
165 this._invalidate(totalHeight, 0, length);
166 }
167
168 /**
169 * @param {number} index
170 */
171 scrollItemAtIndexIntoView(index) {
172 var top = this._offsetAtIndex(index);
173 var bottom = this._offsetAtIndex(index + 1);
174 var scrollTop = this.element.scrollTop;
175 var height = this.element.offsetHeight;
176 if (top < scrollTop)
177 this._update(top);
178 else if (bottom > scrollTop + height)
179 this._update(bottom - height);
180 }
181
182 /**
183 * @return {number}
184 */
185 _elementHeight() {
186 if (!this._fixedHeight)
187 throw 'Only measure in fixed-height scenario';
188 if (!this._measuredHeight && this._items.length)
189 this._measuredHeight = UI.measurePreferredSize(this._elementAtIndex(0), th is.element).height;
190 return this._measuredHeight;
191 }
192
193 /**
194 * @return {number}
195 */
196 _totalHeight() {
197 return this._items.length ? this._items.length * this._elementHeight() : 0;
198 }
199
200 /**
201 * @param {number} offset
202 * @return {number}
203 */
204 _indexAtOffset(offset) {
205 if (!this._items.length)
206 return 0;
207 var index = Math.floor(offset / this._elementHeight());
208 if (index >= this._items.length)
209 return this._items.length - 1;
210 return index;
211 }
212
213 /**
214 * @param {number} index
215 * @return {!Element}
216 */
217 _elementAtIndex(index) {
218 var item = this._items[index];
219 var element = item[this._elementSymbol];
220 if (!element) {
221 if (this._renderer) {
222 element = this._renderer.call(null, item);
223 } else {
224 element = createElement('div');
225 // TODO(dgozman): support variable height.
226 element.style.height = this._measuredHeight + 'px';
227 }
228 item[this._elementSymbol] = element;
229 }
230 return element;
231 }
232
233 /**
234 * @param {number} index
235 * @return {number}
236 */
237 _offsetAtIndex(index) {
238 return this._items.length ? index * this._elementHeight() : 0;
239 }
240
241 /**
242 * @param {number} totalHeight
243 * @param {number} from
244 * @param {number} to
245 */
246 _invalidate(totalHeight, from, to) {
247 var scrollTop = this.element.scrollTop;
248 var availableHeight = this.element.offsetHeight;
249 var heightDelta = this._totalHeight() - totalHeight;
250 if (totalHeight < availableHeight || this._totalHeight() < availableHeight) {
251 this._refresh();
43 return; 252 return;
44 } 253 }
45 254 if (to <= this._firstIndex) {
46 var visibleHeight = this._visibleHeight(); 255 var topHeight = this._topHeight + heightDelta;
47 var visibleFrom = this.element.scrollTop; 256 this._topElement.style.height = topHeight + 'px';
48 var activeHeight = visibleHeight * 2; 257 this.element.scrollTop = scrollTop + heightDelta;
49 var firstActiveIndex = Math.max( 258 this._topHeight = topHeight;
50 Array.prototype.lowerBound.call(this._cumulativeHeights, visibleFrom + 1 - (activeHeight - visibleHeight) / 2),
51 0);
52 var lastActiveIndex = Math.min(
53 Array.prototype.lowerBound.call(
54 this._cumulativeHeights, visibleFrom + visibleHeight + (activeHeight - visibleHeight) / 2),
55 this._itemCount - 1);
56
57 var children = this._innerElement.children;
58 for (var i = children.length - 1; i >= 0; --i) {
59 var element = children[i];
60 if (element[this._indexSymbol] < firstActiveIndex || element[this._indexSy mbol] > lastActiveIndex)
61 element.remove();
62 }
63
64 for (var i = firstActiveIndex; i <= lastActiveIndex; ++i)
65 this._insertElement(i);
66 }
67
68 /**
69 * @param {number} index
70 */
71 _insertElement(index) {
72 var element = this._provider.itemElement(index);
73 if (!element || element.parentElement === this._innerElement)
74 return; 259 return;
75 260 }
76 element.style.position = 'absolute'; 261 if (from >= this._lastIndex) {
77 element.style.top = (this._cumulativeHeights[index - 1] || 0) + 'px'; 262 var bottomHeight = this._bottomHeight + heightDelta;
78 element.style.left = '0'; 263 this._bottomElement.style.height = bottomHeight + 'px';
79 element.style.right = '0'; 264 this.element.scrollTop = scrollTop + heightDelta;
80 element[this._indexSymbol] = index; 265 this._bottomHeight = bottomHeight;
81 this._innerElement.appendChild(element);
82 }
83
84 /**
85 * @return {number}
86 */
87 firstVisibleIndex() {
88 return Math.max(Array.prototype.lowerBound.call(this._cumulativeHeights, thi s.element.scrollTop + 1), 0);
89 }
90
91 /**
92 * @return {number}
93 */
94 lastVisibleIndex() {
95 return Math.min(
96 Array.prototype.lowerBound.call(this._cumulativeHeights, this.element.sc rollTop + this._visibleHeight()),
97 this._itemCount);
98 }
99
100 /**
101 * @param {number} index
102 * @param {boolean=} makeLast
103 */
104 scrollItemIntoView(index, makeLast) {
105 var firstVisibleIndex = this.firstVisibleIndex();
106 var lastVisibleIndex = this.lastVisibleIndex();
107 if (index > firstVisibleIndex && index < lastVisibleIndex)
108 return; 266 return;
109 if (makeLast) 267 }
110 this.forceScrollItemToBeLast(index); 268 this._refresh();
111 else if (index <= firstVisibleIndex) 269 }
112 this.forceScrollItemToBeFirst(index); 270
113 else if (index >= lastVisibleIndex) 271 _refresh() {
114 this.forceScrollItemToBeLast(index); 272 this._firstIndex = 0;
115 } 273 this._lastIndex = 0;
116 274 this.element.removeChildren();
117 /** 275 this.element.appendChild(this._topElement);
118 * @param {number} index 276 this.element.appendChild(this._bottomElement);
119 */ 277 this._update(0);
120 forceScrollItemToBeFirst(index) { 278 }
121 this.element.scrollTop = index > 0 ? this._cumulativeHeights[index - 1] : 0; 279
122 this._update(); 280 _onScroll() {
123 } 281 this._update(this.element.scrollTop);
124 282 }
125 /** 283
126 * @param {number} index 284 /**
127 */ 285 * @param {number} scrollTop
128 forceScrollItemToBeLast(index) { 286 */
129 this.element.scrollTop = this._cumulativeHeights[index] - this._visibleHeigh t(); 287 _update(scrollTop) {
130 this._update(); 288 var totalHeight = this._totalHeight();
131 } 289 if (!totalHeight) {
132 290 this._firstIndex = 0;
133 /** 291 this._lastIndex = 0;
134 * @return {number} 292 this._topHeight = 0;
135 */ 293 this._bottomHeight = 0;
136 _visibleHeight() { 294 this._topElement.style.height = '0px';
137 return this.element.offsetHeight; 295 this._bottomElement.style.height = '0px';
296 return;
297 }
298
299 var height = this.element.offsetHeight;
300 var firstIndex = this._indexAtOffset(Math.max(0, scrollTop - height));
301 var lastIndex = this._indexAtOffset(Math.min(totalHeight, scrollTop + 2 * he ight)) + 1;
302
303 for (var index = this._firstIndex; index < firstIndex; index++) {
304 var element = this._elementAtIndex(index);
305 element.remove();
306 this._firstIndex++;
307 }
308 for (var index = this._lastIndex - 1; index >= lastIndex; index--) {
309 var element = this._elementAtIndex(index);
310 element.remove();
311 this._lastIndex--;
312 }
313 this._firstIndex = Math.min(this._firstIndex, lastIndex);
314 this._lastIndex = Math.max(this._lastIndex, firstIndex);
315 for (var index = this._firstIndex - 1; index >= firstIndex; index--) {
316 var element = this._elementAtIndex(index);
317 this.element.insertBefore(element, this._topElement.nextSibling);
318 }
319 for (var index = this._lastIndex; index < lastIndex; index++) {
320 var element = this._elementAtIndex(index);
321 this.element.insertBefore(element, this._bottomElement);
322 }
323
324 this._firstIndex = firstIndex;
325 this._lastIndex = lastIndex;
326 this._topHeight = this._offsetAtIndex(firstIndex);
327 this._topElement.style.height = this._topHeight + 'px';
328 this._bottomHeight = (totalHeight - this._offsetAtIndex(lastIndex));
329 this._bottomElement.style.height = this._bottomHeight + 'px';
330 this.element.scrollTop = scrollTop;
138 } 331 }
139 }; 332 };
140
141 /**
142 * @interface
143 */
144 UI.ViewportControl.Provider = function() {};
145
146 UI.ViewportControl.Provider.prototype = {
147 /**
148 * @param {number} index
149 * @return {number}
150 */
151 fastItemHeight(index) {
152 return 0;
153 },
154
155 /**
156 * @return {number}
157 */
158 itemCount() {
159 return 0;
160 },
161
162 /**
163 * @param {number} index
164 * @return {?Element}
165 */
166 itemElement(index) {
167 return null;
168 }
169 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698