diff --git a/Avalonia.sln b/Avalonia.sln
index ee3bc05f0e..16de32d661 100644
--- a/Avalonia.sln
+++ b/Avalonia.sln
@@ -262,9 +262,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SafeAreaDemo.iOS", "samples
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Headless", "Headless", "{FF237916-7150-496B-89ED-6CA3292896E7}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Headless.XUnit", "src\Headless\Avalonia.Headless.XUnit\Avalonia.Headless.XUnit.csproj", "{F47F8316-4D4B-4026-8EF3-16B2CFDA8119}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Headless.XUnit", "src\Headless\Avalonia.Headless.XUnit\Avalonia.Headless.XUnit.csproj", "{F47F8316-4D4B-4026-8EF3-16B2CFDA8119}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Headless.UnitTests", "tests\Avalonia.Headless.UnitTests\Avalonia.Headless.UnitTests.csproj", "{3B2405E8-9E7A-46D1-8E2D-EF9ED124C9F2}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Headless.UnitTests", "tests\Avalonia.Headless.UnitTests\Avalonia.Headless.UnitTests.csproj", "{3B2405E8-9E7A-46D1-8E2D-EF9ED124C9F2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -605,14 +605,6 @@ Global
{13F1135D-BA1A-435C-9C5B-A368D1D63DE4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{13F1135D-BA1A-435C-9C5B-A368D1D63DE4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{13F1135D-BA1A-435C-9C5B-A368D1D63DE4}.Release|Any CPU.Build.0 = Release|Any CPU
- {F47F8316-4D4B-4026-8EF3-16B2CFDA8119}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {F47F8316-4D4B-4026-8EF3-16B2CFDA8119}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {F47F8316-4D4B-4026-8EF3-16B2CFDA8119}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {F47F8316-4D4B-4026-8EF3-16B2CFDA8119}.Release|Any CPU.Build.0 = Release|Any CPU
- {3B2405E8-9E7A-46D1-8E2D-EF9ED124C9F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {3B2405E8-9E7A-46D1-8E2D-EF9ED124C9F2}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {3B2405E8-9E7A-46D1-8E2D-EF9ED124C9F2}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {3B2405E8-9E7A-46D1-8E2D-EF9ED124C9F2}.Release|Any CPU.Build.0 = Release|Any CPU
{A82AD1BC-EBE6-4FC3-A13B-D52A50297533}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A82AD1BC-EBE6-4FC3-A13B-D52A50297533}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A82AD1BC-EBE6-4FC3-A13B-D52A50297533}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -637,6 +629,14 @@ Global
{FC956F9A-4C3A-4A1A-ACDD-BB54DCB661DD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FC956F9A-4C3A-4A1A-ACDD-BB54DCB661DD}.Release|Any CPU.Build.0 = Release|Any CPU
{FC956F9A-4C3A-4A1A-ACDD-BB54DCB661DD}.Release|Any CPU.Deploy.0 = Release|Any CPU
+ {F47F8316-4D4B-4026-8EF3-16B2CFDA8119}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F47F8316-4D4B-4026-8EF3-16B2CFDA8119}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F47F8316-4D4B-4026-8EF3-16B2CFDA8119}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F47F8316-4D4B-4026-8EF3-16B2CFDA8119}.Release|Any CPU.Build.0 = Release|Any CPU
+ {3B2405E8-9E7A-46D1-8E2D-EF9ED124C9F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {3B2405E8-9E7A-46D1-8E2D-EF9ED124C9F2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {3B2405E8-9E7A-46D1-8E2D-EF9ED124C9F2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {3B2405E8-9E7A-46D1-8E2D-EF9ED124C9F2}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -681,6 +681,8 @@ Global
{AF915D5C-AB00-4EA0-B5E6-001F4AE84E68} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{4D36CEC8-53F2-40A5-9A37-79AAE356E2DA} = {86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B}
{351337F5-D66F-461B-A957-4EF60BDB4BA6} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
+ {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC} = {FF237916-7150-496B-89ED-6CA3292896E7}
+ {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E} = {FF237916-7150-496B-89ED-6CA3292896E7}
{909A8CBD-7D0E-42FD-B841-022AD8925820} = {8B6A8209-894F-4BA1-B880-965FD453982C}
{11BE52AF-E2DD-4CF0-B19A-05285ACAF571} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{BC594FD5-4AF2-409E-A1E6-04123F54D7C5} = {9B9E3891-2366-4253-A952-D08BCEB71098}
@@ -704,10 +706,6 @@ Global
{C810060E-3809-4B74-A125-F11533AF9C1B} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{C692FE73-43DB-49CE-87FC-F03ED61F25C9} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637}
{F4E36AA8-814E-4704-BC07-291F70F45193} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
- {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC} = {FF237916-7150-496B-89ED-6CA3292896E7}
- {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E} = {FF237916-7150-496B-89ED-6CA3292896E7}
- {F47F8316-4D4B-4026-8EF3-16B2CFDA8119} = {FF237916-7150-496B-89ED-6CA3292896E7}
- {3B2405E8-9E7A-46D1-8E2D-EF9ED124C9F2} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{DDA28789-C21A-4654-86CE-D01E81F095C5} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637}
{2D7C812B-7E73-4252-8EFD-BC8A4D5CCB9F} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{A82AD1BC-EBE6-4FC3-A13B-D52A50297533} = {9B9E3891-2366-4253-A952-D08BCEB71098}
@@ -715,6 +713,8 @@ Global
{22E3BC08-EAF7-4889-BDC4-B4D3046C4E2D} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{4CDAD037-34A2-4CCF-A03A-C6C7B988A572} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{FC956F9A-4C3A-4A1A-ACDD-BB54DCB661DD} = {9B9E3891-2366-4253-A952-D08BCEB71098}
+ {F47F8316-4D4B-4026-8EF3-16B2CFDA8119} = {FF237916-7150-496B-89ED-6CA3292896E7}
+ {3B2405E8-9E7A-46D1-8E2D-EF9ED124C9F2} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A}
diff --git a/build/ExternalConsumers.props b/build/ExternalConsumers.props
new file mode 100644
index 0000000000..d79e951330
--- /dev/null
+++ b/build/ExternalConsumers.props
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/native/Avalonia.Native/src/OSX/AvnView.mm b/native/Avalonia.Native/src/OSX/AvnView.mm
index 6d1ff7cf12..86bacfb819 100644
--- a/native/Avalonia.Native/src/OSX/AvnView.mm
+++ b/native/Avalonia.Native/src/OSX/AvnView.mm
@@ -275,7 +275,7 @@
delta.Y = [event deltaY];
}
- uint32 timestamp = static_cast([event timestamp] * 1000);
+ uint64_t timestamp = static_cast([event timestamp] * 1000);
auto modifiers = [self getModifiers:[event modifierFlags]];
if(type != Move ||
@@ -444,7 +444,7 @@
auto key = s_KeyMap[[event keyCode]];
- uint32_t timestamp = static_cast([event timestamp] * 1000);
+ uint64_t timestamp = static_cast([event timestamp] * 1000);
auto modifiers = [self getModifiers:[event modifierFlags]];
if(_parent != nullptr)
@@ -657,7 +657,7 @@
[self unmarkText];
- uint32_t timestamp = static_cast([NSDate timeIntervalSinceReferenceDate] * 1000);
+ uint64_t timestamp = static_cast([NSDate timeIntervalSinceReferenceDate] * 1000);
_lastKeyHandled = _parent->BaseEvents->RawTextInputEvent(timestamp, [text UTF8String]);
diff --git a/native/Avalonia.Native/src/OSX/AvnWindow.mm b/native/Avalonia.Native/src/OSX/AvnWindow.mm
index 16e1486acc..ef50cdab84 100644
--- a/native/Avalonia.Native/src/OSX/AvnWindow.mm
+++ b/native/Avalonia.Native/src/OSX/AvnWindow.mm
@@ -460,7 +460,7 @@
auto point = [self translateLocalPoint:avnPoint];
AvnVector delta = { 0, 0 };
- _parent->BaseEvents->RawMouseEvent(NonClientLeftButtonDown, static_cast([event timestamp] * 1000), AvnInputModifiersNone, point, delta);
+ _parent->BaseEvents->RawMouseEvent(NonClientLeftButtonDown, static_cast([event timestamp] * 1000), AvnInputModifiersNone, point, delta);
}
if(!_isTransitioningToFullScreen)
diff --git a/readme.md b/readme.md
index c8135080fe..6dd556bd0d 100644
--- a/readme.md
+++ b/readme.md
@@ -1,26 +1,43 @@
-[](https://avaloniaui.net/xpf)
+
+
[](https://t.me/Avalonia)
[](https://gitter.im/AvaloniaUI/Avalonia?utm_campaign=pr-badge&utm_content=badge&utm_medium=badge&utm_source=badge) []( https://aka.ms/dotnet-discord) [](https://dev.azure.com/AvaloniaUI/AvaloniaUI/_build/latest?definitionId=4) [](#backers) [](#sponsors) 
[](https://www.nuget.org/packages/Avalonia) [](https://www.nuget.org/packages/Avalonia) 
-# ⚠️ **v11 Update - Pausing community contributions**
-
-for more information see [this](https://github.com/AvaloniaUI/Avalonia/discussions/10599) discussion.
+⚠️ **v11 Update - [Pausing community contributions](https://github.com/AvaloniaUI/Avalonia/discussions/10599)**
## 📖 About
-Avalonia is a cross-platform UI framework for dotnet, providing a flexible styling system and supporting a wide range of Operating Systems such as Windows, Linux, macOS. Avalonia is mature and production ready. We also have in beta release support for iOS, Android and in early stages support for browser via WASM.
+[Avalonia](https://avaloniaui.net) is a cross-platform UI framework for dotnet, providing a flexible styling system and supporting a wide range of platforms such as Windows, macOS, Linux, iOS, Android and WebAssembly. Avalonia is mature and production ready and is used by companies, including [Schneider Electric](https://avaloniaui.net/showcase#se), [Unity](https://avaloniaui.net/showcase#unity), [JetBrains](https://avaloniaui.net/showcase#rider) and [Github](https://avaloniaui.net/showcase#github).
+
+Considered by many to be the spiritual successor to WPF, Avalonia UI provides a familiar, modern development experience for XAML developers creating cross-platform applications. While Avalonia UI is [similar to WPF](https://docs.avaloniaui.net/misc/wpf), it isn't a 1:1 copy, and you'll find plenty of improvements.
-
+For those seeking a cross-platform WPF, we have created [Avalonia XPF](https://avaloniaui.net/xpf), enabling WPF applications to run on macOS and Linux with little to no code changes. Avalonia XPF is a commercial product and is licensed per-app, per-platform.
-To see the status of some of our features, please see our [Roadmap](https://github.com/AvaloniaUI/Avalonia/issues/2239). You can also see what [breaking changes](https://github.com/AvaloniaUI/Avalonia/issues/3538) we have planned and what our [past breaking changes](https://github.com/AvaloniaUI/Avalonia/wiki/Breaking-Changes) have been. [Awesome Avalonia](https://github.com/AvaloniaCommunity/awesome-avalonia) is community-curated list of awesome Avalonia UI tools, libraries, projects and resources. Go and see what people are building with Avalonia!
+#### Roadmap
+To see the status of some of our features, please see our [Roadmap](https://github.com/AvaloniaUI/Avalonia/issues/2239).
+
+#### Breaking Changes
+You can also see what [breaking changes](https://github.com/AvaloniaUI/Avalonia/issues/3538) we have planned and what our [past breaking changes](https://github.com/AvaloniaUI/Avalonia/wiki/Breaking-Changes) have been.
+
+#### Awesome Avalonia
+[Awesome Avalonia](https://github.com/AvaloniaCommunity/awesome-avalonia) is community-curated list of awesome Avalonia UI tools, libraries, projects and resources. Go and see what people are building with Avalonia!
## 🚀 Getting Started
+See our [Get Started](https://avaloniaui.net/GettingStarted) guide to begin developing apps with Avalonia UI.
+
+### Visual Studio
The Avalonia [Visual Studio Extension](https://marketplace.visualstudio.com/items?itemName=AvaloniaTeam.AvaloniaforVisualStudio) contains project and control templates that will help you get started, or you can use the .NET Core CLI. For a starter guide see our [documentation](https://docs.avaloniaui.net/docs/getting-started).
+### JetBrains Rider
+[JetBrains Rider](https://www.jetbrains.com/rider/whatsnew/?mkt_tok=eyJpIjoiTURBNU1HSmhNV0kwTUdFMiIsInQiOiJtNnU2VEc1TlNLa1ZRVkROYmdZYVpYREJsaU1qdUhmS3dxSzRHczdYWHl0RVlTNDMwSFwvNUs3VENTNVM0bVcyNFdaRmVYZzVWTTF1N3VrQWNGTkJreEhlam1hMlB4UVVWcHBGM1dNOUxoXC95YnRQdGgyUXl1YmZCM3h3d3BVWWdBIn0%3D#avalonia-support) now has official support for Avalonia.
+
+Code completion, inspections and refactorings are supported out of the box, for XAML previewer add `https://plugins.jetbrains.com/plugins/dev/14839` to plugin repositories and install [AvaloniaRider](https://github.com/ForNeVeR/AvaloniaRider) plugin.
+
+### Avalonia Packages
Avalonia is delivered via NuGet package manager. You can find the packages here: https://www.nuget.org/packages/Avalonia/
Use these commands in the Package Manager console to install Avalonia manually:
@@ -30,31 +47,26 @@ Install-Package Avalonia.Desktop
```
## Showcase
+[](https://avaloniaui.net/showcase)
-Examples of UIs built with Avalonia
-
-([Lunacy](https://icons8.com/lunacy))
-
-
-([PlasticSCM](https://www.plasticscm.com/))
-
-([WasabiWallet](https://www.wasabiwallet.io/))
-
-## JetBrains Rider
-
-[JetBrains Rider](https://www.jetbrains.com/rider/whatsnew/?mkt_tok=eyJpIjoiTURBNU1HSmhNV0kwTUdFMiIsInQiOiJtNnU2VEc1TlNLa1ZRVkROYmdZYVpYREJsaU1qdUhmS3dxSzRHczdYWHl0RVlTNDMwSFwvNUs3VENTNVM0bVcyNFdaRmVYZzVWTTF1N3VrQWNGTkJreEhlam1hMlB4UVVWcHBGM1dNOUxoXC95YnRQdGgyUXl1YmZCM3h3d3BVWWdBIn0%3D#avalonia-support) now has official support for Avalonia.
-
-Code completion, inspections and refactorings are supported out of the box, for XAML previewer add `https://plugins.jetbrains.com/plugins/dev/14839` to plugin repositories and install [AvaloniaRider](https://github.com/ForNeVeR/AvaloniaRider) plugin.
+See what others have built with Avalonia UI on our [Showcase](https://avaloniaui.net/Showcase). We welcome submissions!
## Bleeding Edge Builds
We also have a [nightly build](https://github.com/AvaloniaUI/Avalonia/wiki/Using-nightly-build-feed) which tracks the current state of master. Although these packages are less stable than the release on NuGet.org, you'll get all the latest features and bugfixes right away and many of our users actually prefer this feed!
-## Documentation
+## Learning
-Documentation can be found at https://docs.avaloniaui.net. We also have a [tutorial](https://docs.avaloniaui.net/docs/getting-started/programming-with-avalonia) over there for newcomers.
+### Documentation
+Documentation can be found at https://docs.avaloniaui.net.
+
+### Tutorials
+We also have a [tutorial](https://docs.avaloniaui.net/docs/getting-started/programming-with-avalonia) over there for newcomers.
+
+### Samples
+We have a [range of samples](https://github.com/AvaloniaUI/Avalonia.Samples) to help you get started.
## Building and Using
@@ -116,3 +128,8 @@ We have a range of [support plans available](https://avaloniaui.net/support) for
## .NET Foundation
This project is supported by the [.NET Foundation](https://dotnetfoundation.org).
+
+## Avalonia XPF
+Unleash the full potential of your existing WPF apps with our cross-platform UI framework, enabling WPF apps to run on macOS and Linux without requiring expensive and risky rewrites.
+
+[](https://avaloniaui.net/xpf)
diff --git a/samples/ControlCatalog/Pages/ComboBoxPage.xaml b/samples/ControlCatalog/Pages/ComboBoxPage.xaml
index 748a46c447..f3f6cfe0af 100644
--- a/samples/ControlCatalog/Pages/ComboBoxPage.xaml
+++ b/samples/ControlCatalog/Pages/ComboBoxPage.xaml
@@ -98,6 +98,21 @@
Inline Item 3
Inline Item 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
WrapSelection
diff --git a/samples/ControlCatalog/ViewModels/ComboBoxPageViewModel.cs b/samples/ControlCatalog/ViewModels/ComboBoxPageViewModel.cs
index d3e4ea7c31..6ab7bb02e3 100644
--- a/samples/ControlCatalog/ViewModels/ComboBoxPageViewModel.cs
+++ b/samples/ControlCatalog/ViewModels/ComboBoxPageViewModel.cs
@@ -16,5 +16,20 @@ namespace ControlCatalog.ViewModels
get => _wrapSelection;
set => this.RaiseAndSetIfChanged(ref _wrapSelection, value);
}
+
+ public ObservableCollection Values { get; set; } = new ObservableCollection
+ {
+ new IdAndName(){ Id = "Id 1", Name = "Name 1" },
+ new IdAndName(){ Id = "Id 2", Name = "Name 2" },
+ new IdAndName(){ Id = "Id 3", Name = "Name 3" },
+ new IdAndName(){ Id = "Id 4", Name = "Name 4" },
+ new IdAndName(){ Id = "Id 5", Name = "Name 5" },
+ };
+ }
+
+ public class IdAndName
+ {
+ public string Id { get; set; }
+ public string Name { get; set; }
}
}
diff --git a/samples/Sandbox/MainWindow.axaml.cs b/samples/Sandbox/MainWindow.axaml.cs
index b8e9f0ff42..e3dda25b29 100644
--- a/samples/Sandbox/MainWindow.axaml.cs
+++ b/samples/Sandbox/MainWindow.axaml.cs
@@ -1,6 +1,7 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Presenters;
+using Avalonia.Input.TextInput;
using Avalonia.Markup.Xaml;
using Avalonia.Win32.WinRT.Composition;
diff --git a/src/Avalonia.Base/Avalonia.Base.csproj b/src/Avalonia.Base/Avalonia.Base.csproj
index 639c27bf03..eafff3b780 100644
--- a/src/Avalonia.Base/Avalonia.Base.csproj
+++ b/src/Avalonia.Base/Avalonia.Base.csproj
@@ -22,6 +22,7 @@
+
diff --git a/src/Avalonia.Base/Data/BindingPriority.cs b/src/Avalonia.Base/Data/BindingPriority.cs
index 5fd5aae43b..cb7f559e0a 100644
--- a/src/Avalonia.Base/Data/BindingPriority.cs
+++ b/src/Avalonia.Base/Data/BindingPriority.cs
@@ -1,4 +1,5 @@
using System;
+using System.ComponentModel;
namespace Avalonia.Data
{
@@ -47,7 +48,7 @@ namespace Avalonia.Data
///
Unset = int.MaxValue,
- [Obsolete("Use Template priority")]
+ [Obsolete("Use Template priority"), EditorBrowsable(EditorBrowsableState.Never)]
TemplatedParent = Template,
}
}
diff --git a/src/Avalonia.Base/Data/Core/Plugins/DataAnnotationsValidationPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/DataAnnotationsValidationPlugin.cs
index ba5f59ea23..bc300386b9 100644
--- a/src/Avalonia.Base/Data/Core/Plugins/DataAnnotationsValidationPlugin.cs
+++ b/src/Avalonia.Base/Data/Core/Plugins/DataAnnotationsValidationPlugin.cs
@@ -10,7 +10,7 @@ namespace Avalonia.Data.Core.Plugins
///
/// Validates properties on that have s.
///
- internal class DataAnnotationsValidationPlugin : IDataValidationPlugin
+ public class DataAnnotationsValidationPlugin : IDataValidationPlugin
{
///
[RequiresUnreferencedCode(TrimmingMessages.DataValidationPluginRequiresUnreferencedCodeMessage)]
diff --git a/src/Avalonia.Base/Data/Core/Plugins/ExceptionValidationPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/ExceptionValidationPlugin.cs
index e60a341309..2bb8da2c74 100644
--- a/src/Avalonia.Base/Data/Core/Plugins/ExceptionValidationPlugin.cs
+++ b/src/Avalonia.Base/Data/Core/Plugins/ExceptionValidationPlugin.cs
@@ -7,7 +7,7 @@ namespace Avalonia.Data.Core.Plugins
///
/// Validates properties that report errors by throwing exceptions.
///
- internal class ExceptionValidationPlugin : IDataValidationPlugin
+ public class ExceptionValidationPlugin : IDataValidationPlugin
{
///
[RequiresUnreferencedCode(TrimmingMessages.DataValidationPluginRequiresUnreferencedCodeMessage)]
diff --git a/src/Avalonia.Base/Data/Core/Plugins/IndeiValidationPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/IndeiValidationPlugin.cs
index 3384a99333..87a2f67ee8 100644
--- a/src/Avalonia.Base/Data/Core/Plugins/IndeiValidationPlugin.cs
+++ b/src/Avalonia.Base/Data/Core/Plugins/IndeiValidationPlugin.cs
@@ -10,7 +10,7 @@ namespace Avalonia.Data.Core.Plugins
///
/// Validates properties on objects that implement .
///
- internal class IndeiValidationPlugin : IDataValidationPlugin
+ public class IndeiValidationPlugin : IDataValidationPlugin
{
private static readonly WeakEvent
ErrorsChangedWeakEvent = WeakEvent.Register(
diff --git a/src/Avalonia.Base/Data/InstancedBinding.cs b/src/Avalonia.Base/Data/InstancedBinding.cs
index c09c31632e..f93813c0b2 100644
--- a/src/Avalonia.Base/Data/InstancedBinding.cs
+++ b/src/Avalonia.Base/Data/InstancedBinding.cs
@@ -1,4 +1,5 @@
using System;
+using System.ComponentModel;
using Avalonia.Reactive;
using ObservableEx = Avalonia.Reactive.Observable;
@@ -49,7 +50,7 @@ namespace Avalonia.Data
///
public IObservable Source { get; }
- [Obsolete("Use Source property")]
+ [Obsolete("Use Source property"), EditorBrowsable(EditorBrowsableState.Never)]
public IObservable Observable => Source;
///
diff --git a/src/Avalonia.Base/Input/DataFormats.cs b/src/Avalonia.Base/Input/DataFormats.cs
index 35d50e669a..f593ed205f 100644
--- a/src/Avalonia.Base/Input/DataFormats.cs
+++ b/src/Avalonia.Base/Input/DataFormats.cs
@@ -1,4 +1,5 @@
using System;
+using System.ComponentModel;
namespace Avalonia.Input
{
@@ -17,7 +18,7 @@ namespace Avalonia.Input
///
/// Dataformat for one or more filenames
///
- [Obsolete("Use DataFormats.Files, this format is supported only on desktop platforms.")]
+ [Obsolete("Use DataFormats.Files, this format is supported only on desktop platforms."), EditorBrowsable(EditorBrowsableState.Never)]
public static readonly string FileNames = nameof(FileNames);
}
}
diff --git a/src/Avalonia.Base/Input/DataObjectExtensions.cs b/src/Avalonia.Base/Input/DataObjectExtensions.cs
index 6af531b0d8..d2e525cd68 100644
--- a/src/Avalonia.Base/Input/DataObjectExtensions.cs
+++ b/src/Avalonia.Base/Input/DataObjectExtensions.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using System.ComponentModel;
using System.Linq;
using Avalonia.Platform.Storage;
@@ -25,7 +26,7 @@ namespace Avalonia.Input
///
/// Collection of file names. If format isn't available, returns null.
///
- [System.Obsolete("Use GetFiles, this method is supported only on desktop platforms.")]
+ [System.Obsolete("Use GetFiles, this method is supported only on desktop platforms."), EditorBrowsable(EditorBrowsableState.Never)]
public static IEnumerable? GetFileNames(this IDataObject dataObject)
{
return (dataObject.Get(DataFormats.FileNames) as IEnumerable)
diff --git a/src/Avalonia.Base/Layout/LayoutManager.cs b/src/Avalonia.Base/Layout/LayoutManager.cs
index f47738f2e4..7873f83edb 100644
--- a/src/Avalonia.Base/Layout/LayoutManager.cs
+++ b/src/Avalonia.Base/Layout/LayoutManager.cs
@@ -21,6 +21,7 @@ namespace Avalonia.Layout
private readonly Layoutable _owner;
private readonly LayoutQueue _toMeasure = new LayoutQueue(v => !v.IsMeasureValid);
private readonly LayoutQueue _toArrange = new LayoutQueue(v => !v.IsArrangeValid);
+ private readonly List _toArrangeAfterMeasure = new();
private readonly Action _executeLayoutPass;
private List? _effectiveViewportChangedListeners;
private bool _disposed;
@@ -266,9 +267,14 @@ namespace Avalonia.Layout
if (!control.IsArrangeValid)
{
- Arrange(control);
+ if (Arrange(control) == ArrangeResult.AncestorMeasureInvalid)
+ _toArrangeAfterMeasure.Add(control);
}
}
+
+ foreach (var i in _toArrangeAfterMeasure)
+ InvalidateArrange(i);
+ _toArrangeAfterMeasure.Clear();
}
private bool Measure(Layoutable control)
@@ -304,19 +310,19 @@ namespace Avalonia.Layout
return true;
}
- private bool Arrange(Layoutable control)
+ private ArrangeResult Arrange(Layoutable control)
{
if (!control.IsVisible || !control.IsAttachedToVisualTree)
- return false;
+ return ArrangeResult.NotVisible;
if (control.VisualParent is Layoutable parent)
{
- if (!Arrange(parent))
- return false;
+ if (Arrange(parent) is var parentResult && parentResult != ArrangeResult.Arranged)
+ return parentResult;
}
if (!control.IsMeasureValid)
- return false;
+ return ArrangeResult.AncestorMeasureInvalid;
if (!control.IsArrangeValid)
{
@@ -332,7 +338,7 @@ namespace Avalonia.Layout
}
}
- return true;
+ return ArrangeResult.Arranged;
}
private void QueueLayoutPass()
@@ -435,5 +441,12 @@ namespace Avalonia.Layout
public Layoutable Listener { get; }
public Rect Viewport { get; set; }
}
+
+ private enum ArrangeResult
+ {
+ Arranged,
+ NotVisible,
+ AncestorMeasureInvalid,
+ }
}
}
diff --git a/src/Avalonia.Base/Media/Color.cs b/src/Avalonia.Base/Media/Color.cs
index 56a3b0d7a5..50c2faacc0 100644
--- a/src/Avalonia.Base/Media/Color.cs
+++ b/src/Avalonia.Base/Media/Color.cs
@@ -6,6 +6,7 @@
// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
using System;
+using System.ComponentModel;
using System.Globalization;
#if !BUILDTASK
using Avalonia.Animation.Animators;
@@ -465,7 +466,7 @@ namespace Avalonia.Media
}
///
- [Obsolete("Use Color.ToUInt32() instead.")]
+ [Obsolete("Use Color.ToUInt32() instead."), EditorBrowsable(EditorBrowsableState.Never)]
public uint ToUint32()
{
return ToUInt32();
diff --git a/src/Avalonia.Base/Media/DrawingContext.cs b/src/Avalonia.Base/Media/DrawingContext.cs
index 18d6968168..02294368c5 100644
--- a/src/Avalonia.Base/Media/DrawingContext.cs
+++ b/src/Avalonia.Base/Media/DrawingContext.cs
@@ -1,10 +1,10 @@
using System;
using System.Collections.Generic;
+using System.ComponentModel;
using Avalonia.Platform;
using Avalonia.Rendering.SceneGraph;
using Avalonia.Threading;
using Avalonia.Utilities;
-using Avalonia.Media.Imaging;
namespace Avalonia.Media
{
@@ -53,12 +53,10 @@ namespace Avalonia.Media
/// The image.
/// The rect in the image to draw.
/// The rect in the output to draw to.
- /// The bitmap interpolation mode.
- public virtual void DrawImage(IImage source, Rect sourceRect, Rect destRect,
- BitmapInterpolationMode bitmapInterpolationMode = default)
+ public virtual void DrawImage(IImage source, Rect sourceRect, Rect destRect)
{
_ = source ?? throw new ArgumentNullException(nameof(source));
- source.Draw(this, sourceRect, destRect, bitmapInterpolationMode);
+ source.Draw(this, sourceRect, destRect);
}
///
@@ -68,8 +66,7 @@ namespace Avalonia.Media
/// The opacity to draw with.
/// The rect in the image to draw.
/// The rect in the output to draw to.
- /// The bitmap interpolation mode.
- internal abstract void DrawBitmap(IRef source, double opacity, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode = BitmapInterpolationMode.Default);
+ internal abstract void DrawBitmap(IRef source, double opacity, Rect sourceRect, Rect destRect);
///
/// Draws a line.
@@ -286,8 +283,7 @@ namespace Avalonia.Media
Opacity,
Clip,
GeometryClip,
- OpacityMask,
- BitmapBlendMode
+ OpacityMask
}
public RestoreState(DrawingContext context, PushedStateType type)
@@ -312,8 +308,6 @@ namespace Avalonia.Media
_context.PopGeometryClipCore();
else if (_type == PushedStateType.OpacityMask)
_context.PopOpacityMaskCore();
- else if (_type == PushedStateType.BitmapBlendMode)
- _context.PopBitmapBlendModeCore();
}
}
@@ -394,16 +388,6 @@ namespace Avalonia.Media
}
protected abstract void PushOpacityMaskCore(IBrush mask, Rect bounds);
- public PushedState PushBitmapBlendMode(BitmapBlendingMode blendingMode)
- {
- PushBitmapBlendMode(blendingMode);
- _states ??= StateStackPool.Get();
- _states.Push(new RestoreState(this, RestoreState.PushedStateType.BitmapBlendMode));
- return new PushedState(this);
- }
-
- protected abstract void PushBitmapBlendModeCore(BitmapBlendingMode blendingMode);
-
///
/// Pushes a matrix transformation.
///
@@ -417,11 +401,11 @@ namespace Avalonia.Media
return new PushedState(this);
}
- [Obsolete("Use PushTransform")]
+ [Obsolete("Use PushTransform"), EditorBrowsable(EditorBrowsableState.Never)]
public PushedState PushPreTransform(Matrix matrix) => PushTransform(matrix);
- [Obsolete("Use PushTransform")]
+ [Obsolete("Use PushTransform"), EditorBrowsable(EditorBrowsableState.Never)]
public PushedState PushPostTransform(Matrix matrix) => PushTransform(matrix);
- [Obsolete("Use PushTransform")]
+ [Obsolete("Use PushTransform"), EditorBrowsable(EditorBrowsableState.Never)]
public PushedState PushTransformContainer() => PushTransform(Matrix.Identity);
@@ -431,7 +415,6 @@ namespace Avalonia.Media
protected abstract void PopGeometryClipCore();
protected abstract void PopOpacityCore();
protected abstract void PopOpacityMaskCore();
- protected abstract void PopBitmapBlendModeCore();
protected abstract void PopTransformCore();
private static bool PenIsVisible(IPen? pen)
diff --git a/src/Avalonia.Base/Media/DrawingGroup.cs b/src/Avalonia.Base/Media/DrawingGroup.cs
index c96d2aad57..5a5bd50c7c 100644
--- a/src/Avalonia.Base/Media/DrawingGroup.cs
+++ b/src/Avalonia.Base/Media/DrawingGroup.cs
@@ -196,13 +196,7 @@ namespace Avalonia.Media
throw new NotImplementedException();
}
- protected override void PushBitmapBlendModeCore(BitmapBlendingMode blendingMode)
- {
- throw new NotImplementedException();
- }
-
- internal override void DrawBitmap(IRef source, double opacity, Rect sourceRect, Rect destRect,
- BitmapInterpolationMode bitmapInterpolationMode = BitmapInterpolationMode.Default)
+ internal override void DrawBitmap(IRef source, double opacity, Rect sourceRect, Rect destRect)
{
throw new NotImplementedException();
}
@@ -321,8 +315,6 @@ namespace Avalonia.Media
protected override void PopOpacityMaskCore() => Pop();
- protected override void PopBitmapBlendModeCore() => Pop();
-
protected override void PopTransformCore() => Pop();
///
diff --git a/src/Avalonia.Base/Media/DrawingImage.cs b/src/Avalonia.Base/Media/DrawingImage.cs
index 52fbd87db7..c83e8eb6ee 100644
--- a/src/Avalonia.Base/Media/DrawingImage.cs
+++ b/src/Avalonia.Base/Media/DrawingImage.cs
@@ -1,6 +1,5 @@
using System;
using Avalonia.Metadata;
-using Avalonia.Media.Imaging;
namespace Avalonia.Media
{
@@ -43,8 +42,7 @@ namespace Avalonia.Media
void IImage.Draw(
DrawingContext context,
Rect sourceRect,
- Rect destRect,
- BitmapInterpolationMode bitmapInterpolationMode)
+ Rect destRect)
{
var drawing = Drawing;
diff --git a/src/Avalonia.Base/Media/EdgeMode.cs b/src/Avalonia.Base/Media/EdgeMode.cs
new file mode 100644
index 0000000000..f50a2f7164
--- /dev/null
+++ b/src/Avalonia.Base/Media/EdgeMode.cs
@@ -0,0 +1,10 @@
+namespace Avalonia.Media
+{
+ public enum EdgeMode : byte
+ {
+ Unspecified,
+
+ Antialias,
+ Aliased
+ }
+}
diff --git a/src/Avalonia.Base/Media/GlyphRun.cs b/src/Avalonia.Base/Media/GlyphRun.cs
index d795cca894..20e0f96ff7 100644
--- a/src/Avalonia.Base/Media/GlyphRun.cs
+++ b/src/Avalonia.Base/Media/GlyphRun.cs
@@ -153,7 +153,7 @@ namespace Avalonia.Media
///
/// Gets the conservative bounding box of the .
///
- public Rect Bounds => PlatformImpl.Item.Bounds;
+ public Rect Bounds => new Rect(new Size(Metrics.WidthIncludingTrailingWhitespace, Metrics.Height));
///
///
@@ -166,7 +166,7 @@ namespace Avalonia.Media
///
public Point BaselineOrigin
{
- get => PlatformImpl.Item.BaselineOrigin;
+ get => _baselineOrigin ?? new Point(0, Metrics.Baseline);
set => Set(ref _baselineOrigin, value);
}
@@ -676,13 +676,17 @@ namespace Avalonia.Media
}
}
- return new GlyphRunMetrics(
- width,
- trailingWhitespaceLength,
- newLineLength,
- firstCluster,
- lastCluster
- );
+ return new GlyphRunMetrics
+ {
+ Baseline = -GlyphTypeface.Metrics.Ascent * Scale,
+ Width = width,
+ WidthIncludingTrailingWhitespace = widthIncludingTrailingWhitespace,
+ Height = height,
+ NewLineLength = newLineLength,
+ TrailingWhitespaceLength = trailingWhitespaceLength,
+ FirstCluster = firstCluster,
+ LastCluster = lastCluster
+ };
}
private int GetTrailingWhitespaceLength(bool isReversed, out int newLineLength, out int glyphCount)
@@ -820,10 +824,11 @@ namespace Avalonia.Media
private IRef CreateGlyphRunImpl()
{
var platformImpl = s_renderInterface.CreateGlyphRun(
- GlyphTypeface,
- FontRenderingEmSize,
- GlyphInfos,
- _baselineOrigin ?? new Point(0, -GlyphTypeface.Metrics.Ascent * Scale));
+ GlyphTypeface,
+ FontRenderingEmSize,
+ GlyphInfos,
+ BaselineOrigin,
+ Bounds);
_platformImpl = RefCountable.Create(platformImpl);
@@ -835,5 +840,16 @@ namespace Avalonia.Media
_platformImpl?.Dispose();
_platformImpl = null;
}
+
+ ///
+ /// Gets the intersections of specified upper and lower limit.
+ ///
+ /// Upper limit.
+ /// Lower limit.
+ ///
+ public IReadOnlyList GetIntersections(float lowerLimit, float upperLimit)
+ {
+ return PlatformImpl.Item.GetIntersections(lowerLimit, upperLimit);
+ }
}
}
diff --git a/src/Avalonia.Base/Media/GlyphRunMetrics.cs b/src/Avalonia.Base/Media/GlyphRunMetrics.cs
index 09b183d044..9ca1d5ea12 100644
--- a/src/Avalonia.Base/Media/GlyphRunMetrics.cs
+++ b/src/Avalonia.Base/Media/GlyphRunMetrics.cs
@@ -2,23 +2,20 @@
{
public readonly record struct GlyphRunMetrics
{
- public GlyphRunMetrics(double width, int trailingWhitespaceLength, int newLineLength, int firstCluster, int lastCluster)
- {
- Width = width;
- TrailingWhitespaceLength = trailingWhitespaceLength;
- NewLineLength = newLineLength;
- FirstCluster = firstCluster;
- LastCluster = lastCluster;
- }
+ public double Baseline { get; init; }
- public double Width { get; }
+ public double Width { get; init; }
- public int TrailingWhitespaceLength { get; }
+ public double WidthIncludingTrailingWhitespace { get; init; }
- public int NewLineLength { get; }
+ public double Height { get; init; }
- public int FirstCluster { get; }
+ public int TrailingWhitespaceLength { get; init; }
- public int LastCluster { get; }
+ public int NewLineLength { get; init; }
+
+ public int FirstCluster { get; init; }
+
+ public int LastCluster { get; init; }
}
}
diff --git a/src/Avalonia.Base/Media/IImage.cs b/src/Avalonia.Base/Media/IImage.cs
index cbe25b7b58..4e0b952b88 100644
--- a/src/Avalonia.Base/Media/IImage.cs
+++ b/src/Avalonia.Base/Media/IImage.cs
@@ -18,11 +18,9 @@ namespace Avalonia.Media
/// The drawing context.
/// The rect in the image to draw.
/// The rect in the output to draw to.
- /// The bitmap interpolation mode.
void Draw(
DrawingContext context,
Rect sourceRect,
- Rect destRect,
- BitmapInterpolationMode bitmapInterpolationMode);
+ Rect destRect);
}
}
diff --git a/src/Avalonia.Base/Media/ITileBrush.cs b/src/Avalonia.Base/Media/ITileBrush.cs
index cb5a591003..586f6053a1 100644
--- a/src/Avalonia.Base/Media/ITileBrush.cs
+++ b/src/Avalonia.Base/Media/ITileBrush.cs
@@ -39,13 +39,5 @@ namespace Avalonia.Media
/// Gets the brush's tile mode.
///
TileMode TileMode { get; }
-
- ///
- /// Gets the bitmap interpolation mode.
- ///
- ///
- /// The bitmap interpolation mode.
- ///
- BitmapInterpolationMode BitmapInterpolationMode { get; }
}
}
diff --git a/src/Avalonia.Base/Media/Imaging/Bitmap.cs b/src/Avalonia.Base/Media/Imaging/Bitmap.cs
index c4720d772e..07bb3db100 100644
--- a/src/Avalonia.Base/Media/Imaging/Bitmap.cs
+++ b/src/Avalonia.Base/Media/Imaging/Bitmap.cs
@@ -224,15 +224,13 @@ namespace Avalonia.Media.Imaging
void IImage.Draw(
DrawingContext context,
Rect sourceRect,
- Rect destRect,
- BitmapInterpolationMode bitmapInterpolationMode)
+ Rect destRect)
{
context.DrawBitmap(
PlatformImpl,
1,
sourceRect,
- destRect,
- bitmapInterpolationMode);
+ destRect);
}
private static IPlatformRenderInterface GetFactory()
diff --git a/src/Avalonia.Base/Media/Imaging/BitmapBlendingMode.cs b/src/Avalonia.Base/Media/Imaging/BitmapBlendingMode.cs
index eb39020939..73a3f7b269 100644
--- a/src/Avalonia.Base/Media/Imaging/BitmapBlendingMode.cs
+++ b/src/Avalonia.Base/Media/Imaging/BitmapBlendingMode.cs
@@ -3,8 +3,10 @@ namespace Avalonia.Media.Imaging
///
/// Controls the way the bitmaps are drawn together.
///
- public enum BitmapBlendingMode
+ public enum BitmapBlendingMode : byte
{
+ Unspecified,
+
///
/// Source is placed over the destination.
///
@@ -52,6 +54,6 @@ namespace Avalonia.Media.Imaging
///
/// Display the sum of the source image and destination image.
///
- Plus,
+ Plus
}
}
diff --git a/src/Avalonia.Base/Media/Imaging/BitmapInterpolationMode.cs b/src/Avalonia.Base/Media/Imaging/BitmapInterpolationMode.cs
index 7cdb5d8b9f..eaa64892a4 100644
--- a/src/Avalonia.Base/Media/Imaging/BitmapInterpolationMode.cs
+++ b/src/Avalonia.Base/Media/Imaging/BitmapInterpolationMode.cs
@@ -3,12 +3,14 @@
///
/// Controls the performance and quality of bitmap scaling.
///
- public enum BitmapInterpolationMode
+ public enum BitmapInterpolationMode : byte
{
+ Unspecified,
+
///
- /// Uses the default behavior of the underling render backend.
+ /// Disable interpolation.
///
- Default,
+ None,
///
/// The best performance but worst image quality.
@@ -18,7 +20,7 @@
///
/// Good performance and decent image quality.
///
- MediumQuality,
+ MediumQuality,
///
/// Highest quality but worst performance.
diff --git a/src/Avalonia.Base/Media/Imaging/CroppedBitmap.cs b/src/Avalonia.Base/Media/Imaging/CroppedBitmap.cs
index 8cdf5b592a..93556679e9 100644
--- a/src/Avalonia.Base/Media/Imaging/CroppedBitmap.cs
+++ b/src/Avalonia.Base/Media/Imaging/CroppedBitmap.cs
@@ -83,12 +83,12 @@ namespace Avalonia.Media.Imaging
}
}
- public void Draw(DrawingContext context, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode)
+ public void Draw(DrawingContext context, Rect sourceRect, Rect destRect)
{
if (Source is not IBitmap bmp)
return;
var topLeft = SourceRect.TopLeft.ToPointWithDpi(bmp.Dpi);
- Source.Draw(context, sourceRect.Translate(new Vector(topLeft.X, topLeft.Y)), destRect, bitmapInterpolationMode);
+ Source.Draw(context, sourceRect.Translate(new Vector(topLeft.X, topLeft.Y)), destRect);
}
}
}
diff --git a/src/Avalonia.Base/Media/Imaging/RenderTargetBitmap.cs b/src/Avalonia.Base/Media/Imaging/RenderTargetBitmap.cs
index e77dd9d1ab..4921e9b756 100644
--- a/src/Avalonia.Base/Media/Imaging/RenderTargetBitmap.cs
+++ b/src/Avalonia.Base/Media/Imaging/RenderTargetBitmap.cs
@@ -9,7 +9,7 @@ namespace Avalonia.Media.Imaging
///
/// A bitmap that holds the rendering of a .
///
- public class RenderTargetBitmap : Bitmap, IDisposable
+ public class RenderTargetBitmap : Bitmap
{
///
/// Initializes a new instance of the class.
@@ -68,5 +68,11 @@ namespace Avalonia.Media.Imaging
platform.Clear(Colors.Transparent);
return new PlatformDrawingContext(platform);
}
+
+ public override void Dispose()
+ {
+ PlatformImpl.Dispose();
+ base.Dispose();
+ }
}
}
diff --git a/src/Avalonia.Base/Media/ImmediateDrawingContext.cs b/src/Avalonia.Base/Media/ImmediateDrawingContext.cs
index 17c4560523..fdf10596bb 100644
--- a/src/Avalonia.Base/Media/ImmediateDrawingContext.cs
+++ b/src/Avalonia.Base/Media/ImmediateDrawingContext.cs
@@ -79,11 +79,10 @@ namespace Avalonia.Media
/// The bitmap.
/// The rect in the image to draw.
/// The rect in the output to draw to.
- /// The bitmap interpolation mode.
- public void DrawBitmap(IBitmap source, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode = default)
+ public void DrawBitmap(IBitmap source, Rect sourceRect, Rect destRect)
{
_ = source ?? throw new ArgumentNullException(nameof(source));
- PlatformImpl.DrawBitmap(source.PlatformImpl, 1, sourceRect, destRect, bitmapInterpolationMode);
+ PlatformImpl.DrawBitmap(source.PlatformImpl, 1, sourceRect, destRect);
}
///
diff --git a/src/Avalonia.Base/Media/Immutable/ImmutableImageBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableImageBrush.cs
index 668a907fdf..175038ba75 100644
--- a/src/Avalonia.Base/Media/Immutable/ImmutableImageBrush.cs
+++ b/src/Avalonia.Base/Media/Immutable/ImmutableImageBrush.cs
@@ -22,7 +22,6 @@ namespace Avalonia.Media.Immutable
/// How the source rectangle will be stretched to fill the destination rect.
///
/// The tile mode.
- /// The bitmap interpolation mode.
public ImmutableImageBrush(
IBitmap? source,
AlignmentX alignmentX = AlignmentX.Center,
@@ -33,8 +32,7 @@ namespace Avalonia.Media.Immutable
RelativePoint transformOrigin = default,
RelativeRect? sourceRect = null,
Stretch stretch = Stretch.Uniform,
- TileMode tileMode = TileMode.None,
- BitmapInterpolationMode bitmapInterpolationMode = BitmapInterpolationMode.Default)
+ TileMode tileMode = TileMode.None)
: base(
alignmentX,
alignmentY,
@@ -44,8 +42,7 @@ namespace Avalonia.Media.Immutable
transformOrigin,
sourceRect ?? RelativeRect.Fill,
stretch,
- tileMode,
- bitmapInterpolationMode)
+ tileMode)
{
Source = source;
}
diff --git a/src/Avalonia.Base/Media/Immutable/ImmutableTileBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableTileBrush.cs
index 1ee52365e0..7e139af516 100644
--- a/src/Avalonia.Base/Media/Immutable/ImmutableTileBrush.cs
+++ b/src/Avalonia.Base/Media/Immutable/ImmutableTileBrush.cs
@@ -21,7 +21,6 @@ namespace Avalonia.Media.Immutable
/// How the source rectangle will be stretched to fill the destination rect.
///
/// The tile mode.
- /// The bitmap interpolation mode.
protected ImmutableTileBrush(
AlignmentX alignmentX,
AlignmentY alignmentY,
@@ -31,8 +30,7 @@ namespace Avalonia.Media.Immutable
RelativePoint transformOrigin,
RelativeRect sourceRect,
Stretch stretch,
- TileMode tileMode,
- BitmapInterpolationMode bitmapInterpolationMode)
+ TileMode tileMode)
{
AlignmentX = alignmentX;
AlignmentY = alignmentY;
@@ -43,7 +41,6 @@ namespace Avalonia.Media.Immutable
SourceRect = sourceRect;
Stretch = stretch;
TileMode = tileMode;
- BitmapInterpolationMode = bitmapInterpolationMode;
}
///
@@ -60,8 +57,7 @@ namespace Avalonia.Media.Immutable
source.TransformOrigin,
source.SourceRect,
source.Stretch,
- source.TileMode,
- source.BitmapInterpolationMode)
+ source.TileMode)
{
}
@@ -95,8 +91,5 @@ namespace Avalonia.Media.Immutable
///
public TileMode TileMode { get; }
-
- ///
- public BitmapInterpolationMode BitmapInterpolationMode { get; }
}
}
diff --git a/src/Avalonia.Base/Media/PlatformDrawingContext.cs b/src/Avalonia.Base/Media/PlatformDrawingContext.cs
index 4b683c6acb..09c0cd26ac 100644
--- a/src/Avalonia.Base/Media/PlatformDrawingContext.cs
+++ b/src/Avalonia.Base/Media/PlatformDrawingContext.cs
@@ -26,6 +26,12 @@ internal sealed class PlatformDrawingContext : DrawingContext, IDrawingContextWi
_ownsImpl = ownsImpl;
}
+ public RenderOptions RenderOptions
+ {
+ get => _impl.RenderOptions;
+ set => _impl.RenderOptions = value;
+ }
+
protected override void DrawLineCore(IPen pen, Point p1, Point p2) =>
_impl.DrawLine(pen, p1, p2);
@@ -38,9 +44,8 @@ internal sealed class PlatformDrawingContext : DrawingContext, IDrawingContextWi
protected override void DrawEllipseCore(IBrush? brush, IPen? pen, Rect rect) => _impl.DrawEllipse(brush, pen, rect);
- internal override void DrawBitmap(IRef source, double opacity, Rect sourceRect, Rect destRect,
- BitmapInterpolationMode bitmapInterpolationMode = BitmapInterpolationMode.Default) =>
- _impl.DrawBitmap(source, opacity, sourceRect, destRect, bitmapInterpolationMode);
+ internal override void DrawBitmap(IRef source, double opacity, Rect sourceRect, Rect destRect) =>
+ _impl.DrawBitmap(source, opacity, sourceRect, destRect);
public override void Custom(ICustomDrawOperation custom)
{
@@ -77,9 +82,6 @@ internal sealed class PlatformDrawingContext : DrawingContext, IDrawingContextWi
protected override void PushOpacityMaskCore(IBrush mask, Rect bounds) =>
_impl.PushOpacityMask(mask, bounds);
- protected override void PushBitmapBlendModeCore(BitmapBlendingMode blendingMode) =>
- _impl.PushBitmapBlendMode(blendingMode);
-
protected override void PushTransformCore(Matrix matrix)
{
_transforms ??= TransformStackPool.Get();
@@ -96,8 +98,6 @@ internal sealed class PlatformDrawingContext : DrawingContext, IDrawingContextWi
protected override void PopOpacityMaskCore() => _impl.PopOpacityMask();
- protected override void PopBitmapBlendModeCore() => _impl.PopBitmapBlendMode();
-
protected override void PopTransformCore() =>
_impl.Transform =
(_transforms ?? throw new ObjectDisposedException(nameof(PlatformDrawingContext))).Pop();
diff --git a/src/Avalonia.Base/Media/RenderOptions.cs b/src/Avalonia.Base/Media/RenderOptions.cs
index 5863d0ac58..639498543b 100644
--- a/src/Avalonia.Base/Media/RenderOptions.cs
+++ b/src/Avalonia.Base/Media/RenderOptions.cs
@@ -1,36 +1,131 @@
using Avalonia.Media.Imaging;
namespace Avalonia.Media
-{
- public class RenderOptions
+{
+ public readonly record struct RenderOptions
{
+ public BitmapInterpolationMode BitmapInterpolationMode { get; init; }
+ public EdgeMode EdgeMode { get; init; }
+ public TextRenderingMode TextRenderingMode { get; init; }
+ public BitmapBlendingMode BitmapBlendingMode { get; init; }
+
+ ///
+ /// Gets the value of the BitmapInterpolationMode attached property for a visual.
+ ///
+ /// The control.
+ /// The control's left coordinate.
+ public static BitmapInterpolationMode GetBitmapInterpolationMode(Visual visual)
+ {
+ return visual.RenderOptions.BitmapInterpolationMode;
+ }
+
///
- /// Defines the property.
+ /// Sets the value of the BitmapInterpolationMode attached property for a visual.
///
- public static readonly StyledProperty BitmapInterpolationModeProperty =
- AvaloniaProperty.RegisterAttached(
- "BitmapInterpolationMode",
- BitmapInterpolationMode.MediumQuality,
- inherits: true);
+ /// The control.
+ /// The left value.
+ public static void SetBitmapInterpolationMode(Visual visual, BitmapInterpolationMode value)
+ {
+ visual.RenderOptions = visual.RenderOptions with { BitmapInterpolationMode = value };
+ }
///
- /// Gets the value of the BitmapInterpolationMode attached property for a control.
+ /// Gets the value of the BitmapBlendingMode attached property for a visual.
///
- /// The control.
+ /// The control.
/// The control's left coordinate.
- public static BitmapInterpolationMode GetBitmapInterpolationMode(AvaloniaObject element)
+ public static BitmapBlendingMode GetBitmapBlendingMode(Visual visual)
{
- return element.GetValue(BitmapInterpolationModeProperty);
+ return visual.RenderOptions.BitmapBlendingMode;
}
///
- /// Sets the value of the BitmapInterpolationMode attached property for a control.
+ /// Sets the value of the BitmapBlendingMode attached property for a visual.
///
- /// The control.
+ /// The control.
/// The left value.
- public static void SetBitmapInterpolationMode(AvaloniaObject element, BitmapInterpolationMode value)
+ public static void SetBitmapBlendingMode(Visual visual, BitmapBlendingMode value)
{
- element.SetValue(BitmapInterpolationModeProperty, value);
+ visual.RenderOptions = visual.RenderOptions with { BitmapBlendingMode = value };
+ }
+
+ ///
+ /// Gets the value of the EdgeMode attached property for a visual.
+ ///
+ /// The control.
+ /// The control's left coordinate.
+ public static EdgeMode GetEdgeMode(Visual visual)
+ {
+ return visual.RenderOptions.EdgeMode;
+ }
+
+ ///
+ /// Sets the value of the EdgeMode attached property for a visual.
+ ///
+ /// The control.
+ /// The left value.
+ public static void SetEdgeMode(Visual visual, EdgeMode value)
+ {
+ visual.RenderOptions = visual.RenderOptions with { EdgeMode = value };
+ }
+
+ ///
+ /// Gets the value of the TextRenderingMode attached property for a visual.
+ ///
+ /// The control.
+ /// The control's left coordinate.
+ public static TextRenderingMode GetTextRenderingMode(Visual visual)
+ {
+ return visual.RenderOptions.TextRenderingMode;
+ }
+
+ ///
+ /// Sets the value of the TextRenderingMode attached property for a visual.
+ ///
+ /// The control.
+ /// The left value.
+ public static void SetTextRenderingMode(Visual visual, TextRenderingMode value)
+ {
+ visual.RenderOptions = visual.RenderOptions with { TextRenderingMode = value };
+ }
+
+ public RenderOptions MergeWith(RenderOptions other)
+ {
+ var bitmapInterpolationMode = BitmapInterpolationMode;
+
+ if (bitmapInterpolationMode == BitmapInterpolationMode.Unspecified)
+ {
+ bitmapInterpolationMode = other.BitmapInterpolationMode;
+ }
+
+ var edgeMode = EdgeMode;
+
+ if (edgeMode == EdgeMode.Unspecified)
+ {
+ edgeMode = other.EdgeMode;
+ }
+
+ var textRenderingMode = TextRenderingMode;
+
+ if (textRenderingMode == TextRenderingMode.Unspecified)
+ {
+ textRenderingMode = other.TextRenderingMode;
+ }
+
+ var bitmapBlendingMode = BitmapBlendingMode;
+
+ if (bitmapBlendingMode == BitmapBlendingMode.Unspecified)
+ {
+ bitmapBlendingMode = other.BitmapBlendingMode;
+ }
+
+ return new RenderOptions
+ {
+ BitmapInterpolationMode = bitmapInterpolationMode,
+ EdgeMode = edgeMode,
+ TextRenderingMode = textRenderingMode,
+ BitmapBlendingMode = bitmapBlendingMode
+ };
}
}
}
diff --git a/src/Avalonia.Base/Media/TextDecoration.cs b/src/Avalonia.Base/Media/TextDecoration.cs
index e89a7d8826..8661959aa6 100644
--- a/src/Avalonia.Base/Media/TextDecoration.cs
+++ b/src/Avalonia.Base/Media/TextDecoration.cs
@@ -1,10 +1,6 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
using Avalonia.Collections;
-using Avalonia.Collections.Pooled;
using Avalonia.Media.TextFormatting;
-using Avalonia.Platform;
-using Avalonia.Utilities;
namespace Avalonia.Media
{
@@ -218,7 +214,7 @@ namespace Avalonia.Media
{
var offsetY = glyphRun.BaselineOrigin.Y - origin.Y;
- var intersections = glyphRun.PlatformImpl.Item.GetIntersections((float)(thickness * 0.5d - offsetY), (float)(thickness * 1.5d - offsetY));
+ var intersections = glyphRun.GetIntersections((float)(thickness * 0.5d - offsetY), (float)(thickness * 1.5d - offsetY));
if (intersections.Count > 0)
{
diff --git a/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs b/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs
index 253c7075fa..7d4fac337d 100644
--- a/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs
@@ -185,7 +185,9 @@ namespace Avalonia.Media.TextFormatting
}
//Stop at the first missing glyph
- if (!currentCodepoint.IsBreakChar && !glyphTypeface.TryGetGlyph(currentCodepoint, out _))
+ if (!currentCodepoint.IsBreakChar &&
+ currentCodepoint.GeneralCategory != GeneralCategory.Control &&
+ !glyphTypeface.TryGetGlyph(currentCodepoint, out _))
{
break;
}
diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs
index 3264d5e88a..1234067844 100644
--- a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs
@@ -83,7 +83,7 @@ namespace Avalonia.Media.TextFormatting
///
public override void Draw(DrawingContext drawingContext, Point lineOrigin)
{
- var (currentX, currentY) = lineOrigin;
+ var (currentX, currentY) = lineOrigin + new Point(Start, 0);
foreach (var textRun in _textRuns)
{
@@ -698,7 +698,7 @@ namespace Avalonia.Media.TextFormatting
i = lastRunIndex;
//Possible overlap at runs of different direction
- if (directionalWidth == 0)
+ if (directionalWidth == 0 && i < _textRuns.Length - 1)
{
//In case a run only contains a linebreak we don't want to skip it.
if (currentRun is ShapedTextRun shaped)
@@ -844,7 +844,7 @@ namespace Avalonia.Media.TextFormatting
i = firstRunIndex;
//Possible overlap at runs of different direction
- if (directionalWidth == 0)
+ if (directionalWidth == 0 && i > 0)
{
//In case a run only contains a linebreak we don't want to skip it.
if (currentRun is ShapedTextRun shaped)
@@ -860,8 +860,8 @@ namespace Avalonia.Media.TextFormatting
}
}
- TextBounds? textBounds = null;
int coveredLength;
+ TextBounds? textBounds;
switch (currentDirection)
{
@@ -942,6 +942,13 @@ namespace Avalonia.Media.TextFormatting
new TextRunBounds(
new Rect(startX, 0, drawableTextRun.Size.Width, Height), currentPosition, currentRun.Length, currentRun));
}
+ else
+ {
+ //Add potential TextEndOfParagraph
+ textRunBounds.Add(
+ new TextRunBounds(
+ new Rect(endX, 0, 0, Height), currentPosition, currentRun.Length, currentRun));
+ }
currentPosition += currentRun.Length;
@@ -1007,6 +1014,13 @@ namespace Avalonia.Media.TextFormatting
endX += drawableTextRun.Size.Width;
}
+ else
+ {
+ //Add potential TextEndOfParagraph
+ textRunBounds.Add(
+ new TextRunBounds(
+ new Rect(endX, 0, 0, Height), currentPosition, currentRun.Length, currentRun));
+ }
currentPosition += currentRun.Length;
@@ -1409,8 +1423,6 @@ namespace Avalonia.Media.TextFormatting
var fontMetrics = _paragraphProperties.DefaultTextRunProperties.CachedGlyphTypeface.Metrics;
var fontRenderingEmSize = _paragraphProperties.DefaultTextRunProperties.FontRenderingEmSize;
var scale = fontRenderingEmSize / fontMetrics.DesignEmHeight;
-
- var width = 0d;
var widthIncludingWhitespace = 0d;
var trailingWhitespaceLength = 0;
var newLineLength = 0;
@@ -1422,13 +1434,6 @@ namespace Avalonia.Media.TextFormatting
var lineHeight = _paragraphProperties.LineHeight;
- var lastRunIndex = _textRuns.Length - 1;
-
- if (lastRunIndex > 0 && _textRuns[lastRunIndex] is TextEndOfLine)
- {
- lastRunIndex--;
- }
-
for (var index = 0; index < _textRuns.Length; index++)
{
switch (_textRuns[index])
@@ -1486,7 +1491,7 @@ namespace Avalonia.Media.TextFormatting
}
}
- width = widthIncludingWhitespace;
+ var width = widthIncludingWhitespace;
for (var i = _textRuns.Length - 1; i >= 0; i--)
{
diff --git a/src/Avalonia.Base/Media/TextRenderingMode.cs b/src/Avalonia.Base/Media/TextRenderingMode.cs
new file mode 100644
index 0000000000..927d2bce73
--- /dev/null
+++ b/src/Avalonia.Base/Media/TextRenderingMode.cs
@@ -0,0 +1,11 @@
+namespace Avalonia.Media
+{
+ public enum TextRenderingMode : byte
+ {
+ Unspecified,
+
+ SubpixelAntialias,
+ Antialias,
+ Alias
+ }
+}
diff --git a/src/Avalonia.Base/Media/TileBrush.cs b/src/Avalonia.Base/Media/TileBrush.cs
index ab1ee2d604..d7b818a174 100644
--- a/src/Avalonia.Base/Media/TileBrush.cs
+++ b/src/Avalonia.Base/Media/TileBrush.cs
@@ -83,7 +83,6 @@ namespace Avalonia.Media
SourceRectProperty,
StretchProperty,
TileModeProperty);
- RenderOptions.BitmapInterpolationModeProperty.OverrideDefaultValue(BitmapInterpolationMode.Default);
}
///
@@ -140,17 +139,5 @@ namespace Avalonia.Media
get { return (TileMode)GetValue(TileModeProperty); }
set { SetValue(TileModeProperty, value); }
}
-
- ///
- /// Gets or sets the bitmap interpolation mode.
- ///
- ///
- /// The bitmap interpolation mode.
- ///
- public BitmapInterpolationMode BitmapInterpolationMode
- {
- get { return RenderOptions.GetBitmapInterpolationMode(this); }
- set { RenderOptions.SetBitmapInterpolationMode(this, value); }
- }
}
}
diff --git a/src/Avalonia.Base/Platform/IDrawingContextImpl.cs b/src/Avalonia.Base/Platform/IDrawingContextImpl.cs
index ef53024508..d86519656c 100644
--- a/src/Avalonia.Base/Platform/IDrawingContextImpl.cs
+++ b/src/Avalonia.Base/Platform/IDrawingContextImpl.cs
@@ -1,8 +1,8 @@
using System;
using Avalonia.Media;
using Avalonia.Utilities;
-using Avalonia.Media.Imaging;
using Avalonia.Metadata;
+using Avalonia.Media.Imaging;
namespace Avalonia.Platform
{
@@ -12,6 +12,11 @@ namespace Avalonia.Platform
[Unstable]
public interface IDrawingContextImpl : IDisposable
{
+ ///
+ /// Gets or sets the current render options used to control the rendering behavior of drawing operations.
+ ///
+ RenderOptions RenderOptions { get; set; }
+
///
/// Gets or sets the current transform of the drawing context.
///
@@ -30,8 +35,7 @@ namespace Avalonia.Platform
/// The opacity to draw with.
/// The rect in the image to draw.
/// The rect in the output to draw to.
- /// The bitmap interpolation mode.
- void DrawBitmap(IRef source, double opacity, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode = BitmapInterpolationMode.Default);
+ void DrawBitmap(IRef source, double opacity, Rect sourceRect, Rect destRect);
///
/// Draws a bitmap image.
@@ -157,15 +161,6 @@ namespace Avalonia.Platform
void PopGeometryClip();
///
- /// Pushes a bitmap blending value.
- ///
- /// The bitmap blending mode.
- void PushBitmapBlendMode(BitmapBlendingMode blendingMode);
-
- ///
- /// Pops the latest pushed bitmap blending value.
- ///
- void PopBitmapBlendMode();
///
/// Attempts to get an optional feature from the drawing context implementation
diff --git a/src/Avalonia.Base/Platform/IGlyphRunBuffer.cs b/src/Avalonia.Base/Platform/IGlyphRunBuffer.cs
deleted file mode 100644
index c1fc7a5967..0000000000
--- a/src/Avalonia.Base/Platform/IGlyphRunBuffer.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using System;
-using System.Drawing;
-
-namespace Avalonia.Platform
-{
- public interface IGlyphRunBuffer
- {
- Span GlyphIndices { get; }
-
- IGlyphRunImpl Build();
- }
-
- public interface IHorizontalGlyphRunBuffer : IGlyphRunBuffer
- {
- Span GlyphPositions { get; }
- }
-
- public interface IPositionedGlyphRunBuffer : IGlyphRunBuffer
- {
- Span GlyphPositions { get; }
- }
-}
diff --git a/src/Avalonia.Base/Platform/IGlyphRunImpl.cs b/src/Avalonia.Base/Platform/IGlyphRunImpl.cs
index fccea27c43..2342f32307 100644
--- a/src/Avalonia.Base/Platform/IGlyphRunImpl.cs
+++ b/src/Avalonia.Base/Platform/IGlyphRunImpl.cs
@@ -1,25 +1,36 @@
using System;
using System.Collections.Generic;
+using Avalonia.Media;
using Avalonia.Metadata;
namespace Avalonia.Platform
{
///
- /// Actual implementation of a glyph run that stores platform dependent resources.
+ /// An immutable platform representation of a .
///
[Unstable]
- public interface IGlyphRunImpl : IDisposable
+ public interface IGlyphRunImpl : IDisposable
{
///
- /// Gets the conservative bounding box of the glyph run./>.
+ /// Gets the for the .
///
- Rect Bounds { get; }
+ IGlyphTypeface GlyphTypeface { get; }
+
+ ///
+ /// Gets the em size used for rendering the .
+ ///
+ double FontRenderingEmSize { get; }
///
/// Gets the baseline origin of the glyph run./>.
///
Point BaselineOrigin { get; }
+ ///
+ /// Gets the conservative bounding box of the glyph run./>.
+ ///
+ Rect Bounds { get; }
+
///
/// Gets the intersections of specified upper and lower limit.
///
diff --git a/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs b/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs
index 6f62c3be1d..b0d17f9c85 100644
--- a/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs
+++ b/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs
@@ -169,8 +169,9 @@ namespace Avalonia.Platform
/// The font rendering em size.
/// The list of glyphs.
/// The baseline origin of the run. Can be null.
+ /// the conservative bounding box of the run
/// An .
- IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize, IReadOnlyList glyphInfos, Point baselineOrigin);
+ IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize, IReadOnlyList glyphInfos, Point baselineOrigin, Rect bounds);
///
/// Creates a backend-specific object using a low-level API graphics context
diff --git a/src/Avalonia.Base/Platform/StandardAssetLoader.cs b/src/Avalonia.Base/Platform/StandardAssetLoader.cs
index 118e57c7af..1d9363c70d 100644
--- a/src/Avalonia.Base/Platform/StandardAssetLoader.cs
+++ b/src/Avalonia.Base/Platform/StandardAssetLoader.cs
@@ -4,6 +4,7 @@ using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Reflection;
+using Avalonia.Metadata;
using Avalonia.Platform.Internal;
using Avalonia.Utilities;
@@ -12,12 +13,13 @@ namespace Avalonia.Platform;
///
/// Loads assets compiled into the application binary.
///
-internal class StandardAssetLoader : IAssetLoader
+[Unstable("StandardAssetLoader is considered unstable. Please use AssetLoader static class instead.")]
+public class StandardAssetLoader : IAssetLoader
{
private readonly IAssemblyDescriptorResolver _assemblyDescriptorResolver;
private AssemblyDescriptor? _defaultResmAssembly;
- public StandardAssetLoader(IAssemblyDescriptorResolver resolver, Assembly? assembly = null)
+ internal StandardAssetLoader(IAssemblyDescriptorResolver resolver, Assembly? assembly = null)
{
if (assembly == null)
assembly = Assembly.GetEntryAssembly();
@@ -153,6 +155,8 @@ internal class StandardAssetLoader : IAssetLoader
return Enumerable.Empty();
}
+ public static void RegisterResUriParsers() => AssetLoader.RegisterResUriParsers();
+
private bool TryGetAsset(Uri uri, Uri? baseUri, [NotNullWhen(true)] out IAssetDescriptor? assetDescriptor)
{
assetDescriptor = null;
diff --git a/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs b/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs
index 814ecdba29..1bf52729a0 100644
--- a/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs
+++ b/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs
@@ -260,6 +260,8 @@ public class CompositingRenderer : IRendererWithCompositor
if (!comp.Effect.EffectEquals(visual.Effect))
comp.Effect = visual.Effect?.ToImmutable();
+ comp.RenderOptions = visual.RenderOptions;
+
var renderTransform = Matrix.Identity;
if (visual.HasMirrorTransform)
@@ -272,8 +274,6 @@ public class CompositingRenderer : IRendererWithCompositor
renderTransform *= (-offset) * visual.RenderTransform.Value * (offset);
}
-
-
comp.TransformMatrix = MatrixUtils.ToMatrix4x4(renderTransform);
_recorder.BeginUpdate(comp.DrawList);
diff --git a/src/Avalonia.Base/Rendering/Composition/Drawing/CompositionDrawingContext.cs b/src/Avalonia.Base/Rendering/Composition/Drawing/CompositionDrawingContext.cs
index f81cc5a1a0..ec419e6313 100644
--- a/src/Avalonia.Base/Rendering/Composition/Drawing/CompositionDrawingContext.cs
+++ b/src/Avalonia.Base/Rendering/Composition/Drawing/CompositionDrawingContext.cs
@@ -78,15 +78,14 @@ internal sealed class CompositionDrawingContext : DrawingContext, IDrawingContex
}
}
- internal override void DrawBitmap(IRef source, double opacity, Rect sourceRect, Rect destRect,
- BitmapInterpolationMode bitmapInterpolationMode = BitmapInterpolationMode.Default)
+ internal override void DrawBitmap(IRef source, double opacity, Rect sourceRect, Rect destRect)
{
var next = NextDrawAs();
if (next == null ||
- !next.Item.Equals(Transform, source, opacity, sourceRect, destRect, bitmapInterpolationMode))
+ !next.Item.Equals(Transform, source, opacity, sourceRect, destRect))
{
- Add(new ImageNode(Transform, source, opacity, sourceRect, destRect, bitmapInterpolationMode));
+ Add(new ImageNode(Transform, source, opacity, sourceRect, destRect));
}
else
{
@@ -227,20 +226,6 @@ internal sealed class CompositionDrawingContext : DrawingContext, IDrawingContex
}
}
- protected override void PopBitmapBlendModeCore()
- {
- var next = NextDrawAs();
-
- if (next == null || !next.Item.Equals(null))
- {
- Add(new BitmapBlendModeNode());
- }
- else
- {
- ++_drawOperationIndex;
- }
- }
-
protected override void PopOpacityCore()
{
var next = NextDrawAs();
@@ -354,21 +339,6 @@ internal sealed class CompositionDrawingContext : DrawingContext, IDrawingContex
_needsToPopOpacityMask.Push(needsToPop);
}
- ///
- protected override void PushBitmapBlendModeCore(BitmapBlendingMode blendingMode)
- {
- var next = NextDrawAs();
-
- if (next == null || !next.Item.Equals(blendingMode))
- {
- Add(new BitmapBlendModeNode(blendingMode));
- }
- else
- {
- ++_drawOperationIndex;
- }
- }
-
private void Add(T node) where T : class, IDrawOperation
{
if (_drawOperationIndex < _builder.Count)
diff --git a/src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs b/src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs
index 901bdaae0d..5a4890e568 100644
--- a/src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs
+++ b/src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs
@@ -42,15 +42,20 @@ internal class CompositorDrawingContextProxy : IDrawingContextImpl,
set => _impl.Transform = (_transform = value) * PostTransform;
}
+ public RenderOptions RenderOptions
+ {
+ get => _impl.RenderOptions;
+ set => _impl.RenderOptions = value;
+ }
+
public void Clear(Color color)
{
_impl.Clear(color);
}
- public void DrawBitmap(IRef source, double opacity, Rect sourceRect, Rect destRect,
- BitmapInterpolationMode bitmapInterpolationMode = BitmapInterpolationMode.Default)
+ public void DrawBitmap(IRef source, double opacity, Rect sourceRect, Rect destRect)
{
- _impl.DrawBitmap(source, opacity, sourceRect, destRect, bitmapInterpolationMode);
+ _impl.DrawBitmap(source, opacity, sourceRect, destRect);
}
public void DrawBitmap(IRef source, IBrush opacityMask, Rect opacityMaskRect, Rect destRect)
@@ -133,16 +138,6 @@ internal class CompositorDrawingContextProxy : IDrawingContextImpl,
_impl.PopGeometryClip();
}
- public void PushBitmapBlendMode(BitmapBlendingMode blendingMode)
- {
- _impl.PushBitmapBlendMode(blendingMode);
- }
-
- public void PopBitmapBlendMode()
- {
- _impl.PopBitmapBlendMode();
- }
-
public object? GetFeature(Type t) => _impl.GetFeature(t);
diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs
index 3e88b9e77b..45275bdfe1 100644
--- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs
+++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs
@@ -181,7 +181,7 @@ namespace Avalonia.Rendering.Composition.Server
else
targetContext.DrawBitmap(RefCountable.CreateUnownedNotClonable(_layer), 1,
new Rect(_layerSize),
- new Rect(Size), BitmapInterpolationMode.LowQuality);
+ new Rect(Size));
if (DebugOverlays != RendererDebugOverlays.None)
{
diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs
index 6e7ef85183..853b90be5e 100644
--- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs
+++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs
@@ -66,6 +66,8 @@ namespace Avalonia.Rendering.Composition.Server
if (OpacityMaskBrush != null)
canvas.PushOpacityMask(OpacityMaskBrush, boundsRect);
+ canvas.RenderOptions = RenderOptions;
+
RenderCore(canvas, currentTransformedClip);
// Hack to force invalidation of SKMatrix
@@ -122,6 +124,11 @@ namespace Avalonia.Rendering.Composition.Server
var wasVisible = IsVisibleInFrame;
+ if(Parent != null)
+ {
+ RenderOptions = RenderOptions.MergeWith(Parent.RenderOptions);
+ }
+
// Calculate new parent-relative transform
if (_combinedTransformDirty)
{
diff --git a/src/Avalonia.Base/Rendering/ImmediateRenderer.cs b/src/Avalonia.Base/Rendering/ImmediateRenderer.cs
index 9b7d358b1d..3462b1008a 100644
--- a/src/Avalonia.Base/Rendering/ImmediateRenderer.cs
+++ b/src/Avalonia.Base/Rendering/ImmediateRenderer.cs
@@ -44,79 +44,99 @@ namespace Avalonia.Rendering
public static void Render(DrawingContext context, Visual visual, Rect clipRect)
{
- var opacity = visual.Opacity;
- var clipToBounds = visual.ClipToBounds;
- var bounds = new Rect(visual.Bounds.Size);
+ var currentRenderOptions = default(RenderOptions);
+ var platformContext = context as PlatformDrawingContext;
- if (visual.IsVisible && opacity > 0)
+ try
{
- var m = Matrix.CreateTranslation(visual.Bounds.Position);
-
- var renderTransform = Matrix.Identity;
-
- // this should be calculated BEFORE renderTransform
- if (visual.HasMirrorTransform)
+ if (platformContext != null)
{
- var mirrorMatrix = new Matrix(-1.0, 0.0, 0.0, 1.0, visual.Bounds.Width, 0);
- renderTransform *= mirrorMatrix;
- }
+ currentRenderOptions = platformContext.RenderOptions;
- if (visual.RenderTransform != null)
- {
- var origin = visual.RenderTransformOrigin.ToPixels(new Size(visual.Bounds.Width, visual.Bounds.Height));
- var offset = Matrix.CreateTranslation(origin);
- var finalTransform = (-offset) * visual.RenderTransform.Value * (offset);
- renderTransform *= finalTransform;
+ platformContext.RenderOptions = visual.RenderOptions.MergeWith(platformContext.RenderOptions);
}
- m = renderTransform * m;
+ var opacity = visual.Opacity;
+ var clipToBounds = visual.ClipToBounds;
+ var bounds = new Rect(visual.Bounds.Size);
- if (clipToBounds)
+ if (visual.IsVisible && opacity > 0)
{
+ var m = Matrix.CreateTranslation(visual.Bounds.Position);
+
+ var renderTransform = Matrix.Identity;
+
+ // this should be calculated BEFORE renderTransform
+ if (visual.HasMirrorTransform)
+ {
+ var mirrorMatrix = new Matrix(-1.0, 0.0, 0.0, 1.0, visual.Bounds.Width, 0);
+ renderTransform *= mirrorMatrix;
+ }
+
if (visual.RenderTransform != null)
{
- clipRect = new Rect(visual.Bounds.Size);
+ var origin = visual.RenderTransformOrigin.ToPixels(new Size(visual.Bounds.Width, visual.Bounds.Height));
+ var offset = Matrix.CreateTranslation(origin);
+ var finalTransform = (-offset) * visual.RenderTransform.Value * (offset);
+ renderTransform *= finalTransform;
}
- else
+
+ m = renderTransform * m;
+
+ if (clipToBounds)
{
- clipRect = clipRect.Intersect(new Rect(visual.Bounds.Size));
+ if (visual.RenderTransform != null)
+ {
+ clipRect = new Rect(visual.Bounds.Size);
+ }
+ else
+ {
+ clipRect = clipRect.Intersect(new Rect(visual.Bounds.Size));
+ }
}
- }
- using (context.PushTransform(m))
- using (context.PushOpacity(opacity, bounds))
- using (clipToBounds
+ using (context.PushTransform(m))
+ using (context.PushOpacity(opacity, bounds))
+ using (clipToBounds
#pragma warning disable CS0618 // Type or member is obsolete
- ? visual is IVisualWithRoundRectClip roundClipVisual
- ? context.PushClip(new RoundedRect(bounds, roundClipVisual.ClipToBoundsRadius))
- : context.PushClip(bounds)
- : default)
+ ? visual is IVisualWithRoundRectClip roundClipVisual
+ ? context.PushClip(new RoundedRect(bounds, roundClipVisual.ClipToBoundsRadius))
+ : context.PushClip(bounds)
+ : default)
#pragma warning restore CS0618 // Type or member is obsolete
- using (visual.Clip != null ? context.PushGeometryClip(visual.Clip) : default)
- using (visual.OpacityMask != null ? context.PushOpacityMask(visual.OpacityMask, bounds) : default)
- using (context.PushTransform(Matrix.Identity))
- {
- visual.Render(context);
-
- var childrenEnumerable = visual.HasNonUniformZIndexChildren
- ? visual.VisualChildren.OrderBy(x => x, ZIndexComparer.Instance)
- : (IEnumerable)visual.VisualChildren;
-
- foreach (var child in childrenEnumerable)
+ using (visual.Clip != null ? context.PushGeometryClip(visual.Clip) : default)
+ using (visual.OpacityMask != null ? context.PushOpacityMask(visual.OpacityMask, bounds) : default)
+ using (context.PushTransform(Matrix.Identity))
{
- var childBounds = GetTransformedBounds(child);
+ visual.Render(context);
+
+ var childrenEnumerable = visual.HasNonUniformZIndexChildren
+ ? visual.VisualChildren.OrderBy(x => x, ZIndexComparer.Instance)
+ : (IEnumerable)visual.VisualChildren;
- if (!child.ClipToBounds || clipRect.Intersects(childBounds))
+ foreach (var child in childrenEnumerable)
{
- var childClipRect = child.RenderTransform == null
- ? clipRect.Translate(-childBounds.Position)
- : clipRect;
- Render(context, child, childClipRect);
- }
+ var childBounds = GetTransformedBounds(child);
+
+ if (!child.ClipToBounds || clipRect.Intersects(childBounds))
+ {
+ var childClipRect = child.RenderTransform == null
+ ? clipRect.Translate(-childBounds.Position)
+ : clipRect;
+ Render(context, child, childClipRect);
+ }
+ }
}
}
}
+ finally
+ {
+ if (platformContext != null)
+ {
+ platformContext.RenderOptions = currentRenderOptions;
+ }
+ }
}
}
}
diff --git a/src/Avalonia.Base/Rendering/SceneGraph/BitmapBlendModeNode.cs b/src/Avalonia.Base/Rendering/SceneGraph/BitmapBlendModeNode.cs
deleted file mode 100644
index b1190a159b..0000000000
--- a/src/Avalonia.Base/Rendering/SceneGraph/BitmapBlendModeNode.cs
+++ /dev/null
@@ -1,68 +0,0 @@
-using Avalonia.Platform;
-using Avalonia.Media.Imaging;
-
-namespace Avalonia.Rendering.SceneGraph
-{
- ///
- /// A node in the scene graph which represents an bitmap blending mode push or pop.
- ///
- internal class BitmapBlendModeNode : IDrawOperation
- {
- ///
- /// Initializes a new instance of the class that represents an
- /// push.
- ///
- /// The to push.
- public BitmapBlendModeNode(BitmapBlendingMode bitmapBlend)
- {
- BlendingMode = bitmapBlend;
- }
-
- ///
- /// Initializes a new instance of the class that represents an
- /// pop.
- ///
- public BitmapBlendModeNode()
- {
- }
-
- ///
- public Rect Bounds => default;
-
- ///
- /// Gets the BitmapBlend to be pushed or null if the operation represents a pop.
- ///
- public BitmapBlendingMode? BlendingMode { get; }
-
- ///
- public bool HitTest(Point p) => false;
-
- ///
- /// Determines if this draw operation equals another.
- ///
- /// the how to compare
- /// True if the draw operations are the same, otherwise false.
- ///
- /// The properties of the other draw operation are passed in as arguments to prevent
- /// allocation of a not-yet-constructed draw operation object.
- ///
- public bool Equals(BitmapBlendingMode? blendingMode) => BlendingMode == blendingMode;
-
- ///
- public void Render(IDrawingContextImpl context)
- {
- if (BlendingMode.HasValue)
- {
- context.PushBitmapBlendMode(BlendingMode.Value);
- }
- else
- {
- context.PopBitmapBlendMode();
- }
- }
-
- public void Dispose()
- {
- }
- }
-}
diff --git a/src/Avalonia.Base/Rendering/SceneGraph/GlyphRunNode.cs b/src/Avalonia.Base/Rendering/SceneGraph/GlyphRunNode.cs
index 1c4e63b34a..764c5c65f9 100644
--- a/src/Avalonia.Base/Rendering/SceneGraph/GlyphRunNode.cs
+++ b/src/Avalonia.Base/Rendering/SceneGraph/GlyphRunNode.cs
@@ -1,5 +1,4 @@
-using System;
-using Avalonia.Media;
+using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.Utilities;
@@ -18,7 +17,7 @@ namespace Avalonia.Rendering.SceneGraph
/// The glyph run to draw.
public GlyphRunNode(
Matrix transform,
- IImmutableBrush foreground,
+ IImmutableBrush? foreground,
IRef glyphRun)
: base(glyphRun.Item.Bounds, transform, foreground)
{
diff --git a/src/Avalonia.Base/Rendering/SceneGraph/ImageNode.cs b/src/Avalonia.Base/Rendering/SceneGraph/ImageNode.cs
index ac946cc8b2..caf0eee175 100644
--- a/src/Avalonia.Base/Rendering/SceneGraph/ImageNode.cs
+++ b/src/Avalonia.Base/Rendering/SceneGraph/ImageNode.cs
@@ -1,6 +1,5 @@
using Avalonia.Platform;
using Avalonia.Utilities;
-using Avalonia.Media.Imaging;
namespace Avalonia.Rendering.SceneGraph
{
@@ -17,15 +16,13 @@ namespace Avalonia.Rendering.SceneGraph
/// The draw opacity.
/// The source rect.
/// The destination rect.
- /// The bitmap interpolation mode.
- public ImageNode(Matrix transform, IRef source, double opacity, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode)
+ public ImageNode(Matrix transform, IRef source, double opacity, Rect sourceRect, Rect destRect)
: base(destRect, transform)
{
Source = source.Clone();
Opacity = opacity;
SourceRect = sourceRect;
DestRect = destRect;
- BitmapInterpolationMode = bitmapInterpolationMode;
SourceVersion = Source.Item.Version;
}
@@ -53,14 +50,6 @@ namespace Avalonia.Rendering.SceneGraph
/// Gets the destination rect.
///
public Rect DestRect { get; }
-
- ///
- /// Gets the bitmap interpolation mode.
- ///
- ///
- /// The scaling mode.
- ///
- public BitmapInterpolationMode BitmapInterpolationMode { get; }
///
/// Determines if this draw operation equals another.
@@ -70,27 +59,25 @@ namespace Avalonia.Rendering.SceneGraph
/// The opacity of the other draw operation.
/// The source rect of the other draw operation.
/// The dest rect of the other draw operation.
- /// The bitmap interpolation mode.
/// True if the draw operations are the same, otherwise false.
///
/// The properties of the other draw operation are passed in as arguments to prevent
/// allocation of a not-yet-constructed draw operation object.
///
- public bool Equals(Matrix transform, IRef source, double opacity, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode)
+ public bool Equals(Matrix transform, IRef source, double opacity, Rect sourceRect, Rect destRect)
{
return transform == Transform &&
Equals(source.Item, Source.Item) &&
source.Item.Version == SourceVersion &&
opacity == Opacity &&
sourceRect == SourceRect &&
- destRect == DestRect &&
- bitmapInterpolationMode == BitmapInterpolationMode;
+ destRect == DestRect;
}
///
public override void Render(IDrawingContextImpl context)
{
- context.DrawBitmap(Source, Opacity, SourceRect, DestRect, BitmapInterpolationMode);
+ context.DrawBitmap(Source, Opacity, SourceRect, DestRect);
}
///
diff --git a/src/Avalonia.Base/Threading/Dispatcher.Invoke.cs b/src/Avalonia.Base/Threading/Dispatcher.Invoke.cs
index 699186868a..bb1663eac0 100644
--- a/src/Avalonia.Base/Threading/Dispatcher.Invoke.cs
+++ b/src/Avalonia.Base/Threading/Dispatcher.Invoke.cs
@@ -248,11 +248,11 @@ public partial class Dispatcher
/// An operation representing the queued delegate to be invoked.
///
///
- /// Note that the default priority is DispatcherPriority.Normal.
+ /// Note that the default priority is DispatcherPriority.Default.
///
public DispatcherOperation InvokeAsync(Action callback)
{
- return InvokeAsync(callback, DispatcherPriority.Normal, CancellationToken.None);
+ return InvokeAsync(callback, default, CancellationToken.None);
}
///
@@ -326,11 +326,11 @@ public partial class Dispatcher
/// An operation representing the queued delegate to be invoked.
///
///
- /// Note that the default priority is DispatcherPriority.Normal.
+ /// Note that the default priority is DispatcherPriority.Default.
///
public DispatcherOperation InvokeAsync(Func callback)
{
- return InvokeAsync(callback, DispatcherPriority.Normal, CancellationToken.None);
+ return InvokeAsync(callback, DispatcherPriority.Default, CancellationToken.None);
}
///
@@ -541,6 +541,18 @@ public partial class Dispatcher
InvokeAsyncImpl(new DispatcherOperation(this, priority, action, true), CancellationToken.None);
}
+ ///
+ /// Executes the specified Func<Task> asynchronously on the
+ /// thread that the Dispatcher was created on
+ ///
+ ///
+ /// A Func<Task> delegate to invoke through the dispatcher.
+ ///
+ ///
+ /// An task that completes after the task returned from callback finishes.
+ ///
+ public Task InvokeAsync(Func callback) => InvokeAsync(callback, DispatcherPriority.Default);
+
///
/// Executes the specified Func<Task> asynchronously on the
/// thread that the Dispatcher was created on
@@ -556,11 +568,29 @@ public partial class Dispatcher
///
/// An task that completes after the task returned from callback finishes
///
- public Task InvokeAsync(Func callback, DispatcherPriority priority = default)
+ public Task InvokeAsync(Func callback, DispatcherPriority priority)
{
_ = callback ?? throw new ArgumentNullException(nameof(callback));
return InvokeAsync(callback, priority).GetTask().Unwrap();
}
+
+ ///
+ /// Executes the specified Func<Task<TResult>> asynchronously on the
+ /// thread that the Dispatcher was created on
+ ///
+ ///
+ /// A Func<Task<TResult>> delegate to invoke through the dispatcher.
+ ///
+ ///
+ /// The priority that determines in what order the specified
+ /// callback is invoked relative to the other pending operations
+ /// in the Dispatcher.
+ ///
+ ///
+ /// An task that completes after the task returned from callback finishes
+ ///
+ public Task InvokeAsync(Func> action) =>
+ InvokeAsync(action, DispatcherPriority.Default);
///
/// Executes the specified Func<Task<TResult>> asynchronously on the
@@ -577,7 +607,7 @@ public partial class Dispatcher
///
/// An task that completes after the task returned from callback finishes
///
- public Task InvokeAsync(Func> action, DispatcherPriority priority = default)
+ public Task InvokeAsync(Func> action, DispatcherPriority priority)
{
_ = action ?? throw new ArgumentNullException(nameof(action));
return InvokeAsync>(action, priority).GetTask().Unwrap();
diff --git a/src/Avalonia.Base/Threading/DispatcherFrame.cs b/src/Avalonia.Base/Threading/DispatcherFrame.cs
index 1f8974dfa3..e826432475 100644
--- a/src/Avalonia.Base/Threading/DispatcherFrame.cs
+++ b/src/Avalonia.Base/Threading/DispatcherFrame.cs
@@ -91,31 +91,44 @@ public class DispatcherFrame
internal void Run(IControlledDispatcherImpl impl)
{
- // Since the actual platform run loop is controlled by a Cancellation token, we are restarting
- // it if frame still needs to run
- while (Continue)
- RunCore(impl);
- }
-
- private void RunCore(IControlledDispatcherImpl impl)
- {
- if (_isRunning)
- throw new InvalidOperationException("This frame is already running");
- _isRunning = true;
- try
- {
- _cancellationTokenSource = new CancellationTokenSource();
- // Wake up the dispatcher in case it has pending jobs
- Dispatcher.RequestProcessing();
- impl.RunLoop(_cancellationTokenSource.Token);
- }
- finally
+ Dispatcher.VerifyAccess();
+
+ // Since the actual platform run loop is controlled by a Cancellation token, we have an
+ // outer loop that restarts the platform one in case Continue was set to true after being set to false
+ while (true)
{
- _isRunning = false;
- _cancellationTokenSource?.Cancel();
- _cancellationTokenSource = null;
+ // Take the instance lock since `Continue` is changed from one too
+ lock (Dispatcher.InstanceLock)
+ {
+ if (!Continue)
+ return;
+
+ if (_isRunning)
+ throw new InvalidOperationException("This frame is already running");
+
+ _cancellationTokenSource = new CancellationTokenSource();
+ _isRunning = true;
+ }
+
+ try
+ {
+ // Wake up the dispatcher in case it has pending jobs
+ Dispatcher.RequestProcessing();
+ impl.RunLoop(_cancellationTokenSource.Token);
+ }
+ finally
+ {
+ lock (Dispatcher.InstanceLock)
+ {
+ _isRunning = false;
+ _cancellationTokenSource?.Cancel();
+ _cancellationTokenSource?.Dispose();
+ _cancellationTokenSource = null;
+ }
+ }
}
}
+
internal void MaybeExitOnDispatcherRequest()
{
diff --git a/src/Avalonia.Base/Threading/DispatcherOperation.cs b/src/Avalonia.Base/Threading/DispatcherOperation.cs
index 809c41ff02..8bd6d3bc01 100644
--- a/src/Avalonia.Base/Threading/DispatcherOperation.cs
+++ b/src/Avalonia.Base/Threading/DispatcherOperation.cs
@@ -331,6 +331,8 @@ public class DispatcherOperation : DispatcherOperation
private TaskCompletionSource TaskCompletionSource => (TaskCompletionSource)TaskSource!;
+ public new TaskAwaiter GetAwaiter() => GetTask().GetAwaiter();
+
public new Task GetTask() => TaskCompletionSource!.Task;
protected override Task GetTaskCore() => GetTask();
diff --git a/src/Avalonia.Base/Threading/DispatcherPriority.cs b/src/Avalonia.Base/Threading/DispatcherPriority.cs
index 3017b45dc7..a43dd8e4a2 100644
--- a/src/Avalonia.Base/Threading/DispatcherPriority.cs
+++ b/src/Avalonia.Base/Threading/DispatcherPriority.cs
@@ -1,4 +1,5 @@
using System;
+using System.ComponentModel;
namespace Avalonia.Threading
{
@@ -100,7 +101,7 @@ namespace Avalonia.Threading
///
/// The job will be processed with the same priority as data binding.
///
- [Obsolete("WPF compatibility")] public static readonly DispatcherPriority DataBind = new(Layout);
+ [Obsolete("WPF compatibility"), EditorBrowsable(EditorBrowsableState.Never)] public static readonly DispatcherPriority DataBind = new(Layout);
///
/// The job will be processed with normal priority.
diff --git a/src/Avalonia.Base/Visual.cs b/src/Avalonia.Base/Visual.cs
index 8717b5340a..30c89d186f 100644
--- a/src/Avalonia.Base/Visual.cs
+++ b/src/Avalonia.Base/Visual.cs
@@ -318,7 +318,9 @@ namespace Avalonia
internal CompositionDrawListVisual? CompositionVisual { get; private set; }
internal CompositionVisual? ChildCompositionVisual { get; set; }
-
+
+ internal RenderOptions RenderOptions { get; set; }
+
public bool HasNonUniformZIndexChildren { get; private set; }
///
diff --git a/src/Avalonia.Base/VisualTree/IVisualWithRoundRectClip.cs b/src/Avalonia.Base/VisualTree/IVisualWithRoundRectClip.cs
index 9ace215d03..0079515a63 100644
--- a/src/Avalonia.Base/VisualTree/IVisualWithRoundRectClip.cs
+++ b/src/Avalonia.Base/VisualTree/IVisualWithRoundRectClip.cs
@@ -1,14 +1,15 @@
using System;
+using System.ComponentModel;
namespace Avalonia.VisualTree
{
- [Obsolete("Internal API, will be removed in future versions, you've been warned")]
+ [Obsolete("Internal API, will be removed in future versions, you've been warned"), EditorBrowsable(EditorBrowsableState.Never)]
public interface IVisualWithRoundRectClip
{
///
/// Gets a value indicating the corner radius of control's clip bounds
///
- [Obsolete("Internal API, will be removed in future versions, you've been warned")]
+ [Obsolete("Internal API, will be removed in future versions, you've been warned"), EditorBrowsable(EditorBrowsableState.Never)]
CornerRadius ClipToBoundsRadius { get; }
}
diff --git a/src/Avalonia.Base/composition-schema.xml b/src/Avalonia.Base/composition-schema.xml
index 91d718dfd8..a24c249eed 100644
--- a/src/Avalonia.Base/composition-schema.xml
+++ b/src/Avalonia.Base/composition-schema.xml
@@ -30,6 +30,7 @@
+
diff --git a/src/Avalonia.Controls/ComboBox.cs b/src/Avalonia.Controls/ComboBox.cs
index 7fca1b42ac..f41c00662d 100644
--- a/src/Avalonia.Controls/ComboBox.cs
+++ b/src/Avalonia.Controls/ComboBox.cs
@@ -450,7 +450,22 @@ namespace Avalonia.Controls
}
else
{
- SelectionBoxItem = item;
+ if(ItemTemplate is null && DisplayMemberBinding is { } binding)
+ {
+ var template = new FuncDataTemplate((_, _) =>
+ new TextBlock
+ {
+ [TextBlock.DataContextProperty] = item,
+ [!TextBlock.TextProperty] = binding,
+ });
+ var text = template.Build(item);
+ SelectionBoxItem = text;
+ }
+ else
+ {
+ SelectionBoxItem = item;
+ }
+
}
}
diff --git a/src/Avalonia.Controls/ContextMenu.cs b/src/Avalonia.Controls/ContextMenu.cs
index 63e28ea14d..97a8c6fe97 100644
--- a/src/Avalonia.Controls/ContextMenu.cs
+++ b/src/Avalonia.Controls/ContextMenu.cs
@@ -64,7 +64,7 @@ namespace Avalonia.Controls
///
/// Defines the property.
///
- [Obsolete("Use the Placement property instead.")]
+ [Obsolete("Use the Placement property instead."), EditorBrowsable(EditorBrowsableState.Never)]
public static readonly StyledProperty PlacementModeProperty = PlacementProperty;
///
@@ -157,7 +157,7 @@ namespace Avalonia.Controls
}
///
- [Obsolete("Use the Placement property instead.")]
+ [Obsolete("Use the Placement property instead."), EditorBrowsable(EditorBrowsableState.Never)]
public PlacementMode PlacementMode
{
get => GetValue(PlacementProperty);
diff --git a/src/Avalonia.Controls/Generators/ItemContainerGenerator.cs b/src/Avalonia.Controls/Generators/ItemContainerGenerator.cs
index 977552d5a3..91e8dae0a1 100644
--- a/src/Avalonia.Controls/Generators/ItemContainerGenerator.cs
+++ b/src/Avalonia.Controls/Generators/ItemContainerGenerator.cs
@@ -1,4 +1,5 @@
using System;
+using System.ComponentModel;
namespace Avalonia.Controls.Generators
{
@@ -156,10 +157,10 @@ namespace Avalonia.Controls.Generators
///
public void ClearItemContainer(Control container) => _owner.ClearItemContainer(container);
- [Obsolete("Use ItemsControl.ContainerFromIndex")]
+ [Obsolete("Use ItemsControl.ContainerFromIndex"), EditorBrowsable(EditorBrowsableState.Never)]
public Control? ContainerFromIndex(int index) => _owner.ContainerFromIndex(index);
- [Obsolete("Use ItemsControl.IndexFromContainer")]
+ [Obsolete("Use ItemsControl.IndexFromContainer"), EditorBrowsable(EditorBrowsableState.Never)]
public int IndexFromContainer(Control container) => _owner.IndexFromContainer(container);
}
}
diff --git a/src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs b/src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs
index c1cae862a9..717dadb6ea 100644
--- a/src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs
+++ b/src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.ComponentModel;
namespace Avalonia.Controls.Generators
{
@@ -20,13 +21,13 @@ namespace Avalonia.Controls.Generators
internal TreeContainerIndex(TreeView owner) => _owner = owner;
- [Obsolete("Use TreeView.GetRealizedTreeContainers")]
+ [Obsolete("Use TreeView.GetRealizedTreeContainers"), EditorBrowsable(EditorBrowsableState.Never)]
public IEnumerable Containers => _owner.GetRealizedTreeContainers();
- [Obsolete("Use TreeView.TreeContainerFromItem")]
+ [Obsolete("Use TreeView.TreeContainerFromItem"), EditorBrowsable(EditorBrowsableState.Never)]
public Control? ContainerFromItem(object item) => _owner.TreeContainerFromItem(item);
- [Obsolete("Use TreeView.TreeItemFromContainer")]
+ [Obsolete("Use TreeView.TreeItemFromContainer"), EditorBrowsable(EditorBrowsableState.Never)]
public object? ItemFromContainer(Control container) => _owner.TreeItemFromContainer(container);
}
}
diff --git a/src/Avalonia.Controls/Image.cs b/src/Avalonia.Controls/Image.cs
index b14cc78e60..7996b982eb 100644
--- a/src/Avalonia.Controls/Image.cs
+++ b/src/Avalonia.Controls/Image.cs
@@ -91,9 +91,7 @@ namespace Avalonia.Controls
Rect sourceRect = new Rect(sourceSize)
.CenterRect(new Rect(destRect.Size / scale));
- var interpolationMode = RenderOptions.GetBitmapInterpolationMode(this);
-
- context.DrawImage(source, sourceRect, destRect, interpolationMode);
+ context.DrawImage(source, sourceRect, destRect);
}
}
diff --git a/src/Avalonia.Controls/ItemsControl.cs b/src/Avalonia.Controls/ItemsControl.cs
index 3e8e1f7a61..064716fa9b 100644
--- a/src/Avalonia.Controls/ItemsControl.cs
+++ b/src/Avalonia.Controls/ItemsControl.cs
@@ -2,6 +2,7 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
+using System.ComponentModel;
using Avalonia.Automation.Peers;
using Avalonia.Controls.Generators;
using Avalonia.Controls.Metadata;
@@ -676,7 +677,7 @@ namespace Avalonia.Controls
/// TreeView to be able to create a . Can be
/// removed in 12.0.
///
- [Obsolete]
+ [Obsolete, EditorBrowsable(EditorBrowsableState.Never)]
private protected virtual ItemContainerGenerator CreateItemContainerGenerator()
{
return new ItemContainerGenerator(this);
diff --git a/src/Avalonia.Controls/Platform/Dialogs/ISystemDialogImpl.cs b/src/Avalonia.Controls/Platform/Dialogs/ISystemDialogImpl.cs
index 996fff6775..a593caecaf 100644
--- a/src/Avalonia.Controls/Platform/Dialogs/ISystemDialogImpl.cs
+++ b/src/Avalonia.Controls/Platform/Dialogs/ISystemDialogImpl.cs
@@ -1,4 +1,5 @@
using System;
+using System.ComponentModel;
using System.Threading.Tasks;
using Avalonia.Metadata;
@@ -7,7 +8,7 @@ namespace Avalonia.Controls.Platform
///
/// Defines a platform-specific system dialog implementation.
///
- [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API")]
+ [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API"), EditorBrowsable(EditorBrowsableState.Never)]
[Unstable]
public interface ISystemDialogImpl
{
diff --git a/src/Avalonia.Controls/Platform/Dialogs/SystemDialogImpl.cs b/src/Avalonia.Controls/Platform/Dialogs/SystemDialogImpl.cs
index 20bfb440e3..37e6272abd 100644
--- a/src/Avalonia.Controls/Platform/Dialogs/SystemDialogImpl.cs
+++ b/src/Avalonia.Controls/Platform/Dialogs/SystemDialogImpl.cs
@@ -1,4 +1,5 @@
using System;
+using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using Avalonia.Platform.Storage;
@@ -10,7 +11,7 @@ namespace Avalonia.Controls.Platform
///
/// Defines a platform-specific system dialog implementation.
///
- [Obsolete]
+ [Obsolete, EditorBrowsable(EditorBrowsableState.Never)]
internal class SystemDialogImpl : ISystemDialogImpl
{
public async Task ShowFileDialogAsync(FileDialog dialog, Window parent)
diff --git a/src/Avalonia.Controls/Platform/ManagedDispatcherImpl.cs b/src/Avalonia.Controls/Platform/ManagedDispatcherImpl.cs
index 20aa91c83e..fdc098777a 100644
--- a/src/Avalonia.Controls/Platform/ManagedDispatcherImpl.cs
+++ b/src/Avalonia.Controls/Platform/ManagedDispatcherImpl.cs
@@ -58,6 +58,10 @@ public class ManagedDispatcherImpl : IControlledDispatcherImpl
public void RunLoop(CancellationToken token)
{
+ CancellationTokenRegistration registration = default;
+ if (token.CanBeCanceled)
+ registration = token.Register(() => _wakeup.Set());
+
while (!token.IsCancellationRequested)
{
bool signaled;
@@ -105,5 +109,7 @@ public class ManagedDispatcherImpl : IControlledDispatcherImpl
else
_wakeup.WaitOne();
}
+
+ registration.Dispose();
}
}
\ No newline at end of file
diff --git a/src/Avalonia.Controls/Platform/Screen.cs b/src/Avalonia.Controls/Platform/Screen.cs
index 4898c5f912..fde90dc589 100644
--- a/src/Avalonia.Controls/Platform/Screen.cs
+++ b/src/Avalonia.Controls/Platform/Screen.cs
@@ -1,4 +1,5 @@
using System;
+using System.ComponentModel;
namespace Avalonia.Platform
{
@@ -17,7 +18,7 @@ namespace Avalonia.Platform
public double Scaling { get; }
///
- [Obsolete("Use the Scaling property instead.")]
+ [Obsolete("Use the Scaling property instead."), EditorBrowsable(EditorBrowsableState.Never)]
public double PixelDensity => Scaling;
///
@@ -43,7 +44,7 @@ namespace Avalonia.Platform
public bool IsPrimary { get; }
///
- [Obsolete("Use the IsPrimary property instead.")]
+ [Obsolete("Use the IsPrimary property instead."), EditorBrowsable(EditorBrowsableState.Never)]
public bool Primary => IsPrimary;
///
diff --git a/src/Avalonia.Controls/Primitives/Popup.cs b/src/Avalonia.Controls/Primitives/Popup.cs
index eda794f33a..80b7841fc7 100644
--- a/src/Avalonia.Controls/Primitives/Popup.cs
+++ b/src/Avalonia.Controls/Primitives/Popup.cs
@@ -74,7 +74,7 @@ namespace Avalonia.Controls.Primitives
///
/// Defines the property.
///
- [Obsolete("Use the Placement property instead.")]
+ [Obsolete("Use the Placement property instead."), EditorBrowsable(EditorBrowsableState.Never)]
public static readonly StyledProperty PlacementModeProperty = PlacementProperty;
///
@@ -241,7 +241,7 @@ namespace Avalonia.Controls.Primitives
}
///
- [Obsolete("Use the Placement property instead.")]
+ [Obsolete("Use the Placement property instead."), EditorBrowsable(EditorBrowsableState.Never)]
public PlacementMode PlacementMode
{
get => GetValue(PlacementProperty);
diff --git a/src/Avalonia.Controls/Primitives/Thumb.cs b/src/Avalonia.Controls/Primitives/Thumb.cs
index 9854bdbea6..c5d73dc00f 100644
--- a/src/Avalonia.Controls/Primitives/Thumb.cs
+++ b/src/Avalonia.Controls/Primitives/Thumb.cs
@@ -4,7 +4,6 @@ using Avalonia.Controls.Automation.Peers;
using Avalonia.Controls.Metadata;
using Avalonia.Input;
using Avalonia.Interactivity;
-using Avalonia.VisualTree;
namespace Avalonia.Controls.Primitives
{
@@ -47,6 +46,12 @@ namespace Avalonia.Controls.Primitives
remove { RemoveHandler(DragCompletedEvent, value); }
}
+ internal void AdjustDrag(Vector v)
+ {
+ if (_lastPoint.HasValue)
+ _lastPoint = _lastPoint.Value + v;
+ }
+
protected override AutomationPeer OnCreateAutomationPeer() => new ThumbAutomationPeer(this);
protected virtual void OnDragStarted(VectorEventArgs e)
@@ -85,22 +90,20 @@ namespace Avalonia.Controls.Primitives
{
if (_lastPoint.HasValue)
{
- var point = e.GetPosition(null);
var ev = new VectorEventArgs
{
RoutedEvent = DragDeltaEvent,
- Vector = point - _lastPoint.Value,
+ Vector = e.GetPosition(this) - _lastPoint.Value,
};
RaiseEvent(ev);
- _lastPoint = point;
}
}
protected override void OnPointerPressed(PointerPressedEventArgs e)
{
e.Handled = true;
- _lastPoint = e.GetPosition(null);
+ _lastPoint = e.GetPosition(this);
var ev = new VectorEventArgs
{
@@ -123,7 +126,7 @@ namespace Avalonia.Controls.Primitives
var ev = new VectorEventArgs
{
RoutedEvent = DragCompletedEvent,
- Vector = (Vector)e.GetPosition(null),
+ Vector = (Vector)e.GetPosition(this),
};
RaiseEvent(ev);
diff --git a/src/Avalonia.Controls/Primitives/ToggleButton.cs b/src/Avalonia.Controls/Primitives/ToggleButton.cs
index dfaf7bbc45..fa1bc76de4 100644
--- a/src/Avalonia.Controls/Primitives/ToggleButton.cs
+++ b/src/Avalonia.Controls/Primitives/ToggleButton.cs
@@ -1,4 +1,5 @@
using System;
+using System.ComponentModel;
using Avalonia.Automation.Peers;
using Avalonia.Controls.Metadata;
using Avalonia.Data;
@@ -28,7 +29,7 @@ namespace Avalonia.Controls.Primitives
///
/// Defines the event.
///
- [Obsolete("Use IsCheckedChangedEvent instead.")]
+ [Obsolete("Use IsCheckedChangedEvent instead."), EditorBrowsable(EditorBrowsableState.Never)]
public static readonly RoutedEvent CheckedEvent =
RoutedEvent.Register(
nameof(Checked),
@@ -37,7 +38,7 @@ namespace Avalonia.Controls.Primitives
///
/// Defines the event.
///
- [Obsolete("Use IsCheckedChangedEvent instead.")]
+ [Obsolete("Use IsCheckedChangedEvent instead."), EditorBrowsable(EditorBrowsableState.Never)]
public static readonly RoutedEvent UncheckedEvent =
RoutedEvent.Register(
nameof(Unchecked),
@@ -46,7 +47,7 @@ namespace Avalonia.Controls.Primitives
///
/// Defines the event.
///
- [Obsolete("Use IsCheckedChangedEvent instead.")]
+ [Obsolete("Use IsCheckedChangedEvent instead."), EditorBrowsable(EditorBrowsableState.Never)]
public static readonly RoutedEvent IndeterminateEvent =
RoutedEvent.Register(
nameof(Indeterminate),
@@ -72,7 +73,7 @@ namespace Avalonia.Controls.Primitives
///
/// Raised when a is checked.
///
- [Obsolete("Use IsCheckedChanged instead.")]
+ [Obsolete("Use IsCheckedChanged instead."), EditorBrowsable(EditorBrowsableState.Never)]
public event EventHandler? Checked
{
add => AddHandler(CheckedEvent, value);
@@ -82,7 +83,7 @@ namespace Avalonia.Controls.Primitives
///
/// Raised when a is unchecked.
///
- [Obsolete("Use IsCheckedChanged instead.")]
+ [Obsolete("Use IsCheckedChanged instead."), EditorBrowsable(EditorBrowsableState.Never)]
public event EventHandler? Unchecked
{
add => AddHandler(UncheckedEvent, value);
@@ -92,7 +93,7 @@ namespace Avalonia.Controls.Primitives
///
/// Raised when a is neither checked nor unchecked.
///
- [Obsolete("Use IsCheckedChanged instead.")]
+ [Obsolete("Use IsCheckedChanged instead."), EditorBrowsable(EditorBrowsableState.Never)]
public event EventHandler? Indeterminate
{
add => AddHandler(IndeterminateEvent, value);
@@ -168,7 +169,7 @@ namespace Avalonia.Controls.Primitives
/// Called when becomes true.
///
/// Event arguments for the routed event that is raised by the default implementation of this method.
- [Obsolete("Use OnIsCheckedChanged instead.")]
+ [Obsolete("Use OnIsCheckedChanged instead."), EditorBrowsable(EditorBrowsableState.Never)]
protected virtual void OnChecked(RoutedEventArgs e)
{
RaiseEvent(e);
@@ -178,7 +179,7 @@ namespace Avalonia.Controls.Primitives
/// Called when becomes false.
///
/// Event arguments for the routed event that is raised by the default implementation of this method.
- [Obsolete("Use OnIsCheckedChanged instead.")]
+ [Obsolete("Use OnIsCheckedChanged instead."), EditorBrowsable(EditorBrowsableState.Never)]
protected virtual void OnUnchecked(RoutedEventArgs e)
{
RaiseEvent(e);
@@ -188,7 +189,7 @@ namespace Avalonia.Controls.Primitives
/// Called when becomes null.
///
/// Event arguments for the routed event that is raised by the default implementation of this method.
- [Obsolete("Use OnIsCheckedChanged instead.")]
+ [Obsolete("Use OnIsCheckedChanged instead."), EditorBrowsable(EditorBrowsableState.Never)]
protected virtual void OnIndeterminate(RoutedEventArgs e)
{
RaiseEvent(e);
diff --git a/src/Avalonia.Controls/Primitives/Track.cs b/src/Avalonia.Controls/Primitives/Track.cs
index fe4912a33c..f9aa5fc7a0 100644
--- a/src/Avalonia.Controls/Primitives/Track.cs
+++ b/src/Avalonia.Controls/Primitives/Track.cs
@@ -45,6 +45,8 @@ namespace Avalonia.Controls.Primitives
public static readonly StyledProperty IgnoreThumbDragProperty =
AvaloniaProperty.Register(nameof(IgnoreThumbDrag));
+ private Vector _lastDrag;
+
static Track()
{
ThumbProperty.Changed.AddClassHandler((x, e) => x.ThumbChanged(e));
@@ -245,7 +247,10 @@ namespace Avalonia.Controls.Primitives
if (Thumb != null)
{
- Thumb.Arrange(new Rect(offset, pieceSize));
+ var bounds = new Rect(offset, pieceSize);
+ var adjust = CalculateThumbAdjustment(Thumb, bounds);
+ Thumb.Arrange(bounds);
+ Thumb.AdjustDrag(adjust);
}
ThumbCenterOffset = offset.Y + (thumbLength * 0.5);
@@ -277,12 +282,16 @@ namespace Avalonia.Controls.Primitives
if (Thumb != null)
{
- Thumb.Arrange(new Rect(offset, pieceSize));
+ var bounds = new Rect(offset, pieceSize);
+ var adjust = CalculateThumbAdjustment(Thumb, bounds);
+ Thumb.Arrange(bounds);
+ Thumb.AdjustDrag(adjust);
}
ThumbCenterOffset = offset.X + (thumbLength * 0.5);
}
+ _lastDrag = default;
return arrangeSize;
}
@@ -296,6 +305,12 @@ namespace Avalonia.Controls.Primitives
}
}
+ private Vector CalculateThumbAdjustment(Thumb thumb, Rect newThumbBounds)
+ {
+ var thumbDelta = newThumbBounds.Position - thumb.Bounds.Position;
+ return _lastDrag - thumbDelta;
+ }
+
private static void CoerceLength(ref double componentLength, double trackLength)
{
if (componentLength < 0)
@@ -440,10 +455,17 @@ namespace Avalonia.Controls.Primitives
if (IgnoreThumbDrag)
return;
+ var value = Value;
+ var delta = ValueFromDistance(e.Vector.X, e.Vector.Y);
+ var factor = e.Vector / delta;
+
SetCurrentValue(ValueProperty, MathUtilities.Clamp(
- Value + ValueFromDistance(e.Vector.X, e.Vector.Y),
+ value + delta,
Minimum,
Maximum));
+
+ // Record the part of the drag that actually had effect as the last drag delta.
+ _lastDrag = (Value - value) * factor;
}
private void ShowChildren(bool visible)
diff --git a/src/Avalonia.Controls/Screens.cs b/src/Avalonia.Controls/Screens.cs
index 60d5358c49..c65aaafa4b 100644
--- a/src/Avalonia.Controls/Screens.cs
+++ b/src/Avalonia.Controls/Screens.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.ComponentModel;
using System.Linq;
using Avalonia.Platform;
@@ -68,7 +69,7 @@ namespace Avalonia.Controls
///
/// The window impl for which to retrieve the Screen.
/// The .
- [Obsolete("Use ScreenFromWindow(WindowBase) overload.")]
+ [Obsolete("Use ScreenFromWindow(WindowBase) overload."), EditorBrowsable(EditorBrowsableState.Never)]
public Screen? ScreenFromWindow(IWindowBaseImpl window)
{
return _iScreenImpl.ScreenFromWindow(window);
diff --git a/src/Avalonia.Controls/SystemDialog.cs b/src/Avalonia.Controls/SystemDialog.cs
index d2b893df37..8d4dab11d7 100644
--- a/src/Avalonia.Controls/SystemDialog.cs
+++ b/src/Avalonia.Controls/SystemDialog.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using Avalonia.Controls.Platform;
@@ -11,7 +12,7 @@ namespace Avalonia.Controls
///
/// Base class for system file dialogs.
///
- [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API")]
+ [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API"), EditorBrowsable(EditorBrowsableState.Never)]
public abstract class FileDialog : FileSystemDialog
{
///
@@ -29,7 +30,7 @@ namespace Avalonia.Controls
///
/// Base class for system file and directory dialogs.
///
- [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API")]
+ [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API"), EditorBrowsable(EditorBrowsableState.Never)]
public abstract class FileSystemDialog : SystemDialog
{
///
@@ -42,7 +43,7 @@ namespace Avalonia.Controls
///
/// Represents a system dialog that prompts the user to select a location for saving a file.
///
- [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API")]
+ [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API"), EditorBrowsable(EditorBrowsableState.Never)]
public class SaveFileDialog : FileDialog
{
///
@@ -91,7 +92,7 @@ namespace Avalonia.Controls
///
/// Represents a system dialog that allows the user to select one or more files to open.
///
- [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API")]
+ [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API"), EditorBrowsable(EditorBrowsableState.Never)]
public class OpenFileDialog : FileDialog
{
///
@@ -132,7 +133,7 @@ namespace Avalonia.Controls
///
/// Represents a system dialog that allows the user to select a directory.
///
- [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API")]
+ [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API"), EditorBrowsable(EditorBrowsableState.Never)]
public class OpenFolderDialog : FileSystemDialog
{
///
@@ -167,7 +168,7 @@ namespace Avalonia.Controls
///
/// Base class for system dialogs.
///
- [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API")]
+ [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API"), EditorBrowsable(EditorBrowsableState.Never)]
public abstract class SystemDialog
{
static SystemDialog()
@@ -188,7 +189,7 @@ namespace Avalonia.Controls
///
/// Represents a filter in an or an .
///
- [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API")]
+ [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API"), EditorBrowsable(EditorBrowsableState.Never)]
public class FileDialogFilter
{
///
diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs
index 19eaaaa0d9..155d7d5f56 100644
--- a/src/Avalonia.Controls/TextBlock.cs
+++ b/src/Avalonia.Controls/TextBlock.cs
@@ -7,7 +7,6 @@ using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.Media.TextFormatting;
using Avalonia.Metadata;
-using Avalonia.Utilities;
namespace Avalonia.Controls
{
@@ -565,7 +564,8 @@ namespace Avalonia.Controls
context.FillRectangle(background, new Rect(Bounds.Size));
}
- var padding = Padding;
+ var scale = LayoutHelper.GetLayoutScale(this);
+ var padding = LayoutHelper.RoundLayoutThickness(Padding, scale, scale);
var top = padding.Top;
var textHeight = TextLayout.Bounds.Height;
@@ -659,7 +659,6 @@ namespace Avalonia.Controls
protected override Size MeasureOverride(Size availableSize)
{
var scale = LayoutHelper.GetLayoutScale(this);
-
var padding = LayoutHelper.RoundLayoutThickness(Padding, scale, scale);
_constraint = availableSize.Deflate(padding);
@@ -703,19 +702,24 @@ namespace Avalonia.Controls
}
}
- var measuredSize = TextLayout.Bounds.Size.Inflate(padding);
-
- return measuredSize;
+ return TextLayout.Bounds.Size.Inflate(padding);
}
protected override Size ArrangeOverride(Size finalSize)
{
- if (HasComplexContent)
- {
- var scale = LayoutHelper.GetLayoutScale(this);
+ var scale = LayoutHelper.GetLayoutScale(this);
+ var padding = LayoutHelper.RoundLayoutThickness(Padding, scale, scale);
- var padding = LayoutHelper.RoundLayoutThickness(Padding, scale, scale);
+ //Fixes: #11019
+ if (finalSize.Width < _constraint.Width)
+ {
+ _textLayout?.Dispose();
+ _textLayout = null;
+ _constraint = finalSize.Deflate(padding);
+ }
+ if (HasComplexContent)
+ {
var currentY = padding.Top;
foreach (var textLine in TextLayout.TextLines)
@@ -730,7 +734,7 @@ namespace Avalonia.Controls
&& controlRun.Control is Control control)
{
control.Arrange(
- new Rect(new Point(currentX, currentY),
+ new Rect(new Point(currentX, currentY),
new Size(control.DesiredSize.Width, textLine.Height)));
}
diff --git a/src/Avalonia.Controls/TreeView.cs b/src/Avalonia.Controls/TreeView.cs
index 149a95b6c7..0d5a453141 100644
--- a/src/Avalonia.Controls/TreeView.cs
+++ b/src/Avalonia.Controls/TreeView.cs
@@ -3,6 +3,7 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
+using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Avalonia.Collections;
@@ -722,7 +723,7 @@ namespace Avalonia.Controls
}
}
- [Obsolete]
+ [Obsolete, EditorBrowsable(EditorBrowsableState.Never)]
private protected override ItemContainerGenerator CreateItemContainerGenerator()
{
return new TreeItemContainerGenerator(this);
diff --git a/src/Avalonia.Controls/TreeViewItem.cs b/src/Avalonia.Controls/TreeViewItem.cs
index 958a381f1c..ccd4f89204 100644
--- a/src/Avalonia.Controls/TreeViewItem.cs
+++ b/src/Avalonia.Controls/TreeViewItem.cs
@@ -165,7 +165,7 @@ namespace Avalonia.Controls
{
Key.Left => ApplyToItemOrRecursivelyIfCtrl(FocusAwareCollapseItem, e.KeyModifiers),
Key.Right => ApplyToItemOrRecursivelyIfCtrl(ExpandItem, e.KeyModifiers),
- Key.Enter or Key.Space => ApplyToItemOrRecursivelyIfCtrl(IsExpanded ? CollapseItem : ExpandItem, e.KeyModifiers),
+ Key.Enter => ApplyToItemOrRecursivelyIfCtrl(IsExpanded ? CollapseItem : ExpandItem, e.KeyModifiers),
// do not handle CTRL with numpad keys
Key.Subtract => FocusAwareCollapseItem,
diff --git a/src/Avalonia.Controls/Utils/StringUtils.cs b/src/Avalonia.Controls/Utils/StringUtils.cs
index 6c459d7985..2ea47b442a 100644
--- a/src/Avalonia.Controls/Utils/StringUtils.cs
+++ b/src/Avalonia.Controls/Utils/StringUtils.cs
@@ -1,3 +1,4 @@
+using System;
using System.Globalization;
using Avalonia.Media.TextFormatting.Unicode;
@@ -122,6 +123,8 @@ namespace Avalonia.Controls.Utils
{
return 0;
}
+
+ cursor = Math.Min(cursor, text.Length);
int begin;
int i;
diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs
index b9b4ccb0b4..d9db115792 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs
+++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs
@@ -522,7 +522,6 @@ namespace Avalonia.Diagnostics.ViewModels
.GroupBy(x => x.Key)
.ToDictionary(x => x.Key, x => x.ToArray());
- TreePage.PropertiesFilter.FilterString = string.Empty;
var view = new DataGridCollectionView(properties);
view.GroupDescriptions.Add(new DataGridPathGroupDescription(nameof(AvaloniaPropertyViewModel.Group)));
diff --git a/src/Avalonia.Dialogs/ManagedFileDialogExtensions.cs b/src/Avalonia.Dialogs/ManagedFileDialogExtensions.cs
index e9a75ab46a..86c0bfc588 100644
--- a/src/Avalonia.Dialogs/ManagedFileDialogExtensions.cs
+++ b/src/Avalonia.Dialogs/ManagedFileDialogExtensions.cs
@@ -1,6 +1,7 @@
#nullable enable
using System;
+using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using Avalonia.Controls;
@@ -39,11 +40,11 @@ namespace Avalonia.Dialogs
return builder;
}
- [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API")]
+ [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API"), EditorBrowsable(EditorBrowsableState.Never)]
public static Task ShowManagedAsync(this OpenFileDialog dialog, Window parent,
ManagedFileDialogOptions? options = null) => ShowManagedAsync(dialog, parent, options);
- [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API")]
+ [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API"), EditorBrowsable(EditorBrowsableState.Never)]
public static async Task ShowManagedAsync(this OpenFileDialog dialog, Window parent,
ManagedFileDialogOptions? options = null) where TWindow : Window, new()
{
diff --git a/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj b/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj
index 4805c3a034..3401da2d4a 100644
--- a/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj
+++ b/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj
@@ -13,7 +13,7 @@
-
+
diff --git a/src/Avalonia.Native/AvaloniaNativeDragSource.cs b/src/Avalonia.Native/AvaloniaNativeDragSource.cs
index 7f4c462ee0..5063c7a0a0 100644
--- a/src/Avalonia.Native/AvaloniaNativeDragSource.cs
+++ b/src/Avalonia.Native/AvaloniaNativeDragSource.cs
@@ -4,7 +4,6 @@ using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Input.Platform;
-using Avalonia.Interactivity;
using Avalonia.Native.Interop;
namespace Avalonia.Native
@@ -18,16 +17,6 @@ namespace Avalonia.Native
_factory = factory;
}
- private static TopLevel FindRoot(object? element)
- {
- while (element is Interactive interactive && element is not Visual)
- element = interactive.GetInteractiveParent();
- if (element == null)
- return null;
- var visual = (Visual)element;
- return TopLevel.GetTopLevel(visual);
- }
-
class DndCallback : NativeCallbackBase, IAvnDndResultCallback
{
private TaskCompletionSource _tcs;
@@ -46,7 +35,7 @@ namespace Avalonia.Native
public Task DoDragDrop(PointerEventArgs triggerEvent, IDataObject data, DragDropEffects allowedEffects)
{
// Sanity check
- var tl = FindRoot(triggerEvent.Source);
+ var tl = TopLevel.GetTopLevel(triggerEvent.Source as Visual);
var view = tl?.PlatformImpl as WindowBaseImpl;
if (view == null)
throw new ArgumentException();
diff --git a/src/Avalonia.Native/WindowImplBase.cs b/src/Avalonia.Native/WindowImplBase.cs
index 26c3da9d50..b802b1db71 100644
--- a/src/Avalonia.Native/WindowImplBase.cs
+++ b/src/Avalonia.Native/WindowImplBase.cs
@@ -220,17 +220,17 @@ namespace Avalonia.Native
_parent.PositionChanged?.Invoke(position.ToAvaloniaPixelPoint());
}
- void IAvnWindowBaseEvents.RawMouseEvent(AvnRawMouseEventType type, uint timeStamp, AvnInputModifiers modifiers, AvnPoint point, AvnVector delta)
+ void IAvnWindowBaseEvents.RawMouseEvent(AvnRawMouseEventType type, ulong timeStamp, AvnInputModifiers modifiers, AvnPoint point, AvnVector delta)
{
_parent.RawMouseEvent(type, timeStamp, modifiers, point, delta);
}
- int IAvnWindowBaseEvents.RawKeyEvent(AvnRawKeyEventType type, uint timeStamp, AvnInputModifiers modifiers, uint key)
+ int IAvnWindowBaseEvents.RawKeyEvent(AvnRawKeyEventType type, ulong timeStamp, AvnInputModifiers modifiers, uint key)
{
return _parent.RawKeyEvent(type, timeStamp, modifiers, key).AsComBool();
}
- int IAvnWindowBaseEvents.RawTextInputEvent(uint timeStamp, string text)
+ int IAvnWindowBaseEvents.RawTextInputEvent(ulong timeStamp, string text)
{
return _parent.RawTextInputEvent(timeStamp, text).AsComBool();
}
@@ -286,7 +286,7 @@ namespace Avalonia.Native
_native?.Activate();
}
- public bool RawTextInputEvent(uint timeStamp, string text)
+ public bool RawTextInputEvent(ulong timeStamp, string text)
{
if (_inputRoot is null)
return false;
@@ -300,7 +300,7 @@ namespace Avalonia.Native
return args.Handled;
}
- public bool RawKeyEvent(AvnRawKeyEventType type, uint timeStamp, AvnInputModifiers modifiers, uint key)
+ public bool RawKeyEvent(AvnRawKeyEventType type, ulong timeStamp, AvnInputModifiers modifiers, uint key)
{
if (_inputRoot is null)
return false;
@@ -319,7 +319,7 @@ namespace Avalonia.Native
return false;
}
- public void RawMouseEvent(AvnRawMouseEventType type, uint timeStamp, AvnInputModifiers modifiers, AvnPoint point, AvnVector delta)
+ public void RawMouseEvent(AvnRawMouseEventType type, ulong timeStamp, AvnInputModifiers modifiers, AvnPoint point, AvnVector delta)
{
if (_inputRoot is null)
return;
diff --git a/src/Avalonia.Native/avn.idl b/src/Avalonia.Native/avn.idl
index 09e9168d8f..a58a00d59d 100644
--- a/src/Avalonia.Native/avn.idl
+++ b/src/Avalonia.Native/avn.idl
@@ -1,6 +1,7 @@
@clr-namespace Avalonia.Native.Interop
@clr-access internal
@clr-map bool int
+@clr-map u_int64_t ulong
@cpp-preamble @@
#pragma once
#include "com.h"
@@ -583,12 +584,12 @@ interface IAvnWindowBaseEvents : IUnknown
void Resized([const] AvnSize& size, AvnPlatformResizeReason reason);
void PositionChanged(AvnPoint position);
void RawMouseEvent(AvnRawMouseEventType type,
- uint timeStamp,
+ u_int64_t timeStamp,
AvnInputModifiers modifiers,
AvnPoint point,
AvnVector delta);
- bool RawKeyEvent(AvnRawKeyEventType type, uint timeStamp, AvnInputModifiers modifiers, uint key);
- bool RawTextInputEvent(uint timeStamp, [const] char* text);
+ bool RawKeyEvent(AvnRawKeyEventType type, u_int64_t timeStamp, AvnInputModifiers modifiers, uint key);
+ bool RawTextInputEvent(u_int64_t timeStamp, [const] char* text);
void ScalingChanged(double scaling);
void RunRenderPriorityJobs();
void LostFocus();
diff --git a/src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs b/src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs
index 7e73397743..83bf795b03 100644
--- a/src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs
+++ b/src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs
@@ -8,6 +8,8 @@ using Avalonia.Rendering;
using Avalonia.Rendering.Composition;
using Avalonia.VisualTree;
using Avalonia.Platform;
+using System.ComponentModel;
+
namespace Avalonia.OpenGL.Controls
{
public abstract class OpenGlControlBase : Control
@@ -217,7 +219,7 @@ namespace Avalonia.OpenGL.Controls
return true;
}
- [Obsolete("Use RequestNextFrameRendering()")]
+ [Obsolete("Use RequestNextFrameRendering()"), EditorBrowsable(EditorBrowsableState.Never)]
// ReSharper disable once MemberCanBeProtected.Global
public new void InvalidateVisual() => RequestNextFrameRendering();
diff --git a/src/Browser/Avalonia.Browser/webapp/modules/avalonia/input.ts b/src/Browser/Avalonia.Browser/webapp/modules/avalonia/input.ts
index bbc59aba1c..77166e6f21 100644
--- a/src/Browser/Avalonia.Browser/webapp/modules/avalonia/input.ts
+++ b/src/Browser/Avalonia.Browser/webapp/modules/avalonia/input.ts
@@ -266,7 +266,7 @@ export class InputHelper {
}
public static setCursor(inputElement: HTMLInputElement, kind: string) {
- if (kind === "pointer") {
+ if (kind === "default") {
inputElement.style.removeProperty("cursor");
} else {
inputElement.style.cursor = kind;
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 55a1014188..51f6cc92e9 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -3,10 +3,5 @@
-
-
- Shared\_ModuleInitializer.cs
- false
-
-
+
diff --git a/src/Headless/Avalonia.Headless/HeadlessPlatformRenderInterface.cs b/src/Headless/Avalonia.Headless/HeadlessPlatformRenderInterface.cs
index 38375045cb..04e1ea99a5 100644
--- a/src/Headless/Avalonia.Headless/HeadlessPlatformRenderInterface.cs
+++ b/src/Headless/Avalonia.Headless/HeadlessPlatformRenderInterface.cs
@@ -2,11 +2,9 @@ using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
-using System.Numerics;
using System.Runtime.InteropServices;
using Avalonia.Media;
using Avalonia.Platform;
-using Avalonia.Rendering;
using Avalonia.Rendering.SceneGraph;
using Avalonia.Utilities;
using Avalonia.Media.Imaging;
@@ -127,7 +125,8 @@ namespace Avalonia.Headless
IGlyphTypeface glyphTypeface,
double fontRenderingEmSize,
IReadOnlyList glyphInfos,
- Point baselineOrigin)
+ Point baselineOrigin,
+ Rect bounds)
{
return new HeadlessGlyphRunStub();
}
@@ -138,6 +137,10 @@ namespace Avalonia.Headless
public Point BaselineOrigin => new Point(0, 8);
+ public IGlyphTypeface GlyphTypeface => new HeadlessGlyphTypefaceImpl();
+
+ public double FontRenderingEmSize => 12;
+
public void Dispose()
{
}
@@ -374,6 +377,8 @@ namespace Avalonia.Headless
public Matrix Transform { get; set; }
+ public RenderOptions RenderOptions { get; set; }
+
public void Clear(Color color)
{
@@ -451,7 +456,7 @@ namespace Avalonia.Headless
{
}
- public void DrawBitmap(IRef source, double opacity, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode = BitmapInterpolationMode.Default)
+ public void DrawBitmap(IRef source, double opacity, Rect sourceRect, Rect destRect)
{
}
diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs
index cea55f4d29..753fa0203c 100644
--- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs
+++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs
@@ -24,10 +24,8 @@ namespace Avalonia.Skia
private readonly Vector _dpi;
private readonly Stack _maskStack = new();
private readonly Stack _opacityStack = new();
- private readonly Stack _blendingModeStack = new();
private readonly Matrix? _postTransform;
private double _currentOpacity = 1.0f;
- private BitmapBlendingMode _currentBlendingMode = BitmapBlendingMode.SourceOver;
private readonly bool _canTextUseLcdRendering;
private Matrix _currentTransform;
private bool _disposed;
@@ -168,6 +166,8 @@ namespace Avalonia.Skia
public SKCanvas Canvas { get; }
public SKSurface? Surface { get; }
+ public RenderOptions RenderOptions { get; set; }
+
private void CheckLease()
{
if (_leased)
@@ -182,7 +182,7 @@ namespace Avalonia.Skia
}
///
- public void DrawBitmap(IRef source, double opacity, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode)
+ public void DrawBitmap(IRef source, double opacity, Rect sourceRect, Rect destRect)
{
CheckLease();
var drawableImage = (IDrawableBitmapImpl)source.Item;
@@ -191,8 +191,8 @@ namespace Avalonia.Skia
var paint = SKPaintCache.Shared.Get();
paint.Color = new SKColor(255, 255, 255, (byte)(255 * opacity * (_useOpacitySaveLayer ? 1 : _currentOpacity)));
- paint.FilterQuality = bitmapInterpolationMode.ToSKFilterQuality();
- paint.BlendMode = _currentBlendingMode.ToSKBlendMode();
+ paint.FilterQuality = RenderOptions.BitmapInterpolationMode.ToSKFilterQuality();
+ paint.BlendMode = RenderOptions.BitmapBlendingMode.ToSKBlendMode();
drawableImage.Draw(this, s, d, paint);
SKPaintCache.Shared.ReturnReset(paint);
@@ -203,7 +203,7 @@ namespace Avalonia.Skia
{
CheckLease();
PushOpacityMask(opacityMask, opacityMaskRect);
- DrawBitmap(source, 1, new Rect(0, 0, source.Item.PixelSize.Width, source.Item.PixelSize.Height), destRect, BitmapInterpolationMode.Default);
+ DrawBitmap(source, 1, new Rect(0, 0, source.Item.PixelSize.Width, source.Item.PixelSize.Height), destRect);
PopOpacityMask();
}
@@ -519,7 +519,9 @@ namespace Avalonia.Skia
{
var glyphRunImpl = (GlyphRunImpl)glyphRun.Item;
- Canvas.DrawText(glyphRunImpl.TextBlob, (float)glyphRun.Item.BaselineOrigin.X,
+ var textBlob = glyphRunImpl.GetTextBlob(RenderOptions);
+
+ Canvas.DrawText(textBlob, (float)glyphRun.Item.BaselineOrigin.X,
(float)glyphRun.Item.BaselineOrigin.Y, paintWrapper.Paint);
}
}
@@ -649,21 +651,6 @@ namespace Avalonia.Skia
Canvas.Restore();
}
- ///
- public void PushBitmapBlendMode(BitmapBlendingMode blendingMode)
- {
- CheckLease();
- _blendingModeStack.Push(_currentBlendingMode);
- _currentBlendingMode = blendingMode;
- }
-
- ///
- public void PopBitmapBlendMode()
- {
- CheckLease();
- _currentBlendingMode = _blendingModeStack.Pop();
- }
-
///
public void PushOpacityMask(IBrush mask, Rect bounds)
{
@@ -905,12 +892,14 @@ namespace Avalonia.Skia
context.Clear(Colors.Transparent);
context.PushClip(calc.IntermediateClip);
context.Transform = calc.IntermediateTransform;
+ context.RenderOptions = RenderOptions;
+
context.DrawBitmap(
RefCountable.CreateUnownedNotClonable(tileBrushImage),
1,
sourceRect,
- targetRect,
- tileBrush.BitmapInterpolationMode);
+ targetRect);
+
context.PopClip();
}
@@ -1143,7 +1132,7 @@ namespace Avalonia.Skia
{
var paintWrapper = new PaintWrapper(paint);
- paint.IsAntialias = true;
+ paint.IsAntialias = RenderOptions.EdgeMode != EdgeMode.Aliased;
double opacity = brush.Opacity * (_useOpacitySaveLayer ? 1 :_currentOpacity);
diff --git a/src/Skia/Avalonia.Skia/GlyphRunImpl.cs b/src/Skia/Avalonia.Skia/GlyphRunImpl.cs
index 0521e238f3..7331740b56 100644
--- a/src/Skia/Avalonia.Skia/GlyphRunImpl.cs
+++ b/src/Skia/Avalonia.Skia/GlyphRunImpl.cs
@@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
+using Avalonia.Media;
+using Avalonia.Media.TextFormatting;
using Avalonia.Platform;
using SkiaSharp;
@@ -7,30 +9,122 @@ namespace Avalonia.Skia
{
internal class GlyphRunImpl : IGlyphRunImpl
{
- public GlyphRunImpl(SKTextBlob textBlob, Size size, Point baselineOrigin)
+ private readonly GlyphTypefaceImpl _glyphTypefaceImpl;
+ private readonly ushort[] _glyphIndices;
+ private readonly SKPoint[] _glyphPositions;
+
+ private readonly Dictionary _textBlobCache = new(1);
+
+ public GlyphRunImpl(IGlyphTypeface glyphTypeface, double fontRenderingEmSize,
+ IReadOnlyList glyphInfos, Point baselineOrigin, Rect bounds)
{
- TextBlob = textBlob ?? throw new ArgumentNullException(nameof(textBlob));
+ if (glyphTypeface == null)
+ {
+ throw new ArgumentNullException(nameof(glyphTypeface));
+ }
+
+ _glyphTypefaceImpl = (GlyphTypefaceImpl)glyphTypeface;
+
+ if (glyphInfos == null)
+ {
+ throw new ArgumentNullException(nameof(glyphInfos));
+ }
+
+ var count = glyphInfos.Count;
+ _glyphIndices = new ushort[count];
+ _glyphPositions = new SKPoint[count];
+
+ var currentX = 0.0;
+
+ for (int i = 0; i < count; i++)
+ {
+ var glyphInfo = glyphInfos[i];
+ var offset = glyphInfo.GlyphOffset;
+
+ _glyphIndices[i] = glyphInfo.GlyphIndex;
- Bounds = new Rect(new Point(baselineOrigin.X, 0), size);
+ _glyphPositions[i] = new SKPoint((float)(currentX + offset.X), (float)offset.Y);
+ currentX += glyphInfos[i].GlyphAdvance;
+ }
+
+ FontRenderingEmSize = fontRenderingEmSize;
BaselineOrigin = baselineOrigin;
+ Bounds = bounds;
}
- ///
- /// Gets the text blob to draw.
- ///
- public SKTextBlob TextBlob { get; }
+ public IGlyphTypeface GlyphTypeface => _glyphTypefaceImpl;
- public Rect Bounds { get; }
+ public double FontRenderingEmSize { get; }
public Point BaselineOrigin { get; }
- public IReadOnlyList GetIntersections(float upperBound, float lowerBound) =>
- TextBlob.GetIntercepts(lowerBound, upperBound);
+ public Rect Bounds { get; }
+
+ public SKTextBlob GetTextBlob(RenderOptions renderOptions)
+ {
+ var edging = SKFontEdging.SubpixelAntialias;
+
+ switch (renderOptions.TextRenderingMode)
+ {
+ case TextRenderingMode.Alias:
+ edging = SKFontEdging.Alias;
+ break;
+ case TextRenderingMode.Antialias:
+ edging = SKFontEdging.Antialias;
+ break;
+ case TextRenderingMode.Unspecified:
+ edging = renderOptions.EdgeMode == EdgeMode.Aliased ? SKFontEdging.Alias : SKFontEdging.SubpixelAntialias;
+ break;
+ }
+
+ if (_textBlobCache.TryGetValue(edging, out var textBlob))
+ {
+ return textBlob;
+ }
- void IDisposable.Dispose()
+ var font = SKFontCache.Shared.Get();
+
+ font.LinearMetrics = true;
+ font.Subpixel = edging == SKFontEdging.SubpixelAntialias;
+ font.Edging = edging;
+ font.Hinting = SKFontHinting.Full;
+ font.Size = (float)FontRenderingEmSize;
+ font.Typeface = _glyphTypefaceImpl.Typeface;
+ font.Embolden = (_glyphTypefaceImpl.FontSimulations & FontSimulations.Bold) != 0;
+ font.SkewX = (_glyphTypefaceImpl.FontSimulations & FontSimulations.Oblique) != 0 ? -0.2f : 0;
+
+ var builder = SKTextBlobBuilderCache.Shared.Get();
+
+ var runBuffer = builder.AllocatePositionedRun(font, _glyphIndices.Length);
+
+ runBuffer.SetPositions(_glyphPositions);
+ runBuffer.SetGlyphs(_glyphIndices);
+
+ SKFontCache.Shared.Return(font);
+
+ textBlob = builder.Build();
+
+ SKTextBlobBuilderCache.Shared.Return(builder);
+
+ _textBlobCache.Add(edging, textBlob);
+
+ return textBlob;
+ }
+
+ public void Dispose()
{
- TextBlob.Dispose();
+ foreach (var pair in _textBlobCache)
+ {
+ pair.Value.Dispose();
+ }
+ }
+
+ public IReadOnlyList GetIntersections(float lowerLimit, float upperLimit)
+ {
+ var textBlob = GetTextBlob(default);
+
+ return textBlob.GetIntercepts(lowerLimit, upperLimit);
}
}
}
diff --git a/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs b/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs
index 43e10e3e96..8b13cae802 100644
--- a/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs
+++ b/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs
@@ -27,24 +27,24 @@ namespace Avalonia.Skia
const double defaultFontRenderingEmSize = 12.0;
+ Font.OpenTypeMetrics.TryGetPosition(OpenTypeMetricsTag.HorizontalAscender, out var ascent);
+ Font.OpenTypeMetrics.TryGetPosition(OpenTypeMetricsTag.HorizontalDescender, out var descent);
+ Font.OpenTypeMetrics.TryGetPosition(OpenTypeMetricsTag.HorizontalLineGap, out var lineGap);
+ Font.OpenTypeMetrics.TryGetPosition(OpenTypeMetricsTag.StrikeoutOffset, out var strikethroughOffset);
+ Font.OpenTypeMetrics.TryGetPosition(OpenTypeMetricsTag.StrikeoutSize, out var strikethroughSize);
+ Font.OpenTypeMetrics.TryGetPosition(OpenTypeMetricsTag.UnderlineOffset, out var underlineOffset);
+ Font.OpenTypeMetrics.TryGetPosition(OpenTypeMetricsTag.UnderlineSize, out var underlineSize);
+
Metrics = new FontMetrics
{
DesignEmHeight = (short)Typeface.UnitsPerEm,
- Ascent = (int)(metrics.Ascent / defaultFontRenderingEmSize * Typeface.UnitsPerEm),
- Descent = (int)(metrics.Descent / defaultFontRenderingEmSize * Typeface.UnitsPerEm),
- LineGap = (int)(metrics.Leading / defaultFontRenderingEmSize * Typeface.UnitsPerEm),
- UnderlinePosition = metrics.UnderlinePosition != null ?
- (int)(metrics.UnderlinePosition / defaultFontRenderingEmSize * Typeface.UnitsPerEm) :
- 0,
- UnderlineThickness = metrics.UnderlineThickness != null ?
- (int)(metrics.UnderlineThickness / defaultFontRenderingEmSize * Typeface.UnitsPerEm) :
- 0,
- StrikethroughPosition = metrics.StrikeoutPosition != null ?
- (int)(metrics.StrikeoutPosition / defaultFontRenderingEmSize * Typeface.UnitsPerEm) :
- 0,
- StrikethroughThickness = metrics.StrikeoutThickness != null ?
- (int)(metrics.StrikeoutThickness / defaultFontRenderingEmSize * Typeface.UnitsPerEm) :
- 0,
+ Ascent = -ascent,
+ Descent = -descent,
+ LineGap = lineGap,
+ UnderlinePosition = -underlineOffset,
+ UnderlineThickness = underlineSize,
+ StrikethroughPosition = -strikethroughOffset,
+ StrikethroughThickness = strikethroughSize,
IsFixedPitch = Typeface.IsFixedPitch
};
diff --git a/src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs b/src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs
index 797c565ca1..ce292eb826 100644
--- a/src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs
+++ b/src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs
@@ -1,5 +1,4 @@
using Avalonia.Platform;
-using Avalonia.Rendering;
namespace Avalonia.Skia
{
diff --git a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs
index a9a79ff0c5..9e912db797 100644
--- a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs
+++ b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs
@@ -5,8 +5,8 @@ using Avalonia.Media;
using Avalonia.OpenGL;
using Avalonia.Platform;
using Avalonia.Media.Imaging;
-using Avalonia.Media.TextFormatting;
using SkiaSharp;
+using Avalonia.Media.TextFormatting;
namespace Avalonia.Skia
{
@@ -191,16 +191,7 @@ namespace Avalonia.Skia
throw new ArgumentException("Height can't be less than 1", nameof(size));
}
- var createInfo = new SurfaceRenderTarget.CreateInfo
- {
- Width = size.Width,
- Height = size.Height,
- Dpi = dpi,
- DisableTextLcdRendering = false,
- DisableManualFbo = true,
- };
-
- return new SurfaceRenderTarget(createInfo);
+ return new RenderTargetBitmapImpl(size, dpi);
}
///
@@ -209,67 +200,10 @@ namespace Avalonia.Skia
return new WriteableBitmapImpl(size, dpi, format, alphaFormat);
}
- public IGlyphRunImpl CreateGlyphRun(
- IGlyphTypeface glyphTypeface,
- double fontRenderingEmSize,
- IReadOnlyList glyphInfos,
- Point baselineOrigin)
+ public IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize,
+ IReadOnlyList glyphInfos, Point baselineOrigin, Rect bounds)
{
- if (glyphTypeface == null)
- {
- throw new ArgumentNullException(nameof(glyphTypeface));
- }
-
- if (glyphInfos == null)
- {
- throw new ArgumentNullException(nameof(glyphInfos));
- }
-
- var glyphTypefaceImpl = (GlyphTypefaceImpl)glyphTypeface;
-
- var font = SKFontCache.Shared.Get();
-
- font.LinearMetrics = true;
- font.Subpixel = true;
- font.Edging = SKFontEdging.SubpixelAntialias;
- font.Hinting = SKFontHinting.Full;
- font.Size = (float)fontRenderingEmSize;
- font.Typeface = glyphTypefaceImpl.Typeface;
- font.Embolden = (glyphTypefaceImpl.FontSimulations & FontSimulations.Bold) != 0;
- font.SkewX = (glyphTypefaceImpl.FontSimulations & FontSimulations.Oblique) != 0 ? -0.2f : 0;
-
-
- var builder = SKTextBlobBuilderCache.Shared.Get();
- var count = glyphInfos.Count;
-
- var runBuffer = builder.AllocatePositionedRun(font, count);
-
- var glyphSpan = runBuffer.GetGlyphSpan();
- var positionSpan = runBuffer.GetPositionSpan();
-
- SKFontCache.Shared.Return(font);
-
- var width = 0.0;
-
- for (int i = 0; i < count; i++)
- {
- var glyphInfo = glyphInfos[i];
- var offset = glyphInfo.GlyphOffset;
-
- glyphSpan[i] = glyphInfo.GlyphIndex;
-
- positionSpan[i] = new SKPoint((float)(width + offset.X), (float)offset.Y);
-
- width += glyphInfo.GlyphAdvance;
- }
-
- var scale = fontRenderingEmSize / glyphTypeface.Metrics.DesignEmHeight;
- var height = glyphTypeface.Metrics.LineSpacing * scale;
- var skTextBlob = builder.Build();
-
- SKTextBlobBuilderCache.Shared.Return(builder);
-
- return new GlyphRunImpl(skTextBlob, new Size(width, height), baselineOrigin);
+ return new GlyphRunImpl(glyphTypeface, fontRenderingEmSize, glyphInfos, baselineOrigin, bounds);
}
}
}
diff --git a/src/Skia/Avalonia.Skia/RenderTargetBitmapImpl.cs b/src/Skia/Avalonia.Skia/RenderTargetBitmapImpl.cs
new file mode 100644
index 0000000000..e20755b4e2
--- /dev/null
+++ b/src/Skia/Avalonia.Skia/RenderTargetBitmapImpl.cs
@@ -0,0 +1,31 @@
+using System.IO;
+using Avalonia.Controls.Platform.Surfaces;
+using Avalonia.Media.Imaging;
+using Avalonia.Platform;
+using SkiaSharp;
+
+namespace Avalonia.Skia;
+
+internal class RenderTargetBitmapImpl : WriteableBitmapImpl,
+ IRenderTargetBitmapImpl,
+ IFramebufferPlatformSurface
+{
+ private readonly FramebufferRenderTarget _renderTarget;
+
+ public RenderTargetBitmapImpl(PixelSize size, Vector dpi) : base(size, dpi,
+ SKImageInfo.PlatformColorType == SKColorType.Rgba8888 ? PixelFormats.Rgba8888 : PixelFormat.Bgra8888,
+ AlphaFormat.Premul)
+ {
+ _renderTarget = new FramebufferRenderTarget(this);
+ }
+
+ public IDrawingContextImpl CreateDrawingContext() => _renderTarget.CreateDrawingContext();
+
+ public bool IsCorrupted => false;
+
+ public override void Dispose()
+ {
+ _renderTarget.Dispose();
+ base.Dispose();
+ }
+}
\ No newline at end of file
diff --git a/src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs b/src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs
index c66b53284a..a1fdfdbd2e 100644
--- a/src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs
+++ b/src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs
@@ -19,7 +19,8 @@ namespace Avalonia.Skia
return SKFilterQuality.Medium;
case BitmapInterpolationMode.HighQuality:
return SKFilterQuality.High;
- case BitmapInterpolationMode.Default:
+ case BitmapInterpolationMode.None:
+ case BitmapInterpolationMode.Unspecified:
return SKFilterQuality.None;
default:
throw new ArgumentOutOfRangeException(nameof(interpolationMode), interpolationMode, null);
@@ -30,6 +31,7 @@ namespace Avalonia.Skia
{
switch (blendingMode)
{
+ case BitmapBlendingMode.Unspecified:
case BitmapBlendingMode.SourceOver:
return SKBlendMode.SrcOver;
case BitmapBlendingMode.Source:
diff --git a/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs b/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs
index 8ea7434c23..38df7b2933 100644
--- a/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs
+++ b/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs
@@ -131,7 +131,7 @@ namespace Avalonia.Skia
}
///
- public void Dispose()
+ public virtual void Dispose()
{
_bitmap.Dispose();
}
diff --git a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
index 826296b055..fc1a358492 100644
--- a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
+++ b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
@@ -160,59 +160,9 @@ namespace Avalonia.Direct2D1
public IStreamGeometryImpl CreateStreamGeometry() => new StreamGeometryImpl();
public IGeometryImpl CreateGeometryGroup(FillRule fillRule, IReadOnlyList children) => new GeometryGroupImpl(fillRule, children);
public IGeometryImpl CreateCombinedGeometry(GeometryCombineMode combineMode, IGeometryImpl g1, IGeometryImpl g2) => new CombinedGeometryImpl(combineMode, g1, g2);
-
- public IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize,
- IReadOnlyList glyphInfos, Point baselineOrigin)
+ public IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize, IReadOnlyList glyphInfos, Point baselineOrigin, Rect bounds)
{
- var glyphTypefaceImpl = (GlyphTypefaceImpl)glyphTypeface;
-
- var glyphCount = glyphInfos.Count;
-
- var run = new SharpDX.DirectWrite.GlyphRun
- {
- FontFace = glyphTypefaceImpl.FontFace,
- FontSize = (float)fontRenderingEmSize
- };
-
- var indices = new short[glyphCount];
-
- for (var i = 0; i < glyphCount; i++)
- {
- indices[i] = (short)glyphInfos[i].GlyphIndex;
- }
-
- run.Indices = indices;
-
- run.Advances = new float[glyphCount];
-
- var width = 0.0;
-
- for (var i = 0; i < glyphCount; i++)
- {
- var advance = glyphInfos[i].GlyphAdvance;
-
- width += advance;
-
- run.Advances[i] = (float)advance;
- }
-
- run.Offsets = new GlyphOffset[glyphCount];
-
- for (var i = 0; i < glyphCount; i++)
- {
- var (x, y) = glyphInfos[i].GlyphOffset;
-
- run.Offsets[i] = new GlyphOffset
- {
- AdvanceOffset = (float)x,
- AscenderOffset = (float)y
- };
- }
-
- var scale = fontRenderingEmSize / glyphTypeface.Metrics.DesignEmHeight;
- var height = glyphTypeface.Metrics.LineSpacing * scale;
-
- return new GlyphRunImpl(run, new Size(width, height), baselineOrigin);
+ return new GlyphRunImpl(glyphTypeface, fontRenderingEmSize, glyphInfos, baselineOrigin, bounds);
}
class D2DApi : IPlatformRenderInterfaceContext
diff --git a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs
index be5cef35b5..e13e699bec 100644
--- a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs
+++ b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs
@@ -74,6 +74,16 @@ namespace Avalonia.Direct2D1.Media
set => throw new NotSupportedException();
}
+ public RenderOptions RenderOptions
+ {
+ get => _renderOptions;
+ set
+ {
+ _renderOptions = value;
+ ApplyRenderOptions(value);
+ }
+ }
+
///
public void Clear(Color color)
{
@@ -117,15 +127,14 @@ namespace Avalonia.Direct2D1.Media
/// The opacity to draw with.
/// The rect in the image to draw.
/// The rect in the output to draw to.
- /// The bitmap interpolation mode.
- public void DrawBitmap(IRef source, double opacity, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode)
+ public void DrawBitmap(IRef source, double opacity, Rect sourceRect, Rect destRect)
{
using (var d2d = ((BitmapImpl)source.Item).GetDirect2DBitmap(_deviceContext))
{
- var interpolationMode = GetInterpolationMode(bitmapInterpolationMode);
-
+ var interpolationMode = GetInterpolationMode(RenderOptions.BitmapInterpolationMode);
+
// TODO: How to implement CompositeMode here?
-
+
_deviceContext.DrawBitmap(
d2d.Value,
destRect.ToSharpDX(),
@@ -141,13 +150,14 @@ namespace Avalonia.Direct2D1.Media
switch (interpolationMode)
{
case BitmapInterpolationMode.LowQuality:
- return InterpolationMode.NearestNeighbor;
- case BitmapInterpolationMode.MediumQuality:
return InterpolationMode.Linear;
+ case BitmapInterpolationMode.MediumQuality:
+ return InterpolationMode.MultiSampleLinear;
case BitmapInterpolationMode.HighQuality:
return InterpolationMode.HighQualityCubic;
- case BitmapInterpolationMode.Default:
- return InterpolationMode.Linear;
+ case BitmapInterpolationMode.None:
+ case BitmapInterpolationMode.Unspecified:
+ return InterpolationMode.NearestNeighbor;
default:
throw new ArgumentOutOfRangeException(nameof(interpolationMode), interpolationMode, null);
}
@@ -156,15 +166,16 @@ namespace Avalonia.Direct2D1.Media
public static CompositeMode GetCompositeMode(BitmapBlendingMode blendingMode)
{
switch (blendingMode)
- {
+ {
case BitmapBlendingMode.SourceIn:
return CompositeMode.SourceIn;
case BitmapBlendingMode.SourceOut:
return CompositeMode.SourceOut;
+ case BitmapBlendingMode.Unspecified:
case BitmapBlendingMode.SourceOver:
return CompositeMode.SourceOver;
case BitmapBlendingMode.SourceAtop:
- return CompositeMode.SourceAtop;
+ return CompositeMode.SourceAtop;
case BitmapBlendingMode.DestinationIn:
return CompositeMode.DestinationIn;
case BitmapBlendingMode.DestinationOut:
@@ -191,8 +202,10 @@ namespace Avalonia.Direct2D1.Media
/// The rect in the output to draw to.
public void DrawBitmap(IRef source, IBrush opacityMask, Rect opacityMaskRect, Rect destRect)
{
+ var interpolationMode = GetInterpolationMode(RenderOptions.BitmapInterpolationMode);
+
using (var d2dSource = ((BitmapImpl)source.Item).GetDirect2DBitmap(_deviceContext))
- using (var sourceBrush = new BitmapBrush(_deviceContext, d2dSource.Value))
+ using (var sourceBrush = new BitmapBrush1(_deviceContext, d2dSource.Value, new BitmapBrushProperties1 { InterpolationMode = interpolationMode }))
using (var d2dOpacityMask = CreateBrush(opacityMask, opacityMaskRect.Size))
using (var geometry = new SharpDX.Direct2D1.RectangleGeometry(Direct2D1Platform.Direct2D1Factory, destRect.ToDirect2D()))
{
@@ -386,9 +399,11 @@ namespace Avalonia.Direct2D1.Media
{
using (var brush = CreateBrush(foreground, glyphRun.Item.Bounds.Size))
{
- var glyphRunImpl = (GlyphRunImpl)glyphRun.Item;
+ var immutableGlyphRun = (GlyphRunImpl)glyphRun.Item;
- _renderTarget.DrawGlyphRun(glyphRun.Item.BaselineOrigin.ToSharpDX(), glyphRunImpl.GlyphRun,
+ var dxGlyphRun = immutableGlyphRun.GlyphRun;
+
+ _renderTarget.DrawGlyphRun(glyphRun.Item.BaselineOrigin.ToSharpDX(), dxGlyphRun,
brush.PlatformBrush, MeasuringMode.Natural);
}
}
@@ -431,6 +446,8 @@ namespace Avalonia.Direct2D1.Media
readonly Stack _layers = new Stack();
private readonly Stack _layerPool = new Stack();
+ private RenderOptions _renderOptions;
+
///
/// Pushes an opacity value.
///
@@ -544,7 +561,7 @@ namespace Avalonia.Direct2D1.Media
return new ImageBrushImpl(
sceneBrushContent.Brush,
_deviceContext,
- new D2DBitmapImpl(intermediate.Bitmap),
+ new D2DBitmapImpl(intermediate.Bitmap.QueryInterface()),
destinationSize);
}
@@ -608,5 +625,25 @@ namespace Avalonia.Direct2D1.Media
}
public object GetFeature(Type t) => null;
+
+ private void ApplyRenderOptions(RenderOptions renderOptions)
+ {
+ _deviceContext.AntialiasMode = renderOptions.EdgeMode != EdgeMode.Aliased ? AntialiasMode.PerPrimitive : AntialiasMode.Aliased;
+ switch (renderOptions.TextRenderingMode)
+ {
+ case TextRenderingMode.Unspecified:
+ _deviceContext.TextAntialiasMode = renderOptions.EdgeMode != EdgeMode.Aliased ? TextAntialiasMode.Default : TextAntialiasMode.Aliased;
+ break;
+ case TextRenderingMode.Alias:
+ _deviceContext.TextAntialiasMode = TextAntialiasMode.Aliased;
+ break;
+ case TextRenderingMode.Antialias:
+ _deviceContext.TextAntialiasMode = TextAntialiasMode.Grayscale;
+ break;
+ case TextRenderingMode.SubpixelAntialias:
+ _deviceContext.TextAntialiasMode = TextAntialiasMode.Cleartype;
+ break;
+ }
+ }
}
}
diff --git a/src/Windows/Avalonia.Direct2D1/Media/GlyphRunImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/GlyphRunImpl.cs
index 2e7a4b67f6..df147c4525 100644
--- a/src/Windows/Avalonia.Direct2D1/Media/GlyphRunImpl.cs
+++ b/src/Windows/Avalonia.Direct2D1/Media/GlyphRunImpl.cs
@@ -1,31 +1,106 @@
using System;
using System.Collections.Generic;
+using Avalonia.Media;
+using Avalonia.Media.TextFormatting;
using Avalonia.Platform;
using SharpDX.DirectWrite;
+#nullable enable
+
namespace Avalonia.Direct2D1.Media
{
internal class GlyphRunImpl : IGlyphRunImpl
{
- public GlyphRunImpl(GlyphRun glyphRun, Size size, Point baselineOrigin)
+ private readonly GlyphTypefaceImpl _glyphTypefaceImpl;
+
+ private readonly short[] _glyphIndices;
+ private readonly float[] _glyphAdvances;
+ private readonly GlyphOffset[] _glyphOffsets;
+
+ private SharpDX.DirectWrite.GlyphRun? _glyphRun;
+
+ public GlyphRunImpl(IGlyphTypeface glyphTypeface, double fontRenderingEmSize,
+ IReadOnlyList glyphInfos, Point baselineOrigin, Rect bounds)
{
- Bounds = new Rect(new Point(baselineOrigin.X, 0), size);
+ _glyphTypefaceImpl = (GlyphTypefaceImpl)glyphTypeface;
+
+ FontRenderingEmSize = fontRenderingEmSize;
BaselineOrigin = baselineOrigin;
- GlyphRun = glyphRun;
+ Bounds = bounds;
+
+ var glyphCount = glyphInfos.Count;
+
+ _glyphIndices = new short[glyphCount];
+
+ for (var i = 0; i < glyphCount; i++)
+ {
+ _glyphIndices[i] = (short)glyphInfos[i].GlyphIndex;
+ }
+
+ _glyphAdvances = new float[glyphCount];
+
+ var width = 0.0;
+
+ for (var i = 0; i < glyphCount; i++)
+ {
+ var advance = glyphInfos[i].GlyphAdvance;
+
+ width += advance;
+
+ _glyphAdvances[i] = (float)advance;
+ }
+
+ _glyphOffsets = new GlyphOffset[glyphCount];
+
+ for (var i = 0; i < glyphCount; i++)
+ {
+ var (x, y) = glyphInfos[i].GlyphOffset;
+
+ _glyphOffsets[i] = new GlyphOffset
+ {
+ AdvanceOffset = (float)x,
+ AscenderOffset = (float)y
+ };
+ }
+ }
+
+ public SharpDX.DirectWrite.GlyphRun GlyphRun
+ {
+ get
+ {
+ if (_glyphRun != null)
+ {
+ return _glyphRun;
+ }
+
+ _glyphRun = new SharpDX.DirectWrite.GlyphRun
+ {
+ FontFace = _glyphTypefaceImpl.FontFace,
+ FontSize = (float)FontRenderingEmSize,
+ Advances = _glyphAdvances,
+ Indices = _glyphIndices,
+ Offsets = _glyphOffsets
+ };
+
+ return _glyphRun;
+ }
}
- public Rect Bounds{ get; }
+ public IGlyphTypeface GlyphTypeface => _glyphTypefaceImpl;
+
+ public double FontRenderingEmSize { get; }
public Point BaselineOrigin { get; }
- public GlyphRun GlyphRun { get; }
+ public Rect Bounds { get; }
+
+ public IReadOnlyList GetIntersections(float lowerBound, float upperBound) => Array.Empty();
public void Dispose()
{
- //GlyphRun?.Dispose();
- }
+ //_glyphRun?.Dispose();
- public IReadOnlyList GetIntersections(float lowerBound, float upperBound)
- => Array.Empty();
+ _glyphRun = null;
+ }
}
}
diff --git a/src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs
index a08c96c40c..e0611b5870 100644
--- a/src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs
+++ b/src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs
@@ -7,9 +7,7 @@ namespace Avalonia.Direct2D1.Media
{
internal sealed class ImageBrushImpl : BrushImpl
{
- private readonly OptionalDispose _bitmap;
-
- private readonly Avalonia.Media.Imaging.BitmapInterpolationMode _bitmapInterpolationMode;
+ private readonly OptionalDispose _bitmap;
public ImageBrushImpl(
ITileBrush brush,
@@ -40,8 +38,6 @@ namespace Avalonia.Direct2D1.Media
GetBrushProperties(brush, calc.DestinationRect));
}
}
-
- _bitmapInterpolationMode = brush.BitmapInterpolationMode;
}
public override void Dispose()
@@ -103,8 +99,7 @@ namespace Avalonia.Direct2D1.Media
context.Clear(Colors.Transparent);
context.PushClip(calc.IntermediateClip);
context.Transform = calc.IntermediateTransform;
-
- context.DrawBitmap(RefCountable.CreateUnownedNotClonable(bitmap), 1, rect, rect, _bitmapInterpolationMode);
+ context.DrawBitmap(RefCountable.CreateUnownedNotClonable(bitmap), 1, rect, rect);
context.PopClip();
}
diff --git a/src/Windows/Avalonia.Direct2D1/Media/Imaging/BitmapImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/Imaging/BitmapImpl.cs
index 740efe833f..ac21d7cf6e 100644
--- a/src/Windows/Avalonia.Direct2D1/Media/Imaging/BitmapImpl.cs
+++ b/src/Windows/Avalonia.Direct2D1/Media/Imaging/BitmapImpl.cs
@@ -1,7 +1,7 @@
using System;
using System.IO;
using Avalonia.Platform;
-using D2DBitmap = SharpDX.Direct2D1.Bitmap;
+using D2DBitmap = SharpDX.Direct2D1.Bitmap1;
namespace Avalonia.Direct2D1.Media
{
diff --git a/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DBitmapImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DBitmapImpl.cs
index 940d4673b5..2373a90ebc 100644
--- a/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DBitmapImpl.cs
+++ b/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DBitmapImpl.cs
@@ -1,7 +1,8 @@
using System;
using System.IO;
+using Avalonia.Metadata;
+using SharpDX.Direct2D1;
using SharpDX.WIC;
-using Bitmap = SharpDX.Direct2D1.Bitmap;
namespace Avalonia.Direct2D1.Media
{
@@ -10,7 +11,7 @@ namespace Avalonia.Direct2D1.Media
///
internal class D2DBitmapImpl : BitmapImpl
{
- private readonly Bitmap _direct2DBitmap;
+ private readonly Bitmap1 _direct2DBitmap;
///
/// Initialize a new instance of the class
@@ -22,7 +23,7 @@ namespace Avalonia.Direct2D1.Media
/// or if the render target is a ,
/// the device associated with this context, to be renderable.
///
- public D2DBitmapImpl(Bitmap d2DBitmap)
+ public D2DBitmapImpl(Bitmap1 d2DBitmap)
{
_direct2DBitmap = d2DBitmap ?? throw new ArgumentNullException(nameof(d2DBitmap));
}
@@ -36,9 +37,9 @@ namespace Avalonia.Direct2D1.Media
_direct2DBitmap.Dispose();
}
- public override OptionalDispose GetDirect2DBitmap(SharpDX.Direct2D1.RenderTarget target)
+ public override OptionalDispose GetDirect2DBitmap(SharpDX.Direct2D1.RenderTarget target)
{
- return new OptionalDispose(_direct2DBitmap, false);
+ return new OptionalDispose(_direct2DBitmap, false);
}
public override void Save(Stream stream, int? quality = null)
diff --git a/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs
index 6b1ca911fb..2a3bf617f7 100644
--- a/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs
+++ b/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs
@@ -5,7 +5,7 @@ using Avalonia.Rendering;
using Avalonia.Utilities;
using SharpDX;
using SharpDX.Direct2D1;
-using D2DBitmap = SharpDX.Direct2D1.Bitmap;
+using D2DBitmap = SharpDX.Direct2D1.Bitmap1;
namespace Avalonia.Direct2D1.Media.Imaging
{
@@ -14,7 +14,7 @@ namespace Avalonia.Direct2D1.Media.Imaging
private readonly BitmapRenderTarget _renderTarget;
public D2DRenderTargetBitmapImpl(BitmapRenderTarget renderTarget)
- : base(renderTarget.Bitmap)
+ : base(renderTarget.Bitmap.QueryInterface())
{
_renderTarget = renderTarget;
}
@@ -53,7 +53,7 @@ namespace Avalonia.Direct2D1.Media.Imaging
public override OptionalDispose GetDirect2DBitmap(SharpDX.Direct2D1.RenderTarget target)
{
- return new OptionalDispose(_renderTarget.Bitmap, false);
+ return new OptionalDispose(_renderTarget.Bitmap.QueryInterface(), false);
}
public override void Save(Stream stream, int? quality = null)
diff --git a/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs
index 533a29f68c..2fa1e5bd7a 100644
--- a/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs
+++ b/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs
@@ -1,11 +1,10 @@
using System;
using System.IO;
-using Avalonia.Direct2D1.Media.Imaging;
using Avalonia.Win32.Interop;
using SharpDX.WIC;
using APixelFormat = Avalonia.Platform.PixelFormat;
using AlphaFormat = Avalonia.Platform.AlphaFormat;
-using D2DBitmap = SharpDX.Direct2D1.Bitmap;
+using D2DBitmap = SharpDX.Direct2D1.Bitmap1;
using Avalonia.Platform;
using PixelFormat = SharpDX.WIC.PixelFormat;
@@ -22,7 +21,7 @@ namespace Avalonia.Direct2D1.Media
{
switch (interpolationMode)
{
- case Avalonia.Media.Imaging.BitmapInterpolationMode.Default:
+ case Avalonia.Media.Imaging.BitmapInterpolationMode.Unspecified:
return BitmapInterpolationMode.Fant;
case Avalonia.Media.Imaging.BitmapInterpolationMode.LowQuality:
@@ -184,7 +183,10 @@ namespace Avalonia.Direct2D1.Media
{
using var converter = new FormatConverter(Direct2D1Platform.ImagingFactory);
converter.Initialize(WicImpl, SharpDX.WIC.PixelFormat.Format32bppPBGRA);
- return new OptionalDispose(D2DBitmap.FromWicBitmap(renderTarget, converter), true);
+
+ var d2dBitmap = D2DBitmap.FromWicBitmap(renderTarget, converter).QueryInterface();
+
+ return new OptionalDispose(d2dBitmap, true);
}
public override void Save(Stream stream, int? quality = null)
diff --git a/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj b/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj
index 2c5f3e2ed1..61f5996a94 100644
--- a/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj
+++ b/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj
@@ -20,7 +20,6 @@
-
diff --git a/src/Windows/Avalonia.Win32/Win32StorageProvider.cs b/src/Windows/Avalonia.Win32/Win32StorageProvider.cs
index 86574f2fa3..c987732768 100644
--- a/src/Windows/Avalonia.Win32/Win32StorageProvider.cs
+++ b/src/Windows/Avalonia.Win32/Win32StorageProvider.cs
@@ -99,12 +99,14 @@ namespace Avalonia.Win32
}
frm.SetOptions(options);
- if (defaultExtension is not null)
+ if (defaultExtension is null)
{
- fixed (char* pExt = defaultExtension)
- {
- frm.SetDefaultExtension(pExt);
- }
+ defaultExtension = String.Empty;
+ }
+
+ fixed (char* pExt = defaultExtension)
+ {
+ frm.SetDefaultExtension(pExt);
}
suggestedFileName ??= "";
diff --git a/tests/Avalonia.Base.UnitTests/DispatcherTests.cs b/tests/Avalonia.Base.UnitTests/DispatcherTests.cs
index 9ba3f3980d..7b401918ce 100644
--- a/tests/Avalonia.Base.UnitTests/DispatcherTests.cs
+++ b/tests/Avalonia.Base.UnitTests/DispatcherTests.cs
@@ -3,6 +3,8 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
+using System.Threading.Tasks;
+using Avalonia.Controls.Platform;
using Avalonia.Threading;
using Avalonia.Utilities;
using Xunit;
@@ -458,4 +460,46 @@ public class DispatcherTests
}
}
+ [Fact]
+ public void DispatcherInvokeAsyncUnwrapsTasks()
+ {
+ int asyncMethodStage = 0;
+
+ async Task AsyncMethod()
+ {
+ asyncMethodStage = 1;
+ await Task.Delay(200);
+ asyncMethodStage = 2;
+ }
+
+ async Task AsyncMethodWithResult()
+ {
+ await Task.Delay(100);
+ return 1;
+ }
+
+ async Task Test()
+ {
+ await Dispatcher.UIThread.InvokeAsync(AsyncMethod);
+ Assert.Equal(2, asyncMethodStage);
+ Assert.Equal(1, await Dispatcher.UIThread.InvokeAsync(AsyncMethodWithResult));
+ asyncMethodStage = 0;
+
+ await Dispatcher.UIThread.InvokeAsync(AsyncMethod, DispatcherPriority.Default);
+ Assert.Equal(2, asyncMethodStage);
+ Assert.Equal(1, await Dispatcher.UIThread.InvokeAsync(AsyncMethodWithResult, DispatcherPriority.Default));
+
+ Dispatcher.UIThread.ExitAllFrames();
+ }
+
+ using (new DispatcherServices(new ManagedDispatcherImpl(null)))
+ {
+ var t = Test();
+ var cts = new CancellationTokenSource();
+ Task.Delay(3000).ContinueWith(_ => cts.Cancel());
+ Dispatcher.UIThread.MainLoop(cts.Token);
+ Assert.True(t.IsCompletedSuccessfully);
+ t.GetAwaiter().GetResult();
+ }
+ }
}
\ No newline at end of file
diff --git a/tests/Avalonia.Base.UnitTests/Layout/LayoutManagerTests.cs b/tests/Avalonia.Base.UnitTests/Layout/LayoutManagerTests.cs
index 45a6efdd4a..cadf23c754 100644
--- a/tests/Avalonia.Base.UnitTests/Layout/LayoutManagerTests.cs
+++ b/tests/Avalonia.Base.UnitTests/Layout/LayoutManagerTests.cs
@@ -547,5 +547,38 @@ namespace Avalonia.Base.UnitTests.Layout
Assert.True(root.IsMeasureValid);
Assert.True(root.IsArrangeValid);
}
+
+ [Fact]
+ public void GreatGrandparent_Can_Invalidate_Grandparent_Measure_During_Arrange()
+ {
+ // Issue #7706 (second part: scrollbar gets stuck)
+ var child = new LayoutTestControl();
+ var parent = new LayoutTestControl { Child = child };
+ var grandparent = new LayoutTestControl { Child = parent };
+ var greatGrandparent = new LayoutTestControl { Child = grandparent };
+ var root = new LayoutTestRoot { Child = greatGrandparent };
+
+ root.LayoutManager.ExecuteInitialLayoutPass();
+
+ greatGrandparent.DoArrangeOverride = (_, s) =>
+ {
+ grandparent.InvalidateMeasure();
+ return s;
+ };
+
+ child.InvalidateArrange();
+ greatGrandparent.InvalidateArrange();
+
+ root.LayoutManager.ExecuteLayoutPass();
+
+ Assert.True(child.IsMeasureValid);
+ Assert.True(child.IsArrangeValid);
+ Assert.True(parent.IsMeasureValid);
+ Assert.True(parent.IsArrangeValid);
+ Assert.True(greatGrandparent.IsMeasureValid);
+ Assert.True(greatGrandparent.IsArrangeValid);
+ Assert.True(root.IsMeasureValid);
+ Assert.True(root.IsArrangeValid);
+ }
}
}
diff --git a/tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/DrawOperationTests.cs b/tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/DrawOperationTests.cs
index 34878bd08d..86f461673e 100644
--- a/tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/DrawOperationTests.cs
+++ b/tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/DrawOperationTests.cs
@@ -51,8 +51,7 @@ namespace Avalonia.Base.UnitTests.Rendering.SceneGraph
bitmap,
1,
new Rect(1, 1, 1, 1),
- new Rect(1, 1, 1, 1),
- BitmapInterpolationMode.Default);
+ new Rect(1, 1, 1, 1));
Assert.Equal(2, bitmap.RefCount);
diff --git a/tests/Avalonia.Base.UnitTests/VisualTree/MockRenderInterface.cs b/tests/Avalonia.Base.UnitTests/VisualTree/MockRenderInterface.cs
index d494c47a55..37adb03628 100644
--- a/tests/Avalonia.Base.UnitTests/VisualTree/MockRenderInterface.cs
+++ b/tests/Avalonia.Base.UnitTests/VisualTree/MockRenderInterface.cs
@@ -78,7 +78,7 @@ namespace Avalonia.Base.UnitTests.VisualTree
}
public IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize,
- IReadOnlyList glyphInfos, Point baselineOrigin)
+ IReadOnlyList glyphInfos, Point baselineOrigin, Rect bounds)
{
throw new NotImplementedException();
}
diff --git a/tests/Avalonia.Benchmarks/NullDrawingContextImpl.cs b/tests/Avalonia.Benchmarks/NullDrawingContextImpl.cs
index 40d504a0ac..3513843367 100644
--- a/tests/Avalonia.Benchmarks/NullDrawingContextImpl.cs
+++ b/tests/Avalonia.Benchmarks/NullDrawingContextImpl.cs
@@ -15,12 +15,13 @@ namespace Avalonia.Benchmarks
public Matrix Transform { get; set; }
+ public RenderOptions RenderOptions { get; set; }
+
public void Clear(Color color)
{
}
- public void DrawBitmap(IRef source, double opacity, Rect sourceRect, Rect destRect,
- BitmapInterpolationMode bitmapInterpolationMode = BitmapInterpolationMode.Default)
+ public void DrawBitmap(IRef source, double opacity, Rect sourceRect, Rect destRect)
{
}
diff --git a/tests/Avalonia.Benchmarks/NullGlyphRun.cs b/tests/Avalonia.Benchmarks/NullGlyphRun.cs
deleted file mode 100644
index 5b584f302d..0000000000
--- a/tests/Avalonia.Benchmarks/NullGlyphRun.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using System.Collections.Generic;
-using Avalonia.Platform;
-
-namespace Avalonia.Benchmarks
-{
- internal class NullGlyphRun : IGlyphRunImpl
- {
- public Rect Bounds => default;
-
- public Point BaselineOrigin => default;
-
- public void Dispose()
- {
- }
-
- public IReadOnlyList GetIntersections(float lowerBound, float upperBound)
- {
- return null;
- }
- }
-}
diff --git a/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs b/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs
index d40abd9f47..55cd9e8d4b 100644
--- a/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs
+++ b/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs
@@ -124,9 +124,9 @@ namespace Avalonia.Benchmarks
}
public IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize,
- IReadOnlyList glyphInfos, Point baselineOrigin)
+ IReadOnlyList glyphInfos, Point baselineOrigin, Rect bounds)
{
- return new MockGlyphRun(glyphInfos);
+ return new MockGlyphRun(glyphTypeface, fontRenderingEmSize, baselineOrigin, bounds);
}
public IPlatformRenderInterfaceContext CreateBackendContext(IPlatformGraphicsContext graphicsContext)
diff --git a/tests/Avalonia.Controls.UnitTests/ScrollViewerTests.cs b/tests/Avalonia.Controls.UnitTests/ScrollViewerTests.cs
index a7bde551e9..82c4b5f8f0 100644
--- a/tests/Avalonia.Controls.UnitTests/ScrollViewerTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/ScrollViewerTests.cs
@@ -1,10 +1,13 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Layout;
+using Avalonia.Media;
using Avalonia.UnitTests;
+using Avalonia.VisualTree;
using Moq;
using Xunit;
@@ -12,6 +15,8 @@ namespace Avalonia.Controls.UnitTests
{
public class ScrollViewerTests
{
+ private readonly MouseTestHelper _mouse = new();
+
[Fact]
public void Content_Is_Created()
{
@@ -249,6 +254,121 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(new Vector(20, 20), target.Offset);
}
+ [Fact]
+ public void Scroll_Does_Not_Jump_When_Viewport_Becomes_Smaller_While_Dragging_ScrollBar_Thumb()
+ {
+ var content = new TestContent
+ {
+ MeasureSize = new Size(1000, 10000),
+ };
+
+ var target = new ScrollViewer
+ {
+ Template = new FuncControlTemplate(CreateTemplate),
+ Content = content,
+ };
+ var root = new TestRoot(target);
+
+ root.LayoutManager.ExecuteInitialLayoutPass();
+
+ Assert.Equal(new Size(1000, 10000), target.Extent);
+ Assert.Equal(new Size(1000, 1000), target.Viewport);
+
+ // We're working in absolute coordinates (i.e. relative to the root) and clicking on
+ // the center of the vertical thumb.
+ var thumb = GetVerticalThumb(target);
+ var p = GetRootPoint(thumb, thumb.Bounds.Center);
+
+ // Press the mouse button in the center of the thumb.
+ _mouse.Down(thumb, position: p);
+ root.LayoutManager.ExecuteLayoutPass();
+
+ // Drag the thumb down 300 pixels.
+ _mouse.Move(thumb, p += new Vector(0, 300));
+ root.LayoutManager.ExecuteLayoutPass();
+
+ Assert.Equal(new Vector(0, 3000), target.Offset);
+ Assert.Equal(300, thumb.Bounds.Top);
+
+ // Now the extent changes from 10,000 to 5000.
+ content.MeasureSize /= 2;
+ content.InvalidateMeasure();
+ root.LayoutManager.ExecuteLayoutPass();
+
+ // Due to the extent change, the thumb moves down but the value remains the same.
+ Assert.Equal(600, thumb.Bounds.Top);
+ Assert.Equal(new Vector(0, 3000), target.Offset);
+
+ // Drag the thumb down another 100 pixels.
+ _mouse.Move(thumb, p += new Vector(0, 100));
+ root.LayoutManager.ExecuteLayoutPass();
+
+ // The drag should not cause the offset/thumb to jump *up* to the current absolute
+ // mouse position, i.e. it should move down in the direction of the drag even if the
+ // absolute mouse position is now above the thumb.
+ Assert.Equal(700, thumb.Bounds.Top);
+ Assert.Equal(new Vector(0, 3500), target.Offset);
+ }
+
+ [Fact]
+ public void Thumb_Does_Not_Become_Detached_From_Mouse_Position_When_Scrolling_Past_The_Start()
+ {
+ var content = new TestContent();
+ var target = new ScrollViewer
+ {
+ Template = new FuncControlTemplate(CreateTemplate),
+ Content = content,
+ };
+ var root = new TestRoot(target);
+
+ root.LayoutManager.ExecuteInitialLayoutPass();
+
+ Assert.Equal(new Size(1000, 2000), target.Extent);
+ Assert.Equal(new Size(1000, 1000), target.Viewport);
+
+ // We're working in absolute coordinates (i.e. relative to the root) and clicking on
+ // the center of the vertical thumb.
+ var thumb = GetVerticalThumb(target);
+ var p = GetRootPoint(thumb, thumb.Bounds.Center);
+
+ // Press the mouse button in the center of the thumb.
+ _mouse.Down(thumb, position: p);
+ root.LayoutManager.ExecuteLayoutPass();
+
+ // Drag the thumb down 100 pixels.
+ _mouse.Move(thumb, p += new Vector(0, 100));
+ root.LayoutManager.ExecuteLayoutPass();
+
+ Assert.Equal(new Vector(0, 200), target.Offset);
+ Assert.Equal(100, thumb.Bounds.Top);
+
+ // Drag the thumb up 200 pixels - 100 pixels past the top of the scrollbar.
+ _mouse.Move(thumb, p -= new Vector(0, 200));
+ root.LayoutManager.ExecuteLayoutPass();
+
+ Assert.Equal(new Vector(0, 0), target.Offset);
+ Assert.Equal(0, thumb.Bounds.Top);
+
+ // Drag the thumb back down 200 pixels.
+ _mouse.Move(thumb, p += new Vector(0, 200));
+ root.LayoutManager.ExecuteLayoutPass();
+
+ // We should now be back in the state after we first scrolled down 100 pixels.
+ Assert.Equal(new Vector(0, 200), target.Offset);
+ Assert.Equal(100, thumb.Bounds.Top);
+ }
+
+ private Point GetRootPoint(Visual control, Point p)
+ {
+ if (control.GetVisualRoot() is Visual root &&
+ control.TransformToVisual(root) is Matrix m)
+ {
+ return p.Transform(m);
+ }
+
+ throw new InvalidOperationException("Could not get the point in root coordinates.");
+ }
+
private Control CreateTemplate(ScrollViewer control, INameScope scope)
{
return new Grid
@@ -273,6 +393,7 @@ namespace Avalonia.Controls.UnitTests
{
Name = "PART_HorizontalScrollBar",
Orientation = Orientation.Horizontal,
+ Template = new FuncControlTemplate(CreateScrollBarTemplate),
[~ScrollBar.VisibilityProperty] = control[~ScrollViewer.HorizontalScrollBarVisibilityProperty],
[Grid.RowProperty] = 1,
}.RegisterInNameScope(scope),
@@ -280,6 +401,7 @@ namespace Avalonia.Controls.UnitTests
{
Name = "PART_VerticalScrollBar",
Orientation = Orientation.Vertical,
+ Template = new FuncControlTemplate(CreateScrollBarTemplate),
[~ScrollBar.VisibilityProperty] = control[~ScrollViewer.VerticalScrollBarVisibilityProperty],
[Grid.ColumnProperty] = 1,
}.RegisterInNameScope(scope),
@@ -287,6 +409,44 @@ namespace Avalonia.Controls.UnitTests
};
}
+ private Control CreateScrollBarTemplate(ScrollBar scrollBar, INameScope scope)
+ {
+ return new Border
+ {
+ Child = new Track
+ {
+ Name = "track",
+ IsDirectionReversed = true,
+ [!Track.MinimumProperty] = scrollBar[!RangeBase.MinimumProperty],
+ [!Track.MaximumProperty] = scrollBar[!RangeBase.MaximumProperty],
+ [!!Track.ValueProperty] = scrollBar[!!RangeBase.ValueProperty],
+ [!Track.ViewportSizeProperty] = scrollBar[!ScrollBar.ViewportSizeProperty],
+ [!Track.OrientationProperty] = scrollBar[!ScrollBar.OrientationProperty],
+ Thumb = new Thumb
+ {
+ Template = new FuncControlTemplate(CreateThumbTemplate),
+ },
+ }.RegisterInNameScope(scope),
+ };
+ }
+
+ private static Control CreateThumbTemplate(Thumb control, INameScope scope)
+ {
+ return new Border
+ {
+ Background = Brushes.Gray,
+ };
+ }
+
+ private Thumb GetVerticalThumb(ScrollViewer target)
+ {
+ var scrollbar = Assert.IsType(
+ target.GetTemplateChildren().FirstOrDefault(x => x.Name == "PART_VerticalScrollBar"));
+ var track = Assert.IsType(
+ scrollbar.GetTemplateChildren().FirstOrDefault(x => x.Name == "track"));
+ return Assert.IsType(track.Thumb);
+ }
+
private static void InitializeScrollViewer(ScrollViewer target)
{
target.ApplyTemplate();
@@ -295,5 +455,15 @@ namespace Avalonia.Controls.UnitTests
presenter.AttachToScrollViewer();
presenter.UpdateChild();
}
+
+ private class TestContent : Control
+ {
+ public Size MeasureSize { get; set; } = new Size(1000, 2000);
+
+ protected override Size MeasureOverride(Size availableSize)
+ {
+ return MeasureSize;
+ }
+ }
}
}
diff --git a/tests/Avalonia.RenderTests/Media/GlyphRunTests.cs b/tests/Avalonia.RenderTests/Media/GlyphRunTests.cs
index 772d6e1023..1b5083924a 100644
--- a/tests/Avalonia.RenderTests/Media/GlyphRunTests.cs
+++ b/tests/Avalonia.RenderTests/Media/GlyphRunTests.cs
@@ -111,6 +111,29 @@ namespace Avalonia.Direct2D1.RenderTests.Media
CompareImages();
}
+ [Win32Fact("For consistent results")]
+ public async Task Should_Render_GlyphRun_Aliased()
+ {
+ var control = new PositionedGlyphRunControl
+ {
+ [TextElement.ForegroundProperty] = new SolidColorBrush { Color = Colors.Black }
+ };
+
+ RenderOptions.SetTextRenderingMode(control, TextRenderingMode.Alias);
+
+ Decorator target = new Decorator
+ {
+ Padding = new Thickness(8),
+ Width = 190,
+ Height = 120,
+ Child = control
+ };
+
+ await RenderToFile(target);
+
+ CompareImages();
+ }
+
public class GlyphRunGeometryControl : Control
{
public GlyphRunGeometryControl()
diff --git a/tests/Avalonia.RenderTests/Shapes/EllipseTests.cs b/tests/Avalonia.RenderTests/Shapes/EllipseTests.cs
index 43f15e0fad..0851760987 100644
--- a/tests/Avalonia.RenderTests/Shapes/EllipseTests.cs
+++ b/tests/Avalonia.RenderTests/Shapes/EllipseTests.cs
@@ -35,5 +35,49 @@ namespace Avalonia.Direct2D1.RenderTests.Shapes
await RenderToFile(target);
CompareImages();
}
+
+ [Fact]
+ public async Task Should_Render_Circle_Aliased()
+ {
+ var target = new Border
+ {
+ Background = Brushes.White,
+ Padding = new Thickness(8),
+ Width = 200,
+ Height = 200,
+ Child = new Ellipse
+ {
+ Stroke = Brushes.Black,
+ StrokeThickness = 3.5,
+ }
+ };
+
+ RenderOptions.SetEdgeMode(target, EdgeMode.Aliased);
+
+ await RenderToFile(target);
+ CompareImages();
+ }
+
+ [Fact]
+ public async Task Should_Render_Circle_Antialiased()
+ {
+ var target = new Border
+ {
+ Background = Brushes.White,
+ Padding = new Thickness(8),
+ Width = 200,
+ Height = 200,
+ Child = new Ellipse
+ {
+ Stroke = Brushes.Black,
+ StrokeThickness = 3.5,
+ }
+ };
+
+ RenderOptions.SetEdgeMode(target, EdgeMode.Antialias);
+
+ await RenderToFile(target);
+ CompareImages();
+ }
}
}
diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs
index d605ecbfda..1a39dd5223 100644
--- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs
+++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs
@@ -1021,6 +1021,33 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
}
}
+ [Fact]
+ public void Should_GetTextBounds_With_EndOfParagraph()
+ {
+ var text = "abc";
+
+ using (Start())
+ {
+ var defaultProperties = new GenericTextRunProperties(Typeface.Default);
+ var textSource = new SingleBufferTextSource(text, defaultProperties, true);
+
+ var formatter = new TextFormatterImpl();
+
+ var textLine =
+ formatter.FormatLine(textSource, 0, double.PositiveInfinity,
+ new GenericTextParagraphProperties(FlowDirection.LeftToRight, TextAlignment.Left,
+ true, true, defaultProperties, TextWrapping.NoWrap, 0, 0, 0));
+
+ var textBounds = textLine.GetTextBounds(3, 1);
+
+ Assert.Equal(1, textBounds.Count);
+
+ var firstBounds = textBounds.First();
+
+ Assert.True(firstBounds.TextRunBounds.Count > 0);
+ }
+ }
+
private class FixedRunsTextSource : ITextSource
{
private readonly IReadOnlyList _textRuns;
diff --git a/tests/Avalonia.UnitTests/MockGlyphRun.cs b/tests/Avalonia.UnitTests/MockGlyphRun.cs
index 4561d3b3f2..805c0b6a8a 100644
--- a/tests/Avalonia.UnitTests/MockGlyphRun.cs
+++ b/tests/Avalonia.UnitTests/MockGlyphRun.cs
@@ -1,33 +1,34 @@
using System;
using System.Collections.Generic;
-using Avalonia.Media.TextFormatting;
+using Avalonia.Media;
using Avalonia.Platform;
namespace Avalonia.UnitTests
{
public class MockGlyphRun : IGlyphRunImpl
{
- public MockGlyphRun(IReadOnlyList glyphInfos)
+ public MockGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize, Point baselineOrigin, Rect bounds)
{
- var width = 0.0;
+ GlyphTypeface = glyphTypeface;
+ FontRenderingEmSize = fontRenderingEmSize;
+ BaselineOrigin = baselineOrigin;
+ Bounds =bounds;
+ }
- for (var i = 0; i < glyphInfos.Count; ++i)
- {
- width += glyphInfos[i].GlyphAdvance;
- }
+ public IGlyphTypeface GlyphTypeface { get; }
- Bounds = new Rect(new Size(width, 10));
- }
+ public double FontRenderingEmSize { get; }
- public Rect Bounds { get; }
+ public Point BaselineOrigin { get; }
- public Point BaselineOrigin => new Point(0, 8);
+ public Rect Bounds { get; }
public void Dispose()
{
+
}
- public IReadOnlyList GetIntersections(float lowerBound, float upperBound)
+ public IReadOnlyList GetIntersections(float lowerLimit, float upperLimit)
=> Array.Empty();
}
}
diff --git a/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs b/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs
index 720755f2b0..8647461c0e 100644
--- a/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs
+++ b/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs
@@ -5,7 +5,6 @@ using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.Media.Imaging;
using Avalonia.Media.TextFormatting;
-using Avalonia.Rendering;
using Moq;
namespace Avalonia.UnitTests
@@ -149,10 +148,9 @@ namespace Avalonia.UnitTests
throw new NotImplementedException();
}
- public IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize,
- IReadOnlyList glyphInfos, Point baselineOrigin)
+ public IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize, IReadOnlyList glyphInfos, Point baselineOrigin, Rect bounds)
{
- return new MockGlyphRun(glyphInfos);
+ return new MockGlyphRun(glyphTypeface, fontRenderingEmSize, baselineOrigin, bounds);
}
public IPlatformRenderInterfaceContext CreateBackendContext(IPlatformGraphicsContext graphicsContext) => this;
@@ -162,21 +160,6 @@ namespace Avalonia.UnitTests
return Mock.Of();
}
- public IGlyphRunBuffer AllocateGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
- {
- return Mock.Of();
- }
-
- public IHorizontalGlyphRunBuffer AllocateHorizontalGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
- {
- return Mock.Of();
- }
-
- public IPositionedGlyphRunBuffer AllocatePositionedGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
- {
- return Mock.Of();
- }
-
public bool SupportsIndividualRoundRects { get; set; }
public AlphaFormat DefaultAlphaFormat => AlphaFormat.Premul;
diff --git a/tests/Avalonia.UnitTests/MouseTestHelper.cs b/tests/Avalonia.UnitTests/MouseTestHelper.cs
index d63327239b..eff38388e2 100644
--- a/tests/Avalonia.UnitTests/MouseTestHelper.cs
+++ b/tests/Avalonia.UnitTests/MouseTestHelper.cs
@@ -59,7 +59,7 @@ namespace Avalonia.UnitTests
{
_pressedButton = mouseButton;
_pointer.Capture((IInputElement)target);
- source.RaiseEvent(new PointerPressedEventArgs(source, _pointer, (Visual)source, position, Timestamp(), props,
+ source.RaiseEvent(new PointerPressedEventArgs(source, _pointer, GetRoot(target), position, Timestamp(), props,
modifiers, clickCount));
}
}
@@ -68,7 +68,7 @@ namespace Avalonia.UnitTests
public void Move(Interactive target, Interactive source, in Point position, KeyModifiers modifiers = default)
{
- target.RaiseEvent(new PointerEventArgs(InputElement.PointerMovedEvent, source, _pointer, (Visual)target, position,
+ target.RaiseEvent(new PointerEventArgs(InputElement.PointerMovedEvent, source, _pointer, GetRoot(target), position,
Timestamp(), new PointerPointProperties((RawInputModifiers)_pressedButtons, PointerUpdateKind.Other), modifiers));
}
@@ -88,7 +88,7 @@ namespace Avalonia.UnitTests
);
if (ButtonCount(props) == 0)
{
- target.RaiseEvent(new PointerReleasedEventArgs(source, _pointer, (Visual)target, position,
+ target.RaiseEvent(new PointerReleasedEventArgs(source, _pointer, GetRoot(target), position,
Timestamp(), props, modifiers, _pressedButton));
_pointer.Capture(null);
}
@@ -131,5 +131,9 @@ namespace Avalonia.UnitTests
Timestamp(), new PointerPointProperties((RawInputModifiers)_pressedButtons, PointerUpdateKind.Other), KeyModifiers.None));
}
+ private Visual GetRoot(Interactive source)
+ {
+ return ((source as Visual)?.GetVisualRoot() as Visual) ?? (Visual)source;
+ }
}
}
diff --git a/tests/TestFiles/Direct2D1/Media/GlyphRun/Should_Render_GlyphRun_Aliased.expected.png b/tests/TestFiles/Direct2D1/Media/GlyphRun/Should_Render_GlyphRun_Aliased.expected.png
new file mode 100644
index 0000000000..f130809067
Binary files /dev/null and b/tests/TestFiles/Direct2D1/Media/GlyphRun/Should_Render_GlyphRun_Aliased.expected.png differ
diff --git a/tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_Fill_NoTile.expected.png b/tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_Fill_NoTile.expected.png
index 73d147bf77..1864f8b214 100644
Binary files a/tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_Fill_NoTile.expected.png and b/tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_Fill_NoTile.expected.png differ
diff --git a/tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_Tile_Fill.expected.png b/tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_Tile_Fill.expected.png
index c38dcfbcfd..7d54539814 100644
Binary files a/tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_Tile_Fill.expected.png and b/tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_Tile_Fill.expected.png differ
diff --git a/tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_Tile_UniformToFill.expected.png b/tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_Tile_UniformToFill.expected.png
index 23fc402cd8..8d5c3181f7 100644
Binary files a/tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_Tile_UniformToFill.expected.png and b/tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_Tile_UniformToFill.expected.png differ
diff --git a/tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_UniformToFill_NoTile.expected.png b/tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_UniformToFill_NoTile.expected.png
index e173fa6cee..7c1287d245 100644
Binary files a/tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_UniformToFill_NoTile.expected.png and b/tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_UniformToFill_NoTile.expected.png differ
diff --git a/tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_Uniform_NoTile.expected.png b/tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_Uniform_NoTile.expected.png
index de73af8170..b06a6508fe 100644
Binary files a/tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_Uniform_NoTile.expected.png and b/tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_Uniform_NoTile.expected.png differ
diff --git a/tests/TestFiles/Direct2D1/Media/ImageDrawing/ImageDrawing_Fill.expected.png b/tests/TestFiles/Direct2D1/Media/ImageDrawing/ImageDrawing_Fill.expected.png
index acc8532ff9..8404ce831a 100644
Binary files a/tests/TestFiles/Direct2D1/Media/ImageDrawing/ImageDrawing_Fill.expected.png and b/tests/TestFiles/Direct2D1/Media/ImageDrawing/ImageDrawing_Fill.expected.png differ
diff --git a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Fill_NoTile.expected.png b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Fill_NoTile.expected.png
index 110b44a4c0..86d5e7ccef 100644
Binary files a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Fill_NoTile.expected.png and b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Fill_NoTile.expected.png differ
diff --git a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Grip_144_Dpi.expected.png b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Grip_144_Dpi.expected.png
index 1900ade5e2..28370f3bce 100644
Binary files a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Grip_144_Dpi.expected.png and b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Grip_144_Dpi.expected.png differ
diff --git a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_UniformToFill_NoTile.expected.png b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_UniformToFill_NoTile.expected.png
index 2d7ccac2f7..c1898766aa 100644
Binary files a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_UniformToFill_NoTile.expected.png and b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_UniformToFill_NoTile.expected.png differ
diff --git a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Uniform_NoTile.expected.png b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Uniform_NoTile.expected.png
index d9c62a72a8..6840093b98 100644
Binary files a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Uniform_NoTile.expected.png and b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Uniform_NoTile.expected.png differ
diff --git a/tests/TestFiles/Direct2D1/Shapes/Ellipse/Should_Render_Circle_Aliased.expected.png b/tests/TestFiles/Direct2D1/Shapes/Ellipse/Should_Render_Circle_Aliased.expected.png
new file mode 100644
index 0000000000..d051696d78
Binary files /dev/null and b/tests/TestFiles/Direct2D1/Shapes/Ellipse/Should_Render_Circle_Aliased.expected.png differ
diff --git a/tests/TestFiles/Direct2D1/Shapes/Ellipse/Should_Render_Circle_Antialiased.expected.png b/tests/TestFiles/Direct2D1/Shapes/Ellipse/Should_Render_Circle_Antialiased.expected.png
new file mode 100644
index 0000000000..06dc7f5450
Binary files /dev/null and b/tests/TestFiles/Direct2D1/Shapes/Ellipse/Should_Render_Circle_Antialiased.expected.png differ
diff --git a/tests/TestFiles/Skia/Media/GlyphRun/Should_Render_GlyphRun_Aliased.expected.png b/tests/TestFiles/Skia/Media/GlyphRun/Should_Render_GlyphRun_Aliased.expected.png
new file mode 100644
index 0000000000..0075bfcd3a
Binary files /dev/null and b/tests/TestFiles/Skia/Media/GlyphRun/Should_Render_GlyphRun_Aliased.expected.png differ
diff --git a/tests/TestFiles/Skia/Shapes/Ellipse/Should_Render_Circle_Aliased.expected.png b/tests/TestFiles/Skia/Shapes/Ellipse/Should_Render_Circle_Aliased.expected.png
new file mode 100644
index 0000000000..c8b579f821
Binary files /dev/null and b/tests/TestFiles/Skia/Shapes/Ellipse/Should_Render_Circle_Aliased.expected.png differ
diff --git a/tests/TestFiles/Skia/Shapes/Ellipse/Should_Render_Circle_Antialiased.expected.png b/tests/TestFiles/Skia/Shapes/Ellipse/Should_Render_Circle_Antialiased.expected.png
new file mode 100644
index 0000000000..30df84a194
Binary files /dev/null and b/tests/TestFiles/Skia/Shapes/Ellipse/Should_Render_Circle_Antialiased.expected.png differ