protected override Vector2 ArrangeOverride(Vector2 finalSize) { var separation = (int)(ActualSeparation * UIScale); #region Scroll var cHeight = _totalHeight; var vBarSize = _vScrollBar.DesiredSize.X; var(sWidth, sHeight) = finalSize; try { // Suppress events to avoid weird recursion. _suppressScrollValueChanged = true; if (sHeight < cHeight) { sWidth -= vBarSize; } if (sHeight < cHeight) { _vScrollBar.Visible = true; _vScrollBar.Page = sHeight; _vScrollBar.MaxValue = cHeight; } else { _vScrollBar.Visible = false; } } finally { _suppressScrollValueChanged = false; } if (_vScrollBar.Visible) { _vScrollBar.Arrange(UIBox2.FromDimensions(Vector2.Zero, finalSize)); } #endregion #region Rebuild Children /* * Example: * * var _itemHeight = 32; * var separation = 3; * 32 | 32 | Control.Size.Y 0 * 35 | 3 | Padding * 67 | 32 | Control.Size.Y 1 * 70 | 3 | Padding * 102 | 32 | Control.Size.Y 2 * 105 | 3 | Padding * 137 | 32 | Control.Size.Y 3 * * If viewport height is 60 * visible should be 2 items (start = 0, end = 1) * * scroll.Y = 11 * visible should be 3 items (start = 0, end = 2) * * start expected: 11 (item: 0) * var start = (int) (scroll.Y * * if (scroll == 32) then { start = 1 } * var start = (int) (scroll.Y + separation / (_itemHeight + separation)); * var start = (int) (32 + 3 / (32 + 3)); * var start = (int) (35 / 35); * var start = (int) (1); * * scroll = 0, height = 36 * if (scroll + height == 36) then { end = 2 } * var end = (int) Math.Ceiling(scroll.Y + height / (_itemHeight + separation)); * var end = (int) Math.Ceiling(0 + 36 / (32 + 3)); * var end = (int) Math.Ceiling(36 / 35); * var end = (int) Math.Ceiling(1.02857); * var end = (int) 2; * */ var scroll = GetScrollValue(); var oldTopIndex = _topIndex; _topIndex = (int)((scroll.Y + separation) / (_itemHeight + separation)); if (_topIndex != oldTopIndex) { _updateChildren = true; } var oldBottomIndex = _bottomIndex; _bottomIndex = (int)Math.Ceiling((scroll.Y + Height) / (_itemHeight + separation)); _bottomIndex = Math.Min(_bottomIndex, _count); if (_bottomIndex != oldBottomIndex) { _updateChildren = true; } // When scrolling only rebuild visible list when a new item should be visible if (_updateChildren) { _updateChildren = false; foreach (var child in Children.ToArray()) { if (child == _vScrollBar) { continue; } RemoveChild(child); } if (_entityUids != null) { for (var i = _topIndex; i < _bottomIndex; i++) { var entity = _entityUids[i]; var button = new EntityContainerButton(entity); button.OnPressed += OnItemPressed; GenerateItem?.Invoke(entity, button); AddChild(button); } } _vScrollBar.SetPositionLast(); } #endregion #region Layout Children // Use pixel position var pixelWidth = (int)(sWidth * UIScale); var offset = (int)-((scroll.Y - _topIndex * (_itemHeight + separation)) * UIScale); var first = true; foreach (var child in Children) { if (child == _vScrollBar) { continue; } if (!first) { offset += separation; } first = false; var size = child.DesiredPixelSize.Y; var targetBox = new UIBox2i(0, offset, pixelWidth, offset + size); child.ArrangePixel(targetBox); offset += size; } #endregion return(finalSize); }