Skip to content

Commit

Permalink
Fixed some strange edge cases
Browse files Browse the repository at this point in the history
  • Loading branch information
kmcginnes committed Jun 22, 2016
1 parent ab76e26 commit 05a3cbf
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 59 deletions.
33 changes: 32 additions & 1 deletion src/AutoGrid.Tests/describe_stack_panel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ class when_one_child_size_should_be_child_size : WithSubject<StackPanel>
Width = 100,
Height = 100
};
new System.Windows.Controls.StackPanel();
Subject.Children.Add(uiElement);
};

Expand Down Expand Up @@ -74,4 +73,36 @@ class as_vertical
It should_have_desired_size = () => Subject.DesiredSize.ShouldBeEquivalentTo(new Size(100, 300));
}
}

class when_weird_scenario : WithSubject<StackPanel>
{
Establish context = () =>
{
_innerLeft = new StackPanel();
_innerRight = new StackPanel();
StackPanel.SetFill(_innerLeft, StackPanelFill.Fill);
StackPanel.SetFill(_innerRight, StackPanelFill.Fill);
Subject.Children.Add(_innerLeft);
Subject.Children.Add(_innerRight);
Subject.Orientation = Orientation.Horizontal;
//var reallyLongText = Enumerable.Range(0, 150).
var uiElement = new Button
{
Content = "Foo"
};
var uiElement2 = new Button
{
Content = "Bar"
};
_innerLeft.Children.Add(uiElement);
_innerRight.Children.Add(uiElement2);
};

Because of = () => Subject.Arrange(new Rect(new Size(800, 19.96)));

It should_have_1 = () => _innerLeft.ActualWidth.ShouldBeEquivalentTo(400);
It should_have_2 = () => _innerRight.ActualWidth.ShouldBeEquivalentTo(400);
static StackPanel _innerLeft;
static StackPanel _innerRight;
}
}
141 changes: 83 additions & 58 deletions src/AutoGrid/StackPanel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,104 +11,119 @@ protected override Size MeasureOverride(Size constraint)
{
UIElementCollection children = InternalChildren;

double parentWidth = 0; // Our current required width due to children thus far.
double parentHeight = 0; // Our current required height due to children thus far.
double accumulatedWidth = 0; // Total width consumed by children.
double accumulatedHeight = 0; // Total height consumed by children.
double parentWidth = 0;
double parentHeight = 0;
double accumulatedWidth = 0;
double accumulatedHeight = 0;

var isHorizontal = Orientation == Orientation.Horizontal;
foreach (var child in children.OfType<UIElement>().Where(x => GetFill(x) == StackPanelFill.Auto))
var totalMarginToAdd = CalculateTotalMarginToAdd(children, isHorizontal, MarginBetweenChildren);

for (int i = 0; i < children.Count; i++)
{
Size childConstraint; // Contains the suggested input constraint for this child.
Size childDesiredSize; // Contains the return size from child measure.
UIElement child = children[i];

if (child == null) { continue; }

// Handle only the Auto's first to calculate remaining space for Fill's
if (GetFill(child) != StackPanelFill.Auto) { continue; }

// Child constraint is the remaining size; this is total size minus size consumed by previous children.
childConstraint = isHorizontal
? new Size(Math.Max(0.0, constraint.Width - accumulatedWidth),
Math.Max(accumulatedHeight, constraint.Height))
: new Size(Math.Max(accumulatedWidth, constraint.Width),
Math.Max(0.0, constraint.Height - accumulatedHeight));
var childConstraint = new Size(Math.Max(0.0, constraint.Width - accumulatedWidth),
Math.Max(0.0, constraint.Height - accumulatedHeight));

// Measure child.
child.Measure(childConstraint);
childDesiredSize = child.DesiredSize;
var childDesiredSize = child.DesiredSize;

if (isHorizontal)
{
accumulatedWidth += childDesiredSize.Width;
accumulatedHeight = Math.Max(accumulatedHeight, childDesiredSize.Height);
parentHeight = Math.Max(parentHeight, accumulatedHeight + childDesiredSize.Height);
}
else
{
accumulatedWidth = Math.Max(accumulatedWidth, childDesiredSize.Width);
parentWidth = Math.Max(parentWidth, accumulatedWidth + childDesiredSize.Width);
accumulatedHeight += childDesiredSize.Height;
}
}

var visibleChildrenCount = children
.OfType<UIElement>()
.Count(x => isHorizontal ? x.DesiredSize.Width != 0 : x.DesiredSize.Height != 0);
var marginMultiplier = Math.Max(visibleChildrenCount - 1, 0);
var marginToAdd = MarginBetweenChildren*marginMultiplier;

// Add all margin to accumulated size before calculating remaining space for
// Fill elements.
if (isHorizontal)
{
accumulatedWidth += marginToAdd;
accumulatedWidth += totalMarginToAdd;
}
else
{
accumulatedHeight += marginToAdd;
accumulatedHeight += totalMarginToAdd;
}

foreach (var child in children.OfType<UIElement>().Where(x => GetFill(x) == StackPanelFill.Fill))
var totalCountOfFillTypes = children
.OfType<UIElement>()
.Count(x => GetFill(x) == StackPanelFill.Fill
&& x.Visibility != Visibility.Collapsed);

var availableSpaceRemaining = isHorizontal
? Math.Max(0, constraint.Width - accumulatedWidth)
: Math.Max(0, constraint.Height - accumulatedHeight);

var eachFillTypeSize = totalCountOfFillTypes > 0
? availableSpaceRemaining / totalCountOfFillTypes
: 0;

for (int i = 0; i < children.Count; i++)
{
Size childConstraint; // Contains the suggested input constraint for this child.
Size childDesiredSize; // Contains the return size from child measure.

UIElement child = children[i];

if (child == null) { continue; }

// Handle all the Fill's giving them a portion of the remaining space
if (GetFill(child) != StackPanelFill.Fill) { continue; }

// Child constraint is the remaining size; this is total size minus size consumed by previous children.
childConstraint = isHorizontal
? new Size(Math.Max(0.0, constraint.Width - accumulatedWidth),
Math.Max(accumulatedHeight, constraint.Height))
: new Size(Math.Max(accumulatedWidth, constraint.Width),
Math.Max(0.0, constraint.Height - accumulatedHeight));
var childConstraint = isHorizontal
? new Size(eachFillTypeSize,
Math.Max(0.0, constraint.Height - accumulatedHeight))
: new Size(Math.Max(0.0, constraint.Width - accumulatedWidth),
eachFillTypeSize);

// Measure child.
child.Measure(childConstraint);
childDesiredSize = child.DesiredSize;
var childDesiredSize = child.DesiredSize;

if (isHorizontal)
{
accumulatedWidth += childDesiredSize.Width;
accumulatedHeight = Math.Max(accumulatedHeight, childDesiredSize.Height);
parentHeight = Math.Max(parentHeight, accumulatedHeight + childDesiredSize.Height);
}
else
{
accumulatedWidth = Math.Max(accumulatedWidth, childDesiredSize.Width);
parentWidth = Math.Max(parentWidth, accumulatedWidth + childDesiredSize.Width);
accumulatedHeight += childDesiredSize.Height;
}
}

// Make sure the final accumulated size is reflected in parentSize.
parentWidth = Math.Max(parentWidth, accumulatedWidth);
parentHeight = Math.Max(parentHeight, accumulatedHeight);
var parent = new Size(parentWidth, parentHeight);

return new Size(parentWidth, parentHeight);
return parent;
}

protected override Size ArrangeOverride(Size arrangeSize)
{
UIElementCollection children = InternalChildren;
int totalChildrenCount = children.Count;

double accumulatedLeft = 0;
double accumulatedTop = 0;

var isHorizontal = Orientation == Orientation.Horizontal;
var visibleChildrenCount = children
.OfType<UIElement>()
.Count(x => isHorizontal ? x.DesiredSize.Width != 0 : x.DesiredSize.Height != 0);
var marginMultiplier = Math.Max(visibleChildrenCount - 1, 0);
var totalMarginToAdd = MarginBetweenChildren * marginMultiplier;
var marginBetweenChildren = MarginBetweenChildren;

var totalMarginToAdd = CalculateTotalMarginToAdd(children, isHorizontal, marginBetweenChildren);

double allAutoSizedSum = 0.0;
int countOfFillTypes = 0;
Expand All @@ -117,7 +132,7 @@ protected override Size ArrangeOverride(Size arrangeSize)
var fillType = GetFill(child);
if (fillType == StackPanelFill.Fill)
{
if (isHorizontal ? child.DesiredSize.Width != 0 : child.DesiredSize.Height != 0)
if (child.Visibility != Visibility.Collapsed)
countOfFillTypes += 1;
}
else
Expand All @@ -137,17 +152,17 @@ protected override Size ArrangeOverride(Size arrangeSize)
UIElement child = children[i];
if (child == null) { continue; }
Size childDesiredSize = child.DesiredSize;
var isCollapsed = isHorizontal ? childDesiredSize.Width == 0 : childDesiredSize.Height == 0;
var isCollapsed = child.Visibility == Visibility.Collapsed;
var isLastChild = i == totalChildrenCount - 1;
var marginToAdd = isLastChild || isCollapsed ? 0 : MarginBetweenChildren;
var marginToAdd = isLastChild || isCollapsed ? 0 : marginBetweenChildren;
var fillType = GetFill(child);

Rect rcChild = new Rect(
accumulatedLeft,
accumulatedTop,
Math.Max(0.0, arrangeSize.Width - accumulatedLeft),
Math.Max(0.0, arrangeSize.Height - accumulatedTop));

if (isHorizontal)
{
rcChild.Width = fillType == StackPanelFill.Auto || isCollapsed ? childDesiredSize.Width : fillTypeSize;
Expand All @@ -167,37 +182,47 @@ protected override Size ArrangeOverride(Size arrangeSize)
return arrangeSize;
}

static double CalculateTotalMarginToAdd(UIElementCollection children, bool isHorizontal, double marginBetweenChildren)
{
var visibleChildrenCount = children
.OfType<UIElement>()
.Count(x => x.Visibility != Visibility.Collapsed);
var marginMultiplier = Math.Max(visibleChildrenCount - 1, 0);
var totalMarginToAdd = marginBetweenChildren * marginMultiplier;
return totalMarginToAdd;
}

public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register(
"Orientation", typeof(Orientation), typeof(StackPanel),
"Orientation", typeof(Orientation), typeof(StackPanel),
new FrameworkPropertyMetadata(
Orientation.Vertical,
FrameworkPropertyMetadataOptions.AffectsArrange |
Orientation.Vertical,
FrameworkPropertyMetadataOptions.AffectsArrange |
FrameworkPropertyMetadataOptions.AffectsMeasure));

public Orientation Orientation
{
get { return (Orientation) GetValue(OrientationProperty); }
get { return (Orientation)GetValue(OrientationProperty); }
set { SetValue(OrientationProperty, value); }
}

public static readonly DependencyProperty MarginBetweenChildrenProperty = DependencyProperty.Register(
"MarginBetweenChildren", typeof(double), typeof(StackPanel),
new FrameworkPropertyMetadata(
0.0,
FrameworkPropertyMetadataOptions.AffectsArrange |
0.0,
FrameworkPropertyMetadataOptions.AffectsArrange |
FrameworkPropertyMetadataOptions.AffectsMeasure));

public double MarginBetweenChildren
{
get { return (double) GetValue(MarginBetweenChildrenProperty); }
get { return (double)GetValue(MarginBetweenChildrenProperty); }
set { SetValue(MarginBetweenChildrenProperty, value); }
}

public static readonly DependencyProperty FillProperty = DependencyProperty.RegisterAttached(
"Fill", typeof(StackPanelFill), typeof(StackPanel),
"Fill", typeof(StackPanelFill), typeof(StackPanel),
new FrameworkPropertyMetadata(
StackPanelFill.Auto,
FrameworkPropertyMetadataOptions.AffectsArrange |
StackPanelFill.Auto,
FrameworkPropertyMetadataOptions.AffectsArrange |
FrameworkPropertyMetadataOptions.AffectsMeasure |
FrameworkPropertyMetadataOptions.AffectsParentArrange |
FrameworkPropertyMetadataOptions.AffectsParentMeasure));
Expand All @@ -209,7 +234,7 @@ public static void SetFill(DependencyObject element, StackPanelFill value)

public static StackPanelFill GetFill(DependencyObject element)
{
return (StackPanelFill) element.GetValue(FillProperty);
return (StackPanelFill)element.GetValue(FillProperty);
}
}

Expand Down

0 comments on commit 05a3cbf

Please sign in to comment.