Browse Source

Merge branch 'master' into pagetransitions

pull/1795/head
Jumar Macato 8 years ago
committed by GitHub
parent
commit
e62d6debe4
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      .gitignore
  2. 26
      samples/BindingDemo/MainWindow.xaml
  3. 6
      samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml
  4. 6
      samples/ControlCatalog/Pages/BorderPage.xaml
  5. 10
      samples/ControlCatalog/Pages/ButtonPage.xaml
  6. 6
      samples/ControlCatalog/Pages/ButtonSpinnerPage.xaml
  7. 6
      samples/ControlCatalog/Pages/CalendarPage.xaml
  8. 4
      samples/ControlCatalog/Pages/CanvasPage.xaml
  9. 10
      samples/ControlCatalog/Pages/CarouselPage.xaml
  10. 10
      samples/ControlCatalog/Pages/CheckBoxPage.xaml
  11. 6
      samples/ControlCatalog/Pages/ContextMenuPage.xaml
  12. 6
      samples/ControlCatalog/Pages/DatePickerPage.xaml
  13. 4
      samples/ControlCatalog/Pages/DialogsPage.xaml
  14. 6
      samples/ControlCatalog/Pages/DragAndDropPage.xaml
  15. 6
      samples/ControlCatalog/Pages/DropDownPage.xaml
  16. 6
      samples/ControlCatalog/Pages/ExpanderPage.xaml
  17. 4
      samples/ControlCatalog/Pages/ImagePage.xaml
  18. 4
      samples/ControlCatalog/Pages/MenuPage.xaml
  19. 6
      samples/ControlCatalog/Pages/NumericUpDownPage.xaml
  20. 6
      samples/ControlCatalog/Pages/ProgressBarPage.xaml
  21. 8
      samples/ControlCatalog/Pages/RadioButtonPage.xaml
  22. 4
      samples/ControlCatalog/Pages/SliderPage.xaml
  23. 11
      samples/ControlCatalog/Pages/TextBoxPage.xaml
  24. 2
      samples/ControlCatalog/Pages/ToolTipPage.xaml
  25. 4
      samples/ControlCatalog/Pages/TreeViewPage.xaml
  26. 2
      samples/VirtualizationDemo/MainWindow.xaml
  27. 82
      src/Avalonia.Animation/Animation.cs
  28. 2
      src/Avalonia.Animation/AnimatorKeyFrame.cs
  29. 6
      src/Avalonia.Animation/AnimatorStateMachine`1.cs
  30. 9
      src/Avalonia.Animation/Animator`1.cs
  31. 4
      src/Avalonia.Animation/IAnimation.cs
  32. 2
      src/Avalonia.Animation/IAnimator.cs
  33. 9
      src/Avalonia.Controls/Image.cs
  34. 5
      src/Avalonia.Controls/Presenters/ItemVirtualizer.cs
  35. 36
      src/Avalonia.Controls/Presenters/ItemsPresenter.cs
  36. 30
      src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs
  37. 6
      src/Avalonia.Controls/Presenters/TextPresenter.cs
  38. 2
      src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
  39. 5
      src/Avalonia.Controls/Slider.cs
  40. 38
      src/Avalonia.Controls/StackPanel.cs
  41. 14
      src/Avalonia.Controls/TextBox.cs
  42. 12
      src/Avalonia.Controls/VirtualizingStackPanel.cs
  43. 10
      src/Avalonia.Controls/Window.cs
  44. 4
      src/Avalonia.Controls/WindowCollection.cs
  45. 12
      src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj
  46. 4
      src/Avalonia.Diagnostics/DevTools.xaml
  47. 4
      src/Avalonia.Diagnostics/Views/TreePageView.xaml
  48. 6
      src/Avalonia.Visuals/Animation/TransformAnimator.cs
  49. 10
      src/Avalonia.Visuals/Media/DrawingContext.cs
  50. 19
      src/Avalonia.Visuals/Media/ITileBrush.cs
  51. 31
      src/Avalonia.Visuals/Media/Imaging/BitmapInterpolationMode.cs
  52. 11
      src/Avalonia.Visuals/Media/Immutable/ImmutableImageBrush.cs
  53. 16
      src/Avalonia.Visuals/Media/Immutable/ImmutableTileBrush.cs
  54. 12
      src/Avalonia.Visuals/Media/Immutable/ImmutableVisualBrush.cs
  55. 39
      src/Avalonia.Visuals/Media/RenderOptions.cs
  56. 19
      src/Avalonia.Visuals/Media/TileBrush.cs
  57. 4
      src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs
  58. 7
      src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs
  59. 23
      src/Avalonia.Visuals/Rendering/SceneGraph/ImageNode.cs
  60. 7
      src/Gtk/Avalonia.Gtk3/KeyTransform.cs
  61. 38
      src/Skia/Avalonia.Skia/DrawingContextImpl.cs
  62. 25
      src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs
  63. 9
      src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs
  64. 9
      tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization.cs
  65. 22
      tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs
  66. 39
      tests/Avalonia.Controls.UnitTests/SliderTests.cs
  67. 14
      tests/Avalonia.Controls.UnitTests/StackPanelTests.cs
  68. 6
      tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs
  69. 14
      tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/DrawOperationTests.cs

5
.gitignore

@ -165,6 +165,11 @@ $RECYCLE.BIN/
################# #################
.idea .idea
#################
## VS Code
#################
.vscode/
################# #################
## Cake ## Cake
################# #################

26
samples/BindingDemo/MainWindow.xaml

@ -18,18 +18,18 @@
<TabItem Header="Basic"> <TabItem Header="Basic">
<StackPanel Orientation="Vertical"> <StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<StackPanel Margin="18" Gap="4" Width="200"> <StackPanel Margin="18" Spacing="4" Width="200">
<TextBlock FontSize="16" Text="Simple Bindings"/> <TextBlock FontSize="16" Text="Simple Bindings"/>
<TextBox Watermark="Two Way" UseFloatingWatermark="True" Text="{Binding Path=StringValue}" Name="first"/> <TextBox Watermark="Two Way" UseFloatingWatermark="True" Text="{Binding Path=StringValue}" Name="first"/>
<TextBox Watermark="One Way" UseFloatingWatermark="True" Text="{Binding Path=StringValue, Mode=OneWay}"/> <TextBox Watermark="One Way" UseFloatingWatermark="True" Text="{Binding Path=StringValue, Mode=OneWay}"/>
<TextBox Watermark="One Time" UseFloatingWatermark="True" Text="{Binding Path=StringValue, Mode=OneTime}"/> <TextBox Watermark="One Time" UseFloatingWatermark="True" Text="{Binding Path=StringValue, Mode=OneTime}"/>
</StackPanel> </StackPanel>
<StackPanel Margin="18" Gap="4" Width="200"> <StackPanel Margin="18" Spacing="4" Width="200">
<TextBlock FontSize="16" Text="Collection Bindings"/> <TextBlock FontSize="16" Text="Collection Bindings"/>
<TextBox Watermark="Items[1].StringValue" UseFloatingWatermark="True" Text="{Binding Path=Items[1].StringValue}"/> <TextBox Watermark="Items[1].StringValue" UseFloatingWatermark="True" Text="{Binding Path=Items[1].StringValue}"/>
<Button Command="{Binding ShuffleItems}">Shuffle</Button> <Button Command="{Binding ShuffleItems}">Shuffle</Button>
</StackPanel> </StackPanel>
<StackPanel Margin="18" Gap="4" Width="200"> <StackPanel Margin="18" Spacing="4" Width="200">
<TextBlock FontSize="16" Text="Negated Bindings"/> <TextBlock FontSize="16" Text="Negated Bindings"/>
<TextBox Watermark="Boolean String" UseFloatingWatermark="True" Text="{Binding Path=BooleanString}"/> <TextBox Watermark="Boolean String" UseFloatingWatermark="True" Text="{Binding Path=BooleanString}"/>
<CheckBox IsChecked="{Binding !BooleanString}">!BooleanString</CheckBox> <CheckBox IsChecked="{Binding !BooleanString}">!BooleanString</CheckBox>
@ -37,13 +37,13 @@
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<StackPanel Margin="18" Gap="4" Width="200" HorizontalAlignment="Left"> <StackPanel Margin="18" Spacing="4" Width="200" HorizontalAlignment="Left">
<TextBlock FontSize="16" Text="Numeric Bindings"/> <TextBlock FontSize="16" Text="Numeric Bindings"/>
<TextBox Watermark="Double" UseFloatingWatermark="True" Text="{Binding Path=DoubleValue, Mode=TwoWay}"/> <TextBox Watermark="Double" UseFloatingWatermark="True" Text="{Binding Path=DoubleValue, Mode=TwoWay}"/>
<TextBlock Text="{Binding Path=DoubleValue}"/> <TextBlock Text="{Binding Path=DoubleValue}"/>
<ProgressBar Maximum="10" Value="{Binding DoubleValue}"/> <ProgressBar Maximum="10" Value="{Binding DoubleValue}"/>
</StackPanel> </StackPanel>
<StackPanel Margin="18" Gap="4" Width="200" HorizontalAlignment="Left"> <StackPanel Margin="18" Spacing="4" Width="200" HorizontalAlignment="Left">
<TextBlock FontSize="16" Text="Binding Sources"/> <TextBlock FontSize="16" Text="Binding Sources"/>
<TextBox Watermark="Value of first TextBox" UseFloatingWatermark="True" <TextBox Watermark="Value of first TextBox" UseFloatingWatermark="True"
Text="{Binding #first.Text, Mode=TwoWay}"/> Text="{Binding #first.Text, Mode=TwoWay}"/>
@ -52,7 +52,7 @@
<TextBox Watermark="Value of SharedItem.StringValue (duplicate)" UseFloatingWatermark="True" <TextBox Watermark="Value of SharedItem.StringValue (duplicate)" UseFloatingWatermark="True"
Text="{Binding StringValue, Source={StaticResource SharedItem}, Mode=TwoWay}"/> Text="{Binding StringValue, Source={StaticResource SharedItem}, Mode=TwoWay}"/>
</StackPanel> </StackPanel>
<StackPanel Margin="18" Gap="4" Width="200" HorizontalAlignment="Left"> <StackPanel Margin="18" Spacing="4" Width="200" HorizontalAlignment="Left">
<TextBlock FontSize="16" Text="Scheduler"/> <TextBlock FontSize="16" Text="Scheduler"/>
<TextBox Watermark="Background Thread" Text="{Binding CurrentTime, Mode=OneWay}"/> <TextBox Watermark="Background Thread" Text="{Binding CurrentTime, Mode=OneWay}"/>
<TextBlock FontSize="16" Text="Stream Operator"/> <TextBlock FontSize="16" Text="Stream Operator"/>
@ -68,11 +68,11 @@
<TextBlock Text="{Binding StringValue}"/> <TextBlock Text="{Binding StringValue}"/>
</DataTemplate> </DataTemplate>
</StackPanel.DataTemplates> </StackPanel.DataTemplates>
<StackPanel Margin="18" Gap="4" Width="200"> <StackPanel Margin="18" Spacing="4" Width="200">
<TextBlock FontSize="16" Text="Multiple"/> <TextBlock FontSize="16" Text="Multiple"/>
<ListBox Items="{Binding Items}" SelectionMode="Multiple" SelectedItems="{Binding SelectedItems}"/> <ListBox Items="{Binding Items}" SelectionMode="Multiple" SelectedItems="{Binding SelectedItems}"/>
</StackPanel> </StackPanel>
<StackPanel Margin="18" Gap="4" Width="200"> <StackPanel Margin="18" Spacing="4" Width="200">
<TextBlock FontSize="16" Text="Multiple"/> <TextBlock FontSize="16" Text="Multiple"/>
<ListBox Items="{Binding Items}" SelectionMode="Multiple" SelectedItems="{Binding SelectedItems}"/> <ListBox Items="{Binding Items}" SelectionMode="Multiple" SelectedItems="{Binding SelectedItems}"/>
</StackPanel> </StackPanel>
@ -87,16 +87,16 @@
</TabItem> </TabItem>
<TabItem Header="Property Validation"> <TabItem Header="Property Validation">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<StackPanel Margin="18" Gap="4" MinWidth="200" DataContext="{Binding ExceptionDataValidation}"> <StackPanel Margin="18" Spacing="4" MinWidth="200" DataContext="{Binding ExceptionDataValidation}">
<TextBlock FontSize="16" Text="Exception Validation"/> <TextBlock FontSize="16" Text="Exception Validation"/>
<TextBox Watermark="Less Than 10" UseFloatingWatermark="True" Text="{Binding Path=LessThan10}"/> <TextBox Watermark="Less Than 10" UseFloatingWatermark="True" Text="{Binding Path=LessThan10}"/>
</StackPanel> </StackPanel>
<StackPanel Margin="18" Gap="4" MinWidth="200" DataContext="{Binding IndeiDataValidation}"> <StackPanel Margin="18" Spacing="4" MinWidth="200" DataContext="{Binding IndeiDataValidation}">
<TextBlock FontSize="16" Text="INotifyDataErrorInfo Validation"/> <TextBlock FontSize="16" Text="INotifyDataErrorInfo Validation"/>
<TextBox Watermark="Maximum" UseFloatingWatermark="True" Text="{Binding Path=Maximum}"/> <TextBox Watermark="Maximum" UseFloatingWatermark="True" Text="{Binding Path=Maximum}"/>
<TextBox Watermark="Value" UseFloatingWatermark="True" Text="{Binding Path=Value}"/> <TextBox Watermark="Value" UseFloatingWatermark="True" Text="{Binding Path=Value}"/>
</StackPanel> </StackPanel>
<StackPanel Margin="18" Gap="4" MinWidth="200" DataContext="{Binding DataAnnotationsValidation}"> <StackPanel Margin="18" Spacing="4" MinWidth="200" DataContext="{Binding DataAnnotationsValidation}">
<TextBlock FontSize="16" Text="Data Annotations Validation"/> <TextBlock FontSize="16" Text="Data Annotations Validation"/>
<TextBox Watermark="Phone #" UseFloatingWatermark="True" Text="{Binding PhoneNumber}"/> <TextBox Watermark="Phone #" UseFloatingWatermark="True" Text="{Binding PhoneNumber}"/>
<TextBox Watermark="Less Than 10" UseFloatingWatermark="True" Text="{Binding Path=LessThan10}"/> <TextBox Watermark="Less Than 10" UseFloatingWatermark="True" Text="{Binding Path=LessThan10}"/>
@ -104,7 +104,7 @@
</StackPanel> </StackPanel>
</TabItem> </TabItem>
<TabItem Header="Commands"> <TabItem Header="Commands">
<StackPanel Margin="18" Gap="4" Width="200"> <StackPanel Margin="18" Spacing="4" Width="200">
<Button Content="Button" Command="{Binding StringValueCommand}" CommandParameter="Button"/> <Button Content="Button" Command="{Binding StringValueCommand}" CommandParameter="Button"/>
<ToggleButton Content="ToggleButton" IsChecked="{Binding BooleanFlag, Mode=OneWay}" Command="{Binding StringValueCommand}" CommandParameter="ToggleButton"/> <ToggleButton Content="ToggleButton" IsChecked="{Binding BooleanFlag, Mode=OneWay}" Command="{Binding StringValueCommand}" CommandParameter="ToggleButton"/>
<CheckBox Content="CheckBox" IsChecked="{Binding !BooleanFlag, Mode=OneWay}" Command="{Binding StringValueCommand}" CommandParameter="CheckBox"/> <CheckBox Content="CheckBox" IsChecked="{Binding !BooleanFlag, Mode=OneWay}" Command="{Binding StringValueCommand}" CommandParameter="CheckBox"/>
@ -114,4 +114,4 @@
</StackPanel> </StackPanel>
</TabItem> </TabItem>
</TabControl> </TabControl>
</Window> </Window>

6
samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml

@ -1,12 +1,12 @@
<UserControl xmlns="https://github.com/avaloniaui"> <UserControl xmlns="https://github.com/avaloniaui">
<StackPanel Orientation="Vertical" Gap="4"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">AutoCompleteBox</TextBlock> <TextBlock Classes="h1">AutoCompleteBox</TextBlock>
<TextBlock Classes="h2">A control into which the user can input text</TextBlock> <TextBlock Classes="h2">A control into which the user can input text</TextBlock>
<StackPanel Orientation="Horizontal" <StackPanel Orientation="Horizontal"
Margin="0,16,0,0" Margin="0,16,0,0"
HorizontalAlignment="Center" HorizontalAlignment="Center"
Gap="8"> Spacing="8">
<StackPanel Orientation="Vertical"> <StackPanel Orientation="Vertical">
<TextBlock Text="MinimumPrefixLength: 1"/> <TextBlock Text="MinimumPrefixLength: 1"/>
<AutoCompleteBox Width="200" <AutoCompleteBox Width="200"
@ -56,4 +56,4 @@
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</UserControl> </UserControl>

6
samples/ControlCatalog/Pages/BorderPage.xaml

@ -1,12 +1,12 @@
<UserControl xmlns="https://github.com/avaloniaui"> <UserControl xmlns="https://github.com/avaloniaui">
<StackPanel Orientation="Vertical" Gap="4"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Border</TextBlock> <TextBlock Classes="h1">Border</TextBlock>
<TextBlock Classes="h2">A control which decorates a child with a border and background</TextBlock> <TextBlock Classes="h2">A control which decorates a child with a border and background</TextBlock>
<StackPanel Orientation="Vertical" <StackPanel Orientation="Vertical"
Margin="0,16,0,0" Margin="0,16,0,0"
HorizontalAlignment="Center" HorizontalAlignment="Center"
Gap="16"> Spacing="16">
<Border BorderBrush="{DynamicResource ThemeAccentBrush}" BorderThickness="2" Padding="16"> <Border BorderBrush="{DynamicResource ThemeAccentBrush}" BorderThickness="2" Padding="16">
<TextBlock>Border</TextBlock> <TextBlock>Border</TextBlock>
</Border> </Border>
@ -29,4 +29,4 @@
</Border> </Border>
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</UserControl> </UserControl>

10
samples/ControlCatalog/Pages/ButtonPage.xaml

@ -1,14 +1,14 @@
<UserControl xmlns="https://github.com/avaloniaui" <UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel Orientation="Vertical" Gap="4"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Button</TextBlock> <TextBlock Classes="h1">Button</TextBlock>
<TextBlock Classes="h2">A button control</TextBlock> <TextBlock Classes="h2">A button control</TextBlock>
<StackPanel Orientation="Horizontal" <StackPanel Orientation="Horizontal"
Margin="0,16,0,0" Margin="0,16,0,0"
HorizontalAlignment="Center" HorizontalAlignment="Center"
Gap="16"> Spacing="16">
<StackPanel Orientation="Vertical" Gap="8" Width="150"> <StackPanel Orientation="Vertical" Spacing="8" Width="150">
<Button>Button</Button> <Button>Button</Button>
<Button Foreground="White">Foreground</Button> <Button Foreground="White">Foreground</Button>
<Button Background="{DynamicResource ThemeAccentBrush}">Background</Button> <Button Background="{DynamicResource ThemeAccentBrush}">Background</Button>
@ -25,7 +25,7 @@
</Button> </Button>
</StackPanel> </StackPanel>
<StackPanel Orientation="Vertical" Gap="8" Width="150"> <StackPanel Orientation="Vertical" Spacing="8" Width="150">
<Button BorderThickness="0">No Border</Button> <Button BorderThickness="0">No Border</Button>
<Button BorderBrush="{DynamicResource ThemeAccentBrush}">Border Color</Button> <Button BorderBrush="{DynamicResource ThemeAccentBrush}">Border Color</Button>
<Button BorderBrush="{DynamicResource ThemeAccentBrush}" BorderThickness="4">Thick Border</Button> <Button BorderBrush="{DynamicResource ThemeAccentBrush}" BorderThickness="4">Thick Border</Button>
@ -33,4 +33,4 @@
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</UserControl> </UserControl>

6
samples/ControlCatalog/Pages/ButtonSpinnerPage.xaml

@ -1,11 +1,11 @@
<UserControl xmlns="https://github.com/avaloniaui" <UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel Orientation="Vertical" Gap="4"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">ButtonSpinner</TextBlock> <TextBlock Classes="h1">ButtonSpinner</TextBlock>
<TextBlock Classes="h2">The ButtonSpinner control allows you to add button spinners to any element and then respond to the Spin event to manipulate that element.</TextBlock> <TextBlock Classes="h2">The ButtonSpinner control allows you to add button spinners to any element and then respond to the Spin event to manipulate that element.</TextBlock>
<StackPanel Orientation="Vertical" Gap="8" Width="200" Margin="0,20,0,0"> <StackPanel Orientation="Vertical" Spacing="8" Width="200" Margin="0,20,0,0">
<CheckBox Name="allowSpinCheck" IsChecked="True">AllowSpin</CheckBox> <CheckBox Name="allowSpinCheck" IsChecked="True">AllowSpin</CheckBox>
<CheckBox Name="showSpinCheck" IsChecked="True">ShowButtonSpinner</CheckBox> <CheckBox Name="showSpinCheck" IsChecked="True">ShowButtonSpinner</CheckBox>
<ButtonSpinner Spin="OnSpin" Height="30" <ButtonSpinner Spin="OnSpin" Height="30"
@ -21,4 +21,4 @@
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</UserControl> </UserControl>

6
samples/ControlCatalog/Pages/CalendarPage.xaml

@ -1,13 +1,13 @@
<UserControl xmlns="https://github.com/avaloniaui" <UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel Orientation="Vertical" Gap="4"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Calendar</TextBlock> <TextBlock Classes="h1">Calendar</TextBlock>
<TextBlock Classes="h2">A calendar control for selecting dates</TextBlock> <TextBlock Classes="h2">A calendar control for selecting dates</TextBlock>
<StackPanel Orientation="Horizontal" <StackPanel Orientation="Horizontal"
Margin="0,16,0,0" Margin="0,16,0,0"
HorizontalAlignment="Center" HorizontalAlignment="Center"
Gap="16"> Spacing="16">
<StackPanel Orientation="Vertical"> <StackPanel Orientation="Vertical">
<TextBlock Text="SelectionMode: None"/> <TextBlock Text="SelectionMode: None"/>
<Calendar SelectionMode="None" <Calendar SelectionMode="None"
@ -44,4 +44,4 @@
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</UserControl> </UserControl>

4
samples/ControlCatalog/Pages/CanvasPage.xaml

@ -1,5 +1,5 @@
<UserControl xmlns="https://github.com/avaloniaui"> <UserControl xmlns="https://github.com/avaloniaui">
<StackPanel Orientation="Vertical" Gap="4"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Canvas</TextBlock> <TextBlock Classes="h1">Canvas</TextBlock>
<TextBlock Classes="h2">A panel which lays out its children by explicit coordinates</TextBlock> <TextBlock Classes="h2">A panel which lays out its children by explicit coordinates</TextBlock>
<Canvas Background="Yellow" Width="300" Height="400"> <Canvas Background="Yellow" Width="300" Height="400">
@ -31,4 +31,4 @@
<Polyline Points="0,0 65,0 78,-26 91,39 104,-39 117,13 130,0 195,0" Stroke="Brown" Canvas.Left="30" Canvas.Top="350"/> <Polyline Points="0,0 65,0 78,-26 91,39 104,-39 117,13 130,0 195,0" Stroke="Brown" Canvas.Left="30" Canvas.Top="350"/>
</Canvas> </Canvas>
</StackPanel> </StackPanel>
</UserControl> </UserControl>

10
samples/ControlCatalog/Pages/CarouselPage.xaml

@ -1,9 +1,9 @@
<UserControl xmlns="https://github.com/avaloniaui"> <UserControl xmlns="https://github.com/avaloniaui">
<StackPanel Orientation="Vertical" Gap="4"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Carousel</TextBlock> <TextBlock Classes="h1">Carousel</TextBlock>
<TextBlock Classes="h2">An items control that displays its items as pages that fill the control.</TextBlock> <TextBlock Classes="h2">An items control that displays its items as pages that fill the control.</TextBlock>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0 16 0 0" Gap="8"> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0 16 0 0" Spacing="8">
<Button Name="left" VerticalAlignment="Center" Padding="20"> <Button Name="left" VerticalAlignment="Center" Padding="20">
<Path Data="M20,11V13H8L13.5,18.5L12.08,19.92L4.16,12L12.08,4.08L13.5,5.5L8,11H20Z" Fill="Black"/> <Path Data="M20,11V13H8L13.5,18.5L12.08,19.92L4.16,12L12.08,4.08L13.5,5.5L8,11H20Z" Fill="Black"/>
</Button> </Button>
@ -20,7 +20,7 @@
</Button> </Button>
</StackPanel> </StackPanel>
<StackPanel Orientation="Horizontal" Gap="4"> <StackPanel Orientation="Horizontal" Spacing="4">
<TextBlock VerticalAlignment="Center">Transition</TextBlock> <TextBlock VerticalAlignment="Center">Transition</TextBlock>
<DropDown Name="transition" SelectedIndex="1" VerticalAlignment="Center"> <DropDown Name="transition" SelectedIndex="1" VerticalAlignment="Center">
<DropDownItem>None</DropDownItem> <DropDownItem>None</DropDownItem>
@ -29,7 +29,7 @@
</DropDown> </DropDown>
</StackPanel> </StackPanel>
<StackPanel Orientation="Horizontal" Gap="4"> <StackPanel Orientation="Horizontal" Spacing="4">
<TextBlock VerticalAlignment="Center">Orientation</TextBlock> <TextBlock VerticalAlignment="Center">Orientation</TextBlock>
<DropDown Name="orientation" SelectedIndex="1" VerticalAlignment="Center"> <DropDown Name="orientation" SelectedIndex="1" VerticalAlignment="Center">
<DropDownItem>Horizontal</DropDownItem> <DropDownItem>Horizontal</DropDownItem>
@ -38,4 +38,4 @@
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</UserControl> </UserControl>

10
samples/ControlCatalog/Pages/CheckBoxPage.xaml

@ -1,15 +1,15 @@
<UserControl xmlns="https://github.com/avaloniaui" <UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel Orientation="Vertical" Gap="4"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">CheckBox</TextBlock> <TextBlock Classes="h1">CheckBox</TextBlock>
<TextBlock Classes="h2">A check box control</TextBlock> <TextBlock Classes="h2">A check box control</TextBlock>
<StackPanel Orientation="Horizontal" <StackPanel Orientation="Horizontal"
Margin="0,16,0,0" Margin="0,16,0,0"
HorizontalAlignment="Center" HorizontalAlignment="Center"
Gap="16"> Spacing="16">
<StackPanel Orientation="Vertical" <StackPanel Orientation="Vertical"
Gap="16"> Spacing="16">
<CheckBox>Unchecked</CheckBox> <CheckBox>Unchecked</CheckBox>
<CheckBox IsChecked="True">Checked</CheckBox> <CheckBox IsChecked="True">Checked</CheckBox>
<CheckBox IsChecked="{x:Null}">Indeterminate</CheckBox> <CheckBox IsChecked="{x:Null}">Indeterminate</CheckBox>
@ -17,7 +17,7 @@
</StackPanel> </StackPanel>
<StackPanel Orientation="Vertical" <StackPanel Orientation="Vertical"
HorizontalAlignment="Center" HorizontalAlignment="Center"
Gap="16"> Spacing="16">
<CheckBox IsChecked="False" IsThreeState="True">Three State: Unchecked</CheckBox> <CheckBox IsChecked="False" IsThreeState="True">Three State: Unchecked</CheckBox>
<CheckBox IsChecked="True" IsThreeState="True">Three State: Checked</CheckBox> <CheckBox IsChecked="True" IsThreeState="True">Three State: Checked</CheckBox>
<CheckBox IsChecked="{x:Null}" IsThreeState="True">Three State: Indeterminate</CheckBox> <CheckBox IsChecked="{x:Null}" IsThreeState="True">Three State: Indeterminate</CheckBox>
@ -25,4 +25,4 @@
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</UserControl> </UserControl>

6
samples/ControlCatalog/Pages/ContextMenuPage.xaml

@ -1,12 +1,12 @@
<UserControl xmlns="https://github.com/avaloniaui"> <UserControl xmlns="https://github.com/avaloniaui">
<StackPanel Orientation="Vertical" Gap="4"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Context Menu</TextBlock> <TextBlock Classes="h1">Context Menu</TextBlock>
<TextBlock Classes="h2">A right click menu that can be applied to any control.</TextBlock> <TextBlock Classes="h2">A right click menu that can be applied to any control.</TextBlock>
<StackPanel Orientation="Horizontal" <StackPanel Orientation="Horizontal"
Margin="0,16,0,0" Margin="0,16,0,0"
HorizontalAlignment="Center" HorizontalAlignment="Center"
Gap="16"> Spacing="16">
<Border Background="{DynamicResource ThemeAccentBrush}" <Border Background="{DynamicResource ThemeAccentBrush}"
Padding="48,48,48,48"> Padding="48,48,48,48">
<Border.ContextMenu> <Border.ContextMenu>
@ -33,4 +33,4 @@
</Border> </Border>
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</UserControl> </UserControl>

6
samples/ControlCatalog/Pages/DatePickerPage.xaml

@ -1,13 +1,13 @@
<UserControl xmlns="https://github.com/avaloniaui" <UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel Orientation="Vertical" Gap="4"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">DatePicker</TextBlock> <TextBlock Classes="h1">DatePicker</TextBlock>
<TextBlock Classes="h2">A control for selecting dates with a calendar drop-down</TextBlock> <TextBlock Classes="h2">A control for selecting dates with a calendar drop-down</TextBlock>
<StackPanel Orientation="Horizontal" <StackPanel Orientation="Horizontal"
Margin="0,16,0,0" Margin="0,16,0,0"
HorizontalAlignment="Center" HorizontalAlignment="Center"
Gap="16"> Spacing="16">
<StackPanel Orientation="Vertical" <StackPanel Orientation="Vertical"
Width="200"> Width="200">
<TextBlock Text="SelectedDateFormat: Short"/> <TextBlock Text="SelectedDateFormat: Short"/>
@ -43,4 +43,4 @@
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</UserControl> </UserControl>

4
samples/ControlCatalog/Pages/DialogsPage.xaml

@ -1,5 +1,5 @@
<UserControl xmlns="https://github.com/avaloniaui"> <UserControl xmlns="https://github.com/avaloniaui">
<StackPanel Orientation="Vertical" Gap="4" Margin="4"> <StackPanel Orientation="Vertical" Spacing="4" Margin="4">
<Button Name="OpenFile">Open File</Button> <Button Name="OpenFile">Open File</Button>
<Button Name="SaveFile">Save File</Button> <Button Name="SaveFile">Save File</Button>
<Button Name="SelectFolder">Select Folder</Button> <Button Name="SelectFolder">Select Folder</Button>
@ -9,4 +9,4 @@
</StackPanel> </StackPanel>
<Button Name="DecoratedWindow">Decorated window</Button> <Button Name="DecoratedWindow">Decorated window</Button>
</StackPanel> </StackPanel>
</UserControl> </UserControl>

6
samples/ControlCatalog/Pages/DragAndDropPage.xaml

@ -1,12 +1,12 @@
<UserControl xmlns="https://github.com/avaloniaui"> <UserControl xmlns="https://github.com/avaloniaui">
<StackPanel Orientation="Vertical" Gap="4"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Drag+Drop</TextBlock> <TextBlock Classes="h1">Drag+Drop</TextBlock>
<TextBlock Classes="h2">Example of Drag+Drop capabilities</TextBlock> <TextBlock Classes="h2">Example of Drag+Drop capabilities</TextBlock>
<StackPanel Orientation="Horizontal" <StackPanel Orientation="Horizontal"
Margin="0,16,0,0" Margin="0,16,0,0"
HorizontalAlignment="Center" HorizontalAlignment="Center"
Gap="16"> Spacing="16">
<Border BorderBrush="{DynamicResource ThemeAccentBrush}" BorderThickness="2" Padding="16" Name="DragMe"> <Border BorderBrush="{DynamicResource ThemeAccentBrush}" BorderThickness="2" Padding="16" Name="DragMe">
<TextBlock Name="DragState">Drag Me</TextBlock> <TextBlock Name="DragState">Drag Me</TextBlock>
</Border> </Border>
@ -16,4 +16,4 @@
</Border> </Border>
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</UserControl> </UserControl>

6
samples/ControlCatalog/Pages/DropDownPage.xaml

@ -1,9 +1,9 @@
<UserControl xmlns="https://github.com/avaloniaui"> <UserControl xmlns="https://github.com/avaloniaui">
<StackPanel Orientation="Vertical" Gap="4"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">DropDown</TextBlock> <TextBlock Classes="h1">DropDown</TextBlock>
<TextBlock Classes="h2">A drop-down list.</TextBlock> <TextBlock Classes="h2">A drop-down list.</TextBlock>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0 16 0 0" Gap="8"> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0 16 0 0" Spacing="8">
<DropDown SelectedIndex="0"> <DropDown SelectedIndex="0">
<DropDownItem>Inline Items</DropDownItem> <DropDownItem>Inline Items</DropDownItem>
<DropDownItem>Inline Item 2</DropDownItem> <DropDownItem>Inline Item 2</DropDownItem>
@ -28,4 +28,4 @@
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</UserControl> </UserControl>

6
samples/ControlCatalog/Pages/ExpanderPage.xaml

@ -1,12 +1,12 @@
<UserControl xmlns="https://github.com/avaloniaui"> <UserControl xmlns="https://github.com/avaloniaui">
<StackPanel Orientation="Vertical" Gap="4"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Expander</TextBlock> <TextBlock Classes="h1">Expander</TextBlock>
<TextBlock Classes="h2">Expands to show nested content</TextBlock> <TextBlock Classes="h2">Expands to show nested content</TextBlock>
<StackPanel Orientation="Vertical" <StackPanel Orientation="Vertical"
Margin="0,16,0,0" Margin="0,16,0,0"
HorizontalAlignment="Center" HorizontalAlignment="Center"
Gap="16"> Spacing="16">
<Expander Header="Expand Up" ExpandDirection="Up"> <Expander Header="Expand Up" ExpandDirection="Up">
<StackPanel> <StackPanel>
<TextBlock>Expanded content</TextBlock> <TextBlock>Expanded content</TextBlock>
@ -29,4 +29,4 @@
</Expander> </Expander>
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</UserControl> </UserControl>

4
samples/ControlCatalog/Pages/ImagePage.xaml

@ -1,12 +1,12 @@
<UserControl xmlns="https://github.com/avaloniaui"> <UserControl xmlns="https://github.com/avaloniaui">
<StackPanel Orientation="Vertical" Gap="4"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Image</TextBlock> <TextBlock Classes="h1">Image</TextBlock>
<TextBlock Classes="h2">Displays an image</TextBlock> <TextBlock Classes="h2">Displays an image</TextBlock>
<StackPanel Orientation="Horizontal" <StackPanel Orientation="Horizontal"
Margin="0,16,0,0" Margin="0,16,0,0"
HorizontalAlignment="Center" HorizontalAlignment="Center"
Gap="16"> Spacing="16">
<StackPanel Orientation="Vertical"> <StackPanel Orientation="Vertical">
<TextBlock>No Stretch</TextBlock> <TextBlock>No Stretch</TextBlock>
<Image Source="resm:ControlCatalog.Assets.delicate-arch-896885_640.jpg" <Image Source="resm:ControlCatalog.Assets.delicate-arch-896885_640.jpg"

4
samples/ControlCatalog/Pages/MenuPage.xaml

@ -1,12 +1,12 @@
<UserControl xmlns="https://github.com/avaloniaui"> <UserControl xmlns="https://github.com/avaloniaui">
<StackPanel Orientation="Vertical" Gap="4"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Menu</TextBlock> <TextBlock Classes="h1">Menu</TextBlock>
<TextBlock Classes="h2">A window menu</TextBlock> <TextBlock Classes="h2">A window menu</TextBlock>
<StackPanel Orientation="Horizontal" <StackPanel Orientation="Horizontal"
Margin="0,16,0,0" Margin="0,16,0,0"
HorizontalAlignment="Center" HorizontalAlignment="Center"
Gap="16"> Spacing="16">
<Menu> <Menu>
<MenuItem Header="_First"> <MenuItem Header="_First">
<MenuItem Header="Standard _Menu Item"/> <MenuItem Header="Standard _Menu Item"/>

6
samples/ControlCatalog/Pages/NumericUpDownPage.xaml

@ -1,6 +1,6 @@
<UserControl xmlns="https://github.com/avaloniaui" <UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel Orientation="Vertical" Gap="4"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Margin="2" Classes="h1">Numeric up-down control</TextBlock> <TextBlock Margin="2" Classes="h1">Numeric up-down control</TextBlock>
<TextBlock Margin="2" Classes="h2" TextWrapping="Wrap">Numeric up-down control provides a TextBox with button spinners that allow incrementing and decrementing numeric values by using the spinner buttons, keyboard up/down arrows, or mouse wheel.</TextBlock> <TextBlock Margin="2" Classes="h2" TextWrapping="Wrap">Numeric up-down control provides a TextBox with button spinners that allow incrementing and decrementing numeric values by using the spinner buttons, keyboard up/down arrows, or mouse wheel.</TextBlock>
@ -26,7 +26,7 @@
VerticalAlignment="Center" Margin="2"> VerticalAlignment="Center" Margin="2">
<DropDown.ItemTemplate> <DropDown.ItemTemplate>
<DataTemplate> <DataTemplate>
<StackPanel Orientation="Horizontal" Gap="2"> <StackPanel Orientation="Horizontal" Spacing="2">
<TextBlock Text="{Binding Name}"/> <TextBlock Text="{Binding Name}"/>
<TextBlock Text="-"/> <TextBlock Text="-"/>
<TextBlock Text="{Binding Value}"/> <TextBlock Text="{Binding Value}"/>
@ -69,7 +69,7 @@
</Grid> </Grid>
</Grid> </Grid>
<StackPanel Margin="2,10,2,2" Orientation="Horizontal" Gap="10"> <StackPanel Margin="2,10,2,2" Orientation="Horizontal" Spacing="10">
<TextBlock FontSize="14" FontWeight="Bold" VerticalAlignment="Center">Usage of NumericUpDown:</TextBlock> <TextBlock FontSize="14" FontWeight="Bold" VerticalAlignment="Center">Usage of NumericUpDown:</TextBlock>
<NumericUpDown Name="upDown" Minimum="0" Maximum="10" Increment="0.5" <NumericUpDown Name="upDown" Minimum="0" Maximum="10" Increment="0.5"
CultureInfo="en-US" VerticalAlignment="Center" Height="25" Width="100" CultureInfo="en-US" VerticalAlignment="Center" Height="25" Width="100"

6
samples/ControlCatalog/Pages/ProgressBarPage.xaml

@ -1,5 +1,5 @@
<UserControl xmlns="https://github.com/avaloniaui"> <UserControl xmlns="https://github.com/avaloniaui">
<StackPanel Orientation="Vertical" Gap="4"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">ProgressBar</TextBlock> <TextBlock Classes="h1">ProgressBar</TextBlock>
<TextBlock Classes="h2">A progress bar control</TextBlock> <TextBlock Classes="h2">A progress bar control</TextBlock>
@ -7,8 +7,8 @@
<StackPanel Orientation="Horizontal" <StackPanel Orientation="Horizontal"
Margin="0,16,0,0" Margin="0,16,0,0"
HorizontalAlignment="Center" HorizontalAlignment="Center"
Gap="16"> Spacing="16">
<StackPanel Gap="16"> <StackPanel Spacing="16">
<ProgressBar Value="{Binding #hprogress.Value}" /> <ProgressBar Value="{Binding #hprogress.Value}" />
<ProgressBar IsIndeterminate="True"/> <ProgressBar IsIndeterminate="True"/>
</StackPanel> </StackPanel>

8
samples/ControlCatalog/Pages/RadioButtonPage.xaml

@ -1,22 +1,22 @@
<UserControl xmlns="https://github.com/avaloniaui" <UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel Orientation="Vertical" Gap="4"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">RadioButton</TextBlock> <TextBlock Classes="h1">RadioButton</TextBlock>
<TextBlock Classes="h2">Allows the selection of a single option of many</TextBlock> <TextBlock Classes="h2">Allows the selection of a single option of many</TextBlock>
<StackPanel Orientation="Horizontal" <StackPanel Orientation="Horizontal"
Margin="0,16,0,0" Margin="0,16,0,0"
HorizontalAlignment="Center" HorizontalAlignment="Center"
Gap="16"> Spacing="16">
<StackPanel Orientation="Vertical" <StackPanel Orientation="Vertical"
Gap="16"> Spacing="16">
<RadioButton IsChecked="True">Option 1</RadioButton> <RadioButton IsChecked="True">Option 1</RadioButton>
<RadioButton>Option 2</RadioButton> <RadioButton>Option 2</RadioButton>
<RadioButton IsChecked="{x:Null}">Option 3</RadioButton> <RadioButton IsChecked="{x:Null}">Option 3</RadioButton>
<RadioButton IsEnabled="False">Disabled</RadioButton> <RadioButton IsEnabled="False">Disabled</RadioButton>
</StackPanel> </StackPanel>
<StackPanel Orientation="Vertical" <StackPanel Orientation="Vertical"
Gap="16"> Spacing="16">
<RadioButton IsChecked="True" IsThreeState="True">Three States: Option 1</RadioButton> <RadioButton IsChecked="True" IsThreeState="True">Three States: Option 1</RadioButton>
<RadioButton IsChecked="False" IsThreeState="True">Three States: Option 2</RadioButton> <RadioButton IsChecked="False" IsThreeState="True">Three States: Option 2</RadioButton>
<RadioButton IsChecked="{x:Null}" IsThreeState="True">Three States: Option 3</RadioButton> <RadioButton IsChecked="{x:Null}" IsThreeState="True">Three States: Option 3</RadioButton>

4
samples/ControlCatalog/Pages/SliderPage.xaml

@ -1,9 +1,9 @@
<UserControl xmlns="https://github.com/avaloniaui"> <UserControl xmlns="https://github.com/avaloniaui">
<StackPanel Orientation="Vertical" Gap="4"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Slider</TextBlock> <TextBlock Classes="h1">Slider</TextBlock>
<TextBlock Classes="h2">A control that lets the user select from a range of values by moving a Thumb control along a Track.</TextBlock> <TextBlock Classes="h2">A control that lets the user select from a range of values by moving a Thumb control along a Track.</TextBlock>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0 16 0 0" Gap="16"> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0 16 0 0" Spacing="16">
<Slider Value="0" <Slider Value="0"
Minimum="0" Minimum="0"
Maximum="100" Maximum="100"

11
samples/ControlCatalog/Pages/TextBoxPage.xaml

@ -1,14 +1,15 @@
<UserControl xmlns="https://github.com/avaloniaui"> <UserControl xmlns="https://github.com/avaloniaui">
<StackPanel Orientation="Vertical" Gap="4"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">TextBox</TextBlock> <TextBlock Classes="h1">TextBox</TextBlock>
<TextBlock Classes="h2">A control into which the user can input text</TextBlock> <TextBlock Classes="h2">A control into which the user can input text</TextBlock>
<StackPanel Orientation="Horizontal" <StackPanel Orientation="Horizontal"
Margin="0,16,0,0" Margin="0,16,0,0"
HorizontalAlignment="Center" HorizontalAlignment="Center"
Gap="16"> Spacing="16">
<StackPanel Orientation="Vertical" Gap="8"> <StackPanel Orientation="Vertical" Spacing="8">
<TextBox Text="Lorem ipsum dolor sit amet, consectetur adipiscing elit." Width="200" /> <TextBox Text="Lorem ipsum dolor sit amet, consectetur adipiscing elit." Width="200" />
<TextBox Watermark="ReadOnly" IsReadOnly="True" Text="This is read only"/>
<TextBox Width="200" Watermark="Watermark" /> <TextBox Width="200" Watermark="Watermark" />
<TextBox Width="200" <TextBox Width="200"
Watermark="Floating Watermark" Watermark="Floating Watermark"
@ -25,13 +26,13 @@
<TextBox Width="200" Text="Right aligned text" TextAlignment="Right" /> <TextBox Width="200" Text="Right aligned text" TextAlignment="Right" />
</StackPanel> </StackPanel>
<StackPanel Orientation="Vertical" Gap="8"> <StackPanel Orientation="Vertical" Spacing="8">
<TextBox AcceptsReturn="True" TextWrapping="Wrap" Width="200" Height="125" <TextBox AcceptsReturn="True" TextWrapping="Wrap" Width="200" Height="125"
Text="Multiline TextBox with TextWrapping.&#xD;&#xD;Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est." /> Text="Multiline TextBox with TextWrapping.&#xD;&#xD;Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est." />
<TextBox AcceptsReturn="True" Width="200" Height="125" <TextBox AcceptsReturn="True" Width="200" Height="125"
Text="Multiline TextBox with no TextWrapping.&#xD;&#xD;Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est." /> Text="Multiline TextBox with no TextWrapping.&#xD;&#xD;Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est." />
</StackPanel> </StackPanel>
<StackPanel Orientation="Vertical" Gap="8"> <StackPanel Orientation="Vertical" Spacing="8">
<TextBox Width="200" Text="Custom font regular" FontWeight="Normal" FontStyle="Normal" FontFamily="resm:ControlCatalog.Assets.Fonts?assembly=ControlCatalog#Source Sans Pro"/> <TextBox Width="200" Text="Custom font regular" FontWeight="Normal" FontStyle="Normal" FontFamily="resm:ControlCatalog.Assets.Fonts?assembly=ControlCatalog#Source Sans Pro"/>
<TextBox Width="200" Text="Custom font bold" FontWeight="Bold" FontStyle="Normal" FontFamily="resm:ControlCatalog.Assets.Fonts?assembly=ControlCatalog#Source Sans Pro"/> <TextBox Width="200" Text="Custom font bold" FontWeight="Bold" FontStyle="Normal" FontFamily="resm:ControlCatalog.Assets.Fonts?assembly=ControlCatalog#Source Sans Pro"/>
<TextBox Width="200" Text="Custom font italic" FontWeight="Normal" FontStyle="Italic" FontFamily="resm:ControlCatalog.Assets.Fonts.SourceSansPro-Italic.ttf?assembly=ControlCatalog#Source Sans Pro"/> <TextBox Width="200" Text="Custom font italic" FontWeight="Normal" FontStyle="Italic" FontFamily="resm:ControlCatalog.Assets.Fonts.SourceSansPro-Italic.ttf?assembly=ControlCatalog#Source Sans Pro"/>

2
samples/ControlCatalog/Pages/ToolTipPage.xaml

@ -1,6 +1,6 @@
<UserControl xmlns="https://github.com/avaloniaui"> <UserControl xmlns="https://github.com/avaloniaui">
<StackPanel Orientation="Vertical" <StackPanel Orientation="Vertical"
Gap="4"> Spacing="4">
<TextBlock Classes="h1">ToolTip</TextBlock> <TextBlock Classes="h1">ToolTip</TextBlock>
<TextBlock Classes="h2">A control which pops up a hint when a control is hovered</TextBlock> <TextBlock Classes="h2">A control which pops up a hint when a control is hovered</TextBlock>

4
samples/ControlCatalog/Pages/TreeViewPage.xaml

@ -1,12 +1,12 @@
<UserControl xmlns="https://github.com/avaloniaui"> <UserControl xmlns="https://github.com/avaloniaui">
<StackPanel Orientation="Vertical" Gap="4"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">TreeView</TextBlock> <TextBlock Classes="h1">TreeView</TextBlock>
<TextBlock Classes="h2">Displays a hierachical tree of data.</TextBlock> <TextBlock Classes="h2">Displays a hierachical tree of data.</TextBlock>
<StackPanel Orientation="Horizontal" <StackPanel Orientation="Horizontal"
Margin="0,16,0,0" Margin="0,16,0,0"
HorizontalAlignment="Center" HorizontalAlignment="Center"
Gap="16"> Spacing="16">
<TreeView Items="{Binding}" Width="250" Height="350"> <TreeView Items="{Binding}" Width="250" Height="350">
<TreeView.ItemTemplate> <TreeView.ItemTemplate>
<TreeDataTemplate ItemsSource="{Binding Children}"> <TreeDataTemplate ItemsSource="{Binding Children}">

2
samples/VirtualizationDemo/MainWindow.xaml

@ -6,7 +6,7 @@
<StackPanel DockPanel.Dock="Right" <StackPanel DockPanel.Dock="Right"
Margin="16 0 0 0" Margin="16 0 0 0"
MinWidth="150" MinWidth="150"
Gap="4"> Spacing="4">
<DropDown Items="{Binding VirtualizationModes}" <DropDown Items="{Binding VirtualizationModes}"
SelectedItem="{Binding VirtualizationMode}"/> SelectedItem="{Binding VirtualizationMode}"/>
<DropDown Items="{Binding Orientations}" <DropDown Items="{Binding Orientations}"

82
src/Avalonia.Animation/Animation.cs

@ -12,13 +12,14 @@ using System.Reflection;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Reactive.Linq; using System.Reactive.Linq;
using System.Reactive.Disposables;
namespace Avalonia.Animation namespace Avalonia.Animation
{ {
/// <summary> /// <summary>
/// Tracks the progress of an animation. /// Tracks the progress of an animation.
/// </summary> /// </summary>
public class Animation : AvaloniaList<KeyFrame>, IDisposable, IAnimation public class Animation : AvaloniaList<KeyFrame>, IAnimation
{ {
private readonly static List<(Func<AvaloniaProperty, bool> Condition, Type Animator)> Animators = new List<(Func<AvaloniaProperty, bool>, Type)> private readonly static List<(Func<AvaloniaProperty, bool> Condition, Type Animator)> Animators = new List<(Func<AvaloniaProperty, bool>, Type)>
{ {
@ -43,8 +44,6 @@ namespace Avalonia.Animation
return null; return null;
} }
private bool _isChildrenChanged = false;
private List<IDisposable> _subscription = new List<IDisposable>();
public AvaloniaList<IAnimator> _animators { get; set; } = new AvaloniaList<IAnimator>(); public AvaloniaList<IAnimator> _animators { get; set; } = new AvaloniaList<IAnimator>();
/// <summary> /// <summary>
@ -77,20 +76,11 @@ namespace Avalonia.Animation
/// </summary> /// </summary>
public Easing Easing { get; set; } = new LinearEasing(); public Easing Easing { get; set; } = new LinearEasing();
/// <summary> private (IList<IAnimator> Animators, IList<IDisposable> subscriptions) InterpretKeyframes(Animatable control)
/// Triggers when the animation is completed.
/// </summary>
public event EventHandler Done;
public Animation()
{
this.CollectionChanged += delegate { _isChildrenChanged = true; };
}
private IList<IAnimator> InterpretKeyframes(Animatable control)
{ {
var handlerList = new List<(Type type, AvaloniaProperty property)>(); var handlerList = new List<(Type type, AvaloniaProperty property)>();
var animatorKeyFrames = new List<AnimatorKeyFrame>(); var animatorKeyFrames = new List<AnimatorKeyFrame>();
var subscriptions = new List<IDisposable>();
foreach (var keyframe in this) foreach (var keyframe in this)
{ {
@ -115,7 +105,7 @@ namespace Avalonia.Animation
var newKF = new AnimatorKeyFrame(handler, cue); var newKF = new AnimatorKeyFrame(handler, cue);
_subscription.Add(newKF.BindSetter(setter, control)); subscriptions.Add(newKF.BindSetter(setter, control));
animatorKeyFrames.Add(newKF); animatorKeyFrames.Add(newKF);
} }
@ -137,28 +127,38 @@ namespace Avalonia.Animation
animator.Add(keyframe); animator.Add(keyframe);
} }
return newAnimatorInstances; return (newAnimatorInstances, subscriptions);
} }
/// <summary> /// <inheritdocs/>
/// Cancels the animation. public IDisposable Apply(Animatable control, IObservable<bool> match, Action onComplete)
/// </summary>
public void Dispose()
{ {
foreach (var sub in _subscription) var (animators, subscriptions) = InterpretKeyframes(control);
if (animators.Count == 1)
{ {
sub.Dispose(); subscriptions.Add(animators[0].Apply(this, control, match, onComplete));
} }
} else
/// <inheritdocs/>
public IDisposable Apply(Animatable control, IObservable<bool> matchObs)
{
foreach (IAnimator animator in InterpretKeyframes(control))
{ {
_subscription.Add(animator.Apply(this, control, matchObs)); var completionTasks = onComplete != null ? new List<Task>() : null;
foreach (IAnimator animator in animators)
{
Action animatorOnComplete = null;
if (onComplete != null)
{
var tcs = new TaskCompletionSource<object>();
animatorOnComplete = () => tcs.SetResult(null);
completionTasks.Add(tcs.Task);
}
subscriptions.Add(animator.Apply(this, control, match, animatorOnComplete));
}
if (onComplete != null)
{
Task.WhenAll(completionTasks).ContinueWith(_ => onComplete());
}
} }
return this; return new CompositeDisposable(subscriptions);
} }
/// <inheritdocs/> /// <inheritdocs/>
@ -169,26 +169,14 @@ namespace Avalonia.Animation
if (this.RepeatCount == RepeatCount.Loop) if (this.RepeatCount == RepeatCount.Loop)
run.SetException(new InvalidOperationException("Looping animations must not use the Run method.")); run.SetException(new InvalidOperationException("Looping animations must not use the Run method."));
EventHandler doneCallback = null; IDisposable subscriptions = null;
doneCallback = (sender, args) => subscriptions = this.Apply(control, Observable.Return(true), () =>
{ {
if (sender == control) run.SetResult(null);
{ subscriptions?.Dispose();
run.SetResult(null); });
this.Done -= doneCallback;
}
};
this.Done += doneCallback;
this.Apply(control, Observable.Return(true));
return run.Task; return run.Task;
} }
internal void SetDone(Animatable control)
{
Done?.Invoke(control, null);
}
} }
} }

2
src/Avalonia.Animation/AnimatorKeyFrame.cs

@ -16,7 +16,7 @@ namespace Avalonia.Animation
public class AnimatorKeyFrame : AvaloniaObject public class AnimatorKeyFrame : AvaloniaObject
{ {
public static readonly DirectProperty<AnimatorKeyFrame, object> ValueProperty = public static readonly DirectProperty<AnimatorKeyFrame, object> ValueProperty =
AvaloniaProperty.RegisterDirect<AnimatorKeyFrame, object>(nameof(Value), k => k._value, (k, v) => k._value = v); AvaloniaProperty.RegisterDirect<AnimatorKeyFrame, object>(nameof(Value), k => k.Value, (k, v) => k.Value = v);
public AnimatorKeyFrame() public AnimatorKeyFrame()
{ {

6
src/Avalonia.Animation/AnimatorStateMachine`1.cs

@ -35,6 +35,7 @@ namespace Avalonia.Animation
private T _neutralValue; private T _neutralValue;
internal bool _unsubscribe = false; internal bool _unsubscribe = false;
private IObserver<object> _targetObserver; private IObserver<object> _targetObserver;
private readonly Action _onComplete;
[Flags] [Flags]
private enum KeyFramesStates private enum KeyFramesStates
@ -51,7 +52,7 @@ namespace Avalonia.Animation
Disposed Disposed
} }
public void Initialize(Animation animation, Animatable control, Animator<T> animator) public AnimatorStateMachine(Animation animation, Animatable control, Animator<T> animator, Action onComplete)
{ {
_parent = animator; _parent = animator;
_targetAnimation = animation; _targetAnimation = animation;
@ -82,6 +83,7 @@ namespace Avalonia.Animation
_currentState = KeyFramesStates.DoDelay; _currentState = KeyFramesStates.DoDelay;
else else
_currentState = KeyFramesStates.DoRun; _currentState = KeyFramesStates.DoRun;
_onComplete = onComplete;
} }
public void Step(PlayState _playState, Func<double, T, T> Interpolator) public void Step(PlayState _playState, Func<double, T, T> Interpolator)
@ -245,7 +247,7 @@ namespace Avalonia.Animation
} }
_targetObserver.OnCompleted(); _targetObserver.OnCompleted();
_targetAnimation.SetDone(_targetControl); _onComplete?.Invoke();
Dispose(); Dispose();
handled = true; handled = true;
break; break;

9
src/Avalonia.Animation/Animator`1.cs

@ -35,7 +35,7 @@ namespace Avalonia.Animation
} }
/// <inheritdoc/> /// <inheritdoc/>
public virtual IDisposable Apply(Animation animation, Animatable control, IObservable<bool> obsMatch) public virtual IDisposable Apply(Animation animation, Animatable control, IObservable<bool> obsMatch, Action onComplete)
{ {
if (!_isVerfifiedAndConverted) if (!_isVerfifiedAndConverted)
VerifyConvertKeyFrames(); VerifyConvertKeyFrames();
@ -45,7 +45,7 @@ namespace Avalonia.Animation
.Where(p => p && Timing.GetGlobalPlayState() != PlayState.Pause) .Where(p => p && Timing.GetGlobalPlayState() != PlayState.Pause)
.Subscribe(_ => .Subscribe(_ =>
{ {
var timerObs = RunKeyFrames(animation, control); var timerObs = RunKeyFrames(animation, control, onComplete);
}); });
} }
@ -97,10 +97,9 @@ namespace Avalonia.Animation
/// <summary> /// <summary>
/// Runs the KeyFrames Animation. /// Runs the KeyFrames Animation.
/// </summary> /// </summary>
private IDisposable RunKeyFrames(Animation animation, Animatable control) private IDisposable RunKeyFrames(Animation animation, Animatable control, Action onComplete)
{ {
var stateMachine = new AnimatorStateMachine<T>(); var stateMachine = new AnimatorStateMachine<T>(animation, control, this, onComplete);
stateMachine.Initialize(animation, control, this);
Timing.AnimationStateTimer Timing.AnimationStateTimer
.TakeWhile(_ => !stateMachine._unsubscribe) .TakeWhile(_ => !stateMachine._unsubscribe)

4
src/Avalonia.Animation/IAnimation.cs

@ -13,11 +13,11 @@ namespace Avalonia.Animation
/// <summary> /// <summary>
/// Apply the animation to the specified control /// Apply the animation to the specified control
/// </summary> /// </summary>
IDisposable Apply(Animatable control, IObservable<bool> match); IDisposable Apply(Animatable control, IObservable<bool> match, Action onComplete = null);
/// <summary> /// <summary>
/// Run the animation to the specified control /// Run the animation to the specified control
/// </summary> /// </summary>
Task RunAsync(Animatable control); Task RunAsync(Animatable control);
} }
} }

2
src/Avalonia.Animation/IAnimator.cs

@ -17,6 +17,6 @@ namespace Avalonia.Animation
/// <summary> /// <summary>
/// Applies the current KeyFrame group to the specified control. /// Applies the current KeyFrame group to the specified control.
/// </summary> /// </summary>
IDisposable Apply(Animation animation, Animatable control, IObservable<bool> obsMatch); IDisposable Apply(Animation animation, Animatable control, IObservable<bool> obsMatch, Action onComplete);
} }
} }

9
src/Avalonia.Controls/Image.cs

@ -1,12 +1,11 @@
// Copyright (c) The Avalonia Project. All rights reserved. // Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information. // Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Media.Imaging; using Avalonia.Media.Imaging;
namespace Avalonia.Controls namespace Avalonia.Controls
{ {
/// <summary> /// <summary>
/// Displays a <see cref="Bitmap"/> image. /// Displays a <see cref="Bitmap"/> image.
/// </summary> /// </summary>
@ -68,7 +67,9 @@ namespace Avalonia.Controls
Rect sourceRect = new Rect(sourceSize) Rect sourceRect = new Rect(sourceSize)
.CenterRect(new Rect(destRect.Size / scale)); .CenterRect(new Rect(destRect.Size / scale));
context.DrawImage(source, 1, sourceRect, destRect); var interpolationMode = RenderOptions.GetBitmapInterpolationMode(this);
context.DrawImage(source, 1, sourceRect, destRect, interpolationMode);
} }
} }
@ -100,4 +101,4 @@ namespace Avalonia.Controls
} }
} }
} }
} }

5
src/Avalonia.Controls/Presenters/ItemVirtualizer.cs

@ -161,6 +161,11 @@ namespace Avalonia.Controls.Presenters
/// <returns>An <see cref="ItemVirtualizer"/>.</returns> /// <returns>An <see cref="ItemVirtualizer"/>.</returns>
public static ItemVirtualizer Create(ItemsPresenter owner) public static ItemVirtualizer Create(ItemsPresenter owner)
{ {
if (owner.Panel == null)
{
return null;
}
var virtualizingPanel = owner.Panel as IVirtualizingPanel; var virtualizingPanel = owner.Panel as IVirtualizingPanel;
var scrollable = (ILogicalScrollable)owner; var scrollable = (ILogicalScrollable)owner;
ItemVirtualizer result = null; ItemVirtualizer result = null;

36
src/Avalonia.Controls/Presenters/ItemsPresenter.cs

@ -22,7 +22,6 @@ namespace Avalonia.Controls.Presenters
nameof(VirtualizationMode), nameof(VirtualizationMode),
defaultValue: ItemVirtualizationMode.None); defaultValue: ItemVirtualizationMode.None);
private ItemVirtualizer _virtualizer;
private bool _canHorizontallyScroll; private bool _canHorizontallyScroll;
private bool _canVerticallyScroll; private bool _canVerticallyScroll;
@ -76,21 +75,27 @@ namespace Avalonia.Controls.Presenters
/// <inheritdoc/> /// <inheritdoc/>
bool ILogicalScrollable.IsLogicalScrollEnabled bool ILogicalScrollable.IsLogicalScrollEnabled
{ {
get { return _virtualizer?.IsLogicalScrollEnabled ?? false; } get { return Virtualizer?.IsLogicalScrollEnabled ?? false; }
} }
/// <inheritdoc/> /// <inheritdoc/>
Size IScrollable.Extent => _virtualizer.Extent; Size IScrollable.Extent => Virtualizer?.Extent ?? Size.Empty;
/// <inheritdoc/> /// <inheritdoc/>
Vector IScrollable.Offset Vector IScrollable.Offset
{ {
get { return _virtualizer.Offset; } get { return Virtualizer?.Offset ?? new Vector(); }
set { _virtualizer.Offset = CoerceOffset(value); } set
{
if (Virtualizer != null)
{
Virtualizer.Offset = CoerceOffset(value);
}
}
} }
/// <inheritdoc/> /// <inheritdoc/>
Size IScrollable.Viewport => _virtualizer.Viewport; Size IScrollable.Viewport => Virtualizer?.Viewport ?? Bounds.Size;
/// <inheritdoc/> /// <inheritdoc/>
Action ILogicalScrollable.InvalidateScroll { get; set; } Action ILogicalScrollable.InvalidateScroll { get; set; }
@ -101,6 +106,8 @@ namespace Avalonia.Controls.Presenters
/// <inheritdoc/> /// <inheritdoc/>
Size ILogicalScrollable.PageScrollSize => new Size(0, 1); Size ILogicalScrollable.PageScrollSize => new Size(0, 1);
internal ItemVirtualizer Virtualizer { get; private set; }
/// <inheritdoc/> /// <inheritdoc/>
bool ILogicalScrollable.BringIntoView(IControl target, Rect targetRect) bool ILogicalScrollable.BringIntoView(IControl target, Rect targetRect)
{ {
@ -110,29 +117,30 @@ namespace Avalonia.Controls.Presenters
/// <inheritdoc/> /// <inheritdoc/>
IControl ILogicalScrollable.GetControlInDirection(NavigationDirection direction, IControl from) IControl ILogicalScrollable.GetControlInDirection(NavigationDirection direction, IControl from)
{ {
return _virtualizer?.GetControlInDirection(direction, from); return Virtualizer?.GetControlInDirection(direction, from);
} }
public override void ScrollIntoView(object item) public override void ScrollIntoView(object item)
{ {
_virtualizer?.ScrollIntoView(item); Virtualizer?.ScrollIntoView(item);
} }
/// <inheritdoc/> /// <inheritdoc/>
protected override Size MeasureOverride(Size availableSize) protected override Size MeasureOverride(Size availableSize)
{ {
return _virtualizer?.MeasureOverride(availableSize) ?? Size.Empty; return Virtualizer?.MeasureOverride(availableSize) ?? Size.Empty;
} }
protected override Size ArrangeOverride(Size finalSize) protected override Size ArrangeOverride(Size finalSize)
{ {
return _virtualizer?.ArrangeOverride(finalSize) ?? Size.Empty; return Virtualizer?.ArrangeOverride(finalSize) ?? Size.Empty;
} }
/// <inheritdoc/> /// <inheritdoc/>
protected override void PanelCreated(IPanel panel) protected override void PanelCreated(IPanel panel)
{ {
_virtualizer = ItemVirtualizer.Create(this); Virtualizer?.Dispose();
Virtualizer = ItemVirtualizer.Create(this);
((ILogicalScrollable)this).InvalidateScroll?.Invoke(); ((ILogicalScrollable)this).InvalidateScroll?.Invoke();
if (!Panel.IsSet(KeyboardNavigation.DirectionalNavigationProperty)) if (!Panel.IsSet(KeyboardNavigation.DirectionalNavigationProperty))
@ -149,7 +157,7 @@ namespace Avalonia.Controls.Presenters
protected override void ItemsChanged(NotifyCollectionChangedEventArgs e) protected override void ItemsChanged(NotifyCollectionChangedEventArgs e)
{ {
_virtualizer?.ItemsChanged(Items, e); Virtualizer?.ItemsChanged(Items, e);
} }
private Vector CoerceOffset(Vector value) private Vector CoerceOffset(Vector value)
@ -162,8 +170,8 @@ namespace Avalonia.Controls.Presenters
private void VirtualizationModeChanged(AvaloniaPropertyChangedEventArgs e) private void VirtualizationModeChanged(AvaloniaPropertyChangedEventArgs e)
{ {
_virtualizer?.Dispose(); Virtualizer?.Dispose();
_virtualizer = ItemVirtualizer.Create(this); Virtualizer = ItemVirtualizer.Create(this);
((ILogicalScrollable)this).InvalidateScroll?.Invoke(); ((ILogicalScrollable)this).InvalidateScroll?.Invoke();
} }
} }

30
src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs

@ -4,6 +4,7 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Specialized; using System.Collections.Specialized;
using Avalonia.Collections;
using Avalonia.Controls.Generators; using Avalonia.Controls.Generators;
using Avalonia.Controls.Templates; using Avalonia.Controls.Templates;
using Avalonia.Styling; using Avalonia.Styling;
@ -40,6 +41,7 @@ namespace Avalonia.Controls.Presenters
ItemsControl.MemberSelectorProperty.AddOwner<ItemsPresenterBase>(); ItemsControl.MemberSelectorProperty.AddOwner<ItemsPresenterBase>();
private IEnumerable _items; private IEnumerable _items;
private IDisposable _itemsSubscription;
private bool _createdPanel; private bool _createdPanel;
private IItemContainerGenerator _generator; private IItemContainerGenerator _generator;
@ -63,24 +65,12 @@ namespace Avalonia.Controls.Presenters
set set
{ {
if (_createdPanel) _itemsSubscription?.Dispose();
{ _itemsSubscription = null;
INotifyCollectionChanged incc = _items as INotifyCollectionChanged;
if (incc != null)
{
incc.CollectionChanged -= ItemsCollectionChanged;
}
}
if (_createdPanel && value != null) if (_createdPanel && value is INotifyCollectionChanged incc)
{ {
INotifyCollectionChanged incc = value as INotifyCollectionChanged; _itemsSubscription = incc.WeakSubscribe(ItemsCollectionChanged);
if (incc != null)
{
incc.CollectionChanged += ItemsCollectionChanged;
}
} }
SetAndRaise(ItemsProperty, ref _items, value); SetAndRaise(ItemsProperty, ref _items, value);
@ -233,11 +223,9 @@ namespace Avalonia.Controls.Presenters
_createdPanel = true; _createdPanel = true;
INotifyCollectionChanged incc = Items as INotifyCollectionChanged; if (_itemsSubscription == null && Items is INotifyCollectionChanged incc)
if (incc != null)
{ {
incc.CollectionChanged += ItemsCollectionChanged; _itemsSubscription = incc.WeakSubscribe(ItemsCollectionChanged);
} }
PanelCreated(Panel); PanelCreated(Panel);
@ -263,4 +251,4 @@ namespace Avalonia.Controls.Presenters
(e.NewValue as IItemsPresenterHost)?.RegisterItemsPresenter(this); (e.NewValue as IItemsPresenterHost)?.RegisterItemsPresenter(this);
} }
} }
} }

6
src/Avalonia.Controls/Presenters/TextPresenter.cs

@ -189,6 +189,12 @@ namespace Avalonia.Controls.Presenters
_caretTimer.Start(); _caretTimer.Start();
InvalidateVisual(); InvalidateVisual();
} }
else
{
_caretTimer.Start();
InvalidateVisual();
_caretTimer.Stop();
}
if (IsMeasureValid) if (IsMeasureValid)
{ {

2
src/Avalonia.Controls/Primitives/SelectingItemsControl.cs

@ -376,7 +376,7 @@ namespace Avalonia.Controls.Primitives
break; break;
case NotifyCollectionChangedAction.Reset: case NotifyCollectionChangedAction.Reset:
SelectedIndex = IndexOf(e.NewItems, SelectedItem); SelectedIndex = IndexOf(Items, SelectedItem);
break; break;
} }
} }

5
src/Avalonia.Controls/Slider.cs

@ -17,7 +17,7 @@ namespace Avalonia.Controls
/// Defines the <see cref="Orientation"/> property. /// Defines the <see cref="Orientation"/> property.
/// </summary> /// </summary>
public static readonly StyledProperty<Orientation> OrientationProperty = public static readonly StyledProperty<Orientation> OrientationProperty =
AvaloniaProperty.Register<Slider, Orientation>(nameof(Orientation), Orientation.Horizontal); ScrollBar.OrientationProperty.AddOwner<Slider>();
/// <summary> /// <summary>
/// Defines the <see cref="IsSnapToTickEnabled"/> property. /// Defines the <see cref="IsSnapToTickEnabled"/> property.
@ -41,8 +41,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
static Slider() static Slider()
{ {
PseudoClass(OrientationProperty, o => o == Avalonia.Controls.Orientation.Vertical, ":vertical"); OrientationProperty.OverrideDefaultValue(typeof(Slider), Orientation.Horizontal);
PseudoClass(OrientationProperty, o => o == Avalonia.Controls.Orientation.Horizontal, ":horizontal");
Thumb.DragStartedEvent.AddClassHandler<Slider>(x => x.OnThumbDragStarted, RoutingStrategies.Bubble); Thumb.DragStartedEvent.AddClassHandler<Slider>(x => x.OnThumbDragStarted, RoutingStrategies.Bubble);
Thumb.DragDeltaEvent.AddClassHandler<Slider>(x => x.OnThumbDragDelta, RoutingStrategies.Bubble); Thumb.DragDeltaEvent.AddClassHandler<Slider>(x => x.OnThumbDragDelta, RoutingStrategies.Bubble);
Thumb.DragCompletedEvent.AddClassHandler<Slider>(x => x.OnThumbDragCompleted, RoutingStrategies.Bubble); Thumb.DragCompletedEvent.AddClassHandler<Slider>(x => x.OnThumbDragCompleted, RoutingStrategies.Bubble);

38
src/Avalonia.Controls/StackPanel.cs

@ -13,10 +13,10 @@ namespace Avalonia.Controls
public class StackPanel : Panel, INavigableContainer public class StackPanel : Panel, INavigableContainer
{ {
/// <summary> /// <summary>
/// Defines the <see cref="Gap"/> property. /// Defines the <see cref="Spacing"/> property.
/// </summary> /// </summary>
public static readonly StyledProperty<double> GapProperty = public static readonly StyledProperty<double> SpacingProperty =
AvaloniaProperty.Register<StackPanel, double>(nameof(Gap)); AvaloniaProperty.Register<StackPanel, double>(nameof(Spacing));
/// <summary> /// <summary>
/// Defines the <see cref="Orientation"/> property. /// Defines the <see cref="Orientation"/> property.
@ -29,17 +29,17 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
static StackPanel() static StackPanel()
{ {
AffectsMeasure(GapProperty); AffectsMeasure(SpacingProperty);
AffectsMeasure(OrientationProperty); AffectsMeasure(OrientationProperty);
} }
/// <summary> /// <summary>
/// Gets or sets the size of the gap to place between child controls. /// Gets or sets the size of the spacing to place between child controls.
/// </summary> /// </summary>
public double Gap public double Spacing
{ {
get { return GetValue(GapProperty); } get { return GetValue(SpacingProperty); }
set { SetValue(GapProperty, value); } set { SetValue(SpacingProperty, value); }
} }
/// <summary> /// <summary>
@ -152,7 +152,7 @@ namespace Avalonia.Controls
double measuredWidth = 0; double measuredWidth = 0;
double measuredHeight = 0; double measuredHeight = 0;
double gap = Gap; double spacing = Spacing;
bool hasVisibleChild = Children.Any(c => c.IsVisible); bool hasVisibleChild = Children.Any(c => c.IsVisible);
foreach (Control child in Children) foreach (Control child in Children)
@ -162,23 +162,23 @@ namespace Avalonia.Controls
if (Orientation == Orientation.Vertical) if (Orientation == Orientation.Vertical)
{ {
measuredHeight += size.Height + (child.IsVisible ? gap : 0); measuredHeight += size.Height + (child.IsVisible ? spacing : 0);
measuredWidth = Math.Max(measuredWidth, size.Width); measuredWidth = Math.Max(measuredWidth, size.Width);
} }
else else
{ {
measuredWidth += size.Width + (child.IsVisible ? gap : 0); measuredWidth += size.Width + (child.IsVisible ? spacing : 0);
measuredHeight = Math.Max(measuredHeight, size.Height); measuredHeight = Math.Max(measuredHeight, size.Height);
} }
} }
if (Orientation == Orientation.Vertical) if (Orientation == Orientation.Vertical)
{ {
measuredHeight -= (hasVisibleChild ? gap : 0); measuredHeight -= (hasVisibleChild ? spacing : 0);
} }
else else
{ {
measuredWidth -= (hasVisibleChild ? gap : 0); measuredWidth -= (hasVisibleChild ? spacing : 0);
} }
return new Size(measuredWidth, measuredHeight); return new Size(measuredWidth, measuredHeight);
@ -194,7 +194,7 @@ namespace Avalonia.Controls
var orientation = Orientation; var orientation = Orientation;
double arrangedWidth = finalSize.Width; double arrangedWidth = finalSize.Width;
double arrangedHeight = finalSize.Height; double arrangedHeight = finalSize.Height;
double gap = Gap; double spacing = Spacing;
bool hasVisibleChild = Children.Any(c => c.IsVisible); bool hasVisibleChild = Children.Any(c => c.IsVisible);
if (Orientation == Orientation.Vertical) if (Orientation == Orientation.Vertical)
@ -217,25 +217,25 @@ namespace Avalonia.Controls
Rect childFinal = new Rect(0, arrangedHeight, width, childHeight); Rect childFinal = new Rect(0, arrangedHeight, width, childHeight);
ArrangeChild(child, childFinal, finalSize, orientation); ArrangeChild(child, childFinal, finalSize, orientation);
arrangedWidth = Math.Max(arrangedWidth, childWidth); arrangedWidth = Math.Max(arrangedWidth, childWidth);
arrangedHeight += childHeight + (child.IsVisible ? gap : 0); arrangedHeight += childHeight + (child.IsVisible ? spacing : 0);
} }
else else
{ {
double height = Math.Max(childHeight, arrangedHeight); double height = Math.Max(childHeight, arrangedHeight);
Rect childFinal = new Rect(arrangedWidth, 0, childWidth, height); Rect childFinal = new Rect(arrangedWidth, 0, childWidth, height);
ArrangeChild(child, childFinal, finalSize, orientation); ArrangeChild(child, childFinal, finalSize, orientation);
arrangedWidth += childWidth + (child.IsVisible ? gap : 0); arrangedWidth += childWidth + (child.IsVisible ? spacing : 0);
arrangedHeight = Math.Max(arrangedHeight, childHeight); arrangedHeight = Math.Max(arrangedHeight, childHeight);
} }
} }
if (orientation == Orientation.Vertical) if (orientation == Orientation.Vertical)
{ {
arrangedHeight = Math.Max(arrangedHeight - (hasVisibleChild ? gap : 0), finalSize.Height); arrangedHeight = Math.Max(arrangedHeight - (hasVisibleChild ? spacing : 0), finalSize.Height);
} }
else else
{ {
arrangedWidth = Math.Max(arrangedWidth - (hasVisibleChild ? gap : 0), finalSize.Width); arrangedWidth = Math.Max(arrangedWidth - (hasVisibleChild ? spacing : 0), finalSize.Width);
} }
return new Size(arrangedWidth, arrangedHeight); return new Size(arrangedWidth, arrangedHeight);
@ -250,4 +250,4 @@ namespace Avalonia.Controls
child.Arrange(rect); child.Arrange(rect);
} }
} }
} }

14
src/Avalonia.Controls/TextBox.cs

@ -124,7 +124,7 @@ namespace Avalonia.Controls
ScrollViewer.HorizontalScrollBarVisibilityProperty, ScrollViewer.HorizontalScrollBarVisibilityProperty,
horizontalScrollBarVisibility, horizontalScrollBarVisibility,
BindingPriority.Style); BindingPriority.Style);
_undoRedoHelper = new UndoRedoHelper<UndoRedoState>(this); _undoRedoHelper = new UndoRedoHelper<UndoRedoState>(this);
} }
public bool AcceptsReturn public bool AcceptsReturn
@ -262,7 +262,7 @@ namespace Avalonia.Controls
if (IsFocused) if (IsFocused)
{ {
_presenter.ShowCaret(); DecideCaretVisibility();
} }
} }
@ -282,12 +282,20 @@ namespace Avalonia.Controls
} }
else else
{ {
_presenter?.ShowCaret(); DecideCaretVisibility();
} }
e.Handled = true; e.Handled = true;
} }
private void DecideCaretVisibility()
{
if (!IsReadOnly)
_presenter?.ShowCaret();
else
_presenter?.HideCaret();
}
protected override void OnLostFocus(RoutedEventArgs e) protected override void OnLostFocus(RoutedEventArgs e)
{ {
base.OnLostFocus(e); base.OnLostFocus(e);

12
src/Avalonia.Controls/VirtualizingStackPanel.cs

@ -200,7 +200,7 @@ namespace Avalonia.Controls
private void UpdateAdd(IControl child) private void UpdateAdd(IControl child)
{ {
var bounds = Bounds; var bounds = Bounds;
var gap = Gap; var spacing = Spacing;
child.Measure(_availableSpace); child.Measure(_availableSpace);
++_averageCount; ++_averageCount;
@ -208,13 +208,13 @@ namespace Avalonia.Controls
if (Orientation == Orientation.Vertical) if (Orientation == Orientation.Vertical)
{ {
var height = child.DesiredSize.Height; var height = child.DesiredSize.Height;
_takenSpace += height + gap; _takenSpace += height + spacing;
AddToAverageItemSize(height); AddToAverageItemSize(height);
} }
else else
{ {
var width = child.DesiredSize.Width; var width = child.DesiredSize.Width;
_takenSpace += width + gap; _takenSpace += width + spacing;
AddToAverageItemSize(width); AddToAverageItemSize(width);
} }
} }
@ -222,18 +222,18 @@ namespace Avalonia.Controls
private void UpdateRemove(IControl child) private void UpdateRemove(IControl child)
{ {
var bounds = Bounds; var bounds = Bounds;
var gap = Gap; var spacing = Spacing;
if (Orientation == Orientation.Vertical) if (Orientation == Orientation.Vertical)
{ {
var height = child.DesiredSize.Height; var height = child.DesiredSize.Height;
_takenSpace -= height + gap; _takenSpace -= height + spacing;
RemoveFromAverageItemSize(height); RemoveFromAverageItemSize(height);
} }
else else
{ {
var width = child.DesiredSize.Width; var width = child.DesiredSize.Width;
_takenSpace -= width + gap; _takenSpace -= width + spacing;
RemoveFromAverageItemSize(width); RemoveFromAverageItemSize(width);
} }

10
src/Avalonia.Controls/Window.cs

@ -302,17 +302,23 @@ namespace Avalonia.Controls
internal void Close(bool ignoreCancel) internal void Close(bool ignoreCancel)
{ {
bool close = true;
try try
{ {
if (!ignoreCancel && HandleClosing()) if (!ignoreCancel && HandleClosing())
{ {
close = false;
return; return;
} }
} }
finally finally
{ {
PlatformImpl?.Dispose(); if (close)
HandleClosed(); {
PlatformImpl?.Dispose();
HandleClosed();
}
} }
} }

4
src/Avalonia.Controls/WindowCollection.cs

@ -96,7 +96,7 @@ namespace Avalonia
{ {
while (_windows.Count > 0) while (_windows.Count > 0)
{ {
_windows[0].Close(); _windows[0].Close(true);
} }
} }
@ -131,4 +131,4 @@ namespace Avalonia
} }
} }
} }
} }

12
src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj

@ -1,7 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <!-- WARNING! The designer support version number needs to be frozen
To allow projects that implement designer functionality to still
work with newer versions of Avalonia. This version number only
need change when there are breaking changes to designer support api.
-->
<Version>0.7.0</Version>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
@ -37,11 +42,6 @@
<ProjectReference Include="..\Avalonia.Styling\Avalonia.Styling.csproj" /> <ProjectReference Include="..\Avalonia.Styling\Avalonia.Styling.csproj" />
<ProjectReference Include="..\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj" /> <ProjectReference Include="..\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Compile Include="..\Shared\SharedAssemblyInfo.cs">
<Link>Properties\SharedAssemblyInfo.cs</Link>
</Compile>
</ItemGroup>
<Import Project="..\..\build\Microsoft.CSharp.props" /> <Import Project="..\..\build\Microsoft.CSharp.props" />
<Import Project="..\..\build\Rx.props" /> <Import Project="..\..\build\Rx.props" />
</Project> </Project>

4
src/Avalonia.Diagnostics/DevTools.xaml

@ -7,7 +7,7 @@
<ContentControl Content="{Binding Content}" Grid.Row="1"/> <ContentControl Content="{Binding Content}" Grid.Row="1"/>
<StackPanel Gap="4" Orientation="Horizontal" Grid.Row="2"> <StackPanel Spacing="4" Orientation="Horizontal" Grid.Row="2">
<TextBlock>Hold Ctrl+Shift over a control to inspect.</TextBlock> <TextBlock>Hold Ctrl+Shift over a control to inspect.</TextBlock>
<Separator Width="8"/> <Separator Width="8"/>
<TextBlock>Focused:</TextBlock> <TextBlock>Focused:</TextBlock>
@ -17,4 +17,4 @@
<TextBlock Text="{Binding PointerOverElement}"/> <TextBlock Text="{Binding PointerOverElement}"/>
</StackPanel> </StackPanel>
</Grid> </Grid>
</UserControl> </UserControl>

4
src/Avalonia.Diagnostics/Views/TreePageView.xaml

@ -5,7 +5,7 @@
<TreeView.DataTemplates> <TreeView.DataTemplates>
<TreeDataTemplate DataType="vm:TreeNode" <TreeDataTemplate DataType="vm:TreeNode"
ItemsSource="{Binding Children}"> ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal" Gap="8"> <StackPanel Orientation="Horizontal" Spacing="8">
<TextBlock Text="{Binding Type}"/> <TextBlock Text="{Binding Type}"/>
<TextBlock Text="{Binding Classes}"/> <TextBlock Text="{Binding Classes}"/>
</StackPanel> </StackPanel>
@ -21,4 +21,4 @@
<GridSplitter Width="4" Grid.Column="1"/> <GridSplitter Width="4" Grid.Column="1"/>
<ContentControl Content="{Binding Details}" Grid.Column="2"/> <ContentControl Content="{Binding Details}" Grid.Column="2"/>
</Grid> </Grid>
</UserControl> </UserControl>

6
src/Avalonia.Visuals/Animation/TransformAnimator.cs

@ -19,7 +19,7 @@ namespace Avalonia.Animation
DoubleAnimator childKeyFrames; DoubleAnimator childKeyFrames;
/// <inheritdoc/> /// <inheritdoc/>
public override IDisposable Apply(Animation animation, Animatable control, IObservable<bool> obsMatch) public override IDisposable Apply(Animation animation, Animatable control, IObservable<bool> obsMatch, Action onComplete)
{ {
var ctrl = (Visual)control; var ctrl = (Visual)control;
@ -51,7 +51,7 @@ namespace Avalonia.Animation
// It's a transform object so let's target that. // It's a transform object so let's target that.
if (renderTransformType == Property.OwnerType) if (renderTransformType == Property.OwnerType)
{ {
return childKeyFrames.Apply(animation, ctrl.RenderTransform, obsMatch); return childKeyFrames.Apply(animation, ctrl.RenderTransform, obsMatch, onComplete);
} }
// It's a TransformGroup and try finding the target there. // It's a TransformGroup and try finding the target there.
else if (renderTransformType == typeof(TransformGroup)) else if (renderTransformType == typeof(TransformGroup))
@ -60,7 +60,7 @@ namespace Avalonia.Animation
{ {
if (transform.GetType() == Property.OwnerType) if (transform.GetType() == Property.OwnerType)
{ {
return childKeyFrames.Apply(animation, transform, obsMatch); return childKeyFrames.Apply(animation, transform, obsMatch, onComplete);
} }
} }
} }

10
src/Avalonia.Visuals/Media/DrawingContext.cs

@ -2,9 +2,10 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using Avalonia.Media.Imaging; using Avalonia.Media.Imaging;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Visuals.Media.Imaging;
namespace Avalonia.Media namespace Avalonia.Media
{ {
public sealed class DrawingContext : IDisposable public sealed class DrawingContext : IDisposable
{ {
private int _currentLevel; private int _currentLevel;
@ -68,11 +69,12 @@ namespace Avalonia.Media
/// <param name="opacity">The opacity to draw with.</param> /// <param name="opacity">The opacity to draw with.</param>
/// <param name="sourceRect">The rect in the image to draw.</param> /// <param name="sourceRect">The rect in the image to draw.</param>
/// <param name="destRect">The rect in the output to draw to.</param> /// <param name="destRect">The rect in the output to draw to.</param>
public void DrawImage(IBitmap source, double opacity, Rect sourceRect, Rect destRect) /// <param name="bitmapInterpolationMode">The bitmap interpolation mode.</param>
public void DrawImage(IBitmap source, double opacity, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode = default)
{ {
Contract.Requires<ArgumentNullException>(source != null); Contract.Requires<ArgumentNullException>(source != null);
PlatformImpl.DrawImage(source.PlatformImpl, opacity, sourceRect, destRect); PlatformImpl.DrawImage(source.PlatformImpl, opacity, sourceRect, destRect, bitmapInterpolationMode);
} }
/// <summary> /// <summary>
@ -309,4 +311,4 @@ namespace Avalonia.Media
return pen?.Brush != null && pen.Thickness > 0; return pen?.Brush != null && pen.Thickness > 0;
} }
} }
} }

19
src/Avalonia.Visuals/Media/ITileBrush.cs

@ -1,5 +1,10 @@
namespace Avalonia.Media // Copyright (c) The Avalonia Project. All rights reserved.
{ // Licensed under the MIT license. See licence.md file in the project root for full license information.
using Avalonia.Visuals.Media.Imaging;
namespace Avalonia.Media
{
/// <summary> /// <summary>
/// A brush which displays a repeating image. /// A brush which displays a repeating image.
/// </summary> /// </summary>
@ -35,5 +40,13 @@
/// Gets the brush's tile mode. /// Gets the brush's tile mode.
/// </summary> /// </summary>
TileMode TileMode { get; } TileMode TileMode { get; }
/// <summary>
/// Gets the bitmap interpolation mode.
/// </summary>
/// <value>
/// The bitmap interpolation mode.
/// </value>
BitmapInterpolationMode BitmapInterpolationMode { get; }
} }
} }

31
src/Avalonia.Visuals/Media/Imaging/BitmapInterpolationMode.cs

@ -0,0 +1,31 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
namespace Avalonia.Visuals.Media.Imaging
{
/// <summary>
/// Controls the performance and quality of bitmap scaling.
/// </summary>
public enum BitmapInterpolationMode
{
/// <summary>
/// Uses the default behavior of the underling render backend.
/// </summary>
Default,
/// <summary>
/// The best performance but worst image quality.
/// </summary>
LowQuality,
/// <summary>
/// Good performance and decent image quality.
/// </summary>
MediumQuality,
/// <summary>
/// Highest quality but worst performance.
/// </summary>
HighQuality
}
}

11
src/Avalonia.Visuals/Media/Immutable/ImmutableImageBrush.cs

@ -1,5 +1,5 @@
using System; using Avalonia.Media.Imaging;
using Avalonia.Media.Imaging; using Avalonia.Visuals.Media.Imaging;
namespace Avalonia.Media.Immutable namespace Avalonia.Media.Immutable
{ {
@ -21,6 +21,7 @@ namespace Avalonia.Media.Immutable
/// How the source rectangle will be stretched to fill the destination rect. /// How the source rectangle will be stretched to fill the destination rect.
/// </param> /// </param>
/// <param name="tileMode">The tile mode.</param> /// <param name="tileMode">The tile mode.</param>
/// <param name="bitmapInterpolationMode">The bitmap interpolation mode.</param>
public ImmutableImageBrush( public ImmutableImageBrush(
IBitmap source, IBitmap source,
AlignmentX alignmentX = AlignmentX.Center, AlignmentX alignmentX = AlignmentX.Center,
@ -29,7 +30,8 @@ namespace Avalonia.Media.Immutable
double opacity = 1, double opacity = 1,
RelativeRect? sourceRect = null, RelativeRect? sourceRect = null,
Stretch stretch = Stretch.Uniform, Stretch stretch = Stretch.Uniform,
TileMode tileMode = TileMode.None) TileMode tileMode = TileMode.None,
BitmapInterpolationMode bitmapInterpolationMode = BitmapInterpolationMode.Default)
: base( : base(
alignmentX, alignmentX,
alignmentY, alignmentY,
@ -37,7 +39,8 @@ namespace Avalonia.Media.Immutable
opacity, opacity,
sourceRect ?? RelativeRect.Fill, sourceRect ?? RelativeRect.Fill,
stretch, stretch,
tileMode) tileMode,
bitmapInterpolationMode)
{ {
Source = source; Source = source;
} }

16
src/Avalonia.Visuals/Media/Immutable/ImmutableTileBrush.cs

@ -1,4 +1,7 @@
using System; // Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using Avalonia.Visuals.Media.Imaging;
namespace Avalonia.Media.Immutable namespace Avalonia.Media.Immutable
{ {
@ -19,6 +22,7 @@ namespace Avalonia.Media.Immutable
/// How the source rectangle will be stretched to fill the destination rect. /// How the source rectangle will be stretched to fill the destination rect.
/// </param> /// </param>
/// <param name="tileMode">The tile mode.</param> /// <param name="tileMode">The tile mode.</param>
/// <param name="bitmapInterpolationMode">The bitmap interpolation mode.</param>
protected ImmutableTileBrush( protected ImmutableTileBrush(
AlignmentX alignmentX, AlignmentX alignmentX,
AlignmentY alignmentY, AlignmentY alignmentY,
@ -26,7 +30,8 @@ namespace Avalonia.Media.Immutable
double opacity, double opacity,
RelativeRect sourceRect, RelativeRect sourceRect,
Stretch stretch, Stretch stretch,
TileMode tileMode) TileMode tileMode,
BitmapInterpolationMode bitmapInterpolationMode)
{ {
AlignmentX = alignmentX; AlignmentX = alignmentX;
AlignmentY = alignmentY; AlignmentY = alignmentY;
@ -35,6 +40,7 @@ namespace Avalonia.Media.Immutable
SourceRect = sourceRect; SourceRect = sourceRect;
Stretch = stretch; Stretch = stretch;
TileMode = tileMode; TileMode = tileMode;
BitmapInterpolationMode = bitmapInterpolationMode;
} }
/// <summary> /// <summary>
@ -49,7 +55,8 @@ namespace Avalonia.Media.Immutable
source.Opacity, source.Opacity,
source.SourceRect, source.SourceRect,
source.Stretch, source.Stretch,
source.TileMode) source.TileMode,
source.BitmapInterpolationMode)
{ {
} }
@ -73,5 +80,8 @@ namespace Avalonia.Media.Immutable
/// <inheritdoc/> /// <inheritdoc/>
public TileMode TileMode { get; } public TileMode TileMode { get; }
/// <inheritdoc/>
public BitmapInterpolationMode BitmapInterpolationMode { get; }
} }
} }

12
src/Avalonia.Visuals/Media/Immutable/ImmutableVisualBrush.cs

@ -1,4 +1,7 @@
using System; // Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using Avalonia.Visuals.Media.Imaging;
using Avalonia.VisualTree; using Avalonia.VisualTree;
namespace Avalonia.Media.Immutable namespace Avalonia.Media.Immutable
@ -21,6 +24,7 @@ namespace Avalonia.Media.Immutable
/// How the source rectangle will be stretched to fill the destination rect. /// How the source rectangle will be stretched to fill the destination rect.
/// </param> /// </param>
/// <param name="tileMode">The tile mode.</param> /// <param name="tileMode">The tile mode.</param>
/// <param name="bitmapInterpolationMode">Controls the quality of interpolation.</param>
public ImmutableVisualBrush( public ImmutableVisualBrush(
IVisual visual, IVisual visual,
AlignmentX alignmentX = AlignmentX.Center, AlignmentX alignmentX = AlignmentX.Center,
@ -29,7 +33,8 @@ namespace Avalonia.Media.Immutable
double opacity = 1, double opacity = 1,
RelativeRect? sourceRect = null, RelativeRect? sourceRect = null,
Stretch stretch = Stretch.Uniform, Stretch stretch = Stretch.Uniform,
TileMode tileMode = TileMode.None) TileMode tileMode = TileMode.None,
BitmapInterpolationMode bitmapInterpolationMode = BitmapInterpolationMode.Default)
: base( : base(
alignmentX, alignmentX,
alignmentY, alignmentY,
@ -37,7 +42,8 @@ namespace Avalonia.Media.Immutable
opacity, opacity,
sourceRect ?? RelativeRect.Fill, sourceRect ?? RelativeRect.Fill,
stretch, stretch,
tileMode) tileMode,
bitmapInterpolationMode)
{ {
Visual = visual; Visual = visual;
} }

39
src/Avalonia.Visuals/Media/RenderOptions.cs

@ -0,0 +1,39 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using Avalonia.Visuals.Media.Imaging;
namespace Avalonia.Media
{
public class RenderOptions
{
/// <summary>
/// Defines the <see cref="BitmapInterpolationMode"/> property.
/// </summary>
public static readonly StyledProperty<BitmapInterpolationMode> BitmapInterpolationModeProperty =
AvaloniaProperty.RegisterAttached<RenderOptions, AvaloniaObject, BitmapInterpolationMode>(
"BitmapInterpolationMode",
BitmapInterpolationMode.MediumQuality,
inherits: true);
/// <summary>
/// Gets the value of the BitmapInterpolationMode attached property for a control.
/// </summary>
/// <param name="element">The control.</param>
/// <returns>The control's left coordinate.</returns>
public static BitmapInterpolationMode GetBitmapInterpolationMode(AvaloniaObject element)
{
return element.GetValue(BitmapInterpolationModeProperty);
}
/// <summary>
/// Sets the value of the BitmapInterpolationMode attached property for a control.
/// </summary>
/// <param name="element">The control.</param>
/// <param name="value">The left value.</param>
public static void SetBitmapInterpolationMode(AvaloniaObject element, BitmapInterpolationMode value)
{
element.SetValue(BitmapInterpolationModeProperty, value);
}
}
}

19
src/Avalonia.Visuals/Media/TileBrush.cs

@ -1,6 +1,8 @@
// Copyright (c) The Avalonia Project. All rights reserved. // Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information. // Licensed under the MIT license. See licence.md file in the project root for full license information.
using Avalonia.Visuals.Media.Imaging;
namespace Avalonia.Media namespace Avalonia.Media
{ {
/// <summary> /// <summary>
@ -75,6 +77,11 @@ namespace Avalonia.Media
public static readonly StyledProperty<TileMode> TileModeProperty = public static readonly StyledProperty<TileMode> TileModeProperty =
AvaloniaProperty.Register<TileBrush, TileMode>(nameof(TileMode)); AvaloniaProperty.Register<TileBrush, TileMode>(nameof(TileMode));
static TileBrush()
{
RenderOptions.BitmapInterpolationModeProperty.OverrideDefaultValue<TileBrush>(BitmapInterpolationMode.Default);
}
/// <summary> /// <summary>
/// Gets or sets the horizontal alignment of a tile in the destination. /// Gets or sets the horizontal alignment of a tile in the destination.
/// </summary> /// </summary>
@ -129,5 +136,17 @@ namespace Avalonia.Media
get { return (TileMode)GetValue(TileModeProperty); } get { return (TileMode)GetValue(TileModeProperty); }
set { SetValue(TileModeProperty, value); } set { SetValue(TileModeProperty, value); }
} }
/// <summary>
/// Gets or sets the bitmap interpolation mode.
/// </summary>
/// <value>
/// The bitmap interpolation mode.
/// </value>
public BitmapInterpolationMode BitmapInterpolationMode
{
get { return RenderOptions.GetBitmapInterpolationMode(this); }
set { RenderOptions.SetBitmapInterpolationMode(this, value); }
}
} }
} }

4
src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs

@ -4,6 +4,7 @@
using System; using System;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Utilities; using Avalonia.Utilities;
using Avalonia.Visuals.Media.Imaging;
namespace Avalonia.Platform namespace Avalonia.Platform
{ {
@ -30,7 +31,8 @@ namespace Avalonia.Platform
/// <param name="opacity">The opacity to draw with.</param> /// <param name="opacity">The opacity to draw with.</param>
/// <param name="sourceRect">The rect in the image to draw.</param> /// <param name="sourceRect">The rect in the image to draw.</param>
/// <param name="destRect">The rect in the output to draw to.</param> /// <param name="destRect">The rect in the output to draw to.</param>
void DrawImage(IRef<IBitmapImpl> source, double opacity, Rect sourceRect, Rect destRect); /// <param name="bitmapInterpolationMode">The bitmap interpolation mode.</param>
void DrawImage(IRef<IBitmapImpl> source, double opacity, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode = BitmapInterpolationMode.Default);
/// <summary> /// <summary>
/// Draws a bitmap image. /// Draws a bitmap image.

7
src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs

@ -7,6 +7,7 @@ using Avalonia.Media;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Utilities; using Avalonia.Utilities;
using Avalonia.VisualTree; using Avalonia.VisualTree;
using Avalonia.Visuals.Media.Imaging;
namespace Avalonia.Rendering.SceneGraph namespace Avalonia.Rendering.SceneGraph
{ {
@ -114,13 +115,13 @@ namespace Avalonia.Rendering.SceneGraph
} }
/// <inheritdoc/> /// <inheritdoc/>
public void DrawImage(IRef<IBitmapImpl> source, double opacity, Rect sourceRect, Rect destRect) public void DrawImage(IRef<IBitmapImpl> source, double opacity, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode)
{ {
var next = NextDrawAs<ImageNode>(); var next = NextDrawAs<ImageNode>();
if (next == null || !next.Item.Equals(Transform, source, opacity, sourceRect, destRect)) if (next == null || !next.Item.Equals(Transform, source, opacity, sourceRect, destRect, bitmapInterpolationMode))
{ {
Add(new ImageNode(Transform, source, opacity, sourceRect, destRect)); Add(new ImageNode(Transform, source, opacity, sourceRect, destRect, bitmapInterpolationMode));
} }
else else
{ {

23
src/Avalonia.Visuals/Rendering/SceneGraph/ImageNode.cs

@ -4,6 +4,7 @@
using System; using System;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Utilities; using Avalonia.Utilities;
using Avalonia.Visuals.Media.Imaging;
namespace Avalonia.Rendering.SceneGraph namespace Avalonia.Rendering.SceneGraph
{ {
@ -20,7 +21,8 @@ namespace Avalonia.Rendering.SceneGraph
/// <param name="opacity">The draw opacity.</param> /// <param name="opacity">The draw opacity.</param>
/// <param name="sourceRect">The source rect.</param> /// <param name="sourceRect">The source rect.</param>
/// <param name="destRect">The destination rect.</param> /// <param name="destRect">The destination rect.</param>
public ImageNode(Matrix transform, IRef<IBitmapImpl> source, double opacity, Rect sourceRect, Rect destRect) /// <param name="bitmapInterpolationMode">The bitmap interpolation mode.</param>
public ImageNode(Matrix transform, IRef<IBitmapImpl> source, double opacity, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode)
: base(destRect, transform, null) : base(destRect, transform, null)
{ {
Transform = transform; Transform = transform;
@ -28,7 +30,8 @@ namespace Avalonia.Rendering.SceneGraph
Opacity = opacity; Opacity = opacity;
SourceRect = sourceRect; SourceRect = sourceRect;
DestRect = destRect; DestRect = destRect;
} BitmapInterpolationMode = bitmapInterpolationMode;
}
/// <summary> /// <summary>
/// Gets the transform with which the node will be drawn. /// Gets the transform with which the node will be drawn.
@ -55,6 +58,14 @@ namespace Avalonia.Rendering.SceneGraph
/// </summary> /// </summary>
public Rect DestRect { get; } public Rect DestRect { get; }
/// <summary>
/// Gets the bitmap interpolation mode.
/// </summary>
/// <value>
/// The scaling mode.
/// </value>
public BitmapInterpolationMode BitmapInterpolationMode { get; }
/// <summary> /// <summary>
/// Determines if this draw operation equals another. /// Determines if this draw operation equals another.
/// </summary> /// </summary>
@ -63,18 +74,20 @@ namespace Avalonia.Rendering.SceneGraph
/// <param name="opacity">The opacity of the other draw operation.</param> /// <param name="opacity">The opacity of the other draw operation.</param>
/// <param name="sourceRect">The source rect of the other draw operation.</param> /// <param name="sourceRect">The source rect of the other draw operation.</param>
/// <param name="destRect">The dest rect of the other draw operation.</param> /// <param name="destRect">The dest rect of the other draw operation.</param>
/// <param name="bitmapInterpolationMode">The bitmap interpolation mode.</param>
/// <returns>True if the draw operations are the same, otherwise false.</returns> /// <returns>True if the draw operations are the same, otherwise false.</returns>
/// <remarks> /// <remarks>
/// The properties of the other draw operation are passed in as arguments to prevent /// The properties of the other draw operation are passed in as arguments to prevent
/// allocation of a not-yet-constructed draw operation object. /// allocation of a not-yet-constructed draw operation object.
/// </remarks> /// </remarks>
public bool Equals(Matrix transform, IRef<IBitmapImpl> source, double opacity, Rect sourceRect, Rect destRect) public bool Equals(Matrix transform, IRef<IBitmapImpl> source, double opacity, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode)
{ {
return transform == Transform && return transform == Transform &&
Equals(source.Item, Source.Item) && Equals(source.Item, Source.Item) &&
opacity == Opacity && opacity == Opacity &&
sourceRect == SourceRect && sourceRect == SourceRect &&
destRect == DestRect; destRect == DestRect &&
bitmapInterpolationMode == BitmapInterpolationMode;
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -83,7 +96,7 @@ namespace Avalonia.Rendering.SceneGraph
// TODO: Probably need to introduce some kind of locking mechanism in the case of // TODO: Probably need to introduce some kind of locking mechanism in the case of
// WriteableBitmap. // WriteableBitmap.
context.Transform = Transform; context.Transform = Transform;
context.DrawImage(Source, Opacity, SourceRect, DestRect); context.DrawImage(Source, Opacity, SourceRect, DestRect, BitmapInterpolationMode);
} }
/// <inheritdoc/> /// <inheritdoc/>

7
src/Gtk/Avalonia.Gtk3/KeyTransform.cs

@ -33,17 +33,24 @@ namespace Avalonia.Gtk.Common
{ GdkKey.Prior, Key.Prior }, { GdkKey.Prior, Key.Prior },
//{ GdkKey.?, Key.PageDown } //{ GdkKey.?, Key.PageDown }
{ GdkKey.End, Key.End }, { GdkKey.End, Key.End },
{ GdkKey.KP_End, Key.End },
{ GdkKey.Home, Key.Home }, { GdkKey.Home, Key.Home },
{ GdkKey.KP_Home, Key.Home },
{ GdkKey.Left, Key.Left }, { GdkKey.Left, Key.Left },
{ GdkKey.KP_Left, Key.Left },
{ GdkKey.Up, Key.Up }, { GdkKey.Up, Key.Up },
{ GdkKey.KP_Up, Key.Up },
{ GdkKey.Right, Key.Right }, { GdkKey.Right, Key.Right },
{ GdkKey.KP_Right, Key.Right },
{ GdkKey.Down, Key.Down }, { GdkKey.Down, Key.Down },
{ GdkKey.KP_Down, Key.Down },
{ GdkKey.Select, Key.Select }, { GdkKey.Select, Key.Select },
{ GdkKey.Print, Key.Print }, { GdkKey.Print, Key.Print },
{ GdkKey.Execute, Key.Execute }, { GdkKey.Execute, Key.Execute },
//{ GdkKey.?, Key.Snapshot } //{ GdkKey.?, Key.Snapshot }
{ GdkKey.Insert, Key.Insert }, { GdkKey.Insert, Key.Insert },
{ GdkKey.Delete, Key.Delete }, { GdkKey.Delete, Key.Delete },
{ GdkKey.KP_Delete, Key.Delete },
{ GdkKey.Help, Key.Help }, { GdkKey.Help, Key.Help },
//{ GdkKey.?, Key.D0 } //{ GdkKey.?, Key.D0 }
//{ GdkKey.?, Key.D1 } //{ GdkKey.?, Key.D1 }

38
src/Skia/Avalonia.Skia/DrawingContextImpl.cs

@ -10,6 +10,7 @@ using Avalonia.Platform;
using Avalonia.Rendering; using Avalonia.Rendering;
using Avalonia.Rendering.Utilities; using Avalonia.Rendering.Utilities;
using Avalonia.Utilities; using Avalonia.Utilities;
using Avalonia.Visuals.Media.Imaging;
using SkiaSharp; using SkiaSharp;
namespace Avalonia.Skia namespace Avalonia.Skia
@ -95,24 +96,46 @@ namespace Avalonia.Skia
} }
/// <inheritdoc /> /// <inheritdoc />
public void DrawImage(IRef<IBitmapImpl> source, double opacity, Rect sourceRect, Rect destRect) public void DrawImage(IRef<IBitmapImpl> source, double opacity, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode)
{ {
var drawableImage = (IDrawableBitmapImpl) source.Item; var drawableImage = (IDrawableBitmapImpl)source.Item;
var s = sourceRect.ToSKRect(); var s = sourceRect.ToSKRect();
var d = destRect.ToSKRect(); var d = destRect.ToSKRect();
using (var paint = using (var paint =
new SKPaint {Color = new SKColor(255, 255, 255, (byte) (255 * opacity * _currentOpacity))}) new SKPaint
{
Color = new SKColor(255, 255, 255, (byte)(255 * opacity * _currentOpacity))
})
{ {
paint.FilterQuality = GetInterpolationMode(bitmapInterpolationMode);
drawableImage.Draw(this, s, d, paint); drawableImage.Draw(this, s, d, paint);
} }
} }
private static SKFilterQuality GetInterpolationMode(BitmapInterpolationMode interpolationMode)
{
switch (interpolationMode)
{
case BitmapInterpolationMode.LowQuality:
return SKFilterQuality.Low;
case BitmapInterpolationMode.MediumQuality:
return SKFilterQuality.Medium;
case BitmapInterpolationMode.HighQuality:
return SKFilterQuality.High;
case BitmapInterpolationMode.Default:
return SKFilterQuality.None;
default:
throw new ArgumentOutOfRangeException(nameof(interpolationMode), interpolationMode, null);
}
}
/// <inheritdoc /> /// <inheritdoc />
public void DrawImage(IRef<IBitmapImpl> source, IBrush opacityMask, Rect opacityMaskRect, Rect destRect) public void DrawImage(IRef<IBitmapImpl> source, IBrush opacityMask, Rect opacityMaskRect, Rect destRect)
{ {
PushOpacityMask(opacityMask, opacityMaskRect); PushOpacityMask(opacityMask, opacityMaskRect);
DrawImage(source, 1, new Rect(0, 0, source.Item.PixelWidth, source.Item.PixelHeight), destRect); DrawImage(source, 1, new Rect(0, 0, source.Item.PixelWidth, source.Item.PixelHeight), destRect, BitmapInterpolationMode.Default);
PopOpacityMask(); PopOpacityMask();
} }
@ -357,6 +380,7 @@ namespace Avalonia.Skia
/// <param name="targetSize">Target size.</param> /// <param name="targetSize">Target size.</param>
/// <param name="tileBrush">Tile brush to use.</param> /// <param name="tileBrush">Tile brush to use.</param>
/// <param name="tileBrushImage">Tile brush image.</param> /// <param name="tileBrushImage">Tile brush image.</param>
/// <param name="interpolationMode">The bitmap interpolation mode.</param>
private void ConfigureTileBrush(ref PaintWrapper paintWrapper, Size targetSize, ITileBrush tileBrush, IDrawableBitmapImpl tileBrushImage) private void ConfigureTileBrush(ref PaintWrapper paintWrapper, Size targetSize, ITileBrush tileBrush, IDrawableBitmapImpl tileBrushImage)
{ {
var calc = new TileBrushCalculator(tileBrush, var calc = new TileBrushCalculator(tileBrush,
@ -375,7 +399,7 @@ namespace Avalonia.Skia
context.Clear(Colors.Transparent); context.Clear(Colors.Transparent);
context.PushClip(calc.IntermediateClip); context.PushClip(calc.IntermediateClip);
context.Transform = calc.IntermediateTransform; context.Transform = calc.IntermediateTransform;
context.DrawImage(RefCountable.CreateUnownedNotClonable(tileBrushImage), 1, rect, rect); context.DrawImage(RefCountable.CreateUnownedNotClonable(tileBrushImage), 1, rect, rect, tileBrush.BitmapInterpolationMode);
context.PopClip(); context.PopClip();
} }
@ -484,7 +508,7 @@ namespace Avalonia.Skia
} }
else else
{ {
tileBrushImage = (IDrawableBitmapImpl) (tileBrush as IImageBrush)?.Source?.PlatformImpl.Item; tileBrushImage = (IDrawableBitmapImpl)(tileBrush as IImageBrush)?.Source?.PlatformImpl.Item;
} }
if (tileBrush != null && tileBrushImage != null) if (tileBrush != null && tileBrushImage != null)
@ -690,4 +714,4 @@ namespace Avalonia.Skia
} }
} }
} }
} }

25
src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs

@ -10,13 +10,14 @@ using Avalonia.Utilities;
using SharpDX; using SharpDX;
using SharpDX.Direct2D1; using SharpDX.Direct2D1;
using SharpDX.Mathematics.Interop; using SharpDX.Mathematics.Interop;
using BitmapInterpolationMode = Avalonia.Visuals.Media.Imaging.BitmapInterpolationMode;
namespace Avalonia.Direct2D1.Media namespace Avalonia.Direct2D1.Media
{ {
/// <summary> /// <summary>
/// Draws using Direct2D1. /// Draws using Direct2D1.
/// </summary> /// </summary>
public class DrawingContextImpl : IDrawingContextImpl, IDisposable public class DrawingContextImpl : IDrawingContextImpl
{ {
private readonly IVisualBrushRenderer _visualBrushRenderer; private readonly IVisualBrushRenderer _visualBrushRenderer;
private readonly ILayerFactory _layerFactory; private readonly ILayerFactory _layerFactory;
@ -100,19 +101,37 @@ namespace Avalonia.Direct2D1.Media
/// <param name="opacity">The opacity to draw with.</param> /// <param name="opacity">The opacity to draw with.</param>
/// <param name="sourceRect">The rect in the image to draw.</param> /// <param name="sourceRect">The rect in the image to draw.</param>
/// <param name="destRect">The rect in the output to draw to.</param> /// <param name="destRect">The rect in the output to draw to.</param>
public void DrawImage(IRef<IBitmapImpl> source, double opacity, Rect sourceRect, Rect destRect) /// <param name="bitmapInterpolationMode">The bitmap interpolation mode.</param>
public void DrawImage(IRef<IBitmapImpl> source, double opacity, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode)
{ {
using (var d2d = ((BitmapImpl)source.Item).GetDirect2DBitmap(_renderTarget)) using (var d2d = ((BitmapImpl)source.Item).GetDirect2DBitmap(_renderTarget))
{ {
var interpolationMode = GetInterpolationMode(bitmapInterpolationMode);
_renderTarget.DrawBitmap( _renderTarget.DrawBitmap(
d2d.Value, d2d.Value,
destRect.ToSharpDX(), destRect.ToSharpDX(),
(float)opacity, (float)opacity,
BitmapInterpolationMode.Linear, interpolationMode,
sourceRect.ToSharpDX()); sourceRect.ToSharpDX());
} }
} }
private static SharpDX.Direct2D1.BitmapInterpolationMode GetInterpolationMode(BitmapInterpolationMode interpolationMode)
{
switch (interpolationMode)
{
case BitmapInterpolationMode.LowQuality:
return SharpDX.Direct2D1.BitmapInterpolationMode.NearestNeighbor;
case BitmapInterpolationMode.MediumQuality:
case BitmapInterpolationMode.HighQuality:
case BitmapInterpolationMode.Default:
return SharpDX.Direct2D1.BitmapInterpolationMode.Linear;
default:
throw new ArgumentOutOfRangeException(nameof(interpolationMode), interpolationMode, null);
}
}
/// <summary> /// <summary>
/// Draws a bitmap image. /// Draws a bitmap image.
/// </summary> /// </summary>

9
src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs

@ -1,7 +1,6 @@
// Copyright (c) The Avalonia Project. All rights reserved. // Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information. // Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Rendering.Utilities; using Avalonia.Rendering.Utilities;
using Avalonia.Utilities; using Avalonia.Utilities;
@ -11,7 +10,9 @@ namespace Avalonia.Direct2D1.Media
{ {
public sealed class ImageBrushImpl : BrushImpl public sealed class ImageBrushImpl : BrushImpl
{ {
OptionalDispose<Bitmap> _bitmap; private readonly OptionalDispose<Bitmap> _bitmap;
private readonly Visuals.Media.Imaging.BitmapInterpolationMode _bitmapInterpolationMode;
public ImageBrushImpl( public ImageBrushImpl(
ITileBrush brush, ITileBrush brush,
@ -41,6 +42,8 @@ namespace Avalonia.Direct2D1.Media
GetBrushProperties(brush, calc.DestinationRect)); GetBrushProperties(brush, calc.DestinationRect));
} }
} }
_bitmapInterpolationMode = brush.BitmapInterpolationMode;
} }
public override void Dispose() public override void Dispose()
@ -102,7 +105,7 @@ namespace Avalonia.Direct2D1.Media
context.PushClip(calc.IntermediateClip); context.PushClip(calc.IntermediateClip);
context.Transform = calc.IntermediateTransform; context.Transform = calc.IntermediateTransform;
context.DrawImage(RefCountable.CreateUnownedNotClonable(bitmap), 1, rect, rect); context.DrawImage(RefCountable.CreateUnownedNotClonable(bitmap), 1, rect, rect, _bitmapInterpolationMode);
context.PopClip(); context.PopClip();
} }

9
tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization.cs

@ -195,6 +195,15 @@ namespace Avalonia.Controls.UnitTests.Presenters
} }
} }
[Fact]
public void Should_Not_Create_Virtualizer_Before_Panel()
{
var target = CreateTarget();
Assert.Null(target.Panel);
Assert.Null(target.Virtualizer);
}
[Fact] [Fact]
public void Changing_VirtualizationMode_None_To_Simple_Should_Update_Control() public void Changing_VirtualizationMode_None_To_Simple_Should_Update_Control()
{ {

22
tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs

@ -3,6 +3,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq; using System.Linq;
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Controls.Presenters; using Avalonia.Controls.Presenters;
@ -13,6 +14,7 @@ using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Markup.Data; using Avalonia.Markup.Data;
using Avalonia.UnitTests; using Avalonia.UnitTests;
using Moq;
using Xunit; using Xunit;
namespace Avalonia.Controls.UnitTests.Primitives namespace Avalonia.Controls.UnitTests.Primitives
@ -686,6 +688,26 @@ namespace Avalonia.Controls.UnitTests.Primitives
Assert.Null(KeyboardNavigation.GetTabOnceActiveElement((InputElement)panel)); Assert.Null(KeyboardNavigation.GetTabOnceActiveElement((InputElement)panel));
} }
[Fact]
public void Resetting_Items_Collection_Should_Retain_Selection()
{
var itemsMock = new Mock<List<string>>();
var itemsMockAsINCC = itemsMock.As<INotifyCollectionChanged>();
itemsMock.Object.AddRange(new[] { "Foo", "Bar", "Baz" });
var target = new SelectingItemsControl
{
Items = itemsMock.Object
};
target.SelectedIndex = 1;
itemsMockAsINCC.Raise(e => e.CollectionChanged += null, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
Assert.True(target.SelectedIndex == 1);
}
private FuncControlTemplate Template() private FuncControlTemplate Template()
{ {
return new FuncControlTemplate<SelectingItemsControl>(control => return new FuncControlTemplate<SelectingItemsControl>(control =>

39
tests/Avalonia.Controls.UnitTests/SliderTests.cs

@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Text;
using Xunit;
namespace Avalonia.Controls.UnitTests
{
public class SliderTests
{
[Fact]
public void Default_Orientation_Should_Be_Horizontal()
{
var slider = new Slider();
Assert.Equal(Orientation.Horizontal, slider.Orientation);
}
[Fact]
public void Should_Set_Horizontal_Class()
{
var slider = new Slider
{
Orientation = Orientation.Horizontal
};
Assert.Contains(slider.Classes, ":horizontal".Equals);
}
[Fact]
public void Should_Set_Vertical_Class()
{
var slider = new Slider
{
Orientation = Orientation.Vertical
};
Assert.Contains(slider.Classes, ":vertical".Equals);
}
}
}

14
tests/Avalonia.Controls.UnitTests/StackPanelTests.cs

@ -54,11 +54,11 @@ namespace Avalonia.Controls.UnitTests
} }
[Fact] [Fact]
public void Lays_Out_Children_Vertically_With_Gap() public void Lays_Out_Children_Vertically_With_Spacing()
{ {
var target = new StackPanel var target = new StackPanel
{ {
Gap = 10, Spacing = 10,
Children = Children =
{ {
new Border { Height = 20, Width = 120 }, new Border { Height = 20, Width = 120 },
@ -77,11 +77,11 @@ namespace Avalonia.Controls.UnitTests
} }
[Fact] [Fact]
public void Lays_Out_Children_Horizontally_With_Gap() public void Lays_Out_Children_Horizontally_With_Spacing()
{ {
var target = new StackPanel var target = new StackPanel
{ {
Gap = 10, Spacing = 10,
Orientation = Orientation.Horizontal, Orientation = Orientation.Horizontal,
Children = Children =
{ {
@ -150,11 +150,11 @@ namespace Avalonia.Controls.UnitTests
[Theory] [Theory]
[InlineData(Orientation.Horizontal)] [InlineData(Orientation.Horizontal)]
[InlineData(Orientation.Vertical)] [InlineData(Orientation.Vertical)]
public void Gap_Not_Added_For_Invisible_Children(Orientation orientation) public void Spacing_Not_Added_For_Invisible_Children(Orientation orientation)
{ {
var targetThreeChildrenOneInvisble = new StackPanel var targetThreeChildrenOneInvisble = new StackPanel
{ {
Gap = 40, Spacing = 40,
Orientation = orientation, Orientation = orientation,
Children = Children =
{ {
@ -165,7 +165,7 @@ namespace Avalonia.Controls.UnitTests
}; };
var targetTwoChildrenNoneInvisible = new StackPanel var targetTwoChildrenNoneInvisible = new StackPanel
{ {
Gap = 40, Spacing = 40,
Orientation = orientation, Orientation = orientation,
Children = Children =
{ {

6
tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs

@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reactive.Subjects; using System.Reactive.Subjects;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Media; using Avalonia.Media;
@ -10,8 +11,11 @@ using Avalonia.Rendering;
using Avalonia.Rendering.SceneGraph; using Avalonia.Rendering.SceneGraph;
using Avalonia.Threading; using Avalonia.Threading;
using Avalonia.UnitTests; using Avalonia.UnitTests;
using Avalonia.Visuals.Media.Imaging;
using Avalonia.VisualTree; using Avalonia.VisualTree;
using Moq; using Moq;
using Xunit; using Xunit;
namespace Avalonia.Visuals.UnitTests.Rendering namespace Avalonia.Visuals.UnitTests.Rendering
@ -336,7 +340,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering
var context = Mock.Get(target.RenderTarget.CreateDrawingContext(null)); var context = Mock.Get(target.RenderTarget.CreateDrawingContext(null));
var borderLayer = target.Layers[border].Bitmap; var borderLayer = target.Layers[border].Bitmap;
context.Verify(x => x.DrawImage(borderLayer, 0.5, It.IsAny<Rect>(), It.IsAny<Rect>())); context.Verify(x => x.DrawImage(borderLayer, 0.5, It.IsAny<Rect>(), It.IsAny<Rect>(), BitmapInterpolationMode.Default));
} }
private DeferredRenderer CreateTargetAndRunFrame( private DeferredRenderer CreateTargetAndRunFrame(

14
tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/DrawOperationTests.cs

@ -1,13 +1,13 @@
using System; using Avalonia.Media;
using Avalonia.Media;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Rendering.SceneGraph; using Avalonia.Rendering.SceneGraph;
using Avalonia.Utilities; using Avalonia.Utilities;
using Avalonia.Visuals.Media.Imaging;
using Moq; using Moq;
using Xunit; using Xunit;
namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
{ {
public class DrawOperationTests public class DrawOperationTests
{ {
[Fact] [Fact]
@ -46,7 +46,13 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
public void Image_Node_Releases_Reference_To_Bitmap_On_Dispose() public void Image_Node_Releases_Reference_To_Bitmap_On_Dispose()
{ {
var bitmap = RefCountable.Create(Mock.Of<IBitmapImpl>()); var bitmap = RefCountable.Create(Mock.Of<IBitmapImpl>());
var imageNode = new ImageNode(Matrix.Identity, bitmap, 1, new Rect(1,1,1,1), new Rect(1,1,1,1)); var imageNode = new ImageNode(
Matrix.Identity,
bitmap,
1,
new Rect(1, 1, 1, 1),
new Rect(1, 1, 1, 1),
BitmapInterpolationMode.Default);
Assert.Equal(2, bitmap.RefCount); Assert.Equal(2, bitmap.RefCount);

Loading…
Cancel
Save