diff --git a/samples/ControlCatalog/App.xaml b/samples/ControlCatalog/App.xaml index 43971dec4f..95d515ec60 100644 --- a/samples/ControlCatalog/App.xaml +++ b/samples/ControlCatalog/App.xaml @@ -14,6 +14,11 @@ - + + + - \ No newline at end of file + diff --git a/samples/ControlCatalog/ControlCatalog.csproj b/samples/ControlCatalog/ControlCatalog.csproj index dea9b35e24..61f2443eb7 100644 --- a/samples/ControlCatalog/ControlCatalog.csproj +++ b/samples/ControlCatalog/ControlCatalog.csproj @@ -35,4 +35,4 @@ - \ No newline at end of file + diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml index 87cb5e9c5c..ec3bf799b4 100644 --- a/samples/ControlCatalog/MainView.xaml +++ b/samples/ControlCatalog/MainView.xaml @@ -20,8 +20,9 @@ + - + diff --git a/samples/ControlCatalog/Pages/CanvasPage.xaml b/samples/ControlCatalog/Pages/CanvasPage.xaml index 10a38895a2..d6c138a4f7 100644 --- a/samples/ControlCatalog/Pages/CanvasPage.xaml +++ b/samples/ControlCatalog/Pages/CanvasPage.xaml @@ -11,7 +11,8 @@ - + + diff --git a/samples/ControlCatalog/Pages/ListBoxPage.xaml b/samples/ControlCatalog/Pages/ListBoxPage.xaml new file mode 100644 index 0000000000..3dd8be91c2 --- /dev/null +++ b/samples/ControlCatalog/Pages/ListBoxPage.xaml @@ -0,0 +1,13 @@ + + + ListBox + Hosts a collection of ListBoxItem. + + + + + + diff --git a/samples/ControlCatalog/Pages/ListBoxPage.xaml.cs b/samples/ControlCatalog/Pages/ListBoxPage.xaml.cs new file mode 100644 index 0000000000..dbe6c74800 --- /dev/null +++ b/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); + } + + } +} diff --git a/samples/ControlCatalog/Pages/MenuPage.xaml b/samples/ControlCatalog/Pages/MenuPage.xaml index 296cfa8704..6eeb86a00f 100644 --- a/samples/ControlCatalog/Pages/MenuPage.xaml +++ b/samples/ControlCatalog/Pages/MenuPage.xaml @@ -7,29 +7,44 @@ Margin="0,16,0,0" HorizontalAlignment="Center" Spacing="16"> - - - - - - - + + Defined in XAML + + + + + + + + + + + + + + + + + + - - - - + + - - - - - - - - - - + + + + + Dyanamically generated + + + + + + - \ No newline at end of file + diff --git a/samples/ControlCatalog/Pages/MenuPage.xaml.cs b/samples/ControlCatalog/Pages/MenuPage.xaml.cs index d637c172e1..01add3e76e 100644 --- a/samples/ControlCatalog/Pages/MenuPage.xaml.cs +++ b/samples/ControlCatalog/Pages/MenuPage.xaml.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using Avalonia.Controls; using Avalonia.Markup.Xaml; @@ -8,6 +9,37 @@ namespace ControlCatalog.Pages public MenuPage() { this.InitializeComponent(); + DataContext = new[] + { + new MenuItemViewModel + { + Header = "_File", + Items = new[] + { + new MenuItemViewModel { Header = "_Open..." }, + new MenuItemViewModel { Header = "Save" }, + new MenuItemViewModel { Header = "-" }, + new MenuItemViewModel + { + Header = "Recent", + Items = new[] + { + new MenuItemViewModel { Header = "File1.txt" }, + new MenuItemViewModel { Header = "File2.txt" }, + } + }, + } + }, + new MenuItemViewModel + { + Header = "_Edit", + Items = new[] + { + new MenuItemViewModel { Header = "_Copy" }, + new MenuItemViewModel { Header = "_Paste" }, + } + } + }; } private void InitializeComponent() @@ -15,4 +47,10 @@ namespace ControlCatalog.Pages AvaloniaXamlLoader.Load(this); } } + + public class MenuItemViewModel + { + public string Header { get; set; } + public IList Items { get; set; } + } } diff --git a/samples/ControlCatalog/Pages/NumericUpDownPage.xaml b/samples/ControlCatalog/Pages/NumericUpDownPage.xaml index 305bcd177c..2bb6214b58 100644 --- a/samples/ControlCatalog/Pages/NumericUpDownPage.xaml +++ b/samples/ControlCatalog/Pages/NumericUpDownPage.xaml @@ -14,7 +14,7 @@ AllowSpin: - + ClipValueToMinMax: @@ -77,4 +77,4 @@ - \ No newline at end of file + diff --git a/samples/ControlCatalog/Pages/ScreenPage.cs b/samples/ControlCatalog/Pages/ScreenPage.cs index 34aa85b8aa..fd66185832 100644 --- a/samples/ControlCatalog/Pages/ScreenPage.cs +++ b/samples/ControlCatalog/Pages/ScreenPage.cs @@ -42,7 +42,11 @@ namespace ControlCatalog.Pages context.DrawRectangle(p, boundsRect); context.DrawRectangle(p, workingAreaRect); - FormattedText text = new FormattedText(); + FormattedText text = new FormattedText() + { + Typeface = Typeface.Default + }; + text.Text = $"Bounds: {screen.Bounds.Width}:{screen.Bounds.Height}"; 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)); } } -} \ No newline at end of file +} diff --git a/samples/ControlCatalog/SideBar.xaml b/samples/ControlCatalog/SideBar.xaml index 7d72d1821b..cc3c31d13a 100644 --- a/samples/ControlCatalog/SideBar.xaml +++ b/samples/ControlCatalog/SideBar.xaml @@ -36,7 +36,7 @@ - + diff --git a/samples/RenderDemo/SideBar.xaml b/samples/RenderDemo/SideBar.xaml index b5f8ccaf01..26da2cc556 100644 --- a/samples/RenderDemo/SideBar.xaml +++ b/samples/RenderDemo/SideBar.xaml @@ -37,7 +37,7 @@ - + diff --git a/samples/RenderDemo/ViewModels/AnimationsPageViewModel.cs b/samples/RenderDemo/ViewModels/AnimationsPageViewModel.cs index 626a3e7c77..f724baf3c6 100644 --- a/samples/RenderDemo/ViewModels/AnimationsPageViewModel.cs +++ b/samples/RenderDemo/ViewModels/AnimationsPageViewModel.cs @@ -10,21 +10,21 @@ namespace RenderDemo.ViewModels public AnimationsPageViewModel() { - ToggleGlobalPlayState = ReactiveCommand.Create(()=>TogglePlayState()); + ToggleGlobalPlayState = ReactiveCommand.Create(() => TogglePlayState()); } void TogglePlayState() { - switch (Timing.GetGlobalPlayState()) + switch (Animation.GlobalPlayState) { case PlayState.Run: PlayStateText = "Resume all animations"; - Timing.SetGlobalPlayState(PlayState.Pause); + Animation.GlobalPlayState = PlayState.Pause; break; case PlayState.Pause: PlayStateText = "Pause all animations"; - Timing.SetGlobalPlayState(PlayState.Run); + Animation.GlobalPlayState = PlayState.Run; break; } } @@ -36,5 +36,5 @@ namespace RenderDemo.ViewModels } public ReactiveCommand ToggleGlobalPlayState { get; } - } + } } diff --git a/samples/interop/Direct3DInteropSample/MainWindow.cs b/samples/interop/Direct3DInteropSample/MainWindow.cs index 19c31a3af1..1ac4b44a74 100644 --- a/samples/interop/Direct3DInteropSample/MainWindow.cs +++ b/samples/interop/Direct3DInteropSample/MainWindow.cs @@ -1,81 +1,83 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +// 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 Avalonia; using Avalonia.Controls; +using Avalonia.Direct2D1; using Avalonia.Direct2D1.Media; using Avalonia.Markup.Xaml; using Avalonia.Platform; using Avalonia.Rendering; + using SharpDX; using SharpDX.D3DCompiler; using SharpDX.Direct2D1; using SharpDX.Direct3D; using SharpDX.Direct3D11; using SharpDX.DXGI; -using SharpDX.WIC; -using SharpDX.Mathematics; + using AlphaMode = SharpDX.Direct2D1.AlphaMode; using Buffer = SharpDX.Direct3D11.Buffer; -using DeviceContext = SharpDX.Direct3D11.DeviceContext; -using Factory1 = SharpDX.DXGI.Factory1; +using DeviceContext = SharpDX.Direct2D1.DeviceContext; +using Factory2 = SharpDX.DXGI.Factory2; using InputElement = SharpDX.Direct3D11.InputElement; using Matrix = SharpDX.Matrix; using PixelFormat = SharpDX.Direct2D1.PixelFormat; +using Resource = SharpDX.Direct3D11.Resource; namespace Direct3DInteropSample { - class MainWindow : Window + public class MainWindow : Window { - private SharpDX.Direct3D11.Device _d3dDevice; - private SharpDX.DXGI.Device _dxgiDevice; - Texture2D backBuffer = null; - RenderTargetView renderView = null; - Texture2D depthBuffer = null; - DepthStencilView depthView = null; + Texture2D _backBuffer; + RenderTargetView _renderView; + Texture2D _depthBuffer; + DepthStencilView _depthView; private readonly SwapChain _swapChain; - private SwapChainDescription _desc; + private SwapChainDescription1 _desc; private Matrix _proj = Matrix.Identity; - private Matrix _view; + private readonly Matrix _view; private Buffer _contantBuffer; - private SharpDX.Direct2D1.Device _d2dDevice; - private SharpDX.Direct2D1.DeviceContext _d2dContext; - private RenderTarget _d2dRenderTarget; - private MainWindowViewModel _model; + private DeviceContext _deviceContext; + private readonly MainWindowViewModel _model; public MainWindow() { - _dxgiDevice = AvaloniaLocator.Current.GetService(); - _d3dDevice = _dxgiDevice.QueryInterface(); - _d2dDevice = AvaloniaLocator.Current.GetService(); DataContext = _model = new MainWindowViewModel(); - _desc = new SwapChainDescription() + + _desc = new SwapChainDescription1() { BufferCount = 1, - ModeDescription = - new ModeDescription((int)ClientSize.Width, (int)ClientSize.Height, - new Rational(60, 1), Format.R8G8B8A8_UNorm), - IsWindowed = true, - OutputHandle = PlatformImpl?.Handle.Handle ?? IntPtr.Zero, + Width = (int)ClientSize.Width, + Height = (int)ClientSize.Height, + Format = Format.R8G8B8A8_UNorm, SampleDescription = new SampleDescription(1, 0), SwapEffect = SwapEffect.Discard, Usage = Usage.RenderTargetOutput }; - _swapChain = new SwapChain(new Factory1(), _d3dDevice, _desc); + using (var factory = Direct2D1Platform.DxgiDevice.Adapter.GetParent()) + { + _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) }; CreateMesh(); + _view = Matrix.LookAtLH(new Vector3(0, 0, -5), new Vector3(0, 0, 0), Vector3.UnitY); + this.GetObservable(ClientSizeProperty).Subscribe(Resize); + Resize(ClientSize); + AvaloniaXamlLoader.Load(this); + Background = Avalonia.Media.Brushes.Transparent; } @@ -83,29 +85,32 @@ namespace Direct3DInteropSample protected override void HandlePaint(Rect rect) { var viewProj = Matrix.Multiply(_view, _proj); - var context = _d3dDevice.ImmediateContext; + var context = Direct2D1Platform.Direct3D11Device.ImmediateContext; + // Clear views - context.ClearDepthStencilView(depthView, DepthStencilClearFlags.Depth, 1.0f, 0); - context.ClearRenderTargetView(renderView, Color.White); + context.ClearDepthStencilView(_depthView, DepthStencilClearFlags.Depth, 1.0f, 0); + context.ClearRenderTargetView(_renderView, Color.White); // Update WorldViewProj Matrix - var worldViewProj = Matrix.RotationX((float) _model.RotationX) * Matrix.RotationY((float) _model.RotationY) * - Matrix.RotationZ((float) _model.RotationZ) - * Matrix.Scaling((float) _model.Zoom) * viewProj; + var worldViewProj = Matrix.RotationX((float)_model.RotationX) * Matrix.RotationY((float)_model.RotationY) + * Matrix.RotationZ((float)_model.RotationZ) + * Matrix.Scaling((float)_model.Zoom) + * viewProj; worldViewProj.Transpose(); context.UpdateSubresource(ref worldViewProj, _contantBuffer); // Draw the cube context.Draw(36, 0); base.HandlePaint(rect); + // Present! _swapChain.Present(0, PresentFlags.None); } - - void CreateMesh() + private void CreateMesh() { - var device = _d3dDevice; + var device = Direct2D1Platform.Direct3D11Device; + // Compile Vertex and Pixel shaders var vertexShaderByteCode = ShaderBytecode.CompileFromFile("MiniCube.fx", "VS", "vs_4_0"); var vertexShader = new VertexShader(device, vertexShaderByteCode); @@ -114,63 +119,72 @@ namespace Direct3DInteropSample var pixelShader = new PixelShader(device, pixelShaderByteCode); 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 - var layout = new InputLayout(device, signature, new[] - { - new InputElement("POSITION", 0, Format.R32G32B32A32_Float, 0, 0), - new InputElement("COLOR", 0, Format.R32G32B32A32_Float, 16, 0) - }); - - // Instantiate Vertex buiffer from vertex data - var vertices = Buffer.Create(device, BindFlags.VertexBuffer, new[] - { - 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(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, 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(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, 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(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), - }); + var layout = new InputLayout( + device, + signature, + inputElements); + + // Instantiate Vertex buffer from vertex data + var vertices = Buffer.Create( + device, + BindFlags.VertexBuffer, + new[] + { + 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(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, 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(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, 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(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), + }); // Create Constant Buffer _contantBuffer = new Buffer(device, Utilities.SizeOf(), ResourceUsage.Default, BindFlags.ConstantBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0); - var context = _d3dDevice.ImmediateContext; + var context = Direct2D1Platform.Direct3D11Device.ImmediateContext; // Prepare All the stages context.InputAssembler.InputLayout = layout; @@ -181,63 +195,73 @@ namespace Direct3DInteropSample context.PixelShader.Set(pixelShader); } - void Resize(Size size) + private void Resize(Size size) { - Utilities.Dispose(ref _d2dRenderTarget); - Utilities.Dispose(ref backBuffer); - Utilities.Dispose(ref renderView); - Utilities.Dispose(ref depthBuffer); - Utilities.Dispose(ref depthView); - var context = _d3dDevice.ImmediateContext; + Utilities.Dispose(ref _deviceContext); + Utilities.Dispose(ref _backBuffer); + Utilities.Dispose(ref _renderView); + Utilities.Dispose(ref _depthBuffer); + Utilities.Dispose(ref _depthView); + var context = Direct2D1Platform.Direct3D11Device.ImmediateContext; + // 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 - backBuffer = Texture2D.FromSwapChain(_swapChain, 0); + _backBuffer = Resource.FromSwapChain(_swapChain, 0); // Renderview on the backbuffer - renderView = new RenderTargetView(_d3dDevice, backBuffer); + _renderView = new RenderTargetView(Direct2D1Platform.Direct3D11Device, _backBuffer); // Create the depth buffer - depthBuffer = new Texture2D(_d3dDevice, new Texture2DDescription() - { - Format = Format.D32_Float_S8X24_UInt, - ArraySize = 1, - MipLevels = 1, - Width = (int)size.Width, - Height = (int)size.Height, - SampleDescription = new SampleDescription(1, 0), - Usage = ResourceUsage.Default, - BindFlags = BindFlags.DepthStencil, - CpuAccessFlags = CpuAccessFlags.None, - OptionFlags = ResourceOptionFlags.None - }); + _depthBuffer = new Texture2D( + Direct2D1Platform.Direct3D11Device, + new Texture2DDescription() + { + Format = Format.D32_Float_S8X24_UInt, + ArraySize = 1, + MipLevels = 1, + Width = (int)size.Width, + Height = (int)size.Height, + SampleDescription = new SampleDescription(1, 0), + Usage = ResourceUsage.Default, + BindFlags = BindFlags.DepthStencil, + CpuAccessFlags = CpuAccessFlags.None, + OptionFlags = ResourceOptionFlags.None + }); // Create the depth buffer view - depthView = new DepthStencilView(_d3dDevice, depthBuffer); + _depthView = new DepthStencilView(Direct2D1Platform.Direct3D11Device, _depthBuffer); // Setup targets and viewport for rendering 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 _proj = Matrix.PerspectiveFovLH((float)Math.PI / 4.0f, (float)(size.Width / size.Height), 0.1f, 100.0f); using (var dxgiBackBuffer = _swapChain.GetBackBuffer(0)) { - _d2dRenderTarget = new RenderTarget(AvaloniaLocator.Current.GetService() - , dxgiBackBuffer, new RenderTargetProperties + var renderTarget = new SharpDX.Direct2D1.RenderTarget( + Direct2D1Platform.Direct2D1Factory, + dxgiBackBuffer, + new RenderTargetProperties { DpiX = 96, DpiY = 96, Type = RenderTargetType.Default, - PixelFormat = new PixelFormat(Format.Unknown, AlphaMode.Premultiplied) + PixelFormat = new PixelFormat( + Format.Unknown, + AlphaMode.Premultiplied) }); - } + _deviceContext = renderTarget.QueryInterface(); + + renderTarget.Dispose(); + } } - class D3DRenderTarget: IRenderTarget + private class D3DRenderTarget : IRenderTarget { private readonly MainWindow _window; @@ -245,16 +269,14 @@ namespace Direct3DInteropSample { _window = window; } + public void Dispose() { - } public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer) { - return new DrawingContextImpl(visualBrushRenderer, null, _window._d2dRenderTarget, - AvaloniaLocator.Current.GetService(), - AvaloniaLocator.Current.GetService()); + return new DrawingContextImpl(visualBrushRenderer, null, _window._deviceContext); } } diff --git a/src/Android/Avalonia.Android/AndroidPlatform.cs b/src/Android/Avalonia.Android/AndroidPlatform.cs index 7134e7d1b4..2b46bfa492 100644 --- a/src/Android/Avalonia.Android/AndroidPlatform.cs +++ b/src/Android/Avalonia.Android/AndroidPlatform.cs @@ -1,8 +1,4 @@ using System; -using System.IO; -using System.Linq; -using Android.Content; -using Android.Views; using Avalonia.Android.Platform; using Avalonia.Android.Platform.Input; using Avalonia.Android.Platform.SkiaPlatform; @@ -79,4 +75,4 @@ namespace Avalonia.Android return new PopupImpl(); } } -} \ No newline at end of file +} diff --git a/src/Android/Avalonia.Android/AndroidThreadingInterface.cs b/src/Android/Avalonia.Android/AndroidThreadingInterface.cs index 77dfc60b83..6fe77adca1 100644 --- a/src/Android/Avalonia.Android/AndroidThreadingInterface.cs +++ b/src/Android/Avalonia.Android/AndroidThreadingInterface.cs @@ -1,16 +1,7 @@ using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; using System.Reactive.Disposables; -using System.Text; using System.Threading; -using Android.App; -using Android.Content; using Android.OS; -using Android.Runtime; -using Android.Views; -using Android.Widget; using Avalonia.Platform; using Avalonia.Threading; @@ -88,4 +79,3 @@ namespace Avalonia.Android public event Action Signaled; } } - \ No newline at end of file diff --git a/src/Android/Avalonia.Android/AppBuilder.cs b/src/Android/Avalonia.Android/AppBuilder.cs index 6078e3bb98..805bb61655 100644 --- a/src/Android/Avalonia.Android/AppBuilder.cs +++ b/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.Platform; using Avalonia.Shared.PlatformSupport; namespace Avalonia @@ -23,4 +11,4 @@ namespace Avalonia } } -} \ No newline at end of file +} diff --git a/src/Android/Avalonia.Android/AvaloniaActivity.cs b/src/Android/Avalonia.Android/AvaloniaActivity.cs index 21ce555086..d3696aa41d 100644 --- a/src/Android/Avalonia.Android/AvaloniaActivity.cs +++ b/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.Content; using Android.OS; -using Android.Runtime; using Android.Views; -using Android.Widget; namespace Avalonia.Android { @@ -48,4 +41,4 @@ namespace Avalonia.Android return View.DispatchKeyEvent(e); } } -} \ No newline at end of file +} diff --git a/src/Android/Avalonia.Android/AvaloniaView.cs b/src/Android/Avalonia.Android/AvaloniaView.cs index 6c4274a6e4..fbe027db00 100644 --- a/src/Android/Avalonia.Android/AvaloniaView.cs +++ b/src/Android/Avalonia.Android/AvaloniaView.cs @@ -1,12 +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.Android.Platform.SkiaPlatform; @@ -66,4 +59,4 @@ namespace Avalonia.Android public IDisposable ShowDialog() => null; } } -} \ No newline at end of file +} diff --git a/src/Android/Avalonia.Android/CursorFactory.cs b/src/Android/Avalonia.Android/CursorFactory.cs index e6b9433cca..9eb28c67f9 100644 --- a/src/Android/Avalonia.Android/CursorFactory.cs +++ b/src/Android/Avalonia.Android/CursorFactory.cs @@ -9,4 +9,4 @@ namespace Avalonia.Android public IPlatformHandle GetCursor(StandardCursorType cursorType) => new PlatformHandle(IntPtr.Zero, "ZeroCursor"); } -} \ No newline at end of file +} diff --git a/src/Android/Avalonia.Android/Platform/ClipboardImpl.cs b/src/Android/Avalonia.Android/Platform/ClipboardImpl.cs index 8c28b48019..51e0a1e799 100644 --- a/src/Android/Avalonia.Android/Platform/ClipboardImpl.cs +++ b/src/Android/Avalonia.Android/Platform/ClipboardImpl.cs @@ -1,9 +1,9 @@ +using System.Threading.Tasks; using Android.Content; using Android.Runtime; using Android.Views; using Avalonia.Input.Platform; using Avalonia.Platform; -using System.Threading.Tasks; namespace Avalonia.Android.Platform { @@ -44,4 +44,4 @@ namespace Avalonia.Android.Platform return Task.FromResult(null); } } -} \ No newline at end of file +} diff --git a/src/Android/Avalonia.Android/Platform/Input/AndroidKeyboardDevice.cs b/src/Android/Avalonia.Android/Platform/Input/AndroidKeyboardDevice.cs index d5494a69ef..726ccdbbdd 100644 --- a/src/Android/Avalonia.Android/Platform/Input/AndroidKeyboardDevice.cs +++ b/src/Android/Avalonia.Android/Platform/Input/AndroidKeyboardDevice.cs @@ -1,12 +1,11 @@ -using System; -using Avalonia.Input; //using Android.InputMethodServices; using System.Collections.Generic; using Android.Views; +using Avalonia.Input; namespace Avalonia.Android.Platform.Input { - public class AndroidKeyboardDevice : KeyboardDevice, IKeyboardDevice { + public class AndroidKeyboardDevice : KeyboardDevice, IKeyboardDevice { private static readonly Dictionary KeyDic = new Dictionary { // { Keycode.Cancel?, Key.Cancel }, diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/AndroidFramebuffer.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/AndroidFramebuffer.cs index 051a058363..405da12967 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/AndroidFramebuffer.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/AndroidFramebuffer.cs @@ -102,4 +102,4 @@ namespace Avalonia.Android.Platform.SkiaPlatform uint reserved6; } } -} \ No newline at end of file +} diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/InvalidationAwareSurfaceView.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/InvalidationAwareSurfaceView.cs index d2d5909815..02ea702236 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/InvalidationAwareSurfaceView.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/InvalidationAwareSurfaceView.cs @@ -1,17 +1,9 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -using Android.App; using Android.Content; using Android.Graphics; using Android.OS; -using Android.Runtime; using Android.Util; using Android.Views; -using Android.Widget; -using Avalonia.Media; using Avalonia.Platform; namespace Avalonia.Android @@ -90,4 +82,4 @@ namespace Avalonia.Android protected abstract void Draw(); public string HandleDescriptor => "SurfaceView"; } -} \ No newline at end of file +} diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/PopupImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/PopupImpl.cs index 041d91043a..d55755d6e3 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/PopupImpl.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/PopupImpl.cs @@ -1,15 +1,8 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -using Android.App; using Android.Content; using Android.Graphics; -using Android.OS; using Android.Runtime; using Android.Views; -using Android.Widget; using Avalonia.Controls; using Avalonia.Platform; @@ -116,4 +109,4 @@ namespace Avalonia.Android.Platform.SkiaPlatform //Not supported } } -} \ No newline at end of file +} diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs index 6c8db17c71..8e8886269c 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs +++ b/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.Graphics; using Android.Views; +using Avalonia.Android.Platform.Input; using Avalonia.Android.Platform.Specific; using Avalonia.Android.Platform.Specific.Helpers; +using Avalonia.Controls.Platform.Surfaces; using Avalonia.Input; using Avalonia.Input.Raw; 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; namespace Avalonia.Android.Platform.SkiaPlatform @@ -196,4 +193,4 @@ namespace Avalonia.Android.Platform.SkiaPlatform ILockedFramebuffer IFramebufferPlatformSurface.Lock()=>new AndroidFramebuffer(_view.Holder.Surface); } -} \ No newline at end of file +} diff --git a/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs b/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs index ab990066de..c7b01413a9 100644 --- a/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs +++ b/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs @@ -1,3 +1,5 @@ +using System; +using System.ComponentModel; using Android.Content; using Android.Runtime; using Android.Views; @@ -7,8 +9,6 @@ using Avalonia.Controls; using Avalonia.Input; using Avalonia.Input.Raw; using Avalonia.Platform; -using System; -using System.ComponentModel; namespace Avalonia.Android.Platform.Specific.Helpers { @@ -120,7 +120,7 @@ namespace Avalonia.Android.Platform.Specific.Helpers _lastFocusedElement = element; } - public void ActivateAutoShowKeybord() + public void ActivateAutoShowKeyboard() { var kbDevice = (KeyboardDevice.Instance as INotifyPropertyChanged); @@ -142,4 +142,4 @@ namespace Avalonia.Android.Platform.Specific.Helpers HandleEvents = false; } } -} \ No newline at end of file +} diff --git a/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidTouchEventsHelper.cs b/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidTouchEventsHelper.cs index 71822a6f47..112925ab0f 100644 --- a/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidTouchEventsHelper.cs +++ b/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidTouchEventsHelper.cs @@ -1,10 +1,8 @@ -using Android.Graphics; +using System; using Android.Views; using Avalonia.Input; using Avalonia.Input.Raw; -using Avalonia.Media; using Avalonia.Platform; -using System; namespace Avalonia.Android.Platform.Specific.Helpers { @@ -131,4 +129,4 @@ namespace Avalonia.Android.Platform.Specific.Helpers HandleEvents = false; } } -} \ No newline at end of file +} diff --git a/src/Android/Avalonia.Android/Platform/Specific/IAndroidView.cs b/src/Android/Avalonia.Android/Platform/Specific/IAndroidView.cs index 2e7816362d..c72de8e197 100644 --- a/src/Android/Avalonia.Android/Platform/Specific/IAndroidView.cs +++ b/src/Android/Avalonia.Android/Platform/Specific/IAndroidView.cs @@ -1,16 +1,9 @@ -using Android.Graphics; using Android.Views; -using System; -using System.Collections.Generic; namespace Avalonia.Android.Platform.Specific { public interface IAndroidView { - - View View { get; } } - - -} \ No newline at end of file +} diff --git a/src/Android/Avalonia.Android/PlatformIconLoader.cs b/src/Android/Avalonia.Android/PlatformIconLoader.cs index 289025ce1d..88677a9375 100644 --- a/src/Android/Avalonia.Android/PlatformIconLoader.cs +++ b/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 Avalonia.Platform; namespace Avalonia.Android { @@ -54,4 +43,4 @@ namespace Avalonia.Android stream.CopyTo(outputStream); } } -} \ No newline at end of file +} diff --git a/src/Android/Avalonia.Android/SystemDialogImpl.cs b/src/Android/Avalonia.Android/SystemDialogImpl.cs index bad45b78fb..a8d201d66e 100644 --- a/src/Android/Avalonia.Android/SystemDialogImpl.cs +++ b/src/Android/Avalonia.Android/SystemDialogImpl.cs @@ -18,4 +18,4 @@ namespace Avalonia.Android throw new NotImplementedException(); } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Animation/Animatable.cs b/src/Avalonia.Animation/Animatable.cs index a27d996301..8a1a17a6fc 100644 --- a/src/Avalonia.Animation/Animatable.cs +++ b/src/Avalonia.Animation/Animatable.cs @@ -1,48 +1,20 @@ // 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.Linq; -using Avalonia.Data; using System; +using System.Collections.Generic; +using System.Linq; using System.Reactive.Linq; using Avalonia.Collections; -using Avalonia.Animation; -using System.Collections.Generic; -using System.Threading; -using System.Collections.Concurrent; +using Avalonia.Data; namespace Avalonia.Animation { /// - /// Base class for control which can have property transitions. + /// Base class for all animatable objects. /// public class Animatable : AvaloniaObject - { - /// - /// Initializes this object. - /// - public Animatable() - { - Transitions = new Transitions(); - AnimatableTimer = Timing.AnimationStateTimer - .Select(p => - { - if (this._playState == PlayState.Pause) - { - return PlayState.Pause; - } - else return p; - }) - .Publish() - .RefCount(); - } - - /// - /// The specific animations timer for this control. - /// - /// - public IObservable AnimatableTimer; - + { /// /// Defines the property. /// @@ -62,27 +34,25 @@ namespace Avalonia.Animation { get { return _playState; } set { SetAndRaise(PlayStateProperty, ref _playState, value); } - } - /// /// Defines the property. /// - public static readonly DirectProperty> TransitionsProperty = - AvaloniaProperty.RegisterDirect>( + public static readonly DirectProperty TransitionsProperty = + AvaloniaProperty.RegisterDirect( nameof(Transitions), o => o.Transitions, (o, v) => o.Transitions = v); - private IEnumerable _transitions = new AvaloniaList(); + private Transitions _transitions; /// /// Gets or sets the property transitions for the control. /// - public IEnumerable Transitions + public Transitions Transitions { - get { return _transitions; } + get { return _transitions ?? (_transitions = new Transitions()); } set { SetAndRaise(TransitionsProperty, ref _transitions, value); } } @@ -103,6 +73,5 @@ namespace Avalonia.Animation } } } - } -} +} \ No newline at end of file diff --git a/src/Avalonia.Animation/Animation.cs b/src/Avalonia.Animation/Animation.cs index 3dc5b5c71a..2c359ecac3 100644 --- a/src/Avalonia.Animation/Animation.cs +++ b/src/Avalonia.Animation/Animation.cs @@ -1,18 +1,14 @@ // 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.Animation.Easings; -using Avalonia.Animation; -using Avalonia.Collections; -using Avalonia.Metadata; using System; using System.Collections.Generic; -using System.Collections.Specialized; -using System.Reflection; using System.Linq; -using System.Threading.Tasks; -using System.Reactive.Linq; using System.Reactive.Disposables; +using System.Reactive.Linq; +using System.Threading.Tasks; +using Avalonia.Animation.Easings; +using Avalonia.Collections; namespace Avalonia.Animation { @@ -21,6 +17,60 @@ namespace Avalonia.Animation /// public class Animation : AvaloniaList, IAnimation { + /// + /// Gets or sets the animation play state for all animations + /// + public static PlayState GlobalPlayState { get; set; } = PlayState.Run; + + /// + /// Gets or sets the active time of this animation. + /// + public TimeSpan Duration { get; set; } + + /// + /// Gets or sets the repeat count for this animation. + /// + public RepeatCount RepeatCount { get; set; } + + /// + /// Gets or sets the playback direction for this animation. + /// + public PlaybackDirection PlaybackDirection { get; set; } + + /// + /// Gets or sets the value fill mode for this animation. + /// + public FillMode FillMode { get; set; } + + /// + /// Gets or sets the easing function to be used for this animation. + /// + public Easing Easing { get; set; } = new LinearEasing(); + + /// + /// Gets or sets the speed multiple for this animation. + /// + public double SpeedRatio { get; set; } = 1d; + + /// + /// Gets or sets the delay time for this animation. + /// + /// + /// Describes a delay to be added before the animation starts, and optionally between + /// repeats of the animation if is set. + /// + public TimeSpan Delay { get; set; } + + /// + /// Gets or sets a value indicating whether will be applied between + /// iterations of the animation. + /// + /// + /// If this property is not set, then will only be applied to the first + /// iteration of the animation. + /// + public bool DelayBetweenIterations { get; set; } + private readonly static List<(Func Condition, Type Animator)> Animators = new List<(Func, Type)> { ( prop => typeof(double).IsAssignableFrom(prop.PropertyType), typeof(DoubleAnimator) ) @@ -44,38 +94,6 @@ namespace Avalonia.Animation return null; } - public AvaloniaList _animators { get; set; } = new AvaloniaList(); - - /// - /// Run time of this animation. - /// - public TimeSpan Duration { get; set; } - - /// - /// Delay time for this animation. - /// - public TimeSpan Delay { get; set; } - - /// - /// The repeat count for this animation. - /// - public RepeatCount RepeatCount { get; set; } - - /// - /// The playback direction for this animation. - /// - public PlaybackDirection PlaybackDirection { get; set; } - - /// - /// The value fill mode for this animation. - /// - public FillMode FillMode { get; set; } - - /// - /// Easing function to be used. - /// - public Easing Easing { get; set; } = new LinearEasing(); - private (IList Animators, IList subscriptions) InterpretKeyframes(Animatable control) { var handlerList = new List<(Type type, AvaloniaProperty property)>(); diff --git a/src/Avalonia.Animation/AnimationInstance`1.cs b/src/Avalonia.Animation/AnimationInstance`1.cs new file mode 100644 index 0000000000..5a72904ed2 --- /dev/null +++ b/src/Avalonia.Animation/AnimationInstance`1.cs @@ -0,0 +1,228 @@ +using System; +using System.Linq; +using System.Reactive.Linq; +using Avalonia.Animation.Utils; +using Avalonia.Data; +using Avalonia.Reactive; + +namespace Avalonia.Animation +{ + /// + /// Handles interpolatoin and time-related functions + /// for keyframe animations. + /// + internal class AnimationInstance : SingleSubscriberObservableBase + { + private T _lastInterpValue; + private T _firstKFValue; + private long _repeatCount; + private double _currentIteration; + private bool _isLooping; + private bool _gotFirstKFValue; + private bool _gotFirstFrameCount; + private bool _iterationDelay; + private FillMode _fillMode; + private PlaybackDirection _animationDirection; + private Animator _parent; + private Animatable _targetControl; + private T _neutralValue; + private double _speedRatio; + private TimeSpan _delay; + private TimeSpan _duration; + private TimeSpan _firstFrameCount; + private TimeSpan _internalClock; + private TimeSpan? _previousClock; + private Easings.Easing _easeFunc; + private Action _onCompleteAction; + private Func _interpolator; + private IDisposable _timerSubscription; + + public AnimationInstance(Animation animation, Animatable control, Animator animator, Action OnComplete, Func 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; + } + + protected override void Unsubscribed() + { + _timerSubscription?.Dispose(); + } + + protected override void Subscribed() + { + _timerSubscription = Timing.AnimationsTimer + .Subscribe(p => this.Step(p)); + } + + 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 DoPlayStatesAndTime(TimeSpan systemTime) + { + if (Animation.GlobalPlayState == PlayState.Stop || _targetControl.PlayState == PlayState.Stop) + DoComplete(); + + if (!_previousClock.HasValue) + { + _previousClock = systemTime; + _internalClock = TimeSpan.Zero; + } + else + { + if (Animation.GlobalPlayState == PlayState.Pause || _targetControl.PlayState == PlayState.Pause) + { + _previousClock = systemTime; + return; + } + var delta = systemTime - _previousClock; + _internalClock += delta.Value; + _previousClock = systemTime; + } + + if (!_gotFirstKFValue) + { + _firstKFValue = (T)_parent.First().Value; + _gotFirstKFValue = true; + } + + if (!_gotFirstFrameCount) + { + _firstFrameCount = _internalClock; + _gotFirstFrameCount = true; + } + } + + private void InternalStep(TimeSpan systemTime) + { + DoPlayStatesAndTime(systemTime); + + var time = _internalClock - _firstFrameCount; + 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)time.Ticks / iterationEndpoint.Ticks) + 2; + } + else + { + _previousClock = systemTime; + return; + } + + time = TimeSpan.FromTicks(time.Ticks % iterationEndpoint.Ticks); + + if (!_isLooping) + { + if (_currentIteration > _repeatCount) + DoComplete(); + + if (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); + } + } + } +} \ No newline at end of file diff --git a/src/Avalonia.Animation/AnimatorKeyFrame.cs b/src/Avalonia.Animation/AnimatorKeyFrame.cs index 0276c6fa92..09f259e754 100644 --- a/src/Avalonia.Animation/AnimatorKeyFrame.cs +++ b/src/Avalonia.Animation/AnimatorKeyFrame.cs @@ -1,9 +1,5 @@ using System; -using System.Collections.Generic; -using System.Text; using System.ComponentModel; -using Avalonia.Metadata; -using Avalonia.Collections; using Avalonia.Data; using Avalonia.Reactive; @@ -29,6 +25,7 @@ namespace Avalonia.Animation Cue = cue; } + internal bool isNeutral; public Type AnimatorType { get; } public Cue Cue { get; } public AvaloniaProperty Property { get; private set; } diff --git a/src/Avalonia.Animation/AnimatorStateMachine`1.cs b/src/Avalonia.Animation/AnimatorStateMachine`1.cs deleted file mode 100644 index 87e189c997..0000000000 --- a/src/Avalonia.Animation/AnimatorStateMachine`1.cs +++ /dev/null @@ -1,273 +0,0 @@ -using System; -using System.Linq; -using Avalonia.Data; - -namespace Avalonia.Animation -{ - /// - /// Provides statefulness for an iteration of a keyframe animation. - /// - internal class AnimatorStateMachine : IObservable, 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 _parent; - private Animation _targetAnimation; - private Animatable _targetControl; - private T _neutralValue; - internal bool _unsubscribe = false; - private IObserver _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 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 Interpolator) - { - try - { - InternalStep(_playState, Interpolator); - } - catch (Exception e) - { - _targetObserver?.OnError(e); - } - } - - private void InternalStep(PlayState _playState, Func 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 observer) - { - _targetObserver = observer; - return this; - } - - public void Dispose() - { - _unsubscribe = true; - _currentState = KeyFramesStates.Disposed; - } - } -} diff --git a/src/Avalonia.Animation/Animator`1.cs b/src/Avalonia.Animation/Animator`1.cs index eb8b40647d..f0ef55aa9e 100644 --- a/src/Avalonia.Animation/Animator`1.cs +++ b/src/Avalonia.Animation/Animator`1.cs @@ -1,13 +1,10 @@ using System; 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.Reactive.Linq; +using Avalonia.Animation.Utils; +using Avalonia.Collections; using Avalonia.Data; -using System.Reactive.Disposables; namespace Avalonia.Animation { @@ -19,9 +16,9 @@ namespace Avalonia.Animation /// /// List of type-converted keyframes. /// - private readonly SortedList _convertedKeyframes = new SortedList(); - - private bool _isVerfifiedAndConverted; + private readonly List _convertedKeyframes = new List(); + + private bool _isVerifiedAndConverted; /// /// Gets or sets the target property for the keyframe. @@ -31,18 +28,17 @@ namespace Avalonia.Animation public Animator() { // Invalidate keyframes when changed. - this.CollectionChanged += delegate { _isVerfifiedAndConverted = false; }; + this.CollectionChanged += delegate { _isVerifiedAndConverted = false; }; } /// - public virtual IDisposable Apply(Animation animation, Animatable control, IObservable obsMatch, Action onComplete) + public virtual IDisposable Apply(Animation animation, Animatable control, IObservable match, Action onComplete) { - if (!_isVerfifiedAndConverted) + if (!_isVerifiedAndConverted) VerifyConvertKeyFrames(); - return obsMatch - // Ignore triggers when global timers are paused. - .Where(p => p && Timing.GetGlobalPlayState() != PlayState.Pause) + return match + .Where(p => p) .Subscribe(_ => { var timerObs = RunKeyFrames(animation, control, onComplete); @@ -59,53 +55,56 @@ namespace Avalonia.Animation /// The time parameter, relative to the total animation time protected (double IntraKFTime, KeyFramePair KFPair) GetKFPairAndIntraKFTime(double t) { - KeyValuePair firstCue, lastCue; + AnimatorKeyFrame firstCue, lastCue ; int kvCount = _convertedKeyframes.Count; if (kvCount > 2) { - if (DoubleUtils.AboutEqual(t, 0.0) || t < 0.0) + if (t <= 0.0) { - firstCue = _convertedKeyframes.First(); - lastCue = _convertedKeyframes.Skip(1).First(); + firstCue = _convertedKeyframes[0]; + lastCue = _convertedKeyframes[1]; } - else if (DoubleUtils.AboutEqual(t, 1.0) || t > 1.0) + else if (t >= 1.0) { - firstCue = _convertedKeyframes.Skip(kvCount - 2).First(); - lastCue = _convertedKeyframes.Last(); + firstCue = _convertedKeyframes[_convertedKeyframes.Count - 2]; + lastCue = _convertedKeyframes[_convertedKeyframes.Count - 1]; } else { - firstCue = _convertedKeyframes.Last(j => j.Key <= t); - lastCue = _convertedKeyframes.First(j => j.Key >= t); + (double time, int index) maxval = (0.0d, 0); + for (int i = 0; i < _convertedKeyframes.Count; i++) + { + var comp = _convertedKeyframes[i].Cue.CueValue; + if (t >= comp) + { + maxval = (comp, i); + } + } + firstCue = _convertedKeyframes[maxval.index]; + lastCue = _convertedKeyframes[maxval.index + 1]; } } else { - firstCue = _convertedKeyframes.First(); - lastCue = _convertedKeyframes.Last(); + firstCue = _convertedKeyframes[0]; + lastCue = _convertedKeyframes[1]; } - double t0 = firstCue.Key; - double t1 = lastCue.Key; + double t0 = firstCue.Cue.CueValue; + double t1 = lastCue.Cue.CueValue; var intraframeTime = (t - t0) / (t1 - t0); - var firstFrameData = (firstCue.Value.frame.GetTypedValue(), firstCue.Value.isNeutral); - var lastFrameData = (lastCue.Value.frame.GetTypedValue(), lastCue.Value.isNeutral); + var firstFrameData = (firstCue.GetTypedValue(), firstCue.isNeutral); + var lastFrameData = (lastCue.GetTypedValue(), lastCue.isNeutral); return (intraframeTime, new KeyFramePair(firstFrameData, lastFrameData)); } - /// /// Runs the KeyFrames Animation. /// private IDisposable RunKeyFrames(Animation animation, Animatable control, Action onComplete) { - var stateMachine = new AnimatorStateMachine(animation, control, this, onComplete); - - Timing.AnimationStateTimer - .TakeWhile(_ => !stateMachine._unsubscribe) - .Subscribe(p => stateMachine.Step(p, DoInterpolation)); - - return control.Bind(Property, stateMachine, BindingPriority.Animation); + var instance = new AnimationInstance(animation, control, this, onComplete, DoInterpolation); + return control.Bind((AvaloniaProperty)Property, instance, BindingPriority.Animation); } /// @@ -114,18 +113,26 @@ namespace Avalonia.Animation protected abstract T DoInterpolation(double time, T neutralValue); /// - /// 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. /// private void VerifyConvertKeyFrames() { foreach (AnimatorKeyFrame keyframe in this) { - _convertedKeyframes.Add(keyframe.Cue.CueValue, (keyframe, false)); + _convertedKeyframes.Add(keyframe); } AddNeutralKeyFramesIfNeeded(); - _isVerfifiedAndConverted = true; + var copy = _convertedKeyframes.ToList().OrderBy(p => p.Cue.CueValue); + _convertedKeyframes.Clear(); + + foreach (AnimatorKeyFrame keyframe in copy) + { + _convertedKeyframes.Add(keyframe); + } + + _isVerifiedAndConverted = true; } private void AddNeutralKeyFramesIfNeeded() @@ -133,14 +140,14 @@ namespace Avalonia.Animation bool hasStartKey, hasEndKey; hasStartKey = hasEndKey = false; - // Make start and end keyframe mandatory. - foreach (var converted in _convertedKeyframes.Keys) + // Check if there's start and end keyframes. + foreach (var frame in _convertedKeyframes) { - if (DoubleUtils.AboutEqual(converted, 0.0)) + if (frame.Cue.CueValue == 0.0d) { hasStartKey = true; } - else if (DoubleUtils.AboutEqual(converted, 1.0)) + else if (frame.Cue.CueValue == 1.0d) { hasEndKey = true; } @@ -154,12 +161,12 @@ namespace Avalonia.Animation { if (!hasStartKey) { - _convertedKeyframes.Add(0.0d, (new AnimatorKeyFrame { Value = default(T) }, true)); + _convertedKeyframes.Add(new AnimatorKeyFrame(null, new Cue(0.0d)) { Value = default(T), isNeutral = true }); } 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 }); } } } diff --git a/src/Avalonia.Animation/Cue.cs b/src/Avalonia.Animation/Cue.cs index 5a95c108e3..52d1609cf9 100644 --- a/src/Avalonia.Animation/Cue.cs +++ b/src/Avalonia.Animation/Cue.cs @@ -1,13 +1,11 @@ using System; -using System.Collections.Generic; using System.ComponentModel; using System.Globalization; -using System.Text; namespace Avalonia.Animation { /// - /// A Cue object for . + /// Determines the time index for a . /// [TypeConverter(typeof(CueTypeConverter))] public readonly struct Cue : IEquatable, IEquatable @@ -84,5 +82,4 @@ namespace Avalonia.Animation return Cue.Parse((string)value, culture); } } - -} +} \ No newline at end of file diff --git a/src/Avalonia.Animation/DoubleAnimator.cs b/src/Avalonia.Animation/DoubleAnimator.cs index 154f37360c..aeeb29a7dd 100644 --- a/src/Avalonia.Animation/DoubleAnimator.cs +++ b/src/Avalonia.Animation/DoubleAnimator.cs @@ -1,13 +1,4 @@ -using System; -using System.Collections.Generic; -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 { /// /// Animator that handles properties. diff --git a/src/Avalonia.Animation/DoubleTransition.cs b/src/Avalonia.Animation/DoubleTransition.cs index 26f7f975e8..23445e1122 100644 --- a/src/Avalonia.Animation/DoubleTransition.cs +++ b/src/Avalonia.Animation/DoubleTransition.cs @@ -1,7 +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. -using Avalonia.Metadata; using System; using System.Reactive.Linq; diff --git a/src/Avalonia.Animation/Easing/BounceEaseIn.cs b/src/Avalonia.Animation/Easing/BounceEaseIn.cs index 6aa6f171f5..3fc879b3cf 100644 --- a/src/Avalonia.Animation/Easing/BounceEaseIn.cs +++ b/src/Avalonia.Animation/Easing/BounceEaseIn.cs @@ -1,7 +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. - using Avalonia.Animation.Utils; namespace Avalonia.Animation.Easings diff --git a/src/Avalonia.Animation/Easing/Easing.cs b/src/Avalonia.Animation/Easing/Easing.cs index 62bcf723fb..5b0dea6c60 100644 --- a/src/Avalonia.Animation/Easing/Easing.cs +++ b/src/Avalonia.Animation/Easing/Easing.cs @@ -1,10 +1,7 @@ -using Avalonia.Collections; using System; using System.Collections.Generic; -using System.Text; -using System.Reflection; -using System.Linq; using System.ComponentModel; +using System.Linq; namespace Avalonia.Animation.Easings { diff --git a/src/Avalonia.Animation/Easing/EasingTypeConverter.cs b/src/Avalonia.Animation/Easing/EasingTypeConverter.cs index 70ddfd2699..ee15761eff 100644 --- a/src/Avalonia.Animation/Easing/EasingTypeConverter.cs +++ b/src/Avalonia.Animation/Easing/EasingTypeConverter.cs @@ -1,7 +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. -using Avalonia.Animation.Easings; using System; using System.ComponentModel; using System.Globalization; @@ -20,4 +19,4 @@ namespace Avalonia.Animation.Easings return Easing.Parse((string)value); } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Animation/Easing/ElasticEaseIn.cs b/src/Avalonia.Animation/Easing/ElasticEaseIn.cs index 0dea7b20a7..654ac6991b 100644 --- a/src/Avalonia.Animation/Easing/ElasticEaseIn.cs +++ b/src/Avalonia.Animation/Easing/ElasticEaseIn.cs @@ -1,8 +1,8 @@ // 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.Animation.Utils; using System; +using Avalonia.Animation.Utils; namespace Avalonia.Animation.Easings { diff --git a/src/Avalonia.Animation/Easing/ElasticEaseOut.cs b/src/Avalonia.Animation/Easing/ElasticEaseOut.cs index 31e31374a0..1cc38a9dea 100644 --- a/src/Avalonia.Animation/Easing/ElasticEaseOut.cs +++ b/src/Avalonia.Animation/Easing/ElasticEaseOut.cs @@ -1,8 +1,8 @@ // 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.Animation.Utils; using System; +using Avalonia.Animation.Utils; namespace Avalonia.Animation.Easings { diff --git a/src/Avalonia.Animation/Easing/SineEaseIn.cs b/src/Avalonia.Animation/Easing/SineEaseIn.cs index 31a8f0ca94..a4b496e4b6 100644 --- a/src/Avalonia.Animation/Easing/SineEaseIn.cs +++ b/src/Avalonia.Animation/Easing/SineEaseIn.cs @@ -1,8 +1,8 @@ // 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.Animation.Utils; using System; +using Avalonia.Animation.Utils; namespace Avalonia.Animation.Easings { diff --git a/src/Avalonia.Animation/Easing/SineEaseOut.cs b/src/Avalonia.Animation/Easing/SineEaseOut.cs index ed5a998e30..267b4b3809 100644 --- a/src/Avalonia.Animation/Easing/SineEaseOut.cs +++ b/src/Avalonia.Animation/Easing/SineEaseOut.cs @@ -1,8 +1,8 @@ // 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.Animation.Utils; using System; +using Avalonia.Animation.Utils; namespace Avalonia.Animation.Easings { diff --git a/src/Avalonia.Animation/FillMode.cs b/src/Avalonia.Animation/FillMode.cs index 047d185db5..001e1cdeb4 100644 --- a/src/Avalonia.Animation/FillMode.cs +++ b/src/Avalonia.Animation/FillMode.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Avalonia.Animation +namespace Avalonia.Animation { public enum FillMode { diff --git a/src/Avalonia.Animation/FloatTransition.cs b/src/Avalonia.Animation/FloatTransition.cs index b94295d311..825a3f958f 100644 --- a/src/Avalonia.Animation/FloatTransition.cs +++ b/src/Avalonia.Animation/FloatTransition.cs @@ -1,7 +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. -using Avalonia.Metadata; using System; using System.Reactive.Linq; diff --git a/src/Avalonia.Animation/IAnimation.cs b/src/Avalonia.Animation/IAnimation.cs index 905d90fa52..1d545a322a 100644 --- a/src/Avalonia.Animation/IAnimation.cs +++ b/src/Avalonia.Animation/IAnimation.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using System.Threading.Tasks; namespace Avalonia.Animation diff --git a/src/Avalonia.Animation/IAnimator.cs b/src/Avalonia.Animation/IAnimator.cs index 8b763db603..9a4da35a02 100644 --- a/src/Avalonia.Animation/IAnimator.cs +++ b/src/Avalonia.Animation/IAnimator.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Text; namespace Avalonia.Animation { diff --git a/src/Avalonia.Animation/ITransition.cs b/src/Avalonia.Animation/ITransition.cs index 76f45e0c87..e2ffe7fc6e 100644 --- a/src/Avalonia.Animation/ITransition.cs +++ b/src/Avalonia.Animation/ITransition.cs @@ -1,9 +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 Avalonia.Metadata; using System; -using System.Reactive.Linq; namespace Avalonia.Animation { diff --git a/src/Avalonia.Animation/IntegerTransition.cs b/src/Avalonia.Animation/IntegerTransition.cs index 200b93a3df..e3dca5b26d 100644 --- a/src/Avalonia.Animation/IntegerTransition.cs +++ b/src/Avalonia.Animation/IntegerTransition.cs @@ -1,7 +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. -using Avalonia.Metadata; using System; using System.Reactive.Linq; diff --git a/src/Avalonia.Animation/KeyFrame.cs b/src/Avalonia.Animation/KeyFrame.cs index ea04aa0aab..5eb0d2e901 100644 --- a/src/Avalonia.Animation/KeyFrame.cs +++ b/src/Avalonia.Animation/KeyFrame.cs @@ -1,8 +1,5 @@ using System; using System.Collections.Generic; -using System.Text; -using System.ComponentModel; -using Avalonia.Metadata; using Avalonia.Collections; namespace Avalonia.Animation diff --git a/src/Avalonia.Animation/KeyFramePair`1.cs b/src/Avalonia.Animation/KeyFramePair`1.cs index 408b13e0d8..b0622a1580 100644 --- a/src/Avalonia.Animation/KeyFramePair`1.cs +++ b/src/Avalonia.Animation/KeyFramePair`1.cs @@ -1,14 +1,3 @@ -using System; -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 Avalonia.Data; -using System.Reactive.Disposables; - namespace Avalonia.Animation { /// diff --git a/src/Avalonia.Animation/PlayState.cs b/src/Avalonia.Animation/PlayState.cs index a3a6eb34d7..313d33d586 100644 --- a/src/Avalonia.Animation/PlayState.cs +++ b/src/Avalonia.Animation/PlayState.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Avalonia.Animation +namespace Avalonia.Animation { /// /// Determines the playback state of an animation. @@ -24,4 +20,4 @@ namespace Avalonia.Animation /// Stop } -} \ No newline at end of file +} diff --git a/src/Avalonia.Animation/PlaybackDirection.cs b/src/Avalonia.Animation/PlaybackDirection.cs index 39529323da..bbce6106e1 100644 --- a/src/Avalonia.Animation/PlaybackDirection.cs +++ b/src/Avalonia.Animation/PlaybackDirection.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Avalonia.Animation +namespace Avalonia.Animation { /// /// Determines the playback direction of an animation. diff --git a/src/Avalonia.Animation/RepeatCount.cs b/src/Avalonia.Animation/RepeatCount.cs index 2050513df2..9794fe8f36 100644 --- a/src/Avalonia.Animation/RepeatCount.cs +++ b/src/Avalonia.Animation/RepeatCount.cs @@ -1,12 +1,9 @@ // 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.Utilities; using System; -using System.Collections.Generic; using System.ComponentModel; using System.Globalization; -using System.Linq; namespace Avalonia.Animation { @@ -199,4 +196,4 @@ namespace Avalonia.Animation } } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Animation/RepeatCountTypeConverter.cs b/src/Avalonia.Animation/RepeatCountTypeConverter.cs index 604e9b54e7..4eab50c1c7 100644 --- a/src/Avalonia.Animation/RepeatCountTypeConverter.cs +++ b/src/Avalonia.Animation/RepeatCountTypeConverter.cs @@ -1,7 +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. -using Avalonia.Animation.Easings; using System; using System.ComponentModel; using System.Globalization; @@ -20,4 +19,4 @@ namespace Avalonia.Animation return RepeatCount.Parse((string)value); } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Animation/Timing.cs b/src/Avalonia.Animation/Timing.cs index 10d65cca7f..6367425911 100644 --- a/src/Avalonia.Animation/Timing.cs +++ b/src/Avalonia.Animation/Timing.cs @@ -2,10 +2,8 @@ // 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 @@ -15,9 +13,6 @@ namespace Avalonia.Animation /// public static class Timing { - static ulong _transitionsFrameCount; - static PlayState _globalState = PlayState.Run; - /// /// The number of frames per second. /// @@ -32,41 +27,16 @@ namespace Avalonia.Animation /// Initializes static members of the class. /// static Timing() - { + { var globalTimer = Observable.Interval(FrameTick, AvaloniaScheduler.Instance); - AnimationStateTimer = globalTimer - .Select(_ => - { - return _globalState; - }) + AnimationsTimer = globalTimer + .Select(_ => GetTickCount()) .Publish() .RefCount(); - - TransitionsTimer = globalTimer - .Select(p => _transitionsFrameCount++) - .Publish() - .RefCount(); - } - - - /// - /// Sets the animation play state for all animations - /// - public static void SetGlobalPlayState(PlayState playState) - { - Dispatcher.UIThread.VerifyAccess(); - _globalState = playState; } - /// - /// Gets the animation play state for all animations - /// - public static PlayState GetGlobalPlayState() - { - Dispatcher.UIThread.VerifyAccess(); - return _globalState; - } + internal static TimeSpan GetTickCount() => TimeSpan.FromMilliseconds(Environment.TickCount); /// /// Gets the animation timer. @@ -76,48 +46,9 @@ namespace Avalonia.Animation /// defined in . /// The parameter passed to a subsciber is the current playstate of the animation. /// - internal static IObservable AnimationStateTimer + internal static IObservable AnimationsTimer { get; } - - /// - /// Gets the transitions timer. - /// - /// - /// The transitions timer increments usually 60 times per second as - /// defined in . - /// The parameter passed to a subsciber is the number of frames since the animation system was - /// initialized. - /// - public static IObservable TransitionsTimer - { - get; - } - - /// - /// Gets a timer that fires every frame for the specified duration with delay. - /// - /// - /// An observable that notifies the subscriber of the progress along the transition. - /// - /// - /// 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. - /// - public static IObservable 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)); - } - } -} +} \ No newline at end of file diff --git a/src/Avalonia.Animation/TransitionInstance.cs b/src/Avalonia.Animation/TransitionInstance.cs new file mode 100644 index 0000000000..e2719cb472 --- /dev/null +++ b/src/Avalonia.Animation/TransitionInstance.cs @@ -0,0 +1,54 @@ +// 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 +{ + /// + /// Handles the timing and lifetime of a . + /// + internal class TransitionInstance : SingleSubscriberObservableBase + { + private IDisposable timerSubscription; + private TimeSpan startTime; + private TimeSpan duration; + + public TransitionInstance(TimeSpan Duration) + { + duration = Duration; + } + + private void TimerTick(TimeSpan t) + { + var interpVal = (double)(t.Ticks - startTime.Ticks) / duration.Ticks; + + if (interpVal > 1d + || interpVal < 0d) + { + PublishCompleted(); + return; + } + + PublishNext(interpVal); + } + + protected override void Unsubscribed() + { + timerSubscription?.Dispose(); + } + + protected override void Subscribed() + { + startTime = Timing.GetTickCount(); + timerSubscription = Timing.AnimationsTimer + .Subscribe(t => TimerTick(t)); + PublishNext(0.0d); + } + } +} \ No newline at end of file diff --git a/src/Avalonia.Animation/Transition`1.cs b/src/Avalonia.Animation/Transition`1.cs index 5db3082deb..4b01c54f5c 100644 --- a/src/Avalonia.Animation/Transition`1.cs +++ b/src/Avalonia.Animation/Transition`1.cs @@ -1,10 +1,10 @@ // 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; namespace Avalonia.Animation { @@ -24,17 +24,7 @@ namespace Avalonia.Animation /// /// Gets the easing class to be used. /// - public Easing Easing - { - get - { - return _easing ?? (_easing = new LinearEasing()); - } - set - { - _easing = value; - } - } + public Easing Easing { get; set; } = new LinearEasing(); /// public AvaloniaProperty Property @@ -61,8 +51,10 @@ namespace Avalonia.Animation /// public virtual IDisposable Apply(Animatable control, object oldValue, object newValue) { - var transition = DoTransition(Timing.GetTransitionsTimer(control, Duration, TimeSpan.Zero), (T)oldValue, (T)newValue).Select(p => (object)p); - return control.Bind(Property, transition, Data.BindingPriority.Animation); + var transition = DoTransition(new TransitionInstance(Duration), (T)oldValue, (T)newValue); + return control.Bind((AvaloniaProperty)Property, transition, Data.BindingPriority.Animation); } + + } -} \ No newline at end of file +} diff --git a/src/Avalonia.Animation/Utils/DoubleUtils.cs b/src/Avalonia.Animation/Utils/DoubleUtils.cs deleted file mode 100644 index 67943b687d..0000000000 --- a/src/Avalonia.Animation/Utils/DoubleUtils.cs +++ /dev/null @@ -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; - } - } -} diff --git a/src/Avalonia.Animation/Utils/EasingUtils.cs b/src/Avalonia.Animation/Utils/EasingUtils.cs index d07ec3cdf4..1a7688cace 100644 --- a/src/Avalonia.Animation/Utils/EasingUtils.cs +++ b/src/Avalonia.Animation/Utils/EasingUtils.cs @@ -13,6 +13,6 @@ namespace Avalonia.Animation.Utils /// /// Half of /// - internal static double HALFPI = Math.PI / 2d; + internal const double HALFPI = Math.PI / 2d; } } diff --git a/src/Avalonia.Base/Data/Core/Plugins/DataValidationBase.cs b/src/Avalonia.Base/Data/Core/Plugins/DataValidationBase.cs index faec981eed..2e0d1aa805 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/DataValidationBase.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/DataValidationBase.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; -using Avalonia.Data; namespace Avalonia.Data.Core.Plugins { diff --git a/src/Avalonia.Base/PriorityBindingEntry.cs b/src/Avalonia.Base/PriorityBindingEntry.cs index 570bfe03dc..d4a47306a7 100644 --- a/src/Avalonia.Base/PriorityBindingEntry.cs +++ b/src/Avalonia.Base/PriorityBindingEntry.cs @@ -50,6 +50,11 @@ namespace Avalonia get; } + /// + /// Gets a value indicating whether the binding has completed. + /// + public bool HasCompleted { get; private set; } + /// /// The current value of the binding. /// @@ -129,6 +134,8 @@ namespace Avalonia private void Completed() { + HasCompleted = true; + if (Dispatcher.UIThread.CheckAccess()) { _owner.Completed(this); diff --git a/src/Avalonia.Base/PriorityLevel.cs b/src/Avalonia.Base/PriorityLevel.cs index 96661bd7ea..909558b0ce 100644 --- a/src/Avalonia.Base/PriorityLevel.cs +++ b/src/Avalonia.Base/PriorityLevel.cs @@ -112,12 +112,16 @@ namespace Avalonia return Disposable.Create(() => { - Bindings.Remove(node); - entry.Dispose(); - - if (entry.Index >= ActiveBindingIndex) + if (!entry.HasCompleted) { - ActivateFirstBinding(); + Bindings.Remove(node); + + entry.Dispose(); + + if (entry.Index >= ActiveBindingIndex) + { + ActivateFirstBinding(); + } } }); } diff --git a/src/Avalonia.Base/Threading/Dispatcher.cs b/src/Avalonia.Base/Threading/Dispatcher.cs index aa2a7a7a8e..55a9b6984a 100644 --- a/src/Avalonia.Base/Threading/Dispatcher.cs +++ b/src/Avalonia.Base/Threading/Dispatcher.cs @@ -92,6 +92,20 @@ namespace Avalonia.Threading return _jobRunner.InvokeAsync(function, priority); } + /// + public Task InvokeAsync(Func function, DispatcherPriority priority = DispatcherPriority.Normal) + { + Contract.Requires(function != null); + return _jobRunner.InvokeAsync(function, priority).Unwrap(); + } + + /// + public Task InvokeAsync(Func> function, DispatcherPriority priority = DispatcherPriority.Normal) + { + Contract.Requires(function != null); + return _jobRunner.InvokeAsync(function, priority).Unwrap(); + } + /// public void Post(Action action, DispatcherPriority priority = DispatcherPriority.Normal) { diff --git a/src/Avalonia.Base/Threading/IDispatcher.cs b/src/Avalonia.Base/Threading/IDispatcher.cs index 1fdc9da5fe..8f46f99283 100644 --- a/src/Avalonia.Base/Threading/IDispatcher.cs +++ b/src/Avalonia.Base/Threading/IDispatcher.cs @@ -40,5 +40,23 @@ namespace Avalonia.Threading /// The method. /// The priority with which to invoke the method. Task InvokeAsync(Func function, DispatcherPriority priority = DispatcherPriority.Normal); + + /// + /// Queues the specified work to run on the dispatcher thread and returns a proxy for the + /// task returned by . + /// + /// The work to execute asynchronously. + /// The priority with which to invoke the method. + /// A task that represents a proxy for the task returned by . + Task InvokeAsync(Func function, DispatcherPriority priority = DispatcherPriority.Normal); + + /// + /// Queues the specified work to run on the dispatcher thread and returns a proxy for the + /// task returned by . + /// + /// The work to execute asynchronously. + /// The priority with which to invoke the method. + /// A task that represents a proxy for the task returned by . + Task InvokeAsync(Func> function, DispatcherPriority priority = DispatcherPriority.Normal); } } \ No newline at end of file diff --git a/src/Avalonia.Controls/Border.cs b/src/Avalonia.Controls/Border.cs index 5f84421c64..0edb272015 100644 --- a/src/Avalonia.Controls/Border.cs +++ b/src/Avalonia.Controls/Border.cs @@ -43,8 +43,12 @@ namespace Avalonia.Controls /// static Border() { - AffectsRender(BackgroundProperty, BorderBrushProperty, BorderThicknessProperty, CornerRadiusProperty); - AffectsMeasure(BorderThicknessProperty); + AffectsRender( + BackgroundProperty, + BorderBrushProperty, + BorderThicknessProperty, + CornerRadiusProperty); + AffectsMeasure(BorderThicknessProperty); } /// diff --git a/src/Avalonia.Controls/Button.cs b/src/Avalonia.Controls/Button.cs index fa69d72d67..24b2af7996 100644 --- a/src/Avalonia.Controls/Button.cs +++ b/src/Avalonia.Controls/Button.cs @@ -80,7 +80,7 @@ namespace Avalonia.Controls FocusableProperty.OverrideDefaultValue(typeof(Button), true); CommandProperty.Changed.Subscribe(CommandChanged); IsDefaultProperty.Changed.Subscribe(IsDefaultChanged); - PseudoClass(IsPressedProperty, ":pressed"); + PseudoClass