Browse Source

Merge branch 'master' into feature/window-showactive

pull/5063/head
Dan Walmsley 6 years ago
committed by GitHub
parent
commit
e34fe48bdf
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 11
      Documentation/build.md
  2. 2
      NuGet.Config
  3. 2
      build/ApiDiff.props
  4. 8
      build/MicroCom.targets
  5. 2
      build/Rx.props
  6. 2
      native/Avalonia.Native/src/OSX/platformthreading.mm
  7. 10
      native/Avalonia.Native/src/OSX/rendertarget.mm
  8. 18
      nukebuild/Build.cs
  9. 2
      nukebuild/_build.csproj
  10. 30
      samples/ControlCatalog/DecoratedWindow.xaml
  11. 66
      samples/ControlCatalog/MainWindow.xaml
  12. 29
      samples/ControlCatalog/Pages/DialogsPage.xaml.cs
  13. 1
      samples/Sandbox/MainWindow.axaml.cs
  14. 34
      src/Avalonia.Base/Data/Core/TypeCastNode.cs
  15. 24
      src/Avalonia.Base/Utilities/AvaloniaPropertyValueStore.cs
  16. 5
      src/Avalonia.Base/Utilities/MathUtilities.cs
  17. 5
      src/Avalonia.Base/Utilities/StringTokenizer.cs
  18. 35
      src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
  19. 4
      src/Avalonia.Build.Tasks/SpanCompat.cs
  20. 4
      src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
  21. 32
      src/Avalonia.Controls/ComboBox.cs
  22. 10
      src/Avalonia.Controls/GridLength.cs
  23. 3
      src/Avalonia.Controls/NativeMenuBar.cs
  24. 18
      src/Avalonia.Controls/NativeMenuItem.cs
  25. 2
      src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs
  26. 2
      src/Avalonia.Controls/Primitives/ScrollBar.cs
  27. 9
      src/Avalonia.Controls/Primitives/TemplatedControl.cs
  28. 11
      src/Avalonia.Controls/Slider.cs
  29. 2
      src/Avalonia.Input/NavigationDirection.cs
  30. 2
      src/Avalonia.MicroCom/MicroComProxyBase.cs
  31. 28
      src/Avalonia.MicroCom/MicroComRuntime.cs
  32. 4
      src/Avalonia.MicroCom/MicroComVtblBase.cs
  33. 2
      src/Avalonia.Native/AvaloniaNativeMenuExporter.cs
  34. 8
      src/Avalonia.Native/Extensions.cs
  35. 2
      src/Avalonia.Native/IAvnMenuItem.cs
  36. 6
      src/Avalonia.Native/PlatformThreadingInterface.cs
  37. 4
      src/Avalonia.Native/PopupImpl.cs
  38. 4
      src/Avalonia.Native/PredicateCallback.cs
  39. 2
      src/Avalonia.Native/ScreenImpl.cs
  40. 2
      src/Avalonia.Native/SystemDialogs.cs
  41. 12
      src/Avalonia.Native/WindowImpl.cs
  42. 14
      src/Avalonia.Native/WindowImplBase.cs
  43. 1
      src/Avalonia.Native/avn.idl
  44. 8
      src/Avalonia.OpenGL/Egl/EglContext.cs
  45. 3
      src/Avalonia.Styling/ApiCompatBaseline.txt
  46. 13
      src/Avalonia.Styling/Controls/ResourceNodeExtensions.cs
  47. 48
      src/Avalonia.Styling/StyledElement.cs
  48. 15
      src/Avalonia.Themes.Default/PathIcon.xaml
  49. 2
      src/Avalonia.Themes.Default/ToggleSwitch.xaml
  50. 15
      src/Avalonia.Themes.Fluent/PathIcon.xaml
  51. 2
      src/Avalonia.Themes.Fluent/ToggleSwitch.xaml
  52. 42
      src/Avalonia.Visuals/ApiCompatBaseline.txt
  53. 9
      src/Avalonia.Visuals/CornerRadius.cs
  54. 5
      src/Avalonia.Visuals/Matrix.cs
  55. 14
      src/Avalonia.Visuals/Media/Color.cs
  56. 75
      src/Avalonia.Visuals/Media/DashStyle.cs
  57. 56
      src/Avalonia.Visuals/Media/EllipseGeometry.cs
  58. 9
      src/Avalonia.Visuals/Media/KnownColors.cs
  59. 72
      src/Avalonia.Visuals/Media/PathFigure.cs
  60. 20
      src/Avalonia.Visuals/Media/PathGeometry.cs
  61. 9
      src/Avalonia.Visuals/Point.cs
  62. 79
      src/Avalonia.Visuals/Rendering/DeferredRenderer.cs
  63. 7
      src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs
  64. 9
      src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs
  65. 9
      src/Avalonia.Visuals/Size.cs
  66. 9
      src/Avalonia.Visuals/Thickness.cs
  67. 9
      src/Avalonia.Visuals/Vector.cs
  68. 37
      src/Avalonia.Visuals/Visual.cs
  69. 10
      src/Avalonia.X11/X11Window.cs
  70. 2
      src/Avalonia.X11/XI2Manager.cs
  71. 1
      src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj
  72. 39
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlFontFamilyAstNode.cs
  73. 34
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlGridLengthAstNode.cs
  74. 54
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlVectorLikeConstantAstNode.cs
  75. 200
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs
  76. 2
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlTransformInstanceAttachedProperties.cs
  77. 52
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
  78. 32
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs
  79. 2
      src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github
  80. 1
      src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
  81. 2
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindingExtension.cs
  82. 51
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs
  83. 18
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/StrongTypeCastNode.cs
  84. 18
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs
  85. 89
      src/Markup/Avalonia.Markup/Markup/Parsers/BindingExpressionGrammar.cs
  86. 19
      src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionParser.cs
  87. 12
      src/Skia/Avalonia.Skia/DrawingContextImpl.cs
  88. 3
      src/Skia/Avalonia.Skia/PlatformRenderInterface.cs
  89. 5
      src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs
  90. 1
      src/Windows/Avalonia.Win32/.gitignore
  91. 4
      src/Windows/Avalonia.Win32/Avalonia.Win32.csproj
  92. 18
      src/Windows/Avalonia.Win32/Composition/CompositionBlurHost.cs
  93. 171
      src/Windows/Avalonia.Win32/Composition/CompositionConnector.cs
  94. 96
      src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs
  95. 91
      src/Windows/Avalonia.Win32/Composition/D2DEffects.cs
  96. 45
      src/Windows/Avalonia.Win32/Composition/EffectBase.cs
  97. 18
      src/Windows/Avalonia.Win32/Composition/GRAPHICS_EFFECT_PROPERTY_MAPPING.cs
  98. 57
      src/Windows/Avalonia.Win32/Composition/GaussianBlurEffect.cs
  99. 8
      src/Windows/Avalonia.Win32/Composition/IBlurHost.cs
  100. 152
      src/Windows/Avalonia.Win32/Composition/ICompositionDrawingSurfaceInterop.cs

11
Documentation/build.md

@ -60,15 +60,10 @@ git submodule update --init --recursive
### Build native libraries (macOS only) ### Build native libraries (macOS only)
On macOS it is necessary to build and manually install the respective native libraries using [Xcode](https://developer.apple.com/xcode/). The steps to get this working correctly are: On macOS it is necessary to build and manually install the respective native libraries using [Xcode](https://developer.apple.com/xcode/). Execute the build script in the root project with the `CompileNative` task. It will build the headers, build the libraries, and place them in the appropriate place to allow .NET to find them at compilation and run time.
- (for revisions after 2 Nov 2020) Run `./build.sh GenerateCppHeaders` to generate `avalonia-native.h` from `avn.idl`
- Navigate to the Avalonia/native/Avalonia.Native/src/OSX folder and open the `Avalonia.Native.OSX.xcodeproj` project
- Build the library via the Product->Build menu. This will generate binaries in your local path under ~/Library/Developer/Xcode/DerivedData/Avalonia.Native.OSX-*guid* where "guid" is uniquely generated every time you build.
- Manually install the native library by copying it from the build artifacts folder into the shared dynamic library path:
``` ```bash
cd ~/Library/Developer/Xcode/DerivedData/Avalonia.Native.OSX-[guid]/Build/Products/Debug ./build.sh CompileNative
cp libAvalonia.Native.OSX.dylib /usr/local/lib/libAvaloniaNative.dylib
``` ```
### Build and Run Avalonia ### Build and Run Avalonia

2
NuGet.Config

@ -4,6 +4,6 @@
<packageSources> <packageSources>
<clear /> <clear />
<add key="api.nuget.org" value="https://api.nuget.org/v3/index.json" /> <add key="api.nuget.org" value="https://api.nuget.org/v3/index.json" />
<add key="dotnet-eng" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json" /> <add key="dotnet-eng" value="https://nuget.avaloniaui.net/repository/avalonia-devdeps/index.json" protocolVersion="3" />
</packageSources> </packageSources>
</configuration> </configuration>

2
build/ApiDiff.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">
<PropertyGroup> <PropertyGroup>
<ApiContractPackageVersion>0.10.0-preview3</ApiContractPackageVersion> <ApiContractPackageVersion>0.10.0-preview6</ApiContractPackageVersion>
<NugetPackageName Condition="'$(PackageId)' != ''">$(PackageId)</NugetPackageName> <NugetPackageName Condition="'$(PackageId)' != ''">$(PackageId)</NugetPackageName>
<NugetPackageName Condition="'$(PackageId)' == ''">Avalonia</NugetPackageName> <NugetPackageName Condition="'$(PackageId)' == ''">Avalonia</NugetPackageName>
</PropertyGroup> </PropertyGroup>

8
build/MicroCom.targets

@ -2,7 +2,7 @@
<!-- Ensure that code generator is actually built --> <!-- Ensure that code generator is actually built -->
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\tools\MicroComGenerator\MicroComGenerator.csproj"> <ProjectReference Include="$(MSBuildThisFileDirectory)\..\src\tools\MicroComGenerator\MicroComGenerator.csproj">
<ReferenceOutputAssembly>false</ReferenceOutputAssembly> <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
<ExcludeAssets>all</ExcludeAssets> <ExcludeAssets>all</ExcludeAssets>
<SkipGetTargetFrameworkProperties>true</SkipGetTargetFrameworkProperties> <SkipGetTargetFrameworkProperties>true</SkipGetTargetFrameworkProperties>
@ -12,10 +12,10 @@
<Target Name="GenerateAvaloniaNativeComInterop" <Target Name="GenerateAvaloniaNativeComInterop"
BeforeTargets="CoreCompile" BeforeTargets="CoreCompile"
DependsOnTargets="ResolveReferences" DependsOnTargets="ResolveReferences"
Inputs="@(AvnComIdl);$(MSBuildThisFileDirectory)/../tools/MicroComGenerator/**/*.cs" Inputs="@(AvnComIdl);$(MSBuildThisFileDirectory)../src/tools/MicroComGenerator/**/*.cs"
Outputs="%(AvnComIdl.OutputFile)"> Outputs="%(AvnComIdl.OutputFile)">
<Message Importance="high" Text="Generating file %(AvnComIdl.OutputFile) from @(AvnComIdl)" /> <Message Importance="high" Text="Generating file %(AvnComIdl.OutputFile) from @(AvnComIdl)" />
<Exec Command="dotnet ../tools/MicroComGenerator/bin/$(Configuration)/netcoreapp3.1/MicroComGenerator.dll -i @(AvnComIdl) --cs %(AvnComIdl.OutputFile)" LogStandardErrorAsError="true" /> <Exec Command="dotnet $(MSBuildThisFileDirectory)../src/tools/MicroComGenerator/bin/$(Configuration)/netcoreapp3.1/MicroComGenerator.dll -i @(AvnComIdl) --cs %(AvnComIdl.OutputFile)" LogStandardErrorAsError="true" />
<ItemGroup> <ItemGroup>
<!-- Remove and re-add generated file, this is needed for the clean build --> <!-- Remove and re-add generated file, this is needed for the clean build -->
<Compile Remove="%(AvnComIdl.OutputFile)"/> <Compile Remove="%(AvnComIdl.OutputFile)"/>
@ -24,7 +24,7 @@
</Target> </Target>
<ItemGroup> <ItemGroup>
<UpToDateCheckInput Include="@(AvnComIdl)"/> <UpToDateCheckInput Include="@(AvnComIdl)"/>
<UpToDateCheckInput Include="$(MSBuildThisFileDirectory)/../tools/MicroComGenerator/**/*.cs"/> <UpToDateCheckInput Include="$(MSBuildThisFileDirectory)/../src/tools/MicroComGenerator/**/*.cs"/>
</ItemGroup> </ItemGroup>
<PropertyGroup> <PropertyGroup>
<_AvaloniaPatchComInterop>true</_AvaloniaPatchComInterop> <_AvaloniaPatchComInterop>true</_AvaloniaPatchComInterop>

2
build/Rx.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="System.Reactive" Version="4.4.1" /> <PackageReference Include="System.Reactive" Version="5.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

2
native/Avalonia.Native/src/OSX/platformthreading.mm

@ -101,7 +101,7 @@ public:
virtual bool GetCurrentThreadIsLoopThread() override virtual bool GetCurrentThreadIsLoopThread() override
{ {
return [[NSThread currentThread] isMainThread]; return [NSThread isMainThread];
} }
virtual void SetSignaledCallback(IAvnSignaledCallback* cb) override virtual void SetSignaledCallback(IAvnSignaledCallback* cb) override
{ {

10
native/Avalonia.Native/src/OSX/rendertarget.mm

@ -2,6 +2,7 @@
#include "rendertarget.h" #include "rendertarget.h"
#import <IOSurface/IOSurface.h> #import <IOSurface/IOSurface.h>
#import <IOSurface/IOSurfaceObjC.h> #import <IOSurface/IOSurfaceObjC.h>
#import <QuartzCore/QuartzCore.h>
#include <OpenGL/CGLIOSurface.h> #include <OpenGL/CGLIOSurface.h>
#include <OpenGL/OpenGL.h> #include <OpenGL/OpenGL.h>
@ -143,13 +144,17 @@ static IAvnGlSurfaceRenderTarget* CreateGlRenderTarget(IOSurfaceRenderTarget* ta
return _layer; return _layer;
} }
- (void)resize:(AvnPixelSize)size withScale: (float) scale;{ - (void)resize:(AvnPixelSize)size withScale: (float) scale{
@synchronized (lock) { @synchronized (lock) {
if(surface == nil if(surface == nil
|| surface->size.Width != size.Width || surface->size.Width != size.Width
|| surface->size.Height != size.Height || surface->size.Height != size.Height
|| surface->scale != scale) || surface->scale != scale)
{
surface = [[IOSurfaceHolder alloc] initWithSize:size withScale:scale withOpenGlContext:_glContext.getRaw()]; surface = [[IOSurfaceHolder alloc] initWithSize:size withScale:scale withOpenGlContext:_glContext.getRaw()];
[self updateLayer];
}
} }
} }
@ -159,12 +164,15 @@ static IAvnGlSurfaceRenderTarget* CreateGlRenderTarget(IOSurfaceRenderTarget* ta
@synchronized (lock) { @synchronized (lock) {
if(_layer == nil) if(_layer == nil)
return; return;
[CATransaction begin];
[_layer setContents: nil]; [_layer setContents: nil];
if(surface != nil) if(surface != nil)
{ {
[_layer setContentsScale: surface->scale]; [_layer setContentsScale: surface->scale];
[_layer setContents: (__bridge IOSurface*) surface->surface]; [_layer setContents: (__bridge IOSurface*) surface->surface];
} }
[CATransaction commit];
[CATransaction flush];
} }
} }
else else

18
nukebuild/Build.cs

@ -107,7 +107,7 @@ partial class Build : NukeBuild
.AddProperty("JavaSdkDirectory", GetVariable<string>("JAVA_HOME_8_X64"))) .AddProperty("JavaSdkDirectory", GetVariable<string>("JAVA_HOME_8_X64")))
.AddProperty("PackageVersion", Parameters.Version) .AddProperty("PackageVersion", Parameters.Version)
.AddProperty("iOSRoslynPathHackRequired", true) .AddProperty("iOSRoslynPathHackRequired", true)
.SetToolPath(MsBuildExe.Value) .SetProcessToolPath(MsBuildExe.Value)
.SetConfiguration(Parameters.Configuration) .SetConfiguration(Parameters.Configuration)
.SetVerbosity(MSBuildVerbosity.Minimal) .SetVerbosity(MSBuildVerbosity.Minimal)
.Apply(configurator)); .Apply(configurator));
@ -132,10 +132,10 @@ partial class Build : NukeBuild
var webappDir = RootDirectory / "src" / "Avalonia.DesignerSupport" / "Remote" / "HtmlTransport" / "webapp"; var webappDir = RootDirectory / "src" / "Avalonia.DesignerSupport" / "Remote" / "HtmlTransport" / "webapp";
NpmTasks.NpmInstall(c => c NpmTasks.NpmInstall(c => c
.SetWorkingDirectory(webappDir) .SetProcessWorkingDirectory(webappDir)
.SetArgumentConfigurator(a => a.Add("--silent"))); .SetProcessArgumentConfigurator(a => a.Add("--silent")));
NpmTasks.NpmRun(c => c NpmTasks.NpmRun(c => c
.SetWorkingDirectory(webappDir) .SetProcessWorkingDirectory(webappDir)
.SetCommand("dist")); .SetCommand("dist"));
}); });
@ -157,7 +157,7 @@ partial class Build : NukeBuild
{ {
if (Parameters.IsRunningOnWindows) if (Parameters.IsRunningOnWindows)
MsBuildCommon(Parameters.MSBuildSolution, c => c MsBuildCommon(Parameters.MSBuildSolution, c => c
.SetArgumentConfigurator(a => a.Add("/r")) .SetProcessArgumentConfigurator(a => a.Add("/r"))
.AddTargets("Build") .AddTargets("Build")
); );
@ -194,7 +194,7 @@ partial class Build : NukeBuild
var eventsProject = Path.Combine(eventsDirectory, "Avalonia.ReactiveUI.Events.csproj"); var eventsProject = Path.Combine(eventsDirectory, "Avalonia.ReactiveUI.Events.csproj");
if (Parameters.IsRunningOnWindows) if (Parameters.IsRunningOnWindows)
MsBuildCommon(eventsProject, c => c MsBuildCommon(eventsProject, c => c
.SetArgumentConfigurator(a => a.Add("/r")) .SetProcessArgumentConfigurator(a => a.Add("/r"))
.AddTargets("Build") .AddTargets("Build")
); );
else else
@ -242,10 +242,10 @@ partial class Build : NukeBuild
var webappTestDir = RootDirectory / "tests" / "Avalonia.DesignerSupport.Tests" / "Remote" / "HtmlTransport" / "webapp"; var webappTestDir = RootDirectory / "tests" / "Avalonia.DesignerSupport.Tests" / "Remote" / "HtmlTransport" / "webapp";
NpmTasks.NpmInstall(c => c NpmTasks.NpmInstall(c => c
.SetWorkingDirectory(webappTestDir) .SetProcessWorkingDirectory(webappTestDir)
.SetArgumentConfigurator(a => a.Add("--silent"))); .SetProcessArgumentConfigurator(a => a.Add("--silent")));
NpmTasks.NpmRun(c => c NpmTasks.NpmRun(c => c
.SetWorkingDirectory(webappTestDir) .SetProcessWorkingDirectory(webappTestDir)
.SetCommand("test")); .SetCommand("test"));
}); });

2
nukebuild/_build.csproj

@ -10,7 +10,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Nuke.Common" Version="0.24.0" /> <PackageReference Include="Nuke.Common" Version="5.0.0" />
<PackageReference Include="xunit.runner.console" Version="2.3.1" /> <PackageReference Include="xunit.runner.console" Version="2.3.1" />
<PackageReference Include="JetBrains.dotMemoryUnit" Version="3.0.20171219.105559" /> <PackageReference Include="JetBrains.dotMemoryUnit" Version="3.0.20171219.105559" />
<PackageReference Include="vswhere" Version="2.6.7" Condition=" '$(OS)' == 'Windows_NT' " /> <PackageReference Include="vswhere" Version="2.6.7" Condition=" '$(OS)' == 'Windows_NT' " />

30
samples/ControlCatalog/DecoratedWindow.xaml

@ -6,25 +6,21 @@
<NativeMenu.Menu> <NativeMenu.Menu>
<NativeMenu> <NativeMenu>
<NativeMenuItem Header="Decorated"> <NativeMenuItem Header="Decorated">
<NativeMenuItem.Menu> <NativeMenu>
<NativeMenu> <NativeMenuItem Header="Open"/>
<NativeMenuItem Header="Open"/> <NativeMenuItem Header="Recent">
<NativeMenuItem Header="Recent"> <NativeMenuItem.Menu>
<NativeMenuItem.Menu> <NativeMenu/>
<NativeMenu/> </NativeMenuItem.Menu>
</NativeMenuItem.Menu> </NativeMenuItem>
</NativeMenuItem> <NativeMenuItem Header="Quit Avalonia" Gesture="CMD+Q"/>
<NativeMenuItem Header="Quit Avalonia" Gesture="CMD+Q"/> </NativeMenu>
</NativeMenu>
</NativeMenuItem.Menu>
</NativeMenuItem> </NativeMenuItem>
<NativeMenuItem Header="Edit"> <NativeMenuItem Header="Edit">
<NativeMenuItem.Menu> <NativeMenu>
<NativeMenu> <NativeMenuItem Header="Copy"/>
<NativeMenuItem Header="Copy"/> <NativeMenuItem Header="Paste"/>
<NativeMenuItem Header="Paste"/> </NativeMenu>
</NativeMenu>
</NativeMenuItem.Menu>
</NativeMenuItem> </NativeMenuItem>
</NativeMenu> </NativeMenu>
</NativeMenu.Menu> </NativeMenu.Menu>

66
samples/ControlCatalog/MainWindow.xaml

@ -16,47 +16,39 @@
<NativeMenu.Menu> <NativeMenu.Menu>
<NativeMenu> <NativeMenu>
<NativeMenuItem Header="File"> <NativeMenuItem Header="File">
<NativeMenuItem.Menu> <NativeMenu>
<NativeMenu> <NativeMenuItem Icon="/Assets/test_icon.ico" Header="Open" Clicked="OnOpenClicked" Gesture="Ctrl+O"/>
<NativeMenuItem Icon="/Assets/test_icon.ico" Header="Open" Clicked="OnOpenClicked" Gesture="Ctrl+O"/> <NativeMenuItemSeperator/>
<NativeMenuItemSeperator/> <NativeMenuItem Icon="/Assets/github_icon.png" Header="Recent">
<NativeMenuItem Icon="/Assets/github_icon.png" Header="Recent"> <NativeMenu/>
<NativeMenuItem.Menu> </NativeMenuItem>
<NativeMenu/> <NativeMenuItemSeperator/>
</NativeMenuItem.Menu> <NativeMenuItem Header="{x:Static local:MainWindow.MenuQuitHeader}"
</NativeMenuItem> Gesture="{x:Static local:MainWindow.MenuQuitGesture}"
<NativeMenuItemSeperator/> Clicked="OnCloseClicked" />
<NativeMenuItem Header="{x:Static local:MainWindow.MenuQuitHeader}" </NativeMenu>
Gesture="{x:Static local:MainWindow.MenuQuitGesture}"
Clicked="OnCloseClicked" />
</NativeMenu>
</NativeMenuItem.Menu>
</NativeMenuItem> </NativeMenuItem>
<NativeMenuItem Header="Edit"> <NativeMenuItem Header="Edit">
<NativeMenuItem.Menu> <NativeMenu>
<NativeMenu> <NativeMenuItem Header="Copy"/>
<NativeMenuItem Header="Copy"/> <NativeMenuItem Header="Paste"/>
<NativeMenuItem Header="Paste"/> </NativeMenu>
</NativeMenu>
</NativeMenuItem.Menu>
</NativeMenuItem> </NativeMenuItem>
<NativeMenuItem Header="Options"> <NativeMenuItem Header="Options">
<NativeMenuItem.Menu> <NativeMenu>
<NativeMenu> <NativeMenuItem Header="Check Me (None)"
<NativeMenuItem Header="Check Me (None)" Command="{Binding ToggleMenuItemCheckedCommand}"
Command="{Binding ToggleMenuItemCheckedCommand}" ToggleType="None"
ToggleType="None" IsChecked="{Binding IsMenuItemChecked}" />
IsChecked="{Binding IsMenuItemChecked}" /> <NativeMenuItem Header="Check Me (CheckBox)"
<NativeMenuItem Header="Check Me (CheckBox)" Command="{Binding ToggleMenuItemCheckedCommand}"
Command="{Binding ToggleMenuItemCheckedCommand}" ToggleType="CheckBox"
ToggleType="CheckBox" IsChecked="{Binding IsMenuItemChecked}" />
IsChecked="{Binding IsMenuItemChecked}" /> <NativeMenuItem Header="Check Me (Radio)"
<NativeMenuItem Header="Check Me (Radio)" Command="{Binding ToggleMenuItemCheckedCommand}"
Command="{Binding ToggleMenuItemCheckedCommand}" ToggleType="Radio"
ToggleType="Radio" IsChecked="{Binding IsMenuItemChecked}" />
IsChecked="{Binding IsMenuItemChecked}" /> </NativeMenu>
</NativeMenu>
</NativeMenuItem.Menu>
</NativeMenuItem> </NativeMenuItem>
</NativeMenu> </NativeMenu>
</NativeMenu.Menu> </NativeMenu.Menu>

29
samples/ControlCatalog/Pages/DialogsPage.xaml.cs

@ -4,6 +4,7 @@ using System.Linq;
using System.Reflection; using System.Reflection;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Dialogs; using Avalonia.Dialogs;
using Avalonia.Layout;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
#pragma warning disable 4014 #pragma warning disable 4014
@ -112,11 +113,29 @@ namespace ControlCatalog.Pages
private Window CreateSampleWindow() private Window CreateSampleWindow()
{ {
var window = new Window(); Button button;
window.Height = 200;
window.Width = 200; var window = new Window
window.Content = new TextBlock { Text = "Hello world!" }; {
window.WindowStartupLocation = WindowStartupLocation.CenterOwner; Height = 200,
Width = 200,
Content = new StackPanel
{
Spacing = 4,
Children =
{
new TextBlock { Text = "Hello world!" },
(button = new Button
{
HorizontalAlignment = HorizontalAlignment.Center,
Content = "Click to close"
})
}
},
WindowStartupLocation = WindowStartupLocation.CenterOwner
};
button.Click += (_, __) => window.Close();
return window; return window;
} }

1
samples/Sandbox/MainWindow.axaml.cs

@ -1,6 +1,7 @@
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.Win32.WinRT.Composition;
namespace Sandbox namespace Sandbox
{ {

34
src/Avalonia.Base/Data/Core/TypeCastNode.cs

@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Avalonia.Data.Core
{
public class TypeCastNode : ExpressionNode
{
public override string Description => $"as {TargetType.FullName}";
public Type TargetType { get; }
public TypeCastNode(Type type)
{
TargetType = type;
}
protected virtual object Cast(object value)
{
return TargetType.IsInstanceOfType(value) ? value : null;
}
protected override void StartListeningCore(WeakReference<object> reference)
{
if (reference.TryGetTarget(out object target))
{
target = Cast(target);
reference = target == null ? NullReference : new WeakReference<object>(target);
}
base.StartListeningCore(reference);
}
}
}

24
src/Avalonia.Base/Utilities/AvaloniaPropertyValueStore.cs

@ -1,5 +1,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
#nullable enable
namespace Avalonia.Utilities namespace Avalonia.Utilities
{ {
@ -9,12 +12,14 @@ namespace Avalonia.Utilities
/// <typeparam name="TValue">Stored value type.</typeparam> /// <typeparam name="TValue">Stored value type.</typeparam>
internal sealed class AvaloniaPropertyValueStore<TValue> internal sealed class AvaloniaPropertyValueStore<TValue>
{ {
// The last item in the list is always int.MaxValue.
private static readonly Entry[] s_emptyEntries = { new Entry { PropertyId = int.MaxValue, Value = default! } };
private Entry[] _entries; private Entry[] _entries;
public AvaloniaPropertyValueStore() public AvaloniaPropertyValueStore()
{ {
// The last item in the list is always int.MaxValue _entries = s_emptyEntries;
_entries = new[] { new Entry { PropertyId = int.MaxValue, Value = default } };
} }
private (int, bool) TryFindEntry(int propertyId) private (int, bool) TryFindEntry(int propertyId)
@ -86,7 +91,7 @@ namespace Avalonia.Utilities
return (0, false); return (0, false);
} }
public bool TryGetValue(AvaloniaProperty property, out TValue value) public bool TryGetValue(AvaloniaProperty property, [MaybeNull] out TValue value)
{ {
(int index, bool found) = TryFindEntry(property.Id); (int index, bool found) = TryFindEntry(property.Id);
if (!found) if (!found)
@ -132,7 +137,18 @@ namespace Avalonia.Utilities
if (found) if (found)
{ {
Entry[] entries = new Entry[_entries.Length - 1]; var newLength = _entries.Length - 1;
// Special case - one element left means that value store is empty so we can just reuse our "empty" array.
if (newLength == 1)
{
_entries = s_emptyEntries;
return;
}
var entries = new Entry[newLength];
int ix = 0; int ix = 0;
for (int i = 0; i < _entries.Length; ++i) for (int i = 0; i < _entries.Length; ++i)

5
src/Avalonia.Base/Utilities/MathUtilities.cs

@ -5,7 +5,10 @@ namespace Avalonia.Utilities
/// <summary> /// <summary>
/// Provides math utilities not provided in System.Math. /// Provides math utilities not provided in System.Math.
/// </summary> /// </summary>
public static class MathUtilities #if !BUILDTASK
public
#endif
static class MathUtilities
{ {
// smallest such that 1.0+DoubleEpsilon != 1.0 // smallest such that 1.0+DoubleEpsilon != 1.0
internal static readonly double DoubleEpsilon = 2.2204460492503131e-016; internal static readonly double DoubleEpsilon = 2.2204460492503131e-016;

5
src/Avalonia.Base/Utilities/StringTokenizer.cs

@ -4,7 +4,10 @@ using static System.Char;
namespace Avalonia.Utilities namespace Avalonia.Utilities
{ {
public struct StringTokenizer : IDisposable #if !BUILDTASK
public
#endif
struct StringTokenizer : IDisposable
{ {
private const char DefaultSeparatorChar = ','; private const char DefaultSeparatorChar = ',';

35
src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard2.0</TargetFrameworks> <TargetFrameworks>netstandard2.0</TargetFrameworks>
<TargetFrameworks Condition="$(Configuration) == 'Debug'">netstandard2.0;netcoreapp2.0</TargetFrameworks> <TargetFrameworks Condition="$(Configuration) == 'Debug'">netstandard2.0;netcoreapp3.1</TargetFrameworks>
<OutputType>exe</OutputType> <OutputType>exe</OutputType>
<GenerateDocumentationFile>false</GenerateDocumentationFile> <GenerateDocumentationFile>false</GenerateDocumentationFile>
<BuildOutputTargetFolder>tools</BuildOutputTargetFolder> <BuildOutputTargetFolder>tools</BuildOutputTargetFolder>
@ -45,6 +45,12 @@
<Compile Include="../Avalonia.Base/Utilities/IdentifierParser.cs"> <Compile Include="../Avalonia.Base/Utilities/IdentifierParser.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link> <Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile> </Compile>
<Compile Include="../Avalonia.Base/Utilities/StringTokenizer.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Include="../Avalonia.Base/Utilities/MathUtilities.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Include="..\Markup\Avalonia.Markup\Markup\Parsers\ArgumentListParser.cs"> <Compile Include="..\Markup\Avalonia.Markup\Markup\Parsers\ArgumentListParser.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link> <Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile> </Compile>
@ -57,6 +63,33 @@
<Compile Include="../Avalonia.Base/Utilities/StyleClassParser.cs"> <Compile Include="../Avalonia.Base/Utilities/StyleClassParser.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link> <Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile> </Compile>
<Compile Include="../Avalonia.Visuals/Thickness.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Include="../Avalonia.Visuals/Size.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Include="../Avalonia.Visuals/Vector.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Include="../Avalonia.Visuals/Point.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Include="../Avalonia.Visuals/Matrix.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Include="../Avalonia.Visuals/CornerRadius.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Include="../Avalonia.Visuals/Media/Color.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Include="../Avalonia.Visuals/Media/KnownColors.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Include="../Avalonia.Controls/GridLength.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Remove="../Markup/Avalonia.Markup.Xaml.Loader\xamlil.github\**\obj\**\*.cs" /> <Compile Remove="../Markup/Avalonia.Markup.Xaml.Loader\xamlil.github\**\obj\**\*.cs" />
<Compile Remove="../Markup/Avalonia.Markup.Xaml.Loader\xamlil.github\src\XamlX\IL\SreTypeSystem.cs" /> <Compile Remove="../Markup/Avalonia.Markup.Xaml.Loader\xamlil.github\src\XamlX\IL\SreTypeSystem.cs" />
<PackageReference Include="Mono.Cecil" Version="0.11.2" /> <PackageReference Include="Mono.Cecil" Version="0.11.2" />

4
src/Avalonia.Build.Tasks/SpanCompat.cs

@ -1,3 +1,4 @@
#if !NETCOREAPP3_1
namespace System namespace System
{ {
// This is a hack to enable our span code to work inside MSBuild task without referencing System.Memory // This is a hack to enable our span code to work inside MSBuild task without referencing System.Memory
@ -63,6 +64,8 @@ namespace System
} }
public override string ToString() => _length == 0 ? string.Empty : _s.Substring(_start, _length); public override string ToString() => _length == 0 ? string.Empty : _s.Substring(_start, _length);
public static implicit operator ReadOnlySpan<T>(char[] arr) => new ReadOnlySpan<T>(new string(arr));
} }
static class SpanCompatExtensions static class SpanCompatExtensions
@ -71,3 +74,4 @@ namespace System
} }
} }
#endif

4
src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs

@ -46,7 +46,9 @@ namespace Avalonia.Build.Tasks
string output, bool verifyIl, MessageImportance logImportance, string strongNameKey, bool patchCom, string output, bool verifyIl, MessageImportance logImportance, string strongNameKey, bool patchCom,
bool skipXamlCompilation) bool skipXamlCompilation)
{ {
var typeSystem = new CecilTypeSystem(references.Concat(new[] { input }), input); var typeSystem = new CecilTypeSystem(references
.Where(r => !r.ToLowerInvariant().EndsWith("avalonia.build.tasks.dll"))
.Concat(new[] { input }), input);
var asm = typeSystem.TargetAssemblyDefinition; var asm = typeSystem.TargetAssemblyDefinition;

32
src/Avalonia.Controls/ComboBox.cs

@ -173,11 +173,10 @@ namespace Avalonia.Controls
ComboBoxItem.ContentTemplateProperty); ComboBoxItem.ContentTemplateProperty);
} }
/// <inheritdoc/> protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
{ {
base.OnAttachedToLogicalTree(e); base.OnAttachedToVisualTree(e);
this.UpdateSelectionBoxItem(this.SelectedItem); this.UpdateSelectionBoxItem(SelectedItem);
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -373,19 +372,22 @@ namespace Avalonia.Controls
if (control != null) if (control != null)
{ {
control.Measure(Size.Infinity); if (VisualRoot is object)
SelectionBoxItem = new Rectangle
{ {
Width = control.DesiredSize.Width, control.Measure(Size.Infinity);
Height = control.DesiredSize.Height,
Fill = new VisualBrush SelectionBoxItem = new Rectangle
{ {
Visual = control, Width = control.DesiredSize.Width,
Stretch = Stretch.None, Height = control.DesiredSize.Height,
AlignmentX = AlignmentX.Left, Fill = new VisualBrush
} {
}; Visual = control,
Stretch = Stretch.None,
AlignmentX = AlignmentX.Left,
}
};
}
} }
else else
{ {

10
src/Avalonia.Controls/GridLength.cs

@ -8,7 +8,10 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Defines the valid units for a <see cref="GridLength"/>. /// Defines the valid units for a <see cref="GridLength"/>.
/// </summary> /// </summary>
public enum GridUnitType #if !BUILDTASK
public
#endif
enum GridUnitType
{ {
/// <summary> /// <summary>
/// The row or column is auto-sized to fit its content. /// The row or column is auto-sized to fit its content.
@ -29,7 +32,10 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Holds the width or height of a <see cref="Grid"/>'s column and row definitions. /// Holds the width or height of a <see cref="Grid"/>'s column and row definitions.
/// </summary> /// </summary>
public struct GridLength : IEquatable<GridLength> #if !BUILDTASK
public
#endif
struct GridLength : IEquatable<GridLength>
{ {
private readonly GridUnitType _type; private readonly GridUnitType _type;

3
src/Avalonia.Controls/NativeMenuBar.cs

@ -1,7 +1,6 @@
using System; using System;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Styling;
namespace Avalonia.Controls namespace Avalonia.Controls
{ {
@ -16,7 +15,7 @@ namespace Avalonia.Controls
EnableMenuItemClickForwardingProperty.Changed.Subscribe(args => EnableMenuItemClickForwardingProperty.Changed.Subscribe(args =>
{ {
var item = (MenuItem)args.Sender; var item = (MenuItem)args.Sender;
if (args.NewValue.Equals(true)) if (args.NewValue.GetValueOrDefault<bool>())
item.Click += OnMenuItemClick; item.Click += OnMenuItemClick;
else else
item.Click -= OnMenuItemClick; item.Click -= OnMenuItemClick;

18
src/Avalonia.Controls/NativeMenuItem.cs

@ -2,6 +2,7 @@ using System;
using System.Windows.Input; using System.Windows.Input;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Media.Imaging; using Avalonia.Media.Imaging;
using Avalonia.Metadata;
using Avalonia.Utilities; using Avalonia.Utilities;
namespace Avalonia.Controls namespace Avalonia.Controls
@ -62,6 +63,7 @@ namespace Avalonia.Controls
public static readonly DirectProperty<NativeMenuItem, NativeMenu> MenuProperty = public static readonly DirectProperty<NativeMenuItem, NativeMenu> MenuProperty =
AvaloniaProperty.RegisterDirect<NativeMenuItem, NativeMenu>(nameof(Menu), o => o.Menu, (o, v) => o.Menu = v); AvaloniaProperty.RegisterDirect<NativeMenuItem, NativeMenu>(nameof(Menu), o => o.Menu, (o, v) => o.Menu = v);
[Content]
public NativeMenu Menu public NativeMenu Menu
{ {
get => _menu; get => _menu;
@ -151,7 +153,7 @@ namespace Avalonia.Controls
IsEnabled = _command?.CanExecute(null) ?? true; IsEnabled = _command?.CanExecute(null) ?? true;
} }
public bool HasClickHandlers => Clicked != null; public bool HasClickHandlers => Click != null;
public ICommand Command public ICommand Command
{ {
@ -182,11 +184,21 @@ namespace Avalonia.Controls
set { SetValue(CommandParameterProperty, value); } set { SetValue(CommandParameterProperty, value); }
} }
public event EventHandler Clicked; /// <summary>
/// Occurs when a <see cref="NativeMenuItem"/> is clicked.
/// </summary>
public event EventHandler Click;
[Obsolete("Use Click event.")]
public event EventHandler Clicked
{
add => Click += value;
remove => Click -= value;
}
void INativeMenuItemExporterEventsImplBridge.RaiseClicked() void INativeMenuItemExporterEventsImplBridge.RaiseClicked()
{ {
Clicked?.Invoke(this, new EventArgs()); Click?.Invoke(this, new EventArgs());
if (Command?.CanExecute(CommandParameter) == true) if (Command?.CanExecute(CommandParameter) == true)
{ {

2
src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs

@ -231,7 +231,7 @@ namespace Avalonia.Controls.Platform
{ {
var direction = e.Key.ToNavigationDirection(); var direction = e.Key.ToNavigationDirection();
if (direction.HasValue) if (direction?.IsDirectional() == true)
{ {
if (item == null && _isContextMenu) if (item == null && _isContextMenu)
{ {

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

@ -35,7 +35,7 @@ namespace Avalonia.Controls.Primitives
/// Defines the <see cref="Visibility"/> property. /// Defines the <see cref="Visibility"/> property.
/// </summary> /// </summary>
public static readonly StyledProperty<ScrollBarVisibility> VisibilityProperty = public static readonly StyledProperty<ScrollBarVisibility> VisibilityProperty =
AvaloniaProperty.Register<ScrollBar, ScrollBarVisibility>(nameof(Visibility)); AvaloniaProperty.Register<ScrollBar, ScrollBarVisibility>(nameof(Visibility), ScrollBarVisibility.Visible);
/// <summary> /// <summary>
/// Defines the <see cref="Orientation"/> property. /// Defines the <see cref="Orientation"/> property.

9
src/Avalonia.Controls/Primitives/TemplatedControl.cs

@ -354,11 +354,14 @@ namespace Avalonia.Controls.Primitives
{ {
control.SetValue(TemplatedParentProperty, this); control.SetValue(TemplatedParentProperty, this);
foreach (var child in control.LogicalChildren) var children = control.LogicalChildren;
var count = children.Count;
for (var i = 0; i < count; i++)
{ {
if (child is IControl c) if (children[i] is IControl child)
{ {
ApplyTemplatedParent(c); ApplyTemplatedParent(child);
} }
} }
} }

11
src/Avalonia.Controls/Slider.cs

@ -3,6 +3,7 @@ using Avalonia.Collections;
using Avalonia.Controls.Metadata; using Avalonia.Controls.Metadata;
using Avalonia.Controls.Mixins; using Avalonia.Controls.Mixins;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Data;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Layout; using Avalonia.Layout;
@ -94,6 +95,8 @@ namespace Avalonia.Controls
Thumb.DragStartedEvent.AddClassHandler<Slider>((x, e) => x.OnThumbDragStarted(e), RoutingStrategies.Bubble); Thumb.DragStartedEvent.AddClassHandler<Slider>((x, e) => x.OnThumbDragStarted(e), RoutingStrategies.Bubble);
Thumb.DragCompletedEvent.AddClassHandler<Slider>((x, e) => x.OnThumbDragCompleted(e), Thumb.DragCompletedEvent.AddClassHandler<Slider>((x, e) => x.OnThumbDragCompleted(e),
RoutingStrategies.Bubble); RoutingStrategies.Bubble);
ValueProperty.OverrideMetadata<Slider>(new DirectPropertyMetadata<double>(enableDataValidation: true));
} }
/// <summary> /// <summary>
@ -225,6 +228,14 @@ namespace Avalonia.Controls
Value = IsSnapToTickEnabled ? SnapToTick(finalValue) : finalValue; Value = IsSnapToTickEnabled ? SnapToTick(finalValue) : finalValue;
} }
protected override void UpdateDataValidation<T>(AvaloniaProperty<T> property, BindingValue<T> value)
{
if (property == ValueProperty)
{
DataValidationErrors.SetError(this, value.Error);
}
}
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
{ {
base.OnPropertyChanged(change); base.OnPropertyChanged(change);

2
src/Avalonia.Input/NavigationDirection.cs

@ -83,7 +83,7 @@ namespace Avalonia.Input
/// </returns> /// </returns>
public static bool IsDirectional(this NavigationDirection direction) public static bool IsDirectional(this NavigationDirection direction)
{ {
return direction > NavigationDirection.Previous || return direction > NavigationDirection.Previous &&
direction <= NavigationDirection.PageDown; direction <= NavigationDirection.PageDown;
} }

2
src/Avalonia.MicroCom/MicroComProxyBase.cs

@ -56,7 +56,7 @@ namespace Avalonia.MicroCom
{ {
var guid = MicroComRuntime.GetGuidFor(typeof(T)); var guid = MicroComRuntime.GetGuidFor(typeof(T));
var rv = QueryInterface(guid, out var ppv); var rv = QueryInterface(guid, out var ppv);
if (rv != 0) if (rv == 0)
return (T)MicroComRuntime.CreateProxyFor(typeof(T), ppv, true); return (T)MicroComRuntime.CreateProxyFor(typeof(T), ppv, true);
throw new COMException("QueryInterface failed", rv); throw new COMException("QueryInterface failed", rv);
} }

28
src/Avalonia.MicroCom/MicroComRuntime.cs

@ -38,13 +38,20 @@ namespace Avalonia.MicroCom
public static T CreateProxyFor<T>(IntPtr pObject, bool ownsHandle) => (T)CreateProxyFor(typeof(T), pObject, ownsHandle); public static T CreateProxyFor<T>(IntPtr pObject, bool ownsHandle) => (T)CreateProxyFor(typeof(T), pObject, ownsHandle);
public static object CreateProxyFor(Type type, IntPtr pObject, bool ownsHandle) => _factories[type](pObject, ownsHandle); public static object CreateProxyFor(Type type, IntPtr pObject, bool ownsHandle) => _factories[type](pObject, ownsHandle);
public static IntPtr GetNativeIntPtr<T>(this T obj, bool owned = false) where T : IUnknown
=> new IntPtr(GetNativePointer(obj, owned));
public static void* GetNativePointer<T>(T obj, bool owned = false) where T : IUnknown public static void* GetNativePointer<T>(T obj, bool owned = false) where T : IUnknown
{ {
if (obj == null) if (obj == null)
return null; return null;
if (obj is MicroComProxyBase proxy) if (obj is MicroComProxyBase proxy)
{
if(owned)
proxy.AddRef();
return (void*)proxy.NativePointer; return (void*)proxy.NativePointer;
}
if (obj is IMicroComShadowContainer container) if (obj is IMicroComShadowContainer container)
{ {
container.Shadow ??= new MicroComShadow(container); container.Shadow ??= new MicroComShadow(container);
@ -89,10 +96,27 @@ namespace Avalonia.MicroCom
} }
public static T CloneReference<T>(T iface) where T : IUnknown public static T CloneReference<T>(this T iface) where T : IUnknown
{ {
var proxy = (MicroComProxyBase)(object)iface;
var ownedPointer = GetNativePointer(iface, true); var ownedPointer = GetNativePointer(iface, true);
return CreateProxyFor<T>(ownedPointer, true); return CreateProxyFor<T>(ownedPointer, true);
} }
public static T QueryInterface<T>(this IUnknown unknown) where T : IUnknown
{
var proxy = (MicroComProxyBase)unknown;
return proxy.QueryInterface<T>();
}
public static void UnsafeAddRef(this IUnknown unknown)
{
((MicroComProxyBase)unknown).AddRef();
}
public static void UnsafeRelease(this IUnknown unknown)
{
((MicroComProxyBase)unknown).Release();
}
} }
} }

4
src/Avalonia.MicroCom/MicroComVtblBase.cs

@ -7,10 +7,10 @@ namespace Avalonia.MicroCom
public unsafe class MicroComVtblBase public unsafe class MicroComVtblBase
{ {
private List<IntPtr> _methods = new List<IntPtr>(); private List<IntPtr> _methods = new List<IntPtr>();
[UnmanagedFunctionPointerAttribute(CallingConvention.ThisCall)] [UnmanagedFunctionPointerAttribute(CallingConvention.StdCall)]
private delegate int AddRefDelegate(Ccw* ccw); private delegate int AddRefDelegate(Ccw* ccw);
[UnmanagedFunctionPointerAttribute(CallingConvention.ThisCall)] [UnmanagedFunctionPointerAttribute(CallingConvention.StdCall)]
private delegate int QueryInterfaceDelegate(Ccw* ccw, Guid* guid, void** ppv); private delegate int QueryInterfaceDelegate(Ccw* ccw, Guid* guid, void** ppv);
public static IntPtr Vtable { get; } = new MicroComVtblBase().CreateVTable(); public static IntPtr Vtable { get; } = new MicroComVtblBase().CreateVTable();

2
src/Avalonia.Native/AvaloniaNativeMenuExporter.cs

@ -60,7 +60,7 @@ namespace Avalonia.Native
Header = "About Avalonia", Header = "About Avalonia",
}; };
aboutItem.Clicked += async (sender, e) => aboutItem.Click += async (sender, e) =>
{ {
var dialog = new AboutAvaloniaDialog(); var dialog = new AboutAvaloniaDialog();

8
src/Avalonia.Native/Extensions.cs

@ -0,0 +1,8 @@
namespace Avalonia.Native
{
internal static class Extensions
{
public static int AsComBool(this bool b) => b ? 1 : 0;
public static bool FromComBool(this int b) => b != 0;
}
}

2
src/Avalonia.Native/IAvnMenuItem.cs

@ -24,7 +24,7 @@ namespace Avalonia.Native.Interop.Impl
private void UpdateTitle(string title) => SetTitle(title ?? ""); private void UpdateTitle(string title) => SetTitle(title ?? "");
private void UpdateIsChecked(bool isChecked) => SetIsChecked(isChecked); private void UpdateIsChecked(bool isChecked) => SetIsChecked(isChecked.AsComBool());
private void UpdateToggleType(NativeMenuItemToggleType toggleType) private void UpdateToggleType(NativeMenuItemToggleType toggleType)
{ {

6
src/Avalonia.Native/PlatformThreadingInterface.cs

@ -33,9 +33,9 @@ namespace Avalonia.Native
_parent = parent; _parent = parent;
} }
public void Signaled(int priority, bool priorityContainsMeaningfulValue) public void Signaled(int priority, int priorityContainsMeaningfulValue)
{ {
_parent.Signaled?.Invoke(priorityContainsMeaningfulValue ? (DispatcherPriority?)priority : null); _parent.Signaled?.Invoke(priorityContainsMeaningfulValue.FromComBool() ? (DispatcherPriority?)priority : null);
} }
} }
@ -50,7 +50,7 @@ namespace Avalonia.Native
_native.SetSignaledCallback(cb); _native.SetSignaledCallback(cb);
} }
public bool CurrentThreadIsLoopThread => _native.CurrentThreadIsLoopThread; public bool CurrentThreadIsLoopThread => _native.CurrentThreadIsLoopThread.FromComBool();
public event Action<DispatcherPriority?> Signaled; public event Action<DispatcherPriority?> Signaled;

4
src/Avalonia.Native/PopupImpl.cs

@ -50,9 +50,9 @@ namespace Avalonia.Native
// NOP on Popup // NOP on Popup
} }
bool IAvnWindowEvents.Closing() int IAvnWindowEvents.Closing()
{ {
return true; return true.AsComBool();
} }
void IAvnWindowEvents.WindowStateChanged(AvnWindowState state) void IAvnWindowEvents.WindowStateChanged(AvnWindowState state)

4
src/Avalonia.Native/PredicateCallback.cs

@ -12,9 +12,9 @@ namespace Avalonia.Native
_predicate = predicate; _predicate = predicate;
} }
bool IAvnPredicateCallback.Evaluate() int IAvnPredicateCallback.Evaluate()
{ {
return _predicate(); return _predicate().AsComBool();
} }
} }
} }

2
src/Avalonia.Native/ScreenImpl.cs

@ -33,7 +33,7 @@ namespace Avalonia.Native
screen.PixelDensity, screen.PixelDensity,
screen.Bounds.ToAvaloniaPixelRect(), screen.Bounds.ToAvaloniaPixelRect(),
screen.WorkingArea.ToAvaloniaPixelRect(), screen.WorkingArea.ToAvaloniaPixelRect(),
screen.Primary); screen.Primary.FromComBool());
} }
return result; return result;

2
src/Avalonia.Native/SystemDialogs.cs

@ -26,7 +26,7 @@ namespace Avalonia.Native
if (dialog is OpenFileDialog ofd) if (dialog is OpenFileDialog ofd)
{ {
_native.OpenFileDialog(nativeParent, _native.OpenFileDialog(nativeParent,
events, ofd.AllowMultiple, events, ofd.AllowMultiple.AsComBool(),
ofd.Title ?? "", ofd.Title ?? "",
ofd.Directory ?? "", ofd.Directory ?? "",
ofd.InitialFileName ?? "", ofd.InitialFileName ?? "",

12
src/Avalonia.Native/WindowImpl.cs

@ -42,14 +42,14 @@ namespace Avalonia.Native
_parent = parent; _parent = parent;
} }
bool IAvnWindowEvents.Closing() int IAvnWindowEvents.Closing()
{ {
if (_parent.Closing != null) if (_parent.Closing != null)
{ {
return _parent.Closing(); return _parent.Closing().AsComBool();
} }
return true; return true.AsComBool();
} }
void IAvnWindowEvents.WindowStateChanged(AvnWindowState state) void IAvnWindowEvents.WindowStateChanged(AvnWindowState state)
@ -69,7 +69,7 @@ namespace Avalonia.Native
public void CanResize(bool value) public void CanResize(bool value)
{ {
_native.SetCanResize(value); _native.SetCanResize(value.AsComBool());
} }
public void SetSystemDecorations(Controls.SystemDecorations enabled) public void SetSystemDecorations(Controls.SystemDecorations enabled)
@ -145,7 +145,7 @@ namespace Avalonia.Native
{ {
_isExtended = extendIntoClientAreaHint; _isExtended = extendIntoClientAreaHint;
_native.SetExtendClientArea(extendIntoClientAreaHint); _native.SetExtendClientArea(extendIntoClientAreaHint.AsComBool());
InvalidateExtendedMargins(); InvalidateExtendedMargins();
} }
@ -198,7 +198,7 @@ namespace Avalonia.Native
public void SetEnabled(bool enable) public void SetEnabled(bool enable)
{ {
_native.SetEnabled(enable); _native.SetEnabled(enable.AsComBool());
} }
} }
} }

14
src/Avalonia.Native/WindowImplBase.cs

@ -174,7 +174,7 @@ namespace Avalonia.Native
void IAvnWindowBaseEvents.Resized(AvnSize* size) void IAvnWindowBaseEvents.Resized(AvnSize* size)
{ {
if (_parent._native != null) if (_parent?._native != null)
{ {
var s = new Size(size->Width, size->Height); var s = new Size(size->Width, size->Height);
_parent._savedLogicalSize = s; _parent._savedLogicalSize = s;
@ -192,14 +192,14 @@ namespace Avalonia.Native
_parent.RawMouseEvent(type, timeStamp, modifiers, point, delta); _parent.RawMouseEvent(type, timeStamp, modifiers, point, delta);
} }
bool IAvnWindowBaseEvents.RawKeyEvent(AvnRawKeyEventType type, uint timeStamp, AvnInputModifiers modifiers, uint key) int IAvnWindowBaseEvents.RawKeyEvent(AvnRawKeyEventType type, uint timeStamp, AvnInputModifiers modifiers, uint key)
{ {
return _parent.RawKeyEvent(type, timeStamp, modifiers, key); return _parent.RawKeyEvent(type, timeStamp, modifiers, key).AsComBool();
} }
bool IAvnWindowBaseEvents.RawTextInputEvent(uint timeStamp, string text) int IAvnWindowBaseEvents.RawTextInputEvent(uint timeStamp, string text)
{ {
return _parent.RawTextInputEvent(timeStamp, text); return _parent.RawTextInputEvent(timeStamp, text).AsComBool();
} }
@ -388,7 +388,7 @@ namespace Avalonia.Native
public void SetTopmost(bool value) public void SetTopmost(bool value)
{ {
_native.SetTopMost(value); _native.SetTopMost(value.AsComBool());
} }
public double RenderScaling => _native?.Scaling ?? 1; public double RenderScaling => _native?.Scaling ?? 1;
@ -456,7 +456,7 @@ namespace Avalonia.Native
TransparencyLevel = transparencyLevel; TransparencyLevel = transparencyLevel;
_native?.SetBlurEnabled(TransparencyLevel >= WindowTransparencyLevel.Blur); _native?.SetBlurEnabled((TransparencyLevel >= WindowTransparencyLevel.Blur).AsComBool());
TransparencyLevelChanged?.Invoke(TransparencyLevel); TransparencyLevelChanged?.Invoke(TransparencyLevel);
} }
} }

1
src/Avalonia.Native/avn.idl

@ -1,5 +1,6 @@
@clr-namespace Avalonia.Native.Interop @clr-namespace Avalonia.Native.Interop
@clr-access internal @clr-access internal
@clr-map bool int
@cpp-preamble @@ @cpp-preamble @@
#include "com.h" #include "com.h"
#include "key.h" #include "key.h"

8
src/Avalonia.OpenGL/Egl/EglContext.cs

@ -93,6 +93,14 @@ namespace Avalonia.OpenGL.Egl
return MakeCurrent(); return MakeCurrent();
} }
public IDisposable EnsureLocked()
{
if (IsCurrent)
return Disposable.Empty;
Monitor.Enter(_lock);
return Disposable.Create(() => Monitor.Exit(_lock));
}
public bool IsSharedWith(IGlContext context) public bool IsSharedWith(IGlContext context)
{ {
var c = (EglContext)context; var c = (EglContext)context;

3
src/Avalonia.Styling/ApiCompatBaseline.txt

@ -0,0 +1,3 @@
Compat issues with assembly Avalonia.Styling:
MembersMustExist : Member 'public System.IObservable<System.Object> Avalonia.Controls.ResourceNodeExtensions.GetResourceObservable(Avalonia.IStyledElement, System.Object, System.Func<System.Object, System.Object>)' does not exist in the implementation but it does exist in the contract.
Total Issues: 1

13
src/Avalonia.Styling/Controls/ResourceNodeExtensions.cs

@ -60,7 +60,7 @@ namespace Avalonia.Controls
} }
public static IObservable<object?> GetResourceObservable( public static IObservable<object?> GetResourceObservable(
this IStyledElement control, this IResourceHost control,
object key, object key,
Func<object?, object?>? converter = null) Func<object?, object?>? converter = null)
{ {
@ -83,11 +83,11 @@ namespace Avalonia.Controls
private class ResourceObservable : LightweightObservableBase<object?> private class ResourceObservable : LightweightObservableBase<object?>
{ {
private readonly IStyledElement _target; private readonly IResourceHost _target;
private readonly object _key; private readonly object _key;
private readonly Func<object?, object?>? _converter; private readonly Func<object?, object?>? _converter;
public ResourceObservable(IStyledElement target, object key, Func<object?, object?>? converter) public ResourceObservable(IResourceHost target, object key, Func<object?, object?>? converter)
{ {
_target = target; _target = target;
_key = key; _key = key;
@ -147,13 +147,16 @@ namespace Avalonia.Controls
{ {
if (_target.Owner is object) if (_target.Owner is object)
{ {
observer.OnNext(Convert(_target.Owner?.FindResource(_key))); observer.OnNext(Convert(_target.Owner.FindResource(_key)));
} }
} }
private void PublishNext() private void PublishNext()
{ {
PublishNext(Convert(_target.Owner?.FindResource(_key))); if (_target.Owner is object)
{
PublishNext(Convert(_target.Owner.FindResource(_key)));
}
} }
private void OwnerChanged(object sender, EventArgs e) private void OwnerChanged(object sender, EventArgs e)

48
src/Avalonia.Styling/StyledElement.cs

@ -1,8 +1,8 @@
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.ComponentModel; using System.ComponentModel;
using System.Linq;
using Avalonia.Animation; using Avalonia.Animation;
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Controls; using Avalonia.Controls;
@ -479,16 +479,16 @@ namespace Avalonia
switch (e.Action) switch (e.Action)
{ {
case NotifyCollectionChangedAction.Add: case NotifyCollectionChangedAction.Add:
SetLogicalParent(e.NewItems.Cast<ILogical>()); SetLogicalParent(e.NewItems);
break; break;
case NotifyCollectionChangedAction.Remove: case NotifyCollectionChangedAction.Remove:
ClearLogicalParent(e.OldItems.Cast<ILogical>()); ClearLogicalParent(e.OldItems);
break; break;
case NotifyCollectionChangedAction.Replace: case NotifyCollectionChangedAction.Replace:
ClearLogicalParent(e.OldItems.Cast<ILogical>()); ClearLogicalParent(e.OldItems);
SetLogicalParent(e.NewItems.Cast<ILogical>()); SetLogicalParent(e.NewItems);
break; break;
case NotifyCollectionChangedAction.Reset: case NotifyCollectionChangedAction.Reset:
@ -702,13 +702,32 @@ namespace Avalonia
OnDataContextChanged(EventArgs.Empty); OnDataContextChanged(EventArgs.Empty);
} }
private void SetLogicalParent(IEnumerable<ILogical> children) private void SetLogicalParent(IList children)
{ {
foreach (var i in children) var count = children.Count;
for (var i = 0; i < count; i++)
{
var logical = (ILogical) children[i];
if (logical.LogicalParent is null)
{
((ISetLogicalParent)logical).SetParent(this);
}
}
}
private void ClearLogicalParent(IList children)
{
var count = children.Count;
for (var i = 0; i < count; i++)
{ {
if (i.LogicalParent == null) var logical = (ILogical) children[i];
if (logical.LogicalParent == this)
{ {
((ISetLogicalParent)i).SetParent(this); ((ISetLogicalParent)logical).SetParent(null);
} }
} }
} }
@ -784,17 +803,6 @@ namespace Avalonia
} }
} }
private void ClearLogicalParent(IEnumerable<ILogical> children)
{
foreach (var i in children)
{
if (i.LogicalParent == this)
{
((ISetLogicalParent)i).SetParent(null);
}
}
}
private void NotifyResourcesChanged( private void NotifyResourcesChanged(
ResourcesChangedEventArgs? e = null, ResourcesChangedEventArgs? e = null,
bool propagate = true) bool propagate = true)

15
src/Avalonia.Themes.Default/PathIcon.xaml

@ -2,16 +2,19 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style Selector="PathIcon"> <Style Selector="PathIcon">
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundColor}" /> <Setter Property="Foreground" Value="{DynamicResource ThemeForegroundColor}" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Height" Value="{DynamicResource IconElementThemeHeight}" /> <Setter Property="Height" Value="{DynamicResource IconElementThemeHeight}" />
<Setter Property="Width" Value="{DynamicResource IconElementThemeWidth}" /> <Setter Property="Width" Value="{DynamicResource IconElementThemeWidth}" />
<Setter Property="Template"> <Setter Property="Template">
<ControlTemplate> <ControlTemplate>
<Viewbox Height="{TemplateBinding Height}" <Border Background="{TemplateBinding Background}">
Width="{TemplateBinding Width}"> <Viewbox Height="{TemplateBinding Height}"
<Path Fill="{TemplateBinding Foreground}" Width="{TemplateBinding Width}">
Data="{TemplateBinding Data}" <Path Fill="{TemplateBinding Foreground}"
Stretch="Uniform" /> Data="{TemplateBinding Data}"
</Viewbox> Stretch="Uniform" />
</Viewbox>
</Border>
</ControlTemplate> </ControlTemplate>
</Setter> </Setter>
</Style> </Style>

2
src/Avalonia.Themes.Default/ToggleSwitch.xaml

@ -5,7 +5,7 @@
<Thickness x:Key="ToggleSwitchTopHeaderMargin">0,0,0,6</Thickness> <Thickness x:Key="ToggleSwitchTopHeaderMargin">0,0,0,6</Thickness>
<GridLength x:Key="ToggleSwitchPreContentMargin">6</GridLength> <GridLength x:Key="ToggleSwitchPreContentMargin">6</GridLength>
<GridLength x:Key="ToggleSwitchPostContentMargin">6</GridLength> <GridLength x:Key="ToggleSwitchPostContentMargin">6</GridLength>
<x:Double x:Key="ToggleSwitchThemeMinWidth">154</x:Double> <x:Double x:Key="ToggleSwitchThemeMinWidth">0</x:Double>
<Thickness x:Key="ToggleSwitchOnStrokeThickness">0</Thickness> <Thickness x:Key="ToggleSwitchOnStrokeThickness">0</Thickness>
<Thickness x:Key="ToggleSwitchOuterBorderStrokeThickness">1</Thickness> <Thickness x:Key="ToggleSwitchOuterBorderStrokeThickness">1</Thickness>
<SolidColorBrush x:Key="ToggleSwitchContentForeground" Color="{DynamicResource ThemeForegroundColor}" /> <SolidColorBrush x:Key="ToggleSwitchContentForeground" Color="{DynamicResource ThemeForegroundColor}" />

15
src/Avalonia.Themes.Fluent/PathIcon.xaml

@ -10,16 +10,19 @@
</Design.PreviewWith> </Design.PreviewWith>
<Style Selector="PathIcon"> <Style Selector="PathIcon">
<Setter Property="Foreground" Value="{DynamicResource TextControlForeground}" /> <Setter Property="Foreground" Value="{DynamicResource TextControlForeground}" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Height" Value="{DynamicResource IconElementThemeHeight}" /> <Setter Property="Height" Value="{DynamicResource IconElementThemeHeight}" />
<Setter Property="Width" Value="{DynamicResource IconElementThemeWidth}" /> <Setter Property="Width" Value="{DynamicResource IconElementThemeWidth}" />
<Setter Property="Template"> <Setter Property="Template">
<ControlTemplate> <ControlTemplate>
<Viewbox Height="{TemplateBinding Height}" <Border Background="{TemplateBinding Background}">
Width="{TemplateBinding Width}"> <Viewbox Height="{TemplateBinding Height}"
<Path Fill="{TemplateBinding Foreground}" Width="{TemplateBinding Width}">
Data="{TemplateBinding Data}" <Path Fill="{TemplateBinding Foreground}"
Stretch="Uniform" /> Data="{TemplateBinding Data}"
</Viewbox> Stretch="Uniform" />
</Viewbox>
</Border>
</ControlTemplate> </ControlTemplate>
</Setter> </Setter>
</Style> </Style>

2
src/Avalonia.Themes.Fluent/ToggleSwitch.xaml

@ -5,7 +5,7 @@
<Thickness x:Key="ToggleSwitchTopHeaderMargin">0,0,0,6</Thickness> <Thickness x:Key="ToggleSwitchTopHeaderMargin">0,0,0,6</Thickness>
<GridLength x:Key="ToggleSwitchPreContentMargin">6</GridLength> <GridLength x:Key="ToggleSwitchPreContentMargin">6</GridLength>
<GridLength x:Key="ToggleSwitchPostContentMargin">6</GridLength> <GridLength x:Key="ToggleSwitchPostContentMargin">6</GridLength>
<x:Double x:Key="ToggleSwitchThemeMinWidth">154</x:Double> <x:Double x:Key="ToggleSwitchThemeMinWidth">0</x:Double>
</Styles.Resources> </Styles.Resources>
<Design.PreviewWith> <Design.PreviewWith>
<StackPanel Margin="20" Width="250" Spacing="24" > <StackPanel Margin="20" Width="250" Spacing="24" >

42
src/Avalonia.Visuals/ApiCompatBaseline.txt

@ -1,39 +1,5 @@
Compat issues with assembly Avalonia.Visuals: Compat issues with assembly Avalonia.Visuals:
MembersMustExist : Member 'public void Avalonia.Media.DrawingContext.DrawGlyphRun(Avalonia.Media.IBrush, Avalonia.Media.GlyphRun, Avalonia.Point)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.StyledProperty<System.Collections.Generic.IReadOnlyList<System.Double>> Avalonia.StyledProperty<System.Collections.Generic.IReadOnlyList<System.Double>> Avalonia.Media.DashStyle.DashesProperty' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.Media.Typeface Avalonia.Media.FontManager.GetOrAddTypeface(Avalonia.Media.FontFamily, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public System.Collections.Generic.IReadOnlyList<System.Double> Avalonia.Media.DashStyle.Dashes.get()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.Media.Typeface Avalonia.Media.FontManager.MatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public void Avalonia.Media.DashStyle.Dashes.set(System.Collections.Generic.IReadOnlyList<System.Double>)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.Rect Avalonia.Media.Geometry.GetRenderBounds(Avalonia.Media.Pen)' does not exist in the implementation but it does exist in the contract. Total Issues: 3
MembersMustExist : Member 'public System.Boolean Avalonia.Media.Geometry.StrokeContains(Avalonia.Media.Pen, Avalonia.Point)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.Rect Avalonia.Media.GlyphRun.Bounds.get()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.StyledProperty<Avalonia.Point> Avalonia.StyledProperty<Avalonia.Point> Avalonia.Media.GlyphRunDrawing.BaselineOriginProperty' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.Point Avalonia.Media.GlyphRunDrawing.BaselineOrigin.get()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Media.GlyphRunDrawing.BaselineOrigin.set(Avalonia.Point)' does not exist in the implementation but it does exist in the contract.
CannotSealType : Type 'Avalonia.Media.Typeface' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract.
TypeCannotChangeClassification : Type 'Avalonia.Media.Typeface' is a 'struct' in the implementation but is a 'class' in the contract.
CannotMakeMemberNonVirtual : Member 'public System.Boolean Avalonia.Media.Typeface.Equals(System.Object)' is non-virtual in the implementation but is virtual in the contract.
CannotMakeMemberNonVirtual : Member 'public System.Int32 Avalonia.Media.Typeface.GetHashCode()' is non-virtual in the implementation but is virtual in the contract.
TypesMustExist : Type 'Avalonia.Media.Fonts.FontKey' does not exist in the implementation but it does exist in the contract.
CannotAddAbstractMembers : Member 'public Avalonia.Size Avalonia.Media.TextFormatting.DrawableTextRun.Size' is abstract in the implementation but is missing in the contract.
MembersMustExist : Member 'public Avalonia.Rect Avalonia.Media.TextFormatting.DrawableTextRun.Bounds.get()' does not exist in the implementation but it does exist in the contract.
CannotAddAbstractMembers : Member 'public void Avalonia.Media.TextFormatting.DrawableTextRun.Draw(Avalonia.Media.DrawingContext)' is abstract in the implementation but is missing in the contract.
MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.DrawableTextRun.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' does not exist in the implementation but it does exist in the contract.
CannotAddAbstractMembers : Member 'public Avalonia.Size Avalonia.Media.TextFormatting.DrawableTextRun.Size.get()' is abstract in the implementation but is missing in the contract.
MembersMustExist : Member 'public Avalonia.Rect Avalonia.Media.TextFormatting.ShapedTextCharacters.Bounds.get()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.ShapedTextCharacters.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextLayout.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' does not exist in the implementation but it does exist in the contract.
CannotAddAbstractMembers : Member 'public Avalonia.Media.TextFormatting.TextLineBreak Avalonia.Media.TextFormatting.TextLine.TextLineBreak' is abstract in the implementation but is missing in the contract.
CannotAddAbstractMembers : Member 'public void Avalonia.Media.TextFormatting.TextLine.Draw(Avalonia.Media.DrawingContext)' is abstract in the implementation but is missing in the contract.
MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextLine.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.Media.TextFormatting.TextLineBreak Avalonia.Media.TextFormatting.TextLine.LineBreak.get()' does not exist in the implementation but it does exist in the contract.
CannotAddAbstractMembers : Member 'public Avalonia.Media.TextFormatting.TextLineBreak Avalonia.Media.TextFormatting.TextLine.TextLineBreak.get()' is abstract in the implementation but is missing in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IDrawingContextLayerImpl Avalonia.Platform.IDrawingContextImpl.CreateLayer(Avalonia.Size)' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IRenderTargetBitmapImpl Avalonia.Platform.IDrawingContextImpl.CreateLayer(Avalonia.Size)' is present in the contract but not in the implementation.
MembersMustExist : Member 'public Avalonia.Platform.IRenderTargetBitmapImpl Avalonia.Platform.IDrawingContextImpl.CreateLayer(Avalonia.Size)' does not exist in the implementation but it does exist in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IDrawingContextImpl.DrawGlyphRun(Avalonia.Media.IBrush, Avalonia.Media.GlyphRun)' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IDrawingContextImpl.DrawGlyphRun(Avalonia.Media.IBrush, Avalonia.Media.GlyphRun, Avalonia.Point)' is present in the contract but not in the implementation.
MembersMustExist : Member 'public void Avalonia.Platform.IDrawingContextImpl.DrawGlyphRun(Avalonia.Media.IBrush, Avalonia.Media.GlyphRun, Avalonia.Point)' does not exist in the implementation but it does exist in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Fonts.FontKey)' is present in the contract but not in the implementation.
MembersMustExist : Member 'public System.Boolean Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Fonts.FontKey)' does not exist in the implementation but it does exist in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Typeface)' is present in the implementation but not in the contract.
MembersMustExist : Member 'public Avalonia.Utilities.IRef<Avalonia.Platform.IRenderTargetBitmapImpl> Avalonia.Rendering.RenderLayer.Bitmap.get()' does not exist in the implementation but it does exist in the contract.
Total Issues: 37

9
src/Avalonia.Visuals/CornerRadius.cs

@ -1,6 +1,8 @@
using System; using System;
using System.Globalization; using System.Globalization;
#if !BUILDTASK
using Avalonia.Animation.Animators; using Avalonia.Animation.Animators;
#endif
using Avalonia.Utilities; using Avalonia.Utilities;
namespace Avalonia namespace Avalonia
@ -8,11 +10,16 @@ namespace Avalonia
/// <summary> /// <summary>
/// Represents the radii of a rectangle's corners. /// Represents the radii of a rectangle's corners.
/// </summary> /// </summary>
public readonly struct CornerRadius : IEquatable<CornerRadius> #if !BUILDTASK
public
#endif
readonly struct CornerRadius : IEquatable<CornerRadius>
{ {
static CornerRadius() static CornerRadius()
{ {
#if !BUILDTASK
Animation.Animation.RegisterAnimator<CornerRadiusAnimator>(prop => typeof(CornerRadius).IsAssignableFrom(prop.PropertyType)); Animation.Animation.RegisterAnimator<CornerRadiusAnimator>(prop => typeof(CornerRadius).IsAssignableFrom(prop.PropertyType));
#endif
} }
public CornerRadius(double uniformRadius) public CornerRadius(double uniformRadius)

5
src/Avalonia.Visuals/Matrix.cs

@ -7,7 +7,10 @@ namespace Avalonia
/// <summary> /// <summary>
/// A 2x3 matrix. /// A 2x3 matrix.
/// </summary> /// </summary>
public readonly struct Matrix : IEquatable<Matrix> #if !BUILDTASK
public
#endif
readonly struct Matrix : IEquatable<Matrix>
{ {
private readonly double _m11; private readonly double _m11;
private readonly double _m12; private readonly double _m12;

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

@ -1,17 +1,24 @@
using System; using System;
using System.Globalization; using System.Globalization;
#if !BUILDTASK
using Avalonia.Animation.Animators; using Avalonia.Animation.Animators;
#endif
namespace Avalonia.Media namespace Avalonia.Media
{ {
/// <summary> /// <summary>
/// An ARGB color. /// An ARGB color.
/// </summary> /// </summary>
public readonly struct Color : IEquatable<Color> #if !BUILDTASK
public
#endif
readonly struct Color : IEquatable<Color>
{ {
static Color() static Color()
{ {
#if !BUILDTASK
Animation.Animation.RegisterAnimator<ColorAnimator>(prop => typeof(Color).IsAssignableFrom(prop.PropertyType)); Animation.Animation.RegisterAnimator<ColorAnimator>(prop => typeof(Color).IsAssignableFrom(prop.PropertyType));
#endif
} }
/// <summary> /// <summary>
@ -223,7 +230,12 @@ namespace Avalonia.Media
if (input.Length == 3 || input.Length == 4) if (input.Length == 3 || input.Length == 4)
{ {
var extendedLength = 2 * input.Length; var extendedLength = 2 * input.Length;
#if !BUILDTASK
Span<char> extended = stackalloc char[extendedLength]; Span<char> extended = stackalloc char[extendedLength];
#else
char[] extended = new char[extendedLength];
#endif
for (int i = 0; i < input.Length; i++) for (int i = 0; i < input.Length; i++)
{ {

75
src/Avalonia.Visuals/Media/DashStyle.cs

@ -1,11 +1,14 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using Avalonia.Animation;
using Avalonia.Collections;
using Avalonia.Media.Immutable;
#nullable enable
namespace Avalonia.Media namespace Avalonia.Media
{ {
using System;
using System.Collections.Generic;
using System.Linq;
using Avalonia.Animation;
using Avalonia.Media.Immutable;
/// <summary> /// <summary>
/// Represents the sequence of dashes and gaps that will be applied by a <see cref="Pen"/>. /// Represents the sequence of dashes and gaps that will be applied by a <see cref="Pen"/>.
/// </summary> /// </summary>
@ -14,8 +17,8 @@ namespace Avalonia.Media
/// <summary> /// <summary>
/// Defines the <see cref="Dashes"/> property. /// Defines the <see cref="Dashes"/> property.
/// </summary> /// </summary>
public static readonly StyledProperty<IReadOnlyList<double>> DashesProperty = public static readonly StyledProperty<AvaloniaList<double>> DashesProperty =
AvaloniaProperty.Register<DashStyle, IReadOnlyList<double>>(nameof(Dashes)); AvaloniaProperty.Register<DashStyle, AvaloniaList<double>>(nameof(Dashes));
/// <summary> /// <summary>
/// Defines the <see cref="Offset"/> property. /// Defines the <see cref="Offset"/> property.
@ -23,10 +26,10 @@ namespace Avalonia.Media
public static readonly StyledProperty<double> OffsetProperty = public static readonly StyledProperty<double> OffsetProperty =
AvaloniaProperty.Register<DashStyle, double>(nameof(Offset)); AvaloniaProperty.Register<DashStyle, double>(nameof(Offset));
private static ImmutableDashStyle s_dash; private static ImmutableDashStyle? s_dash;
private static ImmutableDashStyle s_dot; private static ImmutableDashStyle? s_dot;
private static ImmutableDashStyle s_dashDot; private static ImmutableDashStyle? s_dashDot;
private static ImmutableDashStyle s_dashDotDot; private static ImmutableDashStyle? s_dashDotDot;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="DashStyle"/> class. /// Initializes a new instance of the <see cref="DashStyle"/> class.
@ -41,9 +44,9 @@ namespace Avalonia.Media
/// </summary> /// </summary>
/// <param name="dashes">The dashes collection.</param> /// <param name="dashes">The dashes collection.</param>
/// <param name="offset">The dash sequence offset.</param> /// <param name="offset">The dash sequence offset.</param>
public DashStyle(IEnumerable<double> dashes, double offset) public DashStyle(IEnumerable<double>? dashes, double offset)
{ {
Dashes = (IReadOnlyList<double>)dashes?.ToList() ?? Array.Empty<double>(); Dashes = (dashes as AvaloniaList<double>) ?? new AvaloniaList<double>(dashes ?? Array.Empty<double>());
Offset = offset; Offset = offset;
} }
@ -61,31 +64,27 @@ namespace Avalonia.Media
/// <summary> /// <summary>
/// Represents a dashed <see cref="DashStyle"/>. /// Represents a dashed <see cref="DashStyle"/>.
/// </summary> /// </summary>
public static IDashStyle Dash => public static IDashStyle Dash => s_dash ??= new ImmutableDashStyle(new double[] { 2, 2 }, 1);
s_dash ?? (s_dash = new ImmutableDashStyle(new double[] { 2, 2 }, 1));
/// <summary> /// <summary>
/// Represents a dotted <see cref="DashStyle"/>. /// Represents a dotted <see cref="DashStyle"/>.
/// </summary> /// </summary>
public static IDashStyle Dot => public static IDashStyle Dot => s_dot ??= new ImmutableDashStyle(new double[] { 0, 2 }, 0);
s_dot ?? (s_dot = new ImmutableDashStyle(new double[] { 0, 2 }, 0));
/// <summary> /// <summary>
/// Represents a dashed dotted <see cref="DashStyle"/>. /// Represents a dashed dotted <see cref="DashStyle"/>.
/// </summary> /// </summary>
public static IDashStyle DashDot => public static IDashStyle DashDot => s_dashDot ??= new ImmutableDashStyle(new double[] { 2, 2, 0, 2 }, 1);
s_dashDot ?? (s_dashDot = new ImmutableDashStyle(new double[] { 2, 2, 0, 2 }, 1));
/// <summary> /// <summary>
/// Represents a dashed double dotted <see cref="DashStyle"/>. /// Represents a dashed double dotted <see cref="DashStyle"/>.
/// </summary> /// </summary>
public static IDashStyle DashDotDot => public static IDashStyle DashDotDot => s_dashDotDot ??= new ImmutableDashStyle(new double[] { 2, 2, 0, 2, 0, 2 }, 1);
s_dashDotDot ?? (s_dashDotDot = new ImmutableDashStyle(new double[] { 2, 2, 0, 2, 0, 2 }, 1));
/// <summary> /// <summary>
/// Gets or sets the length of alternating dashes and gaps. /// Gets or sets the length of alternating dashes and gaps.
/// </summary> /// </summary>
public IReadOnlyList<double> Dashes public AvaloniaList<double> Dashes
{ {
get => GetValue(DashesProperty); get => GetValue(DashesProperty);
set => SetValue(DashesProperty, value); set => SetValue(DashesProperty, value);
@ -100,15 +99,43 @@ namespace Avalonia.Media
set => SetValue(OffsetProperty, value); set => SetValue(OffsetProperty, value);
} }
IReadOnlyList<double> IDashStyle.Dashes => Dashes;
/// <summary> /// <summary>
/// Raised when the dash style changes. /// Raised when the dash style changes.
/// </summary> /// </summary>
public event EventHandler Invalidated; public event EventHandler? Invalidated;
/// <summary> /// <summary>
/// Returns an immutable clone of the <see cref="DashStyle"/>. /// Returns an immutable clone of the <see cref="DashStyle"/>.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public ImmutableDashStyle ToImmutable() => new ImmutableDashStyle(Dashes, Offset); public ImmutableDashStyle ToImmutable() => new ImmutableDashStyle(Dashes, Offset);
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
{
base.OnPropertyChanged(change);
if (change.Property == DashesProperty)
{
var oldValue = change.OldValue.GetValueOrDefault<AvaloniaList<double>>();
var newValue = change.NewValue.GetValueOrDefault<AvaloniaList<double>>();
if (oldValue is object)
{
oldValue.CollectionChanged -= DashesChanged;
}
if (newValue is object)
{
newValue.CollectionChanged += DashesChanged;
}
}
}
private void DashesChanged(object sender, NotifyCollectionChangedEventArgs e)
{
Invalidated?.Invoke(this, e);
}
} }
} }

56
src/Avalonia.Visuals/Media/EllipseGeometry.cs

@ -12,10 +12,28 @@ namespace Avalonia.Media
/// </summary> /// </summary>
public static readonly StyledProperty<Rect> RectProperty = public static readonly StyledProperty<Rect> RectProperty =
AvaloniaProperty.Register<EllipseGeometry, Rect>(nameof(Rect)); AvaloniaProperty.Register<EllipseGeometry, Rect>(nameof(Rect));
/// <summary>
/// Defines the <see cref="RadiusX"/> property.
/// </summary>
public static readonly StyledProperty<double> RadiusXProperty =
AvaloniaProperty.Register<EllipseGeometry, double>(nameof(RadiusX));
/// <summary>
/// Defines the <see cref="RadiusY"/> property.
/// </summary>
public static readonly StyledProperty<double> RadiusYProperty =
AvaloniaProperty.Register<EllipseGeometry, double>(nameof(RadiusY));
/// <summary>
/// Defines the <see cref="Center"/> property.
/// </summary>
public static readonly StyledProperty<Point> CenterProperty =
AvaloniaProperty.Register<EllipseGeometry, Point>(nameof(Center));
static EllipseGeometry() static EllipseGeometry()
{ {
AffectsGeometry(RectProperty); AffectsGeometry(RectProperty, RadiusXProperty, RadiusYProperty, CenterProperty);
} }
/// <summary> /// <summary>
@ -43,6 +61,33 @@ namespace Avalonia.Media
set => SetValue(RectProperty, value); set => SetValue(RectProperty, value);
} }
/// <summary>
/// Gets or sets a double that defines the radius in the X-axis of the ellipse.
/// </summary>
public double RadiusX
{
get => GetValue(RadiusXProperty);
set => SetValue(RadiusXProperty, value);
}
/// <summary>
/// Gets or sets a double that defines the radius in the Y-axis of the ellipse.
/// </summary>
public double RadiusY
{
get => GetValue(RadiusYProperty);
set => SetValue(RadiusYProperty, value);
}
/// <summary>
/// Gets or sets a point that defines the center of the ellipse.
/// </summary>
public Point Center
{
get => GetValue(CenterProperty);
set => SetValue(CenterProperty, value);
}
/// <inheritdoc/> /// <inheritdoc/>
public override Geometry Clone() public override Geometry Clone()
{ {
@ -54,7 +99,14 @@ namespace Avalonia.Media
{ {
var factory = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>(); var factory = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>();
return factory.CreateEllipseGeometry(Rect); if (Rect != default) return factory.CreateEllipseGeometry(Rect);
var originX = Center.X - RadiusX;
var originY = Center.Y - RadiusY;
var width = RadiusX * 2;
var height = RadiusY * 2;
return factory.CreateEllipseGeometry(new Rect(originX, originY, width, height));
} }
} }
} }

9
src/Avalonia.Visuals/Media/KnownColors.cs

@ -8,7 +8,9 @@ namespace Avalonia.Media
{ {
private static readonly IReadOnlyDictionary<string, KnownColor> _knownColorNames; private static readonly IReadOnlyDictionary<string, KnownColor> _knownColorNames;
private static readonly IReadOnlyDictionary<uint, string> _knownColors; private static readonly IReadOnlyDictionary<uint, string> _knownColors;
#if !BUILDTASK
private static readonly Dictionary<KnownColor, ISolidColorBrush> _knownBrushes; private static readonly Dictionary<KnownColor, ISolidColorBrush> _knownBrushes;
#endif
static KnownColors() static KnownColors()
{ {
@ -32,14 +34,19 @@ namespace Avalonia.Media
_knownColorNames = knownColorNames; _knownColorNames = knownColorNames;
_knownColors = knownColors; _knownColors = knownColors;
#if !BUILDTASK
_knownBrushes = new Dictionary<KnownColor, ISolidColorBrush>(); _knownBrushes = new Dictionary<KnownColor, ISolidColorBrush>();
#endif
} }
#if !BUILDTASK
public static ISolidColorBrush GetKnownBrush(string s) public static ISolidColorBrush GetKnownBrush(string s)
{ {
var color = GetKnownColor(s); var color = GetKnownColor(s);
return color != KnownColor.None ? color.ToBrush() : null; return color != KnownColor.None ? color.ToBrush() : null;
} }
#endif
public static KnownColor GetKnownColor(string s) public static KnownColor GetKnownColor(string s)
{ {
@ -61,6 +68,7 @@ namespace Avalonia.Media
return Color.FromUInt32((uint)color); return Color.FromUInt32((uint)color);
} }
#if !BUILDTASK
public static ISolidColorBrush ToBrush(this KnownColor color) public static ISolidColorBrush ToBrush(this KnownColor color)
{ {
lock (_knownBrushes) lock (_knownBrushes)
@ -74,6 +82,7 @@ namespace Avalonia.Media
return brush; return brush;
} }
} }
#endif
} }
internal enum KnownColor : uint internal enum KnownColor : uint

72
src/Avalonia.Visuals/Media/PathFigure.cs

@ -1,3 +1,7 @@
#nullable enable
using System;
using System.Linq;
using Avalonia.Collections;
using Avalonia.Metadata; using Avalonia.Metadata;
namespace Avalonia.Media namespace Avalonia.Media
@ -8,22 +12,36 @@ namespace Avalonia.Media
/// Defines the <see cref="IsClosed"/> property. /// Defines the <see cref="IsClosed"/> property.
/// </summary> /// </summary>
public static readonly StyledProperty<bool> IsClosedProperty public static readonly StyledProperty<bool> IsClosedProperty
= AvaloniaProperty.Register<PathFigure, bool>(nameof(IsClosed), true); = AvaloniaProperty.Register<PathFigure, bool>(nameof(IsClosed), true);
/// <summary> /// <summary>
/// Defines the <see cref="IsFilled"/> property. /// Defines the <see cref="IsFilled"/> property.
/// </summary> /// </summary>
public static readonly StyledProperty<bool> IsFilledProperty public static readonly StyledProperty<bool> IsFilledProperty
= AvaloniaProperty.Register<PathFigure, bool>(nameof(IsFilled), true); = AvaloniaProperty.Register<PathFigure, bool>(nameof(IsFilled), true);
/// <summary> /// <summary>
/// Defines the <see cref="Segments"/> property. /// Defines the <see cref="Segments"/> property.
/// </summary> /// </summary>
public static readonly DirectProperty<PathFigure, PathSegments> SegmentsProperty public static readonly DirectProperty<PathFigure, PathSegments?> SegmentsProperty
= AvaloniaProperty.RegisterDirect<PathFigure, PathSegments>(nameof(Segments), f => f.Segments, (f, s) => f.Segments = s); = AvaloniaProperty.RegisterDirect<PathFigure, PathSegments?>(
nameof(Segments),
f => f.Segments,
(f, s) => f.Segments = s);
/// <summary> /// <summary>
/// Defines the <see cref="StartPoint"/> property. /// Defines the <see cref="StartPoint"/> property.
/// </summary> /// </summary>
public static readonly StyledProperty<Point> StartPointProperty public static readonly StyledProperty<Point> StartPointProperty
= AvaloniaProperty.Register<PathFigure, Point>(nameof(StartPoint)); = AvaloniaProperty.Register<PathFigure, Point>(nameof(StartPoint));
internal event EventHandler? SegmentsInvalidated;
private PathSegments? _segments;
private IDisposable? _segmentsDisposable;
private IDisposable? _segmentsPropertiesDisposable;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="PathFigure"/> class. /// Initializes a new instance of the <see cref="PathFigure"/> class.
@ -33,6 +51,31 @@ namespace Avalonia.Media
Segments = new PathSegments(); Segments = new PathSegments();
} }
static PathFigure()
{
SegmentsProperty.Changed.AddClassHandler<PathFigure>(
(s, e) =>
s.OnSegmentsChanged());
}
private void OnSegmentsChanged()
{
_segmentsDisposable?.Dispose();
_segmentsPropertiesDisposable?.Dispose();
_segmentsDisposable = _segments?.ForEachItem(
_ => InvalidateSegments(),
_ => InvalidateSegments(),
InvalidateSegments);
_segmentsPropertiesDisposable = _segments?.TrackItemPropertyChanged(_ => InvalidateSegments());
}
private void InvalidateSegments()
{
SegmentsInvalidated?.Invoke(this, EventArgs.Empty);
}
/// <summary> /// <summary>
/// Gets or sets a value indicating whether this instance is closed. /// Gets or sets a value indicating whether this instance is closed.
/// </summary> /// </summary>
@ -64,7 +107,7 @@ namespace Avalonia.Media
/// The segments. /// The segments.
/// </value> /// </value>
[Content] [Content]
public PathSegments Segments public PathSegments? Segments
{ {
get { return _segments; } get { return _segments; }
set { SetAndRaise(SegmentsProperty, ref _segments, value); } set { SetAndRaise(SegmentsProperty, ref _segments, value); }
@ -81,22 +124,23 @@ namespace Avalonia.Media
get { return GetValue(StartPointProperty); } get { return GetValue(StartPointProperty); }
set { SetValue(StartPointProperty, value); } set { SetValue(StartPointProperty, value); }
} }
public override string ToString()
=> $"M {StartPoint} {string.Join(" ", _segments ?? Enumerable.Empty<PathSegment>())}{(IsClosed ? "Z" : "")}";
internal void ApplyTo(StreamGeometryContext ctx) internal void ApplyTo(StreamGeometryContext ctx)
{ {
ctx.BeginFigure(StartPoint, IsFilled); ctx.BeginFigure(StartPoint, IsFilled);
foreach (var segment in Segments) if (Segments != null)
{ {
segment.ApplyTo(ctx); foreach (var segment in Segments)
{
segment.ApplyTo(ctx);
}
} }
ctx.EndFigure(IsClosed); ctx.EndFigure(IsClosed);
} }
private PathSegments _segments;
public override string ToString()
=> $"M {StartPoint} {string.Join(" ", _segments)}{(IsClosed ? "Z" : "")}";
} }
} }

20
src/Avalonia.Visuals/Media/PathGeometry.cs

@ -104,12 +104,26 @@ namespace Avalonia.Media
_figuresPropertiesObserver?.Dispose(); _figuresPropertiesObserver?.Dispose();
_figuresObserver = figures?.ForEachItem( _figuresObserver = figures?.ForEachItem(
_ => InvalidateGeometry(), s =>
_ => InvalidateGeometry(), {
() => InvalidateGeometry()); s.SegmentsInvalidated += InvalidateGeometryFromSegments;
InvalidateGeometry();
},
s =>
{
s.SegmentsInvalidated -= InvalidateGeometryFromSegments;
InvalidateGeometry();
},
InvalidateGeometry);
_figuresPropertiesObserver = figures?.TrackItemPropertyChanged(_ => InvalidateGeometry()); _figuresPropertiesObserver = figures?.TrackItemPropertyChanged(_ => InvalidateGeometry());
} }
private void InvalidateGeometryFromSegments(object _, EventArgs __)
{
InvalidateGeometry();
}
public override string ToString() public override string ToString()
=> $"{(FillRule != FillRule.EvenOdd ? "F1 " : "")}{(string.Join(" ", Figures))}"; => $"{(FillRule != FillRule.EvenOdd ? "F1 " : "")}{(string.Join(" ", Figures))}";

9
src/Avalonia.Visuals/Point.cs

@ -1,6 +1,8 @@
using System; using System;
using System.Globalization; using System.Globalization;
#if !BUILDTASK
using Avalonia.Animation.Animators; using Avalonia.Animation.Animators;
#endif
using Avalonia.Utilities; using Avalonia.Utilities;
namespace Avalonia namespace Avalonia
@ -8,11 +10,16 @@ namespace Avalonia
/// <summary> /// <summary>
/// Defines a point. /// Defines a point.
/// </summary> /// </summary>
public readonly struct Point : IEquatable<Point> #if !BUILDTASK
public
#endif
readonly struct Point : IEquatable<Point>
{ {
static Point() static Point()
{ {
#if !BUILDTASK
Animation.Animation.RegisterAnimator<PointAnimator>(prop => typeof(Point).IsAssignableFrom(prop.PropertyType)); Animation.Animation.RegisterAnimator<PointAnimator>(prop => typeof(Point).IsAssignableFrom(prop.PropertyType));
#endif
} }
/// <summary> /// <summary>

79
src/Avalonia.Visuals/Rendering/DeferredRenderer.cs

@ -99,6 +99,11 @@ namespace Avalonia.Rendering
/// </summary> /// </summary>
public string DebugFramesPath { get; set; } public string DebugFramesPath { get; set; }
/// <summary>
/// Forces the renderer to only draw frames on the render thread. Makes Paint to wait until frame is rendered
/// </summary>
public bool RenderOnlyOnRenderThread { get; set; }
/// <inheritdoc/> /// <inheritdoc/>
public event EventHandler<SceneInvalidatedEventArgs> SceneInvalidated; public event EventHandler<SceneInvalidatedEventArgs> SceneInvalidated;
@ -180,11 +185,44 @@ namespace Avalonia.Rendering
/// <inheritdoc/> /// <inheritdoc/>
public void Paint(Rect rect) public void Paint(Rect rect)
{ {
var t = (IRenderLoopTask)this; if (RenderOnlyOnRenderThread)
if(t.NeedsUpdate) {
UpdateScene(); // Renderer is stopped and doesn't tick on the render thread
if(_scene?.Item != null) // This indicates a bug somewhere in our code
Render(true); // (currently happens when a window gets minimized with Show desktop on Windows 10)
if(!_running)
return;
while (true)
{
Scene scene;
bool? updated;
lock (_sceneLock)
{
updated = UpdateScene();
scene = _scene?.Item;
}
// Renderer is in invalid state, skip drawing
if(updated == null)
return;
// Wait for the scene to be rendered or disposed
scene?.Rendered.Wait();
// That was an up-to-date scene, we can return immediately
if (updated == true)
return;
}
}
else
{
var t = (IRenderLoopTask)this;
if (t.NeedsUpdate)
UpdateScene();
if (_scene?.Item != null)
Render(true);
}
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -270,13 +308,20 @@ namespace Avalonia.Rendering
{ {
if (scene?.Item != null) if (scene?.Item != null)
{ {
var overlay = DrawDirtyRects || DrawFps; try
if (DrawDirtyRects) {
_dirtyRectsDisplay.Tick(); var overlay = DrawDirtyRects || DrawFps;
if (overlay) if (DrawDirtyRects)
RenderOverlay(scene.Item, ref context); _dirtyRectsDisplay.Tick();
if (updated || forceComposite || overlay) if (overlay)
RenderComposite(scene.Item, ref context); RenderOverlay(scene.Item, ref context);
if (updated || forceComposite || overlay)
RenderComposite(scene.Item, ref context);
}
finally
{
scene.Item.MarkAsRendered();
}
} }
} }
} }
@ -559,15 +604,15 @@ namespace Avalonia.Rendering
UpdateScene(); UpdateScene();
} }
private void UpdateScene() private bool? UpdateScene()
{ {
Dispatcher.UIThread.VerifyAccess(); Dispatcher.UIThread.VerifyAccess();
lock (_sceneLock) lock (_sceneLock)
{ {
if (_disposed) if (_disposed)
return; return null;
if (_scene?.Item.Generation > _lastSceneId) if (_scene?.Item.Generation > _lastSceneId)
return; return false;
} }
if (_root.IsVisible) if (_root.IsVisible)
{ {
@ -619,6 +664,8 @@ namespace Avalonia.Rendering
SceneInvalidated(this, new SceneInvalidatedEventArgs((IRenderRoot)_root, rect)); SceneInvalidated(this, new SceneInvalidatedEventArgs((IRenderRoot)_root, rect));
} }
return true;
} }
else else
{ {
@ -628,6 +675,8 @@ namespace Avalonia.Rendering
_scene = null; _scene = null;
oldScene?.Dispose(); oldScene?.Dispose();
} }
return null;
} }
} }

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

@ -2,6 +2,7 @@
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using Avalonia.Collections.Pooled; using Avalonia.Collections.Pooled;
using Avalonia.VisualTree; using Avalonia.VisualTree;
@ -13,6 +14,7 @@ namespace Avalonia.Rendering.SceneGraph
public class Scene : IDisposable public class Scene : IDisposable
{ {
private readonly Dictionary<IVisual, IVisualNode> _index; private readonly Dictionary<IVisual, IVisualNode> _index;
private readonly TaskCompletionSource<bool> _rendered = new TaskCompletionSource<bool>();
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Scene"/> class. /// Initializes a new instance of the <see cref="Scene"/> class.
@ -41,6 +43,8 @@ namespace Avalonia.Rendering.SceneGraph
root.LayerRoot = root.Visual; root.LayerRoot = root.Visual;
} }
public Task Rendered => _rendered.Task;
/// <summary> /// <summary>
/// Gets a value identifying the scene's generation. This is incremented each time the scene is cloned. /// Gets a value identifying the scene's generation. This is incremented each time the scene is cloned.
/// </summary> /// </summary>
@ -97,6 +101,7 @@ namespace Avalonia.Rendering.SceneGraph
public void Dispose() public void Dispose()
{ {
_rendered.TrySetResult(false);
foreach (var node in _index.Values) foreach (var node in _index.Values)
{ {
node.Dispose(); node.Dispose();
@ -340,5 +345,7 @@ namespace Avalonia.Rendering.SceneGraph
} }
} }
} }
public void MarkAsRendered() => _rendered.TrySetResult(true);
} }
} }

9
src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs

@ -24,7 +24,8 @@ namespace Avalonia.Rendering.SceneGraph
using (var impl = new DeferredDrawingContextImpl(this, scene.Layers)) using (var impl = new DeferredDrawingContextImpl(this, scene.Layers))
using (var context = new DrawingContext(impl)) using (var context = new DrawingContext(impl))
{ {
Update(context, scene, (VisualNode)scene.Root, scene.Root.Visual.Bounds, true); var clip = new Rect(scene.Root.Visual.Bounds.Size);
Update(context, scene, (VisualNode)scene.Root, clip, true);
} }
} }
@ -77,7 +78,7 @@ namespace Avalonia.Rendering.SceneGraph
using (var impl = new DeferredDrawingContextImpl(this, scene.Layers)) using (var impl = new DeferredDrawingContextImpl(this, scene.Layers))
using (var context = new DrawingContext(impl)) using (var context = new DrawingContext(impl))
{ {
var clip = scene.Root.Visual.Bounds; var clip = new Rect(scene.Root.Visual.Bounds.Size);
if (node.Parent != null) if (node.Parent != null)
{ {
@ -174,7 +175,9 @@ namespace Avalonia.Rendering.SceneGraph
if (visual.IsVisible) if (visual.IsVisible)
{ {
var m = Matrix.CreateTranslation(visual.Bounds.Position); var m = node != scene.Root ?
Matrix.CreateTranslation(visual.Bounds.Position) :
Matrix.Identity;
var renderTransform = Matrix.Identity; var renderTransform = Matrix.Identity;

9
src/Avalonia.Visuals/Size.cs

@ -1,6 +1,8 @@
using System; using System;
using System.Globalization; using System.Globalization;
#if !BUILDTASK
using Avalonia.Animation.Animators; using Avalonia.Animation.Animators;
#endif
using Avalonia.Utilities; using Avalonia.Utilities;
namespace Avalonia namespace Avalonia
@ -8,11 +10,16 @@ namespace Avalonia
/// <summary> /// <summary>
/// Defines a size. /// Defines a size.
/// </summary> /// </summary>
public readonly struct Size : IEquatable<Size> #if !BUILDTASK
public
#endif
readonly struct Size : IEquatable<Size>
{ {
static Size() static Size()
{ {
#if !BUILDTASK
Animation.Animation.RegisterAnimator<SizeAnimator>(prop => typeof(Size).IsAssignableFrom(prop.PropertyType)); Animation.Animation.RegisterAnimator<SizeAnimator>(prop => typeof(Size).IsAssignableFrom(prop.PropertyType));
#endif
} }
/// <summary> /// <summary>

9
src/Avalonia.Visuals/Thickness.cs

@ -1,6 +1,8 @@
using System; using System;
using System.Globalization; using System.Globalization;
#if !BUILDTASK
using Avalonia.Animation.Animators; using Avalonia.Animation.Animators;
#endif
using Avalonia.Utilities; using Avalonia.Utilities;
namespace Avalonia namespace Avalonia
@ -8,11 +10,16 @@ namespace Avalonia
/// <summary> /// <summary>
/// Describes the thickness of a frame around a rectangle. /// Describes the thickness of a frame around a rectangle.
/// </summary> /// </summary>
public readonly struct Thickness : IEquatable<Thickness> #if !BUILDTASK
public
#endif
readonly struct Thickness : IEquatable<Thickness>
{ {
static Thickness() static Thickness()
{ {
#if !BUILDTASK
Animation.Animation.RegisterAnimator<ThicknessAnimator>(prop => typeof(Thickness).IsAssignableFrom(prop.PropertyType)); Animation.Animation.RegisterAnimator<ThicknessAnimator>(prop => typeof(Thickness).IsAssignableFrom(prop.PropertyType));
#endif
} }
/// <summary> /// <summary>

9
src/Avalonia.Visuals/Vector.cs

@ -1,6 +1,8 @@
using System; using System;
using System.Globalization; using System.Globalization;
#if !BUILDTASK
using Avalonia.Animation.Animators; using Avalonia.Animation.Animators;
#endif
using Avalonia.Utilities; using Avalonia.Utilities;
#nullable enable #nullable enable
@ -10,11 +12,16 @@ namespace Avalonia
/// <summary> /// <summary>
/// Defines a vector. /// Defines a vector.
/// </summary> /// </summary>
public readonly struct Vector : IEquatable<Vector> #if !BUILDTASK
public
#endif
readonly struct Vector : IEquatable<Vector>
{ {
static Vector() static Vector()
{ {
#if !BUILDTASK
Animation.Animation.RegisterAnimator<VectorAnimator>(prop => typeof(Vector).IsAssignableFrom(prop.PropertyType)); Animation.Animation.RegisterAnimator<VectorAnimator>(prop => typeof(Vector).IsAssignableFrom(prop.PropertyType));
#endif
} }
/// <summary> /// <summary>

37
src/Avalonia.Visuals/Visual.cs

@ -1,4 +1,5 @@
using System; using System;
using System.Collections;
using System.Collections.Specialized; using System.Collections.Specialized;
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Data; using Avalonia.Data;
@ -619,34 +620,30 @@ namespace Avalonia
switch (e.Action) switch (e.Action)
{ {
case NotifyCollectionChangedAction.Add: case NotifyCollectionChangedAction.Add:
foreach (Visual v in e.NewItems) SetVisualParent(e.NewItems, this);
{
v.SetVisualParent(this);
}
break; break;
case NotifyCollectionChangedAction.Remove: case NotifyCollectionChangedAction.Remove:
foreach (Visual v in e.OldItems) SetVisualParent(e.OldItems, null);
{
v.SetVisualParent(null);
}
break; break;
case NotifyCollectionChangedAction.Replace: case NotifyCollectionChangedAction.Replace:
foreach (Visual v in e.OldItems) SetVisualParent(e.OldItems, null);
{ SetVisualParent(e.NewItems, this);
v.SetVisualParent(null);
}
foreach (Visual v in e.NewItems)
{
v.SetVisualParent(this);
}
break; break;
} }
} }
private static void SetVisualParent(IList children, Visual parent)
{
var count = children.Count;
for (var i = 0; i < count; i++)
{
var visual = (Visual) children[i];
visual.SetVisualParent(parent);
}
}
} }
} }

10
src/Avalonia.X11/X11Window.cs

@ -189,6 +189,11 @@ namespace Avalonia.X11
if (platform.Options.UseDBusMenu) if (platform.Options.UseDBusMenu)
NativeMenuExporter = DBusMenuExporter.TryCreate(_handle); NativeMenuExporter = DBusMenuExporter.TryCreate(_handle);
NativeControlHost = new X11NativeControlHost(_platform, this); NativeControlHost = new X11NativeControlHost(_platform, this);
DispatcherTimer.Run(() =>
{
Paint?.Invoke(default);
return _handle != IntPtr.Zero;
}, TimeSpan.FromMilliseconds(100));
} }
class SurfaceInfo : EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo class SurfaceInfo : EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo
@ -338,7 +343,10 @@ namespace Avalonia.X11
return customRendererFactory.Create(root, loop); return customRendererFactory.Create(root, loop);
return _platform.Options.UseDeferredRendering ? return _platform.Options.UseDeferredRendering ?
new DeferredRenderer(root, loop) : new DeferredRenderer(root, loop)
{
RenderOnlyOnRenderThread = true
} :
(IRenderer)new X11ImmediateRendererProxy(root, loop); (IRenderer)new X11ImmediateRendererProxy(root, loop);
} }

2
src/Avalonia.X11/XI2Manager.cs

@ -351,7 +351,7 @@ namespace Avalonia.X11
if (state.HasFlag(XModifierMask.Mod4Mask)) if (state.HasFlag(XModifierMask.Mod4Mask))
Modifiers |= RawInputModifiers.Meta; Modifiers |= RawInputModifiers.Meta;
Modifiers = ParseButtonState(ev->buttons.MaskLen, ev->buttons.Mask); Modifiers |= ParseButtonState(ev->buttons.MaskLen, ev->buttons.Mask);
Valuators = new Dictionary<int, double>(); Valuators = new Dictionary<int, double>();
Position = new Point(ev->event_x, ev->event_y); Position = new Point(ev->event_x, ev->event_y);

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

@ -4,6 +4,7 @@
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<IsPackable>true</IsPackable> <IsPackable>true</IsPackable>
<PackageId>Avalonia.Markup.Xaml.Loader</PackageId> <PackageId>Avalonia.Markup.Xaml.Loader</PackageId>
<DefineConstants>$(DefineConstants);XAMLX_INTERNAL</DefineConstants>
</PropertyGroup> </PropertyGroup>
<Import Project="IncludeXamlIlSre.props" /> <Import Project="IncludeXamlIlSre.props" />
<ItemGroup> <ItemGroup>

39
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlFontFamilyAstNode.cs

@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers;
using XamlX.Ast;
using XamlX.Emit;
using XamlX.IL;
using XamlX.TypeSystem;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.AstNodes
{
class AvaloniaXamlIlFontFamilyAstNode: XamlAstNode, IXamlAstValueNode, IXamlAstILEmitableNode
{
private readonly AvaloniaXamlIlWellKnownTypes _types;
private readonly string _text;
public IXamlAstTypeReference Type { get; }
public AvaloniaXamlIlFontFamilyAstNode(AvaloniaXamlIlWellKnownTypes types,
string text,
IXamlLineInfo lineInfo) : base(lineInfo)
{
_types = types;
_text = text;
Type = new XamlAstClrTypeReference(lineInfo, types.FontFamily, false);
}
public XamlILNodeEmitResult Emit(XamlEmitContext<IXamlILEmitter, XamlILNodeEmitResult> context, IXamlILEmitter codeGen)
{
codeGen
.Ldloc(context.ContextLocal)
.Castclass(context.Configuration.TypeMappings.UriContextProvider)
.EmitCall(context.Configuration.TypeMappings.UriContextProvider.FindMethod(
"get_BaseUri", _types.Uri, false))
.Ldstr(_text)
.Newobj(_types.FontFamilyConstructorUriName);
return XamlILNodeEmitResult.Type(0, _types.FontFamily);
}
}
}

34
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlGridLengthAstNode.cs

@ -0,0 +1,34 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers;
using XamlX.Ast;
using XamlX.Emit;
using XamlX.IL;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.AstNodes
{
class AvaloniaXamlIlGridLengthAstNode : XamlAstNode, IXamlAstValueNode, IXamlAstILEmitableNode
{
private readonly AvaloniaXamlIlWellKnownTypes _types;
private readonly GridLength _gridLength;
public AvaloniaXamlIlGridLengthAstNode(IXamlLineInfo lineInfo, AvaloniaXamlIlWellKnownTypes types, GridLength gridLength) : base(lineInfo)
{
_types = types;
_gridLength = gridLength;
Type = new XamlAstClrTypeReference(lineInfo, types.GridLength, false);
}
public IXamlAstTypeReference Type { get; }
public XamlILNodeEmitResult Emit(XamlEmitContext<IXamlILEmitter, XamlILNodeEmitResult> context, IXamlILEmitter codeGen)
{
codeGen
.Ldc_R8(_gridLength.Value)
.Ldc_I4((int)_gridLength.GridUnitType)
.Newobj(_types.GridLengthConstructorValueType);
return XamlILNodeEmitResult.Type(0, Type.GetClrType());
}
}
}

54
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlVectorLikeConstantAstNode.cs

@ -0,0 +1,54 @@
using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers;
using XamlX;
using XamlX.Ast;
using XamlX.Emit;
using XamlX.IL;
using XamlX.TypeSystem;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.AstNodes
{
class AvaloniaXamlIlVectorLikeConstantAstNode : XamlAstNode, IXamlAstValueNode, IXamlAstILEmitableNode
{
private readonly IXamlConstructor _constructor;
private readonly double[] _values;
public AvaloniaXamlIlVectorLikeConstantAstNode(IXamlLineInfo lineInfo, AvaloniaXamlIlWellKnownTypes types, IXamlType type, IXamlConstructor constructor, double[] values) : base(lineInfo)
{
var parameters = constructor.Parameters;
if (parameters.Count != values.Length)
{
throw new XamlTypeSystemException($"Constructor that takes {values.Length} parameters is expected, got {parameters.Count} instead.");
}
var elementType = types.XamlIlTypes.Double;
foreach (var parameter in parameters)
{
if (parameter != elementType)
{
throw new XamlTypeSystemException($"Expected parameter of type {elementType}, got {parameter} instead.");
}
}
_constructor = constructor;
_values = values;
Type = new XamlAstClrTypeReference(lineInfo, type, false);
}
public IXamlAstTypeReference Type { get; }
public XamlILNodeEmitResult Emit(XamlEmitContext<IXamlILEmitter, XamlILNodeEmitResult> context, IXamlILEmitter codeGen)
{
foreach (var value in _values)
{
codeGen.Ldc_R8(value);
}
codeGen.Newobj(_constructor);
return XamlILNodeEmitResult.Type(0, Type.GetClrType());
}
}
}

200
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs

@ -1,7 +1,11 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using Avalonia.Controls;
using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.AstNodes;
using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers; using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers;
using Avalonia.Media;
using XamlX; using XamlX;
using XamlX.Ast; using XamlX.Ast;
using XamlX.Emit; using XamlX.Emit;
@ -166,28 +170,196 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
public static bool CustomValueConverter(AstTransformationContext context, public static bool CustomValueConverter(AstTransformationContext context,
IXamlAstValueNode node, IXamlType type, out IXamlAstValueNode result) IXamlAstValueNode node, IXamlType type, out IXamlAstValueNode result)
{ {
if (type.FullName == "System.TimeSpan" if (!(node is XamlAstTextNode textNode))
&& node is XamlAstTextNode tn {
&& !tn.Text.Contains(":")) result = null;
{ return false;
var seconds = double.Parse(tn.Text, CultureInfo.InvariantCulture); }
result = new XamlStaticOrTargetedReturnMethodCallNode(tn,
type.FindMethod("FromSeconds", type, false, context.Configuration.WellKnownTypes.Double), var text = textNode.Text;
new[]
{ var types = context.GetAvaloniaTypes();
new XamlConstantNode(tn, context.Configuration.WellKnownTypes.Double, seconds)
}); if (type.FullName == "System.TimeSpan")
{
var tsText = text.Trim();
if (!TimeSpan.TryParse(tsText, CultureInfo.InvariantCulture, out var timeSpan))
{
// // shorthand seconds format (ie. "0.25")
if (!tsText.Contains(":") && double.TryParse(tsText,
NumberStyles.Float | NumberStyles.AllowThousands,
CultureInfo.InvariantCulture, out var seconds))
timeSpan = TimeSpan.FromSeconds(seconds);
else
throw new XamlX.XamlLoadException($"Unable to parse {text} as a time span", node);
}
result = new XamlStaticOrTargetedReturnMethodCallNode(node,
type.FindMethod("FromTicks", type, false, types.Long),
new[] { new XamlConstantNode(node, types.Long, timeSpan.Ticks) });
return true; return true;
} }
if (type.Equals(types.FontFamily))
{
result = new AvaloniaXamlIlFontFamilyAstNode(types, text, node);
return true;
}
if (type.Equals(types.Thickness))
{
try
{
var thickness = Thickness.Parse(text);
result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Thickness, types.ThicknessFullConstructor,
new[] { thickness.Left, thickness.Top, thickness.Right, thickness.Bottom });
return true;
}
catch
{
throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a thickness", node);
}
}
if (type.Equals(types.Point))
{
try
{
var point = Point.Parse(text);
result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Point, types.PointFullConstructor,
new[] { point.X, point.Y });
return true;
}
catch
{
throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a point", node);
}
}
if (type.Equals(types.Vector))
{
try
{
var vector = Vector.Parse(text);
result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Vector, types.VectorFullConstructor,
new[] { vector.X, vector.Y });
return true;
}
catch
{
throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a vector", node);
}
}
if (type.Equals(types.Size))
{
try
{
var size = Size.Parse(text);
result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Size, types.SizeFullConstructor,
new[] { size.Width, size.Height });
return true;
}
catch
{
throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a size", node);
}
}
if (type.Equals(types.Matrix))
{
try
{
var matrix = Matrix.Parse(text);
result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Matrix, types.MatrixFullConstructor,
new[] { matrix.M11, matrix.M12, matrix.M21, matrix.M22, matrix.M31, matrix.M32 });
return true;
}
catch
{
throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a matrix", node);
}
}
if (type.Equals(types.CornerRadius))
{
try
{
var cornerRadius = CornerRadius.Parse(text);
result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.CornerRadius, types.CornerRadiusFullConstructor,
new[] { cornerRadius.TopLeft, cornerRadius.TopRight, cornerRadius.BottomRight, cornerRadius.BottomLeft });
return true;
}
catch
{
throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a corner radius", node);
}
}
if (type.Equals(types.Color))
{
if (!Color.TryParse(text, out Color color))
{
throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a color", node);
}
result = new XamlStaticOrTargetedReturnMethodCallNode(node,
type.GetMethod(
new FindMethodMethodSignature("FromUInt32", type, types.UInt) { IsStatic = true }),
new[] { new XamlConstantNode(node, types.UInt, color.ToUint32()) });
return true;
}
if (type.Equals(types.GridLength))
{
try
{
var gridLength = GridLength.Parse(text);
result = new AvaloniaXamlIlGridLengthAstNode(node, types, gridLength);
return true;
}
catch
{
throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a grid length", node);
}
}
if (type.Equals(types.Cursor))
{
if (TypeSystemHelpers.TryGetEnumValueNode(types.StandardCursorType, text, node, out var enumConstantNode))
{
var cursorTypeRef = new XamlAstClrTypeReference(node, types.Cursor, false);
result = new XamlAstNewClrObjectNode(node, cursorTypeRef, types.CursorTypeConstructor, new List<IXamlAstValueNode> { enumConstantNode });
return true;
}
}
if (type.FullName == "Avalonia.AvaloniaProperty") if (type.FullName == "Avalonia.AvaloniaProperty")
{ {
var scope = context.ParentNodes().OfType<AvaloniaXamlIlTargetTypeMetadataNode>().FirstOrDefault(); var scope = context.ParentNodes().OfType<AvaloniaXamlIlTargetTypeMetadataNode>().FirstOrDefault();
if (scope == null) if (scope == null)
throw new XamlX.XamlLoadException("Unable to find the parent scope for AvaloniaProperty lookup", node); throw new XamlX.XamlLoadException("Unable to find the parent scope for AvaloniaProperty lookup", node);
if (!(node is XamlAstTextNode text))
throw new XamlX.XamlLoadException("Property should be a text node", node); result = XamlIlAvaloniaPropertyHelper.CreateNode(context, text, scope.TargetType, node );
result = XamlIlAvaloniaPropertyHelper.CreateNode(context, text.Text, scope.TargetType, text);
return true; return true;
} }

2
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlTransformInstanceAttachedProperties.cs

@ -22,7 +22,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
var avaloniaObject = context.Configuration.TypeSystem.FindType("Avalonia.AvaloniaObject"); var avaloniaObject = context.Configuration.TypeSystem.FindType("Avalonia.AvaloniaObject");
if (avaloniaObject.IsAssignableFrom(targetRef.Type) if (avaloniaObject.IsAssignableFrom(targetRef.Type)
&& avaloniaObject.IsAssignableFrom(declaringRef.Type) && avaloniaObject.IsAssignableFrom(declaringRef.Type)
&& !targetRef.Type.IsAssignableFrom(declaringRef.Type)) && !declaringRef.Type.IsAssignableFrom(targetRef.Type))
{ {
// Instance property // Instance property
var clrProp = declaringRef.Type.GetAllProperties().FirstOrDefault(p => p.Name == prop.Name); var clrProp = declaringRef.Type.GetAllProperties().FirstOrDefault(p => p.Name == prop.Name);

52
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs

@ -1,3 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using XamlX.Emit; using XamlX.Emit;
using XamlX.IL; using XamlX.IL;
using XamlX.Transform; using XamlX.Transform;
@ -47,6 +49,29 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
public IXamlType ReflectionBindingExtension { get; } public IXamlType ReflectionBindingExtension { get; }
public IXamlType RelativeSource { get; } public IXamlType RelativeSource { get; }
public IXamlType UInt { get; }
public IXamlType Long { get; }
public IXamlType Uri { get; }
public IXamlType FontFamily { get; }
public IXamlConstructor FontFamilyConstructorUriName { get; }
public IXamlType Thickness { get; }
public IXamlConstructor ThicknessFullConstructor { get; }
public IXamlType Point { get; }
public IXamlConstructor PointFullConstructor { get; }
public IXamlType Vector { get; }
public IXamlConstructor VectorFullConstructor { get; }
public IXamlType Size { get; }
public IXamlConstructor SizeFullConstructor { get; }
public IXamlType Matrix { get; }
public IXamlConstructor MatrixFullConstructor { get; }
public IXamlType CornerRadius { get; }
public IXamlConstructor CornerRadiusFullConstructor { get; }
public IXamlType GridLength { get; }
public IXamlConstructor GridLengthConstructorValueType { get; }
public IXamlType Color { get; }
public IXamlType StandardCursorType { get; }
public IXamlType Cursor { get; }
public IXamlConstructor CursorTypeConstructor { get; }
public AvaloniaXamlIlWellKnownTypes(TransformerConfiguration cfg) public AvaloniaXamlIlWellKnownTypes(TransformerConfiguration cfg)
{ {
@ -104,6 +129,33 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
ItemsRepeater = cfg.TypeSystem.GetType("Avalonia.Controls.ItemsRepeater"); ItemsRepeater = cfg.TypeSystem.GetType("Avalonia.Controls.ItemsRepeater");
ReflectionBindingExtension = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.MarkupExtensions.ReflectionBindingExtension"); ReflectionBindingExtension = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.MarkupExtensions.ReflectionBindingExtension");
RelativeSource = cfg.TypeSystem.GetType("Avalonia.Data.RelativeSource"); RelativeSource = cfg.TypeSystem.GetType("Avalonia.Data.RelativeSource");
UInt = cfg.TypeSystem.GetType("System.UInt32");
Long = cfg.TypeSystem.GetType("System.Int64");
Uri = cfg.TypeSystem.GetType("System.Uri");
FontFamily = cfg.TypeSystem.GetType("Avalonia.Media.FontFamily");
FontFamilyConstructorUriName = FontFamily.GetConstructor(new List<IXamlType> { Uri, XamlIlTypes.String });
(IXamlType, IXamlConstructor) GetNumericTypeInfo(string name, IXamlType componentType, int componentCount)
{
var type = cfg.TypeSystem.GetType(name);
var ctor = type.GetConstructor(Enumerable.Range(0, componentCount).Select(_ => componentType).ToList());
return (type, ctor);
}
(Thickness, ThicknessFullConstructor) = GetNumericTypeInfo("Avalonia.Thickness", XamlIlTypes.Double, 4);
(Point, PointFullConstructor) = GetNumericTypeInfo("Avalonia.Point", XamlIlTypes.Double, 2);
(Vector, VectorFullConstructor) = GetNumericTypeInfo("Avalonia.Vector", XamlIlTypes.Double, 2);
(Size, SizeFullConstructor) = GetNumericTypeInfo("Avalonia.Size", XamlIlTypes.Double, 2);
(Matrix, MatrixFullConstructor) = GetNumericTypeInfo("Avalonia.Matrix", XamlIlTypes.Double, 6);
(CornerRadius, CornerRadiusFullConstructor) = GetNumericTypeInfo("Avalonia.CornerRadius", XamlIlTypes.Double, 4);
GridLength = cfg.TypeSystem.GetType("Avalonia.Controls.GridLength");
GridLengthConstructorValueType = GridLength.GetConstructor(new List<IXamlType> { XamlIlTypes.Double, cfg.TypeSystem.GetType("Avalonia.Controls.GridUnitType") });
Color = cfg.TypeSystem.GetType("Avalonia.Media.Color");
StandardCursorType = cfg.TypeSystem.GetType("Avalonia.Input.StandardCursorType");
Cursor = cfg.TypeSystem.GetType("Avalonia.Input.Cursor");
CursorTypeConstructor = Cursor.GetConstructor(new List<IXamlType> { StandardCursorType });
} }
} }

32
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs

@ -203,6 +203,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
.ParentNodes() .ParentNodes()
.OfType<XamlAstConstructableObjectNode>() .OfType<XamlAstConstructableObjectNode>()
.Where(x => styledElementType.IsAssignableFrom(x.Type.GetClrType())) .Where(x => styledElementType.IsAssignableFrom(x.Type.GetClrType()))
.Skip(1)
.ElementAtOrDefault(ancestor.Level) .ElementAtOrDefault(ancestor.Level)
?.Type.GetClrType(); ?.Type.GetClrType();
@ -242,6 +243,16 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
case RawSourceBindingExpressionNode rawSource: case RawSourceBindingExpressionNode rawSource:
nodes.Add(new RawSourcePathElementNode(rawSource.RawSource)); nodes.Add(new RawSourcePathElementNode(rawSource.RawSource));
break; break;
case BindingExpressionGrammar.TypeCastNode typeCastNode:
var castType = GetType(typeCastNode.Namespace, typeCastNode.TypeName);
if (castType is null)
{
throw new XamlX.XamlParseException($"Unable to resolve cast to type {typeCastNode.Namespace}:{typeCastNode.TypeName} based on XAML tree.", lineInfo);
}
nodes.Add(new TypeCastPathElementNode(castType));
break;
} }
} }
@ -422,7 +433,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
{ {
codeGen.Ldtype(Type) codeGen.Ldtype(Type)
.Ldc_I4(_level) .Ldc_I4(_level)
.EmitCall(context.GetAvaloniaTypes().CompiledBindingPathBuilder.FindMethod(m => m.Name == "FindAncestor")); .EmitCall(context.GetAvaloniaTypes().CompiledBindingPathBuilder.FindMethod(m => m.Name == "Ancestor"));
} }
} }
@ -608,10 +619,10 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
private readonly IXamlAstValueNode _rawSource; private readonly IXamlAstValueNode _rawSource;
public RawSourcePathElementNode(IXamlAstValueNode rawSource) public RawSourcePathElementNode(IXamlAstValueNode rawSource)
:base(rawSource) : base(rawSource)
{ {
_rawSource = rawSource; _rawSource = rawSource;
} }
public IXamlType Type => _rawSource.Type.GetClrType(); public IXamlType Type => _rawSource.Type.GetClrType();
@ -625,6 +636,21 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
} }
} }
class TypeCastPathElementNode : IXamlIlBindingPathElementNode
{
public TypeCastPathElementNode(IXamlType ancestorType)
{
Type = ancestorType;
}
public IXamlType Type { get; }
public void Emit(XamlIlEmitContext context, IXamlILEmitter codeGen)
{
codeGen.EmitCall(context.GetAvaloniaTypes().CompiledBindingPathBuilder.FindMethod(m => m.Name == "TypeCast").MakeGenericMethod(new[] { Type }));
}
}
class XamlIlBindingPathNode : XamlAstNode, IXamlIlBindingPathNode, IXamlAstEmitableNode<IXamlILEmitter, XamlILNodeEmitResult> class XamlIlBindingPathNode : XamlAstNode, IXamlIlBindingPathNode, IXamlAstEmitableNode<IXamlILEmitter, XamlILNodeEmitResult>
{ {
private readonly List<IXamlIlBindingPathElementNode> _transformElements; private readonly List<IXamlIlBindingPathElementNode> _transformElements;

2
src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github

@ -1 +1 @@
Subproject commit 5420df861ce6f2be5ead9efa078fe7242ce88f18 Subproject commit ea80a607c5e9d8f000160dbbb48c27ed4cfafbc9

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

@ -23,6 +23,7 @@
<Compile Include="MarkupExtensions\CompiledBindings\ObservableStreamPlugin.cs" /> <Compile Include="MarkupExtensions\CompiledBindings\ObservableStreamPlugin.cs" />
<Compile Include="MarkupExtensions\CompiledBindings\PropertyInfoAccessorFactory.cs" /> <Compile Include="MarkupExtensions\CompiledBindings\PropertyInfoAccessorFactory.cs" />
<Compile Include="MarkupExtensions\CompiledBindings\PropertyInfoAccessorPlugin.cs" /> <Compile Include="MarkupExtensions\CompiledBindings\PropertyInfoAccessorPlugin.cs" />
<Compile Include="MarkupExtensions\CompiledBindings\StrongTypeCastNode.cs" />
<Compile Include="MarkupExtensions\CompiledBindings\TaskStreamPlugin.cs" /> <Compile Include="MarkupExtensions\CompiledBindings\TaskStreamPlugin.cs" />
<Compile Include="MarkupExtensions\DynamicResourceExtension.cs" /> <Compile Include="MarkupExtensions\DynamicResourceExtension.cs" />
<Compile Include="MarkupExtensions\ResolveByNameExtension.cs" /> <Compile Include="MarkupExtensions\ResolveByNameExtension.cs" />

2
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindingExtension.cs

@ -26,6 +26,8 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
{ {
Path = Path, Path = Path,
Converter = Converter, Converter = Converter,
ConverterParameter = ConverterParameter,
TargetNullValue = TargetNullValue,
FallbackValue = FallbackValue, FallbackValue = FallbackValue,
Mode = Mode, Mode = Mode,
Priority = Priority, Priority = Priority,

51
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Data.Core; using Avalonia.Data.Core;
using Avalonia.Data.Core.Plugins; using Avalonia.Data.Core.Plugins;
@ -53,6 +54,9 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
case IStronglyTypedStreamElement stream: case IStronglyTypedStreamElement stream:
node = new StreamNode(stream.CreatePlugin()); node = new StreamNode(stream.CreatePlugin());
break; break;
case ITypeCastElement typeCast:
node = new StrongTypeCastNode(typeCast.Type, typeCast.Cast);
break;
default: default:
throw new InvalidOperationException($"Unknown binding path element type {element.GetType().FullName}"); throw new InvalidOperationException($"Unknown binding path element type {element.GetType().FullName}");
} }
@ -66,6 +70,9 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
internal SourceMode SourceMode => _elements.Count > 0 && _elements[0] is IControlSourceBindingPathElement ? SourceMode.Control : SourceMode.Data; internal SourceMode SourceMode => _elements.Count > 0 && _elements[0] is IControlSourceBindingPathElement ? SourceMode.Control : SourceMode.Data;
internal object RawSource { get; } internal object RawSource { get; }
public override string ToString()
=> string.Concat(_elements.Select(e => e.ToString()));
} }
public class CompiledBindingPathBuilder public class CompiledBindingPathBuilder
@ -126,6 +133,12 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
return this; return this;
} }
public CompiledBindingPathBuilder TypeCast<T>()
{
_elements.Add(new TypeCastPathElement<T>());
return this;
}
public CompiledBindingPathBuilder SetRawSource(object rawSource) public CompiledBindingPathBuilder SetRawSource(object rawSource)
{ {
_rawSource = rawSource; _rawSource = rawSource;
@ -157,6 +170,9 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
public IPropertyInfo Property { get; } public IPropertyInfo Property { get; }
public Func<WeakReference<object>, IPropertyInfo, IPropertyAccessor> AccessorFactory { get; } public Func<WeakReference<object>, IPropertyInfo, IPropertyAccessor> AccessorFactory { get; }
public override string ToString()
=> $".{Property.Name}";
} }
internal interface IStronglyTypedStreamElement : ICompiledBindingPathElement internal interface IStronglyTypedStreamElement : ICompiledBindingPathElement
@ -164,6 +180,13 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
IStreamPlugin CreatePlugin(); IStreamPlugin CreatePlugin();
} }
internal interface ITypeCastElement : ICompiledBindingPathElement
{
Type Type { get; }
Func<object, object> Cast { get; }
}
internal class TaskStreamPathElement<T> : IStronglyTypedStreamElement internal class TaskStreamPathElement<T> : IStronglyTypedStreamElement
{ {
public static readonly TaskStreamPathElement<T> Instance = new TaskStreamPathElement<T>(); public static readonly TaskStreamPathElement<T> Instance = new TaskStreamPathElement<T>();
@ -181,6 +204,9 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
internal class SelfPathElement : ICompiledBindingPathElement, IControlSourceBindingPathElement internal class SelfPathElement : ICompiledBindingPathElement, IControlSourceBindingPathElement
{ {
public static readonly SelfPathElement Instance = new SelfPathElement(); public static readonly SelfPathElement Instance = new SelfPathElement();
public override string ToString()
=> "$self";
} }
internal class AncestorPathElement : ICompiledBindingPathElement, IControlSourceBindingPathElement internal class AncestorPathElement : ICompiledBindingPathElement, IControlSourceBindingPathElement
@ -193,6 +219,9 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
public Type AncestorType { get; } public Type AncestorType { get; }
public int Level { get; } public int Level { get; }
public override string ToString()
=> $"$parent[{AncestorType?.Name},{Level}]";
} }
internal class VisualAncestorPathElement : ICompiledBindingPathElement, IControlSourceBindingPathElement internal class VisualAncestorPathElement : ICompiledBindingPathElement, IControlSourceBindingPathElement
@ -217,6 +246,9 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
public INameScope NameScope { get; } public INameScope NameScope { get; }
public string Name { get; } public string Name { get; }
public override string ToString()
=> $"#{Name}";
} }
internal class ArrayElementPathElement : ICompiledBindingPathElement internal class ArrayElementPathElement : ICompiledBindingPathElement
@ -229,5 +261,24 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
public int[] Indices { get; } public int[] Indices { get; }
public Type ElementType { get; } public Type ElementType { get; }
public override string ToString()
=> $"[{string.Join(",", Indices)}]";
}
internal class TypeCastPathElement<T> : ITypeCastElement
{
private static object TryCast(object obj)
{
if (obj is T result)
return result;
return null;
}
public Type Type => typeof(T);
public Func<object, object> Cast => TryCast;
public override string ToString()
=> $"({Type.FullName})";
} }
} }

18
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/StrongTypeCastNode.cs

@ -0,0 +1,18 @@
using System;
using Avalonia.Data.Core;
namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
{
public class StrongTypeCastNode : TypeCastNode
{
private Func<object, object> _cast;
public StrongTypeCastNode(Type type, Func<object, object> cast) : base(type)
{
_cast = cast;
}
protected override object Cast(object value)
=> _cast(value);
}
}

18
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs

@ -10,8 +10,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
{ {
public class DynamicResourceExtension : IBinding public class DynamicResourceExtension : IBinding
{ {
private IStyledElement? _anchor; private object? _anchor;
private IResourceProvider? _resourceProvider;
public DynamicResourceExtension() public DynamicResourceExtension()
{ {
@ -30,12 +29,9 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
if (!(provideTarget.TargetObject is IStyledElement)) if (!(provideTarget.TargetObject is IStyledElement))
{ {
_anchor = serviceProvider.GetFirstParent<IStyledElement>(); _anchor = serviceProvider.GetFirstParent<IStyledElement>() ??
serviceProvider.GetFirstParent<IResourceProvider>() ??
if (_anchor is null) (object?)serviceProvider.GetFirstParent<IResourceHost>();
{
_resourceProvider = serviceProvider.GetFirstParent<IResourceProvider>();
}
} }
return this; return this;
@ -52,16 +48,16 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
return null; return null;
} }
var control = target as IStyledElement ?? _anchor as IStyledElement; var control = target as IResourceHost ?? _anchor as IResourceHost;
if (control != null) if (control != null)
{ {
var source = control.GetResourceObservable(ResourceKey, GetConverter(targetProperty)); var source = control.GetResourceObservable(ResourceKey, GetConverter(targetProperty));
return InstancedBinding.OneWay(source); return InstancedBinding.OneWay(source);
} }
else if (_resourceProvider is object) else if (_anchor is IResourceProvider resourceProvider)
{ {
var source = _resourceProvider.GetResourceObservable(ResourceKey, GetConverter(targetProperty)); var source = resourceProvider.GetResourceObservable(ResourceKey, GetConverter(targetProperty));
return InstancedBinding.OneWay(source); return InstancedBinding.OneWay(source);
} }

89
src/Markup/Avalonia.Markup/Markup/Parsers/BindingExpressionGrammar.cs

@ -46,6 +46,10 @@ namespace Avalonia.Markup.Parsers
state = ParseIndexer(ref r, nodes); state = ParseIndexer(ref r, nodes);
break; break;
case State.TypeCast:
state = ParseTypeCast(ref r, nodes);
break;
case State.ElementName: case State.ElementName:
state = ParseElementName(ref r, nodes); state = ParseElementName(ref r, nodes);
mode = SourceMode.Control; mode = SourceMode.Control;
@ -84,6 +88,11 @@ namespace Avalonia.Markup.Parsers
} }
else if (ParseOpenBrace(ref r)) else if (ParseOpenBrace(ref r))
{ {
if (PeekOpenBrace(ref r))
{
return State.TypeCast;
}
return State.AttachedProperty; return State.AttachedProperty;
} }
else if (PeekOpenBracket(ref r)) else if (PeekOpenBracket(ref r))
@ -124,6 +133,10 @@ namespace Avalonia.Markup.Parsers
{ {
return State.Indexer; return State.Indexer;
} }
else if (ParseOpenBrace(ref r))
{
return State.TypeCast;
}
return State.End; return State.End;
} }
@ -132,6 +145,11 @@ namespace Avalonia.Markup.Parsers
{ {
if (ParseOpenBrace(ref r)) if (ParseOpenBrace(ref r))
{ {
if (PeekOpenBrace(ref r))
{
return State.TypeCast;
}
return State.AttachedProperty; return State.AttachedProperty;
} }
else else
@ -152,6 +170,12 @@ namespace Avalonia.Markup.Parsers
{ {
var (ns, owner) = ParseTypeName(ref r); var (ns, owner) = ParseTypeName(ref r);
if(!r.End && r.TakeIf(')'))
{
nodes.Add(new TypeCastNode() { Namespace = ns, TypeName = owner });
return State.AfterMember;
}
if (r.End || !r.TakeIf('.')) if (r.End || !r.TakeIf('.'))
{ {
throw new ExpressionParseException(r.Position, "Invalid attached property name."); throw new ExpressionParseException(r.Position, "Invalid attached property name.");
@ -159,6 +183,11 @@ namespace Avalonia.Markup.Parsers
var name = r.ParseIdentifier(); var name = r.ParseIdentifier();
if (name.Length == 0)
{
throw new ExpressionParseException(r.Position, "Attached Property name expected after '.'.");
}
if (r.End || !r.TakeIf(')')) if (r.End || !r.TakeIf(')'))
{ {
throw new ExpressionParseException(r.Position, "Expected ')'."); throw new ExpressionParseException(r.Position, "Expected ')'.");
@ -186,6 +215,39 @@ namespace Avalonia.Markup.Parsers
return State.AfterMember; return State.AfterMember;
} }
private static State ParseTypeCast(ref CharacterReader r, List<INode> nodes)
{
bool parseMemberBeforeAddCast = ParseOpenBrace(ref r);
var (ns, typeName) = ParseTypeName(ref r);
var result = State.AfterMember;
if (parseMemberBeforeAddCast)
{
if (!ParseCloseBrace(ref r))
{
throw new ExpressionParseException(r.Position, "Expected ')'.");
}
result = ParseBeforeMember(ref r, nodes);
if(r.Peek == '[')
{
result = ParseIndexer(ref r, nodes);
}
}
nodes.Add(new TypeCastNode { Namespace = ns, TypeName = typeName });
if (r.End || !r.TakeIf(')'))
{
throw new ExpressionParseException(r.Position, "Expected ')'.");
}
return result;
}
private static State ParseElementName(ref CharacterReader r, List<INode> nodes) private static State ParseElementName(ref CharacterReader r, List<INode> nodes)
{ {
var name = r.ParseIdentifier(); var name = r.ParseIdentifier();
@ -288,11 +350,21 @@ namespace Avalonia.Markup.Parsers
return !r.End && r.TakeIf('('); return !r.End && r.TakeIf('(');
} }
private static bool ParseCloseBrace(ref CharacterReader r)
{
return !r.End && r.TakeIf(')');
}
private static bool PeekOpenBracket(ref CharacterReader r) private static bool PeekOpenBracket(ref CharacterReader r)
{ {
return !r.End && r.Peek == '['; return !r.End && r.Peek == '[';
} }
private static bool PeekOpenBrace(ref CharacterReader r)
{
return !r.End && r.Peek == '(';
}
private static bool ParseStreamOperator(ref CharacterReader r) private static bool ParseStreamOperator(ref CharacterReader r)
{ {
return !r.End && r.TakeIf('^'); return !r.End && r.TakeIf('^');
@ -322,6 +394,7 @@ namespace Avalonia.Markup.Parsers
BeforeMember, BeforeMember,
AttachedProperty, AttachedProperty,
Indexer, Indexer,
TypeCast,
End, End,
} }
@ -343,9 +416,9 @@ namespace Avalonia.Markup.Parsers
} }
} }
public interface INode {} public interface INode { }
public interface ITransformNode {} public interface ITransformNode { }
public class EmptyExpressionNode : INode { } public class EmptyExpressionNode : INode { }
@ -366,11 +439,11 @@ namespace Avalonia.Markup.Parsers
public IList<string> Arguments { get; set; } public IList<string> Arguments { get; set; }
} }
public class NotNode : INode, ITransformNode {} public class NotNode : INode, ITransformNode { }
public class StreamNode : INode {} public class StreamNode : INode { }
public class SelfNode : INode {} public class SelfNode : INode { }
public class NameNode : INode public class NameNode : INode
{ {
@ -383,5 +456,11 @@ namespace Avalonia.Markup.Parsers
public string TypeName { get; set; } public string TypeName { get; set; }
public int Level { get; set; } public int Level { get; set; }
} }
public class TypeCastNode : INode
{
public string Namespace { get; set; }
public string TypeName { get; set; }
}
} }
} }

19
src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionParser.cs

@ -59,6 +59,9 @@ namespace Avalonia.Markup.Parsers
case BindingExpressionGrammar.NameNode elementName: case BindingExpressionGrammar.NameNode elementName:
nextNode = new ElementNameNode(_nameScope, elementName.Name); nextNode = new ElementNameNode(_nameScope, elementName.Name);
break; break;
case BindingExpressionGrammar.TypeCastNode typeCast:
nextNode = ParseTypeCastNode(typeCast);
break;
} }
if (rootNode is null) if (rootNode is null)
{ {
@ -92,6 +95,22 @@ namespace Avalonia.Markup.Parsers
return new FindAncestorNode(ancestorType, ancestorLevel); return new FindAncestorNode(ancestorType, ancestorLevel);
} }
private TypeCastNode ParseTypeCastNode(BindingExpressionGrammar.TypeCastNode node)
{
Type castType = null;
if (!(node.Namespace is null) && !(node.TypeName is null))
{
if (_typeResolver == null)
{
throw new InvalidOperationException("Cannot parse a binding path with a typed Cast without a type resolver. Maybe you can use a LINQ Expression binding path instead?");
}
castType = _typeResolver(node.Namespace, node.TypeName);
}
return new TypeCastNode(castType);
}
private AvaloniaPropertyAccessorNode ParseAttachedProperty(BindingExpressionGrammar.AttachedPropertyNameNode node) private AvaloniaPropertyAccessorNode ParseAttachedProperty(BindingExpressionGrammar.AttachedPropertyNameNode node)
{ {
if (_typeResolver == null) if (_typeResolver == null)

12
src/Skia/Avalonia.Skia/DrawingContextImpl.cs

@ -434,7 +434,7 @@ namespace Avalonia.Skia
/// <inheritdoc /> /// <inheritdoc />
public IDrawingContextLayerImpl CreateLayer(Size size) public IDrawingContextLayerImpl CreateLayer(Size size)
{ {
return CreateRenderTarget( size); return CreateRenderTarget(size, true);
} }
/// <inheritdoc /> /// <inheritdoc />
@ -673,7 +673,7 @@ namespace Avalonia.Skia
private void ConfigureTileBrush(ref PaintWrapper paintWrapper, Size targetSize, ITileBrush tileBrush, IDrawableBitmapImpl tileBrushImage) private void ConfigureTileBrush(ref PaintWrapper paintWrapper, Size targetSize, ITileBrush tileBrush, IDrawableBitmapImpl tileBrushImage)
{ {
var calc = new TileBrushCalculator(tileBrush, tileBrushImage.PixelSize.ToSizeWithDpi(_dpi), targetSize); var calc = new TileBrushCalculator(tileBrush, tileBrushImage.PixelSize.ToSizeWithDpi(_dpi), targetSize);
var intermediate = CreateRenderTarget(calc.IntermediateSize); var intermediate = CreateRenderTarget(calc.IntermediateSize, false);
paintWrapper.AddDisposable(intermediate); paintWrapper.AddDisposable(intermediate);
@ -748,7 +748,7 @@ namespace Avalonia.Skia
if (intermediateSize.Width >= 1 && intermediateSize.Height >= 1) if (intermediateSize.Width >= 1 && intermediateSize.Height >= 1)
{ {
var intermediate = CreateRenderTarget(intermediateSize); var intermediate = CreateRenderTarget(intermediateSize, false);
using (var ctx = intermediate.CreateDrawingContext(visualBrushRenderer)) using (var ctx = intermediate.CreateDrawingContext(visualBrushRenderer))
{ {
@ -978,9 +978,10 @@ namespace Avalonia.Skia
/// Create new render target compatible with this drawing context. /// Create new render target compatible with this drawing context.
/// </summary> /// </summary>
/// <param name="size">The size of the render target in DIPs.</param> /// <param name="size">The size of the render target in DIPs.</param>
/// <param name="isLayer">Whether the render target is being created for a layer.</param>
/// <param name="format">Pixel format.</param> /// <param name="format">Pixel format.</param>
/// <returns></returns> /// <returns></returns>
private SurfaceRenderTarget CreateRenderTarget(Size size, PixelFormat? format = null) private SurfaceRenderTarget CreateRenderTarget(Size size, bool isLayer, PixelFormat? format = null)
{ {
var pixelSize = PixelSize.FromSizeWithDpi(size, _dpi); var pixelSize = PixelSize.FromSizeWithDpi(size, _dpi);
var createInfo = new SurfaceRenderTarget.CreateInfo var createInfo = new SurfaceRenderTarget.CreateInfo
@ -992,7 +993,8 @@ namespace Avalonia.Skia
DisableTextLcdRendering = !_canTextUseLcdRendering, DisableTextLcdRendering = !_canTextUseLcdRendering,
GrContext = _grContext, GrContext = _grContext,
Gpu = _gpu, Gpu = _gpu,
Session = _session Session = _session,
DisableManualFbo = !isLayer,
}; };
return new SurfaceRenderTarget(createInfo); return new SurfaceRenderTarget(createInfo);

3
src/Skia/Avalonia.Skia/PlatformRenderInterface.cs

@ -124,7 +124,8 @@ namespace Avalonia.Skia
Width = size.Width, Width = size.Width,
Height = size.Height, Height = size.Height,
Dpi = dpi, Dpi = dpi,
DisableTextLcdRendering = false DisableTextLcdRendering = false,
DisableManualFbo = true,
}; };
return new SurfaceRenderTarget(createInfo); return new SurfaceRenderTarget(createInfo);

5
src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs

@ -51,7 +51,8 @@ namespace Avalonia.Skia
_grContext = createInfo.GrContext; _grContext = createInfo.GrContext;
_gpu = createInfo.Gpu; _gpu = createInfo.Gpu;
_surface = _gpu?.TryCreateSurface(PixelSize, createInfo.Session); if (!createInfo.DisableManualFbo)
_surface = _gpu?.TryCreateSurface(PixelSize, createInfo.Session);
if (_surface == null) if (_surface == null)
_surface = new SkiaSurfaceWrapper(CreateSurface(createInfo.GrContext, PixelSize.Width, PixelSize.Height, _surface = new SkiaSurfaceWrapper(CreateSurface(createInfo.GrContext, PixelSize.Width, PixelSize.Height,
createInfo.Format)); createInfo.Format));
@ -220,6 +221,8 @@ namespace Avalonia.Skia
public ISkiaGpu Gpu; public ISkiaGpu Gpu;
public ISkiaGpuRenderSession Session; public ISkiaGpuRenderSession Session;
public bool DisableManualFbo;
} }
} }
} }

1
src/Windows/Avalonia.Win32/.gitignore

@ -0,0 +1 @@
*.Generated.cs

4
src/Windows/Avalonia.Win32/Avalonia.Win32.csproj

@ -5,9 +5,11 @@
<PackageId>Avalonia.Win32</PackageId> <PackageId>Avalonia.Win32</PackageId>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="System.Numerics.Vectors" Version="4.5.0" />
<ProjectReference Include="..\..\..\packages\Avalonia\Avalonia.csproj" /> <ProjectReference Include="..\..\..\packages\Avalonia\Avalonia.csproj" />
<PackageReference Include="Avalonia.Angle.Windows.Natives" Version="2.1.0.2020091801" /> <PackageReference Include="Avalonia.Angle.Windows.Natives" Version="2.1.0.2020091801" />
<PackageReference Include="Microsoft.Windows.SDK.NET" Version="10.0.18362.3-preview" /> <AvnComIdl Include="WinRT\winrt.idl" OutputFile="WinRT\WinRT.Generated.cs" />
</ItemGroup> </ItemGroup>
<Import Project="../../../build/MicroCom.targets" />
<Import Project="$(MSBuildThisFileDirectory)\..\..\..\build\System.Drawing.Common.props" /> <Import Project="$(MSBuildThisFileDirectory)\..\..\..\build\System.Drawing.Common.props" />
</Project> </Project>

18
src/Windows/Avalonia.Win32/Composition/CompositionBlurHost.cs

@ -1,18 +0,0 @@
namespace Avalonia.Win32
{
internal class CompositionBlurHost : IBlurHost
{
Windows.UI.Composition.Visual _blurVisual;
public CompositionBlurHost(Windows.UI.Composition.Visual blurVisual)
{
_blurVisual = blurVisual;
}
public void SetBlur(bool enabled)
{
_blurVisual.IsVisible = enabled;
}
}
}

171
src/Windows/Avalonia.Win32/Composition/CompositionConnector.cs

@ -1,171 +0,0 @@
using System;
using System.Runtime.InteropServices;
using Avalonia.Logging;
using Avalonia.OpenGL;
using Avalonia.OpenGL.Angle;
using Avalonia.OpenGL.Egl;
using Windows.UI.Composition;
using Windows.UI.Composition.Interop;
using WinRT;
namespace Avalonia.Win32
{
internal class CompositionConnector
{
private Compositor _compositor;
private Windows.System.DispatcherQueueController _dispatcherQueueController;
private CompositionGraphicsDevice _graphicsDevice;
internal enum DISPATCHERQUEUE_THREAD_APARTMENTTYPE
{
DQTAT_COM_NONE = 0,
DQTAT_COM_ASTA = 1,
DQTAT_COM_STA = 2
};
internal enum DISPATCHERQUEUE_THREAD_TYPE
{
DQTYPE_THREAD_DEDICATED = 1,
DQTYPE_THREAD_CURRENT = 2,
};
[StructLayout(LayoutKind.Sequential)]
internal struct DispatcherQueueOptions
{
public int dwSize;
[MarshalAs(UnmanagedType.I4)]
public DISPATCHERQUEUE_THREAD_TYPE threadType;
[MarshalAs(UnmanagedType.I4)]
public DISPATCHERQUEUE_THREAD_APARTMENTTYPE apartmentType;
};
[DllImport("coremessaging.dll", EntryPoint = "CreateDispatcherQueueController", CharSet = CharSet.Unicode)]
internal static extern IntPtr CreateDispatcherQueueController(DispatcherQueueOptions options, out IntPtr dispatcherQueueController);
public static CompositionConnector TryCreate(EglPlatformOpenGlInterface egl)
{
const int majorRequired = 10;
const int buildRequired = 16299;
var majorInstalled = Win32Platform.WindowsVersion.Major;
var buildInstalled = Win32Platform.WindowsVersion.Build;
if (majorInstalled >= majorRequired &&
buildInstalled >= buildRequired)
{
try
{
return new CompositionConnector(egl);
}
catch (Exception e)
{
Logger.TryGet(LogEventLevel.Error, "WinUIComposition")?.Log(null, "Unable to initialize WinUI compositor: {0}", e);
return null;
}
}
var osVersionNotice = $"Windows {majorRequired} Build {buildRequired} is required. Your machine has Windows {majorInstalled} Build {buildInstalled} installed.";
Logger.TryGet(LogEventLevel.Warning, "WinUIComposition")?.Log(null,
$"Unable to initialize WinUI compositor: {osVersionNotice}");
return null;
}
public CompositionConnector(EglPlatformOpenGlInterface egl)
{
EnsureDispatcherQueue();
if (_dispatcherQueueController != null)
_compositor = new Compositor();
var interop = _compositor.As<ICompositorInterop>();
var display = egl.Display as AngleWin32EglDisplay;
_graphicsDevice = interop.CreateGraphicsDevice(display.GetDirect3DDevice());
}
public ICompositionDrawingSurfaceInterop InitialiseWindowCompositionTree(IntPtr hwnd, out Windows.UI.Composition.Visual surfaceVisual, out IBlurHost blurHost)
{
var target = CreateDesktopWindowTarget(hwnd);
var surface = _graphicsDevice.CreateDrawingSurface(new Windows.Foundation.Size(0, 0),
Windows.Graphics.DirectX.DirectXPixelFormat.B8G8R8A8UIntNormalized,
Windows.Graphics.DirectX.DirectXAlphaMode.Premultiplied);
var surfaceInterop = surface.As<ICompositionDrawingSurfaceInterop>();
var brush = _compositor.CreateSurfaceBrush(surface);
var visual = _compositor.CreateSpriteVisual();
visual.Brush = brush;
visual.RelativeSizeAdjustment = new System.Numerics.Vector2(1, 1);
var container = _compositor.CreateContainerVisual();
target.Root = container;
var blur = CreateBlur();
blurHost = new CompositionBlurHost(blur);
container.Children.InsertAtTop(blur);
container.Children.InsertAtTop(visual);
visual.CompositeMode = CompositionCompositeMode.SourceOver;
surfaceVisual = container;
return surfaceInterop;
}
private SpriteVisual CreateBlur()
{
var blurEffect = new GaussianBlurEffect(new CompositionEffectSourceParameter("backdrop"));
var backDropBrush = _compositor.CreateBackdropBrush();
var saturateEffect = new SaturationEffect(blurEffect);
var satEffectFactory = _compositor.CreateEffectFactory(saturateEffect);
var satBrush = satEffectFactory.CreateBrush();
satBrush.SetSourceParameter("backdrop", backDropBrush);
var visual = _compositor.CreateSpriteVisual();
visual.IsVisible = false;
visual.RelativeSizeAdjustment = new System.Numerics.Vector2(1.0f, 1.0f);
visual.Brush = satBrush;
return visual;
}
private CompositionTarget CreateDesktopWindowTarget(IntPtr window)
{
var interop = _compositor.As<global::Windows.UI.Composition.Desktop.ICompositorDesktopInterop>();
interop.CreateDesktopWindowTarget(window, false, out var windowTarget);
return Windows.UI.Composition.Desktop.DesktopWindowTarget.FromAbi(windowTarget);
}
private void EnsureDispatcherQueue()
{
if (_dispatcherQueueController == null)
{
DispatcherQueueOptions options = new DispatcherQueueOptions();
options.apartmentType = DISPATCHERQUEUE_THREAD_APARTMENTTYPE.DQTAT_COM_NONE;
options.threadType = DISPATCHERQUEUE_THREAD_TYPE.DQTYPE_THREAD_CURRENT;
options.dwSize = Marshal.SizeOf(typeof(DispatcherQueueOptions));
CreateDispatcherQueueController(options, out var queue);
_dispatcherQueueController = Windows.System.DispatcherQueueController.FromAbi(queue);
}
}
}
}

96
src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs

@ -1,96 +0,0 @@
using System;
using System.Runtime.InteropServices;
using Avalonia.OpenGL;
using Avalonia.OpenGL.Angle;
using Avalonia.OpenGL.Egl;
using Avalonia.OpenGL.Surfaces;
using Windows.UI.Composition.Interop;
namespace Avalonia.Win32
{
internal class CompositionEglGlPlatformSurface : EglGlPlatformSurfaceBase
{
private EglPlatformOpenGlInterface _egl;
private readonly IEglWindowGlPlatformSurfaceInfo _info;
private ICompositionDrawingSurfaceInterop _surfaceInterop;
private Windows.UI.Composition.Visual _surface;
public CompositionEglGlPlatformSurface(EglPlatformOpenGlInterface egl, IEglWindowGlPlatformSurfaceInfo info) : base()
{
_egl = egl;
_info = info;
}
public IBlurHost AttachToCompositionTree(CompositionConnector connector, IntPtr hwnd)
{
using (_egl.PrimaryContext.MakeCurrent())
{
_surfaceInterop = connector.InitialiseWindowCompositionTree(hwnd, out _surface, out var blurHost);
return blurHost;
}
}
public override IGlPlatformSurfaceRenderTarget CreateGlRenderTarget()
{
return new CompositionRenderTarget(_egl, _surface, _surfaceInterop, _info);
}
class CompositionRenderTarget : EglPlatformSurfaceRenderTargetBase
{
private readonly EglPlatformOpenGlInterface _egl;
private readonly IEglWindowGlPlatformSurfaceInfo _info;
private PixelSize _currentSize;
private readonly ICompositionDrawingSurfaceInterop _surfaceInterop;
private static Guid s_Iid = Guid.Parse("6f15aaf2-d208-4e89-9ab4-489535d34f9c");
private Windows.UI.Composition.Visual _compositionVisual;
public CompositionRenderTarget(EglPlatformOpenGlInterface egl,
Windows.UI.Composition.Visual compositionVisual,
ICompositionDrawingSurfaceInterop interopSurface,
IEglWindowGlPlatformSurfaceInfo info)
: base(egl)
{
_egl = egl;
_surfaceInterop = interopSurface;
_info = info;
_currentSize = info.Size;
_compositionVisual = compositionVisual;
using (_egl.PrimaryContext.MakeCurrent())
{
_surfaceInterop.Resize(new POINT { X = _info.Size.Width, Y = _info.Size.Height });
}
_compositionVisual.Size = new System.Numerics.Vector2(_info.Size.Width, _info.Size.Height);
}
public override IGlPlatformSurfaceRenderingSession BeginDraw()
{
IntPtr texture;
EglSurface surface;
using (_egl.PrimaryEglContext.EnsureCurrent())
{
if (_info.Size != _currentSize)
{
_surfaceInterop.Resize(new POINT { X = _info.Size.Width, Y = _info.Size.Height });
_compositionVisual.Size = new System.Numerics.Vector2(_info.Size.Width, _info.Size.Height);
_currentSize = _info.Size;
}
var offset = new POINT();
_surfaceInterop.BeginDraw(
IntPtr.Zero,
ref s_Iid,
out texture, ref offset);
surface = (_egl.Display as AngleWin32EglDisplay).WrapDirect3D11Texture(_egl, texture, offset.X, offset.Y, _info.Size.Width, _info.Size.Height);
}
return base.BeginDraw(surface, _info, () => { _surfaceInterop.EndDraw(); Marshal.Release(texture); surface.Dispose(); }, true);
}
}
}
}

91
src/Windows/Avalonia.Win32/Composition/D2DEffects.cs

@ -1,91 +0,0 @@
using System;
namespace Avalonia.Win32
{
class D2DEffects
{
public static readonly Guid CLSID_D2D12DAffineTransform = new Guid(0x6AA97485, 0x6354, 0x4CFC, 0x90, 0x8C, 0xE4, 0xA7, 0x4F, 0x62, 0xC9, 0x6C);
public static readonly Guid CLSID_D2D13DPerspectiveTransform = new Guid(0xC2844D0B, 0x3D86, 0x46E7, 0x85, 0xBA, 0x52, 0x6C, 0x92, 0x40, 0xF3, 0xFB);
public static readonly Guid CLSID_D2D13DTransform = new Guid(0xE8467B04, 0xEC61, 0x4B8A, 0xB5, 0xDE, 0xD4, 0xD7, 0x3D, 0xEB, 0xEA, 0x5A);
public static readonly Guid CLSID_D2D1ArithmeticComposite = new Guid(0xFC151437, 0x049A, 0x4784, 0xA2, 0x4A, 0xF1, 0xC4, 0xDA, 0xF2, 0x09, 0x87);
public static readonly Guid CLSID_D2D1Atlas = new Guid(0x913E2BE4, 0xFDCF, 0x4FE2, 0xA5, 0xF0, 0x24, 0x54, 0xF1, 0x4F, 0xF4, 0x08);
public static readonly Guid CLSID_D2D1BitmapSource = new Guid(0x5FB6C24D, 0xC6DD, 0x4231, 0x94, 0x4, 0x50, 0xF4, 0xD5, 0xC3, 0x25, 0x2D);
public static readonly Guid CLSID_D2D1Blend = new Guid(0x81C5B77B, 0x13F8, 0x4CDD, 0xAD, 0x20, 0xC8, 0x90, 0x54, 0x7A, 0xC6, 0x5D);
public static readonly Guid CLSID_D2D1Border = new Guid(0x2A2D49C0, 0x4ACF, 0x43C7, 0x8C, 0x6A, 0x7C, 0x4A, 0x27, 0x87, 0x4D, 0x27);
public static readonly Guid CLSID_D2D1Brightness = new Guid(0x8CEA8D1E, 0x77B0, 0x4986, 0xB3, 0xB9, 0x2F, 0x0C, 0x0E, 0xAE, 0x78, 0x87);
public static readonly Guid CLSID_D2D1ColorManagement = new Guid(0x1A28524C, 0xFDD6, 0x4AA4, 0xAE, 0x8F, 0x83, 0x7E, 0xB8, 0x26, 0x7B, 0x37);
public static readonly Guid CLSID_D2D1ColorMatrix = new Guid(0x921F03D6, 0x641C, 0x47DF, 0x85, 0x2D, 0xB4, 0xBB, 0x61, 0x53, 0xAE, 0x11);
public static readonly Guid CLSID_D2D1Composite = new Guid(0x48FC9F51, 0xF6AC, 0x48F1, 0x8B, 0x58, 0x3B, 0x28, 0xAC, 0x46, 0xF7, 0x6D);
public static readonly Guid CLSID_D2D1ConvolveMatrix = new Guid(0x407F8C08, 0x5533, 0x4331, 0xA3, 0x41, 0x23, 0xCC, 0x38, 0x77, 0x84, 0x3E);
public static readonly Guid CLSID_D2D1Crop = new Guid(0xE23F7110, 0x0E9A, 0x4324, 0xAF, 0x47, 0x6A, 0x2C, 0x0C, 0x46, 0xF3, 0x5B);
public static readonly Guid CLSID_D2D1DirectionalBlur = new Guid(0x174319A6, 0x58E9, 0x49B2, 0xBB, 0x63, 0xCA, 0xF2, 0xC8, 0x11, 0xA3, 0xDB);
public static readonly Guid CLSID_D2D1DiscreteTransfer = new Guid(0x90866FCD, 0x488E, 0x454B, 0xAF, 0x06, 0xE5, 0x04, 0x1B, 0x66, 0xC3, 0x6C);
public static readonly Guid CLSID_D2D1DisplacementMap = new Guid(0xEDC48364, 0x417, 0x4111, 0x94, 0x50, 0x43, 0x84, 0x5F, 0xA9, 0xF8, 0x90);
public static readonly Guid CLSID_D2D1DistantDiffuse = new Guid(0x3E7EFD62, 0xA32D, 0x46D4, 0xA8, 0x3C, 0x52, 0x78, 0x88, 0x9A, 0xC9, 0x54);
public static readonly Guid CLSID_D2D1DistantSpecular = new Guid(0x428C1EE5, 0x77B8, 0x4450, 0x8A, 0xB5, 0x72, 0x21, 0x9C, 0x21, 0xAB, 0xDA);
public static readonly Guid CLSID_D2D1DpiCompensation = new Guid(0x6C26C5C7, 0x34E0, 0x46FC, 0x9C, 0xFD, 0xE5, 0x82, 0x37, 0x6, 0xE2, 0x28);
public static readonly Guid CLSID_D2D1Flood = new Guid(0x61C23C20, 0xAE69, 0x4D8E, 0x94, 0xCF, 0x50, 0x07, 0x8D, 0xF6, 0x38, 0xF2);
public static readonly Guid CLSID_D2D1GammaTransfer = new Guid(0x409444C4, 0xC419, 0x41A0, 0xB0, 0xC1, 0x8C, 0xD0, 0xC0, 0xA1, 0x8E, 0x42);
public static readonly Guid CLSID_D2D1GaussianBlur = new Guid(0x1FEB6D69, 0x2FE6, 0x4AC9, 0x8C, 0x58, 0x1D, 0x7F, 0x93, 0xE7, 0xA6, 0xA5);
public static readonly Guid CLSID_D2D1Scale = new Guid(0x9DAF9369, 0x3846, 0x4D0E, 0xA4, 0x4E, 0xC, 0x60, 0x79, 0x34, 0xA5, 0xD7);
public static readonly Guid CLSID_D2D1Histogram = new Guid(0x881DB7D0, 0xF7EE, 0x4D4D, 0xA6, 0xD2, 0x46, 0x97, 0xAC, 0xC6, 0x6E, 0xE8);
public static readonly Guid CLSID_D2D1HueRotation = new Guid(0x0F4458EC, 0x4B32, 0x491B, 0x9E, 0x85, 0xBD, 0x73, 0xF4, 0x4D, 0x3E, 0xB6);
public static readonly Guid CLSID_D2D1LinearTransfer = new Guid(0xAD47C8FD, 0x63EF, 0x4ACC, 0x9B, 0x51, 0x67, 0x97, 0x9C, 0x03, 0x6C, 0x06);
public static readonly Guid CLSID_D2D1LuminanceToAlpha = new Guid(0x41251AB7, 0x0BEB, 0x46F8, 0x9D, 0xA7, 0x59, 0xE9, 0x3F, 0xCC, 0xE5, 0xDE);
public static readonly Guid CLSID_D2D1Morphology = new Guid(0xEAE6C40D, 0x626A, 0x4C2D, 0xBF, 0xCB, 0x39, 0x10, 0x01, 0xAB, 0xE2, 0x02);
public static readonly Guid CLSID_D2D1OpacityMetadata = new Guid(0x6C53006A, 0x4450, 0x4199, 0xAA, 0x5B, 0xAD, 0x16, 0x56, 0xFE, 0xCE, 0x5E);
public static readonly Guid CLSID_D2D1PointDiffuse = new Guid(0xB9E303C3, 0xC08C, 0x4F91, 0x8B, 0x7B, 0x38, 0x65, 0x6B, 0xC4, 0x8C, 0x20);
public static readonly Guid CLSID_D2D1PointSpecular = new Guid(0x09C3CA26, 0x3AE2, 0x4F09, 0x9E, 0xBC, 0xED, 0x38, 0x65, 0xD5, 0x3F, 0x22);
public static readonly Guid CLSID_D2D1Premultiply = new Guid(0x06EAB419, 0xDEED, 0x4018, 0x80, 0xD2, 0x3E, 0x1D, 0x47, 0x1A, 0xDE, 0xB2);
public static readonly Guid CLSID_D2D1Saturation = new Guid(0x5CB2D9CF, 0x327D, 0x459F, 0xA0, 0xCE, 0x40, 0xC0, 0xB2, 0x08, 0x6B, 0xF7);
public static readonly Guid CLSID_D2D1Shadow = new Guid(0xC67EA361, 0x1863, 0x4E69, 0x89, 0xDB, 0x69, 0x5D, 0x3E, 0x9A, 0x5B, 0x6B);
public static readonly Guid CLSID_D2D1SpotDiffuse = new Guid(0x818A1105, 0x7932, 0x44F4, 0xAA, 0x86, 0x08, 0xAE, 0x7B, 0x2F, 0x2C, 0x93);
public static readonly Guid CLSID_D2D1SpotSpecular = new Guid(0xEDAE421E, 0x7654, 0x4A37, 0x9D, 0xB8, 0x71, 0xAC, 0xC1, 0xBE, 0xB3, 0xC1);
public static readonly Guid CLSID_D2D1TableTransfer = new Guid(0x5BF818C3, 0x5E43, 0x48CB, 0xB6, 0x31, 0x86, 0x83, 0x96, 0xD6, 0xA1, 0xD4);
public static readonly Guid CLSID_D2D1Tile = new Guid(0xB0784138, 0x3B76, 0x4BC5, 0xB1, 0x3B, 0x0F, 0xA2, 0xAD, 0x02, 0x65, 0x9F);
public static readonly Guid CLSID_D2D1Turbulence = new Guid(0xCF2BB6AE, 0x889A, 0x4AD7, 0xBA, 0x29, 0xA2, 0xFD, 0x73, 0x2C, 0x9F, 0xC9);
public static readonly Guid CLSID_D2D1UnPremultiply = new Guid(0xFB9AC489, 0xAD8D, 0x41ED, 0x99, 0x99, 0xBB, 0x63, 0x47, 0xD1, 0x10, 0xF7);
}
}

45
src/Windows/Avalonia.Win32/Composition/EffectBase.cs

@ -1,45 +0,0 @@
using System;
using Windows.Graphics.Effects;
using Windows.Graphics.Effects.Interop;
namespace Avalonia.Win32
{
abstract class EffectBase : IGraphicsEffect, IGraphicsEffectSource, global::Windows.Graphics.Effects.Interop.IGraphicsEffectD2D1Interop
{
private IGraphicsEffectSource[] _sources;
public EffectBase(params IGraphicsEffectSource[] sources)
{
_sources = sources;
}
private IGraphicsEffectSource _source;
public virtual string Name { get; set; }
public abstract Guid EffectId { get; }
public abstract uint PropertyCount { get; }
public uint SourceCount => (uint)_sources.Length;
public IGraphicsEffectSource GetSource(uint index)
{
if(index < _sources.Length)
{
return _sources[index];
}
return null;
}
public uint GetNamedPropertyMapping(string name, out GRAPHICS_EFFECT_PROPERTY_MAPPING mapping)
{
throw new NotImplementedException();
}
public abstract object GetProperty(uint index);
}
}

18
src/Windows/Avalonia.Win32/Composition/GRAPHICS_EFFECT_PROPERTY_MAPPING.cs

@ -1,18 +0,0 @@
namespace Windows.Graphics.Effects.Interop
{
public enum GRAPHICS_EFFECT_PROPERTY_MAPPING
{
GRAPHICS_EFFECT_PROPERTY_MAPPING_UNKNOWN,
GRAPHICS_EFFECT_PROPERTY_MAPPING_DIRECT,
GRAPHICS_EFFECT_PROPERTY_MAPPING_VECTORX,
GRAPHICS_EFFECT_PROPERTY_MAPPING_VECTORY,
GRAPHICS_EFFECT_PROPERTY_MAPPING_VECTORZ,
GRAPHICS_EFFECT_PROPERTY_MAPPING_VECTORW,
GRAPHICS_EFFECT_PROPERTY_MAPPING_RECT_TO_VECTOR4,
GRAPHICS_EFFECT_PROPERTY_MAPPING_RADIANS_TO_DEGREES,
GRAPHICS_EFFECT_PROPERTY_MAPPING_COLORMATRIX_ALPHA_MODE,
GRAPHICS_EFFECT_PROPERTY_MAPPING_COLOR_TO_VECTOR3,
GRAPHICS_EFFECT_PROPERTY_MAPPING_COLOR_TO_VECTOR4
};
}

57
src/Windows/Avalonia.Win32/Composition/GaussianBlurEffect.cs

@ -1,57 +0,0 @@
using System;
using Windows.Graphics.Effects;
namespace Avalonia.Win32
{
class GaussianBlurEffect : EffectBase
{
public GaussianBlurEffect(IGraphicsEffectSource source) : base(source)
{
}
enum D2D1_GAUSSIANBLUR_OPTIMIZATION
{
D2D1_GAUSSIANBLUR_OPTIMIZATION_SPEED,
D2D1_GAUSSIANBLUR_OPTIMIZATION_BALANCED,
D2D1_GAUSSIANBLUR_OPTIMIZATION_QUALITY,
D2D1_GAUSSIANBLUR_OPTIMIZATION_FORCE_DWORD
};
enum D2D1_BORDER_MODE
{
D2D1_BORDER_MODE_SOFT,
D2D1_BORDER_MODE_HARD,
D2D1_BORDER_MODE_FORCE_DWORD
};
enum D2D1GaussianBlurProp
{
D2D1_GAUSSIANBLUR_PROP_STANDARD_DEVIATION,
D2D1_GAUSSIANBLUR_PROP_OPTIMIZATION,
D2D1_GAUSSIANBLUR_PROP_BORDER_MODE,
D2D1_GAUSSIANBLUR_PROP_FORCE_DWORD
};
public override Guid EffectId => D2DEffects.CLSID_D2D1GaussianBlur;
public override uint PropertyCount => 3;
public override object GetProperty(uint index)
{
switch ((D2D1GaussianBlurProp)index)
{
case D2D1GaussianBlurProp.D2D1_GAUSSIANBLUR_PROP_STANDARD_DEVIATION:
return 30.0f;
case D2D1GaussianBlurProp.D2D1_GAUSSIANBLUR_PROP_OPTIMIZATION:
return (uint)D2D1_GAUSSIANBLUR_OPTIMIZATION.D2D1_GAUSSIANBLUR_OPTIMIZATION_BALANCED;
case D2D1GaussianBlurProp.D2D1_GAUSSIANBLUR_PROP_BORDER_MODE:
return (uint)D2D1_BORDER_MODE.D2D1_BORDER_MODE_HARD;
}
return null;
}
}
}

8
src/Windows/Avalonia.Win32/Composition/IBlurHost.cs

@ -1,8 +0,0 @@
namespace Avalonia.Win32
{
internal interface IBlurHost
{
void SetBlur(bool enabled);
}
}

152
src/Windows/Avalonia.Win32/Composition/ICompositionDrawingSurfaceInterop.cs

@ -1,152 +0,0 @@
using System;
using System.Runtime.InteropServices;
using WinRT;
namespace Windows.UI.Composition.Interop
{
public struct POINT
{
public int X;
public int Y;
}
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
public int Width => right - left;
public int Height => bottom - top;
}
[WindowsRuntimeType]
[Guid("FD04E6E3-FE0C-4C3C-AB19-A07601A576EE")]
public interface ICompositionDrawingSurfaceInterop
{
void BeginDraw(IntPtr updateRect, ref Guid iid, out IntPtr updateObject, ref POINT point);
void EndDraw();
void Resize(POINT sizePixels);
void ResumeDraw();
void Scroll(RECT scrollRect, RECT clipRect, int offsetX, int offsetY);
void SuspendDraw();
}
}
namespace ABI.Windows.UI.Composition.Interop
{
using global::System;
using global::System.Runtime.InteropServices;
using global::Windows.UI.Composition;
using global::Windows.UI.Composition.Interop;
[Guid("FD04E6E3-FE0C-4C3C-AB19-A07601A576EE")]
internal class ICompositionDrawingSurfaceInterop : global::Windows.UI.Composition.Interop.ICompositionDrawingSurfaceInterop
{
[Guid("FD04E6E3-FE0C-4C3C-AB19-A07601A576EE")]
public unsafe struct Vftbl
{
public delegate int _BeginDraw(IntPtr ThisPtr, IntPtr updateRect, ref Guid iid, out IntPtr updateObject, ref POINT updateOffset);
public delegate int _EndDraw(IntPtr ThisPtr);
public delegate int _Resize(IntPtr ThisPtr, POINT sizePixels);
public delegate int _ResumeDraw(IntPtr ThisPtr);
public delegate int _Scroll(IntPtr ThisPtr, RECT scrollRect, RECT clipRect, int offsetX, int offsetY);
public delegate int _SuspendDraw(IntPtr ThisPtr);
internal global::WinRT.Interop.IUnknownVftbl IUnknownVftbl;
public _BeginDraw BeginDraw;
public _EndDraw EndDraw;
public _Resize Resize;
public _ResumeDraw ResumeDraw;
public _Scroll Scroll;
public _SuspendDraw SuspendDraw;
public static readonly Vftbl AbiToProjectionVftable;
public static readonly IntPtr AbiToProjectionVftablePtr;
static Vftbl()
{
AbiToProjectionVftable = new Vftbl
{
IUnknownVftbl = global::WinRT.Interop.IUnknownVftbl.AbiToProjectionVftbl,
BeginDraw = Do_Abi_BeginDraw,
EndDraw = Do_Abi_EndDraw,
Resize = Do_Abi_Resize
};
AbiToProjectionVftablePtr = Marshal.AllocHGlobal(Marshal.SizeOf<Vftbl>());
Marshal.StructureToPtr(AbiToProjectionVftable, AbiToProjectionVftablePtr, false);
}
private static int Do_Abi_BeginDraw(IntPtr ThisPtr, IntPtr updateRect, ref Guid iid, out IntPtr updateObject, ref POINT updateOffset)
{
updateObject = IntPtr.Zero;
return 0;
}
private static int Do_Abi_EndDraw(IntPtr ThisPtr)
{
return 0;
}
private static int Do_Abi_Resize(IntPtr ThisPtr, POINT sizePixels)
{
return 0;
}
}
internal static ObjectReference<Vftbl> FromAbi(IntPtr thisPtr) => ObjectReference<Vftbl>.FromAbi(thisPtr);
public static implicit operator ICompositionDrawingSurfaceInterop(IObjectReference obj) => (obj != null) ? new ICompositionDrawingSurfaceInterop(obj) : null;
protected readonly ObjectReference<Vftbl> _obj;
public IObjectReference ObjRef { get => _obj; }
public IntPtr ThisPtr => _obj.ThisPtr;
public ObjectReference<I> AsInterface<I>() => _obj.As<I>();
public A As<A>() => _obj.AsType<A>();
public ICompositionDrawingSurfaceInterop(IObjectReference obj) : this(obj.As<Vftbl>()) { }
internal ICompositionDrawingSurfaceInterop(ObjectReference<Vftbl> obj)
{
_obj = obj;
}
public void BeginDraw(IntPtr updateRect, ref Guid iid, out IntPtr updateObject, ref POINT point)
{
Marshal.ThrowExceptionForHR(_obj.Vftbl.BeginDraw(ThisPtr, updateRect, ref iid, out updateObject, ref point));
}
public void EndDraw()
{
Marshal.ThrowExceptionForHR(_obj.Vftbl.EndDraw(ThisPtr));
}
public void Resize(POINT sizePixels)
{
Marshal.ThrowExceptionForHR(_obj.Vftbl.Resize(ThisPtr, sizePixels));
}
public void ResumeDraw()
{
throw new NotImplementedException();
}
public void Scroll(RECT scrollRect, RECT clipRect, int offsetX, int offsetY)
{
throw new NotImplementedException();
}
public void SuspendDraw()
{
throw new NotImplementedException();
}
}
}

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

Loading…
Cancel
Save