private ExtentInfo GetVerticalExtentInfo(Size viewPortSize) { if (_itemsControl == null) { return(new ExtentInfo()); } var extentHeight = Math.Max(TotalItems * ItemHeight, viewPortSize.Height); var maxVerticalOffset = extentHeight;// extentHeight - viewPortSize.Height; var verticalOffset = (StartIndex / (double)TotalItems) * maxVerticalOffset; var info = new ExtentInfo { VirtualCount = _itemsControl.Items.Count, TotalCount = TotalItems, Height = extentHeight, VerticalOffset = verticalOffset, MaxVerticalOffset = maxVerticalOffset }; return(info); }
private ItemLayoutInfo GetLayoutInfo(Size availableSize, double itemHeight, ExtentInfo extentInfo) { if (_itemsControl == null) { return(new ItemLayoutInfo()); } // we need to ensure that there is one realized item prior to the first visible item, and one after the last visible item, // so that keyboard navigation works properly. For example, when focus is on the first visible item, and the user // navigates up, the ListBox selects the previous item, and the scrolls that into view - and this triggers the loading of the rest of the items // in that row var firstVisibleLine = (int)Math.Floor(_offset.Y / itemHeight); var firstRealizedIndex = Math.Max(firstVisibleLine - 1, 0); var firstRealizedItemLeft = firstRealizedIndex * ItemWidth - HorizontalOffset; var firstRealizedItemTop = (firstRealizedIndex) * itemHeight - _offset.Y; var firstCompleteLineTop = (firstVisibleLine == 0 ? firstRealizedItemTop : firstRealizedItemTop + ItemHeight); var completeRealizedLines = (int)Math.Ceiling((availableSize.Height - firstCompleteLineTop) / itemHeight); var lastRealizedIndex = Math.Min(firstRealizedIndex + completeRealizedLines + 2, _itemsControl.Items.Count - 1); return(new ItemLayoutInfo(firstRealizedIndex, firstRealizedItemTop, firstRealizedItemLeft, lastRealizedIndex)); }
/// <summary> /// Updates the values in the x and y extent lists by the changing aabb values. /// </summary> private void UpdateExtentValues() { //NOTE: Comment.. No debug in release //Debug.Assert(_xInfoList.Count == _yInfoList.Count); for (int i = 0; i < _xInfoList.Count; i++) { ExtentInfo xInfo = _xInfoList[i]; ExtentInfo yInfo = _yInfoList[i]; //NOTE: Comment.. No debug in release //Debug.Assert(xInfo.geometry == yInfo.geometry); AABB aabb = xInfo.geometry.aabb; /*xInfo.min.value = aabb.min.X; * xInfo.max.value = aabb.max.X; * yInfo.min.value = aabb.min.Y; * yInfo.max.value = aabb.max.Y;*/ xInfo.min.value = aabb.min.X - fTol; xInfo.max.value = aabb.max.X + fTol; yInfo.min.value = aabb.min.Y - fTol; yInfo.max.value = aabb.max.Y + fTol; } }
private void EnsureScrollOffsetIsWithinConstrains(ExtentInfo extentInfo) { if (Orientation == Orientation.Horizontal) { _offset.Y = Clamp(_offset.Y, 0, extentInfo.MaxScrollOffset); } else { _offset.X = Clamp(_offset.X, 0, extentInfo.MaxScrollOffset); } }
private void CalculateHorizonalScrollInfo() { _extentInfo = GetExtentInfo(RenderSize); UpdateScrollInfo(RenderSize, _extentInfo); EnsureScrollOffsetIsWithinConstrains(_extentInfo); NotifyHorizonalScroll(_extentInfo); }
private void NotifyHorizonalScroll(ExtentInfo extentInfo) { var startCharacter = Math.Ceiling(_offset.X / CharacterWidth); //clamp when required if (startCharacter + extentInfo.MaximumChars > TotalCharacters) startCharacter = Math.Max(0, TotalCharacters - extentInfo.MaximumChars); HorizontalScrollChanged?.Invoke(new TextScrollInfo((int)startCharacter, (int)extentInfo.MaximumChars)); }
protected override Size MeasureOverride(Size availableSize) { if (_itemsControl == null) { return new Size(double.IsInfinity(availableSize.Width) ? 0 : availableSize.Width, double.IsInfinity(availableSize.Height) ? 0 : availableSize.Height); } _isInMeasure = true; _childLayouts.Clear(); _extentInfo = GetExtentInfo(availableSize); EnsureScrollOffsetIsWithinConstrains(_extentInfo); var layoutInfo = GetLayoutInfo(availableSize, ItemHeight, _extentInfo); RecycleItems(layoutInfo); // Determine where the first item is in relation to previously realized items var generatorStartPosition = _itemsGenerator.GeneratorPositionFromIndex(layoutInfo.FirstRealizedItemIndex); var visualIndex = 0; double widestWidth = 0; var currentX = 0;//layoutInfo.FirstRealizedItemLeft; var currentY = layoutInfo.FirstRealizedLineTop; ////1. Calc width, Call back available chars + first char //var width = TotalCharacters * CharacterWidth + 22; using (_itemsGenerator.StartAt(generatorStartPosition, GeneratorDirection.Forward, true)) { var children = new List<UIElement>(); for (var itemIndex = layoutInfo.FirstRealizedItemIndex; itemIndex <= layoutInfo.LastRealizedItemIndex; itemIndex++, visualIndex++) { bool newlyRealized; var child = (UIElement)_itemsGenerator.GenerateNext(out newlyRealized); if (child == null) continue; children.Add(child); SetVirtualItemIndex(child, itemIndex); if (newlyRealized) { InsertInternalChild(visualIndex, child); } else { // check if item needs to be moved into a new position in the Children collection if (visualIndex < Children.Count) { if (Equals(Children[visualIndex], child)) continue; var childCurrentIndex = Children.IndexOf(child); if (childCurrentIndex >= 0) { RemoveInternalChildRange(childCurrentIndex, 1); } InsertInternalChild(visualIndex, child); } else { // we know that the child can't already be in the children collection // because we've been inserting children in correct visualIndex order, // and this child has a visualIndex greater than the Children.Count AddInternalChild(child); } } } //part 2: do the measure foreach (var child in children) { //TODO: Widest = Chars + Additional space = 20 //[ideally should scroll from where the text begins] _itemsGenerator.PrepareItemContainer(child); child.Measure(new Size(_viewportSize.Width, ItemHeight)); widestWidth = Math.Max(widestWidth, child.DesiredSize.Width); } // Console.WriteLine("Widest={0} Calc={1}", widestWidth, width); //part 3: Create the elements foreach (var child in children) { _childLayouts.Add(child, new Rect(currentX, currentY, Math.Max(_viewportSize.Width, _viewportSize.Width), ItemHeight)); currentY += ItemHeight; } } RemoveRedundantChildren(); UpdateScrollInfo(availableSize, _extentInfo); //NotifyHorizonalScroll(_extentInfo); _isInMeasure = false; return new Size(double.IsInfinity(availableSize.Width) ? 0 : availableSize.Width, double.IsInfinity(availableSize.Height) ? 0 : availableSize.Height); }
public Extent(ExtentInfo info, float value, bool isMin) { Info = info; Value = value; IsMin = isMin; }
private void UpdateScrollInfo(Size availableSize, ExtentInfo extentInfo) { _viewportSize = availableSize; _extentSize = new Size(availableSize.Width, extentInfo.ExtentHeight); InvalidateScrollInfo(); }
private void EnsureScrollOffsetIsWithinConstrains(ExtentInfo extentInfo) { _offset.Y = Clamp(_offset.Y, 0, extentInfo.MaxVerticalOffset); }
private void UpdateScrollInfo(Size availableSize, ExtentInfo extentInfo) { _viewportSize = availableSize; _extentSize = Orientation == Orientation.Horizontal ? new Size(availableSize.Width, extentInfo.ExtentSize) : new Size(extentInfo.ExtentSize, availableSize.Height); InvalidateScrollInfo(); }
protected override Size MeasureOverride(Size availableSize) { if (itemsControl == null) { return(new Size(double.IsInfinity(availableSize.Width) ? 0 : availableSize.Width, double.IsInfinity(availableSize.Height) ? 0 : availableSize.Height)); } isInMeasure = true; childLayouts.Clear(); ExtentInfo extentInfo = GetExtentInfo(availableSize); EnsureScrollOffsetIsWithinConstrains(extentInfo); ItemLayoutInfo layoutInfo = GetLayoutInfo(availableSize, ItemHeight, extentInfo); RecycleItems(layoutInfo); // Determine where the first item is in relation to previously realized items GeneratorPosition generatorStartPosition = itemsGenerator.GeneratorPositionFromIndex(layoutInfo.FirstRealizedItemIndex); var visualIndex = 0; double currentX = layoutInfo.FirstRealizedItemLeft; double currentY = layoutInfo.FirstRealizedLineTop; using (itemsGenerator.StartAt(generatorStartPosition, GeneratorDirection.Forward, true)) { for (int itemIndex = layoutInfo.FirstRealizedItemIndex; itemIndex <= layoutInfo.LastRealizedItemIndex; itemIndex++, visualIndex++) { bool newlyRealized; var child = (UIElement)itemsGenerator.GenerateNext(out newlyRealized); SetVirtualItemIndex(child, itemIndex); if (newlyRealized) { InsertInternalChild(visualIndex, child); } else { // check if item needs to be moved into a new position in the Children collection if (visualIndex < Children.Count) { if (!Equals(Children[visualIndex], child)) { int childCurrentIndex = Children.IndexOf(child); if (childCurrentIndex >= 0) { RemoveInternalChildRange(childCurrentIndex, 1); } InsertInternalChild(visualIndex, child); } } else { // we know that the child can't already be in the children collection // because we've been inserting children in correct visualIndex order, // and this child has a visualIndex greater than the Children.Count AddInternalChild(child); } } // only prepare the item once it has been added to the visual tree itemsGenerator.PrepareItemContainer(child); child.Measure(new Size(ItemWidth, ItemHeight)); childLayouts.Add(child, new Rect(currentX, currentY, ItemWidth, ItemHeight)); if (currentX + ItemWidth * 2 >= availableSize.Width) { // wrap to a new line currentY += ItemHeight; currentX = 0; } else { currentX += ItemWidth; } } } RemoveRedundantChildren(); UpdateScrollInfo(availableSize, extentInfo); var desiredSize = new Size(double.IsInfinity(availableSize.Width) ? 0 : availableSize.Width, double.IsInfinity(availableSize.Height) ? 0 : availableSize.Height); isInMeasure = false; return(desiredSize); }
/// <summary> /// Incrementally inserts the min/max extents into the ExtentList. As it /// does so, the method ensures that overlap records, the collisionpair /// map, and all other book-keeping is up todate. /// </summary> /// <param name="ourInfo">The extent info for a give axis</param> public void IncrementalInsertExtent(ExtentInfo ourInfo) { Extent min = ourInfo.min; Extent max = ourInfo.max; Debug.Assert(min.value < max.value); int iMin = InsertIntoSortedList(min); int iMax = InsertIntoSortedList(max); Geom ourGeom = ourInfo.geometry; // As this is a newly inserted extent, we need to update the overlap // information. // RULE 1: Traverse from min to max. Look for other "min" Extents // and when found, add our wrapper/geometry to their list. int iCurr = iMin + 1; while (iCurr != iMax) { if (this[iCurr].isMin) { this[iCurr].info.underConsideration.Add(ourGeom); } iCurr++; } // RULE 2: From min, traverse to the left until we encounter // another "min" extent. If we find one, we add its geometry // to our underConsideration list and go to RULE 3, otherwise // there is no more work to do and we can exit. iCurr = iMin - 1; while (iCurr >= 0 && this[iCurr].isMin == false) { iCurr--; } if (iCurr < 0) { return; } List <Geom> ourUnderConsideration = ourInfo.underConsideration; Extent currExtent = this[iCurr]; ourUnderConsideration.Add(currExtent.info.geometry); // RULE 3: Now that we have found a "min" extent, we take // its existing overlap list and copy it into our underConsideration // list. All except for ourselves. ourUnderConsideration.AddRange(currExtent.info.underConsideration); ourUnderConsideration.Remove(ourGeom); // just in case /*LinkedListNode<Geom> currGeomNode = * currExtent.info.underConsideration.First; * * while (currGeomNode != null) * { * if (currGeomNode.Value != ourGeom) * { * ourUnderConsideration.AddLast(new LinkedListNode<Geom>( * currGeomNode.Value)); * } * currGeomNode = currGeomNode.Next; * }*/ // RULE 4: Move from the found extent back toward our "min" extent. // Whenever and "max" extent is found, we remove its reference // from our extents list. while (iCurr != iMin) { if (currExtent.isMin == false) { ourUnderConsideration.Remove(currExtent.info.geometry); if (ourInfo.overlaps.Remove(currExtent.info.geometry)) { owner.collisionPairs.RemovePair(ourGeom, currExtent.info.geometry); } } currExtent = this[++iCurr]; } }
public Extent(ExtentInfo info, float value, bool isMin) { this.info = info; this.value = value; this.isMin = isMin; }
protected override Size MeasureOverride(Size availableSize) { if (_itemsControl == null) { return(new Size(double.IsInfinity(availableSize.Width) ? 0 : availableSize.Width, double.IsInfinity(availableSize.Height) ? 0 : availableSize.Height)); } _isInMeasure = true; _childLayouts.Clear(); _extentInfo = GetExtentInfo(availableSize); EnsureScrollOffsetIsWithinConstrains(_extentInfo); var layoutInfo = GetLayoutInfo(availableSize, ItemHeight, _extentInfo); RecycleItems(layoutInfo); // Determine where the first item is in relation to previously realized items var generatorStartPosition = _itemsGenerator.GeneratorPositionFromIndex(layoutInfo.FirstRealizedItemIndex); var visualIndex = 0; double widestWidth = 0; var currentX = 0;//layoutInfo.FirstRealizedItemLeft; var currentY = layoutInfo.FirstRealizedLineTop; ////1. Calc width, Call back available chars + first char //var width = TotalCharacters * CharacterWidth + 22; using (_itemsGenerator.StartAt(generatorStartPosition, GeneratorDirection.Forward, true)) { var children = new List <UIElement>(); for (var itemIndex = layoutInfo.FirstRealizedItemIndex; itemIndex <= layoutInfo.LastRealizedItemIndex; itemIndex++, visualIndex++) { bool newlyRealized; var child = (UIElement)_itemsGenerator.GenerateNext(out newlyRealized); if (child == null) { continue; } children.Add(child); SetVirtualItemIndex(child, itemIndex); if (newlyRealized) { InsertInternalChild(visualIndex, child); } else { // check if item needs to be moved into a new position in the Children collection if (visualIndex < Children.Count) { if (Equals(Children[visualIndex], child)) { continue; } var childCurrentIndex = Children.IndexOf(child); if (childCurrentIndex >= 0) { RemoveInternalChildRange(childCurrentIndex, 1); } InsertInternalChild(visualIndex, child); } else { // we know that the child can't already be in the children collection // because we've been inserting children in correct visualIndex order, // and this child has a visualIndex greater than the Children.Count AddInternalChild(child); } } } //part 2: do the measure foreach (var child in children) { //TODO: Widest = Chars + Additional space = 20 //[ideally should scroll from where the text begins] _itemsGenerator.PrepareItemContainer(child); child.Measure(new Size(_viewportSize.Width, ItemHeight)); widestWidth = Math.Max(widestWidth, child.DesiredSize.Width); } // Console.WriteLine("Widest={0} Calc={1}", widestWidth, width); //part 3: Create the elements foreach (var child in children) { _childLayouts.Add(child, new Rect(currentX, currentY, Math.Max(_viewportSize.Width, _viewportSize.Width), ItemHeight)); currentY += ItemHeight; } } RemoveRedundantChildren(); UpdateScrollInfo(availableSize, _extentInfo); //NotifyHorizonalScroll(_extentInfo); _isInMeasure = false; return(new Size(double.IsInfinity(availableSize.Width) ? 0 : availableSize.Width, double.IsInfinity(availableSize.Height) ? 0 : availableSize.Height)); }
private ItemLayoutInfo GetLayoutInfo(Size availableSize, double itemWidth, double itemHeight, ExtentInfo extentInfo) { if (_itemsControl == null) { return(new ItemLayoutInfo()); } // we need to ensure that there is one realized item prior to the first visible item, and one after the last visible item, // so that keyboard navigation works properly. For example, when focus is on the first visible item, and the user // navigates up, the ListBox selects the previous item, and the scrolls that into view - and this triggers the loading of the rest of the items // in that row if (Orientation == Orientation.Horizontal) { var firstVisibleLine = (int)Math.Floor(VerticalOffset / itemHeight); var firstRealizedIndex = Math.Max(extentInfo.ItemsPerRow * firstVisibleLine - 1, 0); var firstRealizedItemLeft = firstRealizedIndex % extentInfo.ItemsPerRow * itemWidth - HorizontalOffset; var firstRealizedItemTop = Math.Floor((double)firstRealizedIndex / extentInfo.ItemsPerRow) * itemHeight - VerticalOffset; var firstCompleteLineTop = (firstVisibleLine == 0 ? firstRealizedItemTop : firstRealizedItemTop + itemHeight); var completeRealizedLines = (int)Math.Ceiling((availableSize.Height - firstCompleteLineTop) / itemHeight); var lastRealizedIndex = Math.Min(firstRealizedIndex + completeRealizedLines * extentInfo.ItemsPerRow + 2, _itemsControl.Items.Count - 1); return(new ItemLayoutInfo(firstRealizedIndex, firstRealizedItemTop, firstRealizedItemLeft, lastRealizedIndex)); } else { var firstVisibleColumn = (int)Math.Floor(HorizontalOffset / itemWidth); var firstRealizedIndex = Math.Max(extentInfo.ItemsPerRow * firstVisibleColumn - 1, 0); var firstRealizedItemTop = firstRealizedIndex % extentInfo.ItemsPerRow * itemHeight - VerticalOffset; var firstRealizedItemLeft = Math.Floor((double)firstRealizedIndex / extentInfo.ItemsPerRow) * itemWidth - HorizontalOffset; var firstCompleteColumnLeft = (firstVisibleColumn == 0 ? firstRealizedItemLeft : firstRealizedItemLeft + itemWidth); var completeRealizedColumns = (int)Math.Ceiling((availableSize.Width - firstCompleteColumnLeft) / itemWidth); var lastRealizedIndex = Math.Min(firstRealizedIndex + completeRealizedColumns * extentInfo.ItemsPerRow + 2, _itemsControl.Items.Count - 1); return(new ItemLayoutInfo(firstRealizedIndex, firstRealizedItemTop, firstRealizedItemLeft, lastRealizedIndex)); } }
private void UpdateScrollInfo(Size availableSize, ExtentInfo extentInfo, double actualWidth) { _viewportSize = availableSize; _extentSize = new Size(actualWidth, extentInfo.Height); InvalidateScrollInfo(); }
private ItemLayoutInfo GetLayoutInfo(Size availableSize, double itemHeight, ExtentInfo extentInfo) { if (_itemsControl == null) { return new ItemLayoutInfo(); } // we need to ensure that there is one realized item prior to the first visible item, and one after the last visible item, // so that keyboard navigation works properly. For example, when focus is on the first visible item, and the user // navigates up, the ListBox selects the previous item, and the scrolls that into view - and this triggers the loading of the rest of the items var firstVisibleLine = (int)Math.Floor(VerticalOffset / itemHeight); var firstRealizedIndex = Math.Max(extentInfo.ItemsPerLine * firstVisibleLine - 1, 0); var firstRealizedItemLeft = firstRealizedIndex % extentInfo.ItemsPerLine * ItemWidth - HorizontalOffset; var firstRealizedItemTop = (firstRealizedIndex / extentInfo.ItemsPerLine) * itemHeight - VerticalOffset; var firstCompleteLineTop = (firstVisibleLine == 0 ? firstRealizedItemTop : firstRealizedItemTop + ItemHeight); var completeRealizedLines = (int)Math.Ceiling((availableSize.Height - firstCompleteLineTop) / itemHeight); var lastRealizedIndex = Math.Min(firstRealizedIndex + completeRealizedLines * extentInfo.ItemsPerLine + 2, _itemsControl.Items.Count - 1); return new ItemLayoutInfo { FirstRealizedItemIndex = firstRealizedIndex, FirstRealizedItemLeft = firstRealizedItemLeft, FirstRealizedLineTop = firstRealizedItemTop, LastRealizedItemIndex = lastRealizedIndex, }; }
private ExtentInfo GetVerticalExtentInfo(Size viewPortSize) { if (_itemsControl == null) return new ExtentInfo(); var extentHeight = Math.Max(TotalItems * ItemHeight, viewPortSize.Height); var maxVerticalOffset = extentHeight;// extentHeight - viewPortSize.Height; var verticalOffset = (StartIndex /(double) TotalItems)*maxVerticalOffset; var info = new ExtentInfo { VirtualCount = _itemsControl.Items.Count, TotalCount = TotalItems, Height = extentHeight, VerticalOffset = verticalOffset, MaxVerticalOffset = maxVerticalOffset }; return info; }