private Uno.UI.IndexPath?GetDecrementedItemIndex(Uno.UI.IndexPath currentItem) { if (!IsGrouping) { if (currentItem.Section > 0) { throw new InvalidOperationException("Received an index with non-zero group, but source is not grouped."); } if (currentItem.Row == 0) { return(null); } return(Uno.UI.IndexPath.FromRowSection(currentItem.Row - 1, 0)); } if (currentItem.Row == 0) { var nextSection = GetNextNonEmptySection(currentItem.Section, -1); if (!nextSection.HasValue) { //No previous non-empty sections return(null); } return(Uno.UI.IndexPath.FromRowSection(GetDisplayGroupCount(nextSection.Value) - 1, nextSection.Value)); } return(Uno.UI.IndexPath.FromRowSection(currentItem.Row - 1, currentItem.Section)); }
protected override Line CreateLine(GeneratorDirection direction, int extentOffset, int breadthOffset, int availableBreadth, RecyclerView.Recycler recycler, RecyclerView.State state, Uno.UI.IndexPath nextVisibleItem, bool isNewGroup ) { var item = GetFlatItemIndex(nextVisibleItem); var view = recycler.GetViewForPosition(item, state); if (!(view is SelectorItem)) { throw new InvalidOperationException($"Expected {nameof(SelectorItem)} but received {view?.GetType().ToString() ?? "<null>"}"); } var size = AddViewAtOffset(view, direction, extentOffset, breadthOffset, availableBreadth); var physicalSize = size.LogicalToPhysicalPixels(); var breadth = (int)(ScrollOrientation == Orientation.Vertical ? physicalSize.Width : physicalSize.Height); return(new Line { NumberOfViews = 1, Extent = (int)(ScrollOrientation == Orientation.Vertical ? physicalSize.Height : physicalSize.Width), FirstItem = nextVisibleItem, LastItem = nextVisibleItem, Breadth = breadth }); }
public CollectionChangedOperation(Uno.UI.IndexPath startingIndex, int range, NotifyCollectionChangedAction action, Element elementType) { StartingIndex = startingIndex; Range = range; Action = action; ElementType = elementType; }
private Uno.UI.IndexPath? GetIncrementedItemIndex(Uno.UI.IndexPath currentItem) { if (!IsGrouping) { if (currentItem.Section > 0) { throw new InvalidOperationException("Received an index with non-zero group, but source is not grouped."); } if (currentItem.Row == NumberOfItems - 1) { return null; } return Uno.UI.IndexPath.FromRowSection(currentItem.Row + 1, 0); } if (currentItem.Row == GetDisplayGroupCount(currentItem.Section) - 1) { var nextSection = GetNextNonEmptySection(currentItem.Section, 1); if (!nextSection.HasValue) { //No more non-empty sections return null; } return Uno.UI.IndexPath.FromRowSection(0, nextSection.Value); } return Uno.UI.IndexPath.FromRowSection(currentItem.Row + 1, currentItem.Section); }
public Line(FrameworkElement[] containerViews, Uno.UI.IndexPath firstItem, Uno.UI.IndexPath lastItem, int firstItemFlat) { if (containerViews.Length == 0) { throw new InvalidOperationException("Line must contain at least one view"); } ContainerViews = containerViews; FirstItem = firstItem; LastItem = lastItem; FirstItemFlat = firstItemFlat; }
/// <summary> /// Gets a flattened item index. Unlike <see cref="GetDisplayIndexFromIndexPath(IndexPath)"/>, this can not be overridden to adjust for /// supplementary elements (eg headers) on derived controls. This represents the (flattened) index in the data source as opposed /// to the 'display' index. /// /// Note that the <see cref="IndexPath"/> is still the 'display' value in that it doesn't 'know about' empty groups if HidesIfEmpty is set to true. /// </summary> internal int GetIndexFromIndexPath(Uno.UI.IndexPath indexPath) { if (indexPath.Section == 0) { return indexPath.Row; } var itemsInPrecedingSections = 0; for (int i = 0; i < indexPath.Section; i++) { itemsInPrecedingSections += GetDisplayGroupCount(i); } return itemsInPrecedingSections + indexPath.Row; }
/// <summary> /// Apply the offset to a collection index resulting from this collection operation. /// </summary> /// <param name="indexPath">The index in the collection prior to the operation</param> /// <returns>The offset position, or null if this position is no longer valid (ie because it has been removed by the operation).</returns> public Uno.UI.IndexPath?Offset(Uno.UI.IndexPath indexPath) { var section = indexPath.Section; var row = indexPath.Row; switch (this) { case var itemAdd when itemAdd.ElementType == CollectionChangedOperation.Element.Item && itemAdd.Action == NotifyCollectionChangedAction.Add && itemAdd.StartingIndex.Section == section && itemAdd.EndIndex.Row <= row: row += itemAdd.Range; break; case var itemRemove when itemRemove.ElementType == CollectionChangedOperation.Element.Item && itemRemove.Action == NotifyCollectionChangedAction.Remove && itemRemove.StartingIndex.Section == section && itemRemove.EndIndex.Row < row: row -= itemRemove.Range; break; case var thisItemRemoved when thisItemRemoved.ElementType == CollectionChangedOperation.Element.Item && (thisItemRemoved.Action == NotifyCollectionChangedAction.Remove || thisItemRemoved.Action == NotifyCollectionChangedAction.Replace) && thisItemRemoved.StartingIndex.Section == section && thisItemRemoved.StartingIndex.Row <= row && thisItemRemoved.EndIndex.Row >= row: // This item has been removed or replaced, the index is no longer valid return(null); // Group operations are currently unsupported case var groupAdd when groupAdd.ElementType == CollectionChangedOperation.Element.Group && groupAdd.Action == NotifyCollectionChangedAction.Add && groupAdd.EndIndex.Section <= section: case var groupRemove when groupRemove.ElementType == CollectionChangedOperation.Element.Group && groupRemove.Action == NotifyCollectionChangedAction.Remove && groupRemove.EndIndex.Section < section: case var thisGroupRemoved when thisGroupRemoved.ElementType == CollectionChangedOperation.Element.Group && (thisGroupRemoved.Action == NotifyCollectionChangedAction.Remove || thisGroupRemoved.Action == NotifyCollectionChangedAction.Replace) && thisGroupRemoved.StartingIndex.Section <= section && thisGroupRemoved.EndIndex.Section >= section: if (this.Log().IsEnabled(LogLevel.Warning)) { this.Log().LogWarning("Collection change not supported"); } break; } return(Uno.UI.IndexPath.FromRowSection(row, section)); }
/// <summary> /// Sequentially applies offsets to a collection index resulting from multiple collection operations. /// </summary> /// <param name="index">The index in the collection prior to the operation</param> /// <param name="collectionChanges">The changes to be applied, in order from oldest to newest.</param> /// <returns>The offset position, or null if this position is no longer valid (ie because it has been removed by one of the operations).</returns> public static Uno.UI.IndexPath?Offset(Uno.UI.IndexPath index, IEnumerable <CollectionChangedOperation> collectionChanges) { Uno.UI.IndexPath?newIndex = index; foreach (var change in collectionChanges) { if (newIndex is Uno.UI.IndexPath newIndexValue) { newIndex = change.Offset(newIndexValue); } else { break; } } return(newIndex); }
internal override int GetDisplayIndexFromIndexPath(Uno.UI.IndexPath indexPath) { var displayIndex = base.GetDisplayIndexFromIndexPath(indexPath); return(ConvertIndexToDisplayPosition(displayIndex)); }
protected override Line CreateLine(GeneratorDirection fillDirection, double extentOffset, double availableBreadth, Uno.UI.IndexPath nextVisibleItem) { var item = GetFlatItemIndex(nextVisibleItem); var view = Generator.DequeueViewForItem(item); AddView(view, fillDirection, extentOffset, 0); return(new Line(new[] { view }, nextVisibleItem, nextVisibleItem, item)); }
protected override Line CreateLine(GeneratorDirection fillDirection, double extentOffset, double availableBreadth, Uno.UI.IndexPath nextVisibleItem) { if (ShouldInsertReorderingView(extentOffset) && GetAndUpdateReorderingIndex() is { } reorderingIndex) { nextVisibleItem = reorderingIndex; } var item = GetFlatItemIndex(nextVisibleItem); var view = Generator.DequeueViewForItem(item); AddView(view, fillDirection, extentOffset, 0); return(new Line(item, (view, nextVisibleItem))); }
protected override Line CreateLine(GeneratorDirection direction, int extentOffset, int breadthOffset, int availableBreadth, RecyclerView.Recycler recycler, RecyclerView.State state, Uno.UI.IndexPath nextVisibleItem, bool isNewGroup ) { var itemsInLine = ResolveMaximumItemsInLine(availableBreadth); var firstItemInLine = nextVisibleItem; //Find first item in line, since the item we are passed is the last if (direction == GeneratorDirection.Backward) { // We are recreating the last line of the group - it may be truncated (if the total items are not an even multiple // of the items-per-line). if (isNewGroup) { itemsInLine = XamlParent.GetItemsOnLastLine(firstItemInLine.Section, itemsInLine); } for (int i = 0; i < itemsInLine - 1; i++) { firstItemInLine = GetNextUnmaterializedItem(GeneratorDirection.Backward, firstItemInLine).Value; var isCorrectGroup = firstItemInLine.Section == nextVisibleItem.Section; if (!isCorrectGroup) { //TODO: fix bug that makes this happen (#47229) } Debug.Assert(isCorrectGroup, GetAssertMessage("First item should not be from a different group")); } } Uno.UI.IndexPath lastItemInLine = firstItemInLine; Uno.UI.IndexPath?currentItem = firstItemInLine; var availableWidth = ResolveAvailableWidth(availableBreadth); var availableHeight = ResolveAvailableHeight(availableBreadth); int usedBreadth = 0; for (int i = 0; i < itemsInLine; i++) { var view = recycler.GetViewForPosition(GetFlatItemIndex(currentItem.Value), state); if (!(view is SelectorItem)) { throw new InvalidOperationException($"Expected {nameof(SelectorItem)} but received {view?.GetType().ToString() ?? "<null>"}"); } //Add view before we measure it, this ensures that DP inheritances are correctly applied AddView(view, direction); var slotSize = new Windows.Foundation.Size(availableWidth, availableHeight).PhysicalToLogicalPixels(); var measuredSize = _layouter.MeasureChild(view, slotSize); var physicalMeasuredSize = measuredSize.LogicalToPhysicalPixels(); var measuredWidth = (int)physicalMeasuredSize.Width; var measuredHeight = (int)physicalMeasuredSize.Height; if (_implicitItemWidth == null) { //Set these values to dimensions of first materialised item _implicitItemWidth = measuredWidth; _implicitItemHeight = measuredHeight; // When an item dimension is not fixed, we need to arrange based on the measured size, // otherwise the arrange will be passed a dimension that is too large and the first // few items will not be visible if (double.IsNaN(ItemWidth)) { slotSize.Width = ViewHelper.PhysicalToLogicalPixels(_implicitItemWidth.Value); } if (double.IsNaN(ItemHeight)) { slotSize.Height = ViewHelper.PhysicalToLogicalPixels(_implicitItemHeight.Value); } availableWidth = ResolveAvailableWidth(availableBreadth); availableHeight = ResolveAvailableHeight(availableBreadth); itemsInLine = ResolveMaximumItemsInLine(availableBreadth); } LayoutChild(view, GeneratorDirection.Forward, //We always lay out view 'top down' so that it is aligned correctly if its height is less than the line height direction == GeneratorDirection.Forward ? extentOffset : extentOffset - ResolveItemExtent().Value, breadthOffset + usedBreadth, slotSize ); usedBreadth += ResolveItemBreadth().Value; lastItemInLine = currentItem.Value; currentItem = GetNextUnmaterializedItem(GeneratorDirection.Forward, currentItem); if (currentItem == null || currentItem.Value.Section != firstItemInLine.Section) { itemsInLine = i + 1; break; } } return(new Line { NumberOfViews = itemsInLine, Extent = ResolveItemExtent().Value, Breadth = usedBreadth, FirstItem = firstItemInLine, LastItem = lastItemInLine }); }
/// <summary> /// Get flattened item index, if the source is grouped, to supply to <see cref="GetElementFromDisplayPosition(int)"/>. /// </summary> internal virtual int GetDisplayIndexFromIndexPath(Uno.UI.IndexPath indexPath) { return(GetIndexFromIndexPath(indexPath)); }
public bool Contains(Uno.UI.IndexPath index) => index >= FirstItem && index <= LastItem;