Skip to content

Commit

Permalink
Added RatingBar.InvertDirection to support inverting "fill" direction (
Browse files Browse the repository at this point in the history
…#3116)

Adds RatingBar.InvertDirection which is then used in all calculations regarding value, fractional value, preview offsets (X and Y), and rating bar button gradient.
  • Loading branch information
nicolaihenriksen authored Mar 2, 2023
1 parent e3e5e7b commit 4590c59
Show file tree
Hide file tree
Showing 4 changed files with 213 additions and 51 deletions.
49 changes: 49 additions & 0 deletions MainDemo.Wpf/RatingBar.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,55 @@
VerticalAlignment="Top"
Text="{Binding ElementName=CustomRatingBarFractionalPreview, Path=Value, StringFormat=Rating: {0}}" />
</StackPanel>

<TextBlock Style="{StaticResource MaterialDesignHeadline5TextBlock}" Text="Rating bar with preview, fractional values, and InvertDirection=true" />

<StackPanel Margin="0,16,0,0" Orientation="Horizontal">
<smtx:XamlDisplay Margin="5,0,0,5"
VerticalContentAlignment="Top"
UniqueKey="fractionalPreviewRatingBar_3">
<materialDesign:RatingBar x:Name="BasicRatingBarFractionalPreview2"
IsPreviewValueEnabled="True"
Max="5"
Min="0"
InvertDirection="True"
ValueIncrements="0.25"
Value="0" />
</smtx:XamlDisplay>

<TextBlock Margin="10,2,0,0"
VerticalAlignment="Top"
Text="{Binding ElementName=BasicRatingBarFractionalPreview2, Path=Value, StringFormat=Rating: {0}}" />

<smtx:XamlDisplay Margin="24,0,0,5" UniqueKey="fractionalPreviewRatingBar_4">
<materialDesign:RatingBar x:Name="CustomRatingBarFractionalPreview2"
IsPreviewValueEnabled="True"
Max="3"
Min="0"
Orientation="Vertical"
InvertDirection="True"
ValueIncrements="0.25"
Value="2">
<materialDesign:RatingBar.ValueItemTemplate>
<DataTemplate DataType="system:Int32">
<Grid>
<materialDesign:PackIcon Width="24"
Height="24"
Kind="Heart" />
<TextBlock HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="8"
Foreground="{DynamicResource PrimaryHueMidForegroundBrush}"
Text="{Binding}" />
</Grid>
</DataTemplate>
</materialDesign:RatingBar.ValueItemTemplate>
</materialDesign:RatingBar>
</smtx:XamlDisplay>
<TextBlock Margin="10,2,0,0"
VerticalAlignment="Top"
Text="{Binding ElementName=CustomRatingBarFractionalPreview2, Path=Value, StringFormat=Rating: {0}}" />
</StackPanel>
</StackPanel>
</UserControl>

67 changes: 47 additions & 20 deletions MaterialDesignThemes.Wpf.Tests/RatingBarTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,29 @@ public void TextBlockForegroundConverter_ShouldReturnFractionalGradientStops_Whe
Assert.Equal(brush.Color.WithAlphaChannel(RatingBar.TextBlockForegroundConverter.SemiTransparent), stop2.Color);
}

[Fact]
public void TextBlockForegroundConverter_ShouldReturnFractionalGradientStops_WhenValueCovers10PercentOfButtonValueAndDirectionIsInverted()
{
// Arrange
SolidColorBrush brush = Brushes.Red;
IMultiValueConverter converter = RatingBar.TextBlockForegroundConverter.Instance;
object[] values = Arrange_TextBlockForegroundConverterValues(brush, value: 1.1, buttonValue: 2, invertDirection: true);

// Act
var result = converter.Convert(values, typeof(Brush), null, CultureInfo.CurrentCulture) as Brush;

// Assert
Assert.IsAssignableFrom<LinearGradientBrush>(result);
LinearGradientBrush resultBrush = (LinearGradientBrush)result!;
Assert.Equal(2, resultBrush.GradientStops.Count);
GradientStop stop1 = resultBrush.GradientStops[0];
GradientStop stop2 = resultBrush.GradientStops[1];
Assert.Equal(0.9, stop1.Offset, 10);
Assert.Equal(brush.Color.WithAlphaChannel(RatingBar.TextBlockForegroundConverter.SemiTransparent), stop1.Color);
Assert.Equal(0.9, stop2.Offset, 10);
Assert.Equal(brush.Color, stop2.Color);
}

[Fact]
public void TextBlockForegroundConverter_ShouldReturnFractionalGradientStops_WhenValueCovers42PercentOfButtonValue()
{
Expand Down Expand Up @@ -277,15 +300,15 @@ public void TextBlockForegroundConverter_ShouldReturnFractionalGradientStops_Whe
Assert.Equal(brush.Color.WithAlphaChannel(RatingBar.TextBlockForegroundConverter.SemiTransparent), stop2.Color);
}

private static object[] Arrange_TextBlockForegroundConverterValues(SolidColorBrush brush, double value, int buttonValue, Orientation orientation = Orientation.Horizontal) =>
new object[] { brush, orientation, value, buttonValue };
private static object[] Arrange_TextBlockForegroundConverterValues(SolidColorBrush brush, double value, int buttonValue, Orientation orientation = Orientation.Horizontal, bool invertDirection = false) =>
new object[] { brush, orientation, invertDirection, value, buttonValue };

[Fact]
public void PreviewIndicatorTransformXConverter_ShouldCenterPreviewIndicator_WhenFractionalValuesAreDisabledAndOrientationIsHorizontal()
{
// Arrange
IMultiValueConverter converter = RatingBar.PreviewIndicatorTransformXConverter.Instance;
object[] values = Arrange_PreviewIndicatorTransformXConverterValues(100, 20, Orientation.Horizontal, false, 1, 1);
object[] values = Arrange_PreviewIndicatorTransformXConverterValues(100, 20, Orientation.Horizontal, false, false, 1, 1);

// Act
double? result = converter.Convert(values, typeof(double), null, CultureInfo.CurrentCulture) as double?;
Expand All @@ -295,27 +318,29 @@ public void PreviewIndicatorTransformXConverter_ShouldCenterPreviewIndicator_Whe
Assert.Equal(40.0, result); // 50% of 100 minus 20/2
}

[Fact]
public void PreviewIndicatorTransformXConverter_ShouldOffsetPreviewIndicatorByPercentage_WhenFractionalValuesAreEnabledAndOrientationIsHorizontal()
[Theory]
[InlineData(false, 15.0)] // 25% of 100 minus 20/2
[InlineData(true, 65.0)] // 75% of 100 minus 20/2
public void PreviewIndicatorTransformXConverter_ShouldOffsetPreviewIndicatorByPercentage_WhenFractionalValuesAreEnabledAndOrientationIsHorizontal(bool invertDirection, double expectedValue)
{
// Arrange
IMultiValueConverter converter = RatingBar.PreviewIndicatorTransformXConverter.Instance;
object[] values = Arrange_PreviewIndicatorTransformXConverterValues(100, 20, Orientation.Horizontal, true, 1.25, 1);
object[] values = Arrange_PreviewIndicatorTransformXConverterValues(100, 20, Orientation.Horizontal, invertDirection, true, 1.25, 1);

// Act
double? result = converter.Convert(values, typeof(double), null, CultureInfo.CurrentCulture) as double?;

// Assert
Assert.NotNull(result);
Assert.Equal(15.0, result); // 25% of 100 minus 20/2
Assert.Equal(expectedValue, result);
}

[Fact]
public void PreviewIndicatorTransformXConverter_ShouldPlacePreviewIndicatorWithSmallMargin_WhenFractionalValuesAreDisabledAndOrientationIsVertical()
{
// Arrange
IMultiValueConverter converter = RatingBar.PreviewIndicatorTransformXConverter.Instance;
object[] values = Arrange_PreviewIndicatorTransformXConverterValues(100, 20, Orientation.Vertical, false, 1, 1);
object[] values = Arrange_PreviewIndicatorTransformXConverterValues(100, 20, Orientation.Vertical, false, false, 1, 1);
double expectedValue = -20 - RatingBar.PreviewIndicatorTransformXConverter.Margin;

// Act
Expand All @@ -331,7 +356,7 @@ public void PreviewIndicatorTransformXConverter_ShouldPlacePreviewIndicatorWithS
{
// Arrange
IMultiValueConverter converter = RatingBar.PreviewIndicatorTransformXConverter.Instance;
object[] values = Arrange_PreviewIndicatorTransformXConverterValues(100, 20, Orientation.Vertical, true, 1.25, 1);
object[] values = Arrange_PreviewIndicatorTransformXConverterValues(100, 20, Orientation.Vertical, false, true, 1.25, 1);
double expectedValue = -20 - RatingBar.PreviewIndicatorTransformXConverter.Margin;

// Act
Expand All @@ -344,15 +369,15 @@ public void PreviewIndicatorTransformXConverter_ShouldPlacePreviewIndicatorWithS



private static object[] Arrange_PreviewIndicatorTransformXConverterValues(double ratingBarButtonActualWidth, double previewValueActualWidth, Orientation orientation, bool isFractionalValueEnabled, double previewValue, int buttonValue) =>
new object[] { ratingBarButtonActualWidth, previewValueActualWidth, orientation, isFractionalValueEnabled, previewValue, buttonValue };
private static object[] Arrange_PreviewIndicatorTransformXConverterValues(double ratingBarButtonActualWidth, double previewValueActualWidth, Orientation orientation, bool invertDirection, bool isFractionalValueEnabled, double previewValue, int buttonValue) =>
new object[] { ratingBarButtonActualWidth, previewValueActualWidth, orientation, invertDirection, isFractionalValueEnabled, previewValue, buttonValue };

[Fact]
public void PreviewIndicatorTransformYConverter_ShouldPlacePreviewIndicatorWithSmallMargin_WhenFractionalValuesAreDisabledAndOrientationIsHorizontal()
{
// Arrange
IMultiValueConverter converter = RatingBar.PreviewIndicatorTransformYConverter.Instance;
object[] values = Arrange_PreviewIndicatorTransformYConverterValues(100, 20, Orientation.Horizontal, false, 1, 1);
object[] values = Arrange_PreviewIndicatorTransformYConverterValues(100, 20, Orientation.Horizontal, false, false, 1, 1);
double expectedValue = -20 - RatingBar.PreviewIndicatorTransformYConverter.Margin;

// Act
Expand All @@ -368,7 +393,7 @@ public void PreviewIndicatorTransformYConverter_ShouldPlacePreviewIndicatorWithS
{
// Arrange
IMultiValueConverter converter = RatingBar.PreviewIndicatorTransformYConverter.Instance;
object[] values = Arrange_PreviewIndicatorTransformYConverterValues(100, 20, Orientation.Horizontal, true, 1.25, 1);
object[] values = Arrange_PreviewIndicatorTransformYConverterValues(100, 20, Orientation.Horizontal, false, true, 1.25, 1);
double expectedValue = -20 - RatingBar.PreviewIndicatorTransformYConverter.Margin;

// Act
Expand All @@ -384,7 +409,7 @@ public void PreviewIndicatorTransformYConverter_ShouldCenterPreviewIndicator_Whe
{
// Arrange
IMultiValueConverter converter = RatingBar.PreviewIndicatorTransformYConverter.Instance;
object[] values = Arrange_PreviewIndicatorTransformYConverterValues(100, 20, Orientation.Vertical, false, 1, 1);
object[] values = Arrange_PreviewIndicatorTransformYConverterValues(100, 20, Orientation.Vertical, false, false, 1, 1);

// Act
double? result = converter.Convert(values, typeof(double), null, CultureInfo.CurrentCulture) as double?;
Expand All @@ -394,23 +419,25 @@ public void PreviewIndicatorTransformYConverter_ShouldCenterPreviewIndicator_Whe
Assert.Equal(40.0, result); // 50% of 100 minus 20/2
}

[Fact]
public void PreviewIndicatorTransformYConverter_ShouldPreviewIndicatorByPercentage_WhenFractionalValuesAreEnabledAndOrientationIsVertical()
[Theory]
[InlineData(false, 15.0)] // 25% of 100 minus 20/2
[InlineData(true, 65.0)] // 75% of 100 minus 20/2
public void PreviewIndicatorTransformYConverter_ShouldPreviewIndicatorByPercentage_WhenFractionalValuesAreEnabledAndOrientationIsVertical(bool invertDirection, double expectedValue)
{
// Arrange
IMultiValueConverter converter = RatingBar.PreviewIndicatorTransformYConverter.Instance;
object[] values = Arrange_PreviewIndicatorTransformYConverterValues(100, 20, Orientation.Vertical, true, 1.25, 1);
object[] values = Arrange_PreviewIndicatorTransformYConverterValues(100, 20, Orientation.Vertical, invertDirection, true, 1.25, 1);

// Act
double? result = converter.Convert(values, typeof(double), null, CultureInfo.CurrentCulture) as double?;

// Assert
Assert.NotNull(result);
Assert.Equal(15.0, result); // 25% of 100 minus 20/2
Assert.Equal(expectedValue, result);
}

private static object[] Arrange_PreviewIndicatorTransformYConverterValues(double ratingBarButtonActualHeight, double previewValueActualHeight, Orientation orientation, bool isFractionalValueEnabled, double previewValue, int buttonValue) =>
new object[] { ratingBarButtonActualHeight, previewValueActualHeight, orientation, isFractionalValueEnabled, previewValue, buttonValue };
private static object[] Arrange_PreviewIndicatorTransformYConverterValues(double ratingBarButtonActualHeight, double previewValueActualHeight, Orientation orientation, bool invertDirection, bool isFractionalValueEnabled, double previewValue, int buttonValue) =>
new object[] { ratingBarButtonActualHeight, previewValueActualHeight, orientation, invertDirection, isFractionalValueEnabled, previewValue, buttonValue };
}

internal static class ColorExtensions
Expand Down
Loading

0 comments on commit 4590c59

Please sign in to comment.