/// <inheritdoc /> protected override Size ArrangeOverride(Size finalSize) { if (Children.Count > 0) { var parentMeasure = new UvMeasure(Orientation, finalSize.Width, finalSize.Height); var spacingMeasure = new UvMeasure(Orientation, HorizontalSpacing, VerticalSpacing); var paddingStart = new UvMeasure(Orientation, Padding.Left, Padding.Top); var paddingEnd = new UvMeasure(Orientation, Padding.Right, Padding.Bottom); var position = new UvMeasure(Orientation, Padding.Left, Padding.Top); double currentV = 0; void Arrange(UIElement child, bool isLast = false) { var desiredMeasure = new UvMeasure(Orientation, child.DesiredSize.Width, child.DesiredSize.Height); if (desiredMeasure.U == 0) { return; // if an item is collapsed, avoid adding the spacing } if ((desiredMeasure.U + position.U + paddingEnd.U) > parentMeasure.U) { // next row! position.U = paddingStart.U; position.V += currentV + spacingMeasure.V; currentV = 0; } // Stretch the last item to fill the available space if (isLast) { desiredMeasure.U = parentMeasure.U - position.U; } // place the item if (Orientation == Orientation.Horizontal) { child.Arrange(new Rect(position.U, position.V, desiredMeasure.U, desiredMeasure.V)); } else { child.Arrange(new Rect(position.V, position.U, desiredMeasure.V, desiredMeasure.U)); } // adjust the location for the next items position.U += desiredMeasure.U + spacingMeasure.U; currentV = Math.Max(desiredMeasure.V, currentV); } var lastIndex = Children.Count - 1; for (var i = 0; i < lastIndex; i++) { Arrange(Children[i]); } Arrange(Children[lastIndex], StretchChild == StretchChild.Last); } return(finalSize); }
/// <inheritdoc /> protected override Size ArrangeOverride(Size finalSize) { var parentMeasure = new UvMeasure(Orientation, finalSize.Width, finalSize.Height); var position = new UvMeasure(); double currentV = 0; foreach (var child in Children) { var desiredMeasure = new UvMeasure(Orientation, child.DesiredSize.Width, child.DesiredSize.Height); if ((desiredMeasure.U + position.U) > parentMeasure.U) { // next row! position.U = 0; position.V += currentV; currentV = 0; } // Place the item if (Orientation == Orientation.Horizontal) { child.Arrange(new Rect(position.U, position.V, child.DesiredSize.Width, child.DesiredSize.Height)); } else { child.Arrange(new Rect(position.V, position.U, child.DesiredSize.Width, child.DesiredSize.Height)); } // adjust the location for the next items position.U += desiredMeasure.U; currentV = Math.Max(desiredMeasure.V, currentV); } return(finalSize); }
/// <inheritdoc /> protected override Size MeasureOverride(Size availableSize) { availableSize.Width = availableSize.Width - Padding.Left - Padding.Right; availableSize.Height = availableSize.Height - Padding.Top - Padding.Bottom; var totalMeasure = UvMeasure.Zero; var parentMeasure = new UvMeasure(Orientation, availableSize.Width, availableSize.Height); var spacingMeasure = new UvMeasure(Orientation, HorizontalSpacing, VerticalSpacing); var lineMeasure = UvMeasure.Zero; foreach (var child in Children) { child.Measure(availableSize); var currentMeasure = new UvMeasure(Orientation, child.DesiredSize.Width, child.DesiredSize.Height); if (parentMeasure.U >= currentMeasure.U + lineMeasure.U + spacingMeasure.U) { lineMeasure.U += currentMeasure.U + spacingMeasure.U; lineMeasure.V = Math.Max(lineMeasure.V, currentMeasure.V); } else { // new line should be added // to get the max U to provide it correctly to ui width ex: ---| or -----| totalMeasure.U = Math.Max(lineMeasure.U, totalMeasure.U); totalMeasure.V += lineMeasure.V + spacingMeasure.V; // if the next new row still can handle more controls if (parentMeasure.U > currentMeasure.U) { // set lineMeasure initial values to the currentMeasure to be calculated later on the new loop lineMeasure = currentMeasure; } // the control will take one row alone else { // validate the new control measures totalMeasure.U = Math.Max(currentMeasure.U, totalMeasure.U); totalMeasure.V += currentMeasure.V; // add new empty line lineMeasure = UvMeasure.Zero; } } } // update value with the last line // if the the last loop is(parentMeasure.U > currentMeasure.U + lineMeasure.U) the total isn't calculated then calculate it // if the last loop is (parentMeasure.U > currentMeasure.U) the currentMeasure isn't added to the total so add it here // for the last condition it is zeros so adding it will make no difference // this way is faster than an if condition in every loop for checking the last item totalMeasure.U = Math.Max(lineMeasure.U, totalMeasure.U); totalMeasure.V += lineMeasure.V; totalMeasure.U = Math.Ceiling(totalMeasure.U); return(Orientation == Orientation.Horizontal ? new Size(totalMeasure.U, totalMeasure.V) : new Size(totalMeasure.V, totalMeasure.U)); }
public void Add(UvMeasure position, UvMeasure size) { ChildrenRects.Add(new UvRect { Position = position, Size = size }); Size = new UvMeasure { U = position.U + size.U, V = Math.Max(Size.V, size.V), }; }
private RowOrColumnSizeWithCount ComputeRowOrColumnSize(UvMeasure availableSize, int startIndex, int?measuredRowOrColumnCount) //Do not include padding. If measuredRowOrColumnCount not null, then it will use measuredRowOrColumnCounts as a tiebreaker in determining row or column allocation when within 1.1 view pixels of the max size. { if (startIndex < 0) { return(new RowOrColumnSizeWithCount() { Count = 0, Size = new UvMeasure(Orientation, 0, 0) }); } var spacingMeasure = new UvMeasure(Orientation, HorizontalSpacing, VerticalSpacing); var size = new UvMeasure(Orientation, 0, 0); int j = 0; int visibleCount = 0; for (var i = startIndex; i < Children.Count; i++) { var child = Children[i]; Visibility visibility = child.Visibility; if (visibility == Visibility.Collapsed) { j++; continue; } double spacingU = 0.0; if (visibleCount > 0) { spacingU = spacingMeasure.U; } var desiredMeasure = new UvMeasure(Orientation, child.DesiredSize.Width, child.DesiredSize.Height); var newSizeU = spacingU + desiredMeasure.U + size.U; if (visibleCount > 0 && size.U > 0 && ((measuredRowOrColumnCount != null && (newSizeU >= availableSize.U + 1.1 || (newSizeU + 1.1 > availableSize.U && j >= measuredRowOrColumnCount.Value))) || (measuredRowOrColumnCount == null && newSizeU > availableSize.U))) { break; } size.U = newSizeU; size.V = Math.Max(desiredMeasure.V, size.V); j++; visibleCount++; } return(new RowOrColumnSizeWithCount() { Count = j, VisibleCount = visibleCount, Size = size }); }
/// <inheritdoc /> protected override Size ArrangeOverride(Size finalSize) { var parentMeasure = new UvMeasure(Orientation, finalSize.Width, finalSize.Height); var spacingMeasure = new UvMeasure(Orientation, HorizontalSpacing, VerticalSpacing); var paddingStart = new UvMeasure(Orientation, Padding.Left, Padding.Top); var paddingEnd = new UvMeasure(Orientation, Padding.Right, Padding.Bottom); var position = new UvMeasure(Orientation, Padding.Left, Padding.Top); double currentV = 0; foreach (var child in Children) { var desiredMeasure = new UvMeasure(Orientation, child.DesiredSize.Width, child.DesiredSize.Height); if ((desiredMeasure.U + position.U + paddingEnd.U) > parentMeasure.U) { // next row! position.U = paddingStart.U; position.V += currentV + spacingMeasure.V; currentV = 0; } // Place the item if (Orientation == Orientation.Horizontal) { child.Arrange(new Rect(position.U, position.V, child.DesiredSize.Width, child.DesiredSize.Height)); } else { child.Arrange(new Rect(position.V, position.U, child.DesiredSize.Width, child.DesiredSize.Height)); } // adjust the location for the next items position.U += desiredMeasure.U + spacingMeasure.U; currentV = Math.Max(desiredMeasure.V, currentV); } return(finalSize); }
private TotalSizeWithCount ComputeTotalSize(UvMeasure availableSize, bool useMeasuredRowOrColumnCounts) //Do not include padding. If useMeasuredRowOrColumnCounts is true, then it will use measuredRowOrColumnCounts as a tiebreaker in determining row or column allocation when within 1.1 view pixels of the max size. { var useMeasuredCounts = useMeasuredRowOrColumnCounts && measuredRowOrColumnCounts != null && measuredRowOrColumnCounts.Aggregate(0, (acc, val) => acc + val) == Children.Count; List <int> rowOrColumnCounts = new List <int>(); int visibleCount = 0; var spacingMeasure = new UvMeasure(Orientation, HorizontalSpacing, VerticalSpacing); var size = new UvMeasure(Orientation, 0, 0); int i = 0; int j = 0; while (i < Children.Count) { int?measuredRowOrColumnCount = null; if (useMeasuredCounts && measuredRowOrColumnCounts != null && j < measuredRowOrColumnCounts.Count) { measuredRowOrColumnCount = measuredRowOrColumnCounts[j]; } var rowOrColumnSize = ComputeRowOrColumnSize(availableSize, i, measuredRowOrColumnCount); if (measuredRowOrColumnCount != null && rowOrColumnSize.Count != measuredRowOrColumnCount) { useMeasuredCounts = false; } size.U = Math.Max(size.U, rowOrColumnSize.Size.U); if (i > 0) { size.V += spacingMeasure.V; } size.V += rowOrColumnSize.Size.V; i += Math.Max(rowOrColumnSize.Count, 1); j++; rowOrColumnCounts.Add(rowOrColumnSize.Count); visibleCount += rowOrColumnSize.VisibleCount; } return(new TotalSizeWithCount() { RowOrColumnCounts = rowOrColumnCounts, VisibleCount = visibleCount, Size = size }); }
/// <summary> /// /// </summary> /// <param name="visibleTop"></param> /// <param name="visibleHeight"></param> /// <param name="parentWidth"></param> /// <param name="layoutChanged"></param> /// <param name="activeWindowScale"></param> /// <returns>Whether the range is updated</returns> bool UpdateActiveRange(double visibleTop, double visibleHeight, double parentWidth, bool layoutChanged, double activeWindowScale = 3) { var visibleCenter = visibleTop + visibleHeight / 2.0; var halfVisibleWindowsSize = (activeWindowScale / 2.0) * visibleHeight; var activeTop = visibleCenter - halfVisibleWindowsSize - RowHeight; var activeBottom = visibleCenter + halfVisibleWindowsSize; var oldFirst = FirstActive; var oldLast = LastActive; FirstActive = -1; if (ItemsSource is IList) { if ((ItemsSource as IList).Count != 0) { FirstActive = 0; } var position = new UvMeasure(); foreach (var child in ItemsSource as IList) { if (position.Y < activeTop) // Cannot see this row within active window { FirstActive = (ItemsSource as IList).IndexOf(child); } else // The FirstActive is found and confirmed { if (!layoutChanged && oldFirst == FirstActive) { // If the layout has not changed and we found that the top is unchanged, // We immedately conclude that bottom is not changed too return(false); } } var childImage = (child as IJustifiedWrapPanelItem); var childWidth = ScaledWidth(childImage, RowHeight); bool newRow = position.X + childWidth > parentWidth && (position.Y != 0 || position.X != 0); if (newRow) { // next row! position.X = childWidth; position.Y += RowHeight; if (position.Y > activeBottom) // Cannot see this row within active window { // Last row is the last active LastActive = (ItemsSource as IList).IndexOf(child) - 1; return(oldFirst != FirstActive || oldLast != LastActive); } } else { // adjust the location for the next items position.X += childWidth; } } LastActive = (ItemsSource as IList).Count - 1; } else { FirstActive = LastActive = -1; } return(oldFirst != FirstActive || oldLast != LastActive); }
protected override Size ArrangeOverride(Size finalSize) { var parentMeasure = new UvMeasure(Orientation, finalSize.Width, finalSize.Height); var spacingMeasure = new UvMeasure(Orientation, HorizontalSpacing, VerticalSpacing); var paddingStart = new UvMeasure(Orientation, Padding.Left, Padding.Top); var paddingEnd = new UvMeasure(Orientation, Padding.Right, Padding.Bottom); UvMeasure availableSize = parentMeasure; availableSize.U -= (paddingStart.U + paddingEnd.U); availableSize.V -= (paddingStart.V + paddingEnd.V); var position = new UvMeasure(Orientation, Padding.Left, Padding.Top); var totalSize = ComputeTotalSize(availableSize, true); double desiredV = new UvMeasure(Orientation, DesiredSize.Width, DesiredSize.Height).V; if (Orientation == Orientation.Horizontal) { if (VerticalContentAlignment == VerticalAlignment.Center || (VerticalContentAlignment == VerticalAlignment.Stretch && totalSize.VisibleCount <= 1)) { position.V = paddingStart.V + (parentMeasure.V - paddingStart.V - paddingEnd.V - totalSize.Size.V) / 2.0; } else if (VerticalContentAlignment == VerticalAlignment.Bottom) { position.V = paddingStart.V + (parentMeasure.V - paddingStart.V - paddingEnd.V - totalSize.Size.V); } else if (VerticalContentAlignment == VerticalAlignment.Stretch && totalSize.VisibleCount > 1) { spacingMeasure.V = (parentMeasure.V - paddingStart.V - paddingEnd.V - totalSize.Size.V) / ((double)(totalSize.VisibleCount - 1)); } } else { if (HorizontalContentAlignment == HorizontalAlignment.Center || (HorizontalContentAlignment == HorizontalAlignment.Stretch && totalSize.VisibleCount <= 1)) { position.V = paddingStart.V + (parentMeasure.V - paddingStart.V - paddingEnd.V - totalSize.Size.V) / 2.0; } else if (HorizontalContentAlignment == HorizontalAlignment.Right) { position.V = paddingStart.V + (parentMeasure.U - paddingStart.V - paddingEnd.V - totalSize.Size.V); } else if (HorizontalContentAlignment == HorizontalAlignment.Stretch && totalSize.VisibleCount > 1) { spacingMeasure.V = (parentMeasure.V - paddingStart.V - paddingEnd.V - totalSize.Size.V) / ((double)(totalSize.VisibleCount - 1)); } } int j = 0; int rowOrColumnCount = 0; RowOrColumnSizeWithCount rowOrColumnSize = new RowOrColumnSizeWithCount() { Count = 0, Size = new UvMeasure() }; var correctedSpacingMeasure = spacingMeasure; for (var i = 0; i < Children.Count; i++) { var child = Children[i]; Visibility visibility = child.Visibility; var desiredMeasure = visibility == Visibility.Visible ? new UvMeasure(Orientation, child.DesiredSize.Width, child.DesiredSize.Height) : new UvMeasure(Orientation, 0, 0); if (j >= rowOrColumnSize.Count) { correctedSpacingMeasure = spacingMeasure; if (i > 0) { position.V += rowOrColumnSize.Size.V + correctedSpacingMeasure.V; } position.U = paddingStart.U; rowOrColumnSize = ComputeRowOrColumnSize(availableSize, i, measuredRowOrColumnCounts != null && rowOrColumnCount < measuredRowOrColumnCounts.Count ? (int?)measuredRowOrColumnCounts[rowOrColumnCount] : null); rowOrColumnCount++; j = 0; if (Orientation == Orientation.Horizontal) { if (HorizontalContentAlignment == HorizontalAlignment.Center || (HorizontalContentAlignment == HorizontalAlignment.Stretch && rowOrColumnSize.VisibleCount <= 1)) { position.U = paddingStart.U + (parentMeasure.U - paddingStart.U - paddingEnd.U - rowOrColumnSize.Size.U) / 2.0; } else if (HorizontalContentAlignment == HorizontalAlignment.Right) { position.U = paddingStart.U + (parentMeasure.U - paddingStart.U - paddingEnd.U - rowOrColumnSize.Size.U); } else if (HorizontalContentAlignment == HorizontalAlignment.Stretch && rowOrColumnSize.VisibleCount > 1) { correctedSpacingMeasure.U = (parentMeasure.U - paddingStart.U - paddingEnd.U - rowOrColumnSize.Size.U) / ((double)(rowOrColumnSize.VisibleCount - 1)); } } else { if (VerticalContentAlignment == VerticalAlignment.Center || (VerticalContentAlignment == VerticalAlignment.Stretch && rowOrColumnSize.VisibleCount <= 1)) { position.U = paddingStart.U + (parentMeasure.U - paddingStart.U - paddingEnd.U - rowOrColumnSize.Size.U) / 2.0; } else if (VerticalContentAlignment == VerticalAlignment.Bottom) { position.U = paddingStart.U + (parentMeasure.U - paddingStart.U - paddingEnd.U - rowOrColumnSize.Size.U); } else if (VerticalContentAlignment == VerticalAlignment.Stretch && rowOrColumnSize.VisibleCount > 1) { correctedSpacingMeasure.U = (parentMeasure.U - paddingStart.U - paddingEnd.U - rowOrColumnSize.Size.U) / ((double)(rowOrColumnSize.VisibleCount - 1)); } } } var correctedPosition = position; var correctedMeasure = desiredMeasure; if (child is FrameworkElement frameworkChild) { if (Orientation == Orientation.Horizontal) { if (frameworkChild.VerticalAlignment == VerticalAlignment.Center) { correctedPosition.V += (rowOrColumnSize.Size.V - desiredMeasure.V) / 2.0; } else if (frameworkChild.VerticalAlignment == VerticalAlignment.Bottom) { correctedPosition.V += (rowOrColumnSize.Size.V - desiredMeasure.V); } else if (frameworkChild.VerticalAlignment == VerticalAlignment.Stretch) { correctedMeasure.V = rowOrColumnSize.Size.V; } } else { if (frameworkChild.HorizontalAlignment == HorizontalAlignment.Center || frameworkChild.HorizontalAlignment == HorizontalAlignment.Stretch) { correctedPosition.V += (rowOrColumnSize.Size.V - desiredMeasure.V) / 2.0; } else if (frameworkChild.HorizontalAlignment == HorizontalAlignment.Right) { correctedPosition.V += (rowOrColumnSize.Size.V - desiredMeasure.V); } else if (frameworkChild.HorizontalAlignment == HorizontalAlignment.Stretch) { correctedMeasure.V = rowOrColumnSize.Size.V; } } } child.Arrange(new Rect(correctedPosition.X, correctedPosition.Y, Math.Max(0.0, correctedMeasure.X), Math.Max(0.0, correctedMeasure.Y))); position.U += desiredMeasure.U; if (visibility != Visibility.Collapsed) { position.U += correctedSpacingMeasure.U; } j++; } return(finalSize); }
private Size UpdateRows(Size availableSize) { _rows.Clear(); var paddingStart = new UvMeasure(Orientation, Padding.Left, Padding.Top); var paddingEnd = new UvMeasure(Orientation, Padding.Right, Padding.Bottom); if (Children.Count == 0) { var emptySize = paddingStart.Add(paddingEnd).ToSize(Orientation); return(emptySize); } var parentMeasure = new UvMeasure(Orientation, availableSize.Width, availableSize.Height); var spacingMeasure = new UvMeasure(Orientation, HorizontalSpacing, VerticalSpacing); var position = new UvMeasure(Orientation, Padding.Left, Padding.Top); var currentRow = new Row(new List <UvRect>(), default); var finalMeasure = new UvMeasure(Orientation, width: 0.0, height: 0.0); void Arrange(UIElement child, bool isLast = false) { var desiredMeasure = new UvMeasure(Orientation, child.DesiredSize); if (desiredMeasure.U == 0) { return; // if an item is collapsed, avoid adding the spacing } if ((desiredMeasure.U + position.U + paddingEnd.U) > parentMeasure.U) { // next row! position.U = paddingStart.U; position.V += currentRow.Size.V + spacingMeasure.V; _rows.Add(currentRow); currentRow = new Row(new List <UvRect>(), default); } // Stretch the last item to fill the available space if (isLast) { desiredMeasure.U = parentMeasure.U - position.U; } currentRow.Add(position, desiredMeasure); // adjust the location for the next items position.U += desiredMeasure.U + spacingMeasure.U; finalMeasure.U = Math.Max(finalMeasure.U, position.U); } var lastIndex = Children.Count - 1; for (var i = 0; i < lastIndex; i++) { Arrange(Children[i]); } Arrange(Children[lastIndex], StretchChild == StretchChild.Last); if (currentRow.ChildrenRects.Count > 0) { _rows.Add(currentRow); } if (_rows.Count == 0) { var emptySize = paddingStart.Add(paddingEnd).ToSize(Orientation); return(emptySize); } // Get max V here before computing final rect var lastRowRect = _rows.Last().Rect; finalMeasure.V = lastRowRect.Position.V + lastRowRect.Size.V; var finalRect = finalMeasure.Add(paddingEnd).ToSize(Orientation); return(finalRect); }
/// <inheritdoc /> protected override Size MeasureOverride(Size availableSize) { availableSize.Width = availableSize.Width - Padding.Left - Padding.Right; availableSize.Height = availableSize.Height - Padding.Top - Padding.Bottom; var totalMeasure = UvMeasure.Zero; var parentMeasure = new UvMeasure(Orientation, availableSize.Width, availableSize.Height); var spacingMeasure = new UvMeasure(Orientation, HorizontalSpacing, VerticalSpacing); var lineMeasure = UvMeasure.Zero; void measure(UIElementCollection elementCollection) { foreach (UIElement child in elementCollection) { if (child is FrameworkElement fe && fe.TemplateChild is Panel nestedPanel) { measure(nestedPanel.Children); continue; } child.Measure(availableSize); var currentMeasure = new UvMeasure(Orientation, child.DesiredSize.Width, child.DesiredSize.Height); if (currentMeasure.U == 0) { continue; // ignore collapsed items } // if this is the first item, do not add spacing. Spacing is added to the "left" double uChange = lineMeasure.U == 0 ? currentMeasure.U : currentMeasure.U + spacingMeasure.U; if (parentMeasure.U >= uChange + lineMeasure.U) { lineMeasure.U += uChange; lineMeasure.V = Math.Max(lineMeasure.V, currentMeasure.V); } else { // new line should be added // to get the max U to provide it correctly to ui width ex: ---| or -----| totalMeasure.U = Math.Max(lineMeasure.U, totalMeasure.U); totalMeasure.V += lineMeasure.V + spacingMeasure.V; // if the next new row still can handle more controls if (parentMeasure.U > currentMeasure.U) { // set lineMeasure initial values to the currentMeasure to be calculated later on the new loop lineMeasure = currentMeasure; } // the control will take one row alone else { // validate the new control measures totalMeasure.U = Math.Max(currentMeasure.U, totalMeasure.U); totalMeasure.V += currentMeasure.V; // add new empty line lineMeasure = UvMeasure.Zero; } } } } measure(Children); // update value with the last line // if the the last loop is(parentMeasure.U > currentMeasure.U + lineMeasure.U) the total isn't calculated then calculate it // if the last loop is (parentMeasure.U > currentMeasure.U) the currentMeasure isn't added to the total so add it here // for the last condition it is zeros so adding it will make no difference // this way is faster than an if condition in every loop for checking the last item totalMeasure.U = Math.Max(lineMeasure.U, totalMeasure.U); totalMeasure.V += lineMeasure.V; totalMeasure.U = Math.Ceiling(totalMeasure.U); return(Orientation == Orientation.Horizontal ? new Size(totalMeasure.U, totalMeasure.V) : new Size(totalMeasure.V, totalMeasure.U)); }
public Row(List <UvRect> childrenRects, UvMeasure size) { ChildrenRects = childrenRects; Size = size; }
public UvMeasure Add(UvMeasure measure) => Add(measure.U, measure.V);