Browse Source

Merge branch 'master' into update/portable-xaml

pull/1855/head
Jumar Macato 8 years ago
committed by GitHub
parent
commit
4d8f3fc8ca
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 9
      samples/ControlCatalog/App.xaml
  2. 2
      samples/ControlCatalog/ControlCatalog.csproj
  3. 3
      samples/ControlCatalog/MainView.xaml
  4. 3
      samples/ControlCatalog/Pages/CanvasPage.xaml
  5. 13
      samples/ControlCatalog/Pages/ListBoxPage.xaml
  6. 25
      samples/ControlCatalog/Pages/ListBoxPage.xaml.cs
  7. 61
      samples/ControlCatalog/Pages/MenuPage.xaml
  8. 97
      samples/ControlCatalog/Pages/MenuPage.xaml.cs
  9. 4
      samples/ControlCatalog/Pages/NumericUpDownPage.xaml
  10. 8
      samples/ControlCatalog/Pages/ScreenPage.cs
  11. 2
      samples/ControlCatalog/SideBar.xaml
  12. 7
      samples/RenderDemo/Pages/AnimationsPage.xaml
  13. 16
      samples/RenderDemo/Pages/AnimationsPage.xaml.cs
  14. 2
      samples/RenderDemo/SideBar.xaml
  15. 28
      samples/RenderDemo/ViewModels/AnimationsPageViewModel.cs
  16. 278
      samples/interop/Direct3DInteropSample/MainWindow.cs
  17. 9
      src/Android/Avalonia.Android/AndroidPlatform.cs
  18. 10
      src/Android/Avalonia.Android/AndroidThreadingInterface.cs
  19. 14
      src/Android/Avalonia.Android/AppBuilder.cs
  20. 9
      src/Android/Avalonia.Android/AvaloniaActivity.cs
  21. 9
      src/Android/Avalonia.Android/AvaloniaView.cs
  22. 2
      src/Android/Avalonia.Android/CursorFactory.cs
  23. 4
      src/Android/Avalonia.Android/Platform/ClipboardImpl.cs
  24. 5
      src/Android/Avalonia.Android/Platform/Input/AndroidKeyboardDevice.cs
  25. 2
      src/Android/Avalonia.Android/Platform/SkiaPlatform/AndroidFramebuffer.cs
  26. 10
      src/Android/Avalonia.Android/Platform/SkiaPlatform/InvalidationAwareSurfaceView.cs
  27. 9
      src/Android/Avalonia.Android/Platform/SkiaPlatform/PopupImpl.cs
  28. 13
      src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
  29. 8
      src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs
  30. 6
      src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidTouchEventsHelper.cs
  31. 9
      src/Android/Avalonia.Android/Platform/Specific/IAndroidView.cs
  32. 15
      src/Android/Avalonia.Android/PlatformIconLoader.cs
  33. 2
      src/Android/Avalonia.Android/SystemDialogImpl.cs
  34. 73
      src/Avalonia.Animation/Animatable.cs
  35. 103
      src/Avalonia.Animation/Animation.cs
  36. 199
      src/Avalonia.Animation/AnimationInstance`1.cs
  37. 5
      src/Avalonia.Animation/AnimatorKeyFrame.cs
  38. 273
      src/Avalonia.Animation/AnimatorStateMachine`1.cs
  39. 135
      src/Avalonia.Animation/Animator`1.cs
  40. 30
      src/Avalonia.Animation/Clock.cs
  41. 72
      src/Avalonia.Animation/ClockBase.cs
  42. 7
      src/Avalonia.Animation/Cue.cs
  43. 65
      src/Avalonia.Animation/DisposeAnimationInstanceSubject.cs
  44. 10
      src/Avalonia.Animation/DoubleAnimator.cs
  45. 1
      src/Avalonia.Animation/DoubleTransition.cs
  46. 1
      src/Avalonia.Animation/Easing/BounceEaseIn.cs
  47. 5
      src/Avalonia.Animation/Easing/Easing.cs
  48. 3
      src/Avalonia.Animation/Easing/EasingTypeConverter.cs
  49. 2
      src/Avalonia.Animation/Easing/ElasticEaseIn.cs
  50. 2
      src/Avalonia.Animation/Easing/ElasticEaseOut.cs
  51. 2
      src/Avalonia.Animation/Easing/SineEaseIn.cs
  52. 2
      src/Avalonia.Animation/Easing/SineEaseOut.cs
  53. 5
      src/Avalonia.Animation/FillMode.cs
  54. 1
      src/Avalonia.Animation/FloatTransition.cs
  55. 13
      src/Avalonia.Animation/IAnimation.cs
  56. 3
      src/Avalonia.Animation/IAnimationSetter.cs
  57. 8
      src/Avalonia.Animation/IAnimator.cs
  58. 11
      src/Avalonia.Animation/IClock.cs
  59. 10
      src/Avalonia.Animation/IGlobalClock.cs
  60. 4
      src/Avalonia.Animation/ITransition.cs
  61. 1
      src/Avalonia.Animation/IntegerTransition.cs
  62. 8
      src/Avalonia.Animation/KeyFrame.cs
  63. 12
      src/Avalonia.Animation/KeyFramePair`1.cs
  64. 7
      src/Avalonia.Animation/PlayState.cs
  65. 5
      src/Avalonia.Animation/PlaybackDirection.cs
  66. 5
      src/Avalonia.Animation/RepeatCount.cs
  67. 3
      src/Avalonia.Animation/RepeatCountTypeConverter.cs
  68. 123
      src/Avalonia.Animation/Timing.cs
  69. 55
      src/Avalonia.Animation/TransitionInstance.cs
  70. 23
      src/Avalonia.Animation/Transition`1.cs
  71. 18
      src/Avalonia.Animation/Utils/DoubleUtils.cs
  72. 2
      src/Avalonia.Animation/Utils/EasingUtils.cs
  73. 1
      src/Avalonia.Base/AvaloniaLocator.cs
  74. 2
      src/Avalonia.Base/AvaloniaObject.cs
  75. 4
      src/Avalonia.Base/AvaloniaProperty.cs
  76. 3
      src/Avalonia.Base/Collections/AvaloniaList.cs
  77. 2
      src/Avalonia.Base/Collections/AvaloniaListConverter.cs
  78. 1
      src/Avalonia.Base/Collections/AvaloniaListExtensions.cs
  79. 4
      src/Avalonia.Base/Collections/IAvaloniaList.cs
  80. 4
      src/Avalonia.Base/Collections/IAvaloniaReadOnlyList.cs
  81. 4
      src/Avalonia.Base/Collections/NotifyCollectionChangedExtensions.cs
  82. 10
      src/Avalonia.Base/Data/BindingChainException.cs
  83. 1
      src/Avalonia.Base/Data/BindingNotification.cs
  84. 2
      src/Avalonia.Base/Data/BindingOperations.cs
  85. 6
      src/Avalonia.Base/Data/Converters/AlwaysEnabledDelegateCommand.cs
  86. 3
      src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs
  87. 1
      src/Avalonia.Base/Data/Converters/IValueConverter.cs
  88. 3
      src/Avalonia.Base/Data/Converters/StringConverters.cs
  89. 2
      src/Avalonia.Base/Data/Core/AvaloniaPropertyAccessorNode.cs
  90. 3
      src/Avalonia.Base/Data/Core/EmptyExpressionNode.cs
  91. 1
      src/Avalonia.Base/Data/Core/ExpressionObserver.cs
  92. 1
      src/Avalonia.Base/Data/Core/ExpressionParseException.cs
  93. 6
      src/Avalonia.Base/Data/Core/ITransformNode.cs
  94. 2
      src/Avalonia.Base/Data/Core/IndexerExpressionNode.cs
  95. 4
      src/Avalonia.Base/Data/Core/IndexerNodeBase.cs
  96. 1
      src/Avalonia.Base/Data/Core/LogicalNotNode.cs
  97. 5
      src/Avalonia.Base/Data/Core/Parsers/ExpressionTreeParser.cs
  98. 2
      src/Avalonia.Base/Data/Core/Parsers/ExpressionVisitorNodeBuilder.cs
  99. 2
      src/Avalonia.Base/Data/Core/Plugins/AvaloniaPropertyAccessorPlugin.cs
  100. 3
      src/Avalonia.Base/Data/Core/Plugins/DataAnnotationsValidationPlugin.cs

9
samples/ControlCatalog/App.xaml

@ -14,6 +14,11 @@
<Setter Property="FontSize" Value="13"/> <Setter Property="FontSize" Value="13"/>
</Style> </Style>
<StyleInclude Source="resm:ControlCatalog.SideBar.xaml"/> <Style Selector="TextBlock.h3">
<Setter Property="Foreground" Value="#a2a2a2"/>
<Setter Property="FontSize" Value="13"/>
</Style>
<StyleInclude Source="resm:ControlCatalog.SideBar.xaml"/>
</Application.Styles> </Application.Styles>
</Application> </Application>

2
samples/ControlCatalog/ControlCatalog.csproj

@ -35,4 +35,4 @@
</ItemGroup> </ItemGroup>
<Import Project="..\..\build\Serilog.props" /> <Import Project="..\..\build\Serilog.props" />
</Project> </Project>

3
samples/ControlCatalog/MainView.xaml

@ -20,8 +20,9 @@
<TabItem Header="Expander"><pages:ExpanderPage/></TabItem> <TabItem Header="Expander"><pages:ExpanderPage/></TabItem>
<TabItem Header="Image"><pages:ImagePage/></TabItem> <TabItem Header="Image"><pages:ImagePage/></TabItem>
<TabItem Header="LayoutTransformControl"><pages:LayoutTransformControlPage/></TabItem> <TabItem Header="LayoutTransformControl"><pages:LayoutTransformControlPage/></TabItem>
<TabItem Header="ListBox"><pages:ListBoxPage/></TabItem>
<TabItem Header="Menu"><pages:MenuPage/></TabItem> <TabItem Header="Menu"><pages:MenuPage/></TabItem>
<TabItem Header="NumericUpDown"><pages:NumericUpDownPage/></TabItem> <TabItem Header="NumericUpDown"><pages:NumericUpDownPage/></TabItem>
<TabItem Header="ProgressBar"><pages:ProgressBarPage/></TabItem> <TabItem Header="ProgressBar"><pages:ProgressBarPage/></TabItem>
<TabItem Header="RadioButton"><pages:RadioButtonPage/></TabItem> <TabItem Header="RadioButton"><pages:RadioButtonPage/></TabItem>
<TabItem Header="Slider"><pages:SliderPage/></TabItem> <TabItem Header="Slider"><pages:SliderPage/></TabItem>

3
samples/ControlCatalog/Pages/CanvasPage.xaml

@ -11,7 +11,8 @@
<GradientStop Offset="1" Color="Transparent"/> <GradientStop Offset="1" Color="Transparent"/>
</LinearGradientBrush.GradientStops> </LinearGradientBrush.GradientStops>
</LinearGradientBrush> </LinearGradientBrush>
</Rectangle.OpacityMask> </Rectangle> </Rectangle.OpacityMask>
</Rectangle>
<Ellipse Fill="Green" Width="58" Height="58" Canvas.Left="88" Canvas.Top="100"/> <Ellipse Fill="Green" Width="58" Height="58" Canvas.Left="88" Canvas.Top="100"/>
<Path Fill="Orange" Data="M 0,0 c 0,0 50,0 50,-50 c 0,0 50,0 50,50 h -50 v 50 l -50,-50 Z" Canvas.Left="30" Canvas.Top="250"/> <Path Fill="Orange" Data="M 0,0 c 0,0 50,0 50,-50 c 0,0 50,0 50,50 h -50 v 50 l -50,-50 Z" Canvas.Left="30" Canvas.Top="250"/>
<Path Fill="OrangeRed" Canvas.Left="180" Canvas.Top="250"> <Path Fill="OrangeRed" Canvas.Left="180" Canvas.Top="250">

13
samples/ControlCatalog/Pages/ListBoxPage.xaml

@ -0,0 +1,13 @@
<UserControl xmlns="https://github.com/avaloniaui">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">ListBox</TextBlock>
<TextBlock Classes="h2">Hosts a collection of ListBoxItem.</TextBlock>
<StackPanel Orientation="Horizontal"
Margin="0,16,0,0"
HorizontalAlignment="Center"
Spacing="16">
<ListBox Items="{Binding}" Width="250" Height="350"></ListBox>
</StackPanel>
</StackPanel>
</UserControl>

25
samples/ControlCatalog/Pages/ListBoxPage.xaml.cs

@ -0,0 +1,25 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace ControlCatalog.Pages
{
public class ListBoxPage : UserControl
{
public ListBoxPage()
{
this.InitializeComponent();
DataContext = Enumerable.Range(1, 10).Select(i => $"Item {i}" )
.ToArray();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

61
samples/ControlCatalog/Pages/MenuPage.xaml

@ -7,29 +7,46 @@
Margin="0,16,0,0" Margin="0,16,0,0"
HorizontalAlignment="Center" HorizontalAlignment="Center"
Spacing="16"> Spacing="16">
<Menu> <StackPanel>
<MenuItem Header="_First"> <TextBlock Classes="h3" Margin="4 8">Defined in XAML</TextBlock>
<MenuItem Header="Standard _Menu Item"/> <Menu>
<Separator/> <MenuItem Header="_First">
<MenuItem Header="Menu with _Submenu"> <MenuItem Header="Standard _Menu Item"/>
<MenuItem Header="Submenu _1"/> <Separator/>
<MenuItem Header="Submenu _2"/> <MenuItem Header="Menu with _Submenu">
<MenuItem Header="Submenu _1"/>
<MenuItem Header="Submenu _2"/>
</MenuItem>
<MenuItem Header="Menu Item with _Icon">
<MenuItem.Icon>
<Image Source="resm:ControlCatalog.Assets.github_icon.png"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Menu Item with _Checkbox">
<MenuItem.Icon>
<CheckBox BorderThickness="0" IsHitTestVisible="False" IsChecked="True"/>
</MenuItem.Icon>
</MenuItem>
</MenuItem> </MenuItem>
<MenuItem Header="Menu Item with _Icon"> <MenuItem Header="_Second">
<MenuItem.Icon> <MenuItem Header="Second _Menu Item"/>
<Image Source="resm:ControlCatalog.Assets.github_icon.png"/>
</MenuItem.Icon>
</MenuItem> </MenuItem>
<MenuItem Header="Menu Item with _Checkbox"> </Menu>
<MenuItem.Icon> </StackPanel>
<CheckBox BorderThickness="0" IsHitTestVisible="False" IsChecked="True"/>
</MenuItem.Icon> <StackPanel>
</MenuItem> <TextBlock Classes="h3" Margin="4 8">Dyanamically generated</TextBlock>
</MenuItem> <Menu Items="{Binding MenuItems}">
<MenuItem Header="_Second"> <Menu.Styles>
<MenuItem Header="Second _Menu Item"/> <Style Selector="MenuItem">
</MenuItem> <Setter Property="Header" Value="{Binding Header}"/>
</Menu> <Setter Property="Items" Value="{Binding Items}"/>
<Setter Property="Command" Value="{Binding Command}"/>
<Setter Property="CommandParameter" Value="{Binding CommandParameter}"/>
</Style>
</Menu.Styles>
</Menu>
</StackPanel>
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</UserControl> </UserControl>

97
samples/ControlCatalog/Pages/MenuPage.xaml.cs

@ -1,5 +1,10 @@
using System.Collections.Generic;
using System.Reactive;
using System.Threading.Tasks;
using System.Windows.Input;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using ReactiveUI;
namespace ControlCatalog.Pages namespace ControlCatalog.Pages
{ {
@ -8,6 +13,51 @@ namespace ControlCatalog.Pages
public MenuPage() public MenuPage()
{ {
this.InitializeComponent(); this.InitializeComponent();
var vm = new MenuPageViewModel();
vm.MenuItems = new[]
{
new MenuItemViewModel
{
Header = "_File",
Items = new[]
{
new MenuItemViewModel { Header = "_Open...", Command = vm.OpenCommand },
new MenuItemViewModel { Header = "Save", Command = vm.SaveCommand },
new MenuItemViewModel { Header = "-" },
new MenuItemViewModel
{
Header = "Recent",
Items = new[]
{
new MenuItemViewModel
{
Header = "File1.txt",
Command = vm.OpenRecentCommand,
CommandParameter = @"c:\foo\File1.txt"
},
new MenuItemViewModel
{
Header = "File2.txt",
Command = vm.OpenRecentCommand,
CommandParameter = @"c:\foo\File2.txt"
},
}
},
}
},
new MenuItemViewModel
{
Header = "_Edit",
Items = new[]
{
new MenuItemViewModel { Header = "_Copy" },
new MenuItemViewModel { Header = "_Paste" },
}
}
};
DataContext = vm;
} }
private void InitializeComponent() private void InitializeComponent()
@ -15,4 +65,51 @@ namespace ControlCatalog.Pages
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);
} }
} }
public class MenuPageViewModel
{
public MenuPageViewModel()
{
OpenCommand = ReactiveCommand.CreateFromTask(Open);
SaveCommand = ReactiveCommand.Create(Save);
OpenRecentCommand = ReactiveCommand.Create<string>(OpenRecent);
}
public IReadOnlyList<MenuItemViewModel> MenuItems { get; set; }
public ReactiveCommand<Unit, Unit> OpenCommand { get; }
public ReactiveCommand<Unit, Unit> SaveCommand { get; }
public ReactiveCommand<string, Unit> OpenRecentCommand { get; }
public async Task Open()
{
var dialog = new OpenFileDialog();
var result = await dialog.ShowAsync();
if (result != null)
{
foreach (var path in result)
{
System.Diagnostics.Debug.WriteLine($"Opened: {path}");
}
}
}
public void Save()
{
System.Diagnostics.Debug.WriteLine("Save");
}
public void OpenRecent(string path)
{
System.Diagnostics.Debug.WriteLine($"Open recent: {path}");
}
}
public class MenuItemViewModel
{
public string Header { get; set; }
public ICommand Command { get; set; }
public object CommandParameter { get; set; }
public IList<MenuItemViewModel> Items { get; set; }
}
} }

4
samples/ControlCatalog/Pages/NumericUpDownPage.xaml

@ -14,7 +14,7 @@
<CheckBox Grid.Row="1" Grid.Column="1" IsChecked="{Binding #upDown.IsReadOnly}" VerticalAlignment="Center" Margin="2"/> <CheckBox Grid.Row="1" Grid.Column="1" IsChecked="{Binding #upDown.IsReadOnly}" VerticalAlignment="Center" Margin="2"/>
<TextBlock Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" Margin="2">AllowSpin:</TextBlock> <TextBlock Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" Margin="2">AllowSpin:</TextBlock>
<CheckBox Grid.Row="2" Grid.Column="1" IsChecked="{Binding #upDown.AllowSpin}" IsEnabled="{Binding #upDown.!IsReadOnly}" VerticalAlignment="Center" Margin="2"/> <CheckBox Grid.Row="2" Grid.Column="1" IsChecked="{Binding #upDown.AllowSpin}" VerticalAlignment="Center" Margin="2"/>
<TextBlock Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" Margin="2">ClipValueToMinMax:</TextBlock> <TextBlock Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" Margin="2">ClipValueToMinMax:</TextBlock>
<CheckBox Grid.Row="3" Grid.Column="1" IsChecked="{Binding #upDown.ClipValueToMinMax}" VerticalAlignment="Center" Margin="2"/> <CheckBox Grid.Row="3" Grid.Column="1" IsChecked="{Binding #upDown.ClipValueToMinMax}" VerticalAlignment="Center" Margin="2"/>
@ -77,4 +77,4 @@
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</UserControl> </UserControl>

8
samples/ControlCatalog/Pages/ScreenPage.cs

@ -42,7 +42,11 @@ namespace ControlCatalog.Pages
context.DrawRectangle(p, boundsRect); context.DrawRectangle(p, boundsRect);
context.DrawRectangle(p, workingAreaRect); context.DrawRectangle(p, workingAreaRect);
FormattedText text = new FormattedText(); FormattedText text = new FormattedText()
{
Typeface = Typeface.Default
};
text.Text = $"Bounds: {screen.Bounds.Width}:{screen.Bounds.Height}"; text.Text = $"Bounds: {screen.Bounds.Width}:{screen.Bounds.Height}";
context.DrawText(Brushes.Black, boundsRect.Position.WithY(boundsRect.Size.Height), text); context.DrawText(Brushes.Black, boundsRect.Position.WithY(boundsRect.Size.Height), text);
@ -59,4 +63,4 @@ namespace ControlCatalog.Pages
context.DrawRectangle(p, new Rect(w.Position.X / 10f + Math.Abs(_leftMost), w.Position.Y / 10, w.Bounds.Width / 10, w.Bounds.Height / 10)); context.DrawRectangle(p, new Rect(w.Position.X / 10f + Math.Abs(_leftMost), w.Position.Y / 10, w.Bounds.Width / 10, w.Bounds.Height / 10));
} }
} }
} }

2
samples/ControlCatalog/SideBar.xaml

@ -36,7 +36,7 @@
<Setter Property="Opacity" Value="0.5"/> <Setter Property="Opacity" Value="0.5"/>
<Setter Property="Transitions"> <Setter Property="Transitions">
<Transitions> <Transitions>
<DoubleTransition Property="Opacity" Duration="0:0:0.5"/> <DoubleTransition Property="Opacity" Duration="0:0:0.2"/>
</Transitions> </Transitions>
</Setter> </Setter>
</Style> </Style>

7
samples/RenderDemo/Pages/AnimationsPage.xaml

@ -107,9 +107,12 @@
</UserControl.Styles> </UserControl.Styles>
<Grid> <Grid>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" ClipToBounds="False"> <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" ClipToBounds="False">
<StackPanel.Clock>
<Clock />
</StackPanel.Clock>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center"> <StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<TextBlock VerticalAlignment="Center">Hover to activate Transform Keyframe Animations.</TextBlock> <TextBlock VerticalAlignment="Center">Hover to activate Transform Keyframe Animations.</TextBlock>
<Button Content="{Binding PlayStateText}" Command="{Binding ToggleGlobalPlayState}"/> <Button Content="{Binding PlayStateText}" Command="{Binding TogglePlayState}" Click="ToggleClock" />
</StackPanel> </StackPanel>
<WrapPanel ClipToBounds="False"> <WrapPanel ClipToBounds="False">
<Border Classes="Test Rect1" Background="DarkRed"/> <Border Classes="Test Rect1" Background="DarkRed"/>
@ -120,4 +123,4 @@
</WrapPanel> </WrapPanel>
</StackPanel> </StackPanel>
</Grid> </Grid>
</UserControl> </UserControl>

16
samples/RenderDemo/Pages/AnimationsPage.xaml.cs

@ -5,6 +5,7 @@ using Avalonia.Controls;
using Avalonia.Controls.Shapes; using Avalonia.Controls.Shapes;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.Media; using Avalonia.Media;
using RenderDemo.ViewModels; using RenderDemo.ViewModels;
@ -23,5 +24,20 @@ namespace RenderDemo.Pages
{ {
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);
} }
private void ToggleClock(object sender, RoutedEventArgs args)
{
var button = sender as Button;
var clock = button.Clock;
if (clock.PlayState == PlayState.Run)
{
clock.PlayState = PlayState.Pause;
}
else if (clock.PlayState == PlayState.Pause)
{
clock.PlayState = PlayState.Run;
}
}
} }
} }

2
samples/RenderDemo/SideBar.xaml

@ -37,7 +37,7 @@
<Setter Property="Opacity" Value="0.5"/> <Setter Property="Opacity" Value="0.5"/>
<Setter Property="Transitions"> <Setter Property="Transitions">
<Transitions> <Transitions>
<DoubleTransition Property="Opacity" Duration="0:0:0.5"/> <DoubleTransition Property="Opacity" Duration="0:0:0.2"/>
</Transitions> </Transitions>
</Setter> </Setter>
</Style> </Style>

28
samples/RenderDemo/ViewModels/AnimationsPageViewModel.cs

@ -6,27 +6,15 @@ namespace RenderDemo.ViewModels
{ {
public class AnimationsPageViewModel : ReactiveObject public class AnimationsPageViewModel : ReactiveObject
{ {
private string _playStateText = "Pause all animations"; private bool _isPlaying = true;
public AnimationsPageViewModel() private string _playStateText = "Pause animations on this page";
{
ToggleGlobalPlayState = ReactiveCommand.Create(()=>TogglePlayState());
}
void TogglePlayState() public void TogglePlayState()
{ {
switch (Timing.GetGlobalPlayState()) PlayStateText = _isPlaying
{ ? "Resume animations on this page" : "Pause animations on this page";
case PlayState.Run: _isPlaying = !_isPlaying;
PlayStateText = "Resume all animations";
Timing.SetGlobalPlayState(PlayState.Pause);
break;
case PlayState.Pause:
PlayStateText = "Pause all animations";
Timing.SetGlobalPlayState(PlayState.Run);
break;
}
} }
public string PlayStateText public string PlayStateText
@ -34,7 +22,5 @@ namespace RenderDemo.ViewModels
get { return _playStateText; } get { return _playStateText; }
set { this.RaiseAndSetIfChanged(ref _playStateText, value); } set { this.RaiseAndSetIfChanged(ref _playStateText, value); }
} }
}
public ReactiveCommand ToggleGlobalPlayState { get; }
}
} }

278
samples/interop/Direct3DInteropSample/MainWindow.cs

@ -1,81 +1,83 @@
using System; // Copyright (c) The Avalonia Project. All rights reserved.
using System.Collections.Generic; // Licensed under the MIT license. See licence.md file in the project root for full license information.
using System.Linq;
using System.Text; using System;
using System.Threading.Tasks;
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Direct2D1;
using Avalonia.Direct2D1.Media; using Avalonia.Direct2D1.Media;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Rendering; using Avalonia.Rendering;
using SharpDX; using SharpDX;
using SharpDX.D3DCompiler; using SharpDX.D3DCompiler;
using SharpDX.Direct2D1; using SharpDX.Direct2D1;
using SharpDX.Direct3D; using SharpDX.Direct3D;
using SharpDX.Direct3D11; using SharpDX.Direct3D11;
using SharpDX.DXGI; using SharpDX.DXGI;
using SharpDX.WIC;
using SharpDX.Mathematics;
using AlphaMode = SharpDX.Direct2D1.AlphaMode; using AlphaMode = SharpDX.Direct2D1.AlphaMode;
using Buffer = SharpDX.Direct3D11.Buffer; using Buffer = SharpDX.Direct3D11.Buffer;
using DeviceContext = SharpDX.Direct3D11.DeviceContext; using DeviceContext = SharpDX.Direct2D1.DeviceContext;
using Factory1 = SharpDX.DXGI.Factory1; using Factory2 = SharpDX.DXGI.Factory2;
using InputElement = SharpDX.Direct3D11.InputElement; using InputElement = SharpDX.Direct3D11.InputElement;
using Matrix = SharpDX.Matrix; using Matrix = SharpDX.Matrix;
using PixelFormat = SharpDX.Direct2D1.PixelFormat; using PixelFormat = SharpDX.Direct2D1.PixelFormat;
using Resource = SharpDX.Direct3D11.Resource;
namespace Direct3DInteropSample namespace Direct3DInteropSample
{ {
class MainWindow : Window public class MainWindow : Window
{ {
private SharpDX.Direct3D11.Device _d3dDevice; Texture2D _backBuffer;
private SharpDX.DXGI.Device _dxgiDevice; RenderTargetView _renderView;
Texture2D backBuffer = null; Texture2D _depthBuffer;
RenderTargetView renderView = null; DepthStencilView _depthView;
Texture2D depthBuffer = null;
DepthStencilView depthView = null;
private readonly SwapChain _swapChain; private readonly SwapChain _swapChain;
private SwapChainDescription _desc; private SwapChainDescription1 _desc;
private Matrix _proj = Matrix.Identity; private Matrix _proj = Matrix.Identity;
private Matrix _view; private readonly Matrix _view;
private Buffer _contantBuffer; private Buffer _contantBuffer;
private SharpDX.Direct2D1.Device _d2dDevice; private DeviceContext _deviceContext;
private SharpDX.Direct2D1.DeviceContext _d2dContext; private readonly MainWindowViewModel _model;
private RenderTarget _d2dRenderTarget;
private MainWindowViewModel _model;
public MainWindow() public MainWindow()
{ {
_dxgiDevice = AvaloniaLocator.Current.GetService<SharpDX.DXGI.Device>();
_d3dDevice = _dxgiDevice.QueryInterface<SharpDX.Direct3D11.Device>();
_d2dDevice = AvaloniaLocator.Current.GetService<SharpDX.Direct2D1.Device>();
DataContext = _model = new MainWindowViewModel(); DataContext = _model = new MainWindowViewModel();
_desc = new SwapChainDescription()
_desc = new SwapChainDescription1()
{ {
BufferCount = 1, BufferCount = 1,
ModeDescription = Width = (int)ClientSize.Width,
new ModeDescription((int)ClientSize.Width, (int)ClientSize.Height, Height = (int)ClientSize.Height,
new Rational(60, 1), Format.R8G8B8A8_UNorm), Format = Format.R8G8B8A8_UNorm,
IsWindowed = true,
OutputHandle = PlatformImpl?.Handle.Handle ?? IntPtr.Zero,
SampleDescription = new SampleDescription(1, 0), SampleDescription = new SampleDescription(1, 0),
SwapEffect = SwapEffect.Discard, SwapEffect = SwapEffect.Discard,
Usage = Usage.RenderTargetOutput Usage = Usage.RenderTargetOutput
}; };
_swapChain = new SwapChain(new Factory1(), _d3dDevice, _desc); using (var factory = Direct2D1Platform.DxgiDevice.Adapter.GetParent<Factory2>())
{
_swapChain = new SwapChain1(factory, Direct2D1Platform.DxgiDevice, PlatformImpl?.Handle.Handle ?? IntPtr.Zero, ref _desc);
}
_d2dContext = new SharpDX.Direct2D1.DeviceContext(_d2dDevice, DeviceContextOptions.None) _deviceContext = new DeviceContext(Direct2D1Platform.Direct2D1Device, DeviceContextOptions.None)
{ {
DotsPerInch = new Size2F(96, 96) DotsPerInch = new Size2F(96, 96)
}; };
CreateMesh(); CreateMesh();
_view = Matrix.LookAtLH(new Vector3(0, 0, -5), new Vector3(0, 0, 0), Vector3.UnitY); _view = Matrix.LookAtLH(new Vector3(0, 0, -5), new Vector3(0, 0, 0), Vector3.UnitY);
this.GetObservable(ClientSizeProperty).Subscribe(Resize); this.GetObservable(ClientSizeProperty).Subscribe(Resize);
Resize(ClientSize); Resize(ClientSize);
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);
Background = Avalonia.Media.Brushes.Transparent; Background = Avalonia.Media.Brushes.Transparent;
} }
@ -83,29 +85,32 @@ namespace Direct3DInteropSample
protected override void HandlePaint(Rect rect) protected override void HandlePaint(Rect rect)
{ {
var viewProj = Matrix.Multiply(_view, _proj); var viewProj = Matrix.Multiply(_view, _proj);
var context = _d3dDevice.ImmediateContext; var context = Direct2D1Platform.Direct3D11Device.ImmediateContext;
// Clear views // Clear views
context.ClearDepthStencilView(depthView, DepthStencilClearFlags.Depth, 1.0f, 0); context.ClearDepthStencilView(_depthView, DepthStencilClearFlags.Depth, 1.0f, 0);
context.ClearRenderTargetView(renderView, Color.White); context.ClearRenderTargetView(_renderView, Color.White);
// Update WorldViewProj Matrix // Update WorldViewProj Matrix
var worldViewProj = Matrix.RotationX((float) _model.RotationX) * Matrix.RotationY((float) _model.RotationY) * var worldViewProj = Matrix.RotationX((float)_model.RotationX) * Matrix.RotationY((float)_model.RotationY)
Matrix.RotationZ((float) _model.RotationZ) * Matrix.RotationZ((float)_model.RotationZ)
* Matrix.Scaling((float) _model.Zoom) * viewProj; * Matrix.Scaling((float)_model.Zoom)
* viewProj;
worldViewProj.Transpose(); worldViewProj.Transpose();
context.UpdateSubresource(ref worldViewProj, _contantBuffer); context.UpdateSubresource(ref worldViewProj, _contantBuffer);
// Draw the cube // Draw the cube
context.Draw(36, 0); context.Draw(36, 0);
base.HandlePaint(rect); base.HandlePaint(rect);
// Present! // Present!
_swapChain.Present(0, PresentFlags.None); _swapChain.Present(0, PresentFlags.None);
} }
private void CreateMesh()
void CreateMesh()
{ {
var device = _d3dDevice; var device = Direct2D1Platform.Direct3D11Device;
// Compile Vertex and Pixel shaders // Compile Vertex and Pixel shaders
var vertexShaderByteCode = ShaderBytecode.CompileFromFile("MiniCube.fx", "VS", "vs_4_0"); var vertexShaderByteCode = ShaderBytecode.CompileFromFile("MiniCube.fx", "VS", "vs_4_0");
var vertexShader = new VertexShader(device, vertexShaderByteCode); var vertexShader = new VertexShader(device, vertexShaderByteCode);
@ -114,63 +119,72 @@ namespace Direct3DInteropSample
var pixelShader = new PixelShader(device, pixelShaderByteCode); var pixelShader = new PixelShader(device, pixelShaderByteCode);
var signature = ShaderSignature.GetInputSignature(vertexShaderByteCode); var signature = ShaderSignature.GetInputSignature(vertexShaderByteCode);
var inputElements = new[]
{
new InputElement("POSITION", 0, Format.R32G32B32A32_Float, 0, 0),
new InputElement("COLOR", 0, Format.R32G32B32A32_Float, 16, 0)
};
// Layout from VertexShader input signature // Layout from VertexShader input signature
var layout = new InputLayout(device, signature, new[] var layout = new InputLayout(
{ device,
new InputElement("POSITION", 0, Format.R32G32B32A32_Float, 0, 0), signature,
new InputElement("COLOR", 0, Format.R32G32B32A32_Float, 16, 0) inputElements);
});
// Instantiate Vertex buffer from vertex data
// Instantiate Vertex buiffer from vertex data var vertices = Buffer.Create(
var vertices = Buffer.Create(device, BindFlags.VertexBuffer, new[] device,
{ BindFlags.VertexBuffer,
new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f), // Front new[]
new Vector4(-1.0f, 1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f), {
new Vector4( 1.0f, 1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f), new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f), // Front
new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f), new Vector4(-1.0f, 1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f),
new Vector4( 1.0f, 1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f), new Vector4( 1.0f, 1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f),
new Vector4( 1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f), new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f),
new Vector4( 1.0f, 1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f),
new Vector4(-1.0f, -1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f), // BACK new Vector4( 1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f),
new Vector4( 1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f),
new Vector4(-1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f), new Vector4(-1.0f, -1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f), // BACK
new Vector4(-1.0f, -1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f), new Vector4( 1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f),
new Vector4( 1.0f, -1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f), new Vector4(-1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f),
new Vector4( 1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f), new Vector4(-1.0f, -1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f),
new Vector4( 1.0f, -1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f),
new Vector4(-1.0f, 1.0f, -1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f), // Top new Vector4( 1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f),
new Vector4(-1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f),
new Vector4( 1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f), new Vector4(-1.0f, 1.0f, -1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f), // Top
new Vector4(-1.0f, 1.0f, -1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f), new Vector4(-1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f),
new Vector4( 1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f), new Vector4( 1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f),
new Vector4( 1.0f, 1.0f, -1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f), new Vector4(-1.0f, 1.0f, -1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f),
new Vector4( 1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f),
new Vector4(-1.0f,-1.0f, -1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f), // Bottom new Vector4( 1.0f, 1.0f, -1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f),
new Vector4( 1.0f,-1.0f, 1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f),
new Vector4(-1.0f,-1.0f, 1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f), new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f), // Bottom
new Vector4(-1.0f,-1.0f, -1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f), new Vector4( 1.0f, -1.0f, 1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f),
new Vector4( 1.0f,-1.0f, -1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f), new Vector4(-1.0f, -1.0f, 1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f),
new Vector4( 1.0f,-1.0f, 1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f), new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f),
new Vector4( 1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f),
new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f), // Left new Vector4( 1.0f, -1.0f, 1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f),
new Vector4(-1.0f, -1.0f, 1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f),
new Vector4(-1.0f, 1.0f, 1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f), new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f), // Left
new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f), new Vector4(-1.0f, -1.0f, 1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f),
new Vector4(-1.0f, 1.0f, 1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f), new Vector4(-1.0f, 1.0f, 1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f),
new Vector4(-1.0f, 1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f), new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f),
new Vector4(-1.0f, 1.0f, 1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f),
new Vector4( 1.0f, -1.0f, -1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f), // Right new Vector4(-1.0f, 1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f),
new Vector4( 1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f),
new Vector4( 1.0f, -1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f), new Vector4( 1.0f, -1.0f, -1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f), // Right
new Vector4( 1.0f, -1.0f, -1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f), new Vector4( 1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f),
new Vector4( 1.0f, 1.0f, -1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f), new Vector4( 1.0f, -1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f),
new Vector4( 1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f), new Vector4( 1.0f, -1.0f, -1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f),
}); new Vector4( 1.0f, 1.0f, -1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f),
new Vector4( 1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f),
});
// Create Constant Buffer // Create Constant Buffer
_contantBuffer = new Buffer(device, Utilities.SizeOf<Matrix>(), ResourceUsage.Default, BindFlags.ConstantBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0); _contantBuffer = new Buffer(device, Utilities.SizeOf<Matrix>(), ResourceUsage.Default, BindFlags.ConstantBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0);
var context = _d3dDevice.ImmediateContext; var context = Direct2D1Platform.Direct3D11Device.ImmediateContext;
// Prepare All the stages // Prepare All the stages
context.InputAssembler.InputLayout = layout; context.InputAssembler.InputLayout = layout;
@ -181,63 +195,73 @@ namespace Direct3DInteropSample
context.PixelShader.Set(pixelShader); context.PixelShader.Set(pixelShader);
} }
void Resize(Size size) private void Resize(Size size)
{ {
Utilities.Dispose(ref _d2dRenderTarget); Utilities.Dispose(ref _deviceContext);
Utilities.Dispose(ref backBuffer); Utilities.Dispose(ref _backBuffer);
Utilities.Dispose(ref renderView); Utilities.Dispose(ref _renderView);
Utilities.Dispose(ref depthBuffer); Utilities.Dispose(ref _depthBuffer);
Utilities.Dispose(ref depthView); Utilities.Dispose(ref _depthView);
var context = _d3dDevice.ImmediateContext; var context = Direct2D1Platform.Direct3D11Device.ImmediateContext;
// Resize the backbuffer // Resize the backbuffer
_swapChain.ResizeBuffers(_desc.BufferCount, (int)size.Width, (int)size.Height, Format.Unknown, SwapChainFlags.None); _swapChain.ResizeBuffers(0, 0, 0, Format.Unknown, SwapChainFlags.None);
// Get the backbuffer from the swapchain // Get the backbuffer from the swapchain
backBuffer = Texture2D.FromSwapChain<Texture2D>(_swapChain, 0); _backBuffer = Resource.FromSwapChain<Texture2D>(_swapChain, 0);
// Renderview on the backbuffer // Renderview on the backbuffer
renderView = new RenderTargetView(_d3dDevice, backBuffer); _renderView = new RenderTargetView(Direct2D1Platform.Direct3D11Device, _backBuffer);
// Create the depth buffer // Create the depth buffer
depthBuffer = new Texture2D(_d3dDevice, new Texture2DDescription() _depthBuffer = new Texture2D(
{ Direct2D1Platform.Direct3D11Device,
Format = Format.D32_Float_S8X24_UInt, new Texture2DDescription()
ArraySize = 1, {
MipLevels = 1, Format = Format.D32_Float_S8X24_UInt,
Width = (int)size.Width, ArraySize = 1,
Height = (int)size.Height, MipLevels = 1,
SampleDescription = new SampleDescription(1, 0), Width = (int)size.Width,
Usage = ResourceUsage.Default, Height = (int)size.Height,
BindFlags = BindFlags.DepthStencil, SampleDescription = new SampleDescription(1, 0),
CpuAccessFlags = CpuAccessFlags.None, Usage = ResourceUsage.Default,
OptionFlags = ResourceOptionFlags.None BindFlags = BindFlags.DepthStencil,
}); CpuAccessFlags = CpuAccessFlags.None,
OptionFlags = ResourceOptionFlags.None
});
// Create the depth buffer view // Create the depth buffer view
depthView = new DepthStencilView(_d3dDevice, depthBuffer); _depthView = new DepthStencilView(Direct2D1Platform.Direct3D11Device, _depthBuffer);
// Setup targets and viewport for rendering // Setup targets and viewport for rendering
context.Rasterizer.SetViewport(new Viewport(0, 0, (int)size.Width, (int)size.Height, 0.0f, 1.0f)); context.Rasterizer.SetViewport(new Viewport(0, 0, (int)size.Width, (int)size.Height, 0.0f, 1.0f));
context.OutputMerger.SetTargets(depthView, renderView); context.OutputMerger.SetTargets(_depthView, _renderView);
// Setup new projection matrix with correct aspect ratio // Setup new projection matrix with correct aspect ratio
_proj = Matrix.PerspectiveFovLH((float)Math.PI / 4.0f, (float)(size.Width / size.Height), 0.1f, 100.0f); _proj = Matrix.PerspectiveFovLH((float)Math.PI / 4.0f, (float)(size.Width / size.Height), 0.1f, 100.0f);
using (var dxgiBackBuffer = _swapChain.GetBackBuffer<Surface>(0)) using (var dxgiBackBuffer = _swapChain.GetBackBuffer<Surface>(0))
{ {
_d2dRenderTarget = new RenderTarget(AvaloniaLocator.Current.GetService<SharpDX.Direct2D1.Factory>() var renderTarget = new SharpDX.Direct2D1.RenderTarget(
, dxgiBackBuffer, new RenderTargetProperties Direct2D1Platform.Direct2D1Factory,
dxgiBackBuffer,
new RenderTargetProperties
{ {
DpiX = 96, DpiX = 96,
DpiY = 96, DpiY = 96,
Type = RenderTargetType.Default, Type = RenderTargetType.Default,
PixelFormat = new PixelFormat(Format.Unknown, AlphaMode.Premultiplied) PixelFormat = new PixelFormat(
Format.Unknown,
AlphaMode.Premultiplied)
}); });
}
_deviceContext = renderTarget.QueryInterface<DeviceContext>();
renderTarget.Dispose();
}
} }
class D3DRenderTarget: IRenderTarget private class D3DRenderTarget : IRenderTarget
{ {
private readonly MainWindow _window; private readonly MainWindow _window;
@ -245,16 +269,14 @@ namespace Direct3DInteropSample
{ {
_window = window; _window = window;
} }
public void Dispose() public void Dispose()
{ {
} }
public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer) public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer)
{ {
return new DrawingContextImpl(visualBrushRenderer, null, _window._d2dRenderTarget, return new DrawingContextImpl(visualBrushRenderer, null, _window._deviceContext);
AvaloniaLocator.Current.GetService<SharpDX.DirectWrite.Factory>(),
AvaloniaLocator.Current.GetService<ImagingFactory>());
} }
} }

9
src/Android/Avalonia.Android/AndroidPlatform.cs

@ -1,8 +1,4 @@
using System; using System;
using System.IO;
using System.Linq;
using Android.Content;
using Android.Views;
using Avalonia.Android.Platform; using Avalonia.Android.Platform;
using Avalonia.Android.Platform.Input; using Avalonia.Android.Platform.Input;
using Avalonia.Android.Platform.SkiaPlatform; using Avalonia.Android.Platform.SkiaPlatform;
@ -56,7 +52,8 @@ namespace Avalonia.Android
.Bind<ISystemDialogImpl>().ToTransient<SystemDialogImpl>() .Bind<ISystemDialogImpl>().ToTransient<SystemDialogImpl>()
.Bind<IWindowingPlatform>().ToConstant(Instance) .Bind<IWindowingPlatform>().ToConstant(Instance)
.Bind<IPlatformIconLoader>().ToSingleton<PlatformIconLoader>() .Bind<IPlatformIconLoader>().ToSingleton<PlatformIconLoader>()
.Bind<IRenderLoop>().ToConstant(new DefaultRenderLoop(60)) .Bind<IRenderTimer>().ToConstant(new DefaultRenderTimer(60))
.Bind<IRenderLoop>().ToConstant(new RenderLoop())
.Bind<IAssetLoader>().ToConstant(new AssetLoader(app.GetType().Assembly)); .Bind<IAssetLoader>().ToConstant(new AssetLoader(app.GetType().Assembly));
SkiaPlatform.Initialize(); SkiaPlatform.Initialize();
@ -79,4 +76,4 @@ namespace Avalonia.Android
return new PopupImpl(); return new PopupImpl();
} }
} }
} }

10
src/Android/Avalonia.Android/AndroidThreadingInterface.cs

@ -1,16 +1,7 @@
using System; using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reactive.Disposables; using System.Reactive.Disposables;
using System.Text;
using System.Threading; using System.Threading;
using Android.App;
using Android.Content;
using Android.OS; using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Threading; using Avalonia.Threading;
@ -88,4 +79,3 @@ namespace Avalonia.Android
public event Action<DispatcherPriority?> Signaled; public event Action<DispatcherPriority?> Signaled;
} }
} }

14
src/Android/Avalonia.Android/AppBuilder.cs

@ -1,16 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Platform;
using Avalonia.Shared.PlatformSupport; using Avalonia.Shared.PlatformSupport;
namespace Avalonia namespace Avalonia
@ -23,4 +11,4 @@ namespace Avalonia
} }
} }
} }

9
src/Android/Avalonia.Android/AvaloniaActivity.cs

@ -1,14 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App; using Android.App;
using Android.Content;
using Android.OS; using Android.OS;
using Android.Runtime;
using Android.Views; using Android.Views;
using Android.Widget;
namespace Avalonia.Android namespace Avalonia.Android
{ {
@ -48,4 +41,4 @@ namespace Avalonia.Android
return View.DispatchKeyEvent(e); return View.DispatchKeyEvent(e);
} }
} }
} }

9
src/Android/Avalonia.Android/AvaloniaView.cs

@ -1,12 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content; using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views; using Android.Views;
using Android.Widget; using Android.Widget;
using Avalonia.Android.Platform.SkiaPlatform; using Avalonia.Android.Platform.SkiaPlatform;
@ -66,4 +59,4 @@ namespace Avalonia.Android
public IDisposable ShowDialog() => null; public IDisposable ShowDialog() => null;
} }
} }
} }

2
src/Android/Avalonia.Android/CursorFactory.cs

@ -9,4 +9,4 @@ namespace Avalonia.Android
public IPlatformHandle GetCursor(StandardCursorType cursorType) public IPlatformHandle GetCursor(StandardCursorType cursorType)
=> new PlatformHandle(IntPtr.Zero, "ZeroCursor"); => new PlatformHandle(IntPtr.Zero, "ZeroCursor");
} }
} }

4
src/Android/Avalonia.Android/Platform/ClipboardImpl.cs

@ -1,9 +1,9 @@
using System.Threading.Tasks;
using Android.Content; using Android.Content;
using Android.Runtime; using Android.Runtime;
using Android.Views; using Android.Views;
using Avalonia.Input.Platform; using Avalonia.Input.Platform;
using Avalonia.Platform; using Avalonia.Platform;
using System.Threading.Tasks;
namespace Avalonia.Android.Platform namespace Avalonia.Android.Platform
{ {
@ -44,4 +44,4 @@ namespace Avalonia.Android.Platform
return Task.FromResult<object>(null); return Task.FromResult<object>(null);
} }
} }
} }

5
src/Android/Avalonia.Android/Platform/Input/AndroidKeyboardDevice.cs

@ -1,12 +1,11 @@
using System;
using Avalonia.Input;
//using Android.InputMethodServices; //using Android.InputMethodServices;
using System.Collections.Generic; using System.Collections.Generic;
using Android.Views; using Android.Views;
using Avalonia.Input;
namespace Avalonia.Android.Platform.Input namespace Avalonia.Android.Platform.Input
{ {
public class AndroidKeyboardDevice : KeyboardDevice, IKeyboardDevice { public class AndroidKeyboardDevice : KeyboardDevice, IKeyboardDevice {
private static readonly Dictionary<Keycode, Key> KeyDic = new Dictionary<Keycode, Key> private static readonly Dictionary<Keycode, Key> KeyDic = new Dictionary<Keycode, Key>
{ {
// { Keycode.Cancel?, Key.Cancel }, // { Keycode.Cancel?, Key.Cancel },

2
src/Android/Avalonia.Android/Platform/SkiaPlatform/AndroidFramebuffer.cs

@ -102,4 +102,4 @@ namespace Avalonia.Android.Platform.SkiaPlatform
uint reserved6; uint reserved6;
} }
} }
} }

10
src/Android/Avalonia.Android/Platform/SkiaPlatform/InvalidationAwareSurfaceView.cs

@ -1,17 +1,9 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content; using Android.Content;
using Android.Graphics; using Android.Graphics;
using Android.OS; using Android.OS;
using Android.Runtime;
using Android.Util; using Android.Util;
using Android.Views; using Android.Views;
using Android.Widget;
using Avalonia.Media;
using Avalonia.Platform; using Avalonia.Platform;
namespace Avalonia.Android namespace Avalonia.Android
@ -90,4 +82,4 @@ namespace Avalonia.Android
protected abstract void Draw(); protected abstract void Draw();
public string HandleDescriptor => "SurfaceView"; public string HandleDescriptor => "SurfaceView";
} }
} }

9
src/Android/Avalonia.Android/Platform/SkiaPlatform/PopupImpl.cs

@ -1,15 +1,8 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content; using Android.Content;
using Android.Graphics; using Android.Graphics;
using Android.OS;
using Android.Runtime; using Android.Runtime;
using Android.Views; using Android.Views;
using Android.Widget;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Platform; using Avalonia.Platform;
@ -116,4 +109,4 @@ namespace Avalonia.Android.Platform.SkiaPlatform
//Not supported //Not supported
} }
} }
} }

13
src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs

@ -1,18 +1,15 @@
using Android.App; using System;
using System.Collections.Generic;
using Android.Content; using Android.Content;
using Android.Graphics; using Android.Graphics;
using Android.Views; using Android.Views;
using Avalonia.Android.Platform.Input;
using Avalonia.Android.Platform.Specific; using Avalonia.Android.Platform.Specific;
using Avalonia.Android.Platform.Specific.Helpers; using Avalonia.Android.Platform.Specific.Helpers;
using Avalonia.Controls.Platform.Surfaces;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Input.Raw; using Avalonia.Input.Raw;
using Avalonia.Platform; using Avalonia.Platform;
using System;
using System.Collections.Generic;
using System.Reactive.Disposables;
using Avalonia.Android.Platform.Input;
using Avalonia.Controls;
using Avalonia.Controls.Platform.Surfaces;
using Avalonia.Rendering; using Avalonia.Rendering;
namespace Avalonia.Android.Platform.SkiaPlatform namespace Avalonia.Android.Platform.SkiaPlatform
@ -196,4 +193,4 @@ namespace Avalonia.Android.Platform.SkiaPlatform
ILockedFramebuffer IFramebufferPlatformSurface.Lock()=>new AndroidFramebuffer(_view.Holder.Surface); ILockedFramebuffer IFramebufferPlatformSurface.Lock()=>new AndroidFramebuffer(_view.Holder.Surface);
} }
} }

8
src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs

@ -1,3 +1,5 @@
using System;
using System.ComponentModel;
using Android.Content; using Android.Content;
using Android.Runtime; using Android.Runtime;
using Android.Views; using Android.Views;
@ -7,8 +9,6 @@ using Avalonia.Controls;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Input.Raw; using Avalonia.Input.Raw;
using Avalonia.Platform; using Avalonia.Platform;
using System;
using System.ComponentModel;
namespace Avalonia.Android.Platform.Specific.Helpers namespace Avalonia.Android.Platform.Specific.Helpers
{ {
@ -120,7 +120,7 @@ namespace Avalonia.Android.Platform.Specific.Helpers
_lastFocusedElement = element; _lastFocusedElement = element;
} }
public void ActivateAutoShowKeybord() public void ActivateAutoShowKeyboard()
{ {
var kbDevice = (KeyboardDevice.Instance as INotifyPropertyChanged); var kbDevice = (KeyboardDevice.Instance as INotifyPropertyChanged);
@ -142,4 +142,4 @@ namespace Avalonia.Android.Platform.Specific.Helpers
HandleEvents = false; HandleEvents = false;
} }
} }
} }

6
src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidTouchEventsHelper.cs

@ -1,10 +1,8 @@
using Android.Graphics; using System;
using Android.Views; using Android.Views;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Input.Raw; using Avalonia.Input.Raw;
using Avalonia.Media;
using Avalonia.Platform; using Avalonia.Platform;
using System;
namespace Avalonia.Android.Platform.Specific.Helpers namespace Avalonia.Android.Platform.Specific.Helpers
{ {
@ -131,4 +129,4 @@ namespace Avalonia.Android.Platform.Specific.Helpers
HandleEvents = false; HandleEvents = false;
} }
} }
} }

9
src/Android/Avalonia.Android/Platform/Specific/IAndroidView.cs

@ -1,16 +1,9 @@
using Android.Graphics;
using Android.Views; using Android.Views;
using System;
using System.Collections.Generic;
namespace Avalonia.Android.Platform.Specific namespace Avalonia.Android.Platform.Specific
{ {
public interface IAndroidView public interface IAndroidView
{ {
View View { get; } View View { get; }
} }
}
}

15
src/Android/Avalonia.Android/PlatformIconLoader.cs

@ -1,16 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Avalonia.Platform;
using System.IO; using System.IO;
using Avalonia.Platform;
namespace Avalonia.Android namespace Avalonia.Android
{ {
@ -54,4 +43,4 @@ namespace Avalonia.Android
stream.CopyTo(outputStream); stream.CopyTo(outputStream);
} }
} }
} }

2
src/Android/Avalonia.Android/SystemDialogImpl.cs

@ -18,4 +18,4 @@ namespace Avalonia.Android
throw new NotImplementedException(); throw new NotImplementedException();
} }
} }
} }

73
src/Avalonia.Animation/Animatable.cs

@ -1,88 +1,46 @@
// 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.Linq;
using Avalonia.Data;
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq; using System.Reactive.Linq;
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Animation; using Avalonia.Data;
using System.Collections.Generic;
using System.Threading;
using System.Collections.Concurrent;
namespace Avalonia.Animation namespace Avalonia.Animation
{ {
/// <summary> /// <summary>
/// Base class for control which can have property transitions. /// Base class for all animatable objects.
/// </summary> /// </summary>
public class Animatable : AvaloniaObject public class Animatable : AvaloniaObject
{ {
/// <summary> public static readonly StyledProperty<IClock> ClockProperty =
/// Initializes this <see cref="Animatable"/> object. AvaloniaProperty.Register<Animatable, IClock>(nameof(Clock), inherits: true);
/// </summary>
public Animatable()
{
Transitions = new Transitions();
AnimatableTimer = Timing.AnimationStateTimer
.Select(p =>
{
if (this._playState == PlayState.Pause)
{
return PlayState.Pause;
}
else return p;
})
.Publish()
.RefCount();
}
/// <summary>
/// The specific animations timer for this control.
/// </summary>
/// <returns></returns>
public IObservable<PlayState> AnimatableTimer;
/// <summary>
/// Defines the <see cref="PlayState"/> property.
/// </summary>
public static readonly DirectProperty<Animatable, PlayState> PlayStateProperty =
AvaloniaProperty.RegisterDirect<Animatable, PlayState>(
nameof(PlayState),
o => o.PlayState,
(o, v) => o.PlayState = v);
private PlayState _playState = PlayState.Run;
/// <summary> public IClock Clock
/// Gets or sets the state of the animation for this
/// control.
/// </summary>
public PlayState PlayState
{ {
get { return _playState; } get => GetValue(ClockProperty);
set { SetAndRaise(PlayStateProperty, ref _playState, value); } set => SetValue(ClockProperty, value);
} }
/// <summary> /// <summary>
/// Defines the <see cref="Transitions"/> property. /// Defines the <see cref="Transitions"/> property.
/// </summary> /// </summary>
public static readonly DirectProperty<Animatable, IEnumerable<ITransition>> TransitionsProperty = public static readonly DirectProperty<Animatable, Transitions> TransitionsProperty =
AvaloniaProperty.RegisterDirect<Animatable, IEnumerable<ITransition>>( AvaloniaProperty.RegisterDirect<Animatable, Transitions>(
nameof(Transitions), nameof(Transitions),
o => o.Transitions, o => o.Transitions,
(o, v) => o.Transitions = v); (o, v) => o.Transitions = v);
private IEnumerable<ITransition> _transitions = new AvaloniaList<ITransition>(); private Transitions _transitions;
/// <summary> /// <summary>
/// Gets or sets the property transitions for the control. /// Gets or sets the property transitions for the control.
/// </summary> /// </summary>
public IEnumerable<ITransition> Transitions public Transitions Transitions
{ {
get { return _transitions; } get { return _transitions ?? (_transitions = new Transitions()); }
set { SetAndRaise(TransitionsProperty, ref _transitions, value); } set { SetAndRaise(TransitionsProperty, ref _transitions, value); }
} }
@ -99,10 +57,9 @@ namespace Avalonia.Animation
if (match != null) if (match != null)
{ {
match.Apply(this, e.OldValue, e.NewValue); match.Apply(this, Clock ?? Avalonia.Animation.Clock.GlobalClock, e.OldValue, e.NewValue);
} }
} }
} }
} }
} }

103
src/Avalonia.Animation/Animation.cs

@ -1,18 +1,14 @@
// 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.Animation.Easings;
using Avalonia.Animation;
using Avalonia.Collections;
using Avalonia.Metadata;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Specialized;
using System.Reflection;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using System.Reactive.Linq;
using System.Reactive.Disposables; using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Threading.Tasks;
using Avalonia.Animation.Easings;
using Avalonia.Collections;
namespace Avalonia.Animation namespace Avalonia.Animation
{ {
@ -21,6 +17,55 @@ namespace Avalonia.Animation
/// </summary> /// </summary>
public class Animation : AvaloniaList<KeyFrame>, IAnimation public class Animation : AvaloniaList<KeyFrame>, IAnimation
{ {
/// <summary>
/// Gets or sets the active time of this animation.
/// </summary>
public TimeSpan Duration { get; set; }
/// <summary>
/// Gets or sets the repeat count for this animation.
/// </summary>
public RepeatCount RepeatCount { get; set; }
/// <summary>
/// Gets or sets the playback direction for this animation.
/// </summary>
public PlaybackDirection PlaybackDirection { get; set; }
/// <summary>
/// Gets or sets the value fill mode for this animation.
/// </summary>
public FillMode FillMode { get; set; }
/// <summary>
/// Gets or sets the easing function to be used for this animation.
/// </summary>
public Easing Easing { get; set; } = new LinearEasing();
/// <summary>
/// Gets or sets the speed multiple for this animation.
/// </summary>
public double SpeedRatio { get; set; } = 1d;
/// <summary>
/// Gets or sets the delay time for this animation.
/// </summary>
/// <remarks>
/// Describes a delay to be added before the animation starts, and optionally between
/// repeats of the animation if <see cref="DelayBetweenIterations"/> is set.
/// </remarks>
public TimeSpan Delay { get; set; }
/// <summary>
/// Gets or sets a value indicating whether <see cref="Delay"/> will be applied between
/// iterations of the animation.
/// </summary>
/// <remarks>
/// If this property is not set, then <see cref="Delay"/> will only be applied to the first
/// iteration of the animation.
/// </remarks>
public bool DelayBetweenIterations { get; set; }
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)>
{ {
( prop => typeof(double).IsAssignableFrom(prop.PropertyType), typeof(DoubleAnimator) ) ( prop => typeof(double).IsAssignableFrom(prop.PropertyType), typeof(DoubleAnimator) )
@ -44,38 +89,6 @@ namespace Avalonia.Animation
return null; return null;
} }
public AvaloniaList<IAnimator> _animators { get; set; } = new AvaloniaList<IAnimator>();
/// <summary>
/// Run time of this animation.
/// </summary>
public TimeSpan Duration { get; set; }
/// <summary>
/// Delay time for this animation.
/// </summary>
public TimeSpan Delay { get; set; }
/// <summary>
/// The repeat count for this animation.
/// </summary>
public RepeatCount RepeatCount { get; set; }
/// <summary>
/// The playback direction for this animation.
/// </summary>
public PlaybackDirection PlaybackDirection { get; set; }
/// <summary>
/// The value fill mode for this animation.
/// </summary>
public FillMode FillMode { get; set; }
/// <summary>
/// Easing function to be used.
/// </summary>
public Easing Easing { get; set; } = new LinearEasing();
private (IList<IAnimator> Animators, IList<IDisposable> subscriptions) InterpretKeyframes(Animatable control) private (IList<IAnimator> Animators, IList<IDisposable> subscriptions) InterpretKeyframes(Animatable control)
{ {
var handlerList = new List<(Type type, AvaloniaProperty property)>(); var handlerList = new List<(Type type, AvaloniaProperty property)>();
@ -131,12 +144,12 @@ namespace Avalonia.Animation
} }
/// <inheritdocs/> /// <inheritdocs/>
public IDisposable Apply(Animatable control, IObservable<bool> match, Action onComplete) public IDisposable Apply(Animatable control, IClock clock, IObservable<bool> match, Action onComplete)
{ {
var (animators, subscriptions) = InterpretKeyframes(control); var (animators, subscriptions) = InterpretKeyframes(control);
if (animators.Count == 1) if (animators.Count == 1)
{ {
subscriptions.Add(animators[0].Apply(this, control, match, onComplete)); subscriptions.Add(animators[0].Apply(this, control, clock, match, onComplete));
} }
else else
{ {
@ -150,7 +163,7 @@ namespace Avalonia.Animation
animatorOnComplete = () => tcs.SetResult(null); animatorOnComplete = () => tcs.SetResult(null);
completionTasks.Add(tcs.Task); completionTasks.Add(tcs.Task);
} }
subscriptions.Add(animator.Apply(this, control, match, animatorOnComplete)); subscriptions.Add(animator.Apply(this, control, clock, match, animatorOnComplete));
} }
if (onComplete != null) if (onComplete != null)
@ -162,7 +175,7 @@ namespace Avalonia.Animation
} }
/// <inheritdocs/> /// <inheritdocs/>
public Task RunAsync(Animatable control) public Task RunAsync(Animatable control, IClock clock = null)
{ {
var run = new TaskCompletionSource<object>(); var run = new TaskCompletionSource<object>();
@ -170,7 +183,7 @@ namespace Avalonia.Animation
run.SetException(new InvalidOperationException("Looping animations must not use the Run method.")); run.SetException(new InvalidOperationException("Looping animations must not use the Run method."));
IDisposable subscriptions = null; IDisposable subscriptions = null;
subscriptions = this.Apply(control, Observable.Return(true), () => subscriptions = this.Apply(control, clock, Observable.Return(true), () =>
{ {
run.SetResult(null); run.SetResult(null);
subscriptions?.Dispose(); subscriptions?.Dispose();

199
src/Avalonia.Animation/AnimationInstance`1.cs

@ -0,0 +1,199 @@
using System;
using System.Linq;
using System.Reactive.Linq;
using Avalonia.Animation.Utils;
using Avalonia.Data;
using Avalonia.Reactive;
namespace Avalonia.Animation
{
/// <summary>
/// Handles interpolation and time-related functions
/// for keyframe animations.
/// </summary>
internal class AnimationInstance<T> : SingleSubscriberObservableBase<T>
{
private T _lastInterpValue;
private T _firstKFValue;
private long _repeatCount;
private double _currentIteration;
private bool _isLooping;
private bool _gotFirstKFValue;
private bool _iterationDelay;
private FillMode _fillMode;
private PlaybackDirection _animationDirection;
private Animator<T> _parent;
private Animatable _targetControl;
private T _neutralValue;
private double _speedRatio;
private TimeSpan _delay;
private TimeSpan _duration;
private Easings.Easing _easeFunc;
private Action _onCompleteAction;
private Func<double, T, T> _interpolator;
private IDisposable _timerSubscription;
private readonly IClock _baseClock;
private IClock _clock;
public AnimationInstance(Animation animation, Animatable control, Animator<T> animator, IClock baseClock, Action OnComplete, Func<double, T, T> Interpolator)
{
if (animation.SpeedRatio <= 0)
throw new InvalidOperationException("Speed ratio cannot be negative or zero.");
if (animation.Duration.TotalSeconds <= 0)
throw new InvalidOperationException("Duration cannot be negative or zero.");
_parent = animator;
_easeFunc = animation.Easing;
_targetControl = control;
_neutralValue = (T)_targetControl.GetValue(_parent.Property);
_speedRatio = animation.SpeedRatio;
_delay = animation.Delay;
_duration = animation.Duration;
_iterationDelay = animation.DelayBetweenIterations;
switch (animation.RepeatCount.RepeatType)
{
case RepeatType.None:
_repeatCount = 1;
break;
case RepeatType.Loop:
_isLooping = true;
break;
case RepeatType.Repeat:
_repeatCount = (long)animation.RepeatCount.Value;
break;
}
_animationDirection = animation.PlaybackDirection;
_fillMode = animation.FillMode;
_onCompleteAction = OnComplete;
_interpolator = Interpolator;
_baseClock = baseClock;
}
protected override void Unsubscribed()
{
_timerSubscription?.Dispose();
_clock.PlayState = PlayState.Stop;
}
protected override void Subscribed()
{
_clock = new Clock(_baseClock);
_timerSubscription = _clock.Subscribe(Step);
}
public void Step(TimeSpan frameTick)
{
try
{
InternalStep(frameTick);
}
catch (Exception e)
{
PublishError(e);
}
}
private void DoComplete()
{
if (_fillMode == FillMode.Forward || _fillMode == FillMode.Both)
_targetControl.SetValue(_parent.Property, _lastInterpValue, BindingPriority.LocalValue);
_onCompleteAction?.Invoke();
PublishCompleted();
}
private void DoDelay()
{
if (_fillMode == FillMode.Backward || _fillMode == FillMode.Both)
if (_currentIteration == 0)
PublishNext(_firstKFValue);
else
PublishNext(_lastInterpValue);
}
private void DoPlayStates()
{
if (_clock.PlayState == PlayState.Stop || _baseClock.PlayState == PlayState.Stop)
DoComplete();
if (!_gotFirstKFValue)
{
_firstKFValue = (T)_parent.First().Value;
_gotFirstKFValue = true;
}
}
private void InternalStep(TimeSpan time)
{
DoPlayStates();
var delayEndpoint = _delay;
var iterationEndpoint = delayEndpoint + _duration;
//determine if time is currently in the first iteration.
if (time >= TimeSpan.Zero & time <= iterationEndpoint)
{
_currentIteration = 1;
}
else if (time > iterationEndpoint)
{
//Subtract first iteration to properly get the subsequent iteration time
time -= iterationEndpoint;
if (!_iterationDelay & delayEndpoint > TimeSpan.Zero)
{
delayEndpoint = TimeSpan.Zero;
iterationEndpoint = _duration;
}
//Calculate the current iteration number
_currentIteration = (int)Math.Floor((double)((double)time.Ticks / iterationEndpoint.Ticks)) + 2;
}
else
{
return;
}
time = TimeSpan.FromTicks((long)(time.Ticks % iterationEndpoint.Ticks));
if (!_isLooping)
{
if ((_currentIteration > _repeatCount) || (time > iterationEndpoint))
DoComplete();
}
// Determine if the current iteration should have its normalized time inverted.
bool isCurIterReverse = _animationDirection == PlaybackDirection.Normal ? false :
_animationDirection == PlaybackDirection.Alternate ? (_currentIteration % 2 == 0) ? false : true :
_animationDirection == PlaybackDirection.AlternateReverse ? (_currentIteration % 2 == 0) ? true : false :
_animationDirection == PlaybackDirection.Reverse ? true : false;
if (delayEndpoint > TimeSpan.Zero & time < delayEndpoint)
{
DoDelay();
}
else
{
// Offset the delay time
time -= delayEndpoint;
iterationEndpoint -= delayEndpoint;
// Normalize time
var interpVal = (double)time.Ticks / iterationEndpoint.Ticks;
if (isCurIterReverse)
interpVal = 1 - interpVal;
// Ease and interpolate
var easedTime = _easeFunc.Ease(interpVal);
_lastInterpValue = _interpolator(easedTime, _neutralValue);
PublishNext(_lastInterpValue);
}
}
}
}

5
src/Avalonia.Animation/AnimatorKeyFrame.cs

@ -1,9 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel; using System.ComponentModel;
using Avalonia.Metadata;
using Avalonia.Collections;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Reactive; using Avalonia.Reactive;
@ -29,6 +25,7 @@ namespace Avalonia.Animation
Cue = cue; Cue = cue;
} }
internal bool isNeutral;
public Type AnimatorType { get; } public Type AnimatorType { get; }
public Cue Cue { get; } public Cue Cue { get; }
public AvaloniaProperty Property { get; private set; } public AvaloniaProperty Property { get; private set; }

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

@ -1,273 +0,0 @@
using System;
using System.Linq;
using Avalonia.Data;
namespace Avalonia.Animation
{
/// <summary>
/// Provides statefulness for an iteration of a keyframe animation.
/// </summary>
internal class AnimatorStateMachine<T> : IObservable<object>, IDisposable
{
object _lastInterpValue;
object _firstKFValue;
private ulong _delayTotalFrameCount;
private ulong _durationTotalFrameCount;
private ulong _delayFrameCount;
private ulong _durationFrameCount;
private ulong _repeatCount;
private ulong _currentIteration;
private bool _isLooping;
private bool _isRepeating;
private bool _isReversed;
private bool _checkLoopAndRepeat;
private bool _gotFirstKFValue;
private FillMode _fillMode;
private PlaybackDirection _animationDirection;
private KeyFramesStates _currentState;
private KeyFramesStates _savedState;
private Animator<T> _parent;
private Animation _targetAnimation;
private Animatable _targetControl;
private T _neutralValue;
internal bool _unsubscribe = false;
private IObserver<object> _targetObserver;
private readonly Action _onComplete;
[Flags]
private enum KeyFramesStates
{
Initialize,
DoDelay,
DoRun,
RunForwards,
RunBackwards,
RunApplyValue,
RunComplete,
Pause,
Stop,
Disposed
}
public AnimatorStateMachine(Animation animation, Animatable control, Animator<T> animator, Action onComplete)
{
_parent = animator;
_targetAnimation = animation;
_targetControl = control;
_neutralValue = (T)_targetControl.GetValue(_parent.Property);
_delayTotalFrameCount = (ulong)(animation.Delay.Ticks / Timing.FrameTick.Ticks);
_durationTotalFrameCount = (ulong)(animation.Duration.Ticks / Timing.FrameTick.Ticks);
switch (animation.RepeatCount.RepeatType)
{
case RepeatType.Loop:
_isLooping = true;
_checkLoopAndRepeat = true;
break;
case RepeatType.Repeat:
_isRepeating = true;
_checkLoopAndRepeat = true;
_repeatCount = animation.RepeatCount.Value;
break;
}
_isReversed = (animation.PlaybackDirection & PlaybackDirection.Reverse) != 0;
_animationDirection = _targetAnimation.PlaybackDirection;
_fillMode = _targetAnimation.FillMode;
if (_durationTotalFrameCount > 0)
_currentState = KeyFramesStates.DoDelay;
else
_currentState = KeyFramesStates.DoRun;
_onComplete = onComplete;
}
public void Step(PlayState _playState, Func<double, T, T> Interpolator)
{
try
{
InternalStep(_playState, Interpolator);
}
catch (Exception e)
{
_targetObserver?.OnError(e);
}
}
private void InternalStep(PlayState _playState, Func<double, T, T> Interpolator)
{
if (!_gotFirstKFValue)
{
_firstKFValue = _parent.First().Value;
_gotFirstKFValue = true;
}
if (_currentState == KeyFramesStates.Disposed)
throw new InvalidProgramException("This KeyFrames Animation is already disposed.");
if (_playState == PlayState.Stop)
_currentState = KeyFramesStates.Stop;
// Save state and pause the machine
if (_playState == PlayState.Pause && _currentState != KeyFramesStates.Pause)
{
_savedState = _currentState;
_currentState = KeyFramesStates.Pause;
}
// Resume the previous state
if (_playState != PlayState.Pause && _currentState == KeyFramesStates.Pause)
_currentState = _savedState;
double _tempDuration = 0d, _easedTime;
bool handled = false;
while (!handled)
{
switch (_currentState)
{
case KeyFramesStates.DoDelay:
if (_fillMode == FillMode.Backward
|| _fillMode == FillMode.Both)
{
if (_currentIteration == 0)
{
_targetObserver.OnNext(_firstKFValue);
}
else
{
_targetObserver.OnNext(_lastInterpValue);
}
}
if (_delayFrameCount > _delayTotalFrameCount)
{
_currentState = KeyFramesStates.DoRun;
}
else
{
handled = true;
_delayFrameCount++;
}
break;
case KeyFramesStates.DoRun:
if (_isReversed)
_currentState = KeyFramesStates.RunBackwards;
else
_currentState = KeyFramesStates.RunForwards;
break;
case KeyFramesStates.RunForwards:
if (_durationFrameCount > _durationTotalFrameCount)
{
_currentState = KeyFramesStates.RunComplete;
}
else
{
_tempDuration = (double)_durationFrameCount / _durationTotalFrameCount;
_currentState = KeyFramesStates.RunApplyValue;
}
break;
case KeyFramesStates.RunBackwards:
if (_durationFrameCount > _durationTotalFrameCount)
{
_currentState = KeyFramesStates.RunComplete;
}
else
{
_tempDuration = (double)(_durationTotalFrameCount - _durationFrameCount) / _durationTotalFrameCount;
_currentState = KeyFramesStates.RunApplyValue;
}
break;
case KeyFramesStates.RunApplyValue:
_easedTime = _targetAnimation.Easing.Ease(_tempDuration);
_durationFrameCount++;
_lastInterpValue = Interpolator(_easedTime, _neutralValue);
_targetObserver.OnNext(_lastInterpValue);
_currentState = KeyFramesStates.DoRun;
handled = true;
break;
case KeyFramesStates.RunComplete:
if (_checkLoopAndRepeat)
{
_delayFrameCount = 0;
_durationFrameCount = 0;
if (_isLooping)
{
_currentState = KeyFramesStates.DoRun;
}
else if (_isRepeating)
{
if (_currentIteration >= _repeatCount)
{
_currentState = KeyFramesStates.Stop;
}
else
{
_currentState = KeyFramesStates.DoRun;
}
_currentIteration++;
}
if (_animationDirection == PlaybackDirection.Alternate
|| _animationDirection == PlaybackDirection.AlternateReverse)
_isReversed = !_isReversed;
break;
}
_currentState = KeyFramesStates.Stop;
break;
case KeyFramesStates.Stop:
if (_fillMode == FillMode.Forward
|| _fillMode == FillMode.Both)
{
_targetControl.SetValue(_parent.Property, _lastInterpValue, BindingPriority.LocalValue);
}
_targetObserver.OnCompleted();
_onComplete?.Invoke();
Dispose();
handled = true;
break;
default:
handled = true;
break;
}
}
}
public IDisposable Subscribe(IObserver<object> observer)
{
_targetObserver = observer;
return this;
}
public void Dispose()
{
_unsubscribe = true;
_currentState = KeyFramesStates.Disposed;
}
}
}

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

@ -1,13 +1,14 @@
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 System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text;
using Avalonia.Collections;
using System.ComponentModel;
using Avalonia.Animation.Utils;
using System.Reactive.Linq;
using System.Linq; using System.Linq;
using System.Reactive.Linq;
using Avalonia.Animation.Utils;
using Avalonia.Collections;
using Avalonia.Data; using Avalonia.Data;
using System.Reactive.Disposables; using Avalonia.Reactive;
namespace Avalonia.Animation namespace Avalonia.Animation
{ {
@ -19,9 +20,9 @@ namespace Avalonia.Animation
/// <summary> /// <summary>
/// List of type-converted keyframes. /// List of type-converted keyframes.
/// </summary> /// </summary>
private readonly SortedList<double, (AnimatorKeyFrame, bool isNeutral)> _convertedKeyframes = new SortedList<double, (AnimatorKeyFrame, bool)>(); private readonly List<AnimatorKeyFrame> _convertedKeyframes = new List<AnimatorKeyFrame>();
private bool _isVerfifiedAndConverted; private bool _isVerifiedAndConverted;
/// <summary> /// <summary>
/// Gets or sets the target property for the keyframe. /// Gets or sets the target property for the keyframe.
@ -31,22 +32,17 @@ namespace Avalonia.Animation
public Animator() public Animator()
{ {
// Invalidate keyframes when changed. // Invalidate keyframes when changed.
this.CollectionChanged += delegate { _isVerfifiedAndConverted = false; }; this.CollectionChanged += delegate { _isVerifiedAndConverted = false; };
} }
/// <inheritdoc/> /// <inheritdoc/>
public virtual IDisposable Apply(Animation animation, Animatable control, IObservable<bool> obsMatch, Action onComplete) public virtual IDisposable Apply(Animation animation, Animatable control, IClock clock, IObservable<bool> match, Action onComplete)
{ {
if (!_isVerfifiedAndConverted) if (!_isVerifiedAndConverted)
VerifyConvertKeyFrames(); VerifyConvertKeyFrames();
return obsMatch var subject = new DisposeAnimationInstanceSubject<T>(this, animation, control, clock, onComplete);
// Ignore triggers when global timers are paused. return match.Subscribe(subject);
.Where(p => p && Timing.GetGlobalPlayState() != PlayState.Pause)
.Subscribe(_ =>
{
var timerObs = RunKeyFrames(animation, control, onComplete);
});
} }
/// <summary> /// <summary>
@ -56,56 +52,85 @@ namespace Avalonia.Animation
/// (i.e., the normalized time between the selected keyframes, relative to the /// (i.e., the normalized time between the selected keyframes, relative to the
/// time parameter). /// time parameter).
/// </summary> /// </summary>
/// <param name="t">The time parameter, relative to the total animation time</param> /// <param name="animationTime">The time parameter, relative to the total animation time</param>
protected (double IntraKFTime, KeyFramePair<T> KFPair) GetKFPairAndIntraKFTime(double t) protected (double IntraKFTime, KeyFramePair<T> KFPair) GetKFPairAndIntraKFTime(double animationTime)
{ {
KeyValuePair<double, (AnimatorKeyFrame frame, bool isNeutral)> firstCue, lastCue; AnimatorKeyFrame firstKeyframe, lastKeyframe;
int kvCount = _convertedKeyframes.Count; int kvCount = _convertedKeyframes.Count;
if (kvCount > 2) if (kvCount > 2)
{ {
if (DoubleUtils.AboutEqual(t, 0.0) || t < 0.0) if (animationTime <= 0.0)
{ {
firstCue = _convertedKeyframes.First(); firstKeyframe = _convertedKeyframes[0];
lastCue = _convertedKeyframes.Skip(1).First(); lastKeyframe = _convertedKeyframes[1];
} }
else if (DoubleUtils.AboutEqual(t, 1.0) || t > 1.0) else if (animationTime >= 1.0)
{ {
firstCue = _convertedKeyframes.Skip(kvCount - 2).First(); firstKeyframe = _convertedKeyframes[_convertedKeyframes.Count - 2];
lastCue = _convertedKeyframes.Last(); lastKeyframe = _convertedKeyframes[_convertedKeyframes.Count - 1];
} }
else else
{ {
firstCue = _convertedKeyframes.Last(j => j.Key <= t); int index = FindClosestBeforeKeyFrame(animationTime);
lastCue = _convertedKeyframes.First(j => j.Key >= t); firstKeyframe = _convertedKeyframes[index];
lastKeyframe = _convertedKeyframes[index + 1];
} }
} }
else else
{ {
firstCue = _convertedKeyframes.First(); firstKeyframe = _convertedKeyframes[0];
lastCue = _convertedKeyframes.Last(); lastKeyframe = _convertedKeyframes[1];
} }
double t0 = firstCue.Key; double t0 = firstKeyframe.Cue.CueValue;
double t1 = lastCue.Key; double t1 = lastKeyframe.Cue.CueValue;
var intraframeTime = (t - t0) / (t1 - t0); var intraframeTime = (animationTime - t0) / (t1 - t0);
var firstFrameData = (firstCue.Value.frame.GetTypedValue<T>(), firstCue.Value.isNeutral); var firstFrameData = (firstKeyframe.GetTypedValue<T>(), firstKeyframe.isNeutral);
var lastFrameData = (lastCue.Value.frame.GetTypedValue<T>(), lastCue.Value.isNeutral); var lastFrameData = (lastKeyframe.GetTypedValue<T>(), lastKeyframe.isNeutral);
return (intraframeTime, new KeyFramePair<T>(firstFrameData, lastFrameData)); return (intraframeTime, new KeyFramePair<T>(firstFrameData, lastFrameData));
} }
private int FindClosestBeforeKeyFrame(double time)
{
int FindClosestBeforeKeyFrame(int startIndex, int length)
{
if (length == 0 || length == 1)
{
return startIndex;
}
int middle = startIndex + (length / 2);
if (_convertedKeyframes[middle].Cue.CueValue < time)
{
return FindClosestBeforeKeyFrame(middle, length - middle);
}
else if (_convertedKeyframes[middle].Cue.CueValue > time)
{
return FindClosestBeforeKeyFrame(startIndex, middle - startIndex);
}
else
{
return middle;
}
}
return FindClosestBeforeKeyFrame(0, _convertedKeyframes.Count);
}
/// <summary> /// <summary>
/// Runs the KeyFrames Animation. /// Runs the KeyFrames Animation.
/// </summary> /// </summary>
private IDisposable RunKeyFrames(Animation animation, Animatable control, Action onComplete) internal IDisposable Run(Animation animation, Animatable control, IClock clock, Action onComplete)
{ {
var stateMachine = new AnimatorStateMachine<T>(animation, control, this, onComplete); var instance = new AnimationInstance<T>(
animation,
Timing.AnimationStateTimer control,
.TakeWhile(_ => !stateMachine._unsubscribe) this,
.Subscribe(p => stateMachine.Step(p, DoInterpolation)); clock ?? control.Clock ?? Clock.GlobalClock,
onComplete,
return control.Bind(Property, stateMachine, BindingPriority.Animation); DoInterpolation);
return control.Bind<T>((AvaloniaProperty<T>)Property, instance, BindingPriority.Animation);
} }
/// <summary> /// <summary>
@ -114,18 +139,18 @@ namespace Avalonia.Animation
protected abstract T DoInterpolation(double time, T neutralValue); protected abstract T DoInterpolation(double time, T neutralValue);
/// <summary> /// <summary>
/// Verifies and converts keyframe values according to this class's target type. /// Verifies, converts and sorts keyframe values according to this class's target type.
/// </summary> /// </summary>
private void VerifyConvertKeyFrames() private void VerifyConvertKeyFrames()
{ {
foreach (AnimatorKeyFrame keyframe in this) foreach (AnimatorKeyFrame keyframe in this)
{ {
_convertedKeyframes.Add(keyframe.Cue.CueValue, (keyframe, false)); _convertedKeyframes.Add(keyframe);
} }
AddNeutralKeyFramesIfNeeded(); AddNeutralKeyFramesIfNeeded();
_isVerfifiedAndConverted = true;
_isVerifiedAndConverted = true;
} }
private void AddNeutralKeyFramesIfNeeded() private void AddNeutralKeyFramesIfNeeded()
@ -133,14 +158,14 @@ namespace Avalonia.Animation
bool hasStartKey, hasEndKey; bool hasStartKey, hasEndKey;
hasStartKey = hasEndKey = false; hasStartKey = hasEndKey = false;
// Make start and end keyframe mandatory. // Check if there's start and end keyframes.
foreach (var converted in _convertedKeyframes.Keys) foreach (var frame in _convertedKeyframes)
{ {
if (DoubleUtils.AboutEqual(converted, 0.0)) if (frame.Cue.CueValue == 0.0d)
{ {
hasStartKey = true; hasStartKey = true;
} }
else if (DoubleUtils.AboutEqual(converted, 1.0)) else if (frame.Cue.CueValue == 1.0d)
{ {
hasEndKey = true; hasEndKey = true;
} }
@ -154,13 +179,13 @@ namespace Avalonia.Animation
{ {
if (!hasStartKey) if (!hasStartKey)
{ {
_convertedKeyframes.Add(0.0d, (new AnimatorKeyFrame { Value = default(T) }, true)); _convertedKeyframes.Insert(0, new AnimatorKeyFrame(null, new Cue(0.0d)) { Value = default(T), isNeutral = true });
} }
if (!hasEndKey) if (!hasEndKey)
{ {
_convertedKeyframes.Add(1.0d, (new AnimatorKeyFrame { Value = default(T) }, true)); _convertedKeyframes.Add(new AnimatorKeyFrame(null, new Cue(1.0d)) { Value = default(T), isNeutral = true });
} }
} }
} }
} }

30
src/Avalonia.Animation/Clock.cs

@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Reactive.Linq;
using System.Text;
using Avalonia.Reactive;
namespace Avalonia.Animation
{
public class Clock : ClockBase
{
public static IClock GlobalClock => AvaloniaLocator.Current.GetService<IGlobalClock>();
private IDisposable _parentSubscription;
public Clock()
:this(GlobalClock)
{
}
public Clock(IClock parent)
{
_parentSubscription = parent.Subscribe(Pulse);
}
protected override void Stop()
{
_parentSubscription?.Dispose();
}
}
}

72
src/Avalonia.Animation/ClockBase.cs

@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.Reactive.Linq;
using System.Text;
using Avalonia.Reactive;
namespace Avalonia.Animation
{
public class ClockBase : IClock
{
private ClockObservable _observable;
private IObservable<TimeSpan> _connectedObservable;
private TimeSpan? _previousTime;
private TimeSpan _internalTime;
protected ClockBase()
{
_observable = new ClockObservable();
_connectedObservable = _observable.Publish().RefCount();
}
protected bool HasSubscriptions => _observable.HasSubscriptions;
public PlayState PlayState { get; set; }
protected void Pulse(TimeSpan systemTime)
{
if (!_previousTime.HasValue)
{
_previousTime = systemTime;
_internalTime = TimeSpan.Zero;
}
else
{
if (PlayState == PlayState.Pause)
{
_previousTime = systemTime;
return;
}
var delta = systemTime - _previousTime;
_internalTime += delta.Value;
_previousTime = systemTime;
}
_observable.Pulse(_internalTime);
if (PlayState == PlayState.Stop)
{
Stop();
}
}
protected virtual void Stop()
{
}
public IDisposable Subscribe(IObserver<TimeSpan> observer)
{
return _connectedObservable.Subscribe(observer);
}
private class ClockObservable : LightweightObservableBase<TimeSpan>
{
public bool HasSubscriptions { get; private set; }
public void Pulse(TimeSpan time) => PublishNext(time);
protected override void Initialize() => HasSubscriptions = true;
protected override void Deinitialize() => HasSubscriptions = false;
}
}
}

7
src/Avalonia.Animation/Cue.cs

@ -1,13 +1,11 @@
using System; using System;
using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Globalization; using System.Globalization;
using System.Text;
namespace Avalonia.Animation namespace Avalonia.Animation
{ {
/// <summary> /// <summary>
/// A Cue object for <see cref="KeyFrame"/>. /// Determines the time index for a <see cref="KeyFrame"/>.
/// </summary> /// </summary>
[TypeConverter(typeof(CueTypeConverter))] [TypeConverter(typeof(CueTypeConverter))]
public readonly struct Cue : IEquatable<Cue>, IEquatable<double> public readonly struct Cue : IEquatable<Cue>, IEquatable<double>
@ -84,5 +82,4 @@ namespace Avalonia.Animation
return Cue.Parse((string)value, culture); return Cue.Parse((string)value, culture);
} }
} }
}
}

65
src/Avalonia.Animation/DisposeAnimationInstanceSubject.cs

@ -0,0 +1,65 @@
// 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 System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using Avalonia.Animation.Utils;
using Avalonia.Collections;
using Avalonia.Data;
using Avalonia.Reactive;
namespace Avalonia.Animation
{
/// <summary>
/// Manages the lifetime of animation instances as determined by its selector state.
/// </summary>
internal class DisposeAnimationInstanceSubject<T> : IObserver<bool>, IDisposable
{
private IDisposable _lastInstance;
private bool _lastMatch;
private Animator<T> _animator;
private Animation _animation;
private Animatable _control;
private Action _onComplete;
private IClock _clock;
public DisposeAnimationInstanceSubject(Animator<T> animator, Animation animation, Animatable control, IClock clock, Action onComplete)
{
this._animator = animator;
this._animation = animation;
this._control = control;
this._onComplete = onComplete;
this._clock = clock;
}
public void Dispose()
{
_lastInstance?.Dispose();
}
public void OnCompleted()
{
}
public void OnError(Exception error)
{
_lastInstance?.Dispose();
}
void IObserver<bool>.OnNext(bool matchVal)
{
if (matchVal != _lastMatch)
{
_lastInstance?.Dispose();
if (matchVal)
{
_lastInstance = _animator.Run(_animation, _control, _clock, _onComplete);
}
_lastMatch = matchVal;
}
}
}
}

10
src/Avalonia.Animation/DoubleAnimator.cs

@ -1,11 +1,5 @@
using System; // Copyright (c) The Avalonia Project. All rights reserved.
using System.Collections.Generic; // Licensed under the MIT license. See licence.md file in the project root for full license information.
using System.Text;
using System.Linq;
using System.Reactive.Linq;
using System.Diagnostics;
using Avalonia.Animation.Utils;
using Avalonia.Data;
namespace Avalonia.Animation namespace Avalonia.Animation
{ {

1
src/Avalonia.Animation/DoubleTransition.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 Avalonia.Metadata;
using System; using System;
using System.Reactive.Linq; using System.Reactive.Linq;

1
src/Avalonia.Animation/Easing/BounceEaseIn.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 Avalonia.Animation.Utils; using Avalonia.Animation.Utils;
namespace Avalonia.Animation.Easings namespace Avalonia.Animation.Easings

5
src/Avalonia.Animation/Easing/Easing.cs

@ -1,10 +1,7 @@
using Avalonia.Collections;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Linq;
using System.ComponentModel; using System.ComponentModel;
using System.Linq;
namespace Avalonia.Animation.Easings namespace Avalonia.Animation.Easings
{ {

3
src/Avalonia.Animation/Easing/EasingTypeConverter.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 Avalonia.Animation.Easings;
using System; using System;
using System.ComponentModel; using System.ComponentModel;
using System.Globalization; using System.Globalization;
@ -20,4 +19,4 @@ namespace Avalonia.Animation.Easings
return Easing.Parse((string)value); return Easing.Parse((string)value);
} }
} }
} }

2
src/Avalonia.Animation/Easing/ElasticEaseIn.cs

@ -1,8 +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.Animation.Utils;
using System; using System;
using Avalonia.Animation.Utils;
namespace Avalonia.Animation.Easings namespace Avalonia.Animation.Easings
{ {

2
src/Avalonia.Animation/Easing/ElasticEaseOut.cs

@ -1,8 +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.Animation.Utils;
using System; using System;
using Avalonia.Animation.Utils;
namespace Avalonia.Animation.Easings namespace Avalonia.Animation.Easings
{ {

2
src/Avalonia.Animation/Easing/SineEaseIn.cs

@ -1,8 +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.Animation.Utils;
using System; using System;
using Avalonia.Animation.Utils;
namespace Avalonia.Animation.Easings namespace Avalonia.Animation.Easings
{ {

2
src/Avalonia.Animation/Easing/SineEaseOut.cs

@ -1,8 +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.Animation.Utils;
using System; using System;
using Avalonia.Animation.Utils;
namespace Avalonia.Animation.Easings namespace Avalonia.Animation.Easings
{ {

5
src/Avalonia.Animation/FillMode.cs

@ -1,6 +1,5 @@
using System; // Copyright (c) The Avalonia Project. All rights reserved.
using System.Collections.Generic; // Licensed under the MIT license. See licence.md file in the project root for full license information.
using System.Text;
namespace Avalonia.Animation namespace Avalonia.Animation
{ {

1
src/Avalonia.Animation/FloatTransition.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 Avalonia.Metadata;
using System; using System;
using System.Reactive.Linq; using System.Reactive.Linq;

13
src/Avalonia.Animation/IAnimation.cs

@ -1,6 +1,7 @@
// 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 System; using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Avalonia.Animation namespace Avalonia.Animation
@ -11,13 +12,13 @@ namespace Avalonia.Animation
public interface IAnimation public interface IAnimation
{ {
/// <summary> /// <summary>
/// Apply the animation to the specified control /// Apply the animation to the specified control and run it when <paramref name="match" /> produces <c>true</c>.
/// </summary> /// </summary>
IDisposable Apply(Animatable control, IObservable<bool> match, Action onComplete = null); IDisposable Apply(Animatable control, IClock clock, IObservable<bool> match, Action onComplete = null);
/// <summary> /// <summary>
/// Run the animation to the specified control /// Run the animation on the specified control.
/// </summary> /// </summary>
Task RunAsync(Animatable control); Task RunAsync(Animatable control, IClock clock);
} }
} }

3
src/Avalonia.Animation/IAnimationSetter.cs

@ -1,3 +1,6 @@
// 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.Animation namespace Avalonia.Animation
{ {
public interface IAnimationSetter public interface IAnimationSetter

8
src/Avalonia.Animation/IAnimator.cs

@ -1,6 +1,8 @@
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 System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text;
namespace Avalonia.Animation namespace Avalonia.Animation
{ {
@ -17,6 +19,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, Action onComplete); IDisposable Apply(Animation animation, Animatable control, IClock clock, IObservable<bool> match, Action onComplete);
} }
} }

11
src/Avalonia.Animation/IClock.cs

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Avalonia.Animation
{
public interface IClock : IObservable<TimeSpan>
{
PlayState PlayState { get; set; }
}
}

10
src/Avalonia.Animation/IGlobalClock.cs

@ -0,0 +1,10 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Avalonia.Animation
{
public interface IGlobalClock : IClock
{
}
}

4
src/Avalonia.Animation/ITransition.cs

@ -1,9 +1,7 @@
// 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.Metadata;
using System; using System;
using System.Reactive.Linq;
namespace Avalonia.Animation namespace Avalonia.Animation
{ {
@ -15,7 +13,7 @@ namespace Avalonia.Animation
/// <summary> /// <summary>
/// Applies the transition to the specified <see cref="Animatable"/>. /// Applies the transition to the specified <see cref="Animatable"/>.
/// </summary> /// </summary>
IDisposable Apply(Animatable control, object oldValue, object newValue); IDisposable Apply(Animatable control, IClock clock, object oldValue, object newValue);
/// <summary> /// <summary>
/// Gets the property to be animated. /// Gets the property to be animated.

1
src/Avalonia.Animation/IntegerTransition.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 Avalonia.Metadata;
using System; using System;
using System.Reactive.Linq; using System.Reactive.Linq;

8
src/Avalonia.Animation/KeyFrame.cs

@ -1,8 +1,8 @@
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 System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using Avalonia.Metadata;
using Avalonia.Collections; using Avalonia.Collections;
namespace Avalonia.Animation namespace Avalonia.Animation

12
src/Avalonia.Animation/KeyFramePair`1.cs

@ -1,13 +1,5 @@
using System; // Copyright (c) The Avalonia Project. All rights reserved.
using System.Collections.Generic; // Licensed under the MIT license. See licence.md file in the project root for full license information.
using System.Text;
using Avalonia.Collections;
using System.ComponentModel;
using Avalonia.Animation.Utils;
using System.Reactive.Linq;
using System.Linq;
using Avalonia.Data;
using System.Reactive.Disposables;
namespace Avalonia.Animation namespace Avalonia.Animation
{ {

7
src/Avalonia.Animation/PlayState.cs

@ -1,6 +1,5 @@
using System; // Copyright (c) The Avalonia Project. All rights reserved.
using System.Collections.Generic; // Licensed under the MIT license. See licence.md file in the project root for full license information.
using System.Text;
namespace Avalonia.Animation namespace Avalonia.Animation
{ {
@ -24,4 +23,4 @@ namespace Avalonia.Animation
/// </summary> /// </summary>
Stop Stop
} }
} }

5
src/Avalonia.Animation/PlaybackDirection.cs

@ -1,6 +1,5 @@
using System; // Copyright (c) The Avalonia Project. All rights reserved.
using System.Collections.Generic; // Licensed under the MIT license. See licence.md file in the project root for full license information.
using System.Text;
namespace Avalonia.Animation namespace Avalonia.Animation
{ {

5
src/Avalonia.Animation/RepeatCount.cs

@ -1,12 +1,9 @@
// 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.Utilities;
using System; using System;
using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Globalization; using System.Globalization;
using System.Linq;
namespace Avalonia.Animation namespace Avalonia.Animation
{ {
@ -199,4 +196,4 @@ namespace Avalonia.Animation
} }
} }
} }
} }

3
src/Avalonia.Animation/RepeatCountTypeConverter.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 Avalonia.Animation.Easings;
using System; using System;
using System.ComponentModel; using System.ComponentModel;
using System.Globalization; using System.Globalization;
@ -20,4 +19,4 @@ namespace Avalonia.Animation
return RepeatCount.Parse((string)value); return RepeatCount.Parse((string)value);
} }
} }
} }

123
src/Avalonia.Animation/Timing.cs

@ -1,123 +0,0 @@
// 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 System;
using System.Diagnostics;
using System.Linq;
using System.Reactive.Linq;
using Avalonia.Data;
using Avalonia.Threading;
namespace Avalonia.Animation
{
/// <summary>
/// Provides global timing functions for animations.
/// </summary>
public static class Timing
{
static ulong _transitionsFrameCount;
static PlayState _globalState = PlayState.Run;
/// <summary>
/// The number of frames per second.
/// </summary>
public const int FramesPerSecond = 60;
/// <summary>
/// The time span of each frame.
/// </summary>
internal static readonly TimeSpan FrameTick = TimeSpan.FromSeconds(1.0 / FramesPerSecond);
/// <summary>
/// Initializes static members of the <see cref="Timing"/> class.
/// </summary>
static Timing()
{
var globalTimer = Observable.Interval(FrameTick, AvaloniaScheduler.Instance);
AnimationStateTimer = globalTimer
.Select(_ =>
{
return _globalState;
})
.Publish()
.RefCount();
TransitionsTimer = globalTimer
.Select(p => _transitionsFrameCount++)
.Publish()
.RefCount();
}
/// <summary>
/// Sets the animation play state for all animations
/// </summary>
public static void SetGlobalPlayState(PlayState playState)
{
Dispatcher.UIThread.VerifyAccess();
_globalState = playState;
}
/// <summary>
/// Gets the animation play state for all animations
/// </summary>
public static PlayState GetGlobalPlayState()
{
Dispatcher.UIThread.VerifyAccess();
return _globalState;
}
/// <summary>
/// Gets the animation timer.
/// </summary>
/// <remarks>
/// The animation timer triggers usually at 60 times per second or as
/// defined in <see cref="FramesPerSecond"/>.
/// The parameter passed to a subsciber is the current playstate of the animation.
/// </remarks>
internal static IObservable<PlayState> AnimationStateTimer
{
get;
}
/// <summary>
/// Gets the transitions timer.
/// </summary>
/// <remarks>
/// The transitions timer increments usually 60 times per second as
/// defined in <see cref="FramesPerSecond"/>.
/// The parameter passed to a subsciber is the number of frames since the animation system was
/// initialized.
/// </remarks>
public static IObservable<ulong> TransitionsTimer
{
get;
}
/// <summary>
/// Gets a timer that fires every frame for the specified duration with delay.
/// </summary>
/// <returns>
/// An observable that notifies the subscriber of the progress along the transition.
/// </returns>
/// <remarks>
/// The parameter passed to the subscriber is the progress along the transition, with
/// 0 being the start and 1 being the end. The observable is guaranteed to fire 0
/// immediately on subscribe and 1 at the end of the duration.
/// </remarks>
public static IObservable<double> GetTransitionsTimer(Animatable control, TimeSpan duration, TimeSpan delay = default(TimeSpan))
{
var startTime = _transitionsFrameCount;
var _duration = (ulong)(duration.Ticks / FrameTick.Ticks);
var endTime = startTime + _duration;
return TransitionsTimer
.TakeWhile(x => x < endTime)
.Select(x => (double)(x - startTime) / _duration)
.StartWith(0.0)
.Concat(Observable.Return(1.0));
}
}
}

55
src/Avalonia.Animation/TransitionInstance.cs

@ -0,0 +1,55 @@
// 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.Metadata;
using System;
using System.Reactive.Linq;
using Avalonia.Animation.Easings;
using Avalonia.Animation.Utils;
using Avalonia.Reactive;
namespace Avalonia.Animation
{
/// <summary>
/// Handles the timing and lifetime of a <see cref="Transition{T}"/>.
/// </summary>
internal class TransitionInstance : SingleSubscriberObservableBase<double>
{
private IDisposable _timerSubscription;
private TimeSpan _duration;
private readonly IClock _baseClock;
private IClock _clock;
public TransitionInstance(IClock clock, TimeSpan Duration)
{
_duration = Duration;
_baseClock = clock;
}
private void TimerTick(TimeSpan t)
{
var interpVal = (double)t.Ticks / _duration.Ticks;
if (interpVal > 1d || interpVal < 0d)
{
PublishCompleted();
return;
}
PublishNext(interpVal);
}
protected override void Unsubscribed()
{
_timerSubscription?.Dispose();
_clock.PlayState = PlayState.Stop;
}
protected override void Subscribed()
{
_clock = new Clock(_baseClock);
_timerSubscription = _clock.Subscribe(TimerTick);
PublishNext(0.0d);
}
}
}

23
src/Avalonia.Animation/Transition`1.cs

@ -1,10 +1,10 @@
// 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.Metadata;
using System; using System;
using System.Reactive.Linq; using System.Reactive.Linq;
using Avalonia.Animation.Easings; using Avalonia.Animation.Easings;
using Avalonia.Animation.Utils;
namespace Avalonia.Animation namespace Avalonia.Animation
{ {
@ -14,7 +14,6 @@ namespace Avalonia.Animation
public abstract class Transition<T> : AvaloniaObject, ITransition public abstract class Transition<T> : AvaloniaObject, ITransition
{ {
private AvaloniaProperty _prop; private AvaloniaProperty _prop;
private Easing _easing;
/// <summary> /// <summary>
/// Gets the duration of the animation. /// Gets the duration of the animation.
@ -24,17 +23,7 @@ namespace Avalonia.Animation
/// <summary> /// <summary>
/// Gets the easing class to be used. /// Gets the easing class to be used.
/// </summary> /// </summary>
public Easing Easing public Easing Easing { get; set; } = new LinearEasing();
{
get
{
return _easing ?? (_easing = new LinearEasing());
}
set
{
_easing = value;
}
}
/// <inheritdocs/> /// <inheritdocs/>
public AvaloniaProperty Property public AvaloniaProperty Property
@ -59,10 +48,10 @@ namespace Avalonia.Animation
public abstract IObservable<T> DoTransition(IObservable<double> progress, T oldValue, T newValue); public abstract IObservable<T> DoTransition(IObservable<double> progress, T oldValue, T newValue);
/// <inheritdocs/> /// <inheritdocs/>
public virtual IDisposable Apply(Animatable control, object oldValue, object newValue) public virtual IDisposable Apply(Animatable control, IClock clock, object oldValue, object newValue)
{ {
var transition = DoTransition(Timing.GetTransitionsTimer(control, Duration, TimeSpan.Zero), (T)oldValue, (T)newValue).Select(p => (object)p); var transition = DoTransition(new TransitionInstance(clock, Duration), (T)oldValue, (T)newValue);
return control.Bind(Property, transition, Data.BindingPriority.Animation); return control.Bind<T>((AvaloniaProperty<T>)Property, transition, Data.BindingPriority.Animation);
} }
} }
} }

18
src/Avalonia.Animation/Utils/DoubleUtils.cs

@ -1,18 +0,0 @@
// 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 System;
using System.Collections.Generic;
using System.Text;
namespace Avalonia.Animation.Utils
{
internal static class DoubleUtils
{
internal static bool AboutEqual(double x, double y)
{
double epsilon = Math.Max(Math.Abs(x), Math.Abs(y)) * 1E-15;
return Math.Abs(x - y) <= epsilon;
}
}
}

2
src/Avalonia.Animation/Utils/EasingUtils.cs

@ -13,6 +13,6 @@ namespace Avalonia.Animation.Utils
/// <summary> /// <summary>
/// Half of <see cref="Math.PI"/> /// Half of <see cref="Math.PI"/>
/// </summary> /// </summary>
internal static double HALFPI = Math.PI / 2d; internal const double HALFPI = Math.PI / 2d;
} }
} }

1
src/Avalonia.Base/AvaloniaLocator.cs

@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reactive.Disposables;
#pragma warning disable CS1591 // Enable me later #pragma warning disable CS1591 // Enable me later

2
src/Avalonia.Base/AvaloniaObject.cs

@ -5,12 +5,10 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Reactive.Disposables;
using System.Reactive.Linq; using System.Reactive.Linq;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Diagnostics; using Avalonia.Diagnostics;
using Avalonia.Logging; using Avalonia.Logging;
using Avalonia.Reactive;
using Avalonia.Threading; using Avalonia.Threading;
using Avalonia.Utilities; using Avalonia.Utilities;

4
src/Avalonia.Base/AvaloniaProperty.cs

@ -231,7 +231,7 @@ namespace Avalonia
} }
/// <summary> /// <summary>
/// Tests two <see cref="AvaloniaProperty"/>s for unequality. /// Tests two <see cref="AvaloniaProperty"/>s for inequality.
/// </summary> /// </summary>
/// <param name="a">The first property.</param> /// <param name="a">The first property.</param>
/// <param name="b">The second property.</param> /// <param name="b">The second property.</param>
@ -558,4 +558,4 @@ namespace Avalonia
public override string ToString() => "(unset)"; public override string ToString() => "(unset)";
} }
} }
} }

3
src/Avalonia.Base/Collections/AvaloniaList.cs

@ -8,7 +8,6 @@ using System.Collections.Specialized;
using System.ComponentModel; using System.ComponentModel;
using System.Linq; using System.Linq;
using Avalonia.Diagnostics; using Avalonia.Diagnostics;
using Avalonia.Platform;
namespace Avalonia.Collections namespace Avalonia.Collections
{ {
@ -551,4 +550,4 @@ namespace Avalonia.Collections
NotifyCountChanged(); NotifyCountChanged();
} }
} }
} }

2
src/Avalonia.Base/Collections/AvaloniaListConverter.cs

@ -40,4 +40,4 @@ namespace Avalonia.Collections
return result; return result;
} }
} }
} }

1
src/Avalonia.Base/Collections/AvaloniaListExtensions.cs

@ -6,7 +6,6 @@ using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.ComponentModel; using System.ComponentModel;
using System.Linq;
using System.Reactive.Disposables; using System.Reactive.Disposables;
namespace Avalonia.Collections namespace Avalonia.Collections

4
src/Avalonia.Base/Collections/IAvaloniaList.cs

@ -6,7 +6,7 @@ using System.Collections.Generic;
namespace Avalonia.Collections namespace Avalonia.Collections
{ {
/// <summary> /// <summary>
/// A notiftying list. /// A notifying list.
/// </summary> /// </summary>
/// <typeparam name="T">The type of the items in the list.</typeparam> /// <typeparam name="T">The type of the items in the list.</typeparam>
public interface IAvaloniaList<T> : IList<T>, IAvaloniaReadOnlyList<T> public interface IAvaloniaList<T> : IList<T>, IAvaloniaReadOnlyList<T>
@ -64,4 +64,4 @@ namespace Avalonia.Collections
/// <param name="count">The number of items to remove.</param> /// <param name="count">The number of items to remove.</param>
void RemoveRange(int index, int count); void RemoveRange(int index, int count);
} }
} }

4
src/Avalonia.Base/Collections/IAvaloniaReadOnlyList.cs

@ -8,10 +8,10 @@ using System.ComponentModel;
namespace Avalonia.Collections namespace Avalonia.Collections
{ {
/// <summary> /// <summary>
/// A read-only notiftying list. /// A read-only notifying list.
/// </summary> /// </summary>
/// <typeparam name="T">The type of the items in the list.</typeparam> /// <typeparam name="T">The type of the items in the list.</typeparam>
public interface IAvaloniaReadOnlyList<out T> : IReadOnlyList<T>, INotifyCollectionChanged, INotifyPropertyChanged public interface IAvaloniaReadOnlyList<out T> : IReadOnlyList<T>, INotifyCollectionChanged, INotifyPropertyChanged
{ {
} }
} }

4
src/Avalonia.Base/Collections/NotifyCollectionChangedExtensions.cs

@ -25,7 +25,7 @@ namespace Avalonia.Collections
} }
/// <summary> /// <summary>
/// Subcribes to the CollectionChanged event using a weak subscription. /// Subscribes to the CollectionChanged event using a weak subscription.
/// </summary> /// </summary>
/// <param name="collection">The collection.</param> /// <param name="collection">The collection.</param>
/// <param name="handler"> /// <param name="handler">
@ -44,7 +44,7 @@ namespace Avalonia.Collections
} }
/// <summary> /// <summary>
/// Subcribes to the CollectionChanged event using a weak subscription. /// Subscribes to the CollectionChanged event using a weak subscription.
/// </summary> /// </summary>
/// <param name="collection">The collection.</param> /// <param name="collection">The collection.</param>
/// <param name="handler"> /// <param name="handler">

10
src/Avalonia.Base/Data/BindingChainException.cs

@ -6,7 +6,7 @@ using System;
namespace Avalonia.Data namespace Avalonia.Data
{ {
/// <summary> /// <summary>
/// An exception returned through <see cref="BindingNotification"/> signalling that a /// An exception returned through <see cref="BindingNotification"/> signaling that a
/// requested binding expression could not be evaluated because of a null in one of the links /// requested binding expression could not be evaluated because of a null in one of the links
/// of the binding chain. /// of the binding chain.
/// </summary> /// </summary>
@ -15,14 +15,14 @@ namespace Avalonia.Data
private string _message; private string _message;
/// <summary> /// <summary>
/// Initalizes a new instance of the <see cref="BindingChainException"/> class. /// Initializes a new instance of the <see cref="BindingChainException"/> class.
/// </summary> /// </summary>
public BindingChainException() public BindingChainException()
{ {
} }
/// <summary> /// <summary>
/// Initalizes a new instance of the <see cref="BindingChainException"/> class. /// Initializes a new instance of the <see cref="BindingChainException"/> class.
/// </summary> /// </summary>
/// <param name="message">The error message.</param> /// <param name="message">The error message.</param>
public BindingChainException(string message) public BindingChainException(string message)
@ -31,7 +31,7 @@ namespace Avalonia.Data
} }
/// <summary> /// <summary>
/// Initalizes a new instance of the <see cref="BindingChainException"/> class. /// Initializes a new instance of the <see cref="BindingChainException"/> class.
/// </summary> /// </summary>
/// <param name="message">The error message.</param> /// <param name="message">The error message.</param>
/// <param name="expression">The expression.</param> /// <param name="expression">The expression.</param>
@ -51,7 +51,7 @@ namespace Avalonia.Data
public string Expression { get; protected set; } public string Expression { get; protected set; }
/// <summary> /// <summary>
/// Gets the point in the expression at which the error occured. /// Gets the point in the expression at which the error occurred.
/// </summary> /// </summary>
public string ExpressionErrorPoint { get; protected set; } public string ExpressionErrorPoint { get; protected set; }

1
src/Avalonia.Base/Data/BindingNotification.cs

@ -2,7 +2,6 @@
// 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 System;
using Avalonia.Logging;
namespace Avalonia.Data namespace Avalonia.Data
{ {

2
src/Avalonia.Base/Data/BindingOperations.cs

@ -20,7 +20,7 @@ namespace Avalonia.Data
/// An optional anchor from which to locate required context. When binding to objects that /// An optional anchor from which to locate required context. When binding to objects that
/// are not in the logical tree, certain types of binding need an anchor into the tree in /// are not in the logical tree, certain types of binding need an anchor into the tree in
/// order to locate named controls or resources. The <paramref name="anchor"/> parameter /// order to locate named controls or resources. The <paramref name="anchor"/> parameter
/// can be used to provice this context. /// can be used to provide this context.
/// </param> /// </param>
/// <returns>An <see cref="IDisposable"/> which can be used to cancel the binding.</returns> /// <returns>An <see cref="IDisposable"/> which can be used to cancel the binding.</returns>
public static IDisposable Apply( public static IDisposable Apply(

6
src/Avalonia.Base/Data/Converters/AlwaysEnabledDelegateCommand.cs

@ -1,10 +1,8 @@
using Avalonia.Utilities; using System;
using System;
using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Reflection; using System.Reflection;
using System.Text;
using System.Windows.Input; using System.Windows.Input;
using Avalonia.Utilities;
namespace Avalonia.Data.Converters namespace Avalonia.Data.Converters
{ {

3
src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs

@ -3,9 +3,8 @@
using System; using System;
using System.Globalization; using System.Globalization;
using Avalonia.Data;
using Avalonia.Utilities;
using System.Windows.Input; using System.Windows.Input;
using Avalonia.Utilities;
namespace Avalonia.Data.Converters namespace Avalonia.Data.Converters
{ {

1
src/Avalonia.Base/Data/Converters/IValueConverter.cs

@ -3,7 +3,6 @@
using System; using System;
using System.Globalization; using System.Globalization;
using Avalonia.Data;
namespace Avalonia.Data.Converters namespace Avalonia.Data.Converters
{ {

3
src/Avalonia.Base/Data/Converters/StringConverters.cs

@ -1,9 +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 System.Globalization;
using Avalonia.Utilities;
namespace Avalonia.Data.Converters namespace Avalonia.Data.Converters
{ {

2
src/Avalonia.Base/Data/Core/AvaloniaPropertyAccessorNode.cs

@ -1,7 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Reactive.Linq; using System.Reactive.Linq;
using System.Text;
using Avalonia.Reactive; using Avalonia.Reactive;
namespace Avalonia.Data.Core namespace Avalonia.Data.Core

3
src/Avalonia.Base/Data/Core/EmptyExpressionNode.cs

@ -1,9 +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 System.Reactive.Linq;
namespace Avalonia.Data.Core namespace Avalonia.Data.Core
{ {
public class EmptyExpressionNode : ExpressionNode public class EmptyExpressionNode : ExpressionNode

1
src/Avalonia.Base/Data/Core/ExpressionObserver.cs

@ -6,7 +6,6 @@ using System.Collections.Generic;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Reactive; using System.Reactive;
using System.Reactive.Linq; using System.Reactive.Linq;
using Avalonia.Data;
using Avalonia.Data.Core.Parsers; using Avalonia.Data.Core.Parsers;
using Avalonia.Data.Core.Plugins; using Avalonia.Data.Core.Plugins;
using Avalonia.Reactive; using Avalonia.Reactive;

1
src/Avalonia.Base/Data/Core/ExpressionParseException.cs

@ -2,7 +2,6 @@
// 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 System;
using Avalonia.Data.Core.Parsers;
namespace Avalonia.Data.Core namespace Avalonia.Data.Core
{ {

6
src/Avalonia.Base/Data/Core/ITransformNode.cs

@ -1,8 +1,4 @@
using System; namespace Avalonia.Data.Core
using System.Collections.Generic;
using System.Text;
namespace Avalonia.Data.Core
{ {
interface ITransformNode interface ITransformNode
{ {

2
src/Avalonia.Base/Data/Core/IndexerExpressionNode.cs

@ -3,8 +3,6 @@ using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Reflection; using System.Reflection;
using System.Text;
using Avalonia.Data;
namespace Avalonia.Data.Core namespace Avalonia.Data.Core
{ {

4
src/Avalonia.Base/Data/Core/IndexerNodeBase.cs

@ -3,12 +3,8 @@ using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.ComponentModel; using System.ComponentModel;
using System.Globalization;
using System.Linq; using System.Linq;
using System.Reactive.Linq; using System.Reactive.Linq;
using System.Reflection;
using System.Text;
using Avalonia.Data;
using Avalonia.Utilities; using Avalonia.Utilities;
namespace Avalonia.Data.Core namespace Avalonia.Data.Core

1
src/Avalonia.Base/Data/Core/LogicalNotNode.cs

@ -3,7 +3,6 @@
using System; using System;
using System.Globalization; using System.Globalization;
using Avalonia.Data;
namespace Avalonia.Data.Core namespace Avalonia.Data.Core
{ {

5
src/Avalonia.Base/Data/Core/Parsers/ExpressionTreeParser.cs

@ -1,8 +1,5 @@
using System; using System.Linq;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Text;
namespace Avalonia.Data.Core.Parsers namespace Avalonia.Data.Core.Parsers
{ {

2
src/Avalonia.Base/Data/Core/Parsers/ExpressionVisitorNodeBuilder.cs

@ -1,10 +1,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Reflection; using System.Reflection;
using System.Text;
namespace Avalonia.Data.Core.Parsers namespace Avalonia.Data.Core.Parsers
{ {

2
src/Avalonia.Base/Data/Core/Plugins/AvaloniaPropertyAccessorPlugin.cs

@ -2,9 +2,7 @@
// 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 System;
using System.Linq;
using System.Reactive.Linq; using System.Reactive.Linq;
using Avalonia.Data;
namespace Avalonia.Data.Core.Plugins namespace Avalonia.Data.Core.Plugins
{ {

3
src/Avalonia.Base/Data/Core/Plugins/DataAnnotationsValidationPlugin.cs

@ -6,7 +6,6 @@ using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using Avalonia.Data;
namespace Avalonia.Data.Core.Plugins namespace Avalonia.Data.Core.Plugins
{ {
@ -31,7 +30,7 @@ namespace Avalonia.Data.Core.Plugins
return new Accessor(reference, name, inner); return new Accessor(reference, name, inner);
} }
private class Accessor : DataValidatiorBase private class Accessor : DataValidationBase
{ {
private ValidationContext _context; private ValidationContext _context;

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save