Skip to content

Commit

Permalink
RadioButtons layout account for available size. (#1719)
Browse files Browse the repository at this point in the history
* Change RadioButton's layout to account for available size when choosing the number of columns to display. Expand the layout test to accomodate.

* Adjust the test to account for differently sized radio buttons down level. add some comments.

* Update comment to make algorithm a little more clear, check for divide by 0
  • Loading branch information
StephenLPeters authored and jevansaks committed Dec 11, 2019
1 parent c653b8a commit 2f2d56f
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 19 deletions.
53 changes: 39 additions & 14 deletions dev/RadioButtons/ColumnMajorUniformToLargestGridLayout.cpp
Expand Up @@ -15,10 +15,6 @@ winrt::Size ColumnMajorUniformToLargestGridLayout::MeasureOverride(
{
if (auto const children = context.Children())
{
auto const maxColumns = std::max(1, MaxColumns());
MUX_ASSERT(maxColumns > 0);
auto const maxItemsPerColumn = static_cast<int>(std::ceil(static_cast<double>(children.Size()) / static_cast<double>(maxColumns)));

m_largestChildSize = [children, availableSize]()
{
auto largestChildWidth = 0.0f;
Expand All @@ -39,12 +35,11 @@ winrt::Size ColumnMajorUniformToLargestGridLayout::MeasureOverride(
return winrt::Size(largestChildWidth, largestChildHeight);
}();

auto const actualColumnCount = std::min(
static_cast<float>(maxColumns),
static_cast<float>(children.Size()));
m_actualColumnCount = CalculateColumns(children.Size(), m_largestChildSize.Width, availableSize.Width);
auto const maxItemsPerColumn = static_cast<int>(std::ceil(static_cast<double>(children.Size()) / static_cast<double>(m_actualColumnCount)));
return winrt::Size(
(m_largestChildSize.Width * actualColumnCount) +
(static_cast<float>(ColumnSpacing()) * (actualColumnCount - 1)),
(m_largestChildSize.Width * m_actualColumnCount) +
(static_cast<float>(ColumnSpacing()) * (m_actualColumnCount - 1)),
(m_largestChildSize.Height * maxItemsPerColumn) +
(static_cast<float>(RowSpacing()) * (maxItemsPerColumn - 1))
);
Expand All @@ -58,11 +53,9 @@ winrt::Size ColumnMajorUniformToLargestGridLayout::ArrangeOverride(
{
if (auto const children = context.Children())
{
auto const maxColumns = std::max(1, MaxColumns());
MUX_ASSERT(maxColumns > 0);
auto const itemCount = children.Size();
auto const minitemsPerColumn = static_cast<int>(std::floor(static_cast<double>(itemCount) / static_cast<double>(maxColumns)));
auto const numberOfColumnsWithExtraElements = static_cast<int>(itemCount % maxColumns);
auto const minitemsPerColumn = static_cast<int>(std::floor(static_cast<float>(itemCount) / m_actualColumnCount));
auto const numberOfColumnsWithExtraElements = static_cast<int>(itemCount % static_cast<int>(m_actualColumnCount));

auto const columnSpacing = static_cast<float>(ColumnSpacing());
auto const rowSpacing = static_cast<float>(RowSpacing());
Expand Down Expand Up @@ -138,6 +131,39 @@ void ColumnMajorUniformToLargestGridLayout::OnMaxColumnsPropertyChanged(const wi
InvalidateMeasure();
}

int ColumnMajorUniformToLargestGridLayout::CalculateColumns(int childCount, float maxItemWidth, float availableWidth)
{
/*
--------------------------------------------------------------
| |-----------|-----------| | widthNeededForExtraColumn |
| | |
| |------| |------| | ColumnSpacing |
| |----| |----| |----| | maxItemWidth |
| O RB O RB O RB | |
--------------------------------------------------------------
*/

// Every column execpt the first takes this ammount of space to fit on screen.
auto const widthNeededForExtraColumn = ColumnSpacing() + maxItemWidth;
// The number of columns from data and api ignoring available space
auto const requestedColumnCount = std::min(MaxColumns(), childCount);

// If columns can be added with effectively 0 extra space return as many columns as needed.
if (widthNeededForExtraColumn < std::numeric_limits<float>::epsilon())
{
return requestedColumnCount;
}

auto const extraWidthAfterFirstColumn = availableWidth - maxItemWidth;
auto const maxExtraColumns = std::max(0.0, std::floor(extraWidthAfterFirstColumn / widthNeededForExtraColumn));

// The smaller of number of columns from data and api and
// the number of columns the available space can support
auto const effectiveColumnCount = std::min(static_cast<double>(requestedColumnCount), maxExtraColumns + 1);
// return 1 even if there isn't any data
return std::max(1, static_cast<int>(effectiveColumnCount));
}

void ColumnMajorUniformToLargestGridLayout::ValidateGreaterThanZero(int value)
{
if (value <= 0)
Expand All @@ -147,7 +173,6 @@ void ColumnMajorUniformToLargestGridLayout::ValidateGreaterThanZero(int value)
}

//Testhooks helpers, only function while m_testHooksEnabled == true

void ColumnMajorUniformToLargestGridLayout::SetTestHooksEnabled(bool enabled)
{
m_testHooksEnabled = enabled;
Expand Down
2 changes: 2 additions & 0 deletions dev/RadioButtons/ColumnMajorUniformToLargestGridLayout.h
Expand Up @@ -37,6 +37,8 @@ class ColumnMajorUniformToLargestGridLayout :
void LayoutChanged(winrt::event_token const& token);

private:
int CalculateColumns(int childCount, float maxItemWidth, float availableWidth);
int m_actualColumnCount{ 1 };
winrt::Size m_largestChildSize{ 0,0 };

//Testhooks helpers, only function while m_testHooksEnabled == true
Expand Down
12 changes: 12 additions & 0 deletions dev/RadioButtons/InteractionTests/RadioButtonsTestPageElements.cs
Expand Up @@ -184,6 +184,18 @@ public ComboBox GetSourceComboBox()
}
private ComboBox SourceComboBox;

public Edit GetBorderWidthTextBox()
{
return GetElement(ref BorderWidthTextBox, "BorderWidthTextBox");
}
private Edit BorderWidthTextBox;

public Button GetSetBorderWidthButton()
{
return GetElement(ref SetBorderWidthButton, "SetBorderWidthButton");
}
private Button SetBorderWidthButton;

public Button GetFocusSelectedItemButton()
{
return GetElement(ref FocusSelectedItemButton, "FocusSelectedItemButton");
Expand Down
26 changes: 25 additions & 1 deletion dev/RadioButtons/InteractionTests/RadioButtonsTests.cs
Expand Up @@ -527,6 +527,7 @@ public void ColumnsTest()
SetItemType(type);
SetNumberOfColumns(1);
SetNumberOfItems(10);
SetBorderWidthToInf();

VerifyLayoutData(10, 1, 0);
SetNumberOfColumns(3);
Expand All @@ -538,10 +539,21 @@ public void ColumnsTest()
SetNumberOfColumns(10);
VerifyLayoutData(1, 10, 0);
SetNumberOfColumns(20);
VerifyLayoutData(0, 10, 10);
VerifyLayoutData(1, 10, 0);

SetNumberOfItems(77);
VerifyLayoutData(3, 20, 17);

SetBorderWidth(100);
VerifyLayoutData(77, 1, 0);
SetBorderWidth(200);
VerifyLayoutData(77, 1, 0);
SetBorderWidth(300);
VerifyLayoutData(38, 2, 1);
SetBorderWidth(500);
VerifyLayoutData(25, 3, 2);
SetBorderWidth(550);
VerifyLayoutData(19, 4, 1);
}
}
}
Expand Down Expand Up @@ -651,6 +663,18 @@ void SetNumberOfItems(int items)
elements.GetSetNumberOfItemsButton().Click();
}

void SetBorderWidth(float width)
{
elements.GetBorderWidthTextBox().SetValue(width.ToString());
elements.GetSetBorderWidthButton().Click();
}

void SetBorderWidthToInf()
{
elements.GetBorderWidthTextBox().SetValue("inf");
elements.GetSetBorderWidthButton().Click();
}

void SetSource(RadioButtonsSourceLocation location)
{
switch(location)
Expand Down
13 changes: 9 additions & 4 deletions dev/RadioButtons/TestUI/RadioButtonsPage.xaml
Expand Up @@ -22,7 +22,9 @@
<Grid Grid.Column="0" Margin="5" MinWidth="500" MaxWidth="800">
<ScrollViewer HorizontalScrollMode="Enabled" HorizontalScrollBarVisibility="Visible">
<StackPanel>
<controls:RadioButtons x:Name="TestRadioButtons" AutomationProperties.Name="TestRadioButtons" Header="I'm the header" SelectionChanged="TestRadioButtons_SelectionChanged" GotFocus="TestRadioButtons_GotFocus" LostFocus="TestRadioButtons_LostFocus"/>
<Border x:Name="TestRadioButtonsBorder" BorderBrush="Black" BorderThickness="1">
<controls:RadioButtons x:Name="TestRadioButtons" AutomationProperties.Name="TestRadioButtons" Header="I'm the header" SelectionChanged="TestRadioButtons_SelectionChanged" GotFocus="TestRadioButtons_GotFocus" LostFocus="TestRadioButtons_LostFocus"/>
</Border>
<controls:RadioButtons x:Name="SecondTestRadioButton" Header="A Second Radio Buttons, 0 selected">
<RadioButton x:Name="TheRadioButton">"I'm really a Radio Button"</RadioButton>
<x:String>I'm just a string</x:String>
Expand Down Expand Up @@ -68,6 +70,7 @@
<RowDefinition Height="Auto"/>
<!--20-->
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
Expand Down Expand Up @@ -111,9 +114,11 @@
<TextBlock Text="Checked:" HorizontalAlignment="Right" VerticalAlignment="Center" Grid.Row="16" Grid.Column="0"/>
<CheckBox x:Name="CustomCheckedCheckBox" AutomationProperties.Name="CustomCheckedCheckBox" HorizontalAlignment="Left" Grid.Row="16" Grid.Column="1"/>
<TextBlock Text="TestPage Utilities" HorizontalAlignment="Center" Foreground="Red" Grid.Row="17" Grid.Column="0" Grid.ColumnSpan="2"/>
<Button Content="Focus Selected Item" AutomationProperties.Name="FocusSelectedItemButton" Grid.Row="18" Grid.Column="0" Grid.ColumnSpan="2" Click="FocusSelectedItemButton_Clicked"/>
<Button Content="Select 5 then change source" AutomationProperties.Name="Select5ThenChangeSourceButton" Grid.Row="19" Grid.Column="0" Grid.ColumnSpan="2" Click="Select5ThenChangeSourceButton_Clicked"/>
<CheckBox x:Name="AllowInvalidValuesCheckBox" AutomationProperties.Name="AllowInvalidValuesCheckBox" Content="Allow Invalid Values" HorizontalAlignment="Center" IsChecked="False" IsEnabled="False" Grid.Row="20" Grid.Column="0" Grid.ColumnSpan="2" />
<TextBox x:Name="BorderWidthTextBox" AutomationProperties.Name="BorderWidthTextBox" Text="inf" Grid.Row="18" Grid.Column="0" HorizontalAlignment="Right"/>
<Button x:Name="SetBorderWidthButton" AutomationProperties.Name="SetBorderWidthButton" Content="Set" Grid.Row="18" Grid.Column="1" HorizontalAlignment="Left" Click="SetBorderWidthButton_Click"/>
<Button Content="Focus Selected Item" AutomationProperties.Name="FocusSelectedItemButton" Grid.Row="19" Grid.Column="0" Grid.ColumnSpan="2" Click="FocusSelectedItemButton_Clicked"/>
<Button Content="Select 5 then change source" AutomationProperties.Name="Select5ThenChangeSourceButton" Grid.Row="20" Grid.Column="0" Grid.ColumnSpan="2" Click="Select5ThenChangeSourceButton_Clicked"/>
<CheckBox x:Name="AllowInvalidValuesCheckBox" AutomationProperties.Name="AllowInvalidValuesCheckBox" Content="Allow Invalid Values" HorizontalAlignment="Center" IsChecked="False" IsEnabled="False" Grid.Row="21" Grid.Column="0" Grid.ColumnSpan="2" />
</Grid>
</ScrollViewer>
<ScrollViewer Margin="5">
Expand Down
21 changes: 21 additions & 0 deletions dev/RadioButtons/TestUI/RadioButtonsPage.xaml.cs
Expand Up @@ -242,6 +242,27 @@ private void FocusSelectedItemButton_Clicked(object sender, RoutedEventArgs e)
((Control)repeater.TryGetElement(TestRadioButtons.SelectedIndex)).Focus(FocusState.Keyboard);
}

private void SetBorderWidthButton_Click(object sender, RoutedEventArgs e)
{
if(this.BorderWidthTextBox.Text == "inf")
{
this.TestRadioButtonsBorder.Width = float.NaN;
this.BorderWidthTextBox.BorderBrush = new SolidColorBrush(Colors.Black);
}
else
{
if(float.TryParse(this.BorderWidthTextBox.Text, out float value))
{
this.TestRadioButtonsBorder.Width = value;
this.BorderWidthTextBox.BorderBrush = new SolidColorBrush(Colors.Black);
}
else
{
this.BorderWidthTextBox.BorderBrush = new SolidColorBrush(Colors.DarkRed);
}
}
}

private void Select5ThenChangeSourceButton_Clicked(object sender, RoutedEventArgs e)
{
TestRadioButtons.SelectedIndex = 5;
Expand Down

0 comments on commit 2f2d56f

Please sign in to comment.