Browse Source

Merge branch 'master' into x11

pull/2011/head
danwalmsley 8 years ago
committed by GitHub
parent
commit
cd957fbb79
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      .gitignore
  2. 3
      .gitmodules
  3. 29
      Avalonia.sln
  4. 2
      Directory.Build.props
  5. 2
      build/Magick.NET-Q16-AnyCPU.props
  6. 1
      build/ReferenceCoreLibraries.props
  7. 4
      build/SkiaSharp.props
  8. 6
      native/Avalonia.Native/src/OSX/gl.mm
  9. 21
      native/Avalonia.Native/src/OSX/window.mm
  10. 8
      nukebuild/.editorconfig
  11. 20
      nukebuild/Build.cs
  12. 6
      nukebuild/BuildParameters.cs
  13. 1
      nukebuild/Numerge
  14. 14
      nukebuild/Shims.cs
  15. 2
      nukebuild/_build.csproj
  16. 23
      nukebuild/numerge.config
  17. 8
      packages/Avalonia/Avalonia.csproj
  18. 2
      packages/Avalonia/Avalonia.props
  19. 35
      samples/RenderDemo/Pages/AnimationsPage.xaml
  20. 2
      samples/RenderDemo/Pages/ClippingPage.xaml
  21. 1
      src/Avalonia.Animation/Animatable.cs
  22. 199
      src/Avalonia.Animation/Animation.cs
  23. 204
      src/Avalonia.Animation/AnimationInstance`1.cs
  24. 73
      src/Avalonia.Animation/Animators/Animator`1.cs
  25. 21
      src/Avalonia.Animation/Animators/BoolAnimator.cs
  26. 24
      src/Avalonia.Animation/Animators/ByteAnimator.cs
  27. 17
      src/Avalonia.Animation/Animators/DecimalAnimator.cs
  28. 17
      src/Avalonia.Animation/Animators/DoubleAnimator.cs
  29. 17
      src/Avalonia.Animation/Animators/FloatAnimator.cs
  30. 24
      src/Avalonia.Animation/Animators/Int16Animator.cs
  31. 24
      src/Avalonia.Animation/Animators/Int32Animator.cs
  32. 24
      src/Avalonia.Animation/Animators/Int64Animator.cs
  33. 24
      src/Avalonia.Animation/Animators/UInt16Animator.cs
  34. 24
      src/Avalonia.Animation/Animators/UInt32Animator.cs
  35. 24
      src/Avalonia.Animation/Animators/UInt64Animator.cs
  36. 1
      src/Avalonia.Animation/Avalonia.Animation.csproj
  37. 3
      src/Avalonia.Animation/DisposeAnimationInstanceSubject.cs
  38. 35
      src/Avalonia.Animation/DoubleAnimator.cs
  39. 0
      src/Avalonia.Animation/Easing/IEasing.cs
  40. 176
      src/Avalonia.Animation/IterationCount.cs
  41. 4
      src/Avalonia.Animation/IterationCountTypeConverter.cs
  42. 15
      src/Avalonia.Animation/KeyFrame.cs
  43. 33
      src/Avalonia.Animation/KeyFramePair`1.cs
  44. 33
      src/Avalonia.Animation/KeyFrames.cs
  45. 3
      src/Avalonia.Animation/Properties/AssemblyInfo.cs
  46. 199
      src/Avalonia.Animation/RepeatCount.cs
  47. 7
      src/Avalonia.Animation/Transitions/DoubleTransition.cs
  48. 0
      src/Avalonia.Animation/Transitions/FloatTransition.cs
  49. 0
      src/Avalonia.Animation/Transitions/IntegerTransition.cs
  50. 1
      src/Avalonia.Base/Avalonia.Base.csproj
  51. 1
      src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
  52. 1
      src/Avalonia.Controls/Avalonia.Controls.csproj
  53. 4
      src/Avalonia.Controls/Button.cs
  54. 1
      src/Avalonia.Controls/Panel.cs
  55. 1
      src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj
  56. 1
      src/Avalonia.DesktopRuntime/Avalonia.DesktopRuntime.csproj
  57. 1
      src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj
  58. 1
      src/Avalonia.Input/Avalonia.Input.csproj
  59. 1
      src/Avalonia.Interactivity/Avalonia.Interactivity.csproj
  60. 1
      src/Avalonia.Layout/Avalonia.Layout.csproj
  61. 1
      src/Avalonia.Logging.Serilog/Avalonia.Logging.Serilog.csproj
  62. 2
      src/Avalonia.Native/PlatformThreadingInterface.cs
  63. 1
      src/Avalonia.Styling/Avalonia.Styling.csproj
  64. 1
      src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj
  65. 4
      src/Avalonia.Themes.Default/ProgressBar.xaml
  66. 70
      src/Avalonia.Visuals/Animation/Animators/ColorAnimator.cs
  67. 27
      src/Avalonia.Visuals/Animation/Animators/CornerRadiusAnimator.cs
  68. 17
      src/Avalonia.Visuals/Animation/Animators/PointAnimator.cs
  69. 23
      src/Avalonia.Visuals/Animation/Animators/RectAnimator.cs
  70. 17
      src/Avalonia.Visuals/Animation/Animators/SizeAnimator.cs
  71. 74
      src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs
  72. 17
      src/Avalonia.Visuals/Animation/Animators/ThicknessAnimator.cs
  73. 37
      src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs
  74. 17
      src/Avalonia.Visuals/Animation/Animators/VectorAnimator.cs
  75. 44
      src/Avalonia.Visuals/Animation/CrossFade.cs
  76. 89
      src/Avalonia.Visuals/Animation/PageSlide.cs
  77. 36
      src/Avalonia.Visuals/Animation/Transitions/CornerRadiusTransition.cs
  78. 7
      src/Avalonia.Visuals/Animation/Transitions/PointTransition.cs
  79. 25
      src/Avalonia.Visuals/Animation/Transitions/SizeTransition.cs
  80. 13
      src/Avalonia.Visuals/Animation/Transitions/ThicknessTransition.cs
  81. 25
      src/Avalonia.Visuals/Animation/Transitions/VectorTransition.cs
  82. 1
      src/Avalonia.Visuals/Avalonia.Visuals.csproj
  83. 6
      src/Avalonia.Visuals/CornerRadius.cs
  84. 3
      src/Avalonia.Visuals/Media/Brush.cs
  85. 7
      src/Avalonia.Visuals/Media/Color.cs
  86. 3
      src/Avalonia.Visuals/Media/SolidColorBrush.cs
  87. 1
      src/Avalonia.Visuals/Media/Transform.cs
  88. 14
      src/Avalonia.Visuals/Point.cs
  89. 6
      src/Avalonia.Visuals/Rect.cs
  90. 6
      src/Avalonia.Visuals/Size.cs
  91. 39
      src/Avalonia.Visuals/Thickness.cs
  92. 10
      src/Avalonia.Visuals/Vector.cs
  93. 2
      src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs
  94. 1
      src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
  95. 1
      src/Markup/Avalonia.Markup/Avalonia.Markup.csproj
  96. 3
      src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs
  97. 23
      src/Skia/Avalonia.Skia/GlRenderTarget.cs
  98. 2
      src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs
  99. 2
      src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs
  100. 8
      src/Windows/Avalonia.Win32/WindowImpl.cs

1
.gitignore

@ -195,3 +195,4 @@ Logs/
ModuleCache.noindex/ ModuleCache.noindex/
Build/Intermediates.noindex/ Build/Intermediates.noindex/
info.plist info.plist
build-intermediate

3
.gitmodules

@ -1,3 +1,6 @@
[submodule "src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github"] [submodule "src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github"]
path = src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github path = src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github
url = https://github.com/AvaloniaUI/Portable.Xaml.git url = https://github.com/AvaloniaUI/Portable.Xaml.git
[submodule "nukebuild/Numerge"]
path = nukebuild/Numerge
url = https://github.com/kekekeks/Numerge.git

29
Avalonia.sln

@ -1,4 +1,4 @@
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15 # Visual Studio 15
VisualStudioVersion = 15.0.27130.2027 VisualStudioVersion = 15.0.27130.2027
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
@ -197,6 +197,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Build.Tasks", "src
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "_build", "nukebuild\_build.csproj", "{3F00BC43-5095-477F-93D8-E65B08179A00}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "_build", "nukebuild\_build.csproj", "{3F00BC43-5095-477F-93D8-E65B08179A00}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Animation.UnitTests", "tests\Avalonia.Animation.UnitTests\Avalonia.Animation.UnitTests.csproj", "{AF227847-E65C-4BE9-BCE9-B551357788E0}"
EndProject
Global Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution GlobalSection(SharedMSBuildProjectFiles) = preSolution
src\Shared\RenderHelpers\RenderHelpers.projitems*{3c4c0cb4-0c0f-4450-a37b-148c84ff905f}*SharedItemsImports = 13 src\Shared\RenderHelpers\RenderHelpers.projitems*{3c4c0cb4-0c0f-4450-a37b-148c84ff905f}*SharedItemsImports = 13
@ -1748,6 +1750,30 @@ Global
{BF28998D-072C-439A-AFBB-2FE5021241E0}.Release|iPhone.Build.0 = Release|Any CPU {BF28998D-072C-439A-AFBB-2FE5021241E0}.Release|iPhone.Build.0 = Release|Any CPU
{BF28998D-072C-439A-AFBB-2FE5021241E0}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {BF28998D-072C-439A-AFBB-2FE5021241E0}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{BF28998D-072C-439A-AFBB-2FE5021241E0}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {BF28998D-072C-439A-AFBB-2FE5021241E0}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.AppStore|iPhone.Build.0 = Debug|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.Debug|iPhone.Build.0 = Debug|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.Release|Any CPU.Build.0 = Release|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.Release|iPhone.ActiveCfg = Release|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.Release|iPhone.Build.0 = Release|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@ -1803,6 +1829,7 @@ Global
{8B5768BB-71F9-4E23-89B5-DDBA6458B856} = {9B9E3891-2366-4253-A952-D08BCEB71098} {8B5768BB-71F9-4E23-89B5-DDBA6458B856} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{D49233F8-F29C-47DD-9975-C4C9E4502720} = {E870DCD7-F46A-498D-83FC-D0FD13E0A11C} {D49233F8-F29C-47DD-9975-C4C9E4502720} = {E870DCD7-F46A-498D-83FC-D0FD13E0A11C}
{3C471044-3640-45E3-B1B2-16D2FF8399EE} = {E870DCD7-F46A-498D-83FC-D0FD13E0A11C} {3C471044-3640-45E3-B1B2-16D2FF8399EE} = {E870DCD7-F46A-498D-83FC-D0FD13E0A11C}
{AF227847-E65C-4BE9-BCE9-B551357788E0} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A} SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A}

2
Directory.Build.props

@ -1,5 +1,5 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<PackageOutputPath Condition="'$(PackageOutputPath)' == ''">$(MSBuildThisFileDirectory)artifacts/nuget</PackageOutputPath> <PackageOutputPath Condition="'$(PackageOutputPath)' == ''">$(MSBuildThisFileDirectory)build-intermediate/nuget</PackageOutputPath>
</PropertyGroup> </PropertyGroup>
</Project> </Project>

2
build/Magick.NET-Q16-AnyCPU.props

@ -1,5 +1,5 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup> <ItemGroup>
<PackageReference Include="Magick.NET-Q16-AnyCPU" Version="7.0.6.102" /> <PackageReference Include="Magick.NET-Q16-AnyCPU" Version="7.9.0.2" />
</ItemGroup> </ItemGroup>
</Project> </Project>

1
build/ReferenceCoreLibraries.props

@ -1,5 +1,4 @@
<Project> <Project>
<Import Condition="'$(TargetFramework)' == 'netcoreapp2.0'" Project="CoreLibraries.props" />
<ItemGroup> <ItemGroup>
<ProjectReference Include="$(MSBuildThisFileDirectory)../packages/Avalonia/Avalonia.csproj" /> <ProjectReference Include="$(MSBuildThisFileDirectory)../packages/Avalonia/Avalonia.csproj" />
</ItemGroup> </ItemGroup>

4
build/SkiaSharp.props

@ -1,6 +1,6 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup> <ItemGroup>
<PackageReference Include="SkiaSharp" Version="1.60.0" /> <PackageReference Include="SkiaSharp" Version="1.68.0" />
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="Avalonia.Skia.Linux.Natives" Version="1.60.0.1" /> <PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="SkiaSharp.NativeAssets.Linux" Version="1.68.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

6
native/Avalonia.Native/src/OSX/gl.mm

@ -208,9 +208,8 @@ public:
virtual ~AvnGlRenderingSession() virtual ~AvnGlRenderingSession()
{ {
glFlush();
[_context flushBuffer]; [_context flushBuffer];
[_context setView:nil]; [NSOpenGLContext clearCurrentContext];
CGLUnlockContext([_context CGLContextObj]); CGLUnlockContext([_context CGLContextObj]);
[_view unlockFocus]; [_view unlockFocus];
} }
@ -241,9 +240,8 @@ public:
auto gl = _context; auto gl = _context;
CGLLockContext([_context CGLContextObj]); CGLLockContext([_context CGLContextObj]);
[gl setView: _view]; [gl setView: _view];
[gl update];
[gl makeCurrentContext]; [gl makeCurrentContext];
auto frame = [_view frame];
*ret = new AvnGlRenderingSession(_window, _view, gl); *ret = new AvnGlRenderingSession(_window, _view, gl);
return S_OK; return S_OK;
} }

21
native/Avalonia.Native/src/OSX/window.mm

@ -425,7 +425,7 @@ private:
[[Window parentWindow] removeChildWindow:Window]; [[Window parentWindow] removeChildWindow:Window];
WindowBaseImpl::Show(); WindowBaseImpl::Show();
return SetWindowState(_lastWindowState); return SetWindowState(Normal);
} }
} }
@ -1184,6 +1184,25 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
} }
} }
- (void)windowDidMiniaturize:(NSNotification *)notification
{
auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->());
if(parent != nullptr)
{
parent->WindowStateChanged();
}
}
- (void)windowDidDeminiaturize:(NSNotification *)notification
{
auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->());
if(parent != nullptr)
{
parent->WindowStateChanged();
}
}
- (BOOL)windowShouldZoom:(NSWindow *)window toFrame:(NSRect)newFrame - (BOOL)windowShouldZoom:(NSWindow *)window toFrame:(NSRect)newFrame
{ {

8
nukebuild/.editorconfig

@ -0,0 +1,8 @@
# editorconfig.org
# top-most EditorConfig file
root = false
# C# files
[*.cs]
dotnet_style_require_accessibility_modifiers = never

20
nukebuild/Build.cs

@ -63,6 +63,7 @@ partial class Build : NukeBuild
DeleteDirectories(Parameters.BuildDirs); DeleteDirectories(Parameters.BuildDirs);
EnsureCleanDirectories(Parameters.BuildDirs); EnsureCleanDirectories(Parameters.BuildDirs);
EnsureCleanDirectory(Parameters.ArtifactsDir); EnsureCleanDirectory(Parameters.ArtifactsDir);
EnsureCleanDirectory(Parameters.NugetIntermediateRoot);
EnsureCleanDirectory(Parameters.NugetRoot); EnsureCleanDirectory(Parameters.NugetRoot);
EnsureCleanDirectory(Parameters.ZipRoot); EnsureCleanDirectory(Parameters.ZipRoot);
EnsureCleanDirectory(Parameters.TestResultsRoot); EnsureCleanDirectory(Parameters.TestResultsRoot);
@ -123,6 +124,7 @@ partial class Build : NukeBuild
.DependsOn(Compile) .DependsOn(Compile)
.Executes(() => .Executes(() =>
{ {
RunCoreTest("./tests/Avalonia.Animation.UnitTests", false);
RunCoreTest("./tests/Avalonia.Base.UnitTests", false); RunCoreTest("./tests/Avalonia.Base.UnitTests", false);
RunCoreTest("./tests/Avalonia.Controls.UnitTests", false); RunCoreTest("./tests/Avalonia.Controls.UnitTests", false);
RunCoreTest("./tests/Avalonia.Input.UnitTests", false); RunCoreTest("./tests/Avalonia.Input.UnitTests", false);
@ -137,12 +139,13 @@ partial class Build : NukeBuild
}); });
Target RunRenderTests => _ => _ Target RunRenderTests => _ => _
.OnlyWhen(() => !Parameters.SkipTests && Parameters.IsRunningOnWindows) .OnlyWhen(() => !Parameters.SkipTests)
.DependsOn(Compile) .DependsOn(Compile)
.Executes(() => .Executes(() =>
{ {
RunCoreTest("./tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj", true); RunCoreTest("./tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj", true);
RunCoreTest("./tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj", true); if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
RunCoreTest("./tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj", true);
}); });
Target RunDesignerTests => _ => _ Target RunDesignerTests => _ => _
@ -181,7 +184,7 @@ partial class Build : NukeBuild
GlobFiles(data.ZipSourceControlCatalogDesktopDir, "*.exe"))); GlobFiles(data.ZipSourceControlCatalogDesktopDir, "*.exe")));
}); });
Target CreateNugetPackages => _ => _ Target CreateIntermediateNugetPackages => _ => _
.DependsOn(Compile) .DependsOn(Compile)
.After(RunTests) .After(RunTests)
.Executes(() => .Executes(() =>
@ -200,6 +203,17 @@ partial class Build : NukeBuild
c.SetConfiguration(Parameters.Configuration) c.SetConfiguration(Parameters.Configuration)
.AddProperty("PackageVersion", Parameters.Version)); .AddProperty("PackageVersion", Parameters.Version));
}); });
Target CreateNugetPackages => _ => _
.DependsOn(CreateIntermediateNugetPackages)
.Executes(() =>
{
var config = Numerge.MergeConfiguration.LoadFile(RootDirectory / "nukebuild" / "numerge.config");
EnsureCleanDirectory(Parameters.NugetRoot);
if(!Numerge.NugetPackageMerger.Merge(Parameters.NugetIntermediateRoot, Parameters.NugetRoot, config,
new NumergeNukeLogger()))
throw new Exception("Package merge failed");
});
Target RunTests => _ => _ Target RunTests => _ => _
.DependsOn(RunCoreLibsTests) .DependsOn(RunCoreLibsTests)

6
nukebuild/BuildParameters.cs

@ -47,6 +47,7 @@ public partial class Build
public bool PublishTestResults { get; } public bool PublishTestResults { get; }
public string Version { get; } public string Version { get; }
public AbsolutePath ArtifactsDir { get; } public AbsolutePath ArtifactsDir { get; }
public AbsolutePath NugetIntermediateRoot { get; }
public AbsolutePath NugetRoot { get; } public AbsolutePath NugetRoot { get; }
public AbsolutePath ZipRoot { get; } public AbsolutePath ZipRoot { get; }
public AbsolutePath BinRoot { get; } public AbsolutePath BinRoot { get; }
@ -117,6 +118,7 @@ public partial class Build
// DIRECTORIES // DIRECTORIES
ArtifactsDir = RootDirectory / "artifacts"; ArtifactsDir = RootDirectory / "artifacts";
NugetRoot = ArtifactsDir / "nuget"; NugetRoot = ArtifactsDir / "nuget";
NugetIntermediateRoot = RootDirectory / "build-intermediate" / "nuget";
ZipRoot = ArtifactsDir / "zip"; ZipRoot = ArtifactsDir / "zip";
BinRoot = ArtifactsDir / "bin"; BinRoot = ArtifactsDir / "bin";
TestResultsRoot = ArtifactsDir / "test-results"; TestResultsRoot = ArtifactsDir / "test-results";
@ -130,9 +132,9 @@ public partial class Build
ZipTargetControlCatalogDesktopDir = ZipRoot / ("ControlCatalog.Desktop-" + FileZipSuffix); ZipTargetControlCatalogDesktopDir = ZipRoot / ("ControlCatalog.Desktop-" + FileZipSuffix);
} }
private static string GetVersion() string GetVersion()
{ {
var xdoc = XDocument.Load("./build/SharedVersion.props"); var xdoc = XDocument.Load(RootDirectory / "build/SharedVersion.props");
return xdoc.Descendants().First(x => x.Name.LocalName == "Version").Value; return xdoc.Descendants().First(x => x.Name.LocalName == "Version").Value;
} }
} }

1
nukebuild/Numerge

@ -0,0 +1 @@
Subproject commit 4464343aef5c8ab7a42fcb20a483a6058199f8b8

14
nukebuild/Shims.cs

@ -5,6 +5,7 @@ using System.IO.Compression;
using System.Linq; using System.Linq;
using Nuke.Common; using Nuke.Common;
using Nuke.Common.IO; using Nuke.Common.IO;
using Numerge;
public partial class Build public partial class Build
{ {
@ -76,4 +77,17 @@ public partial class Build
} }
} }
} }
class NumergeNukeLogger : INumergeLogger
{
public void Log(NumergeLogLevel level, string message)
{
if(level == NumergeLogLevel.Error)
Logger.Error(message);
else if (level == NumergeLogLevel.Warning)
Logger.Warn(message);
else
Logger.Info(message);
}
}
} }

2
nukebuild/_build.csproj

@ -30,6 +30,8 @@
<None Include="..\appveyor.yml" Condition="Exists('..\appveyor.yml')" /> <None Include="..\appveyor.yml" Condition="Exists('..\appveyor.yml')" />
<None Include="..\.travis.yml" Condition="Exists('..\.travis.yml')" /> <None Include="..\.travis.yml" Condition="Exists('..\.travis.yml')" />
<None Include="..\GitVersion.yml" Condition="Exists('..\GitVersion.yml')" /> <None Include="..\GitVersion.yml" Condition="Exists('..\GitVersion.yml')" />
<Compile Remove="Numerge/**/*.*" />
<Compile Include="Numerge/Numerge/**/*.cs" />
</ItemGroup> </ItemGroup>
</Project> </Project>

23
nukebuild/numerge.config

@ -0,0 +1,23 @@
{
"Packages":
[
{
"Id": "Avalonia",
"MergeAll": true,
"Exclude": ["Avalonia.Remote.Protocol"],
"IncomingIncludeAssetsOverride": "",
"Merge": [
{
"Id": "Avalonia.Build.Tasks",
"IgnoreMissingFrameworkBinaries": true,
"DoNotMergeDependencies": true
},
{
"Id": "Avalonia.DesktopRuntime",
"IgnoreMissingFrameworkBinaries": true,
"IgnoreMissingFrameworkDependencies": true
}
]
}
]
}

8
packages/Avalonia/Avalonia.csproj

@ -1,10 +1,10 @@
<Project Sdk="AggregatePackage.NuGet.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard2.0;net461;netcoreapp2.0</TargetFrameworks> <TargetFrameworks>netstandard2.0;net461;netcoreapp2.0</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="../../src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj" EmbedReference="false" /> <ProjectReference Include="../../src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj"/>
<ProjectReference Include="../../src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj" /> <ProjectReference Include="../../src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj" />
</ItemGroup> </ItemGroup>
@ -40,10 +40,6 @@
<Pack>true</Pack> <Pack>true</Pack>
<PackagePath>build\</PackagePath> <PackagePath>build\</PackagePath>
</Content> </Content>
<Content Include="../../src/Avalonia.Build.Tasks/bin/$(Configuration)/netstandard2.0/Avalonia.Build.Tasks.dll">
<Pack>true</Pack>
<PackagePath>tools\</PackagePath>
</Content>
</ItemGroup> </ItemGroup>
<Import Project="..\..\build\SharedVersion.props" /> <Import Project="..\..\build\SharedVersion.props" />
<Import Project="..\..\build\NetFX.props" /> <Import Project="..\..\build\NetFX.props" />

2
packages/Avalonia/Avalonia.props

@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<AvaloniaPreviewerNetCoreToolPath>$(MSBuildThisFileDirectory)\..\tools\netcoreapp2.0\designer\Avalonia.Designer.HostApp.dll</AvaloniaPreviewerNetCoreToolPath> <AvaloniaPreviewerNetCoreToolPath>$(MSBuildThisFileDirectory)\..\tools\netcoreapp2.0\designer\Avalonia.Designer.HostApp.dll</AvaloniaPreviewerNetCoreToolPath>
<AvaloniaPreviewerNetFullToolPath>$(MSBuildThisFileDirectory)\..\tools\net461\designer\Avalonia.Designer.HostApp.exe</AvaloniaPreviewerNetFullToolPath> <AvaloniaPreviewerNetFullToolPath>$(MSBuildThisFileDirectory)\..\tools\net461\designer\Avalonia.Designer.HostApp.exe</AvaloniaPreviewerNetFullToolPath>
<AvaloniaBuildTasksLocation>$(MSBuildThisFileDirectory)\..\tools\Avalonia.Build.Tasks.dll</AvaloniaBuildTasksLocation> <AvaloniaBuildTasksLocation>$(MSBuildThisFileDirectory)\..\tools\netstandard2.0\Avalonia.Build.Tasks.dll</AvaloniaBuildTasksLocation>
<AvaloniaUseExternalMSBuild>false</AvaloniaUseExternalMSBuild> <AvaloniaUseExternalMSBuild>false</AvaloniaUseExternalMSBuild>
</PropertyGroup> </PropertyGroup>
<Import Project="$(MSBuildThisFileDirectory)\AvaloniaBuildTasks.props"/> <Import Project="$(MSBuildThisFileDirectory)\AvaloniaBuildTasks.props"/>

35
samples/RenderDemo/Pages/AnimationsPage.xaml

@ -43,7 +43,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style Selector="Border.Rect1:pointerover"> <Style Selector="Border.Rect1:pointerover">
<Style.Animations> <Style.Animations>
<Animation Duration="0:0:2.5" <Animation Duration="0:0:2.5"
RepeatCount="4" IterationCount="4"
FillMode="None" FillMode="None"
PlaybackDirection="AlternateReverse" PlaybackDirection="AlternateReverse"
Easing="SineEaseInOut"> Easing="SineEaseInOut">
@ -73,7 +73,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style.Animations> <Style.Animations>
<Animation Duration="0:0:0.5" <Animation Duration="0:0:0.5"
Easing="QuadraticEaseInOut" Easing="QuadraticEaseInOut"
RepeatCount="Loop"> IterationCount="Infinite">
<KeyFrame Cue="50%"> <KeyFrame Cue="50%">
<Setter Property="ScaleTransform.ScaleX" Value="0.8"/> <Setter Property="ScaleTransform.ScaleX" Value="0.8"/>
<Setter Property="ScaleTransform.ScaleY" Value="0.8"/> <Setter Property="ScaleTransform.ScaleY" Value="0.8"/>
@ -87,6 +87,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Animation Duration="0:0:3" Easing="BounceEaseInOut"> <Animation Duration="0:0:3" Easing="BounceEaseInOut">
<KeyFrame Cue="48%"> <KeyFrame Cue="48%">
<Setter Property="TranslateTransform.Y" Value="-100"/> <Setter Property="TranslateTransform.Y" Value="-100"/>
<Setter Property="Background" Value="Magenta"/>
</KeyFrame> </KeyFrame>
</Animation> </Animation>
</Style.Animations> </Style.Animations>
@ -103,6 +104,35 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
</Animation> </Animation>
</Style.Animations> </Style.Animations>
</Style> </Style>
<Style Selector="Border.Rect6">
<Style.Animations>
<Animation Duration="0:0:3"
IterationCount="Infinite"
PlaybackDirection="Alternate">
<KeyFrame Cue="0%">
<Setter Property="Background" Value="Red"/>
</KeyFrame>
<KeyFrame Cue="15%">
<Setter Property="Background" Value="Yellow"/>
</KeyFrame>
<KeyFrame Cue="30%">
<Setter Property="Background" Value="Green"/>
</KeyFrame>
<KeyFrame Cue="45%">
<Setter Property="Background" Value="Cyan"/>
</KeyFrame>
<KeyFrame Cue="60%">
<Setter Property="Background" Value="Blue"/>
</KeyFrame>
<KeyFrame Cue="75%">
<Setter Property="Background" Value="Indigo"/>
</KeyFrame>
<KeyFrame Cue="90%">
<Setter Property="Background" Value="Violet"/>
</KeyFrame>
</Animation>
</Style.Animations>
</Style>
</Styles> </Styles>
</UserControl.Styles> </UserControl.Styles>
<Grid> <Grid>
@ -120,6 +150,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Border Classes="Test Rect3"/> <Border Classes="Test Rect3"/>
<Border Classes="Test Rect4" Background="Navy"/> <Border Classes="Test Rect4" Background="Navy"/>
<Border Classes="Test Rect5" Background="SeaGreen"/> <Border Classes="Test Rect5" Background="SeaGreen"/>
<Border Classes="Test Rect6" Background="Red"/>
</WrapPanel> </WrapPanel>
</StackPanel> </StackPanel>
</Grid> </Grid>

2
samples/RenderDemo/Pages/ClippingPage.xaml

@ -8,7 +8,7 @@ xmlns="https://github.com/avaloniaui">
</Style> </Style>
<Style Selector="Border#clipChild"> <Style Selector="Border#clipChild">
<Style.Animations> <Style.Animations>
<Animation Duration="0:0:2" RepeatCount="Loop"> <Animation Duration="0:0:2" IterationCount="Infinite">
<KeyFrame Cue="100%"> <KeyFrame Cue="100%">
<Setter Property="RotateTransform.Angle" Value="360"/> <Setter Property="RotateTransform.Angle" Value="360"/>
</KeyFrame> </KeyFrame>

1
src/Avalonia.Animation/Animatable.cs

@ -7,6 +7,7 @@ using System.Linq;
using System.Reactive.Linq; using System.Reactive.Linq;
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Animation.Animators;
namespace Avalonia.Animation namespace Avalonia.Animation
{ {

199
src/Avalonia.Animation/Animation.cs

@ -7,68 +7,209 @@ using System.Linq;
using System.Reactive.Disposables; using System.Reactive.Disposables;
using System.Reactive.Linq; using System.Reactive.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia.Animation.Animators;
using Avalonia.Animation.Easings; using Avalonia.Animation.Easings;
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Data;
using Avalonia.Metadata;
namespace Avalonia.Animation namespace Avalonia.Animation
{ {
/// <summary> /// <summary>
/// Tracks the progress of an animation. /// Tracks the progress of an animation.
/// </summary> /// </summary>
public class Animation : AvaloniaList<KeyFrame>, IAnimation public class Animation : AvaloniaObject, IAnimation
{ {
/// <summary>
/// Defines the <see cref="Duration"/> property.
/// </summary>
public static readonly DirectProperty<Animation, TimeSpan> DurationProperty =
AvaloniaProperty.RegisterDirect<Animation, TimeSpan>(
nameof(_duration),
o => o._duration,
(o, v) => o._duration = v);
/// <summary>
/// Defines the <see cref="IterationCount"/> property.
/// </summary>
public static readonly DirectProperty<Animation, IterationCount> IterationCountProperty =
AvaloniaProperty.RegisterDirect<Animation, IterationCount>(
nameof(_iterationCount),
o => o._iterationCount,
(o, v) => o._iterationCount = v);
/// <summary>
/// Defines the <see cref="PlaybackDirection"/> property.
/// </summary>
public static readonly DirectProperty<Animation, PlaybackDirection> PlaybackDirectionProperty =
AvaloniaProperty.RegisterDirect<Animation, PlaybackDirection>(
nameof(_playbackDirection),
o => o._playbackDirection,
(o, v) => o._playbackDirection = v);
/// <summary>
/// Defines the <see cref="FillMode"/> property.
/// </summary>
public static readonly DirectProperty<Animation, FillMode> FillModeProperty =
AvaloniaProperty.RegisterDirect<Animation, FillMode>(
nameof(_fillMode),
o => o._fillMode,
(o, v) => o._fillMode = v);
/// <summary>
/// Defines the <see cref="Easing"/> property.
/// </summary>
public static readonly DirectProperty<Animation, Easing> EasingProperty =
AvaloniaProperty.RegisterDirect<Animation, Easing>(
nameof(_easing),
o => o._easing,
(o, v) => o._easing = v);
/// <summary>
/// Defines the <see cref="Delay"/> property.
/// </summary>
public static readonly DirectProperty<Animation, TimeSpan> DelayProperty =
AvaloniaProperty.RegisterDirect<Animation, TimeSpan>(
nameof(_delay),
o => o._delay,
(o, v) => o._delay = v);
/// <summary>
/// Defines the <see cref="DelayBetweenIterations"/> property.
/// </summary>
public static readonly DirectProperty<Animation, TimeSpan> DelayBetweenIterationsProperty =
AvaloniaProperty.RegisterDirect<Animation, TimeSpan>(
nameof(_delayBetweenIterations),
o => o._delayBetweenIterations,
(o, v) => o._delayBetweenIterations = v);
/// <summary>
/// Defines the <see cref="SpeedRatio"/> property.
/// </summary>
public static readonly DirectProperty<Animation, double> SpeedRatioProperty =
AvaloniaProperty.RegisterDirect<Animation, double>(
nameof(_speedRatio),
o => o._speedRatio,
(o, v) => o._speedRatio = v,
defaultBindingMode: BindingMode.TwoWay);
private TimeSpan _duration;
private IterationCount _iterationCount = new IterationCount(1);
private PlaybackDirection _playbackDirection;
private FillMode _fillMode;
private Easing _easing = new LinearEasing();
private TimeSpan _delay = TimeSpan.Zero;
private TimeSpan _delayBetweenIterations = TimeSpan.Zero;
private double _speedRatio = 1d;
/// <summary> /// <summary>
/// Gets or sets the active time of this animation. /// Gets or sets the active time of this animation.
/// </summary> /// </summary>
public TimeSpan Duration { get; set; } public TimeSpan Duration
{
get { return _duration; }
set { SetAndRaise(DurationProperty, ref _duration, value); }
}
/// <summary> /// <summary>
/// Gets or sets the repeat count for this animation. /// Gets or sets the repeat count for this animation.
/// </summary> /// </summary>
public RepeatCount RepeatCount { get; set; } public IterationCount IterationCount
{
get { return _iterationCount; }
set { SetAndRaise(IterationCountProperty, ref _iterationCount, value); }
}
/// <summary> /// <summary>
/// Gets or sets the playback direction for this animation. /// Gets or sets the playback direction for this animation.
/// </summary> /// </summary>
public PlaybackDirection PlaybackDirection { get; set; } public PlaybackDirection PlaybackDirection
{
get { return _playbackDirection; }
set { SetAndRaise(PlaybackDirectionProperty, ref _playbackDirection, value); }
}
/// <summary> /// <summary>
/// Gets or sets the value fill mode for this animation. /// Gets or sets the value fill mode for this animation.
/// </summary> /// </summary>
public FillMode FillMode { get; set; } public FillMode FillMode
{
get { return _fillMode; }
set { SetAndRaise(FillModeProperty, ref _fillMode, value); }
}
/// <summary> /// <summary>
/// Gets or sets the easing function to be used for this animation. /// Gets or sets the easing function to be used for this animation.
/// </summary> /// </summary>
public Easing Easing { get; set; } = new LinearEasing(); public Easing Easing
{
/// <summary> get { return _easing; }
/// Gets or sets the speed multiple for this animation. set { SetAndRaise(EasingProperty, ref _easing, value); }
/// </summary> }
public double SpeedRatio { get; set; } = 1d;
/// <summary> /// <summary>
/// Gets or sets the delay time for this animation. /// Gets or sets the initial delay time for this animation.
/// </summary> /// </summary>
/// <remarks> public TimeSpan Delay
/// Describes a delay to be added before the animation starts, and optionally between {
/// repeats of the animation if <see cref="DelayBetweenIterations"/> is set. get { return _delay; }
/// </remarks> set { SetAndRaise(DelayProperty, ref _delay, value); }
public TimeSpan Delay { get; set; } }
/// <summary> /// <summary>
/// Gets or sets a value indicating whether <see cref="Delay"/> will be applied between /// Gets or sets the delay time in between iterations.
/// iterations of the animation. /// </summary>
public TimeSpan DelayBetweenIterations
{
get { return _delayBetweenIterations; }
set { SetAndRaise(DelayBetweenIterationsProperty, ref _delayBetweenIterations, value); }
}
/// <summary>
/// Gets or sets the speed multiple for this animation.
/// </summary> /// </summary>
/// <remarks> public double SpeedRatio
/// If this property is not set, then <see cref="Delay"/> will only be applied to the first {
/// iteration of the animation. get { return _speedRatio; }
/// </remarks> set { SetAndRaise(SpeedRatioProperty, ref _speedRatio, value); }
public bool DelayBetweenIterations { get; set; } }
/// <summary>
/// Obsolete: Do not use this property, use <see cref="IterationCount"/> instead.
/// </summary>
/// <value></value>
[Obsolete("This property has been superceded by IterationCount.")]
public string RepeatCount
{
get { return IterationCount.ToString(); }
set
{
var val = value.ToUpper();
val = val.Replace("LOOP", "INFINITE");
val = val.Replace("NONE", "1");
IterationCount = IterationCount.Parse(val);
}
}
/// <summary>
/// Gets the children of the <see cref="Animation"/>.
/// </summary>
[Content]
public KeyFrames Children { get; } = new KeyFrames();
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(bool).IsAssignableFrom(prop.PropertyType), typeof(BoolAnimator) ),
( prop => typeof(byte).IsAssignableFrom(prop.PropertyType), typeof(ByteAnimator) ),
( prop => typeof(Int16).IsAssignableFrom(prop.PropertyType), typeof(Int16Animator) ),
( prop => typeof(Int32).IsAssignableFrom(prop.PropertyType), typeof(Int32Animator) ),
( prop => typeof(Int64).IsAssignableFrom(prop.PropertyType), typeof(Int64Animator) ),
( prop => typeof(UInt16).IsAssignableFrom(prop.PropertyType), typeof(UInt16Animator) ),
( prop => typeof(UInt32).IsAssignableFrom(prop.PropertyType), typeof(UInt32Animator) ),
( prop => typeof(UInt64).IsAssignableFrom(prop.PropertyType), typeof(UInt64Animator) ),
( prop => typeof(float).IsAssignableFrom(prop.PropertyType), typeof(FloatAnimator) ),
( prop => typeof(double).IsAssignableFrom(prop.PropertyType), typeof(DoubleAnimator) ),
( prop => typeof(decimal).IsAssignableFrom(prop.PropertyType), typeof(DecimalAnimator) ),
}; };
public static void RegisterAnimator<TAnimator>(Func<AvaloniaProperty, bool> condition) public static void RegisterAnimator<TAnimator>(Func<AvaloniaProperty, bool> condition)
@ -95,9 +236,9 @@ namespace Avalonia.Animation
var animatorKeyFrames = new List<AnimatorKeyFrame>(); var animatorKeyFrames = new List<AnimatorKeyFrame>();
var subscriptions = new List<IDisposable>(); var subscriptions = new List<IDisposable>();
foreach (var keyframe in this) foreach (var keyframe in Children)
{ {
foreach (var setter in keyframe) foreach (var setter in keyframe.Setters)
{ {
var handler = GetAnimatorType(setter.Property); var handler = GetAnimatorType(setter.Property);
@ -179,7 +320,7 @@ namespace Avalonia.Animation
{ {
var run = new TaskCompletionSource<object>(); var run = new TaskCompletionSource<object>();
if (this.RepeatCount == RepeatCount.Loop) if (this.IterationCount == IterationCount.Infinite)
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;

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

@ -1,6 +1,7 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Reactive.Linq; using System.Reactive.Linq;
using Avalonia.Animation.Animators;
using Avalonia.Animation.Utils; using Avalonia.Animation.Utils;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Reactive; using Avalonia.Reactive;
@ -15,78 +16,77 @@ namespace Avalonia.Animation
{ {
private T _lastInterpValue; private T _lastInterpValue;
private T _firstKFValue; private T _firstKFValue;
private long _repeatCount; private ulong? _iterationCount;
private double _currentIteration; private ulong _currentIteration;
private bool _isLooping;
private bool _gotFirstKFValue; private bool _gotFirstKFValue;
private bool _iterationDelay;
private FillMode _fillMode; private FillMode _fillMode;
private PlaybackDirection _animationDirection; private PlaybackDirection _playbackDirection;
private Animator<T> _parent; private Animator<T> _animator;
private Animation _animation;
private Animatable _targetControl; private Animatable _targetControl;
private T _neutralValue; private T _neutralValue;
private double _speedRatio; private double _speedRatioConv;
private TimeSpan _delay; private TimeSpan _initialDelay;
private TimeSpan _iterationDelay;
private TimeSpan _duration; private TimeSpan _duration;
private Easings.Easing _easeFunc; private Easings.Easing _easeFunc;
private Action _onCompleteAction; private Action _onCompleteAction;
private Func<double, T, T> _interpolator; private Func<double, T, T> _interpolator;
private IDisposable _timerSubscription; private IDisposable _timerSub;
private readonly IClock _baseClock; private readonly IClock _baseClock;
private IClock _clock; private IClock _clock;
public AnimationInstance(Animation animation, Animatable control, Animator<T> animator, IClock baseClock, Action OnComplete, Func<double, T, T> Interpolator) public AnimationInstance(Animation animation, Animatable control, Animator<T> animator, IClock baseClock, Action OnComplete, Func<double, T, T> Interpolator)
{ {
if (animation.SpeedRatio <= 0) _animator = animator;
throw new InvalidOperationException("Speed ratio cannot be negative or zero."); _animation = animation;
_targetControl = control;
_onCompleteAction = OnComplete;
_interpolator = Interpolator;
_baseClock = baseClock;
_neutralValue = (T)_targetControl.GetValue(_animator.Property);
if (animation.Duration.TotalSeconds <= 0) FetchProperties();
throw new InvalidOperationException("Duration cannot be negative or zero."); }
_parent = animator; private void FetchProperties()
_easeFunc = animation.Easing; {
_targetControl = control; if (_animation.SpeedRatio < 0d)
_neutralValue = (T)_targetControl.GetValue(_parent.Property); throw new ArgumentOutOfRangeException("SpeedRatio value should not be negative.");
_speedRatio = animation.SpeedRatio; if (_animation.Duration.TotalSeconds <= 0)
throw new InvalidOperationException("Duration value cannot be negative or zero.");
_delay = animation.Delay; _easeFunc = _animation.Easing;
_duration = animation.Duration;
_iterationDelay = animation.DelayBetweenIterations;
switch (animation.RepeatCount.RepeatType) _speedRatioConv = 1d / _animation.SpeedRatio;
{
case RepeatType.None:
_repeatCount = 1;
break;
case RepeatType.Loop:
_isLooping = true;
break;
case RepeatType.Repeat:
_repeatCount = (long)animation.RepeatCount.Value;
break;
}
_animationDirection = animation.PlaybackDirection; _initialDelay = _animation.Delay;
_fillMode = animation.FillMode; _duration = _animation.Duration;
_onCompleteAction = OnComplete; _iterationDelay = _animation.DelayBetweenIterations;
_interpolator = Interpolator;
_baseClock = baseClock; if (_animation.IterationCount.RepeatType == IterationType.Many)
} _iterationCount = _animation.IterationCount.Value;
else
_iterationCount = null;
_playbackDirection = _animation.PlaybackDirection;
_fillMode = _animation.FillMode;
}
protected override void Unsubscribed() protected override void Unsubscribed()
{ {
//Animation may have been stopped before it has finished // Animation may have been stopped before it has finished.
ApplyFinalFill(); ApplyFinalFill();
_timerSubscription?.Dispose(); _timerSub?.Dispose();
_clock.PlayState = PlayState.Stop; _clock.PlayState = PlayState.Stop;
} }
protected override void Subscribed() protected override void Subscribed()
{ {
_clock = new Clock(_baseClock); _clock = new Clock(_baseClock);
_timerSubscription = _clock.Subscribe(Step); _timerSub = _clock.Subscribe(Step);
} }
public void Step(TimeSpan frameTick) public void Step(TimeSpan frameTick)
@ -104,7 +104,7 @@ namespace Avalonia.Animation
private void ApplyFinalFill() private void ApplyFinalFill()
{ {
if (_fillMode == FillMode.Forward || _fillMode == FillMode.Both) if (_fillMode == FillMode.Forward || _fillMode == FillMode.Both)
_targetControl.SetValue(_parent.Property, _lastInterpValue, BindingPriority.LocalValue); _targetControl.SetValue(_animator.Property, _lastInterpValue, BindingPriority.LocalValue);
} }
private void DoComplete() private void DoComplete()
@ -130,7 +130,7 @@ namespace Avalonia.Animation
if (!_gotFirstKFValue) if (!_gotFirstKFValue)
{ {
_firstKFValue = (T)_parent.First().Value; _firstKFValue = (T)_animator.First().Value;
_gotFirstKFValue = true; _gotFirstKFValue = true;
} }
} }
@ -138,75 +138,77 @@ namespace Avalonia.Animation
private void InternalStep(TimeSpan time) private void InternalStep(TimeSpan time)
{ {
DoPlayStates(); DoPlayStates();
var delayEndpoint = _delay;
var iterationEndpoint = delayEndpoint + _duration;
var iterationTime = time;
//determine if time is currently in the first iteration. FetchProperties();
if (time >= TimeSpan.Zero & time <= iterationEndpoint)
{
_currentIteration = 1;
}
else if (time > iterationEndpoint)
{
//Subtract first iteration to properly get the subsequent iteration time
iterationTime -= iterationEndpoint;
if (!_iterationDelay & delayEndpoint > TimeSpan.Zero) // Scale timebases according to speedratio.
{ var indexTime = time.Ticks;
delayEndpoint = TimeSpan.Zero; var iterDuration = _duration.Ticks * _speedRatioConv;
iterationEndpoint = _duration; var iterDelay = _iterationDelay.Ticks * _speedRatioConv;
} var initDelay = _initialDelay.Ticks * _speedRatioConv;
//Calculate the current iteration number
_currentIteration = Math.Min(_repeatCount,(int)Math.Floor((double)((double)iterationTime.Ticks / iterationEndpoint.Ticks)) + 2);
}
else
{
return;
}
// Determine if the current iteration should have its normalized time inverted. if (indexTime > 0 & indexTime <= initDelay)
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 (!_isLooping)
{
var totalTime = _iterationDelay ? _repeatCount * ( _duration.Ticks + _delay.Ticks) : _repeatCount * _duration.Ticks + _delay.Ticks;
if (time.Ticks >= totalTime)
{
var easedTime = _easeFunc.Ease(isCurIterReverse ? 0.0 : 1.0);
_lastInterpValue = _interpolator(easedTime, _neutralValue);
DoComplete();
return;
}
}
iterationTime = TimeSpan.FromTicks((long)(iterationTime.Ticks % iterationEndpoint.Ticks));
if (delayEndpoint > TimeSpan.Zero & iterationTime < delayEndpoint)
{ {
DoDelay(); DoDelay();
} }
else else
{ {
// Offset the delay time // Calculate timebases.
iterationTime -= delayEndpoint; var iterationTime = iterDuration + iterDelay;
iterationEndpoint -= delayEndpoint; var opsTime = indexTime - initDelay;
var playbackTime = opsTime % iterationTime;
// Normalize time _currentIteration = (ulong)(opsTime / iterationTime);
var interpVal = (double)iterationTime.Ticks / iterationEndpoint.Ticks;
if (isCurIterReverse) // Stop animation when the current iteration is beyond the iteration count.
interpVal = 1 - interpVal; if ((_currentIteration + 1) > _iterationCount)
DoComplete();
// Ease and interpolate if (playbackTime <= iterDuration)
var easedTime = _easeFunc.Ease(interpVal); {
_lastInterpValue = _interpolator(easedTime, _neutralValue); // Normalize time for interpolation.
var normalizedTime = playbackTime / iterDuration;
// Check if normalized time needs to be reversed according to PlaybackDirection
bool playbackReversed;
switch (_playbackDirection)
{
case PlaybackDirection.Normal:
playbackReversed = false;
break;
case PlaybackDirection.Reverse:
playbackReversed = true;
break;
case PlaybackDirection.Alternate:
playbackReversed = (_currentIteration % 2 == 0) ? false : true;
break;
case PlaybackDirection.AlternateReverse:
playbackReversed = (_currentIteration % 2 == 0) ? true : false;
break;
default:
throw new InvalidOperationException($"Animation direction value is unknown: {_playbackDirection}");
}
if (playbackReversed)
normalizedTime = 1 - normalizedTime;
// Ease and interpolate
var easedTime = _easeFunc.Ease(normalizedTime);
_lastInterpValue = _interpolator(easedTime, _neutralValue);
PublishNext(_lastInterpValue); PublishNext(_lastInterpValue);
}
else if (playbackTime > iterDuration &
playbackTime <= iterationTime &
iterDelay > 0)
{
// The last iteration's trailing delay should be skipped.
if ((_currentIteration + 1) < _iterationCount)
DoDelay();
else
DoComplete();
}
} }
} }
} }

73
src/Avalonia.Animation/Animator`1.cs → src/Avalonia.Animation/Animators/Animator`1.cs

@ -10,10 +10,10 @@ using Avalonia.Collections;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Reactive; using Avalonia.Reactive;
namespace Avalonia.Animation namespace Avalonia.Animation.Animators
{ {
/// <summary> /// <summary>
/// Base class for KeyFrames objects /// Base class for <see cref="Animator{T}"/> objects
/// </summary> /// </summary>
public abstract class Animator<T> : AvaloniaList<AnimatorKeyFrame>, IAnimator public abstract class Animator<T> : AvaloniaList<AnimatorKeyFrame>, IAnimator
{ {
@ -45,17 +45,10 @@ namespace Avalonia.Animation
return match.Subscribe(subject); return match.Subscribe(subject);
} }
/// <summary> protected T InterpolationHandler(double animationTime, T neutralValue)
/// Get the nearest pair of cue-time ordered keyframes
/// according to the given time parameter that is relative to the
/// total animation time and the normalized intra-keyframe pair time
/// (i.e., the normalized time between the selected keyframes, relative to the
/// time parameter).
/// </summary>
/// <param name="animationTime">The time parameter, relative to the total animation time</param>
protected (double IntraKFTime, KeyFramePair<T> KFPair) GetKFPairAndIntraKFTime(double animationTime)
{ {
AnimatorKeyFrame firstKeyframe, lastKeyframe; AnimatorKeyFrame firstKeyframe, lastKeyframe;
int kvCount = _convertedKeyframes.Count; int kvCount = _convertedKeyframes.Count;
if (kvCount > 2) if (kvCount > 2)
{ {
@ -84,38 +77,31 @@ namespace Avalonia.Animation
double t0 = firstKeyframe.Cue.CueValue; double t0 = firstKeyframe.Cue.CueValue;
double t1 = lastKeyframe.Cue.CueValue; double t1 = lastKeyframe.Cue.CueValue;
var intraframeTime = (animationTime - t0) / (t1 - t0);
var firstFrameData = (firstKeyframe.GetTypedValue<T>(), firstKeyframe.isNeutral); double progress = (animationTime - t0) / (t1 - t0);
var lastFrameData = (lastKeyframe.GetTypedValue<T>(), lastKeyframe.isNeutral);
return (intraframeTime, new KeyFramePair<T>(firstFrameData, lastFrameData)); T oldValue, newValue;
if (firstKeyframe.isNeutral)
oldValue = neutralValue;
else
oldValue = (T)firstKeyframe.Value;
if (lastKeyframe.isNeutral)
newValue = neutralValue;
else
newValue = (T)lastKeyframe.Value;
return Interpolate(progress, oldValue, newValue);
} }
private int FindClosestBeforeKeyFrame(double time) private int FindClosestBeforeKeyFrame(double time)
{ {
int FindClosestBeforeKeyFrame(int startIndex, int length) for (int i = 0; i < _convertedKeyframes.Count; i++)
{ if (_convertedKeyframes[i].Cue.CueValue > time)
if (length == 0 || length == 1) return i - 1;
{
return startIndex;
}
int middle = startIndex + (length / 2); throw new Exception("Index time is out of keyframe time range.");
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>
@ -129,18 +115,15 @@ namespace Avalonia.Animation
this, this,
clock ?? control.Clock ?? Clock.GlobalClock, clock ?? control.Clock ?? Clock.GlobalClock,
onComplete, onComplete,
DoInterpolation); InterpolationHandler);
return control.Bind<T>((AvaloniaProperty<T>)Property, instance, BindingPriority.Animation); return control.Bind<T>((AvaloniaProperty<T>)Property, instance, BindingPriority.Animation);
} }
/// <summary> /// <summary>
/// Interpolates a value given the desired time. /// Interpolates in-between two key values given the desired progress time.
/// </summary> /// </summary>
protected abstract T DoInterpolation(double time, T neutralValue); public abstract T Interpolate(double progress, T oldValue, T newValue);
/// <summary>
/// Verifies, converts and sorts keyframe values according to this class's target type.
/// </summary>
private void VerifyConvertKeyFrames() private void VerifyConvertKeyFrames()
{ {
foreach (AnimatorKeyFrame keyframe in this) foreach (AnimatorKeyFrame keyframe in this)
@ -188,4 +171,4 @@ namespace Avalonia.Animation
} }
} }
} }
} }

21
src/Avalonia.Animation/Animators/BoolAnimator.cs

@ -0,0 +1,21 @@
// 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.Animators
{
/// <summary>
/// Animator that handles <see cref="bool"/> properties.
/// </summary>
public class BoolAnimator : Animator<bool>
{
/// <inheritdocs/>
public override bool Interpolate(double progress, bool oldValue, bool newValue)
{
if(progress >= 1d)
return newValue;
if(progress >= 0)
return oldValue;
return oldValue;
}
}
}

24
src/Avalonia.Animation/Animators/ByteAnimator.cs

@ -0,0 +1,24 @@
// 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;
namespace Avalonia.Animation.Animators
{
/// <summary>
/// Animator that handles <see cref="byte"/> properties.
/// </summary>
public class ByteAnimator : Animator<byte>
{
const double maxVal = (double)byte.MaxValue;
/// <inheritdocs/>
public override byte Interpolate(double progress, byte oldValue, byte newValue)
{
var normOV = oldValue / maxVal;
var normNV = newValue / maxVal;
var deltaV = normNV - normOV;
return (byte)Math.Round(maxVal * ((deltaV * progress) + normOV));
}
}
}

17
src/Avalonia.Animation/Animators/DecimalAnimator.cs

@ -0,0 +1,17 @@
// 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.Animators
{
/// <summary>
/// Animator that handles <see cref="decimal"/> properties.
/// </summary>
public class DecimalAnimator : Animator<decimal>
{
/// <inheritdocs/>
public override decimal Interpolate(double progress, decimal oldValue, decimal newValue)
{
return ((newValue - oldValue) * (decimal)progress) + oldValue;
}
}
}

17
src/Avalonia.Animation/Animators/DoubleAnimator.cs

@ -0,0 +1,17 @@
// 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.Animators
{
/// <summary>
/// Animator that handles <see cref="double"/> properties.
/// </summary>
public class DoubleAnimator : Animator<double>
{
/// <inheritdocs/>
public override double Interpolate(double progress, double oldValue, double newValue)
{
return ((newValue - oldValue) * progress) + oldValue;
}
}
}

17
src/Avalonia.Animation/Animators/FloatAnimator.cs

@ -0,0 +1,17 @@
// 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.Animators
{
/// <summary>
/// Animator that handles <see cref="float"/> properties.
/// </summary>
public class FloatAnimator : Animator<float>
{
/// <inheritdocs/>
public override float Interpolate(double progress, float oldValue, float newValue)
{
return (float)(((newValue - oldValue) * progress) + oldValue);
}
}
}

24
src/Avalonia.Animation/Animators/Int16Animator.cs

@ -0,0 +1,24 @@
// 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;
namespace Avalonia.Animation.Animators
{
/// <summary>
/// Animator that handles <see cref="Int16"/> properties.
/// </summary>
public class Int16Animator : Animator<Int16>
{
const double maxVal = (double)Int16.MaxValue;
/// <inheritdocs/>
public override Int16 Interpolate(double progress, Int16 oldValue, Int16 newValue)
{
var normOV = oldValue / maxVal;
var normNV = newValue / maxVal;
var deltaV = normNV - normOV;
return (Int16)Math.Round(maxVal * ((deltaV * progress) + normOV));
}
}
}

24
src/Avalonia.Animation/Animators/Int32Animator.cs

@ -0,0 +1,24 @@
// 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;
namespace Avalonia.Animation.Animators
{
/// <summary>
/// Animator that handles <see cref="Int32"/> properties.
/// </summary>
public class Int32Animator : Animator<Int32>
{
const double maxVal = (double)Int32.MaxValue;
/// <inheritdocs/>
public override Int32 Interpolate(double progress, Int32 oldValue, Int32 newValue)
{
var normOV = oldValue / maxVal;
var normNV = newValue / maxVal;
var deltaV = normNV - normOV;
return (Int32)Math.Round(maxVal * ((deltaV * progress) + normOV));
}
}
}

24
src/Avalonia.Animation/Animators/Int64Animator.cs

@ -0,0 +1,24 @@
// 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;
namespace Avalonia.Animation.Animators
{
/// <summary>
/// Animator that handles <see cref="Int64"/> properties.
/// </summary>
public class Int64Animator : Animator<Int64>
{
const double maxVal = (double)Int64.MaxValue;
/// <inheritdocs/>
public override Int64 Interpolate(double progress, Int64 oldValue, Int64 newValue)
{
var normOV = oldValue / maxVal;
var normNV = newValue / maxVal;
var deltaV = normNV - normOV;
return (Int64)Math.Round(maxVal * ((deltaV * progress) + normOV));
}
}
}

24
src/Avalonia.Animation/Animators/UInt16Animator.cs

@ -0,0 +1,24 @@
// 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;
namespace Avalonia.Animation.Animators
{
/// <summary>
/// Animator that handles <see cref="UInt16"/> properties.
/// </summary>
public class UInt16Animator : Animator<UInt16>
{
const double maxVal = (double)UInt16.MaxValue;
/// <inheritdocs/>
public override UInt16 Interpolate(double progress, UInt16 oldValue, UInt16 newValue)
{
var normOV = oldValue / maxVal;
var normNV = newValue / maxVal;
var deltaV = normNV - normOV;
return (UInt16)Math.Round(maxVal * ((deltaV * progress) + normOV));
}
}
}

24
src/Avalonia.Animation/Animators/UInt32Animator.cs

@ -0,0 +1,24 @@
// 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;
namespace Avalonia.Animation.Animators
{
/// <summary>
/// Animator that handles <see cref="UInt32"/> properties.
/// </summary>
public class UInt32Animator : Animator<UInt32>
{
const double maxVal = (double)UInt32.MaxValue;
/// <inheritdocs/>
public override UInt32 Interpolate(double progress, UInt32 oldValue, UInt32 newValue)
{
var normOV = oldValue / maxVal;
var normNV = newValue / maxVal;
var deltaV = normNV - normOV;
return (UInt32)Math.Round(maxVal * ((deltaV * progress) + normOV));
}
}
}

24
src/Avalonia.Animation/Animators/UInt64Animator.cs

@ -0,0 +1,24 @@
// 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;
namespace Avalonia.Animation.Animators
{
/// <summary>
/// Animator that handles <see cref="UInt64"/> properties.
/// </summary>
public class UInt64Animator : Animator<UInt64>
{
const double maxVal = (double)UInt64.MaxValue;
/// <inheritdocs/>
public override UInt64 Interpolate(double progress, UInt64 oldValue, UInt64 newValue)
{
var normOV = oldValue / maxVal;
var normNV = newValue / maxVal;
var deltaV = normNV - normOV;
return (UInt64)Math.Round(maxVal * ((deltaV * progress) + normOV));
}
}
}

1
src/Avalonia.Animation/Avalonia.Animation.csproj

@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Avalonia.Base\Avalonia.Base.csproj" /> <ProjectReference Include="..\Avalonia.Base\Avalonia.Base.csproj" />

3
src/Avalonia.Animation/DisposeAnimationInstanceSubject.cs

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reactive.Linq; using System.Reactive.Linq;
using Avalonia.Animation.Animators;
using Avalonia.Animation.Utils; using Avalonia.Animation.Utils;
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Data; using Avalonia.Data;
@ -33,8 +34,6 @@ namespace Avalonia.Animation
this._onComplete = onComplete; this._onComplete = onComplete;
this._clock = clock; this._clock = clock;
} }
public void Dispose() public void Dispose()
{ {
_lastInstance?.Dispose(); _lastInstance?.Dispose();

35
src/Avalonia.Animation/DoubleAnimator.cs

@ -1,35 +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.
namespace Avalonia.Animation
{
/// <summary>
/// Animator that handles <see cref="double"/> properties.
/// </summary>
public class DoubleAnimator : Animator<double>
{
/// <inheritdocs/>
protected override double DoInterpolation(double t, double neutralValue)
{
var pair = GetKFPairAndIntraKFTime(t);
double y0, y1;
var firstKF = pair.KFPair.FirstKeyFrame;
var secondKF = pair.KFPair.SecondKeyFrame;
if (firstKF.isNeutral)
y0 = neutralValue;
else
y0 = firstKF.TargetValue;
if (secondKF.isNeutral)
y1 = neutralValue;
else
y1 = secondKF.TargetValue;
// Do linear parametric interpolation
return y0 + (pair.IntraKFTime) * (y1 - y0);
}
}
}

0
src/Avalonia.Animation/IEasing.cs → src/Avalonia.Animation/Easing/IEasing.cs

176
src/Avalonia.Animation/IterationCount.cs

@ -0,0 +1,176 @@
// 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.ComponentModel;
using System.Globalization;
namespace Avalonia.Animation
{
/// <summary>
/// Defines the valid modes for a <see cref="IterationCount"/>.
/// </summary>
public enum IterationType
{
Many,
Infinite
}
/// <summary>
/// Determines the number of iterations of an animation.
/// Also defines its repeat behavior.
/// </summary>
[TypeConverter(typeof(IterationCountTypeConverter))]
public struct IterationCount : IEquatable<IterationCount>
{
private readonly IterationType _type;
private readonly ulong _value;
/// <summary>
/// Initializes a new instance of the <see cref="IterationCount"/> struct.
/// </summary>
/// <param name="value">The number of iterations of an animation.</param>
public IterationCount(ulong value)
: this(value, IterationType.Many)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="IterationCount"/> struct.
/// </summary>
/// <param name="value">The size of the IterationCount.</param>
/// <param name="type">The unit of the IterationCount.</param>
public IterationCount(ulong value, IterationType type)
{
if (type > IterationType.Infinite)
{
throw new ArgumentException("Invalid value", "type");
}
_type = type;
_value = value;
}
/// <summary>
/// Gets an instance of <see cref="IterationCount"/> that indicates that an animation
/// should repeat forever.
/// </summary>
public static IterationCount Infinite => new IterationCount(0, IterationType.Infinite);
/// <summary>
/// Gets the unit of the <see cref="IterationCount"/>.
/// </summary>
public IterationType RepeatType => _type;
/// <summary>
/// Gets a value that indicates whether the <see cref="IterationCount"/> is set to loop.
/// </summary>
public bool IsInfinite => _type == IterationType.Infinite;
/// <summary>
/// Gets the number of repeat iterations.
/// </summary>
public ulong Value => _value;
/// <summary>
/// Compares two IterationCount structures for equality.
/// </summary>
/// <param name="a">The first IterationCount.</param>
/// <param name="b">The second IterationCount.</param>
/// <returns>True if the structures are equal, otherwise false.</returns>
public static bool operator ==(IterationCount a, IterationCount b)
{
return (a.IsInfinite && b.IsInfinite)
|| (a._value == b._value && a._type == b._type);
}
/// <summary>
/// Compares two IterationCount structures for inequality.
/// </summary>
/// <param name="rc1">The first IterationCount.</param>
/// <param name="rc2">The first IterationCount.</param>
/// <returns>True if the structures are unequal, otherwise false.</returns>
public static bool operator !=(IterationCount rc1, IterationCount rc2)
{
return !(rc1 == rc2);
}
/// <summary>
/// Determines whether the <see cref="IterationCount"/> is equal to the specified object.
/// </summary>
/// <param name="o">The object with which to test equality.</param>
/// <returns>True if the objects are equal, otherwise false.</returns>
public override bool Equals(object o)
{
if (o == null)
{
return false;
}
if (!(o is IterationCount))
{
return false;
}
return this == (IterationCount)o;
}
/// <summary>
/// Compares two IterationCount structures for equality.
/// </summary>
/// <param name="IterationCount">The structure with which to test equality.</param>
/// <returns>True if the structures are equal, otherwise false.</returns>
public bool Equals(IterationCount IterationCount)
{
return this == IterationCount;
}
/// <summary>
/// Gets a hash code for the IterationCount.
/// </summary>
/// <returns>The hash code.</returns>
public override int GetHashCode()
{
return _value.GetHashCode() ^ _type.GetHashCode();
}
/// <summary>
/// Gets a string representation of the <see cref="IterationCount"/>.
/// </summary>
/// <returns>The string representation.</returns>
public override string ToString()
{
if (IsInfinite)
{
return "Infinite";
}
string s = _value.ToString();
return s;
}
/// <summary>
/// Parses a string to return a <see cref="IterationCount"/>.
/// </summary>
/// <param name="s">The string.</param>
/// <returns>The <see cref="IterationCount"/>.</returns>
public static IterationCount Parse(string s)
{
s = s.ToUpperInvariant().Trim();
if (s.EndsWith("INFINITE"))
{
return Infinite;
}
else
{
if (s.StartsWith("-"))
throw new InvalidCastException("IterationCount can't be a negative number.");
var value = ulong.Parse(s, CultureInfo.InvariantCulture);
return new IterationCount(value);
}
}
}
}

4
src/Avalonia.Animation/RepeatCountTypeConverter.cs → src/Avalonia.Animation/IterationCountTypeConverter.cs

@ -7,7 +7,7 @@ using System.Globalization;
namespace Avalonia.Animation namespace Avalonia.Animation
{ {
public class RepeatCountTypeConverter : TypeConverter public class IterationCountTypeConverter : TypeConverter
{ {
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{ {
@ -16,7 +16,7 @@ namespace Avalonia.Animation
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{ {
return RepeatCount.Parse((string)value); return IterationCount.Parse((string)value);
} }
} }
} }

15
src/Avalonia.Animation/KeyFrame.cs

@ -4,6 +4,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Metadata;
namespace Avalonia.Animation namespace Avalonia.Animation
{ {
@ -17,7 +18,7 @@ namespace Avalonia.Animation
/// Stores data regarding a specific key /// Stores data regarding a specific key
/// point and value in an animation. /// point and value in an animation.
/// </summary> /// </summary>
public class KeyFrame : AvaloniaList<IAnimationSetter> public class KeyFrame : AvaloniaObject
{ {
private TimeSpan _ktimeSpan; private TimeSpan _ktimeSpan;
private Cue _kCue; private Cue _kCue;
@ -26,13 +27,11 @@ namespace Avalonia.Animation
{ {
} }
public KeyFrame(IEnumerable<IAnimationSetter> items) : base(items) /// <summary>
{ /// Gets the setters of <see cref="KeyFrame"/>.
} /// </summary>
[Content]
public KeyFrame(params IAnimationSetter[] items) : base(items) public AvaloniaList<IAnimationSetter> Setters { get; } = new AvaloniaList<IAnimationSetter>();
{
}
internal KeyFrameTimingMode TimingMode { get; private set; } internal KeyFrameTimingMode TimingMode { get; private set; }

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

@ -1,33 +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.
namespace Avalonia.Animation
{
/// <summary>
/// Represents a pair of keyframe, usually the
/// Start and End keyframes of a <see cref="Animator{T}"/> object.
/// </summary>
public struct KeyFramePair<T>
{
/// <summary>
/// Initializes this <see cref="KeyFramePair{T}"/>
/// </summary>
/// <param name="FirstKeyFrame"></param>
/// <param name="LastKeyFrame"></param>
public KeyFramePair((T TargetValue, bool isNeutral) FirstKeyFrame, (T TargetValue, bool isNeutral) LastKeyFrame) : this()
{
this.FirstKeyFrame = FirstKeyFrame;
this.SecondKeyFrame = LastKeyFrame;
}
/// <summary>
/// First <see cref="KeyFrame"/> object.
/// </summary>
public (T TargetValue, bool isNeutral) FirstKeyFrame { get; }
/// <summary>
/// Second <see cref="KeyFrame"/> object.
/// </summary>
public (T TargetValue, bool isNeutral) SecondKeyFrame { get; }
}
}

33
src/Avalonia.Animation/KeyFrames.cs

@ -0,0 +1,33 @@
// 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 Avalonia.Collections;
namespace Avalonia.Animation
{
/// <summary>
/// A collection of <see cref="KeyFrame"/>s.
/// </summary>
public class KeyFrames : AvaloniaList<KeyFrame>
{
/// <summary>
/// Initializes a new instance of the <see cref="KeyFrames"/> class.
/// </summary>
public KeyFrames()
{
ResetBehavior = ResetBehavior.Remove;
}
/// <summary>
/// Initializes a new instance of the <see cref="KeyFrames"/> class.
/// </summary>
/// <param name="items">The initial items in the collection.</param>
public KeyFrames(IEnumerable<KeyFrame> items)
: base(items)
{
ResetBehavior = ResetBehavior.Remove;
}
}
}

3
src/Avalonia.Animation/Properties/AssemblyInfo.cs

@ -5,4 +5,5 @@ using Avalonia.Metadata;
using System.Reflection; using System.Reflection;
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Animation")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Animation")]
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Animation.Easings")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Animation.Easings")]
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Animation.Animators")]

199
src/Avalonia.Animation/RepeatCount.cs

@ -1,199 +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.ComponentModel;
using System.Globalization;
namespace Avalonia.Animation
{
/// <summary>
/// Defines the valid modes for a <see cref="RepeatCount"/>.
/// </summary>
public enum RepeatType
{
None,
Repeat,
Loop
}
/// <summary>
/// Determines the number of iterations of an animation.
/// Also defines its repeat behavior.
/// </summary>
[TypeConverter(typeof(RepeatCountTypeConverter))]
public struct RepeatCount : IEquatable<RepeatCount>
{
private readonly RepeatType _type;
private readonly ulong _value;
/// <summary>
/// Initializes a new instance of the <see cref="RepeatCount"/> struct.
/// </summary>
/// <param name="value">The number of iterations of an animation.</param>
public RepeatCount(ulong value)
: this(value, RepeatType.Repeat)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="RepeatCount"/> struct.
/// </summary>
/// <param name="value">The size of the RepeatCount.</param>
/// <param name="type">The unit of the RepeatCount.</param>
public RepeatCount(ulong value, RepeatType type)
{
if (type < RepeatType.None || type > RepeatType.Loop)
{
throw new ArgumentException("Invalid value", "type");
}
_type = type;
_value = value;
}
/// <summary>
/// Gets an instance of <see cref="RepeatCount"/> that indicates that an animation
/// should repeat forever.
/// </summary>
public static RepeatCount Loop => new RepeatCount(0, RepeatType.Loop);
/// <summary>
/// Gets an instance of <see cref="RepeatCount"/> that indicates that an animation
/// should not repeat.
/// </summary>
public static RepeatCount None => new RepeatCount(0, RepeatType.None);
/// <summary>
/// Gets the unit of the <see cref="RepeatCount"/>.
/// </summary>
public RepeatType RepeatType => _type;
/// <summary>
/// Gets a value that indicates whether the <see cref="RepeatCount"/> is set to loop.
/// </summary>
public bool IsLoop => _type == RepeatType.Loop;
/// <summary>
/// Gets a value that indicates whether the <see cref="RepeatCount"/> is set to not repeat.
/// </summary>
public bool IsNone => _type == RepeatType.None;
/// <summary>
/// Gets the number of repeat iterations.
/// </summary>
public ulong Value => _value;
/// <summary>
/// Compares two RepeatCount structures for equality.
/// </summary>
/// <param name="a">The first RepeatCount.</param>
/// <param name="b">The second RepeatCount.</param>
/// <returns>True if the structures are equal, otherwise false.</returns>
public static bool operator ==(RepeatCount a, RepeatCount b)
{
return (a.IsNone && b.IsNone) && (a.IsLoop && b.IsLoop)
|| (a._value == b._value && a._type == b._type);
}
/// <summary>
/// Compares two RepeatCount structures for inequality.
/// </summary>
/// <param name="rc1">The first RepeatCount.</param>
/// <param name="rc2">The first RepeatCount.</param>
/// <returns>True if the structures are unequal, otherwise false.</returns>
public static bool operator !=(RepeatCount rc1, RepeatCount rc2)
{
return !(rc1 == rc2);
}
/// <summary>
/// Determines whether the <see cref="RepeatCount"/> is equal to the specified object.
/// </summary>
/// <param name="o">The object with which to test equality.</param>
/// <returns>True if the objects are equal, otherwise false.</returns>
public override bool Equals(object o)
{
if (o == null)
{
return false;
}
if (!(o is RepeatCount))
{
return false;
}
return this == (RepeatCount)o;
}
/// <summary>
/// Compares two RepeatCount structures for equality.
/// </summary>
/// <param name="RepeatCount">The structure with which to test equality.</param>
/// <returns>True if the structures are equal, otherwise false.</returns>
public bool Equals(RepeatCount RepeatCount)
{
return this == RepeatCount;
}
/// <summary>
/// Gets a hash code for the RepeatCount.
/// </summary>
/// <returns>The hash code.</returns>
public override int GetHashCode()
{
return _value.GetHashCode() ^ _type.GetHashCode();
}
/// <summary>
/// Gets a string representation of the <see cref="RepeatCount"/>.
/// </summary>
/// <returns>The string representation.</returns>
public override string ToString()
{
if (IsLoop)
{
return "Auto";
}
else if (IsNone)
{
return "None";
}
string s = _value.ToString();
return s;
}
/// <summary>
/// Parses a string to return a <see cref="RepeatCount"/>.
/// </summary>
/// <param name="s">The string.</param>
/// <returns>The <see cref="RepeatCount"/>.</returns>
public static RepeatCount Parse(string s)
{
s = s.ToUpperInvariant().Trim();
if (s == "NONE")
{
return None;
}
else if (s.EndsWith("LOOP"))
{
return Loop;
}
else
{
if(s.StartsWith("-"))
throw new InvalidCastException("RepeatCount can't be a negative number.");
var value = ulong.Parse(s, CultureInfo.InvariantCulture);
if (value == 1)
return None;
return new RepeatCount(value);
}
}
}
}

7
src/Avalonia.Animation/DoubleTransition.cs → src/Avalonia.Animation/Transitions/DoubleTransition.cs

@ -14,9 +14,12 @@ namespace Avalonia.Animation
/// <inheritdocs/> /// <inheritdocs/>
public override IObservable<double> DoTransition(IObservable<double> progress, double oldValue, double newValue) public override IObservable<double> DoTransition(IObservable<double> progress, double oldValue, double newValue)
{ {
var delta = newValue - oldValue;
return progress return progress
.Select(p => Easing.Ease(p) * delta + oldValue); .Select(p =>
{
var f = Easing.Ease(p);
return ((newValue - oldValue) * f) + oldValue;
});
} }
} }
} }

0
src/Avalonia.Animation/FloatTransition.cs → src/Avalonia.Animation/Transitions/FloatTransition.cs

0
src/Avalonia.Animation/IntegerTransition.cs → src/Avalonia.Animation/Transitions/IntegerTransition.cs

1
src/Avalonia.Base/Avalonia.Base.csproj

@ -4,7 +4,6 @@
<AssemblyName>Avalonia.Base</AssemblyName> <AssemblyName>Avalonia.Base</AssemblyName>
<RootNamespace>Avalonia</RootNamespace> <RootNamespace>Avalonia</RootNamespace>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks> <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Avalonia.Build.Tasks\Avalonia.Build.Tasks.csproj"/> <ProjectReference Include="..\Avalonia.Build.Tasks\Avalonia.Build.Tasks.csproj"/>

1
src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj

@ -4,7 +4,6 @@
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<BuildOutputTargetFolder>tools</BuildOutputTargetFolder> <BuildOutputTargetFolder>tools</BuildOutputTargetFolder>
<DefineConstants>$(DefineConstants);BUILDTASK</DefineConstants> <DefineConstants>$(DefineConstants);BUILDTASK</DefineConstants>
<Packable>false</Packable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

1
src/Avalonia.Controls/Avalonia.Controls.csproj

@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Avalonia.Animation\Avalonia.Animation.csproj" /> <ProjectReference Include="..\Avalonia.Animation\Avalonia.Animation.csproj" />

4
src/Avalonia.Controls/Button.cs

@ -253,10 +253,8 @@ namespace Avalonia.Controls
IsPressed = false; IsPressed = false;
e.Handled = true; e.Handled = true;
var hittest = this.GetVisualsAt(e.GetPosition(this));
if (ClickMode == ClickMode.Release && if (ClickMode == ClickMode.Release &&
hittest.Any(c => c == this || (c as IStyledElement)?.TemplatedParent == this)) this.GetVisualsAt(e.GetPosition(this)).Any(c => this == c || this.IsVisualAncestorOf(c)))
{ {
OnClick(); OnClick();
} }

1
src/Avalonia.Controls/Panel.cs

@ -31,7 +31,6 @@ namespace Avalonia.Controls
static Panel() static Panel()
{ {
AffectsRender<Panel>(BackgroundProperty); AffectsRender<Panel>(BackgroundProperty);
ClipToBoundsProperty.OverrideDefaultValue<Panel>(true);
} }
/// <summary> /// <summary>

1
src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj

@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<IsPackable>false</IsPackable>
<!-- WARNING! The designer support version number needs to be frozen <!-- WARNING! The designer support version number needs to be frozen
To allow projects that implement designer functionality to still To allow projects that implement designer functionality to still
work with newer versions of Avalonia. This version number only work with newer versions of Avalonia. This version number only

1
src/Avalonia.DesktopRuntime/Avalonia.DesktopRuntime.csproj

@ -2,7 +2,6 @@
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net461;netcoreapp2.0</TargetFrameworks> <TargetFrameworks>net461;netcoreapp2.0</TargetFrameworks>
<IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

1
src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj

@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj" /> <ProjectReference Include="..\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj" />

1
src/Avalonia.Input/Avalonia.Input.csproj

@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Avalonia.Animation\Avalonia.Animation.csproj" /> <ProjectReference Include="..\Avalonia.Animation\Avalonia.Animation.csproj" />

1
src/Avalonia.Interactivity/Avalonia.Interactivity.csproj

@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Avalonia.Animation\Avalonia.Animation.csproj" /> <ProjectReference Include="..\Avalonia.Animation\Avalonia.Animation.csproj" />

1
src/Avalonia.Layout/Avalonia.Layout.csproj

@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Avalonia.Animation\Avalonia.Animation.csproj" /> <ProjectReference Include="..\Avalonia.Animation\Avalonia.Animation.csproj" />

1
src/Avalonia.Logging.Serilog/Avalonia.Logging.Serilog.csproj

@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Avalonia.Base\Avalonia.Base.csproj" /> <ProjectReference Include="..\Avalonia.Base\Avalonia.Base.csproj" />

2
src/Avalonia.Native/PlatformThreadingInterface.cs

@ -68,8 +68,6 @@ namespace Avalonia.Native
lock (l) lock (l)
{ {
cancellation?.Cancel(); cancellation?.Cancel();
cancellation?.Dispose();
cancellation = null;
} }
}); });
try try

1
src/Avalonia.Styling/Avalonia.Styling.csproj

@ -3,7 +3,6 @@
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<AssemblyName>Avalonia.Styling</AssemblyName> <AssemblyName>Avalonia.Styling</AssemblyName>
<RootNamespace>Avalonia</RootNamespace> <RootNamespace>Avalonia</RootNamespace>
<IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Avalonia.Animation\Avalonia.Animation.csproj" /> <ProjectReference Include="..\Avalonia.Animation\Avalonia.Animation.csproj" />

1
src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj

@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj" /> <ProjectReference Include="..\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj" />

4
src/Avalonia.Themes.Default/ProgressBar.xaml

@ -33,7 +33,7 @@
<Style Selector="ProgressBar:horizontal:indeterminate /template/ Border#PART_Indicator"> <Style Selector="ProgressBar:horizontal:indeterminate /template/ Border#PART_Indicator">
<Style.Animations> <Style.Animations>
<Animation Duration="0:0:3" <Animation Duration="0:0:3"
RepeatCount="Loop" IterationCount="Infinite"
Easing="LinearEasing"> Easing="LinearEasing">
<KeyFrame Cue="0%"> <KeyFrame Cue="0%">
<Setter Property="TranslateTransform.X" <Setter Property="TranslateTransform.X"
@ -49,7 +49,7 @@
<Style Selector="ProgressBar:vertical:indeterminate /template/ Border#PART_Indicator"> <Style Selector="ProgressBar:vertical:indeterminate /template/ Border#PART_Indicator">
<Style.Animations> <Style.Animations>
<Animation Duration="0:0:3" <Animation Duration="0:0:3"
RepeatCount="Loop" IterationCount="Infinite"
Easing="LinearEasing"> Easing="LinearEasing">
<KeyFrame Cue="0%"> <KeyFrame Cue="0%">
<Setter Property="TranslateTransform.Y" <Setter Property="TranslateTransform.Y"

70
src/Avalonia.Visuals/Animation/Animators/ColorAnimator.cs

@ -0,0 +1,70 @@
// Original color interpolation code was written by Romain Guy and Francois Blavoet
// and adopted from LottieSharp Project (https://github.com/ascora/LottieSharp).
using System;
using System.Reactive.Disposables;
using Avalonia.Logging;
using Avalonia.Media;
namespace Avalonia.Animation.Animators
{
/// <summary>
/// Animator that interpolates <see cref="Color"/> through
/// gamma sRGB color space for better visual result.
/// </summary>
public class ColorAnimator : Animator<Color>
{
// Opto-electronic conversion function for the sRGB color space
// Takes a gamma-encoded sRGB value and converts it to a linear sRGB value
private static double OECF_sRGB(double linear)
{
// IEC 61966-2-1:1999
return linear <= 0.0031308d ? linear * 12.92d : (double)(Math.Pow(linear, 1.0d / 2.4d) * 1.055d - 0.055d);
}
// Electro-optical conversion function for the sRGB color space
// Takes a linear sRGB value and converts it to a gamma-encoded sRGB value
private static double EOCF_sRGB(double srgb)
{
// IEC 61966-2-1:1999
return srgb <= 0.04045d ? srgb / 12.92d : (double)Math.Pow((srgb + 0.055d) / 1.055d, 2.4d);
}
public override Color Interpolate(double progress, Color oldValue, Color newValue)
{
// normalize sRGB values.
var oldA = oldValue.A / 255d;
var oldR = oldValue.R / 255d;
var oldG = oldValue.G / 255d;
var oldB = oldValue.B / 255d;
var newA = newValue.A / 255d;
var newR = newValue.R / 255d;
var newG = newValue.G / 255d;
var newB = newValue.B / 255d;
// convert from sRGB to linear
oldR = EOCF_sRGB(oldR);
oldG = EOCF_sRGB(oldG);
oldB = EOCF_sRGB(oldB);
newR = EOCF_sRGB(newR);
newG = EOCF_sRGB(newG);
newB = EOCF_sRGB(newB);
// compute the interpolated color in linear space
var a = oldA + progress * (newA - oldA);
var r = oldR + progress * (newR - oldR);
var g = oldG + progress * (newG - oldG);
var b = oldB + progress * (newB - oldB);
// convert back to sRGB in the [0..255] range
a = a * 255d;
r = OECF_sRGB(r) * 255d;
g = OECF_sRGB(g) * 255d;
b = OECF_sRGB(b) * 255d;
return new Color((byte)Math.Round(a), (byte)Math.Round(r), (byte)Math.Round(g), (byte)Math.Round(b));
}
}
}

27
src/Avalonia.Visuals/Animation/Animators/CornerRadiusAnimator.cs

@ -0,0 +1,27 @@
using System;
using Avalonia.Logging;
using Avalonia.Media;
namespace Avalonia.Animation.Animators
{
/// <summary>
/// Animator that handles <see cref="CornerRadius"/> properties.
/// </summary>
public class CornerRadiusAnimator : Animator<CornerRadius>
{
public override CornerRadius Interpolate(double progress, CornerRadius oldValue, CornerRadius newValue)
{
var deltaTL = newValue.TopLeft - oldValue.TopLeft;
var deltaTR = newValue.TopRight - oldValue.TopRight;
var deltaBR = newValue.BottomRight - oldValue.BottomRight;
var deltaBL = newValue.BottomLeft - oldValue.BottomLeft;
var nTL = progress * deltaTL + oldValue.TopLeft;
var nTR = progress * deltaTR + oldValue.TopRight;
var nBR = progress * deltaBR + oldValue.BottomRight;
var nBL = progress * deltaBL + oldValue.BottomLeft;
return new CornerRadius(nTL, nTR, nBR, nBL);
}
}
}

17
src/Avalonia.Visuals/Animation/Animators/PointAnimator.cs

@ -0,0 +1,17 @@
using System;
using Avalonia.Logging;
using Avalonia.Media;
namespace Avalonia.Animation.Animators
{
/// <summary>
/// Animator that handles <see cref="Point"/> properties.
/// </summary>
public class PointAnimator : Animator<Point>
{
public override Point Interpolate(double progress, Point oldValue, Point newValue)
{
return ((newValue - oldValue) * progress) + oldValue;
}
}
}

23
src/Avalonia.Visuals/Animation/Animators/RectAnimator.cs

@ -0,0 +1,23 @@
using System;
using Avalonia.Logging;
using Avalonia.Media;
namespace Avalonia.Animation.Animators
{
/// <summary>
/// Animator that handles <see cref="Rect"/> properties.
/// </summary>
public class RectAnimator : Animator<Rect>
{
public override Rect Interpolate(double progress, Rect oldValue, Rect newValue)
{
var deltaPos = newValue.Position - oldValue.Position;
var deltaSize = newValue.Size - oldValue.Size;
var newPos = (deltaPos * progress) + oldValue.Position;
var newSize = (deltaSize * progress) + oldValue.Size;
return new Rect(newPos, newSize);
}
}
}

17
src/Avalonia.Visuals/Animation/Animators/SizeAnimator.cs

@ -0,0 +1,17 @@
using System;
using Avalonia.Logging;
using Avalonia.Media;
namespace Avalonia.Animation.Animators
{
/// <summary>
/// Animator that handles <see cref="Size"/> properties.
/// </summary>
public class SizeAnimator : Animator<Size>
{
public override Size Interpolate(double progress, Size oldValue, Size newValue)
{
return ((newValue - oldValue) * progress) + oldValue;
}
}
}

74
src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs

@ -0,0 +1,74 @@
using System;
using System.Reactive.Disposables;
using Avalonia.Logging;
using Avalonia.Media;
using Avalonia.Media.Immutable;
namespace Avalonia.Animation.Animators
{
/// <summary>
/// Animator that handles <see cref="SolidColorBrush"/>.
/// </summary>
public class SolidColorBrushAnimator : Animator<SolidColorBrush>
{
ColorAnimator _colorAnimator;
void InitializeColorAnimator()
{
_colorAnimator = new ColorAnimator();
foreach (AnimatorKeyFrame keyframe in this)
{
_colorAnimator.Add(keyframe);
}
_colorAnimator.Property = SolidColorBrush.ColorProperty;
}
public override IDisposable Apply(Animation animation, Animatable control, IClock clock, IObservable<bool> match, Action onComplete)
{
foreach (var keyframe in this)
{
if (keyframe.Value as ISolidColorBrush == null)
return Disposable.Empty;
// Preprocess keyframe values to Color if the xaml parser converts them to ISCB.
if (keyframe.Value.GetType() == typeof(ImmutableSolidColorBrush))
{
keyframe.Value = ((ImmutableSolidColorBrush)keyframe.Value).Color;
}
}
// Add SCB if the target prop is empty.
if (control.GetValue(Property) == null)
control.SetValue(Property, new SolidColorBrush(Colors.Transparent));
var targetVal = control.GetValue(Property);
// Continue if target prop is not empty & is a SolidColorBrush derivative.
if (typeof(ISolidColorBrush).IsAssignableFrom(targetVal.GetType()))
{
if (_colorAnimator == null)
InitializeColorAnimator();
SolidColorBrush finalTarget;
// If it's ISCB, change it back to SCB.
if (targetVal.GetType() == typeof(ImmutableSolidColorBrush))
{
var col = (ImmutableSolidColorBrush)targetVal;
targetVal = new SolidColorBrush(col.Color);
control.SetValue(Property, targetVal);
}
finalTarget = targetVal as SolidColorBrush;
return _colorAnimator.Apply(animation, finalTarget, clock ?? control.Clock, match, onComplete);
}
return Disposable.Empty;
}
public override SolidColorBrush Interpolate(double p, SolidColorBrush o, SolidColorBrush n) => null;
}
}

17
src/Avalonia.Visuals/Animation/Animators/ThicknessAnimator.cs

@ -0,0 +1,17 @@
using System;
using Avalonia.Logging;
using Avalonia.Media;
namespace Avalonia.Animation.Animators
{
/// <summary>
/// Animator that handles <see cref="Thickness"/> properties.
/// </summary>
public class ThicknessAnimator : Animator<Thickness>
{
public override Thickness Interpolate(double progress, Thickness oldValue, Thickness newValue)
{
return ((newValue - oldValue) * progress) + oldValue;
}
}
}

37
src/Avalonia.Visuals/Animation/TransformAnimator.cs → src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs

@ -2,14 +2,14 @@
using Avalonia.Logging; using Avalonia.Logging;
using Avalonia.Media; using Avalonia.Media;
namespace Avalonia.Animation namespace Avalonia.Animation.Animators
{ {
/// <summary> /// <summary>
/// Animator that handles <see cref="Transform"/> properties. /// Animator that handles <see cref="Transform"/> properties.
/// </summary> /// </summary>
public class TransformAnimator : Animator<double> public class TransformAnimator : Animator<double>
{ {
DoubleAnimator childAnimator; DoubleAnimator _doubleAnimator;
/// <inheritdoc/> /// <inheritdoc/>
public override IDisposable Apply(Animation animation, Animatable control, IClock clock, IObservable<bool> obsMatch, Action onComplete) public override IDisposable Apply(Animation animation, Animatable control, IClock clock, IObservable<bool> obsMatch, Action onComplete)
@ -27,7 +27,7 @@ namespace Avalonia.Animation
// default RenderTransform order. // default RenderTransform order.
normalTransform.Children.Add(new ScaleTransform()); normalTransform.Children.Add(new ScaleTransform());
normalTransform.Children.Add(new SkewTransform()); normalTransform.Children.Add(new SkewTransform());
normalTransform.Children.Add(new RotateTransform()); normalTransform.Children.Add(new RotateTransform());
normalTransform.Children.Add(new TranslateTransform()); normalTransform.Children.Add(new TranslateTransform());
@ -36,15 +36,22 @@ namespace Avalonia.Animation
var renderTransformType = ctrl.RenderTransform.GetType(); var renderTransformType = ctrl.RenderTransform.GetType();
if (childAnimator == null) if (_doubleAnimator == null)
{ {
InitializeChildAnimator(); _doubleAnimator = new DoubleAnimator();
foreach (AnimatorKeyFrame keyframe in this)
{
_doubleAnimator.Add(keyframe);
}
_doubleAnimator.Property = Property;
} }
// It's a transform object so let's target that. // It's a transform object so let's target that.
if (renderTransformType == Property.OwnerType) if (renderTransformType == Property.OwnerType)
{ {
return childAnimator.Apply(animation, ctrl.RenderTransform, clock ?? control.Clock, obsMatch, onComplete); return _doubleAnimator.Apply(animation, ctrl.RenderTransform, clock ?? control.Clock, obsMatch, onComplete);
} }
// It's a TransformGroup and try finding the target there. // It's a TransformGroup and try finding the target there.
else if (renderTransformType == typeof(TransformGroup)) else if (renderTransformType == typeof(TransformGroup))
@ -53,7 +60,7 @@ namespace Avalonia.Animation
{ {
if (transform.GetType() == Property.OwnerType) if (transform.GetType() == Property.OwnerType)
{ {
return childAnimator.Apply(animation, transform, clock ?? control.Clock, obsMatch, onComplete); return _doubleAnimator.Apply(animation, transform, clock ?? control.Clock, obsMatch, onComplete);
} }
} }
} }
@ -73,19 +80,7 @@ namespace Avalonia.Animation
return null; return null;
} }
void InitializeChildAnimator() /// <inheritdocs/>
{ public override double Interpolate(double p, double o, double n) => 0;
childAnimator = new DoubleAnimator();
foreach (AnimatorKeyFrame keyframe in this)
{
childAnimator.Add(keyframe);
}
childAnimator.Property = Property;
}
/// <inheritdocs/>
protected override double DoInterpolation(double time, double neutralValue) => 0;
} }
} }

17
src/Avalonia.Visuals/Animation/Animators/VectorAnimator.cs

@ -0,0 +1,17 @@
using System;
using Avalonia.Logging;
using Avalonia.Media;
namespace Avalonia.Animation.Animators
{
/// <summary>
/// Animator that handles <see cref="Vector"/> properties.
/// </summary>
public class VectorAnimator : Animator<Vector>
{
public override Vector Interpolate(double progress, Vector oldValue, Vector newValue)
{
return ((newValue - oldValue) * progress) + oldValue;
}
}
}

44
src/Avalonia.Visuals/Animation/CrossFade.cs

@ -21,7 +21,7 @@ namespace Avalonia.Animation
/// Initializes a new instance of the <see cref="CrossFade"/> class. /// Initializes a new instance of the <see cref="CrossFade"/> class.
/// </summary> /// </summary>
public CrossFade() public CrossFade()
:this(TimeSpan.Zero) : this(TimeSpan.Zero)
{ {
} }
@ -33,30 +33,40 @@ namespace Avalonia.Animation
{ {
_fadeOutAnimation = new Animation _fadeOutAnimation = new Animation
{ {
new KeyFrame Children =
( {
new Setter new KeyFrame()
{ {
Property = Visual.OpacityProperty, Setters =
Value = 0d {
new Setter
{
Property = Visual.OpacityProperty,
Value = 0d
}
},
Cue = new Cue(1d)
} }
)
{
Cue = new Cue(1d)
} }
}; };
_fadeInAnimation = new Animation _fadeInAnimation = new Animation
{ {
new KeyFrame Children =
( {
new Setter new KeyFrame()
{ {
Property = Visual.OpacityProperty, Setters =
Value = 0d {
new Setter
{
Property = Visual.OpacityProperty,
Value = 0d
}
},
Cue = new Cue(0d)
} }
)
{
Cue = new Cue(0d)
} }
}; };
_fadeOutAnimation.Duration = _fadeInAnimation.Duration = duration; _fadeOutAnimation.Duration = _fadeInAnimation.Duration = duration;

89
src/Avalonia.Visuals/Animation/PageSlide.cs

@ -74,34 +74,36 @@ namespace Avalonia.Animation
var distance = Orientation == SlideAxis.Horizontal ? parent.Bounds.Width : parent.Bounds.Height; var distance = Orientation == SlideAxis.Horizontal ? parent.Bounds.Width : parent.Bounds.Height;
var translateProperty = Orientation == SlideAxis.Horizontal ? TranslateTransform.XProperty : TranslateTransform.YProperty; var translateProperty = Orientation == SlideAxis.Horizontal ? TranslateTransform.XProperty : TranslateTransform.YProperty;
// TODO: Implement relevant transition logic here (or discard this class)
// in favor of XAML based transition for pages
if (from != null) if (from != null)
{ {
var animation = new Animation var animation = new Animation
{ {
new KeyFrame Children =
(
new Setter
{
Property = translateProperty,
Value = 0d
}
)
{ {
Cue = new Cue(0d) new KeyFrame
},
new KeyFrame
(
new Setter
{ {
Property = translateProperty, Setters =
Value = forward ? -distance : distance {
} new Setter
) {
{ Property = translateProperty,
Cue = new Cue(1d) Value = 0d
}
},
Cue = new Cue(0d)
},
new KeyFrame
{
Setters =
{
new Setter
{
Property = translateProperty,
Value = forward ? -distance : distance
}
},
Cue = new Cue(1d)
}
} }
}; };
animation.Duration = Duration; animation.Duration = Duration;
@ -113,29 +115,34 @@ namespace Avalonia.Animation
to.IsVisible = true; to.IsVisible = true;
var animation = new Animation var animation = new Animation
{ {
Children =
{
new KeyFrame new KeyFrame
(
new Setter
{ {
Property = translateProperty, Setters =
Value = forward ? distance : -distance {
} new Setter
) {
{ Property = translateProperty,
Cue = new Cue(0d) Value = forward ? distance : -distance
}, }
new KeyFrame },
( Cue = new Cue(0d)
new Setter },
new KeyFrame
{ {
Property = translateProperty, Setters =
Value = 0d {
new Setter
{
Property = translateProperty,
Value = 0d
}
},
Cue = new Cue(1d)
} }
) }
{
Cue = new Cue(1d)
},
}; };
animation.Duration = Duration; animation.Duration = Duration;
tasks.Add(animation.RunAsync(to)); tasks.Add(animation.RunAsync(to));

36
src/Avalonia.Visuals/Animation/Transitions/CornerRadiusTransition.cs

@ -0,0 +1,36 @@
// 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.Reactive.Linq;
namespace Avalonia.Animation
{
/// <summary>
/// Transition class that handles <see cref="AvaloniaProperty"/> with <see cref="CornerRadius"/> type.
/// </summary>
public class CornerRadiusTransition : Transition<CornerRadius>
{
/// <inheritdocs/>
public override IObservable<CornerRadius> DoTransition(IObservable<double> progress, CornerRadius oldValue, CornerRadius newValue)
{
return progress
.Select(p =>
{
var f = Easing.Ease(p);
var deltaTL = newValue.TopLeft - oldValue.TopLeft;
var deltaTR = newValue.TopRight - oldValue.TopRight;
var deltaBR = newValue.BottomRight - oldValue.BottomRight;
var deltaBL = newValue.BottomLeft - oldValue.BottomLeft;
var nTL = f * deltaTL + oldValue.TopLeft;
var nTR = f * deltaTR + oldValue.TopRight;
var nBR = f * deltaBR + oldValue.BottomRight;
var nBL = f * deltaBL + oldValue.BottomLeft;
return new CornerRadius(nTL, nTR, nBR, nBL);
});
}
}
}

7
src/Avalonia.Visuals/Animation/PointTransition.cs → src/Avalonia.Visuals/Animation/Transitions/PointTransition.cs

@ -14,16 +14,11 @@ namespace Avalonia.Animation
/// <inheritdocs/> /// <inheritdocs/>
public override IObservable<Point> DoTransition(IObservable<double> progress, Point oldValue, Point newValue) public override IObservable<Point> DoTransition(IObservable<double> progress, Point oldValue, Point newValue)
{ {
var deltaX = newValue.X - oldValue.Y;
var deltaY = newValue.X - oldValue.Y;
return progress return progress
.Select(p => .Select(p =>
{ {
var f = Easing.Ease(p); var f = Easing.Ease(p);
var nX = f * deltaX + oldValue.X; return ((newValue - oldValue) * f) + oldValue;
var nY = f * deltaY + oldValue.Y;
return new Point(nX, nY);
}); });
} }
} }

25
src/Avalonia.Visuals/Animation/Transitions/SizeTransition.cs

@ -0,0 +1,25 @@
// 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.Reactive.Linq;
namespace Avalonia.Animation
{
/// <summary>
/// Transition class that handles <see cref="AvaloniaProperty"/> with <see cref="Size"/> type.
/// </summary>
public class SizeTransition : Transition<Size>
{
/// <inheritdocs/>
public override IObservable<Size> DoTransition(IObservable<double> progress, Size oldValue, Size newValue)
{
return progress
.Select(p =>
{
var f = Easing.Ease(p);
return ((newValue - oldValue) * f) + oldValue;
});
}
}
}

13
src/Avalonia.Visuals/Animation/ThicknessTransition.cs → src/Avalonia.Visuals/Animation/Transitions/ThicknessTransition.cs

@ -14,20 +14,11 @@ namespace Avalonia.Animation
/// <inheritdocs/> /// <inheritdocs/>
public override IObservable<Thickness> DoTransition(IObservable<double> progress, Thickness oldValue, Thickness newValue) public override IObservable<Thickness> DoTransition(IObservable<double> progress, Thickness oldValue, Thickness newValue)
{ {
var deltaL = newValue.Left - oldValue.Left;
var deltaT = newValue.Top - oldValue.Top;
var deltaR = newValue.Right - oldValue.Right;
var deltaB = newValue.Bottom - oldValue.Bottom;
return progress return progress
.Select(p => .Select(p =>
{ {
var f = Easing.Ease(p); var f = Easing.Ease(p);
var nL = f * deltaL + oldValue.Left; return ((newValue - oldValue) * f) + oldValue;
var nT = f * deltaT + oldValue.Right;
var nR = f * deltaR + oldValue.Top;
var nB = f * deltaB + oldValue.Bottom;
return new Thickness(nL, nT, nR, nB);
}); });
} }
} }

25
src/Avalonia.Visuals/Animation/Transitions/VectorTransition.cs

@ -0,0 +1,25 @@
// 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.Reactive.Linq;
namespace Avalonia.Animation
{
/// <summary>
/// Transition class that handles <see cref="AvaloniaProperty"/> with <see cref="Vector"/> type.
/// </summary>
public class VectorTransition : Transition<Vector>
{
/// <inheritdocs/>
public override IObservable<Vector> DoTransition(IObservable<double> progress, Vector oldValue, Vector newValue)
{
return progress
.Select(p =>
{
var f = Easing.Ease(p);
return ((newValue - oldValue) * f) + oldValue;
});
}
}
}

1
src/Avalonia.Visuals/Avalonia.Visuals.csproj

@ -2,7 +2,6 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace>Avalonia</RootNamespace> <RootNamespace>Avalonia</RootNamespace>
<IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Avalonia.Animation\Avalonia.Animation.csproj" /> <ProjectReference Include="..\Avalonia.Animation\Avalonia.Animation.csproj" />

6
src/Avalonia.Visuals/CornerRadius.cs

@ -3,12 +3,18 @@
using System; using System;
using System.Globalization; using System.Globalization;
using Avalonia.Animation.Animators;
using Avalonia.Utilities; using Avalonia.Utilities;
namespace Avalonia namespace Avalonia
{ {
public struct CornerRadius public struct CornerRadius
{ {
static CornerRadius()
{
Animation.Animation.RegisterAnimator<CornerRadiusAnimator>(prop => typeof(CornerRadius).IsAssignableFrom(prop.PropertyType));
}
public CornerRadius(double uniformRadius) public CornerRadius(double uniformRadius)
{ {
TopLeft = TopRight = BottomLeft = BottomRight = uniformRadius; TopLeft = TopRight = BottomLeft = BottomRight = uniformRadius;

3
src/Avalonia.Visuals/Media/Brush.cs

@ -3,6 +3,7 @@
using System; using System;
using System.ComponentModel; using System.ComponentModel;
using Avalonia.Animation;
namespace Avalonia.Media namespace Avalonia.Media
{ {
@ -10,7 +11,7 @@ namespace Avalonia.Media
/// Describes how an area is painted. /// Describes how an area is painted.
/// </summary> /// </summary>
[TypeConverter(typeof(BrushConverter))] [TypeConverter(typeof(BrushConverter))]
public abstract class Brush : AvaloniaObject, IMutableBrush public abstract class Brush : Animatable, IMutableBrush
{ {
/// <summary> /// <summary>
/// Defines the <see cref="Opacity"/> property. /// Defines the <see cref="Opacity"/> property.

7
src/Avalonia.Visuals/Media/Color.cs

@ -3,6 +3,8 @@
using System; using System;
using System.Globalization; using System.Globalization;
using Avalonia.Animation;
using Avalonia.Animation.Animators;
namespace Avalonia.Media namespace Avalonia.Media
{ {
@ -11,6 +13,11 @@ namespace Avalonia.Media
/// </summary> /// </summary>
public readonly struct Color public readonly struct Color
{ {
static Color()
{
Animation.Animation.RegisterAnimator<ColorAnimator>(prop => typeof(Color).IsAssignableFrom(prop.PropertyType));
}
/// <summary> /// <summary>
/// Gets or sets the Alpha component of the color. /// Gets or sets the Alpha component of the color.
/// </summary> /// </summary>

3
src/Avalonia.Visuals/Media/SolidColorBrush.cs

@ -1,6 +1,8 @@
// Copyright (c) The Avalonia Project. All rights reserved. // Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information. // Licensed under the MIT license. See licence.md file in the project root for full license information.
using Avalonia.Animation;
using Avalonia.Animation.Animators;
using Avalonia.Media.Immutable; using Avalonia.Media.Immutable;
namespace Avalonia.Media namespace Avalonia.Media
@ -18,6 +20,7 @@ namespace Avalonia.Media
static SolidColorBrush() static SolidColorBrush()
{ {
Animation.Animation.RegisterAnimator<SolidColorBrushAnimator>(prop => typeof(IBrush).IsAssignableFrom(prop.PropertyType));
AffectsRender<SolidColorBrush>(ColorProperty); AffectsRender<SolidColorBrush>(ColorProperty);
} }

1
src/Avalonia.Visuals/Media/Transform.cs

@ -3,6 +3,7 @@
using System; using System;
using Avalonia.Animation; using Avalonia.Animation;
using Avalonia.Animation.Animators;
using Avalonia.VisualTree; using Avalonia.VisualTree;
namespace Avalonia.Media namespace Avalonia.Media

14
src/Avalonia.Visuals/Point.cs

@ -2,6 +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.Globalization; using System.Globalization;
using Avalonia.Animation.Animators;
using Avalonia.Utilities; using Avalonia.Utilities;
namespace Avalonia namespace Avalonia
@ -11,6 +12,11 @@ namespace Avalonia
/// </summary> /// </summary>
public readonly struct Point public readonly struct Point
{ {
static Point()
{
Animation.Animation.RegisterAnimator<PointAnimator>(prop => typeof(Point).IsAssignableFrom(prop.PropertyType));
}
/// <summary> /// <summary>
/// The X position. /// The X position.
/// </summary> /// </summary>
@ -133,7 +139,7 @@ namespace Avalonia
/// <param name="p">Point to multiply</param> /// <param name="p">Point to multiply</param>
/// <param name="k">Factor</param> /// <param name="k">Factor</param>
/// <returns>Points having its coordinates multiplied</returns> /// <returns>Points having its coordinates multiplied</returns>
public static Point operator *(Point p, double k) => new Point(p.X*k, p.Y*k); public static Point operator *(Point p, double k) => new Point(p.X * k, p.Y * k);
/// <summary> /// <summary>
/// Multiplies a point by a factor coordinate-wise /// Multiplies a point by a factor coordinate-wise
@ -141,7 +147,7 @@ namespace Avalonia
/// <param name="p">Point to multiply</param> /// <param name="p">Point to multiply</param>
/// <param name="k">Factor</param> /// <param name="k">Factor</param>
/// <returns>Points having its coordinates multiplied</returns> /// <returns>Points having its coordinates multiplied</returns>
public static Point operator *(double k, Point p) => new Point(p.X*k, p.Y*k); public static Point operator *(double k, Point p) => new Point(p.X * k, p.Y * k);
/// <summary> /// <summary>
/// Divides a point by a factor coordinate-wise /// Divides a point by a factor coordinate-wise
@ -149,8 +155,8 @@ namespace Avalonia
/// <param name="p">Point to divide by</param> /// <param name="p">Point to divide by</param>
/// <param name="k">Factor</param> /// <param name="k">Factor</param>
/// <returns>Points having its coordinates divided</returns> /// <returns>Points having its coordinates divided</returns>
public static Point operator /(Point p, double k) => new Point(p.X/k, p.Y/k); public static Point operator /(Point p, double k) => new Point(p.X / k, p.Y / k);
/// <summary> /// <summary>
/// Applies a matrix to a point. /// Applies a matrix to a point.
/// </summary> /// </summary>

6
src/Avalonia.Visuals/Rect.cs

@ -3,6 +3,7 @@
using System; using System;
using System.Globalization; using System.Globalization;
using Avalonia.Animation.Animators;
using Avalonia.Utilities; using Avalonia.Utilities;
namespace Avalonia namespace Avalonia
@ -12,6 +13,11 @@ namespace Avalonia
/// </summary> /// </summary>
public readonly struct Rect public readonly struct Rect
{ {
static Rect()
{
Animation.Animation.RegisterAnimator<RectAnimator>(prop => typeof(Rect).IsAssignableFrom(prop.PropertyType));
}
/// <summary> /// <summary>
/// An empty rectangle. /// An empty rectangle.
/// </summary> /// </summary>

6
src/Avalonia.Visuals/Size.cs

@ -3,6 +3,7 @@
using System; using System;
using System.Globalization; using System.Globalization;
using Avalonia.Animation.Animators;
using Avalonia.Utilities; using Avalonia.Utilities;
namespace Avalonia namespace Avalonia
@ -12,6 +13,11 @@ namespace Avalonia
/// </summary> /// </summary>
public readonly struct Size public readonly struct Size
{ {
static Size()
{
Animation.Animation.RegisterAnimator<SizeAnimator>(prop => typeof(Size).IsAssignableFrom(prop.PropertyType));
}
/// <summary> /// <summary>
/// A size representing infinity. /// A size representing infinity.
/// </summary> /// </summary>

39
src/Avalonia.Visuals/Thickness.cs

@ -3,6 +3,8 @@
using System; using System;
using System.Globalization; using System.Globalization;
using Avalonia.Animation;
using Avalonia.Animation.Animators;
using Avalonia.Utilities; using Avalonia.Utilities;
namespace Avalonia namespace Avalonia
@ -12,6 +14,11 @@ namespace Avalonia
/// </summary> /// </summary>
public readonly struct Thickness public readonly struct Thickness
{ {
static Thickness()
{
Animation.Animation.RegisterAnimator<ThicknessAnimator>(prop => typeof(Thickness).IsAssignableFrom(prop.PropertyType));
}
/// <summary> /// <summary>
/// The thickness on the left. /// The thickness on the left.
/// </summary> /// </summary>
@ -134,6 +141,36 @@ namespace Avalonia
a.Bottom + b.Bottom); a.Bottom + b.Bottom);
} }
/// <summary>
/// Subtracts two Thicknesses.
/// </summary>
/// <param name="a">The first thickness.</param>
/// <param name="b">The second thickness.</param>
/// <returns>The equality.</returns>
public static Thickness operator -(Thickness a, Thickness b)
{
return new Thickness(
a.Left - b.Left,
a.Top - b.Top,
a.Right - b.Right,
a.Bottom - b.Bottom);
}
/// <summary>
/// Multiplies a Thickness to a scalar.
/// </summary>
/// <param name="a">The thickness.</param>
/// <param name="b">The scalar.</param>
/// <returns>The equality.</returns>
public static Thickness operator *(Thickness a, double b)
{
return new Thickness(
a.Left * b,
a.Top * b,
a.Right * b,
a.Bottom * b);
}
/// <summary> /// <summary>
/// Adds a Thickness to a Size. /// Adds a Thickness to a Size.
/// </summary> /// </summary>
@ -169,7 +206,7 @@ namespace Avalonia
{ {
using (var tokenizer = new StringTokenizer(s, CultureInfo.InvariantCulture, exceptionMessage: "Invalid Thickness")) using (var tokenizer = new StringTokenizer(s, CultureInfo.InvariantCulture, exceptionMessage: "Invalid Thickness"))
{ {
if(tokenizer.TryReadDouble(out var a)) if (tokenizer.TryReadDouble(out var a))
{ {
if (tokenizer.TryReadDouble(out var b)) if (tokenizer.TryReadDouble(out var b))
{ {

10
src/Avalonia.Visuals/Vector.cs

@ -3,6 +3,7 @@
using System; using System;
using System.Globalization; using System.Globalization;
using Avalonia.Animation.Animators;
using JetBrains.Annotations; using JetBrains.Annotations;
namespace Avalonia namespace Avalonia
@ -12,6 +13,11 @@ namespace Avalonia
/// </summary> /// </summary>
public readonly struct Vector public readonly struct Vector
{ {
static Vector()
{
Animation.Animation.RegisterAnimator<VectorAnimator>(prop => typeof(Vector).IsAssignableFrom(prop.PropertyType));
}
/// <summary> /// <summary>
/// The X vector. /// The X vector.
/// </summary> /// </summary>
@ -60,7 +66,7 @@ namespace Avalonia
/// <returns>The dot product</returns> /// <returns>The dot product</returns>
public static double operator *(Vector a, Vector b) public static double operator *(Vector a, Vector b)
{ {
return a.X*b.X + a.Y*b.Y; return a.X * b.X + a.Y * b.Y;
} }
/// <summary> /// <summary>
@ -88,7 +94,7 @@ namespace Avalonia
/// <summary> /// <summary>
/// Length of the vector /// Length of the vector
/// </summary> /// </summary>
public double Length => Math.Sqrt(X*X + Y*Y); public double Length => Math.Sqrt(X * X + Y * Y);
/// <summary> /// <summary>
/// Negates a vector. /// Negates a vector.

2
src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs

@ -50,7 +50,7 @@ namespace Avalonia.Gtk3
{ {
Resolver.Custom = options.CustomResolver; Resolver.Custom = options.CustomResolver;
UseDeferredRendering = EnvOption("USE_DEFERRED_RENDERING", true, options.UseDeferredRendering); UseDeferredRendering = EnvOption("USE_DEFERRED_RENDERING", true, options.UseDeferredRendering);
var useGpu = EnvOption("USE_GPU", false, options.UseGpuAcceleration); var useGpu = EnvOption("USE_GPU", true, options.UseGpuAcceleration);
if (!s_gtkInitialized) if (!s_gtkInitialized)
{ {
try try

1
src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj

@ -4,7 +4,6 @@
<DefineConstants>PCL;NETSTANDARD;NETSTANDARD2_0;HAS_TYPE_CONVERTER;HAS_CUSTOM_ATTRIBUTE_PROVIDER</DefineConstants> <DefineConstants>PCL;NETSTANDARD;NETSTANDARD2_0;HAS_TYPE_CONVERTER;HAS_CUSTOM_ATTRIBUTE_PROVIDER</DefineConstants>
<EnableDefaultCompileItems>False</EnableDefaultCompileItems> <EnableDefaultCompileItems>False</EnableDefaultCompileItems>
<EnableDefaultItems>false</EnableDefaultItems> <EnableDefaultItems>false</EnableDefaultItems>
<IsPackable>false</IsPackable>
<NoWarn>CS1591</NoWarn> <NoWarn>CS1591</NoWarn>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

1
src/Markup/Avalonia.Markup/Avalonia.Markup.csproj

@ -2,7 +2,6 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace>Avalonia</RootNamespace> <RootNamespace>Avalonia</RootNamespace>
<IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\Avalonia.Base\Avalonia.Base.csproj" /> <ProjectReference Include="..\..\Avalonia.Base\Avalonia.Base.csproj" />

3
src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs

@ -42,7 +42,8 @@ namespace Avalonia.Skia
{ {
var framebuffer = _platformSurface.Lock(); var framebuffer = _platformSurface.Lock();
var framebufferImageInfo = new SKImageInfo(framebuffer.Size.Width, framebuffer.Size.Height, var framebufferImageInfo = new SKImageInfo(framebuffer.Size.Width, framebuffer.Size.Height,
framebuffer.Format.ToSkColorType(), SKAlphaType.Premul); framebuffer.Format.ToSkColorType(),
framebuffer.Format == PixelFormat.Rgb565 ? SKAlphaType.Opaque : SKAlphaType.Premul);
CreateSurface(framebufferImageInfo, framebuffer); CreateSurface(framebufferImageInfo, framebuffer);

23
src/Skia/Avalonia.Skia/GlRenderTarget.cs

@ -31,24 +31,18 @@ namespace Avalonia.Skia
var size = session.Size; var size = session.Size;
var scaling = session.Scaling; var scaling = session.Scaling;
GRBackendRenderTargetDesc desc = new GRBackendRenderTargetDesc gl.Viewport(0, 0, size.Width, size.Height);
{
Width = size.Width,
Height = size.Height,
SampleCount = disp.SampleCount,
StencilBits = disp.StencilSize,
Config = GRPixelConfig.Rgba8888,
Origin=GRSurfaceOrigin.BottomLeft,
RenderTargetHandle = new IntPtr(fb)
};
gl.Viewport(0, 0, desc.Width, desc.Height);
gl.ClearStencil(0); gl.ClearStencil(0);
gl.ClearColor(0, 0, 0, 0); gl.ClearColor(0, 0, 0, 0);
gl.Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); gl.Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
var surface = SKSurface.Create(_grContext, desc); GRBackendRenderTarget renderTarget =
new GRBackendRenderTarget(size.Width, size.Height, disp.SampleCount, disp.StencilSize,
new GRGlFramebufferInfo((uint)fb, GRPixelConfig.Rgba8888.ToGlSizedFormat()));
var surface = SKSurface.Create(_grContext, renderTarget,
GRSurfaceOrigin.BottomLeft,
GRPixelConfig.Rgba8888.ToColorType());
var nfo = new DrawingContextImpl.CreateInfo var nfo = new DrawingContextImpl.CreateInfo
{ {
GrContext = _grContext, GrContext = _grContext,
@ -62,6 +56,7 @@ namespace Avalonia.Skia
{ {
surface.Canvas.Flush(); surface.Canvas.Flush();
surface.Dispose(); surface.Dispose();
renderTarget.Dispose();
session.Dispose(); session.Dispose();
})); }));
} }

2
src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs

@ -137,7 +137,7 @@ namespace Avalonia.Skia
{ {
var colorType = PixelFormatHelper.ResolveColorType(format); var colorType = PixelFormatHelper.ResolveColorType(format);
return new SKImageInfo(width, height, colorType, SKAlphaType.Premul); return new SKImageInfo(Math.Max(width, 1), Math.Max(height, 1), colorType, SKAlphaType.Premul);
} }
/// <summary> /// <summary>

2
src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs

@ -41,7 +41,7 @@ namespace Avalonia.Skia
var nfo = new SKImageInfo(size.Width, size.Height, colorType, SKAlphaType.Premul); var nfo = new SKImageInfo(size.Width, size.Height, colorType, SKAlphaType.Premul);
var blob = runtimePlatform.AllocBlob(nfo.BytesSize); var blob = runtimePlatform.AllocBlob(nfo.BytesSize);
_bitmap.InstallPixels(nfo, blob.Address, nfo.RowBytes, null, s_releaseDelegate, blob); _bitmap.InstallPixels(nfo, blob.Address, nfo.RowBytes, s_releaseDelegate, blob);
} }
else else
{ {

8
src/Windows/Avalonia.Win32/WindowImpl.cs

@ -281,10 +281,10 @@ namespace Avalonia.Win32
var f = Scaling; var f = Scaling;
var r = new UnmanagedMethods.RECT var r = new UnmanagedMethods.RECT
{ {
left = (int)(rect.X * f), left = (int)Math.Floor(rect.X * f),
top = (int)(rect.Y * f), top = (int)Math.Floor(rect.Y * f),
right = (int)(rect.Right * f), right = (int)Math.Ceiling(rect.Right * f),
bottom = (int)(rect.Bottom * f), bottom = (int)Math.Ceiling(rect.Bottom * f),
}; };
UnmanagedMethods.InvalidateRect(_hwnd, ref r, false); UnmanagedMethods.InvalidateRect(_hwnd, ref r, false);

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

Loading…
Cancel
Save