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

Side by Side Diff: runtime/observatory/lib/src/elements/containers/virtual_collection.dart

Issue 2998103002: Refactor of Observatory virtual-collection (Closed)
Patch Set: Remove unnecessary layer of indirection Created 3 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | runtime/observatory/lib/src/elements/css/shared.css » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 import 'dart:async'; 5 import 'dart:async';
6 import 'dart:html'; 6 import 'dart:html';
7 import 'dart:math' as math;
7 import 'package:observatory/src/elements/helpers/rendering_scheduler.dart'; 8 import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
8 import 'package:observatory/src/elements/helpers/tag.dart'; 9 import 'package:observatory/src/elements/helpers/tag.dart';
9 10
10 typedef HtmlElement VirtualCollectionCreateCallback(); 11 typedef HtmlElement VirtualCollectionCreateCallback();
11 typedef List<HtmlElement> VirtualCollectionHeaderCallback(); 12 typedef List<HtmlElement> VirtualCollectionHeaderCallback();
12 typedef void VirtualCollectionUpdateCallback( 13 typedef void VirtualCollectionUpdateCallback(
13 HtmlElement el, dynamic item, int index); 14 HtmlElement el, dynamic item, int index);
14 15
15 class VirtualCollectionElement extends HtmlElement implements Renderable { 16 class VirtualCollectionElement extends HtmlElement implements Renderable {
16 static const tag = const Tag<VirtualCollectionElement>('virtual-collection'); 17 static const tag = const Tag<VirtualCollectionElement>('virtual-collection');
17 18
18 RenderingScheduler<VirtualCollectionElement> _r; 19 RenderingScheduler<VirtualCollectionElement> _r;
19 20
20 Stream<RenderedEvent<VirtualCollectionElement>> get onRendered => 21 Stream<RenderedEvent<VirtualCollectionElement>> get onRendered =>
21 _r.onRendered; 22 _r.onRendered;
22 23
23 VirtualCollectionCreateCallback _create; 24 VirtualCollectionCreateCallback _create;
24 VirtualCollectionHeaderCallback _createHeader; 25 VirtualCollectionHeaderCallback _createHeader;
25 VirtualCollectionUpdateCallback _update; 26 VirtualCollectionUpdateCallback _update;
26 double _itemHeight; 27 double _itemHeight;
27 double _headerHeight = 0.0;
28 int _top; 28 int _top;
29 double _height; 29 double _height;
30 List _items; 30 List _items;
31 StreamSubscription _onScrollSubscription; 31 StreamSubscription _onScrollSubscription;
32 StreamSubscription _onResizeSubscription; 32 StreamSubscription _onResizeSubscription;
33 33
34 List get items => _items; 34 List get items => _items;
35 35
36 set items(Iterable value) { 36 set items(Iterable value) {
37 _items = new List.unmodifiable(value); 37 _items = new List.unmodifiable(value);
(...skipping 19 matching lines...) Expand all
57 } 57 }
58 58
59 VirtualCollectionElement.created() : super.created(); 59 VirtualCollectionElement.created() : super.created();
60 60
61 @override 61 @override
62 attached() { 62 attached() {
63 super.attached(); 63 super.attached();
64 _r.enable(); 64 _r.enable();
65 _top = null; 65 _top = null;
66 _itemHeight = null; 66 _itemHeight = null;
67 _onScrollSubscription = onScroll.listen(_onScroll); 67 _onScrollSubscription = _viewport.onScroll.listen(_onScroll);
68 _onResizeSubscription = window.onResize.listen(_onResize); 68 _onResizeSubscription = window.onResize.listen(_onResize);
69 } 69 }
70 70
71 @override 71 @override
72 detached() { 72 detached() {
73 super.detached(); 73 super.detached();
74 _r.disable(notify: true); 74 _r.disable(notify: true);
75 children = const []; 75 children = const [];
76 _onScrollSubscription.cancel(); 76 _onScrollSubscription.cancel();
77 _onResizeSubscription.cancel(); 77 _onResizeSubscription.cancel();
78 } 78 }
79 79
80 final DivElement _header = new DivElement()..classes = ['header']; 80 DivElement _header;
81 final DivElement _scroller = new DivElement()..classes = ['scroller']; 81 final DivElement _viewport = new DivElement()
82 final DivElement _shifter = new DivElement()..classes = ['shifter']; 82 ..classes = ['viewport', 'container'];
83 final DivElement _container = new DivElement()..classes = ['container']; 83 final DivElement _spacer = new DivElement()..classes = ['spacer'];
84 final DivElement _buffer = new DivElement()..classes = ['buffer'];
84 85
85 dynamic getItemFromElement(HtmlElement element) { 86 dynamic getItemFromElement(HtmlElement element) {
86 final el_index = _container.children.indexOf(element); 87 final el_index = _buffer.children.indexOf(element);
87 if (el_index < 0) { 88 if (el_index < 0) {
88 return null; 89 return null;
89 } 90 }
90 final item_index = _top + 91 final item_index =
91 el_index - 92 _top + el_index - (_buffer.children.length * _inverse_preload).floor();
92 (_container.children.length * _inverse_preload).floor();
93 if (0 <= item_index && item_index < items.length) { 93 if (0 <= item_index && item_index < items.length) {
94 return _items[item_index]; 94 return _items[item_index];
95 } 95 }
96 return null; 96 return null;
97 } 97 }
98 98
99 /// The preloaded element before and after the visible area are: 99 /// The preloaded element before and after the visible area are:
100 /// 1/preload_size of the number of items in the visble area. 100 /// 1/preload_size of the number of items in the visble area.
101 /// See shared.css for the "top:-25%;".
102 static const int _preload = 2; 101 static const int _preload = 2;
103 102
104 /// L = length of all the elements loaded 103 /// L = length of all the elements loaded
105 /// l = length of the visible area 104 /// l = length of the visible area
106 /// 105 ///
107 /// L = l + 2 * l / _preload 106 /// L = l + 2 * l / _preload
108 /// l = L * _preload / (_preload + 2) 107 /// l = L * _preload / (_preload + 2)
109 /// 108 ///
110 /// tail = l / _preload = L * 1 / (_preload + 2) = L * _inverse_preload 109 /// tail = l / _preload = L * 1 / (_preload + 2) = L * _inverse_preload
111 static const double _inverse_preload = 1 / (_preload + 2); 110 static const double _inverse_preload = 1 / (_preload + 2);
112 111
113 var _takeIntoView; 112 var _takeIntoView;
114 113
115 void takeIntoView(item) { 114 void takeIntoView(item) {
116 _takeIntoView = item; 115 _takeIntoView = item;
117 _r.dirty(); 116 _r.dirty();
118 } 117 }
119 118
120 void render() { 119 void render() {
121 if (children.isEmpty) { 120 if (children.isEmpty) {
122 children = [ 121 children = [
123 _scroller 122 _viewport
124 ..children = [ 123 ..children = [
125 _shifter 124 _spacer
126 ..children = [ 125 ..children = [
127 _container..children = [_create()] 126 _buffer..children = [_create()]
128 ] 127 ],
129 ], 128 ]
130 ]; 129 ];
131 if (_createHeader != null) { 130 if (_createHeader != null) {
132 _header.children = [ 131 _header = new DivElement()
133 new DivElement() 132 ..classes = ['header', 'container']
134 ..classes = ['container'] 133 ..children = _createHeader();
135 ..children = _createHeader() 134 children.insert(0, _header);
136 ]; 135 final rect = _header.getBoundingClientRect();
137 _scroller.children.insert(0, _header); 136 _header.classes.add('attached');
138 _headerHeight = _header.children[0].getBoundingClientRect().height; 137 _viewport.style.top = '${rect.height}px';
138 final width = _header.children.fold(0, _foldWidth);
139 _buffer.style.minWidth = '${width}px';
139 } 140 }
140 _itemHeight = _container.children[0].getBoundingClientRect().height; 141 _itemHeight = _buffer.children[0].getBoundingClientRect().height;
141 _height = getBoundingClientRect().height; 142 _height = getBoundingClientRect().height;
142 } 143 }
143 144
144 if (_takeIntoView != null) { 145 if (_takeIntoView != null) {
145 final index = items.indexOf(_takeIntoView); 146 final index = items.indexOf(_takeIntoView);
146 if (index >= 0) { 147 if (index >= 0) {
147 final minScrollTop = _itemHeight * (index + 1) - _height; 148 final minScrollTop = _itemHeight * (index + 1) - _height;
148 final maxScrollTop = _itemHeight * index; 149 final maxScrollTop = _itemHeight * index;
149 scrollTop = ((maxScrollTop - minScrollTop) / 2 + minScrollTop).floor(); 150 _viewport.scrollTop =
151 ((maxScrollTop - minScrollTop) / 2 + minScrollTop).floor();
150 } 152 }
151 _takeIntoView = null; 153 _takeIntoView = null;
152 } 154 }
153 155
154 final top = (scrollTop / _itemHeight).floor(); 156 final top = (_viewport.scrollTop / _itemHeight).floor();
155 157
156 _updateHeader(); 158 _spacer.style.height = '${_itemHeight*(_items.length)}px';
157 _scroller.style.height = '${_itemHeight*(_items.length)+_headerHeight}px';
158 final tail_length = (_height / _itemHeight / _preload).ceil(); 159 final tail_length = (_height / _itemHeight / _preload).ceil();
159 final length = tail_length * 2 + tail_length * _preload; 160 final length = tail_length * 2 + tail_length * _preload;
160 161
161 if (_container.children.length < length) { 162 if (_buffer.children.length < length) {
162 while (_container.children.length != length) { 163 while (_buffer.children.length != length) {
163 var e = _create(); 164 var e = _create();
164 e..style.display = 'hidden'; 165 e..style.display = 'hidden';
165 _container.children.add(e); 166 _buffer.children.add(e);
166 } 167 }
167 _top = null; // force update; 168 _top = null; // force update;
168 } 169 }
169 170
170 if ((_top == null) || ((top - _top).abs() >= tail_length)) { 171 if ((_top == null) || ((top - _top).abs() >= tail_length)) {
171 _shifter.style.top = '${_itemHeight*(top-tail_length)}px'; 172 _buffer.style.top = '${_itemHeight*(top-tail_length)}px';
172 int i = top - tail_length; 173 int i = top - tail_length;
173 for (final HtmlElement e in _container.children) { 174 for (final HtmlElement e in _buffer.children) {
174 if (0 <= i && i < _items.length) { 175 if (0 <= i && i < _items.length) {
175 e..style.display = null; 176 e..style.display = null;
176 _update(e, _items[i], i); 177 _update(e, _items[i], i);
177 } else { 178 } else {
178 e.style.display = 'hidden'; 179 e.style.display = 'hidden';
179 } 180 }
180 i++; 181 i++;
181 } 182 }
182 _top = top; 183 _top = top;
183 } 184 }
185 _updateHeader();
186 }
187
188 double _foldWidth(double value, HtmlElement child) {
189 return math.max(value, child.getBoundingClientRect().width);
184 } 190 }
185 191
186 void _updateHeader() { 192 void _updateHeader() {
187 _header.style.top = '${scrollTop}px'; 193 if (_header != null) {
194 _header.style.left = '${-_viewport.scrollLeft}px';
195 final width = _buffer.getBoundingClientRect().width;
196 _header.children.last.style.width = '${width}px';
197 }
188 } 198 }
189 199
190 void _onScroll(_) { 200 void _onScroll(_) {
191 // needed to avoid flickering 201 _r.dirty();
202 // We anticipate the header in advance to avoid flickering
192 _updateHeader(); 203 _updateHeader();
193 _r.dirty();
194 } 204 }
195 205
196 void _onResize(_) { 206 void _onResize(_) {
197 final newHeight = getBoundingClientRect().height; 207 final newHeight = getBoundingClientRect().height;
198 if (newHeight > _height) { 208 if (newHeight > _height) {
199 _height = newHeight; 209 _height = newHeight;
200 _r.dirty(); 210 _r.dirty();
211 } else {
212 // Even if we are not updating the structure the computed size is going to
213 // change
214 _updateHeader();
201 } 215 }
202 } 216 }
203 } 217 }
OLDNEW
« no previous file with comments | « no previous file | runtime/observatory/lib/src/elements/css/shared.css » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698