Browse Source

Implement StretchDirection for Viewbox. Cleanup Viewbox.

pull/5917/head
Dariusz Komosinski 5 years ago
parent
commit
b3e2b05d1f
  1. 28
      samples/ControlCatalog/Pages/ViewboxPage.xaml
  2. 31
      samples/ControlCatalog/Pages/ViewboxPage.xaml.cs
  3. 4
      src/Avalonia.Controls/Image.cs
  4. 81
      src/Avalonia.Controls/Viewbox.cs
  5. 56
      tests/Avalonia.Controls.UnitTests/ViewboxTests.cs

28
samples/ControlCatalog/Pages/ViewboxPage.xaml

@ -22,14 +22,38 @@
</StreamGeometry>
</UserControl.Resources>
<Grid RowDefinitions="Auto,*">
<Grid RowDefinitions="Auto,*,*">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Viewbox</TextBlock>
<TextBlock Classes="h2">A control used to scale single child.</TextBlock>
</StackPanel>
<Grid Grid.Row="1" ColumnDefinitions="*,Auto" HorizontalAlignment="Center" Margin="0,10,0,0">
<Border HorizontalAlignment="Center" Grid.Column="0" BorderThickness="1" BorderBrush="Orange" Width="200" Height="200">
<Border VerticalAlignment="Center" HorizontalAlignment="Center" Grid.Column="0" BorderThickness="1" BorderBrush="CornflowerBlue" Width="{Binding #WidthSlider.Value}" Height="{Binding #HeightSlider.Value}" >
<Viewbox x:Name="Viewbox"
StretchDirection="{Binding #StretchDirectionSelector.SelectedItem}">
<Path Width="50" Stretch="Uniform" Fill="CornflowerBlue" Data="{StaticResource Acorn}"/>
</Viewbox>
</Border>
</Border>
<StackPanel HorizontalAlignment="Left" Orientation="Vertical" Grid.Column="1" Margin="8,0,0,0" Width="150">
<TextBlock Text="Width" />
<Slider Minimum="10" Maximum="200" Value="100" x:Name="WidthSlider" TickFrequency="25" TickPlacement="TopLeft" />
<TextBlock Text="Height" />
<Slider Minimum="10" Maximum="200" Value="100" x:Name="HeightSlider" TickFrequency="25" TickPlacement="TopLeft" />
<TextBlock Text="Stretch" />
<ComboBox x:Name="StretchSelector" HorizontalAlignment="Stretch" Margin="0,0,0,2" SelectionChanged="StretchSelector_OnSelectionChanged" />
<TextBlock Text="Stretch Direction" />
<ComboBox x:Name="StretchDirectionSelector" HorizontalAlignment="Stretch" />
</StackPanel>
</Grid>
<Grid ColumnDefinitions="Auto,*,*"
RowDefinitions="*,*,*,*"
Grid.Row="1" Margin="48"
Grid.Row="2" Margin="48"
MaxWidth="400">
<TextBlock Grid.Row="0" VerticalAlignment="Center">None</TextBlock>
<TextBlock Grid.Row="1" VerticalAlignment="Center">Fill</TextBlock>

31
samples/ControlCatalog/Pages/ViewboxPage.xaml.cs

@ -1,18 +1,47 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
namespace ControlCatalog.Pages
{
public class ViewboxPage : UserControl
{
private readonly Viewbox _viewbox;
private readonly ComboBox _stretchSelector;
public ViewboxPage()
{
this.InitializeComponent();
InitializeComponent();
_viewbox = this.FindControl<Viewbox>("Viewbox");
_stretchSelector = this.FindControl<ComboBox>("StretchSelector");
_stretchSelector.Items = new[]
{
Stretch.Uniform, Stretch.UniformToFill, Stretch.Fill, Stretch.None
};
_stretchSelector.SelectedIndex = 0;
var stretchDirectionSelector = this.FindControl<ComboBox>("StretchDirectionSelector");
stretchDirectionSelector.Items = new[]
{
StretchDirection.Both, StretchDirection.DownOnly, StretchDirection.UpOnly
};
stretchDirectionSelector.SelectedIndex = 0;
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
private void StretchSelector_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
_viewbox.Stretch = (Stretch) _stretchSelector.SelectedItem!;
}
}
}

4
src/Avalonia.Controls/Image.cs

@ -31,8 +31,8 @@ namespace Avalonia.Controls
static Image()
{
AffectsRender<Image>(SourceProperty, StretchProperty);
AffectsMeasure<Image>(SourceProperty, StretchProperty);
AffectsRender<Image>(SourceProperty, StretchProperty, StretchDirectionProperty);
AffectsMeasure<Image>(SourceProperty, StretchProperty, StretchDirectionProperty);
}
/// <summary>

81
src/Avalonia.Controls/Viewbox.cs

@ -1,40 +1,50 @@
using System;
using Avalonia.Media;
using Avalonia.Media;
namespace Avalonia.Controls
{
/// <summary>
/// Viewbox is used to scale single child.
/// Viewbox is used to scale single child to fit in the available space.
/// </summary>
/// <seealso cref="Avalonia.Controls.Decorator" />
public class Viewbox : Decorator
{
/// <summary>
/// The stretch property
/// Defines the <see cref="Stretch"/> property.
/// </summary>
public static readonly AvaloniaProperty<Stretch> StretchProperty =
AvaloniaProperty.RegisterDirect<Viewbox, Stretch>(nameof(Stretch),
v => v.Stretch, (c, v) => c.Stretch = v, Stretch.Uniform);
AvaloniaProperty.Register<Image, Stretch>(nameof(Stretch), Stretch.Uniform);
/// <summary>
/// Defines the <see cref="StretchDirection"/> property.
/// </summary>
public static readonly StyledProperty<StretchDirection> StretchDirectionProperty =
AvaloniaProperty.Register<Viewbox, StretchDirection>(nameof(StretchDirection), StretchDirection.Both);
private Stretch _stretch = Stretch.Uniform;
static Viewbox()
{
ClipToBoundsProperty.OverrideDefaultValue<Viewbox>(true);
AffectsMeasure<Viewbox>(StretchProperty, StretchDirectionProperty);
}
/// <summary>
/// Gets or sets the stretch mode,
/// which determines how child fits into the available space.
/// </summary>
/// <value>
/// The stretch.
/// </value>
public Stretch Stretch
{
get => _stretch;
set => SetAndRaise(StretchProperty, ref _stretch, value);
}
static Viewbox()
/// <summary>
/// Gets or sets a value controlling in what direction contents will be stretched.
/// </summary>
public StretchDirection StretchDirection
{
ClipToBoundsProperty.OverrideDefaultValue<Viewbox>(true);
AffectsMeasure<Viewbox>(StretchProperty);
get => GetValue(StretchDirectionProperty);
set => SetValue(StretchDirectionProperty, value);
}
protected override Size MeasureOverride(Size availableSize)
@ -47,9 +57,9 @@ namespace Avalonia.Controls
var childSize = child.DesiredSize;
var scale = GetScale(availableSize, childSize, Stretch);
var size = Stretch.CalculateSize(availableSize, childSize, StretchDirection);
return (childSize * scale).Constrain(availableSize);
return size.Constrain(availableSize);
}
return new Size();
@ -62,7 +72,9 @@ namespace Avalonia.Controls
if (child != null)
{
var childSize = child.DesiredSize;
var scale = GetScale(finalSize, childSize, Stretch);
var scale = Stretch.CalculateScaling(finalSize, childSize, StretchDirection);
// TODO: Viewbox should have another decorator as a child so we won't affect other render transforms.
var scaleTransform = child.RenderTransform as ScaleTransform;
if (scaleTransform == null)
@ -81,44 +93,5 @@ namespace Avalonia.Controls
return new Size();
}
private static Vector GetScale(Size availableSize, Size childSize, Stretch stretch)
{
double scaleX = 1.0;
double scaleY = 1.0;
bool validWidth = !double.IsPositiveInfinity(availableSize.Width);
bool validHeight = !double.IsPositiveInfinity(availableSize.Height);
if (stretch != Stretch.None && (validWidth || validHeight))
{
scaleX = childSize.Width <= 0.0 ? 0.0 : availableSize.Width / childSize.Width;
scaleY = childSize.Height <= 0.0 ? 0.0 : availableSize.Height / childSize.Height;
if (!validWidth)
{
scaleX = scaleY;
}
else if (!validHeight)
{
scaleY = scaleX;
}
else
{
switch (stretch)
{
case Stretch.Uniform:
scaleX = scaleY = Math.Min(scaleX, scaleY);
break;
case Stretch.UniformToFill:
scaleX = scaleY = Math.Max(scaleX, scaleY);
break;
}
}
}
return new Vector(scaleX, scaleY);
}
}
}

56
tests/Avalonia.Controls.UnitTests/ViewboxTests.cs

@ -114,5 +114,61 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(2.0, scaleTransform.ScaleX);
Assert.Equal(2.0, scaleTransform.ScaleY);
}
[Theory]
[InlineData(50, 100, 50, 100, 50, 100, 1)]
[InlineData(50, 100, 150, 150, 50, 100, 1)]
[InlineData(50, 100, 25, 50, 25, 50, 0.5)]
public void Viewbox_Should_Return_Correct_SizeAndScale_StretchDirection_DownOnly(
double childWidth, double childHeight,
double viewboxWidth, double viewboxHeight,
double expectedWidth, double expectedHeight,
double expectedScale)
{
var target = new Viewbox
{
Child = new Control { Width = childWidth, Height = childHeight },
StretchDirection = StretchDirection.DownOnly
};
target.Measure(new Size(viewboxWidth, viewboxHeight));
target.Arrange(new Rect(default, target.DesiredSize));
Assert.Equal(new Size(expectedWidth, expectedHeight), target.DesiredSize);
var scaleTransform = target.Child.RenderTransform as ScaleTransform;
Assert.NotNull(scaleTransform);
Assert.Equal(expectedScale, scaleTransform.ScaleX);
Assert.Equal(expectedScale, scaleTransform.ScaleY);
}
[Theory]
[InlineData(50, 100, 50, 100, 50, 100, 1)]
[InlineData(50, 100, 25, 50, 25, 50, 1)]
[InlineData(50, 100, 150, 150, 75, 150, 1.5)]
public void Viewbox_Should_Return_Correct_SizeAndScale_StretchDirection_UpOnly(
double childWidth, double childHeight,
double viewboxWidth, double viewboxHeight,
double expectedWidth, double expectedHeight,
double expectedScale)
{
var target = new Viewbox
{
Child = new Control { Width = childWidth, Height = childHeight },
StretchDirection = StretchDirection.UpOnly
};
target.Measure(new Size(viewboxWidth, viewboxHeight));
target.Arrange(new Rect(default, target.DesiredSize));
Assert.Equal(new Size(expectedWidth, expectedHeight), target.DesiredSize);
var scaleTransform = target.Child.RenderTransform as ScaleTransform;
Assert.NotNull(scaleTransform);
Assert.Equal(expectedScale, scaleTransform.ScaleX);
Assert.Equal(expectedScale, scaleTransform.ScaleY);
}
}
}

Loading…
Cancel
Save