private OrientedSize ProcessSpacingAndOverflow(double spacing, OrientedSize desiredOriented, OrientedSize availableOriented, out bool isHiddenChanged) { var childrenCount = Panel.Elements.Count; var orientation = Panel.Orientation; var target = availableOriented.Direct; isHiddenChanged = false; // Current length is greater than available and we have no possibility to stretch down -> mark elements as hidden var current = 0.0; var visible = 0.0; var hasHiddenChildren = false; var visibleChildrenCount = 0; if (desiredOriented.Direct.IsGreaterThan(availableOriented.Direct)) { var stretchOverflow = FlexElementCollection.Mount(FlexElements.Capacity); try { // Process Pinned Flexible for (var index = 0; index < childrenCount; index++) { var flexElement = FlexElements[index]; if (CanPinStretch(flexElement) == false) { continue; } flexElement.StretchDirection = FlexStretchDirection.Shrink; stretchOverflow.Add(flexElement); Panel.SetIsHidden(Panel.Elements[index], false); } // Process Pinned for (var index = 0; index < childrenCount; index++) { var child = Panel.Elements[index]; if (child.Visibility == Visibility.Collapsed) { continue; } var flexElement = FlexElements[index]; if (CanPin(flexElement) == false) { continue; } current += flexElement.ActualLength; current += spacing; visible = current; Panel.SetIsHidden(Panel.Elements[index], false); } // Process Hide for (var index = 0; index < childrenCount; index++) { var child = Panel.Elements[index]; if (child.Visibility == Visibility.Collapsed) { continue; } visibleChildrenCount++; var flexElement = FlexElements[index]; if (CanPin(flexElement)) { continue; } current += flexElement.ActualLength; if (CanHide(flexElement) == false) { isHiddenChanged |= Panel.GetIsHidden(child); Panel.SetIsHidden(child, false); current += spacing; visible = current; continue; } var isOverflowed = current.IsGreaterThan(target, XamlConstants.LayoutComparisonPrecision); current += spacing; if (isOverflowed == false) { visible = current; } isHiddenChanged |= Panel.GetIsHidden(child) != isOverflowed; hasHiddenChildren |= isOverflowed; Panel.SetIsHidden(child, isOverflowed); } if (visibleChildrenCount > 0) { visible = visible - spacing; } Panel.HasHiddenChildren = hasHiddenChildren; // Stretch Pinned if (visible.IsGreaterThan(availableOriented.Direct) && stretchOverflow.Count > 0) { var currentPinStretch = stretchOverflow.Actual; var pinStretchTarget = (currentPinStretch - (visible - availableOriented.Direct)).Clamp(0, availableOriented.Direct); var pinStretchDesired = stretchOverflow.Stretch(FlexStretch.Fill, pinStretchTarget, FlexDistributor.Equalizer); if (pinStretchDesired < currentPinStretch) { var pinStretchIndex = 0; for (var index = 0; index < childrenCount; index++) { var flexElement = FlexElements[index]; if (CanPinStretch(flexElement)) { FlexElements[index] = stretchOverflow[pinStretchIndex++].WithStretchDirection(FlexStretchDirection.Shrink).WithMaxLength(flexElement.ActualLength); } else { FlexElements[index] = FlexElements[index].WithStretchDirection(FlexStretchDirection.Shrink).WithShrinkPriority(short.MaxValue); } } FinalMeasureItems(availableOriented, spacing, true); return(OrientedSize.Create(orientation, availableOriented.Direct, desiredOriented.Indirect)); } } } finally { FlexElementCollection.Release(stretchOverflow); } return(OrientedSize.Create(orientation, visible.Clamp(0, availableOriented.Direct), desiredOriented.Indirect)); } for (var index = 0; index < childrenCount; index++) { var flexElement = FlexElements[index]; var child = Panel.Elements[index]; if (child.Visibility == Visibility.Collapsed) { continue; } visibleChildrenCount++; current += flexElement.ActualLength; current += spacing; Panel.SetIsHidden(Panel.Elements[index], false); } Panel.HasHiddenChildren = false; visible = Math.Max(0, current); if (visibleChildrenCount > 0) { visible = visible - spacing; } return(OrientedSize.Create(orientation, visible.Clamp(0, availableOriented.Direct), desiredOriented.Indirect)); }
private Size ArrangeCoreImpl(Size finalSize) { var flexPanel = Panel; var flexPanelEx = Panel as IFlexPanelEx; var allowMeasure = flexPanelEx?.AllowMeasureInArrange ?? false; var orientation = flexPanel.Orientation; var useLayoutRounding = flexPanel.UseLayoutRounding; var spacing = GetRoundSpacing(flexPanel.Spacing, useLayoutRounding); for (var index = 0; index < flexPanel.Elements.Count; index++) { FlexElements[index] = FlexElements[index].WithUIElement(flexPanel.Elements[index], orientation); } var currentFlexElements = FlexElementCollection.Mount(FlexElements.Capacity); try { FlexElements.CopyTo(currentFlexElements); while (true) { var nextArrangePass = false; var size = new OrientedSize(orientation); var spacingDelta = 0.0; var finalOriented = finalSize.AsOriented(orientation); var finalIndirect = finalOriented.Indirect; var currentPoint = new OrientedPoint(orientation); var childFinalOriented = new OrientedSize(orientation); // Stretch Stretch(currentFlexElements, spacing, finalOriented.Direct, true); for (var index = 0; index < flexPanel.Elements.Count; index++) { var child = flexPanel.Elements[index]; var flexElement = currentFlexElements[index]; if (child.Visibility == Visibility.Collapsed) { continue; } if (flexPanel.GetIsHidden(child)) { child.Arrange(XamlConstants.ZeroRect); flexElement.ActualLength = 0.0; currentFlexElements[index] = flexElement; continue; } var desiredOriented = child.DesiredSize.AsOriented(orientation); childFinalOriented.Direct = flexElement.ActualLength; childFinalOriented.Indirect = Math.Max(finalIndirect, desiredOriented.Indirect); // Arrange Child var rect = new Rect(XamlConstants.ZeroPoint, childFinalOriented.Size).Offset(currentPoint); if (useLayoutRounding) { rect = rect.LayoutRound(RoundingMode.MidPointFromZero); } if (_measureInfinite && allowMeasure && desiredOriented.Direct.IsGreaterThan(childFinalOriented.Direct)) { var remeasureOriented = desiredOriented; remeasureOriented.ChangeDirect(childFinalOriented.Direct); child.Measure(remeasureOriented.Size); } child.Arrange(rect); var arrangeSize = GetActualArrangeSize(child); if (arrangeSize.IsEmpty == false) { rect.Width = arrangeSize.Width; rect.Height = arrangeSize.Height; } var finalChildDirect = rect.Size().AsOriented(orientation).Direct; if (IsArrangeFixed(flexElement) == false && finalChildDirect.IsLessThan(childFinalOriented.Direct)) { var length = finalChildDirect; flexElement.SetLengths(length, length, length, length); currentFlexElements[index] = flexElement; nextArrangePass = true; break; } if (useLayoutRounding) { var rectSize = rect.Size().AsOriented(orientation); flexElement.ActualLength = rectSize.Direct; currentPoint.Direct = Math.Max(0, (currentPoint.Direct + rectSize.Direct + spacing).LayoutRound(orientation, RoundingMode.MidPointFromZero)); } else { var rectSize = rect.Size().AsOriented(orientation); flexElement.ActualLength = rectSize.Direct; currentPoint.Direct = Math.Max(0, currentPoint.Direct + rectSize.Direct + spacing); } currentFlexElements[index] = flexElement; spacingDelta += spacing; size = size.StackSize(childFinalOriented); } if (nextArrangePass) { continue; } if (spacingDelta.Equals(0.0) == false) { size.Direct = Math.Max(0, size.Direct + spacingDelta - spacing); } var result = finalSize; if (orientation == Orientation.Horizontal) { result.Width = flexPanel.ShouldFill(Orientation.Horizontal) ? finalSize.Width : Math.Min(finalSize.Width, size.Width); } else { result.Height = flexPanel.ShouldFill(Orientation.Vertical) ? finalSize.Height : Math.Min(finalSize.Height, size.Height); } return(result); } } finally { FlexElementCollection.Release(currentFlexElements); } }