Index: runtime/observatory/lib/src/elements/containers/virtual_collection.dart |
diff --git a/runtime/observatory/lib/src/elements/containers/virtual_collection.dart b/runtime/observatory/lib/src/elements/containers/virtual_collection.dart |
index 688ea0a40357fce1fbebc43a9aec9044dd8745d0..ddbd17012c926e5fd22bc561a364ec617bcb5659 100644 |
--- a/runtime/observatory/lib/src/elements/containers/virtual_collection.dart |
+++ b/runtime/observatory/lib/src/elements/containers/virtual_collection.dart |
@@ -4,6 +4,7 @@ |
import 'dart:async'; |
import 'dart:html'; |
+import 'dart:math' as math; |
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart'; |
import 'package:observatory/src/elements/helpers/tag.dart'; |
@@ -24,7 +25,6 @@ class VirtualCollectionElement extends HtmlElement implements Renderable { |
VirtualCollectionHeaderCallback _createHeader; |
VirtualCollectionUpdateCallback _update; |
double _itemHeight; |
- double _headerHeight = 0.0; |
int _top; |
double _height; |
List _items; |
@@ -64,7 +64,7 @@ class VirtualCollectionElement extends HtmlElement implements Renderable { |
_r.enable(); |
_top = null; |
_itemHeight = null; |
- _onScrollSubscription = onScroll.listen(_onScroll); |
+ _onScrollSubscription = _viewport.onScroll.listen(_onScroll); |
_onResizeSubscription = window.onResize.listen(_onResize); |
} |
@@ -77,19 +77,19 @@ class VirtualCollectionElement extends HtmlElement implements Renderable { |
_onResizeSubscription.cancel(); |
} |
- final DivElement _header = new DivElement()..classes = ['header']; |
- final DivElement _scroller = new DivElement()..classes = ['scroller']; |
- final DivElement _shifter = new DivElement()..classes = ['shifter']; |
- final DivElement _container = new DivElement()..classes = ['container']; |
+ DivElement _header; |
+ final DivElement _viewport = new DivElement() |
+ ..classes = ['viewport', 'container']; |
+ final DivElement _spacer = new DivElement()..classes = ['spacer']; |
+ final DivElement _buffer = new DivElement()..classes = ['buffer']; |
dynamic getItemFromElement(HtmlElement element) { |
- final el_index = _container.children.indexOf(element); |
+ final el_index = _buffer.children.indexOf(element); |
if (el_index < 0) { |
return null; |
} |
- final item_index = _top + |
- el_index - |
- (_container.children.length * _inverse_preload).floor(); |
+ final item_index = |
+ _top + el_index - (_buffer.children.length * _inverse_preload).floor(); |
if (0 <= item_index && item_index < items.length) { |
return _items[item_index]; |
} |
@@ -98,7 +98,6 @@ class VirtualCollectionElement extends HtmlElement implements Renderable { |
/// The preloaded element before and after the visible area are: |
/// 1/preload_size of the number of items in the visble area. |
- /// See shared.css for the "top:-25%;". |
static const int _preload = 2; |
/// L = length of all the elements loaded |
@@ -120,24 +119,26 @@ class VirtualCollectionElement extends HtmlElement implements Renderable { |
void render() { |
if (children.isEmpty) { |
children = [ |
- _scroller |
+ _viewport |
..children = [ |
- _shifter |
+ _spacer |
..children = [ |
- _container..children = [_create()] |
- ] |
- ], |
+ _buffer..children = [_create()] |
+ ], |
+ ] |
]; |
if (_createHeader != null) { |
- _header.children = [ |
- new DivElement() |
- ..classes = ['container'] |
- ..children = _createHeader() |
- ]; |
- _scroller.children.insert(0, _header); |
- _headerHeight = _header.children[0].getBoundingClientRect().height; |
+ _header = new DivElement() |
+ ..classes = ['header', 'container'] |
+ ..children = _createHeader(); |
+ children.insert(0, _header); |
+ final rect = _header.getBoundingClientRect(); |
+ _header.classes.add('attached'); |
+ _viewport.style.top = '${rect.height}px'; |
+ final width = _header.children.fold(0, _foldWidth); |
+ _buffer.style.minWidth = '${width}px'; |
} |
- _itemHeight = _container.children[0].getBoundingClientRect().height; |
+ _itemHeight = _buffer.children[0].getBoundingClientRect().height; |
_height = getBoundingClientRect().height; |
} |
@@ -146,31 +147,31 @@ class VirtualCollectionElement extends HtmlElement implements Renderable { |
if (index >= 0) { |
final minScrollTop = _itemHeight * (index + 1) - _height; |
final maxScrollTop = _itemHeight * index; |
- scrollTop = ((maxScrollTop - minScrollTop) / 2 + minScrollTop).floor(); |
+ _viewport.scrollTop = |
+ ((maxScrollTop - minScrollTop) / 2 + minScrollTop).floor(); |
} |
_takeIntoView = null; |
} |
- final top = (scrollTop / _itemHeight).floor(); |
+ final top = (_viewport.scrollTop / _itemHeight).floor(); |
- _updateHeader(); |
- _scroller.style.height = '${_itemHeight*(_items.length)+_headerHeight}px'; |
+ _spacer.style.height = '${_itemHeight*(_items.length)}px'; |
final tail_length = (_height / _itemHeight / _preload).ceil(); |
final length = tail_length * 2 + tail_length * _preload; |
- if (_container.children.length < length) { |
- while (_container.children.length != length) { |
+ if (_buffer.children.length < length) { |
+ while (_buffer.children.length != length) { |
var e = _create(); |
e..style.display = 'hidden'; |
- _container.children.add(e); |
+ _buffer.children.add(e); |
} |
_top = null; // force update; |
} |
if ((_top == null) || ((top - _top).abs() >= tail_length)) { |
- _shifter.style.top = '${_itemHeight*(top-tail_length)}px'; |
+ _buffer.style.top = '${_itemHeight*(top-tail_length)}px'; |
int i = top - tail_length; |
- for (final HtmlElement e in _container.children) { |
+ for (final HtmlElement e in _buffer.children) { |
if (0 <= i && i < _items.length) { |
e..style.display = null; |
_update(e, _items[i], i); |
@@ -181,16 +182,25 @@ class VirtualCollectionElement extends HtmlElement implements Renderable { |
} |
_top = top; |
} |
+ _updateHeader(); |
+ } |
+ |
+ double _foldWidth(double value, HtmlElement child) { |
+ return math.max(value, child.getBoundingClientRect().width); |
} |
void _updateHeader() { |
- _header.style.top = '${scrollTop}px'; |
+ if (_header != null) { |
+ _header.style.left = '${-_viewport.scrollLeft}px'; |
+ final width = _buffer.getBoundingClientRect().width; |
+ _header.children.last.style.width = '${width}px'; |
+ } |
} |
void _onScroll(_) { |
- // needed to avoid flickering |
- _updateHeader(); |
_r.dirty(); |
+ // We anticipate the header in advance to avoid flickering |
+ _updateHeader(); |
} |
void _onResize(_) { |
@@ -198,6 +208,10 @@ class VirtualCollectionElement extends HtmlElement implements Renderable { |
if (newHeight > _height) { |
_height = newHeight; |
_r.dirty(); |
+ } else { |
+ // Even if we are not updating the structure the computed size is going to |
+ // change |
+ _updateHeader(); |
} |
} |
} |