Skip to content

Commit

Permalink
Adds CalculateDimension function to properly deal with dimension calc…
Browse files Browse the repository at this point in the history
…ulation

Adds the unit tests to check whether children has proper dimensions
  • Loading branch information
OlivierMiracle committed Aug 15, 2024
1 parent 2e0d2fb commit c35ea02
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 83 deletions.
28 changes: 22 additions & 6 deletions src/Dock.Avalonia/Controls/ProportionalStackPanel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -255,14 +255,14 @@ protected override Size MeasureOverride(Size constraint)
{
case Orientation.Horizontal:
{
var width = Math.Max(0, (constraint.Width - splitterThickness) * proportion);
var width = CalculateDimension(constraint.Width - splitterThickness, proportion, i);
var size = constraint.WithWidth(width);
control.Measure(size);
break;
}
case Orientation.Vertical:
{
var height = Math.Max(0, (constraint.Height - splitterThickness) * proportion);
var height = CalculateDimension(constraint.Height - splitterThickness, proportion, i);
var size = constraint.WithHeight(height);
control.Measure(size);
break;
Expand Down Expand Up @@ -299,7 +299,7 @@ protected override Size MeasureOverride(Size constraint)
}
else
{
usedWidth += Math.Max(0, (constraint.Width - splitterThickness) * proportion);
usedWidth += CalculateDimension(constraint.Width - splitterThickness, proportion, i);
}

break;
Expand All @@ -314,7 +314,7 @@ protected override Size MeasureOverride(Size constraint)
}
else
{
usedHeight += Math.Max(0, (constraint.Height - splitterThickness) * proportion);
usedHeight += CalculateDimension(constraint.Height - splitterThickness, proportion, i);
}

break;
Expand Down Expand Up @@ -397,7 +397,7 @@ protected override Size ArrangeOverride(Size arrangeSize)
else
{
Debug.Assert(!double.IsNaN(proportion));
var width = Math.Max(0, (arrangeSize.Width - splitterThickness) * proportion);
var width = CalculateDimension(arrangeSize.Width - splitterThickness, proportion, i);
remainingRect = remainingRect.WithWidth(width);
left += width;
}
Expand All @@ -414,7 +414,7 @@ protected override Size ArrangeOverride(Size arrangeSize)
else
{
Debug.Assert(!double.IsNaN(proportion));
var height = Math.Max(0, (arrangeSize.Height - splitterThickness) * proportion);
var height = CalculateDimension(arrangeSize.Height - splitterThickness, proportion, i);
remainingRect = remainingRect.WithHeight(height);
top += height;
}
Expand All @@ -430,6 +430,22 @@ protected override Size ArrangeOverride(Size arrangeSize)

return arrangeSize;
}

private double CalculateDimension(double dimension, double proportion, int childIndex)
{
var childDimension = dimension * proportion;
var flooredChildDimension = Math.Floor(childDimension);

// checks whether division doesn't leave a fraction
if (childDimension == flooredChildDimension)
return Math.Max(0, flooredChildDimension);
else
{
// if so, it assigns the divided pixel to the first control in proportional split
int isFirst = childIndex == 0 ? 1 : 0;
return Math.Max(0, flooredChildDimension + isFirst);
}
}

/// <inheritdoc/>
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
Expand Down
174 changes: 97 additions & 77 deletions tests/Dock.Avalonia.UnitTests/Controls/ProportionalStackPanelTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Layout;
Expand Down Expand Up @@ -26,12 +28,7 @@ public void Lays_Out_Children_Horizontal()
Width = 300,
Height = 100,
Orientation = Orientation.Horizontal,
Children =
{
new Border(),
new ProportionalStackPanelSplitter(),
new Border()
}
Children = { new Border(), new ProportionalStackPanelSplitter(), new Border() }
};

target.Measure(Size.Infinity);
Expand All @@ -51,12 +48,7 @@ public void Lays_Out_Children_Vertical()
Width = 100,
Height = 300,
Orientation = Orientation.Vertical,
Children =
{
new Border(),
new ProportionalStackPanelSplitter(),
new Border()
}
Children = { new Border(), new ProportionalStackPanelSplitter(), new Border() }
};

target.Measure(Size.Infinity);
Expand All @@ -68,6 +60,76 @@ public void Lays_Out_Children_Vertical()
Assert.Equal(new Rect(0, 152, 100, 148), target.Children[2].Bounds);
}

private static IEnumerable<object[]> GetBorderTestsData()
{
yield return [0.5, 604, 300, 300];
yield return [0.25, 604, 150, 450];
yield return [0.6283185307179586476925286766559, 604, 377, 223];
yield return [0.3141592653589793238462643383279, 604, 189, 411];
}

[Theory]
[MemberData(nameof(GetBorderTestsData))]
public void Should_Not_Trim_Borders_Horizontal(
double proportion,
double expectedWidth,
double expectedFirstChildHeight,
double expectedSecondChildHeight)
{
var target = new ProportionalStackPanel()
{
Width = expectedWidth,
Height = 100,
Orientation = Orientation.Horizontal,
Children =
{
new Border { [ProportionalStackPanel.ProportionProperty] = proportion },
new ProportionalStackPanelSplitter(),
new Border { [ProportionalStackPanel.ProportionProperty] = 1 - proportion }
}
};

target.Measure(Size.Infinity);
target.Arrange(new Rect(target.DesiredSize));

var width = target.Children.Sum(c => c.Bounds.Width);

Assert.Equal(expectedFirstChildHeight, target.Children[0].Bounds.Width);
Assert.Equal(expectedSecondChildHeight, target.Children[2].Bounds.Width);
Assert.Equal(expectedWidth, width);
}

[Theory]
[MemberData(nameof(GetBorderTestsData))]
public void Should_Not_Trim_Borders_Vertical(
double proportion,
double expectedHeight,
double expectedFirstChildHeight,
double expectedSecondChildHeight)
{
var target = new ProportionalStackPanel()
{
Width = 100,
Height = expectedHeight,
Orientation = Orientation.Vertical,
Children =
{
new Border { [ProportionalStackPanel.ProportionProperty] = proportion },
new ProportionalStackPanelSplitter(),
new Border { [ProportionalStackPanel.ProportionProperty] = 1 - proportion }
}
};

target.Measure(Size.Infinity);
target.Arrange(new Rect(target.DesiredSize));

var height = target.Children.Sum(c => c.Bounds.Height);

Assert.Equal(expectedFirstChildHeight, target.Children[0].Bounds.Height);
Assert.Equal(expectedSecondChildHeight, target.Children[2].Bounds.Height);
Assert.Equal(expectedHeight, height);
}

[Fact]
public void Lays_Out_Children_Default()
{
Expand All @@ -84,61 +146,38 @@ public void Lays_Out_Children_Default()
{
new Border()
{
Background = Brushes.Red,
[ProportionalStackPanel.ProportionProperty] = 0.5
Background = Brushes.Red, [ProportionalStackPanel.ProportionProperty] = 0.5
},
new ProportionalStackPanelSplitter(),
new Border()
{
Background = Brushes.Green
},
new Border() { Background = Brushes.Green },
new ProportionalStackPanelSplitter(),
new Border()
{
Background = Brushes.Blue
}
new Border() { Background = Brushes.Blue }
}
},
new ProportionalStackPanelSplitter(),
new ProportionalStackPanel()
{
Children =
{
new Border()
{
Background = Brushes.Blue,
},
new Border() { Background = Brushes.Blue, },
new ProportionalStackPanelSplitter(),
new Border()
{
Background = Brushes.Red
},
new Border() { Background = Brushes.Red },
new ProportionalStackPanelSplitter(),
new Border()
{
Background=Brushes.Green
}
new Border() { Background = Brushes.Green }
}
},
new ProportionalStackPanelSplitter(),
new ProportionalStackPanel()
{
Children =
{
new Border()
{
Background = Brushes.Green,
},
new Border() { Background = Brushes.Green, },
new ProportionalStackPanelSplitter(),
new Border()
{
Background = Brushes.Blue
},
new Border() { Background = Brushes.Blue },
new ProportionalStackPanelSplitter(),
new Border()
{
Background=Brushes.Red,
[ProportionalStackPanel.ProportionProperty] = 0.5
Background = Brushes.Red, [ProportionalStackPanel.ProportionProperty] = 0.5
}
}
},
Expand All @@ -163,49 +202,30 @@ public void Lays_Out_Children_ItemsControl()
{
Width = 1000,
Height = 500,
ItemsPanel = new ItemsPanelTemplate()
{
Content = new ProportionalStackPanel()
ItemsPanel =
new ItemsPanelTemplate()
{
Orientation = Orientation.Horizontal
}
},
Content = new ProportionalStackPanel() { Orientation = Orientation.Horizontal }
},
ItemsSource = new List<Control>()
{
new Border()
{
Background = Brushes.Green
},
new Border() { Background = Brushes.Green },
new ProportionalStackPanelSplitter(),
new Border()
{
Background = Brushes.Blue
},
new Border() { Background = Brushes.Blue },
new ProportionalStackPanelSplitter(),
new ItemsControl()
{
ItemsPanel = new ItemsPanelTemplate()
{
Content = new ProportionalStackPanel()
ItemsPanel =
new ItemsPanelTemplate()
{
Orientation = Orientation.Vertical,
}
},
Content = new ProportionalStackPanel() { Orientation = Orientation.Vertical, }
},
ItemsSource = new List<Control>()
{
new Border()
{
Background = Brushes.Green
},
new Border() { Background = Brushes.Green },
new ProportionalStackPanelSplitter(),
new Border()
{
Background = Brushes.Blue
},
new Border()
{
Background = Brushes.Red
}
new Border() { Background = Brushes.Blue },
new Border() { Background = Brushes.Red }
}
}
}
Expand Down

0 comments on commit c35ea02

Please sign in to comment.