diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 721a0415f4..e67fa14c57 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -31,6 +31,8 @@ jobs:
condition: not(canceled())
- job: macOS
+ variables:
+ SolutionDir: '$(Build.SourcesDirectory)'
pool:
vmImage: 'macOS-10.14'
steps:
@@ -97,6 +99,8 @@ jobs:
- job: Windows
pool:
vmImage: 'windows-2019'
+ variables:
+ SolutionDir: '$(Build.SourcesDirectory)'
steps:
- task: UseDotNet@2
displayName: 'Use .NET Core SDK 3.1.401'
diff --git a/build/Assets/Icon.png b/build/Assets/Icon.png
new file mode 100644
index 0000000000..41a2a618fb
Binary files /dev/null and b/build/Assets/Icon.png differ
diff --git a/build/SharedVersion.props b/build/SharedVersion.props
index d3cebef418..95556af160 100644
--- a/build/SharedVersion.props
+++ b/build/SharedVersion.props
@@ -10,10 +10,16 @@
CS1591
latest
MIT
- https://avatars2.githubusercontent.com/u/14075148?s=200
+ Icon.png
Avalonia is a WPF/UWP-inspired cross-platform XAML-based UI framework providing a flexible styling system and supporting a wide range of Operating Systems such as Windows (.NET Framework, .NET Core), Linux (via Xorg), MacOS and with experimental support for Android and iOS.
avalonia;avaloniaui;mvvm;rx;reactive extensions;android;ios;mac;forms;wpf;net;netstandard;net461;uwp;xamarin
https://github.com/AvaloniaUI/Avalonia/releases
git
+ $(MSBuildThisFileDirectory)\avalonia.snk
+ True
+
+
+
+
diff --git a/build/SourceLink.props b/build/SourceLink.props
index 0c9b6a34f8..e27727c9e8 100644
--- a/build/SourceLink.props
+++ b/build/SourceLink.props
@@ -1,5 +1,5 @@
-
+
-
\ No newline at end of file
+
diff --git a/build/XUnit.props b/build/XUnit.props
index 079565d184..53f524b8e9 100644
--- a/build/XUnit.props
+++ b/build/XUnit.props
@@ -11,4 +11,8 @@
+
+ $(MSBuildThisFileDirectory)\avalonia.snk
+ True
+
diff --git a/build/avalonia.snk b/build/avalonia.snk
new file mode 100644
index 0000000000..10b49deb31
Binary files /dev/null and b/build/avalonia.snk differ
diff --git a/dirs.proj b/dirs.proj
index bf32abef72..594f2c22d3 100644
--- a/dirs.proj
+++ b/dirs.proj
@@ -21,6 +21,7 @@
+
diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm
index fb945a105e..67b1ea50a6 100644
--- a/native/Avalonia.Native/src/OSX/window.mm
+++ b/native/Avalonia.Native/src/OSX/window.mm
@@ -1338,6 +1338,12 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
}
_parent->BaseEvents->RunRenderPriorityJobs();
+
+ if (_parent == nullptr)
+ {
+ return;
+ }
+
_parent->BaseEvents->Paint();
}
diff --git a/nukebuild/BuildTasksPatcher.cs b/nukebuild/BuildTasksPatcher.cs
index 44f01da669..0b650ca7cc 100644
--- a/nukebuild/BuildTasksPatcher.cs
+++ b/nukebuild/BuildTasksPatcher.cs
@@ -29,7 +29,8 @@ public class BuildTasksPatcher
InputAssemblies = new[]
{
temp, typeof(Mono.Cecil.AssemblyDefinition).Assembly.GetModules()[0]
- .FullyQualifiedName
+ .FullyQualifiedName,
+ typeof(Mono.Cecil.Rocks.MethodBodyRocks).Assembly.GetModules()[0].FullyQualifiedName
},
SearchDirectories = new string[0],
OutputFile = output
diff --git a/nukebuild/_build.csproj b/nukebuild/_build.csproj
index b06e49f2eb..77cfb83427 100644
--- a/nukebuild/_build.csproj
+++ b/nukebuild/_build.csproj
@@ -16,7 +16,7 @@
-
+
diff --git a/packages/Avalonia/AvaloniaBuildTasks.targets b/packages/Avalonia/AvaloniaBuildTasks.targets
index 84a62bb5c0..612c368633 100644
--- a/packages/Avalonia/AvaloniaBuildTasks.targets
+++ b/packages/Avalonia/AvaloniaBuildTasks.targets
@@ -87,6 +87,9 @@
ProjectDirectory="$(MSBuildProjectDirectory)"
VerifyIl="$(AvaloniaXamlIlVerifyIl)"
ReportImportance="$(AvaloniaXamlReportImportance)"
+ AssemblyOriginatorKeyFile="$(AssemblyOriginatorKeyFile)"
+ SignAssembly="$(SignAssembly)"
+ DelaySign="$(DelaySign)"
/>
()
+ if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+ desktop.MainWindow = new MainWindow();
+ base.OnFrameworkInitializationCompleted();
+ }
+
+ public static int Main(string[] args)
+ => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
+
+ public static AppBuilder BuildAvaloniaApp()
+ => AppBuilder.Configure()
.UsePlatformDetect()
.UseReactiveUI()
- .LogToDebug()
- .Start();
- }
+ .LogToTrace();
}
}
diff --git a/samples/ControlCatalog.Desktop/Program.cs b/samples/ControlCatalog.Desktop/Program.cs
index b2df1953f5..5af646b180 100644
--- a/samples/ControlCatalog.Desktop/Program.cs
+++ b/samples/ControlCatalog.Desktop/Program.cs
@@ -10,19 +10,15 @@ namespace ControlCatalog
internal class Program
{
[STAThread]
- static void Main(string[] args)
- {
- // TODO: Make this work with GTK/Skia/Cairo depending on command-line args
- // again.
- BuildAvaloniaApp().Start();
- }
+ public static int Main(string[] args)
+ => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
///
/// This method is needed for IDE previewer infrastructure
///
public static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure()
- .LogToDebug()
+ .LogToTrace()
.UsePlatformDetect()
.UseReactiveUI();
diff --git a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj
index 28b0257eda..d5aedf7783 100644
--- a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj
+++ b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj
@@ -11,9 +11,8 @@
-
-
+
diff --git a/samples/ControlCatalog.NetCore/Program.cs b/samples/ControlCatalog.NetCore/Program.cs
index 142736a0bb..675ea2e10f 100644
--- a/samples/ControlCatalog.NetCore/Program.cs
+++ b/samples/ControlCatalog.NetCore/Program.cs
@@ -7,12 +7,11 @@ using System.Threading.Tasks;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.Dialogs;
using Avalonia.Headless;
using Avalonia.LogicalTree;
-using Avalonia.Skia;
using Avalonia.ReactiveUI;
using Avalonia.Threading;
-using Avalonia.Dialogs;
namespace ControlCatalog.NetCore
{
@@ -121,7 +120,7 @@ namespace ControlCatalog.NetCore
.UseSkia()
.UseReactiveUI()
.UseManagedSystemDialogs()
- .LogToDebug();
+ .LogToTrace();
static void SilenceConsole()
{
diff --git a/samples/Previewer/App.xaml.cs b/samples/Previewer/App.xaml.cs
index fffa987a27..ab83d45cd3 100644
--- a/samples/Previewer/App.xaml.cs
+++ b/samples/Previewer/App.xaml.cs
@@ -1,4 +1,5 @@
using Avalonia;
+using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
namespace Previewer
@@ -9,6 +10,13 @@ namespace Previewer
{
AvaloniaXamlLoader.Load(this);
}
+
+ public override void OnFrameworkInitializationCompleted()
+ {
+ if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+ desktop.MainWindow = new MainWindow();
+ base.OnFrameworkInitializationCompleted();
+ }
}
-}
\ No newline at end of file
+}
diff --git a/samples/Previewer/Program.cs b/samples/Previewer/Program.cs
index 48363e27f2..b12b93974a 100644
--- a/samples/Previewer/Program.cs
+++ b/samples/Previewer/Program.cs
@@ -1,13 +1,14 @@
-using System;
-using Avalonia;
+using Avalonia;
namespace Previewer
{
class Program
{
- static void Main(string[] args)
- {
- AppBuilder.Configure().UsePlatformDetect().Start();
- }
+ public static AppBuilder BuildAvaloniaApp()
+ => AppBuilder.Configure()
+ .UsePlatformDetect();
+
+ public static int Main(string[] args)
+ => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
}
-}
\ No newline at end of file
+}
diff --git a/samples/RenderDemo/App.xaml.cs b/samples/RenderDemo/App.xaml.cs
index 340ccdae19..e6ec963d75 100644
--- a/samples/RenderDemo/App.xaml.cs
+++ b/samples/RenderDemo/App.xaml.cs
@@ -1,4 +1,5 @@
using Avalonia;
+using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
@@ -11,9 +12,17 @@ namespace RenderDemo
AvaloniaXamlLoader.Load(this);
}
+ public override void OnFrameworkInitializationCompleted()
+ {
+ if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+ desktop.MainWindow = new MainWindow();
+ base.OnFrameworkInitializationCompleted();
+ }
+
// TODO: Make this work with GTK/Skia/Cairo depending on command-line args
// again.
- static void Main(string[] args) => BuildAvaloniaApp().Start();
+ static void Main(string[] args)
+ => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
// App configuration, used by the entry point and previewer
static AppBuilder BuildAvaloniaApp()
@@ -24,6 +33,6 @@ namespace RenderDemo
})
.UsePlatformDetect()
.UseReactiveUI()
- .LogToDebug();
+ .LogToTrace();
}
}
diff --git a/samples/RenderDemo/Pages/GlyphRunPage.xaml b/samples/RenderDemo/Pages/GlyphRunPage.xaml
index fb3e318a0e..c2914e8847 100644
--- a/samples/RenderDemo/Pages/GlyphRunPage.xaml
+++ b/samples/RenderDemo/Pages/GlyphRunPage.xaml
@@ -6,9 +6,9 @@
x:Class="RenderDemo.Pages.GlyphRunPage">
-
-
+
diff --git a/samples/RenderDemo/Pages/GlyphRunPage.xaml.cs b/samples/RenderDemo/Pages/GlyphRunPage.xaml.cs
index ddee880288..857358f6b2 100644
--- a/samples/RenderDemo/Pages/GlyphRunPage.xaml.cs
+++ b/samples/RenderDemo/Pages/GlyphRunPage.xaml.cs
@@ -9,7 +9,7 @@ namespace RenderDemo.Pages
{
public class GlyphRunPage : UserControl
{
- private DrawingPresenter _drawingPresenter;
+ private Image _imageControl;
private GlyphTypeface _glyphTypeface = Typeface.Default.GlyphTypeface;
private readonly Random _rand = new Random();
private ushort[] _glyphIndices = new ushort[1];
@@ -25,7 +25,8 @@ namespace RenderDemo.Pages
{
AvaloniaXamlLoader.Load(this);
- _drawingPresenter = this.FindControl("drawingPresenter");
+ _imageControl = this.FindControl("imageControl");
+ _imageControl.Source = new DrawingImage();
DispatcherTimer.Run(() =>
{
@@ -73,7 +74,7 @@ namespace RenderDemo.Pages
drawingGroup.Children.Add(geometryDrawing);
- _drawingPresenter.Drawing = drawingGroup;
+ (_imageControl.Source as DrawingImage).Drawing = drawingGroup;
}
}
}
diff --git a/samples/Sandbox/Program.cs b/samples/Sandbox/Program.cs
index 4d7eda8d9f..7d41a8b8c0 100644
--- a/samples/Sandbox/Program.cs
+++ b/samples/Sandbox/Program.cs
@@ -10,7 +10,7 @@ namespace Sandbox
AppBuilder.Configure()
.UsePlatformDetect()
.UseReactiveUI()
- .LogToDebug()
+ .LogToTrace()
.StartWithClassicDesktopLifetime(args);
}
}
diff --git a/samples/VirtualizationDemo/App.xaml.cs b/samples/VirtualizationDemo/App.xaml.cs
index 5990dd282c..81b80c1f40 100644
--- a/samples/VirtualizationDemo/App.xaml.cs
+++ b/samples/VirtualizationDemo/App.xaml.cs
@@ -1,4 +1,5 @@
using Avalonia;
+using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
namespace VirtualizationDemo
@@ -9,5 +10,12 @@ namespace VirtualizationDemo
{
AvaloniaXamlLoader.Load(this);
}
+
+ public override void OnFrameworkInitializationCompleted()
+ {
+ if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+ desktop.MainWindow = new MainWindow();
+ base.OnFrameworkInitializationCompleted();
+ }
}
}
diff --git a/samples/VirtualizationDemo/Program.cs b/samples/VirtualizationDemo/Program.cs
index 93ea5e1b88..46c05f74b2 100644
--- a/samples/VirtualizationDemo/Program.cs
+++ b/samples/VirtualizationDemo/Program.cs
@@ -1,19 +1,17 @@
-using System;
-using Avalonia;
-using Avalonia.Controls;
+using Avalonia;
using Avalonia.ReactiveUI;
namespace VirtualizationDemo
{
class Program
{
- static void Main(string[] args)
- {
- AppBuilder.Configure()
- .UsePlatformDetect()
- .UseReactiveUI()
- .LogToDebug()
- .Start();
- }
+ public static AppBuilder BuildAvaloniaApp()
+ => AppBuilder.Configure()
+ .UsePlatformDetect()
+ .UseReactiveUI()
+ .LogToTrace();
+
+ public static int Main(string[] args)
+ => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
}
}
diff --git a/samples/interop/Direct3DInteropSample/App.paml.cs b/samples/interop/Direct3DInteropSample/App.paml.cs
index 1b6d5fd39c..29365decfe 100644
--- a/samples/interop/Direct3DInteropSample/App.paml.cs
+++ b/samples/interop/Direct3DInteropSample/App.paml.cs
@@ -1,4 +1,5 @@
using Avalonia;
+using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
namespace Direct3DInteropSample
@@ -9,5 +10,12 @@ namespace Direct3DInteropSample
{
AvaloniaXamlLoader.Load(this);
}
+
+ public override void OnFrameworkInitializationCompleted()
+ {
+ if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+ desktop.MainWindow = new MainWindow();
+ base.OnFrameworkInitializationCompleted();
+ }
}
}
diff --git a/samples/interop/Direct3DInteropSample/Program.cs b/samples/interop/Direct3DInteropSample/Program.cs
index 21302fa68a..bf8e76d7e4 100644
--- a/samples/interop/Direct3DInteropSample/Program.cs
+++ b/samples/interop/Direct3DInteropSample/Program.cs
@@ -1,19 +1,16 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using Avalonia;
+using Avalonia;
namespace Direct3DInteropSample
{
class Program
{
- static void Main(string[] args)
- {
- AppBuilder.Configure()
- .With(new Win32PlatformOptions {UseDeferredRendering = false})
- .UseWin32().UseDirect2D1().Start();
- }
+ public static AppBuilder BuildAvaloniaApp()
+ => AppBuilder.Configure()
+ .With(new Win32PlatformOptions { UseDeferredRendering = false })
+ .UseWin32()
+ .UseDirect2D1();
+
+ public static int Main(string[] args)
+ => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
}
}
diff --git a/samples/interop/NativeEmbedSample/NativeEmbedSample.csproj b/samples/interop/NativeEmbedSample/NativeEmbedSample.csproj
index cc831ef8ae..c25442b52c 100644
--- a/samples/interop/NativeEmbedSample/NativeEmbedSample.csproj
+++ b/samples/interop/NativeEmbedSample/NativeEmbedSample.csproj
@@ -9,11 +9,10 @@
-
-
+
Designer
diff --git a/src/Avalonia.Animation/Properties/AssemblyInfo.cs b/src/Avalonia.Animation/Properties/AssemblyInfo.cs
index d34fd06272..de0b4a9971 100644
--- a/src/Avalonia.Animation/Properties/AssemblyInfo.cs
+++ b/src/Avalonia.Animation/Properties/AssemblyInfo.cs
@@ -6,5 +6,5 @@ using System.Runtime.CompilerServices;
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Animation.Easings")]
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Animation.Animators")]
-[assembly: InternalsVisibleTo("Avalonia.LeakTests")]
-[assembly: InternalsVisibleTo("Avalonia.Animation.UnitTests")]
+[assembly: InternalsVisibleTo("Avalonia.LeakTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")]
+[assembly: InternalsVisibleTo("Avalonia.Animation.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")]
diff --git a/src/Avalonia.Base/ApiCompatBaseline.txt b/src/Avalonia.Base/ApiCompatBaseline.txt
index 4668a572c5..5d19373a92 100644
--- a/src/Avalonia.Base/ApiCompatBaseline.txt
+++ b/src/Avalonia.Base/ApiCompatBaseline.txt
@@ -1,3 +1,4 @@
Compat issues with assembly Avalonia.Base:
CannotAddAbstractMembers : Member 'protected System.IObservable Avalonia.AvaloniaProperty.GetChanged()' is abstract in the implementation but is missing in the contract.
-Total Issues: 1
+TypesMustExist : Type 'Avalonia.Logging.DebugLogSink' does not exist in the implementation but it does exist in the contract.
+Total Issues: 2
diff --git a/src/Avalonia.Base/Logging/DebugLogSink.cs b/src/Avalonia.Base/Logging/TraceLogSink.cs
similarity index 92%
rename from src/Avalonia.Base/Logging/DebugLogSink.cs
rename to src/Avalonia.Base/Logging/TraceLogSink.cs
index 3695afa860..f597844378 100644
--- a/src/Avalonia.Base/Logging/DebugLogSink.cs
+++ b/src/Avalonia.Base/Logging/TraceLogSink.cs
@@ -6,12 +6,12 @@ using Avalonia.Utilities;
namespace Avalonia.Logging
{
- public class DebugLogSink : ILogSink
+ public class TraceLogSink : ILogSink
{
private readonly LogEventLevel _level;
private readonly IList _areas;
- public DebugLogSink(
+ public TraceLogSink(
LogEventLevel minimumLevel,
IList areas = null)
{
@@ -28,7 +28,7 @@ namespace Avalonia.Logging
{
if (IsEnabled(level, area))
{
- Debug.WriteLine(Format
diff --git a/src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs b/src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs
index 95e59dde2b..8e1f6c257d 100644
--- a/src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs
+++ b/src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs
@@ -39,7 +39,9 @@ namespace Avalonia.Build.Tasks
var res = XamlCompilerTaskExecutor.Compile(BuildEngine, input,
File.ReadAllLines(ReferencesFilePath).Where(l => !string.IsNullOrWhiteSpace(l)).ToArray(),
- ProjectDirectory, OutputPath, VerifyIl, outputImportance);
+ ProjectDirectory, OutputPath, VerifyIl, outputImportance,
+ (SignAssembly && !DelaySign) ? AssemblyOriginatorKeyFile : null
+ );
if (!res.Success)
return false;
if (!res.WrittenFile)
@@ -73,6 +75,10 @@ namespace Avalonia.Build.Tasks
public string OutputPath { get; set; }
public bool VerifyIl { get; set; }
+
+ public string AssemblyOriginatorKeyFile { get; set; }
+ public bool SignAssembly { get; set; }
+ public bool DelaySign { get; set; }
public string ReportImportance { get; set; }
diff --git a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
index c9c9c562bd..6b01af2ede 100644
--- a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
+++ b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
@@ -42,7 +42,7 @@ namespace Avalonia.Build.Tasks
}
public static CompileResult Compile(IBuildEngine engine, string input, string[] references, string projectDirectory,
- string output, bool verifyIl, MessageImportance logImportance)
+ string output, bool verifyIl, MessageImportance logImportance, string strongNameKey)
{
var typeSystem = new CecilTypeSystem(references.Concat(new[] {input}), input);
var asm = typeSystem.TargetAssemblyDefinition;
@@ -345,6 +345,20 @@ namespace Avalonia.Build.Tasks
}
res.Remove();
}
+
+
+ // Technically that's a hack, but it fixes corert incompatibility caused by deterministic builds
+ int dupeCounter = 1;
+ foreach (var grp in typeDef.NestedTypes.GroupBy(x => x.Name))
+ {
+ if (grp.Count() > 1)
+ {
+ foreach (var dupe in grp)
+ dupe.Name += "_dup" + dupeCounter++;
+ }
+ }
+
+
return true;
}
@@ -361,10 +375,12 @@ namespace Avalonia.Build.Tasks
loaderDispatcherMethod.Body.Instructions.Add(Instruction.Create(OpCodes.Ldnull));
loaderDispatcherMethod.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
- asm.Write(output, new WriterParameters
- {
- WriteSymbols = asm.MainModule.HasSymbols
- });
+
+ var writerParameters = new WriterParameters { WriteSymbols = asm.MainModule.HasSymbols };
+ if (!string.IsNullOrWhiteSpace(strongNameKey))
+ writerParameters.StrongNameKeyBlob = File.ReadAllBytes(strongNameKey);
+
+ asm.Write(output, writerParameters);
return new CompileResult(true, true);
}
diff --git a/src/Avalonia.Controls.DataGrid/DataGridColumn.cs b/src/Avalonia.Controls.DataGrid/DataGridColumn.cs
index 23c4acdf6c..92ddd4e736 100644
--- a/src/Avalonia.Controls.DataGrid/DataGridColumn.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGridColumn.cs
@@ -665,6 +665,7 @@ namespace Avalonia.Controls
///
/// The data item represented by the row that contains the intended cell.
///
+ /// When the method returns, contains the applied binding.
///
/// A new editing element that is bound to the column's property value.
///
diff --git a/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs b/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs
index 856d1f6566..7f8d205949 100644
--- a/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs
@@ -340,8 +340,6 @@ namespace Avalonia.Controls
if (OwningGrid != null && OwningGrid.ColumnHeaders != null)
{
- args.Pointer.Capture(this);
-
_dragMode = DragMode.MouseDown;
_frozenColumnsWidth = OwningGrid.ColumnsInternal.GetVisibleFrozenEdgedColumnsWidth();
_lastMousePositionHeaders = this.Translate(OwningGrid.ColumnHeaders, mousePosition);
@@ -413,8 +411,9 @@ namespace Avalonia.Controls
}
//TODO DragEvents
- internal void OnMouseMove(ref bool handled, Point mousePosition, Point mousePositionHeaders)
+ internal void OnMouseMove(PointerEventArgs args, Point mousePosition, Point mousePositionHeaders)
{
+ var handled = args.Handled;
if (handled || OwningGrid == null || OwningGrid.ColumnHeaders == null)
{
return;
@@ -438,7 +437,10 @@ namespace Avalonia.Controls
}
_lastMousePositionHeaders = mousePositionHeaders;
-
+
+ if (args.Pointer.Captured != this && _dragMode == DragMode.Drag)
+ args.Pointer.Capture(this);
+
SetDragCursor(mousePosition);
}
@@ -506,8 +508,7 @@ namespace Avalonia.Controls
Point mousePosition = e.GetPosition(this);
Point mousePositionHeaders = e.GetPosition(OwningGrid.ColumnHeaders);
- bool handled = false;
- OnMouseMove(ref handled, mousePosition, mousePositionHeaders);
+ OnMouseMove(e, mousePosition, mousePositionHeaders);
}
///
diff --git a/src/Avalonia.Controls.DataGrid/Properties/AssemblyInfo.cs b/src/Avalonia.Controls.DataGrid/Properties/AssemblyInfo.cs
index 82b01e99bb..72dd949421 100644
--- a/src/Avalonia.Controls.DataGrid/Properties/AssemblyInfo.cs
+++ b/src/Avalonia.Controls.DataGrid/Properties/AssemblyInfo.cs
@@ -2,8 +2,8 @@ using System.Reflection;
using System.Runtime.CompilerServices;
using Avalonia.Metadata;
-[assembly: InternalsVisibleTo("Avalonia.Controls.DataGrid.UnitTests")]
-[assembly: InternalsVisibleTo("Avalonia.DesignerSupport")]
+[assembly: InternalsVisibleTo("Avalonia.Controls.DataGrid.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")]
+[assembly: InternalsVisibleTo("Avalonia.DesignerSupport, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")]
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls")]
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.Collections")]
diff --git a/src/Avalonia.Controls/ApiCompatBaseline.txt b/src/Avalonia.Controls/ApiCompatBaseline.txt
index af88c569a6..16c801201c 100644
--- a/src/Avalonia.Controls/ApiCompatBaseline.txt
+++ b/src/Avalonia.Controls/ApiCompatBaseline.txt
@@ -8,6 +8,12 @@ MembersMustExist : Member 'public void Avalonia.Controls.ListBox.Selection.set(A
TypesMustExist : Type 'Avalonia.Controls.SelectionModel' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'Avalonia.Controls.SelectionModelChildrenRequestedEventArgs' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'Avalonia.Controls.SelectionModelSelectionChangedEventArgs' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public Avalonia.StyledProperty Avalonia.StyledProperty Avalonia.Controls.SplitView.ContentProperty' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public Avalonia.StyledProperty Avalonia.StyledProperty Avalonia.Controls.SplitView.PaneProperty' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public Avalonia.Controls.IControl Avalonia.Controls.SplitView.Content.get()' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public void Avalonia.Controls.SplitView.Content.set(Avalonia.Controls.IControl)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public Avalonia.Controls.IControl Avalonia.Controls.SplitView.Pane.get()' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public void Avalonia.Controls.SplitView.Pane.set(Avalonia.Controls.IControl)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.DirectProperty Avalonia.DirectProperty Avalonia.Controls.TreeView.SelectionProperty' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.Interactivity.RoutedEvent Avalonia.Interactivity.RoutedEvent Avalonia.Controls.TreeView.SelectionChangedEvent' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.Controls.ISelectionModel Avalonia.Controls.TreeView.Selection.get()' does not exist in the implementation but it does exist in the contract.
@@ -17,4 +23,4 @@ InterfacesShouldHaveSameMembers : Interface member 'public System.String[] Avalo
MembersMustExist : Member 'public Avalonia.DirectProperty Avalonia.DirectProperty Avalonia.Controls.Primitives.SelectingItemsControl.SelectionProperty' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'protected Avalonia.Controls.ISelectionModel Avalonia.Controls.Primitives.SelectingItemsControl.Selection.get()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'protected void Avalonia.Controls.Primitives.SelectingItemsControl.Selection.set(Avalonia.Controls.ISelectionModel)' does not exist in the implementation but it does exist in the contract.
-Total Issues: 18
+Total Issues: 24
diff --git a/src/Avalonia.Controls/ContextMenu.cs b/src/Avalonia.Controls/ContextMenu.cs
index c4df5c1815..ec9649bc64 100644
--- a/src/Avalonia.Controls/ContextMenu.cs
+++ b/src/Avalonia.Controls/ContextMenu.cs
@@ -62,6 +62,12 @@ namespace Avalonia.Controls
public static readonly StyledProperty PlacementRectProperty =
AvaloniaProperty.Register(nameof(PlacementRect));
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty WindowManagerAddShadowHintProperty =
+ Popup.WindowManagerAddShadowHintProperty.AddOwner();
+
///
/// Defines the property.
///
@@ -158,6 +164,12 @@ namespace Avalonia.Controls
set { SetValue(PlacementModeProperty, value); }
}
+ public bool WindowManagerAddShadowHint
+ {
+ get { return GetValue(WindowManagerAddShadowHintProperty); }
+ set { SetValue(WindowManagerAddShadowHintProperty, value); }
+ }
+
///
/// Gets or sets the the anchor rectangle within the parent that the context menu will be placed
/// relative to when is .
@@ -267,6 +279,7 @@ namespace Avalonia.Controls
PlacementTarget = PlacementTarget ?? control,
IsLightDismissEnabled = true,
OverlayDismissEventPassThrough = true,
+ WindowManagerAddShadowHint = WindowManagerAddShadowHint,
};
_popup.Opened += PopupOpened;
diff --git a/src/Avalonia.Controls/ItemsControl.cs b/src/Avalonia.Controls/ItemsControl.cs
index 3aec06e4eb..4dc8aec6f3 100644
--- a/src/Avalonia.Controls/ItemsControl.cs
+++ b/src/Avalonia.Controls/ItemsControl.cs
@@ -169,7 +169,7 @@ namespace Avalonia.Controls
///
/// The collection.
/// The index.
- /// The index of the item or -1 if the item was not found.
+ /// The item at the given index or null if the index is out of bounds.
protected static object ElementAt(IEnumerable items, int index)
{
if (index != -1 && index < items.Count())
diff --git a/src/Avalonia.Controls/LoggingExtensions.cs b/src/Avalonia.Controls/LoggingExtensions.cs
index 44e570bdfa..9eb3b140f6 100644
--- a/src/Avalonia.Controls/LoggingExtensions.cs
+++ b/src/Avalonia.Controls/LoggingExtensions.cs
@@ -1,25 +1,36 @@
-using Avalonia.Controls;
+using System;
+using Avalonia.Controls;
using Avalonia.Logging;
namespace Avalonia
{
public static class LoggingExtensions
{
+ [Obsolete("Use LogToTrace")]
+ public static T LogToDebug(
+ this T builder,
+ LogEventLevel level = LogEventLevel.Warning,
+ params string[] areas)
+ where T : AppBuilderBase, new()
+ {
+ return LogToTrace(builder, level, areas);
+ }
+
///
- /// Logs Avalonia events to the sink.
+ /// Logs Avalonia events to the sink.
///
/// The application class type.
/// The app builder instance.
/// The minimum level to log.
/// The areas to log. Valid values are listed in .
/// The app builder instance.
- public static T LogToDebug(
+ public static T LogToTrace(
this T builder,
LogEventLevel level = LogEventLevel.Warning,
params string[] areas)
where T : AppBuilderBase, new()
{
- Logger.Sink = new DebugLogSink(level, areas);
+ Logger.Sink = new TraceLogSink(level, areas);
return builder;
}
}
diff --git a/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs b/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs
index bdc68bee7e..7d50ef7d33 100644
--- a/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs
+++ b/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs
@@ -512,6 +512,13 @@ namespace Avalonia.Controls.Presenters
var generator = Owner.ItemContainerGenerator;
var newOffset = -1.0;
+ //better not trigger any container generation/recycle while or layout stuff
+ //before panel is attached/visible
+ if (!panel.IsAttachedToVisualTree)
+ {
+ return null;
+ }
+
if (!panel.IsMeasureValid && panel.PreviousMeasure.HasValue)
{
//before any kind of scrolling we need to make sure panel measure is valid
diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs
index 6a6d37605d..078d8050bf 100644
--- a/src/Avalonia.Controls/Presenters/TextPresenter.cs
+++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs
@@ -4,6 +4,7 @@ using Avalonia.Media;
using Avalonia.Metadata;
using Avalonia.Threading;
using Avalonia.VisualTree;
+using Avalonia.Layout;
namespace Avalonia.Controls.Presenters
{
@@ -312,7 +313,24 @@ namespace Avalonia.Controls.Presenters
context.FillRectangle(background, new Rect(Bounds.Size));
}
- context.DrawText(Foreground, new Point(), FormattedText);
+ double top = 0;
+ var textSize = FormattedText.Bounds.Size;
+
+ if (Bounds.Height < textSize.Height)
+ {
+ switch (VerticalAlignment)
+ {
+ case VerticalAlignment.Center:
+ top += (Bounds.Height - textSize.Height) / 2;
+ break;
+
+ case VerticalAlignment.Bottom:
+ top += (Bounds.Height - textSize.Height);
+ break;
+ }
+ }
+
+ context.DrawText(Foreground, new Point(0, top), FormattedText);
}
public override void Render(DrawingContext context)
diff --git a/src/Avalonia.Controls/Primitives/IPopupHost.cs b/src/Avalonia.Controls/Primitives/IPopupHost.cs
index 82a49c4189..ab81fe869e 100644
--- a/src/Avalonia.Controls/Primitives/IPopupHost.cs
+++ b/src/Avalonia.Controls/Primitives/IPopupHost.cs
@@ -47,6 +47,7 @@ namespace Avalonia.Controls.Primitives
/// The offset, in device-independent pixels.
/// The anchor point.
/// The popup gravity.
+ /// Defines how a popup position will be adjusted if the unadjusted position would result in the popup being partly constrained.
///
/// The anchor rect. If null, the bounds of will be used.
///
diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
index 4317d795f1..280f46be9f 100644
--- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
+++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
@@ -354,19 +354,15 @@ namespace Avalonia.Controls.Primitives
///
/// The control that raised the event.
/// The container or null if the event did not originate in a container.
- protected IControl? GetContainerFromEventSource(IInteractive eventSource)
+ protected IControl? GetContainerFromEventSource(IInteractive? eventSource)
{
- var parent = (IVisual)eventSource;
-
- while (parent != null)
+ for (var current = eventSource as IVisual; current != null; current = current.VisualParent)
{
- if (parent is IControl control && control.LogicalParent == this
- && ItemContainerGenerator?.IndexFromContainer(control) != -1)
+ if (current is IControl control && control.LogicalParent == this &&
+ ItemContainerGenerator?.IndexFromContainer(control) != -1)
{
return control;
}
-
- parent = parent.VisualParent;
}
return null;
@@ -670,7 +666,7 @@ namespace Avalonia.Controls.Primitives
/// false.
///
protected bool UpdateSelectionFromEventSource(
- IInteractive eventSource,
+ IInteractive? eventSource,
bool select = true,
bool rangeModifier = false,
bool toggleModifier = false,
@@ -794,18 +790,13 @@ namespace Avalonia.Controls.Primitives
/// The event.
private void ContainerSelectionChanged(RoutedEventArgs e)
{
- if (!_ignoreContainerSelectionChanged)
+ if (!_ignoreContainerSelectionChanged &&
+ e.Source is IControl control &&
+ e.Source is ISelectable selectable &&
+ control.LogicalParent == this &&
+ ItemContainerGenerator?.IndexFromContainer(control) != -1)
{
- var control = e.Source as IControl;
- var selectable = e.Source as ISelectable;
-
- if (control != null &&
- selectable != null &&
- control.LogicalParent == this &&
- ItemContainerGenerator?.IndexFromContainer(control) != -1)
- {
- UpdateSelection(control, selectable.IsSelected);
- }
+ UpdateSelection(control, selectable.IsSelected);
}
if (e.Source != this)
@@ -824,12 +815,11 @@ namespace Avalonia.Controls.Primitives
{
try
{
- var selectable = container as ISelectable;
bool result;
_ignoreContainerSelectionChanged = true;
- if (selectable != null)
+ if (container is ISelectable selectable)
{
result = selectable.IsSelected;
selectable.IsSelected = selected;
diff --git a/src/Avalonia.Controls/Properties/AssemblyInfo.cs b/src/Avalonia.Controls/Properties/AssemblyInfo.cs
index 672fbe294e..5e18115ac8 100644
--- a/src/Avalonia.Controls/Properties/AssemblyInfo.cs
+++ b/src/Avalonia.Controls/Properties/AssemblyInfo.cs
@@ -2,8 +2,8 @@ using System.Reflection;
using System.Runtime.CompilerServices;
using Avalonia.Metadata;
-[assembly: InternalsVisibleTo("Avalonia.Controls.UnitTests")]
-[assembly: InternalsVisibleTo("Avalonia.DesignerSupport")]
+[assembly: InternalsVisibleTo("Avalonia.Controls.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")]
+[assembly: InternalsVisibleTo("Avalonia.DesignerSupport, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")]
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia")]
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls")]
diff --git a/src/Avalonia.Controls/Shapes/Shape.cs b/src/Avalonia.Controls/Shapes/Shape.cs
index 7d1525afc4..0b7595ec9a 100644
--- a/src/Avalonia.Controls/Shapes/Shape.cs
+++ b/src/Avalonia.Controls/Shapes/Shape.cs
@@ -62,7 +62,6 @@ namespace Avalonia.Controls.Shapes
private Matrix _transform = Matrix.Identity;
private Geometry? _definingGeometry;
private Geometry? _renderedGeometry;
- private bool _calculateTransformOnArrange;
static Shape()
{
@@ -248,52 +247,21 @@ namespace Avalonia.Controls.Shapes
protected override Size MeasureOverride(Size availableSize)
{
- bool deferCalculateTransform;
- switch (Stretch)
+ if (DefiningGeometry is null)
{
- case Stretch.Fill:
- case Stretch.UniformToFill:
- deferCalculateTransform = double.IsInfinity(availableSize.Width) || double.IsInfinity(availableSize.Height);
- break;
- case Stretch.Uniform:
- deferCalculateTransform = double.IsInfinity(availableSize.Width) && double.IsInfinity(availableSize.Height);
- break;
- case Stretch.None:
- default:
- deferCalculateTransform = false;
- break;
+ return default;
}
- if (deferCalculateTransform)
- {
- _calculateTransformOnArrange = true;
- return DefiningGeometry?.Bounds.Size ?? Size.Empty;
- }
- else
- {
- _calculateTransformOnArrange = false;
- return CalculateShapeSizeAndSetTransform(availableSize);
- }
+ return CalculateSizeAndTransform(availableSize, DefiningGeometry.Bounds, Stretch).size;
}
protected override Size ArrangeOverride(Size finalSize)
- {
- if (_calculateTransformOnArrange)
- {
- _calculateTransformOnArrange = false;
- CalculateShapeSizeAndSetTransform(finalSize);
- }
-
- return finalSize;
- }
-
- private Size CalculateShapeSizeAndSetTransform(Size availableSize)
{
if (DefiningGeometry != null)
{
// This should probably use GetRenderBounds(strokeThickness) but then the calculations
// will multiply the stroke thickness as well, which isn't correct.
- var (size, transform) = CalculateSizeAndTransform(availableSize, DefiningGeometry.Bounds, Stretch);
+ var (_, transform) = CalculateSizeAndTransform(finalSize, DefiningGeometry.Bounds, Stretch);
if (_transform != transform)
{
@@ -301,13 +269,13 @@ namespace Avalonia.Controls.Shapes
_renderedGeometry = null;
}
- return size;
+ return finalSize;
}
return Size.Empty;
}
- internal static (Size, Matrix) CalculateSizeAndTransform(Size availableSize, Rect shapeBounds, Stretch Stretch)
+ internal static (Size size, Matrix transform) CalculateSizeAndTransform(Size availableSize, Rect shapeBounds, Stretch Stretch)
{
Size shapeSize = new Size(shapeBounds.Right, shapeBounds.Bottom);
Matrix translate = Matrix.Identity;
diff --git a/src/Avalonia.Controls/SplitView.cs b/src/Avalonia.Controls/SplitView.cs
index 8267efc466..4500d52484 100644
--- a/src/Avalonia.Controls/SplitView.cs
+++ b/src/Avalonia.Controls/SplitView.cs
@@ -9,6 +9,8 @@ using Avalonia.Platform;
using Avalonia.VisualTree;
using System;
using System.Reactive.Disposables;
+using Avalonia.Controls.Presenters;
+using Avalonia.Controls.Templates;
namespace Avalonia.Controls
{
@@ -78,7 +80,7 @@ namespace Avalonia.Controls
[PseudoClasses(":compactoverlay", ":compactinline", ":overlay", ":inline")]
[PseudoClasses(":left", ":right")]
[PseudoClasses(":lightdismiss")]
- public class SplitView : TemplatedControl
+ public class SplitView : ContentControl
{
/*
Pseudo classes & combos
@@ -87,12 +89,6 @@ namespace Avalonia.Controls
:left :right
*/
- ///
- /// Defines the property
- ///
- public static readonly StyledProperty ContentProperty =
- AvaloniaProperty.Register(nameof(Content));
-
///
/// Defines the property
///
@@ -133,8 +129,14 @@ namespace Avalonia.Controls
///
/// Defines the property
///
- public static readonly StyledProperty PaneProperty =
- AvaloniaProperty.Register(nameof(Pane));
+ public static readonly StyledProperty PaneProperty =
+ AvaloniaProperty.Register(nameof(Pane));
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty PaneTemplateProperty =
+ AvaloniaProperty.Register(nameof(PaneTemplate));
///
/// Defines the property
@@ -166,16 +168,7 @@ namespace Avalonia.Controls
CompactPaneLengthProperty.Changed.AddClassHandler((x, v) => x.OnCompactPaneLengthChanged(v));
PanePlacementProperty.Changed.AddClassHandler((x, v) => x.OnPanePlacementChanged(v));
DisplayModeProperty.Changed.AddClassHandler((x, v) => x.OnDisplayModeChanged(v));
- }
-
- ///
- /// Gets or sets the content of the SplitView
- ///
- [Content]
- public IControl Content
- {
- get => GetValue(ContentProperty);
- set => SetValue(ContentProperty, value);
+
}
///
@@ -265,11 +258,20 @@ namespace Avalonia.Controls
///
/// Gets or sets the Pane for the SplitView
///
- public IControl Pane
+ public object Pane
{
get => GetValue(PaneProperty);
set => SetValue(PaneProperty, value);
}
+
+ ///
+ /// Gets or sets the data template used to display the header content of the control.
+ ///
+ public IDataTemplate? PaneTemplate
+ {
+ get => GetValue(PaneTemplateProperty);
+ set => SetValue(PaneTemplateProperty, value);
+ }
///
/// Gets or sets whether WinUI equivalent LightDismissOverlayMode is enabled
@@ -312,6 +314,18 @@ namespace Avalonia.Controls
///
public event EventHandler PaneOpening;
+ protected override bool RegisterContentPresenter(IContentPresenter presenter)
+ {
+ var result = base.RegisterContentPresenter(presenter);
+
+ if (presenter.Name == "PART_PanePresenter")
+ {
+ return true;
+ }
+
+ return result;
+ }
+
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs
index d61519e697..31517ba59d 100644
--- a/src/Avalonia.Controls/TextBlock.cs
+++ b/src/Avalonia.Controls/TextBlock.cs
@@ -3,6 +3,7 @@ using Avalonia.LogicalTree;
using Avalonia.Media;
using Avalonia.Media.TextFormatting;
using Avalonia.Metadata;
+using Avalonia.Layout;
namespace Avalonia.Controls
{
@@ -427,14 +428,32 @@ namespace Avalonia.Controls
case TextAlignment.Center:
offsetX = (width - TextLayout.Size.Width) / 2;
break;
+
case TextAlignment.Right:
- offsetX = width - TextLayout.Size.Width;
+ offsetX = width - TextLayout.Size.Width;
break;
}
var padding = Padding;
- using (context.PushPostTransform(Matrix.CreateTranslation(padding.Left + offsetX, padding.Top)))
+ var top = padding.Top;
+ var textSize = TextLayout.Size;
+
+ if (Bounds.Height < textSize.Height)
+ {
+ switch (VerticalAlignment)
+ {
+ case VerticalAlignment.Center:
+ top += (Bounds.Height - textSize.Height) / 2;
+ break;
+
+ case VerticalAlignment.Bottom:
+ top += (Bounds.Height - textSize.Height);
+ break;
+ }
+ }
+
+ using (context.PushPostTransform(Matrix.CreateTranslation(padding.Left + offsetX, top)))
{
TextLayout.Draw(context);
}
diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs
index 317b6d3f2e..54574a7e1c 100644
--- a/src/Avalonia.Controls/Window.cs
+++ b/src/Avalonia.Controls/Window.cs
@@ -451,7 +451,7 @@ namespace Avalonia.Controls
///
/// The dialog result.
///
- /// When the window is shown with the
+ /// When the window is shown with the
/// or method, the
/// resulting task will produce the value when the window
/// is closed.
diff --git a/src/Avalonia.Dialogs/ApiCompatBaseline.txt b/src/Avalonia.Dialogs/ApiCompatBaseline.txt
new file mode 100644
index 0000000000..fcc74cf864
--- /dev/null
+++ b/src/Avalonia.Dialogs/ApiCompatBaseline.txt
@@ -0,0 +1 @@
+Total Issues: 0
diff --git a/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj b/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj
index 260d7350d2..81af76dc20 100644
--- a/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj
+++ b/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj
@@ -6,9 +6,7 @@
+
-
-
-
diff --git a/src/Avalonia.Input/KeyGesture.cs b/src/Avalonia.Input/KeyGesture.cs
index aa6fcc8bff..e155666631 100644
--- a/src/Avalonia.Input/KeyGesture.cs
+++ b/src/Avalonia.Input/KeyGesture.cs
@@ -144,7 +144,10 @@ namespace Avalonia.Input
return s.ToString();
}
- public bool Matches(KeyEventArgs keyEvent) => ResolveNumPadOperationKey(keyEvent.Key) == Key && keyEvent.KeyModifiers == KeyModifiers;
+ public bool Matches(KeyEventArgs keyEvent) =>
+ keyEvent != null &&
+ keyEvent.KeyModifiers == KeyModifiers &&
+ ResolveNumPadOperationKey(keyEvent.Key) == ResolveNumPadOperationKey(Key);
// TODO: Move that to external key parser
private static Key ParseKey(string key)
diff --git a/src/Avalonia.Input/MouseDevice.cs b/src/Avalonia.Input/MouseDevice.cs
index cec5029c18..5c63546f5d 100644
--- a/src/Avalonia.Input/MouseDevice.cs
+++ b/src/Avalonia.Input/MouseDevice.cs
@@ -19,6 +19,7 @@ namespace Avalonia.Input
private readonly Pointer _pointer;
private bool _disposed;
+ private PixelPoint? _position;
public MouseDevice(Pointer? pointer = null)
{
@@ -39,10 +40,11 @@ namespace Avalonia.Input
///
/// Gets the mouse position, in screen coordinates.
///
+ [Obsolete("Use events instead")]
public PixelPoint Position
{
- get;
- protected set;
+ get => _position ?? new PixelPoint(-1, -1);
+ protected set => _position = value;
}
///
@@ -91,7 +93,16 @@ namespace Avalonia.Input
public void SceneInvalidated(IInputRoot root, Rect rect)
{
- var clientPoint = root.PointToClient(Position);
+ // Pointer is outside of the target area
+ if (_position == null )
+ {
+ if (root.PointerOverElement != null)
+ ClearPointerOver(this, 0, root, PointerPointProperties.None, KeyModifiers.None);
+ return;
+ }
+
+
+ var clientPoint = root.PointToClient(_position.Value);
if (rect.Contains(clientPoint))
{
@@ -132,7 +143,7 @@ namespace Avalonia.Input
if(mouse._disposed)
return;
- Position = e.Root.PointToScreen(e.Position);
+ _position = e.Root.PointToScreen(e.Position);
var props = CreateProperties(e);
var keyModifiers = KeyModifiersUtils.ConvertToKey(e.InputModifiers);
switch (e.Type)
@@ -176,6 +187,7 @@ namespace Avalonia.Input
device = device ?? throw new ArgumentNullException(nameof(device));
root = root ?? throw new ArgumentNullException(nameof(root));
+ _position = null;
ClearPointerOver(this, timestamp, root, properties, inputModifiers);
}
diff --git a/src/Avalonia.Layout/Properties/AssemblyInfo.cs b/src/Avalonia.Layout/Properties/AssemblyInfo.cs
index 0a6a32493a..efcbf184b5 100644
--- a/src/Avalonia.Layout/Properties/AssemblyInfo.cs
+++ b/src/Avalonia.Layout/Properties/AssemblyInfo.cs
@@ -1,7 +1,7 @@
using System.Runtime.CompilerServices;
using Avalonia.Metadata;
-[assembly: InternalsVisibleTo("Avalonia.Layout.UnitTests")]
+[assembly: InternalsVisibleTo("Avalonia.Layout.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")]
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Layout")]
diff --git a/src/Avalonia.Native/WindowImplBase.cs b/src/Avalonia.Native/WindowImplBase.cs
index 5d35c773d7..824f62aee5 100644
--- a/src/Avalonia.Native/WindowImplBase.cs
+++ b/src/Avalonia.Native/WindowImplBase.cs
@@ -251,6 +251,9 @@ namespace Avalonia.Native
public bool RawTextInputEvent(uint timeStamp, string text)
{
+ if (_inputRoot is null)
+ return false;
+
Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1);
var args = new RawTextInputEventArgs(_keyboard, timeStamp, _inputRoot, text);
@@ -262,6 +265,9 @@ namespace Avalonia.Native
public bool RawKeyEvent(AvnRawKeyEventType type, uint timeStamp, AvnInputModifiers modifiers, uint key)
{
+ if (_inputRoot is null)
+ return false;
+
Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1);
var args = new RawKeyEventArgs(_keyboard, timeStamp, _inputRoot, (RawKeyEventType)type, (Key)key, (RawInputModifiers)modifiers);
@@ -278,6 +284,9 @@ namespace Avalonia.Native
public void RawMouseEvent(AvnRawMouseEventType type, uint timeStamp, AvnInputModifiers modifiers, AvnPoint point, AvnVector delta)
{
+ if (_inputRoot is null)
+ return;
+
Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1);
switch (type)
diff --git a/src/Avalonia.OpenGL/Angle/AngleEglInterface.cs b/src/Avalonia.OpenGL/Angle/AngleEglInterface.cs
index 8c9b028164..3a332f37ad 100644
--- a/src/Avalonia.OpenGL/Angle/AngleEglInterface.cs
+++ b/src/Avalonia.OpenGL/Angle/AngleEglInterface.cs
@@ -1,15 +1,13 @@
using System;
using System.Runtime.InteropServices;
using Avalonia.OpenGL.Egl;
-using Avalonia.Platform;
-using Avalonia.Platform.Interop;
namespace Avalonia.OpenGL.Angle
{
public class AngleEglInterface : EglInterface
{
- [DllImport("libegl.dll", CharSet = CharSet.Ansi)]
- static extern IntPtr eglGetProcAddress(string proc);
+ [DllImport("av_libGLESv2.dll", CharSet = CharSet.Ansi)]
+ static extern IntPtr EGL_GetProcAddress(string proc);
public AngleEglInterface() : base(LoadAngle())
{
@@ -20,14 +18,14 @@ namespace Avalonia.OpenGL.Angle
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
- var disp = eglGetProcAddress("eglGetPlatformDisplayEXT");
+ var disp = EGL_GetProcAddress("eglGetPlatformDisplayEXT");
if (disp == IntPtr.Zero)
{
throw new OpenGlException("libegl.dll doesn't have eglGetPlatformDisplayEXT entry point");
}
- return eglGetProcAddress;
+ return EGL_GetProcAddress;
}
throw new PlatformNotSupportedException();
diff --git a/src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs b/src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs
index 191fb53204..94e7728d68 100644
--- a/src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs
+++ b/src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs
@@ -82,7 +82,14 @@ namespace Avalonia.OpenGL.Angle
{
if (PlatformApi != AngleOptions.PlatformApi.DirectX11)
throw new InvalidOperationException("Current platform API is " + PlatformApi);
- return egl.CreatePBufferFromClientBuffer(EGL_D3D_TEXTURE_ANGLE, handle, new[] { EGL_NONE, EGL_NONE });
+ return egl.CreatePBufferFromClientBuffer(EGL_D3D_TEXTURE_ANGLE, handle, new[] { EGL_NONE, EGL_NONE });
+ }
+
+ public EglSurface WrapDirect3D11Texture(EglPlatformOpenGlInterface egl, IntPtr handle, int offsetX, int offsetY, int width, int height)
+ {
+ if (PlatformApi != AngleOptions.PlatformApi.DirectX11)
+ throw new InvalidOperationException("Current platform API is " + PlatformApi);
+ return egl.CreatePBufferFromClientBuffer(EGL_D3D_TEXTURE_ANGLE, handle, new[] { EGL_WIDTH, width, EGL_HEIGHT, height, EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE, EGL_TRUE, EGL_TEXTURE_OFFSET_X_ANGLE, offsetX, EGL_TEXTURE_OFFSET_Y_ANGLE, offsetY, EGL_NONE });
}
}
}
diff --git a/src/Avalonia.OpenGL/Egl/EglConsts.cs b/src/Avalonia.OpenGL/Egl/EglConsts.cs
index 58f5f1cef5..530d3401f6 100644
--- a/src/Avalonia.OpenGL/Egl/EglConsts.cs
+++ b/src/Avalonia.OpenGL/Egl/EglConsts.cs
@@ -205,5 +205,11 @@ namespace Avalonia.OpenGL.Egl
public const int EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE = 0x3200;
public const int EGL_D3D_TEXTURE_ANGLE = 0x33A3;
+
+
+ public const int EGL_TEXTURE_OFFSET_X_ANGLE = 0x3490;
+ public const int EGL_TEXTURE_OFFSET_Y_ANGLE = 0x3491;
+
+ public const int EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE = 0x33A6;
}
}
diff --git a/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj b/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj
index d01a495108..3d7bd2548a 100644
--- a/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj
+++ b/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj
@@ -2,6 +2,7 @@
netstandard2.0
Avalonia.ReactiveUI
+ True
diff --git a/src/Avalonia.Styling/Properties/AssemblyInfo.cs b/src/Avalonia.Styling/Properties/AssemblyInfo.cs
index b231f3b406..f6b69d8150 100644
--- a/src/Avalonia.Styling/Properties/AssemblyInfo.cs
+++ b/src/Avalonia.Styling/Properties/AssemblyInfo.cs
@@ -5,4 +5,4 @@ using Avalonia.Metadata;
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls")]
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.LogicalTree")]
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Styling")]
-[assembly: InternalsVisibleTo("Avalonia.Styling.UnitTests")]
\ No newline at end of file
+[assembly: InternalsVisibleTo("Avalonia.Styling.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")]
diff --git a/src/Avalonia.Themes.Default/SplitView.xaml b/src/Avalonia.Themes.Default/SplitView.xaml
index 2fb59cdbc4..18da3edb46 100644
--- a/src/Avalonia.Themes.Default/SplitView.xaml
+++ b/src/Avalonia.Themes.Default/SplitView.xaml
@@ -32,12 +32,12 @@
ClipToBounds="True"
HorizontalAlignment="Left"
ZIndex="100">
-
+
-
+
@@ -106,14 +106,14 @@
ClipToBounds="True"
HorizontalAlignment="Right"
ZIndex="100">
-
+
-
+
diff --git a/src/Avalonia.Themes.Fluent/SplitView.xaml b/src/Avalonia.Themes.Fluent/SplitView.xaml
index 71e92459f1..7d6f7a9b9a 100644
--- a/src/Avalonia.Themes.Fluent/SplitView.xaml
+++ b/src/Avalonia.Themes.Fluent/SplitView.xaml
@@ -32,12 +32,12 @@
ClipToBounds="True"
HorizontalAlignment="Left"
ZIndex="100">
-
+
-
+
@@ -106,14 +106,14 @@
ClipToBounds="True"
HorizontalAlignment="Right"
ZIndex="100">
-
+
-
+
diff --git a/src/Avalonia.Visuals/ApiCompatBaseline.txt b/src/Avalonia.Visuals/ApiCompatBaseline.txt
index 7fb44152c9..2d00d82a46 100644
--- a/src/Avalonia.Visuals/ApiCompatBaseline.txt
+++ b/src/Avalonia.Visuals/ApiCompatBaseline.txt
@@ -2,6 +2,8 @@ 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.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 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 Avalonia.Rect Avalonia.Media.Geometry.GetRenderBounds(Avalonia.Media.Pen)' does not exist in the implementation but it does exist in the contract.
+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.StyledProperty 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.
@@ -34,4 +36,4 @@ InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalon
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.Rendering.RenderLayer.Bitmap.get()' does not exist in the implementation but it does exist in the contract.
-Total Issues: 35
+Total Issues: 37
diff --git a/src/Avalonia.Visuals/Media/Drawing.cs b/src/Avalonia.Visuals/Media/Drawing.cs
index 6bc808e407..66d05e7e48 100644
--- a/src/Avalonia.Visuals/Media/Drawing.cs
+++ b/src/Avalonia.Visuals/Media/Drawing.cs
@@ -1,11 +1,19 @@
-using Avalonia.Platform;
-
-namespace Avalonia.Media
+namespace Avalonia.Media
{
+ ///
+ /// Abstract class that describes a 2-D drawing.
+ ///
public abstract class Drawing : AvaloniaObject
{
+ ///
+ /// Draws this drawing to the given .
+ ///
+ /// The drawing context.
public abstract void Draw(DrawingContext context);
+ ///
+ /// Gets the drawing's bounding rectangle.
+ ///
public abstract Rect GetBounds();
}
}
diff --git a/src/Avalonia.Visuals/Media/Geometry.cs b/src/Avalonia.Visuals/Media/Geometry.cs
index 33aafb58fc..ccef6665a9 100644
--- a/src/Avalonia.Visuals/Media/Geometry.cs
+++ b/src/Avalonia.Visuals/Media/Geometry.cs
@@ -84,7 +84,7 @@ namespace Avalonia.Media
///
/// The stroke thickness.
/// The bounding rectangle.
- public Rect GetRenderBounds(Pen pen) => PlatformImpl?.GetRenderBounds(pen) ?? Rect.Empty;
+ public Rect GetRenderBounds(IPen pen) => PlatformImpl?.GetRenderBounds(pen) ?? Rect.Empty;
///
/// Indicates whether the geometry's fill contains the specified point.
@@ -102,7 +102,7 @@ namespace Avalonia.Media
/// The pen to use.
/// The point.
/// true if the geometry contains the point; otherwise, false.
- public bool StrokeContains(Pen pen, Point point)
+ public bool StrokeContains(IPen pen, Point point)
{
return PlatformImpl?.StrokeContains(pen, point) == true;
}
diff --git a/src/Avalonia.Visuals/Media/GeometryDrawing.cs b/src/Avalonia.Visuals/Media/GeometryDrawing.cs
index 4df3aa8ae2..e46e3a7d5f 100644
--- a/src/Avalonia.Visuals/Media/GeometryDrawing.cs
+++ b/src/Avalonia.Visuals/Media/GeometryDrawing.cs
@@ -1,12 +1,38 @@
-using Avalonia.Metadata;
+using Avalonia.Media.Immutable;
+using Avalonia.Metadata;
namespace Avalonia.Media
{
+ ///
+ /// Represents a drawing operation that combines
+ /// a geometry with and brush and/or pen to produce rendered content.
+ ///
public class GeometryDrawing : Drawing
{
+ // Adding the Pen's stroke thickness here could yield wrong results due to transforms.
+ private static readonly IPen s_boundsPen = new ImmutablePen(Colors.Black.ToUint32(), 0);
+
+ ///
+ /// Defines the property.
+ ///
public static readonly StyledProperty GeometryProperty =
AvaloniaProperty.Register(nameof(Geometry));
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty BrushProperty =
+ AvaloniaProperty.Register(nameof(Brush), Brushes.Transparent);
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty PenProperty =
+ AvaloniaProperty.Register(nameof(Pen));
+
+ ///
+ /// Gets or sets the that describes the shape of this .
+ ///
[Content]
public Geometry Geometry
{
@@ -14,18 +40,18 @@ namespace Avalonia.Media
set => SetValue(GeometryProperty, value);
}
- public static readonly StyledProperty BrushProperty =
- AvaloniaProperty.Register(nameof(Brush), Brushes.Transparent);
-
+ ///
+ /// Gets or sets the used to fill the interior of the shape described by this .
+ ///
public IBrush Brush
{
get => GetValue(BrushProperty);
set => SetValue(BrushProperty, value);
}
- public static readonly StyledProperty PenProperty =
- AvaloniaProperty.Register(nameof(Pen));
-
+ ///
+ /// Gets or sets the used to stroke this .
+ ///
public IPen Pen
{
get => GetValue(PenProperty);
@@ -42,9 +68,7 @@ namespace Avalonia.Media
public override Rect GetBounds()
{
- // adding the Pen's stroke thickness here could yield wrong results due to transforms
- var pen = new Pen(Brushes.Black, 0);
- return Geometry?.GetRenderBounds(pen) ?? new Rect();
+ return Geometry?.GetRenderBounds(s_boundsPen) ?? Rect.Empty;
}
}
}
diff --git a/src/Avalonia.Visuals/Media/Imaging/Bitmap.cs b/src/Avalonia.Visuals/Media/Imaging/Bitmap.cs
index ca303211cd..5a0c57b333 100644
--- a/src/Avalonia.Visuals/Media/Imaging/Bitmap.cs
+++ b/src/Avalonia.Visuals/Media/Imaging/Bitmap.cs
@@ -99,7 +99,6 @@ namespace Avalonia.Media.Imaging
/// Initializes a new instance of the class.
///
/// The pixel format.
- /// The alpha format.
/// The pointer to the source bytes.
/// The size of the bitmap in device pixels.
/// The DPI of the bitmap.
diff --git a/src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs b/src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs
index cfe2cf979a..d6e88a7507 100644
--- a/src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs
+++ b/src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs
@@ -69,7 +69,7 @@ namespace Avalonia.Platform
/// If the pen is null, then no stoke is performed. If both the pen and the brush are null, then the drawing is not visible.
///
void DrawRectangle(IBrush brush, IPen pen, RoundedRect rect,
- BoxShadows boxShadow = default);
+ BoxShadows boxShadows = default);
///
/// Draws text.
diff --git a/src/Avalonia.Visuals/Properties/AssemblyInfo.cs b/src/Avalonia.Visuals/Properties/AssemblyInfo.cs
index 5d802c27b9..5bb9ae78b0 100644
--- a/src/Avalonia.Visuals/Properties/AssemblyInfo.cs
+++ b/src/Avalonia.Visuals/Properties/AssemblyInfo.cs
@@ -2,13 +2,13 @@ using System.Reflection;
using System.Runtime.CompilerServices;
using Avalonia.Metadata;
-[assembly: InternalsVisibleTo("Avalonia.Visuals.UnitTests")]
+[assembly: InternalsVisibleTo("Avalonia.Visuals.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")]
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Animation")]
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Media")]
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Media.Imaging")]
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Media.Transformation")]
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia")]
-[assembly: InternalsVisibleTo("Avalonia.Direct2D1.RenderTests")]
-[assembly: InternalsVisibleTo("Avalonia.Skia.RenderTests")]
-[assembly: InternalsVisibleTo("Avalonia.Skia.UnitTests")]
+[assembly: InternalsVisibleTo("Avalonia.Direct2D1.RenderTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")]
+[assembly: InternalsVisibleTo("Avalonia.Skia.RenderTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")]
+[assembly: InternalsVisibleTo("Avalonia.Skia.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")]
diff --git a/src/Avalonia.Visuals/Rendering/RenderLayer.cs b/src/Avalonia.Visuals/Rendering/RenderLayer.cs
index 7a79e45716..768e832fe5 100644
--- a/src/Avalonia.Visuals/Rendering/RenderLayer.cs
+++ b/src/Avalonia.Visuals/Rendering/RenderLayer.cs
@@ -30,12 +30,12 @@ namespace Avalonia.Rendering
{
if (Size != size || Scaling != scaling)
{
- Bitmap.Dispose();
var resized = RefCountable.Create(drawingContext.CreateLayer(size));
using (var context = resized.Item.CreateDrawingContext(null))
{
- context.Clear(Colors.Transparent);
+ Bitmap.Dispose();
+ context.Clear(default);
Bitmap = resized;
Scaling = scaling;
diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/ExperimentalAcrylicNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/ExperimentalAcrylicNode.cs
index 336d11e3fd..8bd079d070 100644
--- a/src/Avalonia.Visuals/Rendering/SceneGraph/ExperimentalAcrylicNode.cs
+++ b/src/Avalonia.Visuals/Rendering/SceneGraph/ExperimentalAcrylicNode.cs
@@ -13,10 +13,9 @@ namespace Avalonia.Rendering.SceneGraph
///
/// Initializes a new instance of the class.
///
- /// The transform.
+ /// The transform.
+ ///
/// The rectangle to draw.
- /// The box shadow parameters
- /// Child scenes for drawing visual brushes.
public ExperimentalAcrylicNode(
Matrix transform,
IExperimentalAcrylicMaterial material,
@@ -44,7 +43,7 @@ namespace Avalonia.Rendering.SceneGraph
/// Determines if this draw operation equals another.
///
/// The transform of the other draw operation.
- /// The fill of the other draw operation.
+ /// The fill of the other draw operation.
/// The rectangle of the other draw operation.
/// True if the draw operations are the same, otherwise false.
///
diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs
index 8a19679c77..508ca0ad18 100644
--- a/src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs
+++ b/src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs
@@ -63,7 +63,6 @@ namespace Avalonia.Rendering.SceneGraph
/// The fill of the other draw operation.
/// The stroke of the other draw operation.
/// The geometry of the other draw operation.
- /// The box shadow parameters
/// True if the draw operations are the same, otherwise false.
///
/// The properties of the other draw operation are passed in as arguments to prevent
diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/RectangleNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/RectangleNode.cs
index ec1a7753b1..d0c4566485 100644
--- a/src/Avalonia.Visuals/Rendering/SceneGraph/RectangleNode.cs
+++ b/src/Avalonia.Visuals/Rendering/SceneGraph/RectangleNode.cs
@@ -73,7 +73,7 @@ namespace Avalonia.Rendering.SceneGraph
/// The fill of the other draw operation.
/// The stroke of the other draw operation.
/// The rectangle of the other draw operation.
- /// The box shadow parameters of the other draw operation
+ /// The box shadow parameters of the other draw operation
/// True if the draw operations are the same, otherwise false.
///
/// The properties of the other draw operation are passed in as arguments to prevent
diff --git a/src/Avalonia.X11/XI2Manager.cs b/src/Avalonia.X11/XI2Manager.cs
index 742973e0da..b3a24e6c37 100644
--- a/src/Avalonia.X11/XI2Manager.cs
+++ b/src/Avalonia.X11/XI2Manager.cs
@@ -13,7 +13,10 @@ namespace Avalonia.X11
{
XiEventType.XI_Motion,
XiEventType.XI_ButtonPress,
- XiEventType.XI_ButtonRelease
+ XiEventType.XI_ButtonRelease,
+ XiEventType.XI_Leave,
+ XiEventType.XI_Enter,
+
};
private static readonly XiEventType[] MultiTouchEventTypes = new XiEventType[]
@@ -162,7 +165,9 @@ namespace Avalonia.X11
| XEventMask.Button4MotionMask
| XEventMask.Button5MotionMask
| XEventMask.ButtonPressMask
- | XEventMask.ButtonReleaseMask;
+ | XEventMask.ButtonReleaseMask
+ | XEventMask.LeaveWindowMask
+ | XEventMask.EnterWindowMask;
}
public void OnWindowDestroyed(IntPtr xid) => _clients.Remove(xid);
@@ -175,14 +180,39 @@ namespace Avalonia.X11
_pointerDevice.Update(changed->Classes, changed->NumClasses);
}
-
+
if ((xev->evtype >= XiEventType.XI_ButtonPress && xev->evtype <= XiEventType.XI_Motion)
- || (xev->evtype>=XiEventType.XI_TouchBegin&&xev->evtype<=XiEventType.XI_TouchEnd))
+ || (xev->evtype >= XiEventType.XI_TouchBegin && xev->evtype <= XiEventType.XI_TouchEnd))
{
var dev = (XIDeviceEvent*)xev;
if (_clients.TryGetValue(dev->EventWindow, out var client))
OnDeviceEvent(client, new ParsedDeviceEvent(dev));
}
+
+ if (xev->evtype == XiEventType.XI_Leave || xev->evtype == XiEventType.XI_Enter)
+ {
+ var rev = (XIEnterLeaveEvent*)xev;
+ if (_clients.TryGetValue(rev->EventWindow, out var client))
+ OnEnterLeaveEvent(client, ref *rev);
+ }
+ }
+
+ void OnEnterLeaveEvent(IXI2Client client, ref XIEnterLeaveEvent ev)
+ {
+ if (ev.evtype == XiEventType.XI_Leave)
+ {
+ var buttons = ParsedDeviceEvent.ParseButtonState(ev.buttons.MaskLen, ev.buttons.Mask);
+ var detail = ev.detail;
+ if ((detail == XiEnterLeaveDetail.XINotifyNonlinearVirtual ||
+ detail == XiEnterLeaveDetail.XINotifyNonlinear ||
+ detail == XiEnterLeaveDetail.XINotifyVirtual)
+ && buttons == default)
+ {
+ client.ScheduleXI2Input(new RawPointerEventArgs(client.MouseDevice, (ulong)ev.time.ToInt64(),
+ client.InputRoot,
+ RawPointerEventType.LeaveWindow, new Point(ev.event_x, ev.event_y), buttons));
+ }
+ }
}
void OnDeviceEvent(IXI2Client client, ParsedDeviceEvent ev)
@@ -284,6 +314,29 @@ namespace Avalonia.X11
public int Detail { get; set; }
public bool Emulated { get; set; }
public Dictionary Valuators { get; }
+
+ public static RawInputModifiers ParseButtonState(int len, byte* buttons)
+ {
+ RawInputModifiers rv = default;
+ if (len > 0)
+ {
+ if (XIMaskIsSet(buttons, 1))
+ rv |= RawInputModifiers.LeftMouseButton;
+ if (XIMaskIsSet(buttons, 2))
+ rv |= RawInputModifiers.MiddleMouseButton;
+ if (XIMaskIsSet(buttons, 3))
+ rv |= RawInputModifiers.RightMouseButton;
+ if (len > 1)
+ {
+ if (XIMaskIsSet(buttons, 8))
+ rv |= RawInputModifiers.XButton1MouseButton;
+ if (XIMaskIsSet(buttons, 9))
+ rv |= RawInputModifiers.XButton2MouseButton;
+ }
+ }
+ return rv;
+ }
+
public ParsedDeviceEvent(XIDeviceEvent* ev)
{
Type = ev->evtype;
@@ -298,27 +351,16 @@ namespace Avalonia.X11
if (state.HasFlag(XModifierMask.Mod4Mask))
Modifiers |= RawInputModifiers.Meta;
- if (ev->buttons.MaskLen > 0)
- {
- var buttons = ev->buttons.Mask;
- if (XIMaskIsSet(buttons, 1))
- Modifiers |= RawInputModifiers.LeftMouseButton;
- if (XIMaskIsSet(buttons, 2))
- Modifiers |= RawInputModifiers.MiddleMouseButton;
- if (XIMaskIsSet(buttons, 3))
- Modifiers |= RawInputModifiers.RightMouseButton;
- if (XIMaskIsSet(buttons, 8))
- Modifiers |= RawInputModifiers.XButton1MouseButton;
- if (XIMaskIsSet(buttons, 9))
- Modifiers |= RawInputModifiers.XButton2MouseButton;
- }
+ Modifiers = ParseButtonState(ev->buttons.MaskLen, ev->buttons.Mask);
Valuators = new Dictionary();
Position = new Point(ev->event_x, ev->event_y);
var values = ev->valuators.Values;
- for (var c = 0; c < ev->valuators.MaskLen * 8; c++)
- if (XIMaskIsSet(ev->valuators.Mask, c))
- Valuators[c] = *values++;
+ if(ev->valuators.Mask != null)
+ for (var c = 0; c < ev->valuators.MaskLen * 8; c++)
+ if (XIMaskIsSet(ev->valuators.Mask, c))
+ Valuators[c] = *values++;
+
if (Type == XiEventType.XI_ButtonPress || Type == XiEventType.XI_ButtonRelease)
Button = ev->detail;
Detail = ev->detail;
diff --git a/src/Avalonia.X11/XIStructs.cs b/src/Avalonia.X11/XIStructs.cs
index 4675ef47f2..f4581a99ba 100644
--- a/src/Avalonia.X11/XIStructs.cs
+++ b/src/Avalonia.X11/XIStructs.cs
@@ -236,6 +236,34 @@ namespace Avalonia.X11
public XIModifierState mods;
public XIModifierState group;
}
+
+ [StructLayout(LayoutKind.Sequential)]
+ unsafe struct XIEnterLeaveEvent
+ {
+ public XEventName type; /* GenericEvent */
+ public UIntPtr serial; /* # of last request processed by server */
+ public Bool send_event; /* true if this came from a SendEvent request */
+ public IntPtr display; /* Display the event was read from */
+ public int extension; /* XI extension offset */
+ public XiEventType evtype;
+ public IntPtr time;
+ public int deviceid;
+ public int sourceid;
+ public XiEnterLeaveDetail detail;
+ public IntPtr RootWindow;
+ public IntPtr EventWindow;
+ public IntPtr ChildWindow;
+ public double root_x;
+ public double root_y;
+ public double event_x;
+ public double event_y;
+ public int mode;
+ public int focus;
+ public int same_screen;
+ public XIButtonState buttons;
+ public XIModifierState mods;
+ public XIModifierState group;
+ }
[Flags]
public enum XiDeviceEventFlags : int
@@ -286,5 +314,18 @@ namespace Avalonia.X11
XI_BarrierLeave = 26,
XI_LASTEVENT = XI_BarrierLeave,
}
-
+
+ enum XiEnterLeaveDetail
+ {
+ XINotifyAncestor = 0,
+ XINotifyVirtual = 1,
+ XINotifyInferior = 2,
+ XINotifyNonlinear = 3,
+ XINotifyNonlinearVirtual = 4,
+ XINotifyPointer = 5,
+ XINotifyPointerRoot = 6,
+ XINotifyDetailNone = 7
+
+ }
+
}
diff --git a/src/Linux/Avalonia.LinuxFramebuffer/Output/Drm.cs b/src/Linux/Avalonia.LinuxFramebuffer/Output/Drm.cs
index 787a2e4cb8..9c476b1b63 100644
--- a/src/Linux/Avalonia.LinuxFramebuffer/Output/Drm.cs
+++ b/src/Linux/Avalonia.LinuxFramebuffer/Output/Drm.cs
@@ -113,22 +113,22 @@ namespace Avalonia.LinuxFramebuffer.Output
[StructLayout(LayoutKind.Sequential)]
public struct drmModeConnector {
public uint connector_id;
- public uint encoder_id; /**< Encoder currently connected to */
+ public uint encoder_id; // Encoder currently connected to
public uint connector_type;
public uint connector_type_id;
public DrmModeConnection connection;
- public uint mmWidth, mmHeight; /**< HxW in millimeters */
+ public uint mmWidth, mmHeight; // HxW in millimeters
public DrmModeSubPixel subpixel;
public int count_modes;
public drmModeModeInfo* modes;
public int count_props;
- public uint *props; /**< List of property ids */
- public ulong *prop_values; /**< List of property values */
+ public uint *props; // List of property ids
+ public ulong *prop_values; // List of property values
public int count_encoders;
- public uint *encoders; /**< List of encoder ids */
+ public uint *encoders; //List of encoder ids
}
[StructLayout(LayoutKind.Sequential)]
@@ -143,14 +143,14 @@ namespace Avalonia.LinuxFramebuffer.Output
[StructLayout(LayoutKind.Sequential)]
public struct drmModeCrtc {
public uint crtc_id;
- public uint buffer_id; /**< FB id to connect to 0 = disconnect */
+ public uint buffer_id; // FB id to connect to 0 = disconnect
- public uint x, y; /**< Position on the framebuffer */
+ public uint x, y; // Position on the framebuffer
public uint width, height;
public int mode_valid;
public drmModeModeInfo mode;
- public int gamma_size; /**< Number of gamma stops */
+ public int gamma_size; // Number of gamma stops
}
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaRuntimeXamlLoader.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaRuntimeXamlLoader.cs
index 4569970d01..4df07bcdd8 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaRuntimeXamlLoader.cs
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaRuntimeXamlLoader.cs
@@ -3,7 +3,6 @@ using System.IO;
using System.Reflection;
using System.Text;
using Avalonia.Markup.Xaml.XamlIl;
-// ReSharper disable CheckNamespace
namespace Avalonia.Markup.Xaml
{
@@ -13,10 +12,10 @@ namespace Avalonia.Markup.Xaml
/// Loads XAML from a string.
///
/// The string containing the XAML.
- /// Default assembly for clr-namespace:
- ///
- /// The optional instance into which the XAML should be loaded.
- ///
+ /// Default assembly for clr-namespace:.
+ /// The optional instance into which the XAML should be loaded.
+ /// The URI of the XAML being loaded.
+ /// Indicates whether the XAML is being loaded in design mode.
/// The loaded object.
public static object Load(string xaml, Assembly localAssembly = null, object rootInstance = null, Uri uri = null, bool designMode = false)
{
@@ -28,13 +27,35 @@ namespace Avalonia.Markup.Xaml
}
}
+ ///
+ /// Loads XAML from a stream.
+ ///
+ /// The stream containing the XAML.
+ /// Default assembly for clr-namespace:
+ /// The optional instance into which the XAML should be loaded.
+ /// The URI of the XAML being loaded.
+ /// Indicates whether the XAML is being loaded in design mode.
+ /// The loaded object.
public static object Load(Stream stream, Assembly localAssembly, object rootInstance = null, Uri uri = null,
bool designMode = false)
=> AvaloniaXamlIlRuntimeCompiler.Load(stream, localAssembly, rootInstance, uri, designMode);
+ ///
+ /// Parse XAML from a string.
+ ///
+ /// The string containing the XAML.
+ /// Default assembly for clr-namespace:.
+ /// The loaded object.
public static object Parse(string xaml, Assembly localAssembly = null)
=> Load(xaml, localAssembly);
+ ///
+ /// Parse XAML from a string.
+ ///
+ /// The type of the returned object.
+ /// >The string containing the XAML.
+ /// >Default assembly for clr-namespace:.
+ /// The loaded object.
public static T Parse(string xaml, Assembly localAssembly = null)
=> (T)Parse(xaml, localAssembly);
diff --git a/src/Markup/Avalonia.Markup.Xaml/Properties/AssemblyInfo.cs b/src/Markup/Avalonia.Markup.Xaml/Properties/AssemblyInfo.cs
index 7e4a095bdf..cee2b8a484 100644
--- a/src/Markup/Avalonia.Markup.Xaml/Properties/AssemblyInfo.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/Properties/AssemblyInfo.cs
@@ -5,5 +5,5 @@ using System.Runtime.CompilerServices;
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Markup.Xaml.MarkupExtensions")]
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Markup.Xaml.Styling")]
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Markup.Xaml.Templates")]
-[assembly: InternalsVisibleTo("Avalonia.Markup.Xaml.UnitTests")]
+[assembly: InternalsVisibleTo("Avalonia.Markup.Xaml.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")]
diff --git a/src/Markup/Avalonia.Markup/Properties/AssemblyInfo.cs b/src/Markup/Avalonia.Markup/Properties/AssemblyInfo.cs
index 72c1659a1e..3da708bc9c 100644
--- a/src/Markup/Avalonia.Markup/Properties/AssemblyInfo.cs
+++ b/src/Markup/Avalonia.Markup/Properties/AssemblyInfo.cs
@@ -4,4 +4,4 @@ using System.Runtime.CompilerServices;
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Data")]
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Markup.Data")]
-[assembly: InternalsVisibleTo("Avalonia.Markup.UnitTests")]
+[assembly: InternalsVisibleTo("Avalonia.Markup.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")]
diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs
index cbc896813f..a32b3327c2 100644
--- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs
+++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs
@@ -36,6 +36,7 @@ namespace Avalonia.Skia
private readonly SKPaint _fillPaint = new SKPaint();
private readonly SKPaint _boxShadowPaint = new SKPaint();
private static SKShader s_acrylicNoiseShader;
+ private readonly ISkiaGpuRenderSession _session;
///
/// Context create info.
@@ -76,6 +77,8 @@ namespace Avalonia.Skia
/// Skia GPU provider context (optional)
///
public ISkiaGpu Gpu;
+
+ public ISkiaGpuRenderSession CurrentSession;
}
///
@@ -96,6 +99,8 @@ namespace Avalonia.Skia
Surface = createInfo.Surface;
Canvas = createInfo.Canvas ?? createInfo.Surface?.Canvas;
+ _session = createInfo.CurrentSession;
+
if (Canvas == null)
{
throw new ArgumentException("Invalid create info - no Canvas provided", nameof(createInfo));
@@ -969,7 +974,8 @@ namespace Avalonia.Skia
Format = format,
DisableTextLcdRendering = !_canTextUseLcdRendering,
GrContext = _grContext,
- Gpu = _gpu
+ Gpu = _gpu,
+ Session = _session
};
return new SurfaceRenderTarget(createInfo);
diff --git a/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs b/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs
index 5571bd890d..ceccc481f9 100644
--- a/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs
+++ b/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs
@@ -1,6 +1,5 @@
using System;
using System.Runtime.InteropServices;
-using Avalonia.Media;
using Avalonia.Platform;
using HarfBuzzSharp;
using SkiaSharp;
@@ -24,40 +23,33 @@ namespace Avalonia.Skia
Font.SetFunctionsOpenType();
- Font.GetScale(out var xScale, out _);
+ DesignEmHeight = (short)Typeface.UnitsPerEm;
- DesignEmHeight = (short)xScale;
+ var metrics = Typeface.ToFont().Metrics;
- if (!Font.TryGetHorizontalFontExtents(out var fontExtents))
- {
- Font.TryGetVerticalFontExtents(out fontExtents);
- }
+ const double defaultFontRenderingEmSize = 12.0;
- Ascent = -fontExtents.Ascender;
+ Ascent = (int)(metrics.Ascent / defaultFontRenderingEmSize * Typeface.UnitsPerEm);
- Descent = -fontExtents.Descender;
+ Descent = (int)(metrics.Descent / defaultFontRenderingEmSize * Typeface.UnitsPerEm);
- LineGap = fontExtents.LineGap;
+ LineGap = (int)(metrics.Leading / defaultFontRenderingEmSize * Typeface.UnitsPerEm);
- if (Font.OpenTypeMetrics.TryGetPosition(OpenTypeMetricsTag.UnderlineOffset, out var underlinePosition))
- {
- UnderlinePosition = underlinePosition;
- }
+ UnderlinePosition = metrics.UnderlinePosition != null ?
+ (int)(metrics.UnderlinePosition / defaultFontRenderingEmSize * Typeface.UnitsPerEm) :
+ 0;
- if (Font.OpenTypeMetrics.TryGetPosition(OpenTypeMetricsTag.UnderlineSize, out var underlineThickness))
- {
- UnderlineThickness = underlineThickness;
- }
+ UnderlineThickness = metrics.UnderlineThickness != null ?
+ (int)(metrics.UnderlineThickness / defaultFontRenderingEmSize * Typeface.UnitsPerEm) :
+ 0;
- if (Font.OpenTypeMetrics.TryGetPosition(OpenTypeMetricsTag.StrikeoutOffset, out var strikethroughPosition))
- {
- StrikethroughPosition = strikethroughPosition;
- }
+ StrikethroughPosition = metrics.StrikeoutPosition != null ?
+ (int)(metrics.StrikeoutPosition / defaultFontRenderingEmSize * Typeface.UnitsPerEm) :
+ 0;
- if (Font.OpenTypeMetrics.TryGetPosition(OpenTypeMetricsTag.StrikeoutSize, out var strikethroughThickness))
- {
- StrikethroughThickness = strikethroughThickness;
- }
+ StrikethroughThickness = metrics.StrikeoutThickness != null ?
+ (int)(metrics.StrikeoutThickness / defaultFontRenderingEmSize * Typeface.UnitsPerEm) :
+ 0;
IsFixedPitch = Typeface.IsFixedPitch;
}
diff --git a/src/Skia/Avalonia.Skia/Gpu/ISkiaGpu.cs b/src/Skia/Avalonia.Skia/Gpu/ISkiaGpu.cs
index 869c261f1b..aa86df7c23 100644
--- a/src/Skia/Avalonia.Skia/Gpu/ISkiaGpu.cs
+++ b/src/Skia/Avalonia.Skia/Gpu/ISkiaGpu.cs
@@ -21,7 +21,7 @@ namespace Avalonia.Skia
/// Creates an offscreen render target surface
///
/// size in pixels
- ISkiaSurface TryCreateSurface(PixelSize size);
+ ISkiaSurface TryCreateSurface(PixelSize size, ISkiaGpuRenderSession session);
}
public interface ISkiaSurface : IDisposable
diff --git a/src/Skia/Avalonia.Skia/Gpu/ISkiaGpuRenderSession.cs b/src/Skia/Avalonia.Skia/Gpu/ISkiaGpuRenderSession.cs
index a4e2bfed52..5faa6a41ec 100644
--- a/src/Skia/Avalonia.Skia/Gpu/ISkiaGpuRenderSession.cs
+++ b/src/Skia/Avalonia.Skia/Gpu/ISkiaGpuRenderSession.cs
@@ -22,5 +22,7 @@ namespace Avalonia.Skia
/// Scaling factor.
///
double ScaleFactor { get; }
+
+ GRSurfaceOrigin SurfaceOrigin { get; }
}
}
diff --git a/src/Skia/Avalonia.Skia/Gpu/OpenGl/FboSkiaSurface.cs b/src/Skia/Avalonia.Skia/Gpu/OpenGl/FboSkiaSurface.cs
index 9ee8d698ba..8ab275df63 100644
--- a/src/Skia/Avalonia.Skia/Gpu/OpenGl/FboSkiaSurface.cs
+++ b/src/Skia/Avalonia.Skia/Gpu/OpenGl/FboSkiaSurface.cs
@@ -14,7 +14,7 @@ namespace Avalonia.Skia
private int _texture;
private static readonly bool[] TrueFalse = new[] { true, false };
- public FboSkiaSurface(GRContext grContext, IGlContext glContext, PixelSize pixelSize)
+ public FboSkiaSurface(GRContext grContext, IGlContext glContext, PixelSize pixelSize, GRSurfaceOrigin surfaceOrigin)
{
_grContext = grContext;
_glContext = glContext;
@@ -93,7 +93,7 @@ namespace Avalonia.Skia
var target = new GRBackendRenderTarget(pixelSize.Width, pixelSize.Height, 0, 8,
new GRGlFramebufferInfo((uint)_fbo, SKColorType.Rgba8888.ToGlSizedFormat()));
Surface = SKSurface.Create(_grContext, target,
- GRSurfaceOrigin.BottomLeft, SKColorType.Rgba8888);
+ surfaceOrigin, SKColorType.Rgba8888);
CanBlit = gl.BlitFramebuffer != null;
}
diff --git a/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlRenderTarget.cs b/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlRenderTarget.cs
index 6df8df9a4c..b4c5619c85 100644
--- a/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlRenderTarget.cs
+++ b/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlRenderTarget.cs
@@ -39,6 +39,8 @@ namespace Avalonia.Skia
_backendRenderTarget = backendRenderTarget;
_surface = surface;
_glSession = glSession;
+
+ SurfaceOrigin = glSession.IsYFlipped ? GRSurfaceOrigin.TopLeft : GRSurfaceOrigin.BottomLeft;
}
public void Dispose()
{
@@ -48,6 +50,8 @@ namespace Avalonia.Skia
GrContext.Flush();
_glSession.Dispose();
}
+
+ public GRSurfaceOrigin SurfaceOrigin { get; }
public GRContext GrContext { get; }
public SKSurface SkSurface => _surface;
@@ -73,10 +77,6 @@ namespace Avalonia.Skia
$"Can't create drawing context for surface with {size} size and {scaling} scaling");
}
- gl.Viewport(0, 0, size.Width, size.Height);
- gl.ClearStencil(0);
- gl.ClearColor(0, 0, 0, 0);
- gl.Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
lock (_grContext)
{
_grContext.ResetContext();
@@ -89,6 +89,7 @@ namespace Avalonia.Skia
SKColorType.Rgba8888);
success = true;
+
return new GlGpuSession(_grContext, renderTarget, surface, glSession);
}
}
diff --git a/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs b/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs
index c02d813e24..a393f0b64d 100644
--- a/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs
+++ b/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs
@@ -47,7 +47,7 @@ namespace Avalonia.Skia
return null;
}
- public ISkiaSurface TryCreateSurface(PixelSize size)
+ public ISkiaSurface TryCreateSurface(PixelSize size, ISkiaGpuRenderSession session)
{
// Only windows platform needs our FBO trickery
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
@@ -62,7 +62,7 @@ namespace Avalonia.Skia
return null;
try
{
- var surface = new FboSkiaSurface(_grContext, _glContext, size);
+ var surface = new FboSkiaSurface(_grContext, _glContext, size, session?.SurfaceOrigin ?? GRSurfaceOrigin.TopLeft);
_canCreateSurfaces = true;
return surface;
}
diff --git a/src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs b/src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs
index 9992c9ba8c..6626546c0c 100644
--- a/src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs
+++ b/src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs
@@ -33,7 +33,8 @@ namespace Avalonia.Skia
Dpi = SkiaPlatform.DefaultDpi * session.ScaleFactor,
VisualBrushRenderer = visualBrushRenderer,
DisableTextLcdRendering = true,
- Gpu = _skiaGpu
+ Gpu = _skiaGpu,
+ CurrentSession = session
};
return new DrawingContextImpl(nfo, session);
diff --git a/src/Skia/Avalonia.Skia/Properties/AssemblyInfo.cs b/src/Skia/Avalonia.Skia/Properties/AssemblyInfo.cs
index f6aabfae39..b923e3a9f6 100644
--- a/src/Skia/Avalonia.Skia/Properties/AssemblyInfo.cs
+++ b/src/Skia/Avalonia.Skia/Properties/AssemblyInfo.cs
@@ -1,4 +1,4 @@
using System.Runtime.CompilerServices;
-[assembly: InternalsVisibleTo("Avalonia.Skia.RenderTests")]
-[assembly: InternalsVisibleTo("Avalonia.Skia.UnitTests")]
+[assembly: InternalsVisibleTo("Avalonia.Skia.RenderTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")]
+[assembly: InternalsVisibleTo("Avalonia.Skia.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")]
diff --git a/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs b/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs
index 61b599a731..6347c64aed 100644
--- a/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs
+++ b/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs
@@ -51,7 +51,7 @@ namespace Avalonia.Skia
_grContext = createInfo.GrContext;
_gpu = createInfo.Gpu;
- _surface = _gpu?.TryCreateSurface(PixelSize);
+ _surface = _gpu?.TryCreateSurface(PixelSize, createInfo.Session);
if (_surface == null)
_surface = new SkiaSurfaceWrapper(CreateSurface(createInfo.GrContext, PixelSize.Width, PixelSize.Height,
createInfo.Format));
@@ -100,7 +100,7 @@ namespace Avalonia.Skia
VisualBrushRenderer = visualBrushRenderer,
DisableTextLcdRendering = _disableLcdRendering,
GrContext = _grContext,
- Gpu = _gpu
+ Gpu = _gpu,
};
return new DrawingContextImpl(createInfo, Disposable.Create(() => Version++));
@@ -218,6 +218,8 @@ namespace Avalonia.Skia
public GRContext GrContext;
public ISkiaGpu Gpu;
+
+ public ISkiaGpuRenderSession Session;
}
}
}
diff --git a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
index ae927d44a5..6ae27870e8 100644
--- a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
+++ b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
@@ -238,22 +238,46 @@ namespace Avalonia.Direct2D1
width = 0;
- for (var i = 0; i < glyphCount; i++)
+ var scale = (float)(glyphRun.FontRenderingEmSize / glyphTypeface.DesignEmHeight);
+
+ if (glyphRun.GlyphAdvances.IsEmpty)
+ {
+ for (var i = 0; i < glyphCount; i++)
+ {
+ var advance = glyphTypeface.GetGlyphAdvance(glyphRun.GlyphIndices[i]) * scale;
+
+ run.Advances[i] = advance;
+
+ width += advance;
+ }
+ }
+ else
+ {
+ for (var i = 0; i < glyphCount; i++)
+ {
+ var advance = (float)glyphRun.GlyphAdvances[i];
+
+ run.Advances[i] = advance;
+
+ width += advance;
+ }
+ }
+
+ if (glyphRun.GlyphOffsets.IsEmpty)
{
- run.Advances[i] = (float)glyphRun.GlyphAdvances[i];
- width += run.Advances[i];
+ return new GlyphRunImpl(run);
}
run.Offsets = new GlyphOffset[glyphCount];
for (var i = 0; i < glyphCount; i++)
{
- var offset = glyphRun.GlyphOffsets[i];
+ var (x, y) = glyphRun.GlyphOffsets[i];
run.Offsets[i] = new GlyphOffset
{
- AdvanceOffset = (float)offset.X,
- AscenderOffset = (float)offset.Y
+ AdvanceOffset = (float)x,
+ AscenderOffset = (float)y
};
}
diff --git a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs
index 59292d605c..ace658654d 100644
--- a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs
+++ b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs
@@ -323,7 +323,6 @@ namespace Avalonia.Direct2D1.Media
///
/// The foreground.
/// The glyph run.
- ///
public void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun)
{
using (var brush = CreateBrush(foreground, glyphRun.Size))
diff --git a/src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs
index 254b5684a4..20b09a9aac 100644
--- a/src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs
+++ b/src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs
@@ -1,6 +1,6 @@
-using System.Globalization;
+using System;
+using System.Globalization;
using Avalonia.Media;
-using Avalonia.Media.TextFormatting;
using Avalonia.Media.TextFormatting.Unicode;
using Avalonia.Platform;
using Avalonia.Utilities;
@@ -15,51 +15,9 @@ namespace Avalonia.Direct2D1.Media
{
using (var buffer = new Buffer())
{
- buffer.ContentType = ContentType.Unicode;
+ FillBuffer(buffer, text);
- var breakCharPosition = text.Length - 1;
-
- var codepoint = Codepoint.ReadAt(text, breakCharPosition, out var count);
-
- if (codepoint.IsBreakChar)
- {
- var breakCharCount = 1;
-
- if (text.Length > 1)
- {
- var previousCodepoint = Codepoint.ReadAt(text, breakCharPosition - count, out _);
-
- if (codepoint == '\r' && previousCodepoint == '\n'
- || codepoint == '\n' && previousCodepoint == '\r')
- {
- breakCharCount = 2;
- }
- }
-
- if (breakCharPosition != text.Start)
- {
- buffer.AddUtf16(text.Buffer.Span.Slice(0, text.Length - breakCharCount));
- }
-
- var cluster = buffer.GlyphInfos.Length > 0 ?
- buffer.GlyphInfos[buffer.Length - 1].Cluster + 1 :
- (uint)text.Start;
-
- switch (breakCharCount)
- {
- case 1:
- buffer.Add('\u200C', cluster);
- break;
- case 2:
- buffer.Add('\u200C', cluster);
- buffer.Add('\u200D', cluster);
- break;
- }
- }
- else
- {
- buffer.AddUtf16(text.Buffer.Span);
- }
+ buffer.Language = new Language(culture ?? CultureInfo.CurrentCulture);
buffer.GuessSegmentProperties();
@@ -67,44 +25,38 @@ namespace Avalonia.Direct2D1.Media
var font = ((GlyphTypefaceImpl)glyphTypeface.PlatformImpl).Font;
- buffer.Language = new Language(culture ?? CultureInfo.CurrentCulture);
-
font.Shape(buffer);
font.GetScale(out var scaleX, out _);
var textScale = fontRenderingEmSize / scaleX;
- var len = buffer.Length;
+ var bufferLength = buffer.Length;
- var info = buffer.GetGlyphInfoSpan();
+ var glyphInfos = buffer.GetGlyphInfoSpan();
- var pos = buffer.GetGlyphPositionSpan();
+ var glyphPositions = buffer.GetGlyphPositionSpan();
- var glyphIndices = new ushort[len];
+ var glyphIndices = new ushort[bufferLength];
- var clusters = new ushort[len];
+ var clusters = new ushort[bufferLength];
- var glyphAdvances = new double[len];
+ double[] glyphAdvances = null;
- var glyphOffsets = new Vector[len];
+ Vector[] glyphOffsets = null;
- for (var i = 0; i < len; i++)
+ for (var i = 0; i < bufferLength; i++)
{
- glyphIndices[i] = (ushort)info[i].Codepoint;
-
- clusters[i] = (ushort)(text.Start + info[i].Cluster);
-
- var advanceX = pos[i].XAdvance * textScale;
- // Depends on direction of layout
- //var advanceY = pos[i].YAdvance * textScale;
+ glyphIndices[i] = (ushort)glyphInfos[i].Codepoint;
- glyphAdvances[i] = advanceX;
+ clusters[i] = (ushort)glyphInfos[i].Cluster;
- var offsetX = pos[i].XOffset * textScale;
- var offsetY = pos[i].YOffset * textScale;
+ if (!glyphTypeface.IsFixedPitch)
+ {
+ SetAdvance(glyphPositions, i, textScale, ref glyphAdvances);
+ }
- glyphOffsets[i] = new Vector(offsetX, offsetY);
+ SetOffset(glyphPositions, i, textScale, ref glyphOffsets);
}
return new GlyphRun(glyphTypeface, fontRenderingEmSize,
@@ -115,5 +67,79 @@ namespace Avalonia.Direct2D1.Media
new ReadOnlySlice(clusters));
}
}
+
+ private static void FillBuffer(Buffer buffer, ReadOnlySlice text)
+ {
+ buffer.ContentType = ContentType.Unicode;
+
+ var i = 0;
+
+ while (i < text.Length)
+ {
+ var codepoint = Codepoint.ReadAt(text, i, out var count);
+
+ var cluster = (uint)(text.Start + i);
+
+ if (codepoint.IsBreakChar)
+ {
+ if (i + 1 < text.Length)
+ {
+ var nextCodepoint = Codepoint.ReadAt(text, i + 1, out _);
+
+ if (nextCodepoint == '\r' && codepoint == '\n' || nextCodepoint == '\n' && codepoint == '\r')
+ {
+ count++;
+
+ buffer.Add('\u200C', cluster);
+
+ buffer.Add('\u200D', cluster);
+ }
+ else
+ {
+ buffer.Add('\u200C', cluster);
+ }
+ }
+ else
+ {
+ buffer.Add('\u200C', cluster);
+ }
+ }
+ else
+ {
+ buffer.Add(codepoint, cluster);
+ }
+
+ i += count;
+ }
+ }
+
+ private static void SetOffset(ReadOnlySpan glyphPositions, int index, double textScale,
+ ref Vector[] offsetBuffer)
+ {
+ var position = glyphPositions[index];
+
+ if (position.XOffset == 0 && position.YOffset == 0)
+ {
+ return;
+ }
+
+ offsetBuffer ??= new Vector[glyphPositions.Length];
+
+ var offsetX = position.XOffset * textScale;
+
+ var offsetY = position.YOffset * textScale;
+
+ offsetBuffer[index] = new Vector(offsetX, offsetY);
+ }
+
+ private static void SetAdvance(ReadOnlySpan glyphPositions, int index, double textScale,
+ ref double[] advanceBuffer)
+ {
+ advanceBuffer ??= new double[glyphPositions.Length];
+
+ // Depends on direction of layout
+ // advanceBuffer[index] = buffer.GlyphPositions[index].YAdvance * textScale;
+ advanceBuffer[index] = glyphPositions[index].XAdvance * textScale;
+ }
}
}
diff --git a/src/Windows/Avalonia.Direct2D1/Properties/AssemblyInfo.cs b/src/Windows/Avalonia.Direct2D1/Properties/AssemblyInfo.cs
index 5d53fd4aae..78e7b4aad6 100644
--- a/src/Windows/Avalonia.Direct2D1/Properties/AssemblyInfo.cs
+++ b/src/Windows/Avalonia.Direct2D1/Properties/AssemblyInfo.cs
@@ -6,6 +6,6 @@ using Avalonia.Direct2D1;
[assembly: ExportRenderingSubsystem(OperatingSystemType.WinNT, 1, "Direct2D1", typeof(Direct2D1Platform), nameof(Direct2D1Platform.Initialize),
typeof(Direct2DChecker))]
-[assembly: InternalsVisibleTo("Avalonia.Direct2D1.RenderTests")]
-[assembly: InternalsVisibleTo("Avalonia.Direct2D1.UnitTests")]
+[assembly: InternalsVisibleTo("Avalonia.Direct2D1.RenderTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")]
+[assembly: InternalsVisibleTo("Avalonia.Direct2D1.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")]
diff --git a/src/Windows/Avalonia.Win32.Interop/Avalonia.Win32.Interop.csproj b/src/Windows/Avalonia.Win32.Interop/Avalonia.Win32.Interop.csproj
index 0d0dd98a77..0e11ee3b92 100644
--- a/src/Windows/Avalonia.Win32.Interop/Avalonia.Win32.Interop.csproj
+++ b/src/Windows/Avalonia.Win32.Interop/Avalonia.Win32.Interop.csproj
@@ -2,7 +2,7 @@
net461
true
- true
+ true
true
true
Avalonia.Win32.Interoperability
diff --git a/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj b/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj
index 7fc24d4d3d..5889d919df 100644
--- a/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj
+++ b/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj
@@ -6,7 +6,8 @@
-
+
+
diff --git a/src/Windows/Avalonia.Win32/Composition/CompositionBlurHost.cs b/src/Windows/Avalonia.Win32/Composition/CompositionBlurHost.cs
new file mode 100644
index 0000000000..4c090e797c
--- /dev/null
+++ b/src/Windows/Avalonia.Win32/Composition/CompositionBlurHost.cs
@@ -0,0 +1,18 @@
+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;
+ }
+ }
+}
+
diff --git a/src/Windows/Avalonia.Win32/Composition/CompositionConnector.cs b/src/Windows/Avalonia.Win32/Composition/CompositionConnector.cs
new file mode 100644
index 0000000000..fc18b623bf
--- /dev/null
+++ b/src/Windows/Avalonia.Win32/Composition/CompositionConnector.cs
@@ -0,0 +1,143 @@
+using System;
+using System.Runtime.InteropServices;
+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 CompositionConnector(EglPlatformOpenGlInterface egl)
+ {
+ EnsureDispatcherQueue();
+
+ if (_dispatcherQueueController != null)
+ _compositor = new Compositor();
+
+ var interop = _compositor.As();
+
+ 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();
+
+ 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 blurEffectFactory = _compositor.CreateEffectFactory(blurEffect);
+
+ var blurBrush = blurEffectFactory.CreateBrush();
+ var backDropBrush = _compositor.CreateBackdropBrush();
+
+ blurBrush.SetSourceParameter("backdrop", backDropBrush);
+
+ 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();
+
+ 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);
+ }
+ }
+ }
+}
+
diff --git a/src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs b/src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs
new file mode 100644
index 0000000000..441da93787
--- /dev/null
+++ b/src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs
@@ -0,0 +1,96 @@
+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);
+ }
+ }
+ }
+}
+
diff --git a/src/Windows/Avalonia.Win32/Composition/D2DEffects.cs b/src/Windows/Avalonia.Win32/Composition/D2DEffects.cs
new file mode 100644
index 0000000000..1c761ee082
--- /dev/null
+++ b/src/Windows/Avalonia.Win32/Composition/D2DEffects.cs
@@ -0,0 +1,91 @@
+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);
+ }
+}
+
diff --git a/src/Windows/Avalonia.Win32/Composition/EffectBase.cs b/src/Windows/Avalonia.Win32/Composition/EffectBase.cs
new file mode 100644
index 0000000000..ca5b15971e
--- /dev/null
+++ b/src/Windows/Avalonia.Win32/Composition/EffectBase.cs
@@ -0,0 +1,45 @@
+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);
+ }
+}
+
diff --git a/src/Windows/Avalonia.Win32/Composition/GRAPHICS_EFFECT_PROPERTY_MAPPING.cs b/src/Windows/Avalonia.Win32/Composition/GRAPHICS_EFFECT_PROPERTY_MAPPING.cs
new file mode 100644
index 0000000000..f5d5fc4ad3
--- /dev/null
+++ b/src/Windows/Avalonia.Win32/Composition/GRAPHICS_EFFECT_PROPERTY_MAPPING.cs
@@ -0,0 +1,18 @@
+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
+ };
+}
+
diff --git a/src/Windows/Avalonia.Win32/Composition/GaussianBlurEffect.cs b/src/Windows/Avalonia.Win32/Composition/GaussianBlurEffect.cs
new file mode 100644
index 0000000000..342e68eeb4
--- /dev/null
+++ b/src/Windows/Avalonia.Win32/Composition/GaussianBlurEffect.cs
@@ -0,0 +1,57 @@
+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;
+ }
+ }
+}
+
diff --git a/src/Windows/Avalonia.Win32/Composition/IBlurHost.cs b/src/Windows/Avalonia.Win32/Composition/IBlurHost.cs
new file mode 100644
index 0000000000..6ab470d81c
--- /dev/null
+++ b/src/Windows/Avalonia.Win32/Composition/IBlurHost.cs
@@ -0,0 +1,8 @@
+namespace Avalonia.Win32
+{
+ internal interface IBlurHost
+ {
+ void SetBlur(bool enabled);
+ }
+}
+
diff --git a/src/Windows/Avalonia.Win32/Composition/ICompositionDrawingSurfaceInterop.cs b/src/Windows/Avalonia.Win32/Composition/ICompositionDrawingSurfaceInterop.cs
new file mode 100644
index 0000000000..2eac796376
--- /dev/null
+++ b/src/Windows/Avalonia.Win32/Composition/ICompositionDrawingSurfaceInterop.cs
@@ -0,0 +1,152 @@
+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());
+ 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 FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr);
+
+ public static implicit operator ICompositionDrawingSurfaceInterop(IObjectReference obj) => (obj != null) ? new ICompositionDrawingSurfaceInterop(obj) : null;
+ protected readonly ObjectReference _obj;
+ public IObjectReference ObjRef { get => _obj; }
+ public IntPtr ThisPtr => _obj.ThisPtr;
+ public ObjectReference AsInterface() => _obj.As();
+ public A As() => _obj.AsType();
+
+ public ICompositionDrawingSurfaceInterop(IObjectReference obj) : this(obj.As()) { }
+ internal ICompositionDrawingSurfaceInterop(ObjectReference 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();
+ }
+ }
+}
+
diff --git a/src/Windows/Avalonia.Win32/Composition/ICompositorDesktopInterop.cs b/src/Windows/Avalonia.Win32/Composition/ICompositorDesktopInterop.cs
new file mode 100644
index 0000000000..1d4cd3450f
--- /dev/null
+++ b/src/Windows/Avalonia.Win32/Composition/ICompositorDesktopInterop.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Runtime.InteropServices;
+using WinRT;
+
+namespace Windows.UI.Composition.Desktop
+{
+ [WindowsRuntimeType]
+ [Guid("29E691FA-4567-4DCA-B319-D0F207EB6807")]
+ public interface ICompositorDesktopInterop
+ {
+ void CreateDesktopWindowTarget(IntPtr hwndTarget, bool isTopmost, out IntPtr test);
+ }
+}
+
diff --git a/src/Windows/Avalonia.Win32/Composition/ICompositorDesktopInterop1.cs b/src/Windows/Avalonia.Win32/Composition/ICompositorDesktopInterop1.cs
new file mode 100644
index 0000000000..1c3f06d679
--- /dev/null
+++ b/src/Windows/Avalonia.Win32/Composition/ICompositorDesktopInterop1.cs
@@ -0,0 +1,69 @@
+using WinRT;
+
+namespace ABI.Windows.UI.Composition.Desktop
+{
+ using global::System;
+ using global::System.Runtime.InteropServices;
+
+ [Guid("29E691FA-4567-4DCA-B319-D0F207EB6807")]
+ internal class ICompositorDesktopInterop : global::Windows.UI.Composition.Desktop.ICompositorDesktopInterop
+
+ {
+ [Guid("29E691FA-4567-4DCA-B319-D0F207EB6807")]
+ public struct Vftbl
+ {
+ public delegate int _CreateDesktopWindowTarget(IntPtr thisPtr, IntPtr hwndTarget, byte isTopMost, out IntPtr desktopWindowTarget);
+
+ internal global::WinRT.Interop.IUnknownVftbl IUnknownVftbl;
+ public _CreateDesktopWindowTarget CreateDesktopWindowTarget;
+
+
+ public static readonly Vftbl AbiToProjectionVftable;
+ public static readonly IntPtr AbiToProjectionVftablePtr;
+
+ static Vftbl()
+ {
+ AbiToProjectionVftable = new Vftbl
+ {
+ IUnknownVftbl = global::WinRT.Interop.IUnknownVftbl.AbiToProjectionVftbl,
+ CreateDesktopWindowTarget = Do_Abi_Create_Desktop_Window_Target
+ };
+ AbiToProjectionVftablePtr = Marshal.AllocHGlobal(Marshal.SizeOf());
+ Marshal.StructureToPtr(AbiToProjectionVftable, AbiToProjectionVftablePtr, false);
+ }
+
+ private static int Do_Abi_Create_Desktop_Window_Target(IntPtr thisPtr, IntPtr hwndTarget, byte isTopMost, out IntPtr desktopWindowTarget)
+ {
+ try
+ {
+ ComWrappersSupport.FindObject(thisPtr).CreateDesktopWindowTarget(hwndTarget, isTopMost != 0, out desktopWindowTarget);
+ return 0;
+ }
+ catch (Exception ex)
+ {
+ desktopWindowTarget = IntPtr.Zero;
+ return Marshal.GetHRForException(ex);
+ }
+ }
+ }
+ internal static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr);
+
+ public static implicit operator ICompositorDesktopInterop(IObjectReference obj) => (obj != null) ? new ICompositorDesktopInterop(obj) : null;
+ protected readonly ObjectReference _obj;
+ public IObjectReference ObjRef { get => _obj; }
+ public IntPtr ThisPtr => _obj.ThisPtr;
+ public ObjectReference AsInterface() => _obj.As();
+ public A As() => _obj.AsType();
+ public ICompositorDesktopInterop(IObjectReference obj) : this(obj.As()) { }
+ internal ICompositorDesktopInterop(ObjectReference obj)
+ {
+ _obj = obj;
+ }
+
+ public void CreateDesktopWindowTarget(IntPtr hwndTarget, bool isTopmost, out IntPtr test)
+ {
+ Marshal.ThrowExceptionForHR(_obj.Vftbl.CreateDesktopWindowTarget(ThisPtr, hwndTarget, isTopmost ? (byte)1 : (byte)0, out test));
+ }
+ }
+}
+
diff --git a/src/Windows/Avalonia.Win32/Composition/ICompositorInterop.cs b/src/Windows/Avalonia.Win32/Composition/ICompositorInterop.cs
new file mode 100644
index 0000000000..d9b25e497e
--- /dev/null
+++ b/src/Windows/Avalonia.Win32/Composition/ICompositorInterop.cs
@@ -0,0 +1,139 @@
+using System;
+using System.Runtime.InteropServices;
+using WinRT;
+
+namespace Windows.UI.Composition.Interop
+{
+ [WindowsRuntimeType]
+ [Guid("25297D5C-3AD4-4C9C-B5CF-E36A38512330")]
+ public interface ICompositorInterop
+ {
+ ICompositionSurface CreateCompositionSurfaceForHandle(IntPtr swapChain);
+
+ ICompositionSurface CreateCompositionSurfaceForSwapChain(IntPtr swapChain);
+
+ CompositionGraphicsDevice CreateGraphicsDevice(IntPtr renderingDevice);
+ }
+}
+
+namespace ABI.Windows.UI.Composition.Interop
+{
+ using global::System;
+ using global::System.Runtime.InteropServices;
+ using global::Windows.UI.Composition;
+
+ [Guid("25297D5C-3AD4-4C9C-B5CF-E36A38512330")]
+ internal class ICompositorInterop : global::Windows.UI.Composition.Interop.ICompositorInterop
+
+ {
+ [Guid("25297D5C-3AD4-4C9C-B5CF-E36A38512330")]
+ public struct Vftbl
+ {
+ public delegate int _CreateCompositionSurfaceForHandle(IntPtr ThisPtr, IntPtr swapChain, out IntPtr result);
+ public delegate int _CreateCompositionSurfaceForSwapChain(IntPtr ThisPtr, IntPtr swapChain, out IntPtr result);
+ public delegate int _CreateGraphicsDevice(IntPtr ThisPtr, IntPtr renderingDevice, out IntPtr result);
+
+ internal global::WinRT.Interop.IUnknownVftbl IUnknownVftbl;
+ public _CreateCompositionSurfaceForHandle CreateCompositionSurfaceForHandle;
+ public _CreateCompositionSurfaceForSwapChain CreateCompositionSurfaceForSwapChain;
+ public _CreateGraphicsDevice CreateGraphicsDevice;
+
+
+ public static readonly Vftbl AbiToProjectionVftable;
+ public static readonly IntPtr AbiToProjectionVftablePtr;
+
+ static Vftbl()
+ {
+ AbiToProjectionVftable = new Vftbl
+ {
+ IUnknownVftbl = global::WinRT.Interop.IUnknownVftbl.AbiToProjectionVftbl,
+
+ CreateCompositionSurfaceForHandle = Do_Abi_Create_Composition_Surface_For_Handle,
+ CreateCompositionSurfaceForSwapChain = Do_Abi_Create_Composition_Surface_For_SwapChain,
+ CreateGraphicsDevice= Do_Abi_Create_Graphics_Device
+ };
+ AbiToProjectionVftablePtr = Marshal.AllocHGlobal(Marshal.SizeOf());
+ Marshal.StructureToPtr(AbiToProjectionVftable, AbiToProjectionVftablePtr, false);
+ }
+
+ private static int Do_Abi_Create_Composition_Surface_For_Handle(IntPtr thisPtr, IntPtr swapChain, out IntPtr surface)
+ {
+ try
+ {
+ surface = IntPtr.Zero;
+ //surface = ComWrappersSupport.FindObject(thisPtr).CreateCompositionSurfaceForHandle(swapChain);
+ return 0;
+ }
+ catch (Exception ex)
+ {
+ surface = IntPtr.Zero;
+ return Marshal.GetHRForException(ex);
+ }
+ }
+
+ private static int Do_Abi_Create_Composition_Surface_For_SwapChain(IntPtr thisPtr, IntPtr swapChain, out IntPtr surface)
+ {
+ try
+ {
+ surface = IntPtr.Zero;
+ //surface = ComWrappersSupport.FindObject(thisPtr).CreateCompositionSurfaceForSwapChain(swapChain);
+ return 0;
+ }
+ catch (Exception ex)
+ {
+ surface = IntPtr.Zero;
+ return Marshal.GetHRForException(ex);
+ }
+ }
+
+ private static int Do_Abi_Create_Graphics_Device(IntPtr thisPtr, IntPtr renderingDevice, out IntPtr graphicsDevice)
+ {
+ try
+ {
+ graphicsDevice = ComWrappersSupport.FindObject(thisPtr).CreateGraphicsDevice(renderingDevice).ThisPtr;
+ return 0;
+ }
+ catch (Exception ex)
+ {
+ graphicsDevice = IntPtr.Zero;
+ return Marshal.GetHRForException(ex);
+ }
+ }
+ }
+ internal static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr);
+
+ public static implicit operator ICompositorInterop(IObjectReference obj) => (obj != null) ? new ICompositorInterop(obj) : null;
+ protected readonly ObjectReference _obj;
+ public IObjectReference ObjRef { get => _obj; }
+ public IntPtr ThisPtr => _obj.ThisPtr;
+ public ObjectReference AsInterface() => _obj.As();
+ public A As() => _obj.AsType();
+ public ICompositorInterop(IObjectReference obj) : this(obj.As()) { }
+ internal ICompositorInterop(ObjectReference obj)
+ {
+ _obj = obj;
+ }
+
+ public ICompositionSurface CreateCompositionSurfaceForHandle(IntPtr swapChain)
+ {
+ Marshal.ThrowExceptionForHR(_obj.Vftbl.CreateCompositionSurfaceForHandle(ThisPtr, swapChain, out var compositionSurface));
+
+ return null;
+ }
+
+ public ICompositionSurface CreateCompositionSurfaceForSwapChain(IntPtr swapChain)
+ {
+ Marshal.ThrowExceptionForHR(_obj.Vftbl.CreateCompositionSurfaceForSwapChain(ThisPtr, swapChain, out var compositionSurface));
+
+ return null;
+ }
+
+ public CompositionGraphicsDevice CreateGraphicsDevice(IntPtr renderingDevice)
+ {
+ Marshal.ThrowExceptionForHR(_obj.Vftbl.CreateGraphicsDevice(ThisPtr, renderingDevice, out var graphicsDevice));
+
+ return CompositionGraphicsDevice.FromAbi(graphicsDevice);
+ }
+ }
+}
+
diff --git a/src/Windows/Avalonia.Win32/Composition/IGraphicsEffectD2D1Interop.cs b/src/Windows/Avalonia.Win32/Composition/IGraphicsEffectD2D1Interop.cs
new file mode 100644
index 0000000000..74d3939a98
--- /dev/null
+++ b/src/Windows/Avalonia.Win32/Composition/IGraphicsEffectD2D1Interop.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Runtime.InteropServices;
+using WinRT;
+
+namespace Windows.Graphics.Effects.Interop
+{
+ [WindowsRuntimeType]
+ [Guid("2FC57384-A068-44D7-A331-30982FCF7177")]
+ public interface IGraphicsEffectD2D1Interop
+ {
+ Guid EffectId { get; }
+
+ uint GetNamedPropertyMapping(string name, out GRAPHICS_EFFECT_PROPERTY_MAPPING mapping);
+
+ object GetProperty(uint index);
+
+ uint PropertyCount { get; }
+
+ IGraphicsEffectSource GetSource(uint index);
+
+ uint SourceCount { get; }
+ }
+}
+
diff --git a/src/Windows/Avalonia.Win32/Composition/IGraphicsEffectD2D1Interop1.cs b/src/Windows/Avalonia.Win32/Composition/IGraphicsEffectD2D1Interop1.cs
new file mode 100644
index 0000000000..8466b05fb5
--- /dev/null
+++ b/src/Windows/Avalonia.Win32/Composition/IGraphicsEffectD2D1Interop1.cs
@@ -0,0 +1,217 @@
+using WinRT;
+
+namespace ABI.Windows.Graphics.Effects.Interop
+{
+ using global::System;
+ using global::System.Runtime.InteropServices;
+
+ [Guid("2FC57384-A068-44D7-A331-30982FCF7177")]
+ internal class IGraphicsEffectD2D1Interop : global::Windows.Graphics.Effects.Interop.IGraphicsEffectD2D1Interop
+
+ {
+ [Guid("2FC57384-A068-44D7-A331-30982FCF7177")]
+ public struct Vftbl
+ {
+ public delegate int _GetEffectId(IntPtr thisPtr, out Guid guid);
+ public delegate int _GetNamedPropertyMapping(IntPtr thisPtr, IntPtr name, IntPtr index, IntPtr mapping);
+ public delegate int _GetProperty(IntPtr thisPtr, uint index, out IntPtr value);
+ public unsafe delegate int _GetPropertyCount(IntPtr thisPtr, uint* count);
+ public delegate int _GetSource(IntPtr thisPtr, uint index, out IntPtr source);
+ public delegate int _GetSourceCount(IntPtr thisPtr, out uint count);
+
+ internal global::WinRT.Interop.IUnknownVftbl IUnknownVftbl;
+ public _GetEffectId GetEffectId;
+ public _GetNamedPropertyMapping GetNamedPropertyMapping;
+ public _GetPropertyCount GetPropertyCount;
+ public _GetProperty GetProperty;
+ public _GetSource GetSource;
+ public _GetSourceCount GetSourceCount;
+
+ public static readonly Vftbl AbiToProjectionVftable;
+ public static readonly IntPtr AbiToProjectionVftablePtr;
+
+ unsafe static Vftbl()
+ {
+ AbiToProjectionVftable = new Vftbl
+ {
+ IUnknownVftbl = global::WinRT.Interop.IUnknownVftbl.AbiToProjectionVftbl,
+ GetEffectId = Do_Abi_Get_Effect_Id,
+ GetNamedPropertyMapping = Do_Abi_Get_Property_Mapping,
+ GetPropertyCount = Do_Abi_Get_Property_Count,
+ GetProperty = Do_Abi_Get_Property,
+ GetSource = Do_Abi_Get_Source,
+ GetSourceCount = Do_Abi_Get_Source_Count
+
+ };
+ AbiToProjectionVftablePtr = Marshal.AllocHGlobal(Marshal.SizeOf());
+ Marshal.StructureToPtr(AbiToProjectionVftable, AbiToProjectionVftablePtr, false);
+ }
+
+ private static int Do_Abi_Get_Effect_Id(IntPtr thisPtr, out Guid guid)
+ {
+ guid = default;
+
+ try
+ {
+ guid = ComWrappersSupport.FindObject(thisPtr).EffectId;
+ }
+ catch (Exception ex)
+ {
+ return Marshal.GetHRForException(ex);
+ }
+
+ return 0;
+ }
+
+ private static int Do_Abi_Get_Property_Mapping(IntPtr thisPtr, IntPtr name, IntPtr index, IntPtr mapping)
+ {
+ try
+ {
+ ComWrappersSupport.FindObject(thisPtr).GetNamedPropertyMapping(MarshalString.FromAbi(name), out var mappingResult);
+ }
+ catch (Exception ex)
+ {
+ return Marshal.GetHRForException(ex);
+ }
+
+ return 0;
+ }
+
+ private static int Do_Abi_Get_Property(IntPtr thisPtr, uint index, out IntPtr value)
+ {
+ value = default;
+
+ try
+ {
+ value = MarshalInspectable.CreateMarshaler(
+ ComWrappersSupport.FindObject(thisPtr).GetProperty(index))
+ .As(Guid.Parse("4BD682DD-7554-40E9-9A9B-82654EDE7E62"))
+ .GetRef();
+
+ }
+ catch (Exception ex)
+ {
+ return Marshal.GetHRForException(ex);
+ }
+
+ return 0;
+ }
+
+ unsafe private static int Do_Abi_Get_Property_Count(IntPtr thisPtr, uint* count)
+ {
+
+ try
+ {
+ var res = ComWrappersSupport.FindObject(thisPtr).PropertyCount;
+
+ if (count != null)
+ {
+ *count = res;
+ }
+ }
+ catch (Exception ex)
+ {
+ return Marshal.GetHRForException(ex);
+ }
+
+ return 0;
+ }
+
+ private static int Do_Abi_Get_Source(IntPtr thisPtr, uint index, out IntPtr value)
+ {
+ value = default;
+
+ try
+ {
+ var source = ComWrappersSupport.FindObject(thisPtr).GetSource(index);
+
+ value = MarshalInterface.FromManaged(source);
+ }
+ catch (Exception ex)
+ {
+ return Marshal.GetHRForException(ex);
+ }
+
+ return 0;
+ }
+
+ private static int Do_Abi_Get_Source_Count(IntPtr thisPtr, out uint count)
+ {
+ count = default;
+
+ try
+ {
+ count = ComWrappersSupport.FindObject(thisPtr).SourceCount;
+ }
+ catch (Exception ex)
+ {
+ return Marshal.GetHRForException(ex);
+ }
+
+ return 0;
+ }
+ }
+ internal static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr);
+
+ public static implicit operator IGraphicsEffectD2D1Interop(IObjectReference obj) => (obj != null) ? new IGraphicsEffectD2D1Interop(obj) : null;
+ protected readonly ObjectReference _obj;
+ public IObjectReference ObjRef { get => _obj; }
+ public IntPtr ThisPtr => _obj.ThisPtr;
+
+ public ObjectReference AsInterface() => _obj.As();
+ public A As() => _obj.AsType();
+ public IGraphicsEffectD2D1Interop(IObjectReference obj) : this(obj.As()) { }
+ internal IGraphicsEffectD2D1Interop(ObjectReference obj)
+ {
+ _obj = obj;
+ }
+
+ public Guid EffectId
+ {
+ get
+ {
+ Marshal.ThrowExceptionForHR(_obj.Vftbl.GetEffectId(ThisPtr, out Guid guid));
+ return guid;
+ }
+ }
+
+ public uint PropertyCount
+ {
+ get
+ {
+ unsafe
+ {
+ uint count = default;
+ Marshal.ThrowExceptionForHR(_obj.Vftbl.GetPropertyCount(ThisPtr, &count));
+ return count;
+ }
+ }
+ }
+
+ public uint SourceCount
+ {
+ get
+ {
+ Marshal.ThrowExceptionForHR(_obj.Vftbl.GetSourceCount(ThisPtr, out uint count));
+ return count;
+ }
+ }
+
+ public uint GetNamedPropertyMapping(string name, out global::Windows.Graphics.Effects.Interop.GRAPHICS_EFFECT_PROPERTY_MAPPING mapping)
+ {
+ throw new NotImplementedException();
+ }
+
+ public object GetProperty(uint index)
+ {
+ // Marshal.ThrowExceptionForHR(_obj.Vftbl.GetProperty(ThisPtr, index, out IntPtr value));
+ throw new NotImplementedException();
+ }
+
+ public global::Windows.Graphics.Effects.IGraphicsEffectSource GetSource(uint index)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
+
diff --git a/src/Windows/Avalonia.Win32/Composition/SaturationEffect.cs b/src/Windows/Avalonia.Win32/Composition/SaturationEffect.cs
new file mode 100644
index 0000000000..90eca22d8e
--- /dev/null
+++ b/src/Windows/Avalonia.Win32/Composition/SaturationEffect.cs
@@ -0,0 +1,35 @@
+using System;
+using Windows.Graphics.Effects;
+
+
+namespace Avalonia.Win32
+{
+ class SaturationEffect : EffectBase
+ {
+ public SaturationEffect(IGraphicsEffect source) : base(source)
+ {
+ }
+
+ enum D2D1_SATURATION_PROP
+ {
+ D2D1_SATURATION_PROP_SATURATION,
+ D2D1_SATURATION_PROP_FORCE_DWORD
+ };
+
+ public override Guid EffectId => D2DEffects.CLSID_D2D1Saturation;
+
+ public override uint PropertyCount => 1;
+
+ public override object GetProperty(uint index)
+ {
+ switch ((D2D1_SATURATION_PROP)index)
+ {
+ case D2D1_SATURATION_PROP.D2D1_SATURATION_PROP_SATURATION:
+ return 2.0f;
+ }
+
+ return null;
+ }
+ }
+}
+
diff --git a/src/Windows/Avalonia.Win32/Interop/TaskBarList.cs b/src/Windows/Avalonia.Win32/Interop/TaskBarList.cs
index 1b01ebbe7f..88b907aeec 100644
--- a/src/Windows/Avalonia.Win32/Interop/TaskBarList.cs
+++ b/src/Windows/Avalonia.Win32/Interop/TaskBarList.cs
@@ -13,6 +13,7 @@ namespace Avalonia.Win32.Interop
///
/// Ported from https://github.com/chromium/chromium/blob/master/ui/views/win/fullscreen_handler.cc
///
+ /// The window handle.
/// Fullscreen state.
public static unsafe void MarkFullscreen(IntPtr hwnd, bool fullscreen)
{
diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
index 5feb6c9e46..a527a35c52 100644
--- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
+++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
@@ -466,6 +466,7 @@ namespace Avalonia.Win32.Interop
WS_VSCROLL = 0x200000,
WS_EX_DLGMODALFRAME = 0x00000001,
WS_EX_NOPARENTNOTIFY = 0x00000004,
+ WS_EX_NOREDIRECTIONBITMAP = 0x00200000,
WS_EX_TOPMOST = 0x00000008,
WS_EX_ACCEPTFILES = 0x00000010,
WS_EX_TRANSPARENT = 0x00000020,
diff --git a/src/Windows/Avalonia.Win32/Win32GlManager.cs b/src/Windows/Avalonia.Win32/Win32GlManager.cs
index 523a059e0e..6a6ce2ec3c 100644
--- a/src/Windows/Avalonia.Win32/Win32GlManager.cs
+++ b/src/Windows/Avalonia.Win32/Win32GlManager.cs
@@ -7,8 +7,6 @@ namespace Avalonia.Win32
{
static class Win32GlManager
{
- private static bool s_attemptedToInitialize;
-
public static void Initialize()
{
AvaloniaLocator.CurrentMutable.Bind().ToLazy(() =>
@@ -19,9 +17,21 @@ namespace Avalonia.Win32
var wgl = WglPlatformOpenGlInterface.TryCreate();
return wgl;
}
-
+
if (opts?.AllowEglInitialization == true)
- return EglPlatformOpenGlInterface.TryCreate(() => new AngleWin32EglDisplay());
+ {
+ var egl = EglPlatformOpenGlInterface.TryCreate(() => new AngleWin32EglDisplay());
+
+ if (egl is { } &&
+ opts?.UseWindowsUIComposition == true &&
+ Win32Platform.WindowsVersion.Major >= 10 &&
+ Win32Platform.WindowsVersion.Build >= 16299)
+ {
+ AvaloniaLocator.CurrentMutable.BindToSelf(new CompositionConnector(egl));
+ }
+
+ return egl;
+ }
return null;
});
diff --git a/src/Windows/Avalonia.Win32/Win32Platform.cs b/src/Windows/Avalonia.Win32/Win32Platform.cs
index 61c1a4f45e..5b16cae26e 100644
--- a/src/Windows/Avalonia.Win32/Win32Platform.cs
+++ b/src/Windows/Avalonia.Win32/Win32Platform.cs
@@ -12,6 +12,7 @@ using Avalonia.Controls.Platform;
using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.OpenGL;
+using Avalonia.OpenGL.Egl;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.Threading;
@@ -46,6 +47,15 @@ namespace Avalonia
new GlVersion(GlProfileType.OpenGL, 4, 0),
new GlVersion(GlProfileType.OpenGL, 3, 2),
};
+
+ ///
+ /// Render Avalonia to a Texture inside the Windows.UI.Composition tree.
+ ///
+ ///
+ /// Supported on Windows 10 build 16299 and above. Ignored on other versions.
+ /// This is recommended if you need to use AcrylicBlur or acrylic in your applications.
+ ///
+ public bool UseWindowsUIComposition { get; set; } = true;
}
}
@@ -104,7 +114,7 @@ namespace Avalonia.Win32
.Bind().ToConstant(new WindowsMountedVolumeInfoProvider());
Win32GlManager.Initialize();
-
+
_uiThread = Thread.CurrentThread;
if (OleContext.Current != null)
diff --git a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs
index 01576500e7..370e2a2a4b 100644
--- a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs
+++ b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs
@@ -343,16 +343,21 @@ namespace Avalonia.Win32
case WindowsMessage.WM_PAINT:
{
- using (_rendererLock.Lock())
+ using (_rendererLock.Lock())
+ {
+ if (BeginPaint(_hwnd, out PAINTSTRUCT ps) != IntPtr.Zero)
{
- Paint?.Invoke(default);
+ var f = RenderScaling;
+ var r = ps.rcPaint;
+ Paint?.Invoke(new Rect(r.left / f, r.top / f, (r.right - r.left) / f,
+ (r.bottom - r.top) / f));
+ EndPaint(_hwnd, ref ps);
}
-
- ValidateRect(hWnd, IntPtr.Zero);
-
- return IntPtr.Zero;
}
+ return IntPtr.Zero;
+ }
+
case WindowsMessage.WM_SIZE:
{
using (_rendererLock.Lock())
diff --git a/src/Windows/Avalonia.Win32/WindowImpl.CustomCaptionProc.cs b/src/Windows/Avalonia.Win32/WindowImpl.CustomCaptionProc.cs
index a3b7574369..68bd40da79 100644
--- a/src/Windows/Avalonia.Win32/WindowImpl.CustomCaptionProc.cs
+++ b/src/Windows/Avalonia.Win32/WindowImpl.CustomCaptionProc.cs
@@ -1,5 +1,4 @@
using System;
-using System.Diagnostics;
using Avalonia.Controls;
using Avalonia.Input;
using static Avalonia.Win32.Interop.UnmanagedMethods;
@@ -11,70 +10,78 @@ namespace Avalonia.Win32
public partial class WindowImpl
{
// Hit test the frame for resizing and moving.
- HitTestValues HitTestNCA(IntPtr hWnd, IntPtr wParam, IntPtr lParam)
+ private HitTestValues HitTestNCA(IntPtr hWnd, IntPtr wParam, IntPtr lParam)
{
- // Get the point coordinates for the hit test.
+ // Get the point coordinates for the hit test (screen space).
var ptMouse = PointFromLParam(lParam);
- // Get the window rectangle.
+ // Get the window rectangle.
GetWindowRect(hWnd, out var rcWindow);
// Get the frame rectangle, adjusted for the style without a caption.
- RECT rcFrame = new RECT();
+ var rcFrame = new RECT();
AdjustWindowRectEx(ref rcFrame, (uint)(WindowStyles.WS_OVERLAPPEDWINDOW & ~WindowStyles.WS_CAPTION), false, 0);
- RECT border_thickness = new RECT();
+ var borderThickness = new RECT();
if (GetStyle().HasFlag(WindowStyles.WS_THICKFRAME))
{
- AdjustWindowRectEx(ref border_thickness, (uint)(GetStyle()), false, 0);
- border_thickness.left *= -1;
- border_thickness.top *= -1;
+ AdjustWindowRectEx(ref borderThickness, (uint)(GetStyle()), false, 0);
+ borderThickness.left *= -1;
+ borderThickness.top *= -1;
}
else if (GetStyle().HasFlag(WindowStyles.WS_BORDER))
{
- border_thickness = new RECT { bottom = 1, left = 1, right = 1, top = 1 };
+ borderThickness = new RECT { bottom = 1, left = 1, right = 1, top = 1 };
}
if (_extendTitleBarHint >= 0)
{
- border_thickness.top = (int)(_extendedMargins.Top * RenderScaling);
+ borderThickness.top = (int)(_extendedMargins.Top * RenderScaling);
}
// Determine if the hit test is for resizing. Default middle (1,1).
ushort uRow = 1;
ushort uCol = 1;
- bool fOnResizeBorder = false;
+ bool onResizeBorder = false;
- // Determine if the point is at the top or bottom of the window.
- if (ptMouse.Y >= rcWindow.top && ptMouse.Y < rcWindow.top + border_thickness.top)
+ // Determine if the point is at the left or right of the window.
+ if (ptMouse.X >= rcWindow.left && ptMouse.X < rcWindow.left + borderThickness.left)
{
- fOnResizeBorder = (ptMouse.Y < (rcWindow.top - rcFrame.top));
- uRow = 0;
+ uCol = 0; // left side
}
- else if (ptMouse.Y < rcWindow.bottom && ptMouse.Y >= rcWindow.bottom - border_thickness.bottom)
+ else if (ptMouse.X < rcWindow.right && ptMouse.X >= rcWindow.right - borderThickness.right)
{
- uRow = 2;
+ uCol = 2; // right side
}
- // Determine if the point is at the left or right of the window.
- if (ptMouse.X >= rcWindow.left && ptMouse.X < rcWindow.left + border_thickness.left)
+ // Determine if the point is at the top or bottom of the window.
+ if (ptMouse.Y >= rcWindow.top && ptMouse.Y < rcWindow.top + borderThickness.top)
{
- uCol = 0; // left side
+ onResizeBorder = (ptMouse.Y < (rcWindow.top - rcFrame.top));
+
+ // Two cases where we have a valid row 0 hit test:
+ // - window resize border (top resize border hit)
+ // - area below resize border that is actual titlebar (caption hit).
+ if (onResizeBorder || uCol == 1)
+ {
+ uRow = 0;
+ }
}
- else if (ptMouse.X < rcWindow.right && ptMouse.X >= rcWindow.right - border_thickness.right)
+ else if (ptMouse.Y < rcWindow.bottom && ptMouse.Y >= rcWindow.bottom - borderThickness.bottom)
{
- uCol = 2; // right side
+ uRow = 2;
}
- // Hit test (HTTOPLEFT, ... HTBOTTOMRIGHT)
- HitTestValues[][] hitTests = new[]
+ ReadOnlySpan hitZones = stackalloc HitTestValues[]
{
- new []{ HitTestValues.HTTOPLEFT, fOnResizeBorder ? HitTestValues.HTTOP : HitTestValues.HTCAPTION, HitTestValues.HTTOPRIGHT },
- new []{ HitTestValues.HTLEFT, HitTestValues.HTNOWHERE, HitTestValues.HTRIGHT },
- new []{ HitTestValues.HTBOTTOMLEFT, HitTestValues.HTBOTTOM, HitTestValues.HTBOTTOMRIGHT },
+ HitTestValues.HTTOPLEFT, onResizeBorder ? HitTestValues.HTTOP : HitTestValues.HTCAPTION,
+ HitTestValues.HTTOPRIGHT, HitTestValues.HTLEFT, HitTestValues.HTNOWHERE, HitTestValues.HTRIGHT,
+ HitTestValues.HTBOTTOMLEFT, HitTestValues.HTBOTTOM, HitTestValues.HTBOTTOMRIGHT
};
- return hitTests[uRow][uCol];
+ var zoneIndex = uRow * 3 + uCol;
+
+ return hitZones[zoneIndex];
}
protected virtual IntPtr CustomCaptionProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam, ref bool callDwp)
diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs
index 7079a0120c..715c8fc01d 100644
--- a/src/Windows/Avalonia.Win32/WindowImpl.cs
+++ b/src/Windows/Avalonia.Win32/WindowImpl.cs
@@ -7,6 +7,7 @@ using Avalonia.Controls.Platform;
using Avalonia.Input;
using Avalonia.Input.Raw;
using Avalonia.OpenGL;
+using Avalonia.OpenGL.Angle;
using Avalonia.OpenGL.Egl;
using Avalonia.OpenGL.Surfaces;
using Avalonia.Platform;
@@ -48,6 +49,8 @@ namespace Avalonia.Win32
private Thickness _extendedMargins;
private Thickness _offScreenMargin;
private double _extendTitleBarHint = -1;
+ private bool _isUsingComposition;
+ private IBlurHost _blurHost;
#if USE_MANAGED_DRAG
private readonly ManagedWindowResizeDragHelper _managedDrag;
@@ -102,16 +105,37 @@ namespace Avalonia.Win32
};
_rendererLock = new ManagedDeferredRendererLock();
+ var glPlatform = AvaloniaLocator.Current.GetService();
+
+ var compositionConnector = AvaloniaLocator.Current.GetService();
+
+ _isUsingComposition = compositionConnector is { } &&
+ glPlatform is EglPlatformOpenGlInterface egl &&
+ egl.Display is AngleWin32EglDisplay angleDisplay &&
+ angleDisplay.PlatformApi == AngleOptions.PlatformApi.DirectX11;
CreateWindow();
_framebuffer = new FramebufferManager(_hwnd);
-
- var glPlatform = AvaloniaLocator.Current.GetService();
- if(glPlatform is EglPlatformOpenGlInterface egl)
- _gl = new EglGlPlatformSurface(egl, this);
- else if (glPlatform is WglPlatformOpenGlInterface wgl)
- _gl = new WglGlPlatformSurface(wgl.PrimaryContext, this);
+ if (glPlatform != null)
+ {
+ if (_isUsingComposition)
+ {
+ var cgl = new CompositionEglGlPlatformSurface(glPlatform as EglPlatformOpenGlInterface, this);
+ _blurHost = cgl.AttachToCompositionTree(compositionConnector, _hwnd);
+
+ _gl = cgl;
+
+ _isUsingComposition = true;
+ }
+ else
+ {
+ if (glPlatform is EglPlatformOpenGlInterface egl2)
+ _gl = new EglGlPlatformSurface(egl2, this);
+ else if (glPlatform is WglPlatformOpenGlInterface wgl)
+ _gl = new WglGlPlatformSurface(wgl.PrimaryContext, this);
+ }
+ }
Screen = new ScreenImpl();
@@ -328,54 +352,63 @@ namespace Avalonia.Win32
private WindowTransparencyLevel Win10EnableBlur(WindowTransparencyLevel transparencyLevel)
{
- bool canUseAcrylic = Win32Platform.WindowsVersion.Major > 10 || Win32Platform.WindowsVersion.Build >= 19628;
-
- var accent = new AccentPolicy();
- var accentStructSize = Marshal.SizeOf(accent);
-
- if (transparencyLevel == WindowTransparencyLevel.AcrylicBlur && !canUseAcrylic)
+ if (_isUsingComposition)
{
- transparencyLevel = WindowTransparencyLevel.Blur;
- }
+ _blurHost?.SetBlur(transparencyLevel >= WindowTransparencyLevel.Blur);
- switch (transparencyLevel)
+ return transparencyLevel;
+ }
+ else
{
- default:
- case WindowTransparencyLevel.None:
- accent.AccentState = AccentState.ACCENT_DISABLED;
- break;
+ bool canUseAcrylic = Win32Platform.WindowsVersion.Major > 10 || Win32Platform.WindowsVersion.Build >= 19628;
- case WindowTransparencyLevel.Transparent:
- accent.AccentState = AccentState.ACCENT_ENABLE_TRANSPARENTGRADIENT;
- break;
+ var accent = new AccentPolicy();
+ var accentStructSize = Marshal.SizeOf(accent);
- case WindowTransparencyLevel.Blur:
- accent.AccentState = AccentState.ACCENT_ENABLE_BLURBEHIND;
- break;
+ if (transparencyLevel == WindowTransparencyLevel.AcrylicBlur && !canUseAcrylic)
+ {
+ transparencyLevel = WindowTransparencyLevel.Blur;
+ }
- case WindowTransparencyLevel.AcrylicBlur:
- case (WindowTransparencyLevel.AcrylicBlur + 1): // hack-force acrylic.
- accent.AccentState = AccentState.ACCENT_ENABLE_ACRYLIC;
- transparencyLevel = WindowTransparencyLevel.AcrylicBlur;
- break;
- }
+ switch (transparencyLevel)
+ {
+ default:
+ case WindowTransparencyLevel.None:
+ accent.AccentState = AccentState.ACCENT_DISABLED;
+ break;
+
+ case WindowTransparencyLevel.Transparent:
+ accent.AccentState = AccentState.ACCENT_ENABLE_TRANSPARENTGRADIENT;
+ break;
+
+ case WindowTransparencyLevel.Blur:
+ accent.AccentState = AccentState.ACCENT_ENABLE_BLURBEHIND;
+ break;
+
+ case WindowTransparencyLevel.AcrylicBlur:
+ case (WindowTransparencyLevel.AcrylicBlur + 1): // hack-force acrylic.
+ accent.AccentState = AccentState.ACCENT_ENABLE_ACRYLIC;
+ transparencyLevel = WindowTransparencyLevel.AcrylicBlur;
+ break;
+ }
- accent.AccentFlags = 2;
- accent.GradientColor = 0x01000000;
+ accent.AccentFlags = 2;
+ accent.GradientColor = 0x01000000;
- var accentPtr = Marshal.AllocHGlobal(accentStructSize);
- Marshal.StructureToPtr(accent, accentPtr, false);
+ var accentPtr = Marshal.AllocHGlobal(accentStructSize);
+ Marshal.StructureToPtr(accent, accentPtr, false);
- var data = new WindowCompositionAttributeData();
- data.Attribute = WindowCompositionAttribute.WCA_ACCENT_POLICY;
- data.SizeOfData = accentStructSize;
- data.Data = accentPtr;
+ var data = new WindowCompositionAttributeData();
+ data.Attribute = WindowCompositionAttribute.WCA_ACCENT_POLICY;
+ data.SizeOfData = accentStructSize;
+ data.Data = accentPtr;
- SetWindowCompositionAttribute(_hwnd, ref data);
+ SetWindowCompositionAttribute(_hwnd, ref data);
- Marshal.FreeHGlobal(accentPtr);
+ Marshal.FreeHGlobal(accentPtr);
- return transparencyLevel;
+ return transparencyLevel;
+ }
}
public IEnumerable Surfaces => new object[] { Handle, _gl, _framebuffer };
@@ -622,7 +655,7 @@ namespace Avalonia.Win32
protected virtual IntPtr CreateWindowOverride(ushort atom)
{
return CreateWindowEx(
- 0,
+ _isUsingComposition ? (int)WindowStyles.WS_EX_NOREDIRECTIONBITMAP : 0,
atom,
null,
(int)WindowStyles.WS_OVERLAPPEDWINDOW | (int) WindowStyles.WS_CLIPCHILDREN,
diff --git a/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj b/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj
index 51d18e55d1..a2d27fd579 100644
--- a/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj
+++ b/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj
@@ -23,5 +23,8 @@
+
+
+
diff --git a/tests/Avalonia.Animation.UnitTests/AnimatableTests.cs b/tests/Avalonia.Animation.UnitTests/AnimatableTests.cs
index 784f40fe1f..7633a761a3 100644
--- a/tests/Avalonia.Animation.UnitTests/AnimatableTests.cs
+++ b/tests/Avalonia.Animation.UnitTests/AnimatableTests.cs
@@ -113,7 +113,7 @@ namespace Avalonia.Animation.UnitTests
It.IsAny(),
1.0,
0.5));
- target.ResetCalls();
+ target.Invocations.Clear();
control.SetValue(Visual.OpacityProperty, 0.8, BindingPriority.StyleTrigger);
@@ -135,7 +135,7 @@ namespace Avalonia.Animation.UnitTests
target.Setup(x => x.Apply(control, It.IsAny(), 1.0, 0.5)).Returns(sub.Object);
control.Opacity = 0.5;
- sub.ResetCalls();
+ sub.Invocations.Clear();
control.Opacity = 0.4;
sub.Verify(x => x.Dispose());
@@ -158,7 +158,7 @@ namespace Avalonia.Animation.UnitTests
control.Opacity = 0.5;
Assert.Equal(0.9, control.Opacity);
- target.ResetCalls();
+ target.Invocations.Clear();
control.Opacity = 0.4;
@@ -182,7 +182,7 @@ namespace Avalonia.Animation.UnitTests
It.IsAny(),
1.0,
0.5));
- target.ResetCalls();
+ target.Invocations.Clear();
var root = (TestRoot)control.Parent;
root.Child = null;
diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Metadata.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Metadata.cs
index 161911dfd5..2edb3deff0 100644
--- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Metadata.cs
+++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Metadata.cs
@@ -67,7 +67,7 @@ namespace Avalonia.Base.UnitTests
public static readonly DirectProperty DirectProperty =
AvaloniaProperty.RegisterDirect("Styled", o => o.Direct, unsetValue: "foo");
- private string _direct;
+ private string _direct = default;
public string Direct
{
@@ -92,7 +92,7 @@ namespace Avalonia.Base.UnitTests
public static readonly DirectProperty DirectProperty =
Class1.DirectProperty.AddOwner(o => o.Direct, unsetValue: "baz");
- private string _direct;
+ private string _direct = default;
static Class3()
{
diff --git a/tests/Avalonia.Base.UnitTests/Collections/AvaloniaListExtenionsTests.cs b/tests/Avalonia.Base.UnitTests/Collections/AvaloniaListExtenionsTests.cs
index 7f118a2c1d..b1b2d3d8f2 100644
--- a/tests/Avalonia.Base.UnitTests/Collections/AvaloniaListExtenionsTests.cs
+++ b/tests/Avalonia.Base.UnitTests/Collections/AvaloniaListExtenionsTests.cs
@@ -6,10 +6,12 @@ namespace Avalonia.Base.UnitTests.Collections
{
public class AvaloniaListExtenionsTests
{
+#pragma warning disable CS0618 // Type or member is obsolete
[Fact]
public void CreateDerivedList_Creates_Initial_Items()
{
var source = new AvaloniaList(new[] { 0, 1, 2, 3 });
+
var target = source.CreateDerivedList(x => new Wrapper(x));
var result = target.Select(x => x.Value).ToList();
@@ -137,6 +139,8 @@ namespace Avalonia.Base.UnitTests.Collections
Assert.Equal(source, result);
}
+#pragma warning restore CS0618 // Type or member is obsolete
+
private class Wrapper
{
diff --git a/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.cs b/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.cs
index 1e1f4bd4b2..339cf8a334 100644
--- a/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.cs
+++ b/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.cs
@@ -356,7 +356,7 @@ namespace Avalonia.Base.UnitTests.Data.Core
}
[Fact]
- public async Task Null_Value_Should_Use_TargetNullValue()
+ public void Null_Value_Should_Use_TargetNullValue()
{
var data = new Class1 { StringValue = "foo" };
diff --git a/tests/Avalonia.Base.UnitTests/Data/DefaultValueConverterTests.cs b/tests/Avalonia.Base.UnitTests/Data/DefaultValueConverterTests.cs
index efa81dcc1b..f522acf9ce 100644
--- a/tests/Avalonia.Base.UnitTests/Data/DefaultValueConverterTests.cs
+++ b/tests/Avalonia.Base.UnitTests/Data/DefaultValueConverterTests.cs
@@ -1,12 +1,11 @@
+using System;
+using System.ComponentModel;
using System.Globalization;
-using Avalonia.Controls;
-using Avalonia.Data;
-using Xunit;
using System.Windows.Input;
-using System;
+using Avalonia.Data;
using Avalonia.Data.Converters;
using Avalonia.Layout;
-using System.ComponentModel;
+using Xunit;
namespace Avalonia.Base.UnitTests.Data.Converters
{
@@ -251,6 +250,11 @@ namespace Avalonia.Base.UnitTests.Data.Converters
{
return obj is CustomType other && this.Value == other.Value;
}
+
+ public override int GetHashCode()
+ {
+ return 8399587^Value.GetHashCode();
+ }
}
private class CustomTypeConverter : TypeConverter
diff --git a/tests/Avalonia.Benchmarks/NullRenderer.cs b/tests/Avalonia.Benchmarks/NullRenderer.cs
index 9a756aaf0b..c1701b5d57 100644
--- a/tests/Avalonia.Benchmarks/NullRenderer.cs
+++ b/tests/Avalonia.Benchmarks/NullRenderer.cs
@@ -9,8 +9,9 @@ namespace Avalonia.Benchmarks
{
public bool DrawFps { get; set; }
public bool DrawDirtyRects { get; set; }
+#pragma warning disable CS0067
public event EventHandler SceneInvalidated;
-
+#pragma warning restore CS0067
public void AddDirty(IVisual visual)
{
}
diff --git a/tests/Avalonia.Benchmarks/NullThreadingPlatform.cs b/tests/Avalonia.Benchmarks/NullThreadingPlatform.cs
index ba84b5afcc..bb469a6b33 100644
--- a/tests/Avalonia.Benchmarks/NullThreadingPlatform.cs
+++ b/tests/Avalonia.Benchmarks/NullThreadingPlatform.cs
@@ -23,6 +23,9 @@ namespace Avalonia.Benchmarks
public bool CurrentThreadIsLoopThread => true;
+#pragma warning disable CS0067
public event Action Signaled;
+#pragma warning restore CS0067
+
}
}
diff --git a/tests/Avalonia.Controls.UnitTests/Avalonia.Controls.UnitTests.csproj b/tests/Avalonia.Controls.UnitTests/Avalonia.Controls.UnitTests.csproj
index 19c4454d3d..7a6d77ef46 100644
--- a/tests/Avalonia.Controls.UnitTests/Avalonia.Controls.UnitTests.csproj
+++ b/tests/Avalonia.Controls.UnitTests/Avalonia.Controls.UnitTests.csproj
@@ -22,7 +22,6 @@
-
diff --git a/tests/Avalonia.Controls.UnitTests/BorderTests.cs b/tests/Avalonia.Controls.UnitTests/BorderTests.cs
index 6e2599c9fd..ab33eaaff9 100644
--- a/tests/Avalonia.Controls.UnitTests/BorderTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/BorderTests.cs
@@ -54,7 +54,7 @@ namespace Avalonia.Controls.UnitTests
var root = new TestRoot(target);
var renderer = Mock.Get(root.Renderer);
- renderer.ResetCalls();
+ renderer.Invocations.Clear();
((SolidColorBrush)target.Background).Color = Colors.Green;
diff --git a/tests/Avalonia.Controls.UnitTests/CanvasTests.cs b/tests/Avalonia.Controls.UnitTests/CanvasTests.cs
index da1698330f..11a349f53e 100644
--- a/tests/Avalonia.Controls.UnitTests/CanvasTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/CanvasTests.cs
@@ -1,5 +1,6 @@
using System;
using Avalonia.Controls.Shapes;
+using Avalonia.UnitTests;
using Xunit;
namespace Avalonia.Controls.UnitTests
@@ -9,6 +10,8 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Left_Property_Should_Work()
{
+ using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
+
Rectangle rect;
var target = new Canvas
{
@@ -34,6 +37,8 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Top_Property_Should_Work()
{
+ using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
+
Rectangle rect;
var target = new Canvas
{
@@ -59,6 +64,8 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Right_Property_Should_Work()
{
+ using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
+
Rectangle rect;
var target = new Canvas
{
@@ -84,6 +91,8 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Bottom_Property_Should_Work()
{
+ using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
+
Rectangle rect;
var target = new Canvas
{
diff --git a/tests/Avalonia.Controls.UnitTests/LayoutTransformControlTests.cs b/tests/Avalonia.Controls.UnitTests/LayoutTransformControlTests.cs
index 13c946b549..60139c2881 100644
--- a/tests/Avalonia.Controls.UnitTests/LayoutTransformControlTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/LayoutTransformControlTests.cs
@@ -1,5 +1,6 @@
using Avalonia.Controls.Shapes;
using Avalonia.Media;
+using Avalonia.UnitTests;
using Xunit;
namespace Avalonia.Controls.UnitTests
@@ -171,6 +172,8 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Should_Generate_RotateTransform_90_degrees()
{
+ using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
+
LayoutTransformControl lt = CreateWithChildAndMeasureAndTransform(
100,
25,
@@ -193,6 +196,8 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Should_Generate_RotateTransform_minus_90_degrees()
{
+ using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
+
LayoutTransformControl lt = CreateWithChildAndMeasureAndTransform(
100,
25,
@@ -215,6 +220,8 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Should_Generate_ScaleTransform_x2()
{
+ using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
+
LayoutTransformControl lt = CreateWithChildAndMeasureAndTransform(
100,
50,
@@ -236,6 +243,8 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Should_Generate_SkewTransform_45_degrees()
{
+ using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
+
LayoutTransformControl lt = CreateWithChildAndMeasureAndTransform(
100,
100,
@@ -258,6 +267,8 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Should_Generate_SkewTransform_minus_45_degrees()
{
+ using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
+
LayoutTransformControl lt = CreateWithChildAndMeasureAndTransform(
100,
100,
@@ -279,6 +290,8 @@ namespace Avalonia.Controls.UnitTests
private static void TransformMeasureSizeTest(Size size, Transform transform, Size expectedSize)
{
+ using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
+
LayoutTransformControl lt = CreateWithChildAndMeasureAndTransform(
size.Width,
size.Height,
@@ -292,6 +305,8 @@ namespace Avalonia.Controls.UnitTests
private static void TransformRootBoundsTest(Size size, Transform transform, Rect expectedBounds)
{
+ using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
+
LayoutTransformControl lt = CreateWithChildAndMeasureAndTransform(size.Width, size.Height, transform);
Rect outBounds = lt.TransformRoot.Bounds;
diff --git a/tests/Avalonia.Controls.UnitTests/PanelTests.cs b/tests/Avalonia.Controls.UnitTests/PanelTests.cs
index 79ee6d1236..f189638c7d 100644
--- a/tests/Avalonia.Controls.UnitTests/PanelTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/PanelTests.cs
@@ -127,7 +127,7 @@ namespace Avalonia.Controls.UnitTests
var root = new TestRoot(target);
var renderer = Mock.Get(root.Renderer);
- renderer.ResetCalls();
+ renderer.Invocations.Clear();
((SolidColorBrush)target.Background).Color = Colors.Green;
diff --git a/tests/Avalonia.Controls.UnitTests/Platform/DefaultMenuInteractionHandlerTests.cs b/tests/Avalonia.Controls.UnitTests/Platform/DefaultMenuInteractionHandlerTests.cs
index 64f35049ce..1a11091b81 100644
--- a/tests/Avalonia.Controls.UnitTests/Platform/DefaultMenuInteractionHandlerTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/Platform/DefaultMenuInteractionHandlerTests.cs
@@ -503,26 +503,26 @@ namespace Avalonia.Controls.UnitTests.Platform
target.PointerEnter(item, enter);
Assert.True(timer.ActionIsQueued);
Mock.Get(parentItem).VerifySet(x => x.SelectedItem = item);
- Mock.Get(parentItem).ResetCalls();
+ Mock.Get(parentItem).Invocations.Clear();
// SubMenu shown after a delay.
timer.Pulse();
Mock.Get(item).Verify(x => x.Open());
Mock.Get(item).SetupGet(x => x.IsSubMenuOpen).Returns(true);
- Mock.Get(item).ResetCalls();
+ Mock.Get(item).Invocations.Clear();
// Pointer briefly exits item, but submenu remains open.
target.PointerLeave(item, leave);
Mock.Get(item).Verify(x => x.Close(), Times.Never);
- Mock.Get(item).ResetCalls();
+ Mock.Get(item).Invocations.Clear();
// Pointer enters child item; is selected.
enter.Source = childItem;
target.PointerEnter(childItem, enter);
Mock.Get(item).VerifySet(x => x.SelectedItem = childItem);
Mock.Get(parentItem).VerifySet(x => x.SelectedItem = item);
- Mock.Get(item).ResetCalls();
- Mock.Get(parentItem).ResetCalls();
+ Mock.Get(item).Invocations.Clear();
+ Mock.Get(parentItem).Invocations.Clear();
}
[Fact]
diff --git a/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Standalone.cs b/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Standalone.cs
index c0440ebc7b..c7aa583b6f 100644
--- a/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Standalone.cs
+++ b/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Standalone.cs
@@ -212,7 +212,7 @@ namespace Avalonia.Controls.UnitTests.Presenters
var root = new TestRoot(target);
var renderer = Mock.Get(root.Renderer);
- renderer.ResetCalls();
+ renderer.Invocations.Clear();
((SolidColorBrush)target.Background).Color = Colors.Green;
diff --git a/tests/Avalonia.Controls.UnitTests/RelativePanelTests.cs b/tests/Avalonia.Controls.UnitTests/RelativePanelTests.cs
index 6e171a58e7..6b2f05c923 100644
--- a/tests/Avalonia.Controls.UnitTests/RelativePanelTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/RelativePanelTests.cs
@@ -1,4 +1,5 @@
using Avalonia.Controls.Shapes;
+using Avalonia.UnitTests;
using Xunit;
namespace Avalonia.Controls.UnitTests
@@ -8,6 +9,7 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Lays_Out_1_Child_Next_the_other()
{
+ using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
var rect1 = new Rectangle { Height = 20, Width = 20 };
var rect2 = new Rectangle { Height = 20, Width = 20 };
@@ -34,6 +36,7 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Lays_Out_1_Child_Below_the_other()
{
+ using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
var rect1 = new Rectangle { Height = 20, Width = 20 };
var rect2 = new Rectangle { Height = 20, Width = 20 };
@@ -60,6 +63,7 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void RelativePanel_Can_Center()
{
+ using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
var rect1 = new Rectangle { Height = 20, Width = 20 };
var rect2 = new Rectangle { Height = 20, Width = 20 };
@@ -86,6 +90,7 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void LeftOf_Measures_Correctly()
{
+ using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
var rect1 = new Rectangle { Height = 20, Width = 20 };
var rect2 = new Rectangle { Height = 20, Width = 20 };
@@ -111,6 +116,7 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Above_Measures_Correctly()
{
+ using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
var rect1 = new Rectangle { Height = 20, Width = 20 };
var rect2 = new Rectangle { Height = 20, Width = 20 };
diff --git a/tests/Avalonia.Controls.UnitTests/Shapes/EllipseTests.cs b/tests/Avalonia.Controls.UnitTests/Shapes/EllipseTests.cs
new file mode 100644
index 0000000000..626945894f
--- /dev/null
+++ b/tests/Avalonia.Controls.UnitTests/Shapes/EllipseTests.cs
@@ -0,0 +1,57 @@
+using Avalonia.Controls.Shapes;
+using Avalonia.Media;
+using Avalonia.UnitTests;
+using Xunit;
+
+namespace Avalonia.Controls.UnitTests.Shapes
+{
+ public class EllipseTests
+ {
+ [Fact]
+ public void Measure_Does_Not_Set_RenderedGeometry_Rect()
+ {
+ using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
+
+ var target = new Ellipse();
+
+ target.Measure(new Size(100, 100));
+
+ var geometry = Assert.IsType(target.RenderedGeometry);
+ Assert.Equal(default, geometry.Rect);
+ }
+
+ [Fact]
+ public void Arrange_Sets_RenderedGeometry_Properties()
+ {
+ using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
+
+ var target = new Ellipse();
+
+ target.Measure(new Size(100, 100));
+ target.Arrange(new Rect(0, 0, 100, 100));
+
+ var geometry = Assert.IsType(target.RenderedGeometry);
+ Assert.Equal(new Rect(0, 0, 100, 100), geometry.Rect);
+ }
+
+ [Fact]
+ public void Rearranging_Updates_RenderedGeometry_Rect()
+ {
+ using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
+
+ var target = new Ellipse();
+
+ target.Measure(new Size(100, 100));
+ target.Arrange(new Rect(0, 0, 100, 100));
+
+ var geometry = Assert.IsType(target.RenderedGeometry);
+ Assert.Equal(new Rect(0, 0, 100, 100), geometry.Rect);
+
+ target.Measure(new Size(200, 200));
+ target.Arrange(new Rect(0, 0, 200, 200));
+
+ geometry = Assert.IsType(target.RenderedGeometry);
+ Assert.Equal(new Rect(0, 0, 200, 200), geometry.Rect);
+ }
+ }
+}
diff --git a/tests/Avalonia.Controls.UnitTests/Shapes/PathTests.cs b/tests/Avalonia.Controls.UnitTests/Shapes/PathTests.cs
index 88c64e76cc..8b8656a76b 100644
--- a/tests/Avalonia.Controls.UnitTests/Shapes/PathTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/Shapes/PathTests.cs
@@ -34,5 +34,149 @@ namespace Avalonia.Controls.UnitTests.Shapes
root.Child = null;
}
+
+ [Theory]
+ [InlineData(Stretch.None, 100, 200)]
+ [InlineData(Stretch.Fill, 500, 500)]
+ [InlineData(Stretch.Uniform, 250, 500)]
+ [InlineData(Stretch.UniformToFill, 500, 500)]
+ public void Calculates_Correct_DesiredSize_For_Finite_Bounds(Stretch stretch, double expectedWidth, double expectedHeight)
+ {
+ using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
+
+ var target = new Path()
+ {
+ Data = new RectangleGeometry { Rect = new Rect(0, 0, 100, 200) },
+ Stretch = stretch,
+ };
+
+ target.Measure(new Size(500, 500));
+
+ Assert.Equal(new Size(expectedWidth, expectedHeight), target.DesiredSize);
+ }
+
+ [Theory]
+ [InlineData(Stretch.None)]
+ [InlineData(Stretch.Fill)]
+ [InlineData(Stretch.Uniform)]
+ [InlineData(Stretch.UniformToFill)]
+ public void Calculates_Correct_DesiredSize_For_Infinite_Bounds(Stretch stretch)
+ {
+ using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
+
+ var target = new Path()
+ {
+ Data = new RectangleGeometry { Rect = new Rect(0, 0, 100, 200) },
+ Stretch = stretch,
+ };
+
+ target.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
+
+ Assert.Equal(new Size(100, 200), target.DesiredSize);
+ }
+
+ [Fact]
+ public void Measure_Does_Not_Update_RenderedGeometry_Transform()
+ {
+ using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
+
+ var target = new Path
+ {
+ Data = new RectangleGeometry { Rect = new Rect(0, 0, 100, 200) },
+ Stretch = Stretch.Fill,
+ };
+
+ target.Measure(new Size(500, 500));
+
+ Assert.Null(target.RenderedGeometry.Transform);
+ }
+
+ [Theory]
+ [InlineData(Stretch.None, 1, 1)]
+ [InlineData(Stretch.Fill, 5, 2.5)]
+ [InlineData(Stretch.Uniform, 2.5, 2.5)]
+ [InlineData(Stretch.UniformToFill, 5, 5)]
+ public void Arrange_Updates_RenderedGeometry_Transform(Stretch stretch, double expectedScaleX, double expectedScaleY)
+ {
+ using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
+
+ var target = new Path
+ {
+ Data = new RectangleGeometry { Rect = new Rect(0, 0, 100, 200) },
+ Stretch = stretch,
+ };
+
+ target.Measure(new Size(500, 500));
+ target.Arrange(new Rect(0, 0, 500, 500));
+
+ if (expectedScaleX == 1 && expectedScaleY == 1)
+ {
+ Assert.Null(target.RenderedGeometry.Transform);
+ }
+ else
+ {
+ Assert.Equal(Matrix.CreateScale(expectedScaleX, expectedScaleY), target.RenderedGeometry.Transform.Value);
+ }
+ }
+
+ [Fact]
+ public void Arrange_Reserves_All_Of_Arrange_Rect()
+ {
+ using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
+
+ RectangleGeometry geometry;
+ var target = new Path
+ {
+ Data = geometry = new RectangleGeometry { Rect = new Rect(0, 0, 100, 200) },
+ Stretch = Stretch.Uniform,
+ };
+
+ target.Measure(new Size(400, 400));
+ target.Arrange(new Rect(0, 0, 400, 400));
+
+ Assert.Equal(new Rect(0, 0, 100, 200), geometry.Rect);
+ Assert.Equal(Matrix.CreateScale(2, 2), target.RenderedGeometry.Transform.Value);
+ Assert.Equal(new Rect(0, 0, 400, 400), target.Bounds);
+ }
+
+ [Fact]
+ public void Measure_Without_Arrange_Does_Not_Clear_RenderedGeometry_Transform()
+ {
+ using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
+
+ var target = new Path
+ {
+ Data = new RectangleGeometry { Rect = new Rect(0, 0, 100, 100) },
+ Stretch = Stretch.Fill,
+ };
+
+ target.Measure(new Size(200, 200));
+ target.Arrange(new Rect(0, 0, 200, 200));
+
+ Assert.Equal(Matrix.CreateScale(2, 2), target.RenderedGeometry.Transform.Value);
+
+ target.Measure(new Size(300, 300));
+
+ Assert.Equal(Matrix.CreateScale(2, 2), target.RenderedGeometry.Transform.Value);
+ }
+
+ [Fact]
+ public void Arrange_Without_Measure_Updates_RenderedGeometry_Transform()
+ {
+ using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
+
+ var target = new Path
+ {
+ Data = new RectangleGeometry { Rect = new Rect(0, 0, 100, 100) },
+ Stretch = Stretch.Fill,
+ };
+
+ target.Measure(new Size(200, 200));
+ target.Arrange(new Rect(0, 0, 200, 200));
+ Assert.Equal(Matrix.CreateScale(2, 2), target.RenderedGeometry.Transform.Value);
+
+ target.Arrange(new Rect(0, 0, 300, 300));
+ Assert.Equal(Matrix.CreateScale(3, 3), target.RenderedGeometry.Transform.Value);
+ }
}
}
diff --git a/tests/Avalonia.Controls.UnitTests/Shapes/RectangleTests.cs b/tests/Avalonia.Controls.UnitTests/Shapes/RectangleTests.cs
index 0ec73edec0..8d8ce10d4c 100644
--- a/tests/Avalonia.Controls.UnitTests/Shapes/RectangleTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/Shapes/RectangleTests.cs
@@ -1,7 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using Avalonia.Controls.Shapes;
+using Avalonia.Controls.Shapes;
using Avalonia.Media;
using Avalonia.UnitTests;
using Moq;
@@ -11,6 +8,53 @@ namespace Avalonia.Controls.UnitTests.Shapes
{
public class RectangleTests
{
+ [Fact]
+ public void Measure_Does_Not_Set_RenderedGeometry_Rect()
+ {
+ using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
+
+ var target = new Rectangle();
+
+ target.Measure(new Size(100, 100));
+
+ var geometry = Assert.IsType(target.RenderedGeometry);
+ Assert.Equal(Rect.Empty, geometry.Rect);
+ }
+
+ [Fact]
+ public void Arrange_Sets_RenderedGeometry_Rect()
+ {
+ using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
+
+ var target = new Rectangle();
+
+ target.Measure(new Size(100, 100));
+ target.Arrange(new Rect(0, 0, 100, 100));
+
+ var geometry = Assert.IsType(target.RenderedGeometry);
+ Assert.Equal(new Rect(0, 0, 100, 100), geometry.Rect);
+ }
+
+ [Fact]
+ public void Rearranging_Updates_RenderedGeometry_Rect()
+ {
+ using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
+
+ var target = new Rectangle();
+
+ target.Measure(new Size(100, 100));
+ target.Arrange(new Rect(0, 0, 100, 100));
+
+ var geometry = Assert.IsType(target.RenderedGeometry);
+ Assert.Equal(new Rect(0, 0, 100, 100), geometry.Rect);
+
+ target.Measure(new Size(200, 200));
+ target.Arrange(new Rect(0, 0, 200, 200));
+
+ geometry = Assert.IsType(target.RenderedGeometry);
+ Assert.Equal(new Rect(0, 0, 200, 200), geometry.Rect);
+ }
+
[Fact]
public void Changing_Fill_Brush_Color_Should_Invalidate_Visual()
{
@@ -21,7 +65,7 @@ namespace Avalonia.Controls.UnitTests.Shapes
var root = new TestRoot(target);
var renderer = Mock.Get(root.Renderer);
- renderer.ResetCalls();
+ renderer.Invocations.Clear();
((SolidColorBrush)target.Fill).Color = Colors.Green;
@@ -38,7 +82,7 @@ namespace Avalonia.Controls.UnitTests.Shapes
var root = new TestRoot(target);
var renderer = Mock.Get(root.Renderer);
- renderer.ResetCalls();
+ renderer.Invocations.Clear();
((SolidColorBrush)target.Stroke).Color = Colors.Green;
diff --git a/tests/Avalonia.Controls.UnitTests/TextBlockTests.cs b/tests/Avalonia.Controls.UnitTests/TextBlockTests.cs
index 2d2c197220..b180a536a5 100644
--- a/tests/Avalonia.Controls.UnitTests/TextBlockTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/TextBlockTests.cs
@@ -37,7 +37,7 @@ namespace Avalonia.Controls.UnitTests
var root = new TestRoot(target);
var renderer = Mock.Get(root.Renderer);
- renderer.ResetCalls();
+ renderer.Invocations.Clear();
((SolidColorBrush)target.Background).Color = Colors.Green;
@@ -54,7 +54,7 @@ namespace Avalonia.Controls.UnitTests
var root = new TestRoot(target);
var renderer = Mock.Get(root.Renderer);
- renderer.ResetCalls();
+ renderer.Invocations.Clear();
((SolidColorBrush)target.Foreground).Color = Colors.Green;
diff --git a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs
index 9253d8a07f..b805683393 100644
--- a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs
@@ -2,7 +2,9 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
+using System.ComponentModel;
using System.Linq;
+using System.Runtime.CompilerServices;
using Avalonia.Collections;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Templates;
@@ -14,7 +16,7 @@ using Avalonia.Interactivity;
using Avalonia.LogicalTree;
using Avalonia.Styling;
using Avalonia.UnitTests;
-using ReactiveUI;
+using JetBrains.Annotations;
using Xunit;
namespace Avalonia.Controls.UnitTests
@@ -480,8 +482,12 @@ namespace Avalonia.Controls.UnitTests
var selectedValues = new List();
- dataContext.WhenAnyValue(x => x.SelectedItem)
- .Subscribe(x => selectedValues.Add(x));
+ dataContext.PropertyChanged += (_, e) =>
+ {
+ if (e.PropertyName == nameof(TestDataContext.SelectedItem))
+ selectedValues.Add(dataContext.SelectedItem);
+ };
+ selectedValues.Add(dataContext.SelectedItem);
_mouse.Click((Interactive)target.Presenter.Panel.Children[0], MouseButton.Left);
_mouse.Click((Interactive)target.Presenter.Panel.Children[2], MouseButton.Left);
@@ -1326,7 +1332,7 @@ namespace Avalonia.Controls.UnitTests
{
}
- private class TestDataContext : ReactiveObject
+ private class TestDataContext : INotifyPropertyChanged
{
private string _selectedItem;
@@ -1342,9 +1348,13 @@ namespace Avalonia.Controls.UnitTests
get { return _selectedItem; }
set
{
- this.RaiseAndSetIfChanged(ref _selectedItem, value);
+ _selectedItem = value;
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedItem)));
}
}
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
}
}
}
diff --git a/tests/Avalonia.Controls.UnitTests/ViewboxTests.cs b/tests/Avalonia.Controls.UnitTests/ViewboxTests.cs
index ad0f318d2f..e005bafbf9 100644
--- a/tests/Avalonia.Controls.UnitTests/ViewboxTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/ViewboxTests.cs
@@ -1,5 +1,6 @@
using Avalonia.Controls.Shapes;
using Avalonia.Media;
+using Avalonia.UnitTests;
using Xunit;
namespace Avalonia.Controls.UnitTests
@@ -9,6 +10,8 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Viewbox_Stretch_Uniform_Child()
{
+ using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
+
var target = new Viewbox() { Child = new Rectangle() { Width = 100, Height = 50 } };
target.Measure(new Size(200, 200));
@@ -25,6 +28,8 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Viewbox_Stretch_None_Child()
{
+ using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
+
var target = new Viewbox() { Stretch = Stretch.None, Child = new Rectangle() { Width = 100, Height = 50 } };
target.Measure(new Size(200, 200));
@@ -41,6 +46,8 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Viewbox_Stretch_Fill_Child()
{
+ using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
+
var target = new Viewbox() { Stretch = Stretch.Fill, Child = new Rectangle() { Width = 100, Height = 50 } };
target.Measure(new Size(200, 200));
@@ -57,6 +64,8 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Viewbox_Stretch_UniformToFill_Child()
{
+ using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
+
var target = new Viewbox() { Stretch = Stretch.UniformToFill, Child = new Rectangle() { Width = 100, Height = 50 } };
target.Measure(new Size(200, 200));
@@ -73,6 +82,8 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Viewbox_Stretch_Uniform_Child_With_Unrestricted_Width()
{
+ using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
+
var target = new Viewbox() { Child = new Rectangle() { Width = 100, Height = 50 } };
target.Measure(new Size(double.PositiveInfinity, 200));
@@ -89,6 +100,8 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Viewbox_Stretch_Uniform_Child_With_Unrestricted_Height()
{
+ using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
+
var target = new Viewbox() { Child = new Rectangle() { Width = 100, Height = 50 } };
target.Measure(new Size(200, double.PositiveInfinity));
diff --git a/tests/Avalonia.DesignerSupport.TestApp/App.xaml.cs b/tests/Avalonia.DesignerSupport.TestApp/App.xaml.cs
index 653a51232b..97b6b9021a 100644
--- a/tests/Avalonia.DesignerSupport.TestApp/App.xaml.cs
+++ b/tests/Avalonia.DesignerSupport.TestApp/App.xaml.cs
@@ -1,8 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
namespace Avalonia.DesignerSupport.TestApp
@@ -13,5 +9,12 @@ namespace Avalonia.DesignerSupport.TestApp
{
AvaloniaXamlLoader.Load(this);
}
+
+ public override void OnFrameworkInitializationCompleted()
+ {
+ if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+ desktop.MainWindow = new MainWindow();
+ base.OnFrameworkInitializationCompleted();
+ }
}
}
diff --git a/tests/Avalonia.DesignerSupport.TestApp/Program.cs b/tests/Avalonia.DesignerSupport.TestApp/Program.cs
index c7c60a4eca..b8428511b3 100644
--- a/tests/Avalonia.DesignerSupport.TestApp/Program.cs
+++ b/tests/Avalonia.DesignerSupport.TestApp/Program.cs
@@ -1,24 +1,14 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using Avalonia.Controls;
-
-namespace Avalonia.DesignerSupport.TestApp
+namespace Avalonia.DesignerSupport.TestApp
{
static class Program
{
///
/// The main entry point for the application.
///
- static void Main()
- {
- BuildAvaloniaApp().Start();
- }
+ public static int Main(string[] args)
+ => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
- private static AppBuilder BuildAvaloniaApp()
- {
- return AppBuilder.Configure().UsePlatformDetect();
- }
+ public static AppBuilder BuildAvaloniaApp()
+ => AppBuilder.Configure().UsePlatformDetect();
}
}
diff --git a/tests/Avalonia.Layout.UnitTests/LayoutableTests.cs b/tests/Avalonia.Layout.UnitTests/LayoutableTests.cs
index 44a5af94b9..a8aa0bbf0e 100644
--- a/tests/Avalonia.Layout.UnitTests/LayoutableTests.cs
+++ b/tests/Avalonia.Layout.UnitTests/LayoutableTests.cs
@@ -112,7 +112,7 @@ namespace Avalonia.Layout.UnitTests
root.Measure(Size.Infinity);
root.Arrange(new Rect(root.DesiredSize));
- target.ResetCalls();
+ target.Invocations.Clear();
control.InvalidateMeasure();
control.InvalidateMeasure();
@@ -133,7 +133,7 @@ namespace Avalonia.Layout.UnitTests
root.Measure(Size.Infinity);
root.Arrange(new Rect(root.DesiredSize));
- target.ResetCalls();
+ target.Invocations.Clear();
control.InvalidateArrange();
control.InvalidateArrange();
@@ -163,7 +163,7 @@ namespace Avalonia.Layout.UnitTests
Assert.False(control.IsMeasureValid);
Assert.True(root.IsMeasureValid);
- target.ResetCalls();
+ target.Invocations.Clear();
root.Child = control;
diff --git a/tests/Avalonia.Layout.UnitTests/LayoutableTests_EffectiveViewportChanged.cs b/tests/Avalonia.Layout.UnitTests/LayoutableTests_EffectiveViewportChanged.cs
index 504e3fa585..f1cec24516 100644
--- a/tests/Avalonia.Layout.UnitTests/LayoutableTests_EffectiveViewportChanged.cs
+++ b/tests/Avalonia.Layout.UnitTests/LayoutableTests_EffectiveViewportChanged.cs
@@ -15,7 +15,9 @@ namespace Avalonia.Layout.UnitTests
[Fact]
public async Task EffectiveViewportChanged_Not_Raised_When_Control_Added_To_Tree()
{
+#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
await RunOnUIThread.Execute(async () =>
+#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
{
var root = CreateRoot();
var target = new Canvas();
diff --git a/tests/Avalonia.Layout.UnitTests/ShapeLayoutTests.cs b/tests/Avalonia.Layout.UnitTests/ShapeLayoutTests.cs
deleted file mode 100644
index 2ccd6fb04c..0000000000
--- a/tests/Avalonia.Layout.UnitTests/ShapeLayoutTests.cs
+++ /dev/null
@@ -1,113 +0,0 @@
-using System.Collections.Generic;
-using Avalonia.Controls;
-using Avalonia.Controls.Shapes;
-using Avalonia.Media;
-using Avalonia.Platform;
-using Avalonia.UnitTests;
-using Xunit;
-
-namespace Avalonia.Layout.UnitTests
-
-{
- public class ShapeLayoutTests : TestWithServicesBase
- {
-
- public ShapeLayoutTests()
- {
- AvaloniaLocator.CurrentMutable
- .Bind().ToSingleton();
- }
-
- [Fact]
- public void Shape_Transformation_Calculation_Should_Be_Deferred_To_Arrange_When_Strech_Is_Fill_And_Aviable_Size_Is_Infinite()
- {
- var shape = new Polygon()
- {
- Points = new List
- {
- new Point(0, 0),
- new Point(10, 5),
- new Point(0, 10)
- },
- Stretch = Stretch.Fill
- };
-
- var availableSize = new Size(double.PositiveInfinity, double.PositiveInfinity);
- shape.Measure(availableSize);
- Geometry postMeasureGeometry = shape.RenderedGeometry;
- Transform postMeasureTransform = postMeasureGeometry.Transform;
-
- var finalSize = new Size(100, 50);
- var finalRect = new Rect(finalSize);
- shape.Arrange(finalRect);
-
- Geometry postArrangeGeometry = shape.RenderedGeometry;
- Transform postArrangeTransform = postArrangeGeometry.Transform;
-
- Assert.NotEqual(postMeasureGeometry, postArrangeGeometry);
- Assert.NotEqual(postMeasureTransform, postArrangeTransform);
- Assert.Equal(finalSize, shape.Bounds.Size);
- }
-
- [Fact]
- public void Shape_Transformation_Calculation_Should_Not_Be_Deferred_To_Arrange_When_Strech_Is_Fill_And_Aviable_Size_Is_Finite()
- {
- var shape = new Polygon()
- {
- Points = new List
- {
- new Point(0, 0),
- new Point(10, 5),
- new Point(0, 10)
- },
- Stretch = Stretch.Fill
- };
-
- var availableSize = new Size(100, 50);
- shape.Measure(availableSize);
- Geometry postMeasureGeometry = shape.RenderedGeometry;
- Transform postMeasureTransform = postMeasureGeometry.Transform;
-
- var finalRect = new Rect(availableSize);
- shape.Arrange(finalRect);
-
- Geometry postArrangeGeometry = shape.RenderedGeometry;
- Transform postArrangeTransform = postArrangeGeometry.Transform;
-
- Assert.Equal(postMeasureGeometry, postArrangeGeometry);
- Assert.Equal(postMeasureTransform, postArrangeTransform);
- Assert.Equal(availableSize, shape.Bounds.Size);
- }
-
- [Fact]
- public void Shape_Transformation_Calculation_Should_Not_Be_Deferred_To_Arrange_When_Strech_Is_None()
- {
- var shape = new Polygon()
- {
- Points = new List
- {
- new Point(0, 0),
- new Point(10, 5),
- new Point(0, 10)
- },
- Stretch = Stretch.None
- };
-
- var availableSize = new Size(double.PositiveInfinity, double.PositiveInfinity);
- shape.Measure(availableSize);
- Geometry postMeasureGeometry = shape.RenderedGeometry;
- Transform postMeasureTransform = postMeasureGeometry.Transform;
-
- var finalSize = new Size(100, 50);
- var finalRect = new Rect(finalSize);
- shape.Arrange(finalRect);
-
- Geometry postArrangeGeometry = shape.RenderedGeometry;
- Transform postArrangeTransform = postArrangeGeometry.Transform;
-
- Assert.Equal(postMeasureGeometry, postArrangeGeometry);
- Assert.Equal(postMeasureTransform, postArrangeTransform);
- Assert.Equal(finalSize, shape.Bounds.Size);
- }
- }
-}
diff --git a/tests/Avalonia.LeakTests/ControlTests.cs b/tests/Avalonia.LeakTests/ControlTests.cs
index 0c7b966f29..b03ae00cfe 100644
--- a/tests/Avalonia.LeakTests/ControlTests.cs
+++ b/tests/Avalonia.LeakTests/ControlTests.cs
@@ -460,7 +460,7 @@ namespace Avalonia.LeakTests
AttachShowAndDetachContextMenu(window);
- Mock.Get(window.PlatformImpl).ResetCalls();
+ Mock.Get(window.PlatformImpl).Invocations.Clear();
dotMemory.Check(memory =>
Assert.Equal(initialMenuCount, memory.GetObjects(where => where.Type.Is()).ObjectsCount));
dotMemory.Check(memory =>
@@ -505,7 +505,7 @@ namespace Avalonia.LeakTests
BuildAndShowContextMenu(window);
BuildAndShowContextMenu(window);
- Mock.Get(window.PlatformImpl).ResetCalls();
+ Mock.Get(window.PlatformImpl).Invocations.Clear();
dotMemory.Check(memory =>
Assert.Equal(initialMenuCount, memory.GetObjects(where => where.Type.Is()).ObjectsCount));
dotMemory.Check(memory =>
@@ -601,8 +601,9 @@ namespace Avalonia.LeakTests
{
public bool DrawFps { get; set; }
public bool DrawDirtyRects { get; set; }
+#pragma warning disable CS0067
public event EventHandler SceneInvalidated;
-
+#pragma warning restore CS0067
public void AddDirty(IVisual visual)
{
}
diff --git a/tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_Negation.cs b/tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_Negation.cs
index 0be3bbbb9f..0028502eeb 100644
--- a/tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_Negation.cs
+++ b/tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_Negation.cs
@@ -168,10 +168,23 @@ namespace Avalonia.Markup.UnitTests.Parsers
private class Test : INotifyDataErrorInfo
{
+ private string _dataValidationError;
+
public bool Foo { get; set; }
public object Bar { get; set; }
- public string DataValidationError { get; set; }
+ public string DataValidationError
+ {
+ get => _dataValidationError;
+ set
+ {
+ if (value == _dataValidationError)
+ return;
+ _dataValidationError = value;
+ ErrorsChanged?
+ .Invoke(this, new DataErrorsChangedEventArgs(nameof(DataValidationError)));
+ }
+ }
public bool HasErrors => !string.IsNullOrWhiteSpace(DataValidationError);
public event EventHandler ErrorsChanged;
diff --git a/tests/Avalonia.RenderTests/Controls/TextBlockTests.cs b/tests/Avalonia.RenderTests/Controls/TextBlockTests.cs
index ab769e4ff0..53a2959848 100644
--- a/tests/Avalonia.RenderTests/Controls/TextBlockTests.cs
+++ b/tests/Avalonia.RenderTests/Controls/TextBlockTests.cs
@@ -40,5 +40,59 @@ namespace Avalonia.Direct2D1.RenderTests.Controls
await RenderToFile(target);
CompareImages();
}
+
+
+ [Win32Fact("Has text")]
+ public async Task RestrictedHeight_VerticalAlign()
+ {
+ IControl text(VerticalAlignment verticalAlingnment, bool clip = true, bool restrictHeight = true)
+ {
+ return new Border()
+ {
+ BorderBrush = Brushes.Blue,
+ BorderThickness = new Thickness(1),
+ VerticalAlignment = VerticalAlignment.Center,
+ HorizontalAlignment = HorizontalAlignment.Center,
+ Height = restrictHeight ? 20 : double.NaN,
+ Margin = new Thickness(1),
+ Child = new TextBlock
+ {
+ FontFamily = new FontFamily("Courier New"),
+ Background = Brushes.Red,
+ FontSize = 24,
+ Foreground = Brushes.Black,
+ Text = "L",
+ VerticalAlignment = verticalAlingnment,
+ ClipToBounds = clip
+ }
+ };
+ }
+ Decorator target = new Decorator
+ {
+ Padding = new Thickness(8),
+ Width = 180,
+ Height = 80,
+
+ Child = new StackPanel()
+ {
+ Orientation = Orientation.Horizontal,
+ Children =
+ {
+ text(VerticalAlignment.Stretch, restrictHeight: false),
+ text(VerticalAlignment.Center),
+ text(VerticalAlignment.Stretch),
+ text(VerticalAlignment.Top),
+ text(VerticalAlignment.Bottom),
+ text(VerticalAlignment.Center, clip:false),
+ text(VerticalAlignment.Stretch, clip:false),
+ text(VerticalAlignment.Top, clip:false),
+ text(VerticalAlignment.Bottom, clip:false),
+ }
+ }
+ };
+
+ await RenderToFile(target);
+ CompareImages();
+ }
}
}
diff --git a/tests/Avalonia.RenderTests/Media/BitmapTests.cs b/tests/Avalonia.RenderTests/Media/BitmapTests.cs
index d52539c371..b629304b77 100644
--- a/tests/Avalonia.RenderTests/Media/BitmapTests.cs
+++ b/tests/Avalonia.RenderTests/Media/BitmapTests.cs
@@ -102,7 +102,9 @@ namespace Avalonia.Direct2D1.RenderTests.Media
[InlineData(PixelFormat.Bgra8888), InlineData(PixelFormat.Rgba8888)]
public void WriteableBitmapShouldBeUsable(PixelFormat fmt)
{
+#pragma warning disable CS0618 // Type or member is obsolete
var writeableBitmap = new WriteableBitmap(new PixelSize(256, 256), new Vector(96, 96), fmt);
+#pragma warning restore CS0618 // Type or member is obsolete
var data = new int[256 * 256];
for (int y = 0; y < 256; y++)
diff --git a/tests/Avalonia.Styling.UnitTests/ResourceDictionaryTests.cs b/tests/Avalonia.Styling.UnitTests/ResourceDictionaryTests.cs
index 84b0d09b61..d91d1fab9e 100644
--- a/tests/Avalonia.Styling.UnitTests/ResourceDictionaryTests.cs
+++ b/tests/Avalonia.Styling.UnitTests/ResourceDictionaryTests.cs
@@ -108,7 +108,7 @@ namespace Avalonia.Styling.UnitTests
var target = new ResourceDictionary { { "foo", "bar" } };
((IResourceProvider)target).AddOwner(host.Object);
- host.ResetCalls();
+ host.Invocations.Clear();
((IResourceProvider)target).RemoveOwner(host.Object);
host.Verify(x => x.NotifyHostedResourcesChanged(It.IsAny()));
@@ -120,7 +120,7 @@ namespace Avalonia.Styling.UnitTests
var host = new Mock();
var target = new ResourceDictionary(host.Object);
- host.ResetCalls();
+ host.Invocations.Clear();
target.Add("foo", "bar");
host.Verify(x => x.NotifyHostedResourcesChanged(It.IsAny()));
@@ -132,7 +132,7 @@ namespace Avalonia.Styling.UnitTests
var host = new Mock();
var target = new ResourceDictionary(host.Object);
- host.ResetCalls();
+ host.Invocations.Clear();
target.MergedDictionaries.Add(new ResourceDictionary
{
{ "foo", "bar" },
@@ -149,7 +149,7 @@ namespace Avalonia.Styling.UnitTests
var host = new Mock();
var target = new ResourceDictionary(host.Object);
- host.ResetCalls();
+ host.Invocations.Clear();
target.MergedDictionaries.Add(new ResourceDictionary());
host.Verify(
@@ -169,7 +169,7 @@ namespace Avalonia.Styling.UnitTests
}
};
- host.ResetCalls();
+ host.Invocations.Clear();
target.MergedDictionaries.RemoveAt(0);
host.Verify(
@@ -189,7 +189,7 @@ namespace Avalonia.Styling.UnitTests
}
};
- host.ResetCalls();
+ host.Invocations.Clear();
((IResourceDictionary)target.MergedDictionaries[0]).Add("foo", "bar");
host.Verify(
diff --git a/tests/Avalonia.Styling.UnitTests/SelectorTests_Child.cs b/tests/Avalonia.Styling.UnitTests/SelectorTests_Child.cs
index 8ae556cc6f..d011c16ebb 100644
--- a/tests/Avalonia.Styling.UnitTests/SelectorTests_Child.cs
+++ b/tests/Avalonia.Styling.UnitTests/SelectorTests_Child.cs
@@ -1,12 +1,7 @@
-using System;
using System.Collections.Generic;
-using System.Linq;
-using System.Reactive;
using System.Reactive.Linq;
using System.Threading.Tasks;
-using Avalonia.Collections;
using Avalonia.Controls;
-using Avalonia.Data;
using Avalonia.LogicalTree;
using Xunit;
@@ -85,76 +80,6 @@ namespace Avalonia.Styling.UnitTests
get => Parent;
set => ((ISetLogicalParent)this).SetParent(value);
}
-
- public void ClearValue(AvaloniaProperty property)
- {
- throw new NotImplementedException();
- }
-
- public void ClearValue(AvaloniaProperty property)
- {
- throw new NotImplementedException();
- }
-
- public void AddInheritanceChild(IAvaloniaObject child)
- {
- throw new NotImplementedException();
- }
-
- public void RemoveInheritanceChild(IAvaloniaObject child)
- {
- throw new NotImplementedException();
- }
-
- public void InheritanceParentChanged(StyledPropertyBase property, IAvaloniaObject oldParent, IAvaloniaObject newParent)
- {
- throw new NotImplementedException();
- }
-
- public void InheritedPropertyChanged(AvaloniaProperty property, Optional oldValue, Optional newValue)
- {
- throw new NotImplementedException();
- }
-
- public void ClearValue(StyledPropertyBase property)
- {
- throw new NotImplementedException();
- }
-
- public void ClearValue(DirectPropertyBase property)
- {
- throw new NotImplementedException();
- }
-
- public T GetValue(StyledPropertyBase property)
- {
- throw new NotImplementedException();
- }
-
- public T GetValue(DirectPropertyBase property)
- {
- throw new NotImplementedException();
- }
-
- public void SetValue(StyledPropertyBase property, T value, BindingPriority priority = BindingPriority.LocalValue)
- {
- throw new NotImplementedException();
- }
-
- public void SetValue(DirectPropertyBase property, T value)
- {
- throw new NotImplementedException();
- }
-
- public IDisposable Bind(StyledPropertyBase property, IObservable> source, BindingPriority priority = BindingPriority.LocalValue)
- {
- throw new NotImplementedException();
- }
-
- public IDisposable Bind(DirectPropertyBase property, IObservable> source)
- {
- throw new NotImplementedException();
- }
}
public class TestLogical1 : TestLogical
diff --git a/tests/Avalonia.Styling.UnitTests/SelectorTests_Descendent.cs b/tests/Avalonia.Styling.UnitTests/SelectorTests_Descendent.cs
index 81485f0345..aacf2ce223 100644
--- a/tests/Avalonia.Styling.UnitTests/SelectorTests_Descendent.cs
+++ b/tests/Avalonia.Styling.UnitTests/SelectorTests_Descendent.cs
@@ -1,11 +1,7 @@
-using System;
using System.Linq;
-using System.Reactive;
using System.Reactive.Linq;
using System.Threading.Tasks;
-using Avalonia.Collections;
using Avalonia.Controls;
-using Avalonia.Data;
using Avalonia.LogicalTree;
using Xunit;
@@ -115,76 +111,6 @@ namespace Avalonia.Styling.UnitTests
get => Parent;
set => ((ISetLogicalParent)this).SetParent(value);
}
-
- public void ClearValue(AvaloniaProperty property)
- {
- throw new NotImplementedException();
- }
-
- public void ClearValue(AvaloniaProperty property)
- {
- throw new NotImplementedException();
- }
-
- public void AddInheritanceChild(IAvaloniaObject child)
- {
- throw new NotImplementedException();
- }
-
- public void RemoveInheritanceChild(IAvaloniaObject child)
- {
- throw new NotImplementedException();
- }
-
- public void InheritanceParentChanged(StyledPropertyBase property, IAvaloniaObject oldParent, IAvaloniaObject newParent)
- {
- throw new NotImplementedException();
- }
-
- public void InheritedPropertyChanged(AvaloniaProperty property, Optional oldValue, Optional newValue)
- {
- throw new NotImplementedException();
- }
-
- public void ClearValue(StyledPropertyBase property)
- {
- throw new NotImplementedException();
- }
-
- public void ClearValue(DirectPropertyBase property)
- {
- throw new NotImplementedException();
- }
-
- public T GetValue(StyledPropertyBase property)
- {
- throw new NotImplementedException();
- }
-
- public T GetValue(DirectPropertyBase property)
- {
- throw new NotImplementedException();
- }
-
- public void SetValue(StyledPropertyBase property, T value, BindingPriority priority = BindingPriority.LocalValue)
- {
- throw new NotImplementedException();
- }
-
- public void SetValue(DirectPropertyBase property, T value)
- {
- throw new NotImplementedException();
- }
-
- public IDisposable Bind(StyledPropertyBase property, IObservable> source, BindingPriority priority = BindingPriority.LocalValue)
- {
- throw new NotImplementedException();
- }
-
- public IDisposable Bind(DirectPropertyBase property, IObservable> source)
- {
- throw new NotImplementedException();
- }
}
public class TestLogical1 : TestLogical
diff --git a/tests/Avalonia.Styling.UnitTests/StyleTests.cs b/tests/Avalonia.Styling.UnitTests/StyleTests.cs
index 3e2bd68cf0..df94887340 100644
--- a/tests/Avalonia.Styling.UnitTests/StyleTests.cs
+++ b/tests/Avalonia.Styling.UnitTests/StyleTests.cs
@@ -443,7 +443,7 @@ namespace Avalonia.Styling.UnitTests
var resources = new Mock();
target.Resources = resources.Object;
- host.ResetCalls();
+ host.Invocations.Clear();
((IResourceProvider)target).AddOwner(host.Object);
resources.Verify(x => x.AddOwner(host.Object), Times.Once);
}
diff --git a/tests/Avalonia.Styling.UnitTests/StylesTests.cs b/tests/Avalonia.Styling.UnitTests/StylesTests.cs
index 9d3704c91d..88ab124e01 100644
--- a/tests/Avalonia.Styling.UnitTests/StylesTests.cs
+++ b/tests/Avalonia.Styling.UnitTests/StylesTests.cs
@@ -15,7 +15,7 @@ namespace Avalonia.Styling.UnitTests
var style = new Mock();
var rp = style.As();
- host.ResetCalls();
+ host.Invocations.Clear();
target.Add(style.Object);
rp.Verify(x => x.AddOwner(host.Object));
@@ -29,7 +29,7 @@ namespace Avalonia.Styling.UnitTests
var style = new Mock();
var rp = style.As();
- host.ResetCalls();
+ host.Invocations.Clear();
target.Add(style.Object);
target.Remove(style.Object);
@@ -58,7 +58,7 @@ namespace Avalonia.Styling.UnitTests
var resources = new Mock();
target.Resources = resources.Object;
- host.ResetCalls();
+ host.Invocations.Clear();
((IResourceProvider)target).AddOwner(host.Object);
resources.Verify(x => x.AddOwner(host.Object), Times.Once);
}
@@ -87,7 +87,7 @@ namespace Avalonia.Styling.UnitTests
var resourceProvider = style.As();
target.Add(style.Object);
- host.ResetCalls();
+ host.Invocations.Clear();
((IResourceProvider)target).AddOwner(host.Object);
resourceProvider.Verify(x => x.AddOwner(host.Object), Times.Once);
}
diff --git a/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj b/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj
index b1d89037da..11394c6e5c 100644
--- a/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj
+++ b/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj
@@ -6,6 +6,8 @@
Library
false
latest
+ ..\..\build\avalonia.snk
+ True
diff --git a/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs b/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs
index 08df23cbe1..e73a76357a 100644
--- a/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs
+++ b/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs
@@ -34,7 +34,7 @@ namespace Avalonia.UnitTests
public IGeometryImpl CreateRectangleGeometry(Rect rect)
{
- return Mock.Of();
+ return Mock.Of(x => x.Bounds == rect);
}
public IRenderTarget CreateRenderTarget(IEnumerable surfaces)
diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs b/tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs
index bfcc341eed..e58eea42d8 100644
--- a/tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs
+++ b/tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs
@@ -756,7 +756,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering
private void IgnoreFirstFrame(IRenderLoopTask task, Mock sceneBuilder)
{
RunFrame(task);
- sceneBuilder.ResetCalls();
+ sceneBuilder.Invocations.Clear();
}
private void RunFrame(IRenderLoopTask task)
diff --git a/tests/Avalonia.Visuals.UnitTests/VisualTests.cs b/tests/Avalonia.Visuals.UnitTests/VisualTests.cs
index 97ad608346..447a68aa69 100644
--- a/tests/Avalonia.Visuals.UnitTests/VisualTests.cs
+++ b/tests/Avalonia.Visuals.UnitTests/VisualTests.cs
@@ -173,7 +173,7 @@ namespace Avalonia.Visuals.UnitTests
};
root.Child = child;
- renderer.ResetCalls();
+ renderer.Invocations.Clear();
root.Child = null;
renderer.Verify(x => x.AddDirty(child));
diff --git a/tests/TestFiles/Direct2D1/Controls/TextBlock/RestrictedHeight_VerticalAlign.expected.png b/tests/TestFiles/Direct2D1/Controls/TextBlock/RestrictedHeight_VerticalAlign.expected.png
new file mode 100644
index 0000000000..c5a0a14e52
Binary files /dev/null and b/tests/TestFiles/Direct2D1/Controls/TextBlock/RestrictedHeight_VerticalAlign.expected.png differ
diff --git a/tests/TestFiles/Skia/Controls/TextBlock/RestrictedHeight_VerticalAlign.expected.png b/tests/TestFiles/Skia/Controls/TextBlock/RestrictedHeight_VerticalAlign.expected.png
new file mode 100644
index 0000000000..c5a0a14e52
Binary files /dev/null and b/tests/TestFiles/Skia/Controls/TextBlock/RestrictedHeight_VerticalAlign.expected.png differ