Merge branch 'freeze-ios-android-target-versions' into nunit-headless-intergration
This commit is contained in:
+3
-1
@@ -177,7 +177,9 @@ dotnet_diagnostic.CA1828.severity = warning
|
||||
dotnet_diagnostic.CA1829.severity = warning
|
||||
#CA1847: Use string.Contains(char) instead of string.Contains(string) with single characters
|
||||
dotnet_diagnostic.CA1847.severity = warning
|
||||
#CACA2211:Non-constant fields should not be visible
|
||||
#CA1854: Prefer the IDictionary.TryGetValue(TKey, out TValue) method
|
||||
dotnet_diagnostic.CA1854.severity = warning
|
||||
#CA2211:Non-constant fields should not be visible
|
||||
dotnet_diagnostic.CA2211.severity = error
|
||||
|
||||
# Wrapping preferences
|
||||
|
||||
+14
-1
@@ -262,13 +262,14 @@ 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.NUnit", "src\Headless\Avalonia.Headless.NUnit\Avalonia.Headless.NUnit.csproj", "{ED976634-B118-43F8-8B26-0279C7A7044F}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Headless.XUnit.UnitTests", "tests\Avalonia.Headless.XUnit.UnitTests\Avalonia.Headless.XUnit.UnitTests.csproj", "{EBA7613E-C36C-4E0C-AB45-71B143F86219}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Headless.NUnit.UnitTests", "tests\Avalonia.Headless.NUnit.UnitTests\Avalonia.Headless.NUnit.UnitTests.csproj", "{47025FBC-2130-42EE-98C9-D3989B3B9446}"
|
||||
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
|
||||
@@ -649,6 +650,14 @@ Global
|
||||
{47025FBC-2130-42EE-98C9-D3989B3B9446}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{47025FBC-2130-42EE-98C9-D3989B3B9446}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{47025FBC-2130-42EE-98C9-D3989B3B9446}.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
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -693,6 +702,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}
|
||||
@@ -729,6 +740,8 @@ Global
|
||||
{ED976634-B118-43F8-8B26-0279C7A7044F} = {FF237916-7150-496B-89ED-6CA3292896E7}
|
||||
{EBA7613E-C36C-4E0C-AB45-71B143F86219} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
|
||||
{47025FBC-2130-42EE-98C9-D3989B3B9446} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
|
||||
{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}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<InternalsVisibleTo Include="AvaloniaUI.Xpf.WinApiShim, PublicKey=0024000004800000940000000602000000240000525341310004000001000100a7f4b8b7db0bfb8d74992dc94ecafae031019197ff4263d87ac0a5835fab101c973ccab6fa6e7d90e8f987374f7c6de18dd0b5cd7d6c41e574a8bc66b64836b7c7e707e1aa393d27e33a08f372c1c9965be81658937c85698f4a1c0f73be68a61ffce06d49d1366bf18464c20a29859ccf105fc2d5e35c7ae68919eab668bf8e" />
|
||||
<InternalsVisibleTo Include="System.Windows.Forms, PublicKey=00000000000000000400000000000000" />
|
||||
<InternalsVisibleTo Include="System.Windows.Forms.Primitives, PublicKey=00000000000000000400000000000000" />
|
||||
<InternalsVisibleTo Include="PresentationFramework-SystemData, PublicKey=00000000000000000400000000000000" />
|
||||
<InternalsVisibleTo Include="PresentationFramework.Aero, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" />
|
||||
<InternalsVisibleTo Include="System.Xaml, PublicKey=00000000000000000400000000000000" />
|
||||
<InternalsVisibleTo Include="PresentationFramework, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" />
|
||||
<InternalsVisibleTo Include="PresentationFramework-SystemDrawing, PublicKey=00000000000000000400000000000000" />
|
||||
<InternalsVisibleTo Include="PresentationFramework-SystemCore, PublicKey=00000000000000000400000000000000" />
|
||||
<InternalsVisibleTo Include="WindowsFormsIntegration, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" />
|
||||
<InternalsVisibleTo Include="PresentationFramework.AeroLite, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" />
|
||||
<InternalsVisibleTo Include="PresentationFramework.Aero2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" />
|
||||
<InternalsVisibleTo Include="UIAutomationClient, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" />
|
||||
<InternalsVisibleTo Include="PresentationFramework.Luna, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" />
|
||||
<InternalsVisibleTo Include="UIAutomationTypes, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" />
|
||||
<InternalsVisibleTo Include="PresentationFramework.Royale, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" />
|
||||
<InternalsVisibleTo Include="System.Windows.Input.Manipulations, PublicKey=00000000000000000400000000000000" />
|
||||
<InternalsVisibleTo Include="System.Windows.Controls.Ribbon, PublicKey=00000000000000000400000000000000" />
|
||||
<InternalsVisibleTo Include="PresentationFramework-SystemXml, PublicKey=00000000000000000400000000000000" />
|
||||
<InternalsVisibleTo Include="ReachFramework, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" />
|
||||
<InternalsVisibleTo Include="System.Printing, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" />
|
||||
<InternalsVisibleTo Include="PresentationFramework-SystemXmlLinq, PublicKey=00000000000000000400000000000000" />
|
||||
<InternalsVisibleTo Include="PresentationUI, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" />
|
||||
<InternalsVisibleTo Include="Atlantis, PublicKey=0024000004800000940000000602000000240000525341310004000001000100a7f4b8b7db0bfb8d74992dc94ecafae031019197ff4263d87ac0a5835fab101c973ccab6fa6e7d90e8f987374f7c6de18dd0b5cd7d6c41e574a8bc66b64836b7c7e707e1aa393d27e33a08f372c1c9965be81658937c85698f4a1c0f73be68a61ffce06d49d1366bf18464c20a29859ccf105fc2d5e35c7ae68919eab668bf8e" />
|
||||
<InternalsVisibleTo Include="WindowsBase, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" />
|
||||
<InternalsVisibleTo Include="PresentationFramework.Classic, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" />
|
||||
<InternalsVisibleTo Include="UIAutomationProvider, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" />
|
||||
<InternalsVisibleTo Include="PresentationCore, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -9,10 +9,11 @@
|
||||
<ProjectReference Remove="**/*.shproj" />
|
||||
<ProjectReference Remove="src/Markup/Avalonia.Markup.Xaml/PortableXaml/**/*.*proj" />
|
||||
<ProjectReference Remove="src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github/**/*.*proj" />
|
||||
<!-- Exclude iOS, Android and Web samples from build -->
|
||||
<!-- Exclude iOS, Android and Browser samples from build -->
|
||||
<ProjectReference Remove="samples/*.iOS/*.csproj" />
|
||||
<ProjectReference Remove="samples/*.Android/*.csproj" />
|
||||
<ProjectReference Remove="samples/*.Web/*.csproj" />
|
||||
<ProjectReference Remove="samples/*.Browser/*.csproj" />
|
||||
<ProjectReference Remove="samples/*.Blazor/*.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="!$([MSBuild]::IsOsPlatform('Windows')) OR '$(MSBuildRuntimeType)' != 'Full'">
|
||||
<ProjectReference Remove="src/Windows/Avalonia.Win32.Interop/Avalonia.Win32.Interop.csproj" />
|
||||
|
||||
@@ -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) 
|
||||
<br />
|
||||
[](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.
|
||||
|
||||
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!
|
||||
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.
|
||||
|
||||
#### 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 <b>NuGet</b> 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
|
||||
|
||||
Examples of UIs built with Avalonia
|
||||
<video src="https://user-images.githubusercontent.com/4672627/152325602-28df36ec-6444-44a6-aebe-90ad52c8f27a.mp4"></video>
|
||||
([Lunacy](https://icons8.com/lunacy))
|
||||
|
||||

|
||||
([PlasticSCM](https://www.plasticscm.com/))
|
||||
|
||||

|
||||
([WasabiWallet](https://www.wasabiwallet.io/))
|
||||
[](https://avaloniaui.net/showcase)
|
||||
|
||||
|
||||
## 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)
|
||||
|
||||
@@ -98,6 +98,21 @@
|
||||
<ComboBoxItem>Inline Item 3</ComboBoxItem>
|
||||
<ComboBoxItem>Inline Item 4</ComboBoxItem>
|
||||
</ComboBox>
|
||||
|
||||
<ComboBox WrapSelection="{Binding WrapSelection}" ItemsSource="{Binding Values}" DisplayMemberBinding="{Binding Name}">
|
||||
|
||||
</ComboBox>
|
||||
|
||||
<ComboBox WrapSelection="{Binding WrapSelection}" ItemsSource="{Binding Values}" >
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="{Binding Name}"></TextBlock>
|
||||
<TextBlock Text="{Binding Id}"></TextBlock>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
</WrapPanel>
|
||||
|
||||
<CheckBox IsChecked="{Binding WrapSelection}">WrapSelection</CheckBox>
|
||||
|
||||
@@ -16,5 +16,20 @@ namespace ControlCatalog.ViewModels
|
||||
get => _wrapSelection;
|
||||
set => this.RaiseAndSetIfChanged(ref _wrapSelection, value);
|
||||
}
|
||||
|
||||
public ObservableCollection<IdAndName> Values { get; set; } = new ObservableCollection<IdAndName>
|
||||
{
|
||||
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; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
<Import Project="..\..\build\SourceGenerators.props" />
|
||||
<ItemGroup>
|
||||
<Compile Include="..\Shared\IsExternalInit.cs" Link="IsExternalInit.cs" />
|
||||
<Compile Include="..\Shared\ModuleInitializer.cs" Link="ModuleInitializer.cs" />
|
||||
<Compile Include="..\Shared\StringCompatibilityExtensions.cs" Link="Compatibility\StringCompatibilityExtensions.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Avalonia.Data
|
||||
{
|
||||
@@ -47,7 +48,7 @@ namespace Avalonia.Data
|
||||
/// </summary>
|
||||
Unset = int.MaxValue,
|
||||
|
||||
[Obsolete("Use Template priority")]
|
||||
[Obsolete("Use Template priority"), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
TemplatedParent = Template,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Avalonia.Data.Core.Plugins
|
||||
/// <summary>
|
||||
/// Validates properties on that have <see cref="ValidationAttribute"/>s.
|
||||
/// </summary>
|
||||
internal class DataAnnotationsValidationPlugin : IDataValidationPlugin
|
||||
public class DataAnnotationsValidationPlugin : IDataValidationPlugin
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
[RequiresUnreferencedCode(TrimmingMessages.DataValidationPluginRequiresUnreferencedCodeMessage)]
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace Avalonia.Data.Core.Plugins
|
||||
/// <summary>
|
||||
/// Validates properties that report errors by throwing exceptions.
|
||||
/// </summary>
|
||||
internal class ExceptionValidationPlugin : IDataValidationPlugin
|
||||
public class ExceptionValidationPlugin : IDataValidationPlugin
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
[RequiresUnreferencedCode(TrimmingMessages.DataValidationPluginRequiresUnreferencedCodeMessage)]
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Avalonia.Data.Core.Plugins
|
||||
/// <summary>
|
||||
/// Validates properties on objects that implement <see cref="INotifyDataErrorInfo"/>.
|
||||
/// </summary>
|
||||
internal class IndeiValidationPlugin : IDataValidationPlugin
|
||||
public class IndeiValidationPlugin : IDataValidationPlugin
|
||||
{
|
||||
private static readonly WeakEvent<INotifyDataErrorInfo, DataErrorsChangedEventArgs>
|
||||
ErrorsChangedWeakEvent = WeakEvent.Register<INotifyDataErrorInfo, DataErrorsChangedEventArgs>(
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using Avalonia.Reactive;
|
||||
using ObservableEx = Avalonia.Reactive.Observable;
|
||||
|
||||
@@ -49,7 +50,7 @@ namespace Avalonia.Data
|
||||
/// </summary>
|
||||
public IObservable<object?> Source { get; }
|
||||
|
||||
[Obsolete("Use Source property")]
|
||||
[Obsolete("Use Source property"), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public IObservable<object?> Observable => Source;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Avalonia.Input
|
||||
{
|
||||
@@ -17,7 +18,7 @@ namespace Avalonia.Input
|
||||
/// <summary>
|
||||
/// Dataformat for one or more filenames
|
||||
/// </summary>
|
||||
[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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
/// <returns>
|
||||
/// Collection of file names. If format isn't available, returns null.
|
||||
/// </returns>
|
||||
[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<string>? GetFileNames(this IDataObject dataObject)
|
||||
{
|
||||
return (dataObject.Get(DataFormats.FileNames) as IEnumerable<string>)
|
||||
|
||||
@@ -122,6 +122,11 @@ namespace Avalonia.Input
|
||||
{
|
||||
scope = scope ?? throw new ArgumentNullException(nameof(scope));
|
||||
|
||||
if (element is not null && !CanFocus(element))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_focusScopes.TryGetValue(scope, out var existingElement))
|
||||
{
|
||||
if (element != existingElement)
|
||||
@@ -242,6 +247,6 @@ namespace Avalonia.Input
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsVisible(IInputElement e) => (e as Visual)?.IsVisible ?? true;
|
||||
private static bool IsVisible(IInputElement e) => (e as Visual)?.IsEffectivelyVisible ?? true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -647,6 +647,10 @@ namespace Avalonia.Input
|
||||
{
|
||||
PseudoClasses.Set(":focus-within", change.GetNewValue<bool>());
|
||||
}
|
||||
else if (change.Property == IsVisibleProperty && !change.GetNewValue<bool>() && IsFocused)
|
||||
{
|
||||
FocusManager.Instance?.Focus(null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -48,9 +48,9 @@ namespace Avalonia.Input.TextInput
|
||||
}
|
||||
|
||||
_transformTracker.SetVisual(_client?.TextViewVisual);
|
||||
UpdateCursorRect();
|
||||
|
||||
_im?.SetClient(_client);
|
||||
UpdateCursorRect();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace Avalonia.Layout
|
||||
private readonly Layoutable _owner;
|
||||
private readonly LayoutQueue<Layoutable> _toMeasure = new LayoutQueue<Layoutable>(v => !v.IsMeasureValid);
|
||||
private readonly LayoutQueue<Layoutable> _toArrange = new LayoutQueue<Layoutable>(v => !v.IsArrangeValid);
|
||||
private readonly List<Layoutable> _toArrangeAfterMeasure = new();
|
||||
private readonly Action _executeLayoutPass;
|
||||
private List<EffectiveViewportChangedListener>? _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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Color.ToUInt32"/>
|
||||
[Obsolete("Use Color.ToUInt32() instead.")]
|
||||
[Obsolete("Use Color.ToUInt32() instead."), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public uint ToUint32()
|
||||
{
|
||||
return ToUInt32();
|
||||
|
||||
@@ -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
|
||||
/// <param name="source">The image.</param>
|
||||
/// <param name="sourceRect">The rect in the image to draw.</param>
|
||||
/// <param name="destRect">The rect in the output to draw to.</param>
|
||||
/// <param name="bitmapInterpolationMode">The bitmap interpolation mode.</param>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -68,8 +66,7 @@ namespace Avalonia.Media
|
||||
/// <param name="opacity">The opacity to draw with.</param>
|
||||
/// <param name="sourceRect">The rect in the image to draw.</param>
|
||||
/// <param name="destRect">The rect in the output to draw to.</param>
|
||||
/// <param name="bitmapInterpolationMode">The bitmap interpolation mode.</param>
|
||||
internal abstract void DrawBitmap(IRef<IBitmapImpl> source, double opacity, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode = BitmapInterpolationMode.Default);
|
||||
internal abstract void DrawBitmap(IRef<IBitmapImpl> source, double opacity, Rect sourceRect, Rect destRect);
|
||||
|
||||
/// <summary>
|
||||
/// 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);
|
||||
|
||||
/// <summary>
|
||||
/// Pushes a matrix transformation.
|
||||
/// </summary>
|
||||
@@ -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)
|
||||
|
||||
@@ -196,13 +196,7 @@ namespace Avalonia.Media
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override void PushBitmapBlendModeCore(BitmapBlendingMode blendingMode)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
internal override void DrawBitmap(IRef<IBitmapImpl> source, double opacity, Rect sourceRect, Rect destRect,
|
||||
BitmapInterpolationMode bitmapInterpolationMode = BitmapInterpolationMode.Default)
|
||||
internal override void DrawBitmap(IRef<IBitmapImpl> 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();
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Avalonia.Media
|
||||
{
|
||||
public enum EdgeMode : byte
|
||||
{
|
||||
Unspecified,
|
||||
|
||||
Antialias,
|
||||
Aliased
|
||||
}
|
||||
}
|
||||
@@ -153,7 +153,7 @@ namespace Avalonia.Media
|
||||
/// <summary>
|
||||
/// Gets the conservative bounding box of the <see cref="GlyphRun"/>.
|
||||
/// </summary>
|
||||
public Rect Bounds => PlatformImpl.Item.Bounds;
|
||||
public Rect Bounds => new Rect(new Size(Metrics.WidthIncludingTrailingWhitespace, Metrics.Height));
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
@@ -166,7 +166,7 @@ namespace Avalonia.Media
|
||||
/// </summary>
|
||||
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<IGlyphRunImpl> 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the intersections of specified upper and lower limit.
|
||||
/// </summary>
|
||||
/// <param name="lowerLimit">Upper limit.</param>
|
||||
/// <param name="upperLimit">Lower limit.</param>
|
||||
/// <returns></returns>
|
||||
public IReadOnlyList<float> GetIntersections(float lowerLimit, float upperLimit)
|
||||
{
|
||||
return PlatformImpl.Item.GetIntersections(lowerLimit, upperLimit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,11 +18,9 @@ namespace Avalonia.Media
|
||||
/// <param name="context">The drawing context.</param>
|
||||
/// <param name="sourceRect">The rect in the image to draw.</param>
|
||||
/// <param name="destRect">The rect in the output to draw to.</param>
|
||||
/// <param name="bitmapInterpolationMode">The bitmap interpolation mode.</param>
|
||||
void Draw(
|
||||
DrawingContext context,
|
||||
Rect sourceRect,
|
||||
Rect destRect,
|
||||
BitmapInterpolationMode bitmapInterpolationMode);
|
||||
Rect destRect);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,13 +39,5 @@ namespace Avalonia.Media
|
||||
/// Gets the brush's tile mode.
|
||||
/// </summary>
|
||||
TileMode TileMode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bitmap interpolation mode.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The bitmap interpolation mode.
|
||||
/// </value>
|
||||
BitmapInterpolationMode BitmapInterpolationMode { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -3,8 +3,10 @@ namespace Avalonia.Media.Imaging
|
||||
/// <summary>
|
||||
/// Controls the way the bitmaps are drawn together.
|
||||
/// </summary>
|
||||
public enum BitmapBlendingMode
|
||||
public enum BitmapBlendingMode : byte
|
||||
{
|
||||
Unspecified,
|
||||
|
||||
/// <summary>
|
||||
/// Source is placed over the destination.
|
||||
/// </summary>
|
||||
@@ -52,6 +54,6 @@ namespace Avalonia.Media.Imaging
|
||||
/// <summary>
|
||||
/// Display the sum of the source image and destination image.
|
||||
/// </summary>
|
||||
Plus,
|
||||
Plus
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,12 +3,14 @@
|
||||
/// <summary>
|
||||
/// Controls the performance and quality of bitmap scaling.
|
||||
/// </summary>
|
||||
public enum BitmapInterpolationMode
|
||||
public enum BitmapInterpolationMode : byte
|
||||
{
|
||||
Unspecified,
|
||||
|
||||
/// <summary>
|
||||
/// Uses the default behavior of the underling render backend.
|
||||
/// Disable interpolation.
|
||||
/// </summary>
|
||||
Default,
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// The best performance but worst image quality.
|
||||
@@ -18,7 +20,7 @@
|
||||
/// <summary>
|
||||
/// Good performance and decent image quality.
|
||||
/// </summary>
|
||||
MediumQuality,
|
||||
MediumQuality,
|
||||
|
||||
/// <summary>
|
||||
/// Highest quality but worst performance.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Avalonia.Media.Imaging
|
||||
/// <summary>
|
||||
/// A bitmap that holds the rendering of a <see cref="Visual"/>.
|
||||
/// </summary>
|
||||
public class RenderTargetBitmap : Bitmap, IDisposable
|
||||
public class RenderTargetBitmap : Bitmap
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RenderTargetBitmap"/> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,11 +79,10 @@ namespace Avalonia.Media
|
||||
/// <param name="source">The bitmap.</param>
|
||||
/// <param name="sourceRect">The rect in the image to draw.</param>
|
||||
/// <param name="destRect">The rect in the output to draw to.</param>
|
||||
/// <param name="bitmapInterpolationMode">The bitmap interpolation mode.</param>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -22,7 +22,6 @@ namespace Avalonia.Media.Immutable
|
||||
/// How the source rectangle will be stretched to fill the destination rect.
|
||||
/// </param>
|
||||
/// <param name="tileMode">The tile mode.</param>
|
||||
/// <param name="bitmapInterpolationMode">The bitmap interpolation mode.</param>
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ namespace Avalonia.Media.Immutable
|
||||
/// How the source rectangle will be stretched to fill the destination rect.
|
||||
/// </param>
|
||||
/// <param name="tileMode">The tile mode.</param>
|
||||
/// <param name="bitmapInterpolationMode">The bitmap interpolation mode.</param>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -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
|
||||
|
||||
/// <inheritdoc/>
|
||||
public TileMode TileMode { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public BitmapInterpolationMode BitmapInterpolationMode { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<IBitmapImpl> source, double opacity, Rect sourceRect, Rect destRect,
|
||||
BitmapInterpolationMode bitmapInterpolationMode = BitmapInterpolationMode.Default) =>
|
||||
_impl.DrawBitmap(source, opacity, sourceRect, destRect, bitmapInterpolationMode);
|
||||
internal override void DrawBitmap(IRef<IBitmapImpl> 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();
|
||||
|
||||
@@ -1,36 +1,131 @@
|
||||
using Avalonia.Media.Imaging;
|
||||
|
||||
namespace Avalonia.Media
|
||||
{
|
||||
public class RenderOptions
|
||||
{
|
||||
public readonly record struct RenderOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the <see cref="BitmapInterpolationMode"/> property.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<BitmapInterpolationMode> BitmapInterpolationModeProperty =
|
||||
AvaloniaProperty.RegisterAttached<RenderOptions, AvaloniaObject, BitmapInterpolationMode>(
|
||||
"BitmapInterpolationMode",
|
||||
BitmapInterpolationMode.MediumQuality,
|
||||
inherits: true);
|
||||
public BitmapInterpolationMode BitmapInterpolationMode { get; init; }
|
||||
public EdgeMode EdgeMode { get; init; }
|
||||
public TextRenderingMode TextRenderingMode { get; init; }
|
||||
public BitmapBlendingMode BitmapBlendingMode { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the BitmapInterpolationMode attached property for a control.
|
||||
/// Gets the value of the BitmapInterpolationMode attached property for a visual.
|
||||
/// </summary>
|
||||
/// <param name="element">The control.</param>
|
||||
/// <param name="visual">The control.</param>
|
||||
/// <returns>The control's left coordinate.</returns>
|
||||
public static BitmapInterpolationMode GetBitmapInterpolationMode(AvaloniaObject element)
|
||||
public static BitmapInterpolationMode GetBitmapInterpolationMode(Visual visual)
|
||||
{
|
||||
return element.GetValue(BitmapInterpolationModeProperty);
|
||||
return visual.RenderOptions.BitmapInterpolationMode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the value of the BitmapInterpolationMode attached property for a control.
|
||||
/// Sets the value of the BitmapInterpolationMode attached property for a visual.
|
||||
/// </summary>
|
||||
/// <param name="element">The control.</param>
|
||||
/// <param name="visual">The control.</param>
|
||||
/// <param name="value">The left value.</param>
|
||||
public static void SetBitmapInterpolationMode(AvaloniaObject element, BitmapInterpolationMode value)
|
||||
public static void SetBitmapInterpolationMode(Visual visual, BitmapInterpolationMode value)
|
||||
{
|
||||
element.SetValue(BitmapInterpolationModeProperty, value);
|
||||
visual.RenderOptions = visual.RenderOptions with { BitmapInterpolationMode = value };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the BitmapBlendingMode attached property for a visual.
|
||||
/// </summary>
|
||||
/// <param name="visual">The control.</param>
|
||||
/// <returns>The control's left coordinate.</returns>
|
||||
public static BitmapBlendingMode GetBitmapBlendingMode(Visual visual)
|
||||
{
|
||||
return visual.RenderOptions.BitmapBlendingMode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the value of the BitmapBlendingMode attached property for a visual.
|
||||
/// </summary>
|
||||
/// <param name="visual">The control.</param>
|
||||
/// <param name="value">The left value.</param>
|
||||
public static void SetBitmapBlendingMode(Visual visual, BitmapBlendingMode value)
|
||||
{
|
||||
visual.RenderOptions = visual.RenderOptions with { BitmapBlendingMode = value };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the EdgeMode attached property for a visual.
|
||||
/// </summary>
|
||||
/// <param name="visual">The control.</param>
|
||||
/// <returns>The control's left coordinate.</returns>
|
||||
public static EdgeMode GetEdgeMode(Visual visual)
|
||||
{
|
||||
return visual.RenderOptions.EdgeMode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the value of the EdgeMode attached property for a visual.
|
||||
/// </summary>
|
||||
/// <param name="visual">The control.</param>
|
||||
/// <param name="value">The left value.</param>
|
||||
public static void SetEdgeMode(Visual visual, EdgeMode value)
|
||||
{
|
||||
visual.RenderOptions = visual.RenderOptions with { EdgeMode = value };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the TextRenderingMode attached property for a visual.
|
||||
/// </summary>
|
||||
/// <param name="visual">The control.</param>
|
||||
/// <returns>The control's left coordinate.</returns>
|
||||
public static TextRenderingMode GetTextRenderingMode(Visual visual)
|
||||
{
|
||||
return visual.RenderOptions.TextRenderingMode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the value of the TextRenderingMode attached property for a visual.
|
||||
/// </summary>
|
||||
/// <param name="visual">The control.</param>
|
||||
/// <param name="value">The left value.</param>
|
||||
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
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ namespace Avalonia.Media.TextFormatting
|
||||
/// <inheritdoc/>
|
||||
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--)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace Avalonia.Media
|
||||
{
|
||||
public enum TextRenderingMode : byte
|
||||
{
|
||||
Unspecified,
|
||||
|
||||
SubpixelAntialias,
|
||||
Antialias,
|
||||
Alias
|
||||
}
|
||||
}
|
||||
@@ -83,7 +83,6 @@ namespace Avalonia.Media
|
||||
SourceRectProperty,
|
||||
StretchProperty,
|
||||
TileModeProperty);
|
||||
RenderOptions.BitmapInterpolationModeProperty.OverrideDefaultValue<TileBrush>(BitmapInterpolationMode.Default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -140,17 +139,5 @@ namespace Avalonia.Media
|
||||
get { return (TileMode)GetValue(TileModeProperty); }
|
||||
set { SetValue(TileModeProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the bitmap interpolation mode.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The bitmap interpolation mode.
|
||||
/// </value>
|
||||
public BitmapInterpolationMode BitmapInterpolationMode
|
||||
{
|
||||
get { return RenderOptions.GetBitmapInterpolationMode(this); }
|
||||
set { RenderOptions.SetBitmapInterpolationMode(this, value); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the current render options used to control the rendering behavior of drawing operations.
|
||||
/// </summary>
|
||||
RenderOptions RenderOptions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the current transform of the drawing context.
|
||||
/// </summary>
|
||||
@@ -30,8 +35,7 @@ namespace Avalonia.Platform
|
||||
/// <param name="opacity">The opacity to draw with.</param>
|
||||
/// <param name="sourceRect">The rect in the image to draw.</param>
|
||||
/// <param name="destRect">The rect in the output to draw to.</param>
|
||||
/// <param name="bitmapInterpolationMode">The bitmap interpolation mode.</param>
|
||||
void DrawBitmap(IRef<IBitmapImpl> source, double opacity, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode = BitmapInterpolationMode.Default);
|
||||
void DrawBitmap(IRef<IBitmapImpl> source, double opacity, Rect sourceRect, Rect destRect);
|
||||
|
||||
/// <summary>
|
||||
/// Draws a bitmap image.
|
||||
@@ -157,15 +161,6 @@ namespace Avalonia.Platform
|
||||
void PopGeometryClip();
|
||||
|
||||
/// <summary>
|
||||
/// Pushes a bitmap blending value.
|
||||
/// </summary>
|
||||
/// <param name="blendingMode">The bitmap blending mode.</param>
|
||||
void PushBitmapBlendMode(BitmapBlendingMode blendingMode);
|
||||
|
||||
/// <summary>
|
||||
/// Pops the latest pushed bitmap blending value.
|
||||
/// </summary>
|
||||
void PopBitmapBlendMode();
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to get an optional feature from the drawing context implementation
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
|
||||
namespace Avalonia.Platform
|
||||
{
|
||||
public interface IGlyphRunBuffer
|
||||
{
|
||||
Span<ushort> GlyphIndices { get; }
|
||||
|
||||
IGlyphRunImpl Build();
|
||||
}
|
||||
|
||||
public interface IHorizontalGlyphRunBuffer : IGlyphRunBuffer
|
||||
{
|
||||
Span<float> GlyphPositions { get; }
|
||||
}
|
||||
|
||||
public interface IPositionedGlyphRunBuffer : IGlyphRunBuffer
|
||||
{
|
||||
Span<PointF> GlyphPositions { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,36 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Metadata;
|
||||
|
||||
namespace Avalonia.Platform
|
||||
{
|
||||
/// <summary>
|
||||
/// Actual implementation of a glyph run that stores platform dependent resources.
|
||||
/// An immutable platform representation of a <see cref="GlyphRun"/>.
|
||||
/// </summary>
|
||||
[Unstable]
|
||||
public interface IGlyphRunImpl : IDisposable
|
||||
public interface IGlyphRunImpl : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the conservative bounding box of the glyph run./>.
|
||||
/// Gets the <see cref="IGlyphTypeface"/> for the <see cref="IGlyphRunImpl"/>.
|
||||
/// </summary>
|
||||
Rect Bounds { get; }
|
||||
IGlyphTypeface GlyphTypeface { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the em size used for rendering the <see cref="IGlyphRunImpl"/>.
|
||||
/// </summary>
|
||||
double FontRenderingEmSize { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the baseline origin of the glyph run./>.
|
||||
/// </summary>
|
||||
Point BaselineOrigin { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the conservative bounding box of the glyph run./>.
|
||||
/// </summary>
|
||||
Rect Bounds { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the intersections of specified upper and lower limit.
|
||||
/// </summary>
|
||||
|
||||
@@ -169,8 +169,9 @@ namespace Avalonia.Platform
|
||||
/// <param name="fontRenderingEmSize">The font rendering em size.</param>
|
||||
/// <param name="glyphInfos">The list of glyphs.</param>
|
||||
/// <param name="baselineOrigin">The baseline origin of the run. Can be null.</param>
|
||||
/// <param name="bounds">the conservative bounding box of the run</param>
|
||||
/// <returns>An <see cref="IGlyphRunImpl"/>.</returns>
|
||||
IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize, IReadOnlyList<GlyphInfo> glyphInfos, Point baselineOrigin);
|
||||
IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize, IReadOnlyList<GlyphInfo> glyphInfos, Point baselineOrigin, Rect bounds);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a backend-specific object using a low-level API graphics context
|
||||
|
||||
@@ -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;
|
||||
/// <summary>
|
||||
/// Loads assets compiled into the application binary.
|
||||
/// </summary>
|
||||
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<Uri>();
|
||||
}
|
||||
|
||||
public static void RegisterResUriParsers() => AssetLoader.RegisterResUriParsers();
|
||||
|
||||
private bool TryGetAsset(Uri uri, Uri? baseUri, [NotNullWhen(true)] out IAssetDescriptor? assetDescriptor)
|
||||
{
|
||||
assetDescriptor = null;
|
||||
|
||||
@@ -50,7 +50,8 @@ internal static class StorageProviderHelpers
|
||||
}
|
||||
}
|
||||
|
||||
public static string NameWithExtension(string path, string? defaultExtension, FilePickerFileType? filter)
|
||||
[return: NotNullIfNotNull(nameof(path))]
|
||||
public static string? NameWithExtension(string? path, string? defaultExtension, FilePickerFileType? filter)
|
||||
{
|
||||
var name = Path.GetFileName(path);
|
||||
if (name != null && !Path.HasExtension(name))
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -78,15 +78,14 @@ internal sealed class CompositionDrawingContext : DrawingContext, IDrawingContex
|
||||
}
|
||||
}
|
||||
|
||||
internal override void DrawBitmap(IRef<IBitmapImpl> source, double opacity, Rect sourceRect, Rect destRect,
|
||||
BitmapInterpolationMode bitmapInterpolationMode = BitmapInterpolationMode.Default)
|
||||
internal override void DrawBitmap(IRef<IBitmapImpl> source, double opacity, Rect sourceRect, Rect destRect)
|
||||
{
|
||||
var next = NextDrawAs<ImageNode>();
|
||||
|
||||
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<BitmapBlendModeNode>();
|
||||
|
||||
if (next == null || !next.Item.Equals(null))
|
||||
{
|
||||
Add(new BitmapBlendModeNode());
|
||||
}
|
||||
else
|
||||
{
|
||||
++_drawOperationIndex;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void PopOpacityCore()
|
||||
{
|
||||
var next = NextDrawAs<OpacityNode>();
|
||||
@@ -354,21 +339,6 @@ internal sealed class CompositionDrawingContext : DrawingContext, IDrawingContex
|
||||
_needsToPopOpacityMask.Push(needsToPop);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void PushBitmapBlendModeCore(BitmapBlendingMode blendingMode)
|
||||
{
|
||||
var next = NextDrawAs<BitmapBlendModeNode>();
|
||||
|
||||
if (next == null || !next.Item.Equals(blendingMode))
|
||||
{
|
||||
Add(new BitmapBlendModeNode(blendingMode));
|
||||
}
|
||||
else
|
||||
{
|
||||
++_drawOperationIndex;
|
||||
}
|
||||
}
|
||||
|
||||
private void Add<T>(T node) where T : class, IDrawOperation
|
||||
{
|
||||
if (_drawOperationIndex < _builder.Count)
|
||||
|
||||
@@ -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<IBitmapImpl> source, double opacity, Rect sourceRect, Rect destRect,
|
||||
BitmapInterpolationMode bitmapInterpolationMode = BitmapInterpolationMode.Default)
|
||||
public void DrawBitmap(IRef<IBitmapImpl> source, double opacity, Rect sourceRect, Rect destRect)
|
||||
{
|
||||
_impl.DrawBitmap(source, opacity, sourceRect, destRect, bitmapInterpolationMode);
|
||||
_impl.DrawBitmap(source, opacity, sourceRect, destRect);
|
||||
}
|
||||
|
||||
public void DrawBitmap(IRef<IBitmapImpl> 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);
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
platformContext.RenderOptions = visual.RenderOptions.MergeWith(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;
|
||||
}
|
||||
var opacity = visual.Opacity;
|
||||
var clipToBounds = visual.ClipToBounds;
|
||||
var bounds = new Rect(visual.Bounds.Size);
|
||||
|
||||
m = renderTransform * m;
|
||||
|
||||
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
|
||||
{
|
||||
clipRect = clipRect.Intersect(new Rect(visual.Bounds.Size));
|
||||
}
|
||||
}
|
||||
|
||||
using (context.PushTransform(m))
|
||||
using (context.PushOpacity(opacity, bounds))
|
||||
using (clipToBounds
|
||||
m = renderTransform * m;
|
||||
|
||||
if (clipToBounds)
|
||||
{
|
||||
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
|
||||
#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>)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);
|
||||
|
||||
if (!child.ClipToBounds || clipRect.Intersects(childBounds))
|
||||
var childrenEnumerable = visual.HasNonUniformZIndexChildren
|
||||
? visual.VisualChildren.OrderBy(x => x, ZIndexComparer.Instance)
|
||||
: (IEnumerable<Visual>)visual.VisualChildren;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
using Avalonia.Platform;
|
||||
using Avalonia.Media.Imaging;
|
||||
|
||||
namespace Avalonia.Rendering.SceneGraph
|
||||
{
|
||||
/// <summary>
|
||||
/// A node in the scene graph which represents an bitmap blending mode push or pop.
|
||||
/// </summary>
|
||||
internal class BitmapBlendModeNode : IDrawOperation
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BitmapBlendModeNode"/> class that represents an
|
||||
/// <see cref="BitmapBlendingMode"/> push.
|
||||
/// </summary>
|
||||
/// <param name="bitmapBlend">The <see cref="BitmapBlendingMode"/> to push.</param>
|
||||
public BitmapBlendModeNode(BitmapBlendingMode bitmapBlend)
|
||||
{
|
||||
BlendingMode = bitmapBlend;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BitmapBlendModeNode"/> class that represents an
|
||||
/// <see cref="BitmapBlendingMode"/> pop.
|
||||
/// </summary>
|
||||
public BitmapBlendModeNode()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Rect Bounds => default;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the BitmapBlend to be pushed or null if the operation represents a pop.
|
||||
/// </summary>
|
||||
public BitmapBlendingMode? BlendingMode { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool HitTest(Point p) => false;
|
||||
|
||||
/// <summary>
|
||||
/// Determines if this draw operation equals another.
|
||||
/// </summary>
|
||||
/// <param name="blendingMode">the <see cref="BitmapBlendModeNode"/> how to compare</param>
|
||||
/// <returns>True if the draw operations are the same, otherwise false.</returns>
|
||||
/// <remarks>
|
||||
/// The properties of the other draw operation are passed in as arguments to prevent
|
||||
/// allocation of a not-yet-constructed draw operation object.
|
||||
/// </remarks>
|
||||
public bool Equals(BitmapBlendingMode? blendingMode) => BlendingMode == blendingMode;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Render(IDrawingContextImpl context)
|
||||
{
|
||||
if (BlendingMode.HasValue)
|
||||
{
|
||||
context.PushBitmapBlendMode(BlendingMode.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.PopBitmapBlendMode();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
/// <param name="glyphRun">The glyph run to draw.</param>
|
||||
public GlyphRunNode(
|
||||
Matrix transform,
|
||||
IImmutableBrush foreground,
|
||||
IImmutableBrush? foreground,
|
||||
IRef<IGlyphRunImpl> glyphRun)
|
||||
: base(glyphRun.Item.Bounds, transform, foreground)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
/// <param name="opacity">The draw opacity.</param>
|
||||
/// <param name="sourceRect">The source rect.</param>
|
||||
/// <param name="destRect">The destination rect.</param>
|
||||
/// <param name="bitmapInterpolationMode">The bitmap interpolation mode.</param>
|
||||
public ImageNode(Matrix transform, IRef<IBitmapImpl> source, double opacity, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode)
|
||||
public ImageNode(Matrix transform, IRef<IBitmapImpl> 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.
|
||||
/// </summary>
|
||||
public Rect DestRect { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bitmap interpolation mode.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The scaling mode.
|
||||
/// </value>
|
||||
public BitmapInterpolationMode BitmapInterpolationMode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines if this draw operation equals another.
|
||||
@@ -70,27 +59,25 @@ namespace Avalonia.Rendering.SceneGraph
|
||||
/// <param name="opacity">The opacity of the other draw operation.</param>
|
||||
/// <param name="sourceRect">The source rect of the other draw operation.</param>
|
||||
/// <param name="destRect">The dest rect of the other draw operation.</param>
|
||||
/// <param name="bitmapInterpolationMode">The bitmap interpolation mode.</param>
|
||||
/// <returns>True if the draw operations are the same, otherwise false.</returns>
|
||||
/// <remarks>
|
||||
/// The properties of the other draw operation are passed in as arguments to prevent
|
||||
/// allocation of a not-yet-constructed draw operation object.
|
||||
/// </remarks>
|
||||
public bool Equals(Matrix transform, IRef<IBitmapImpl> source, double opacity, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode)
|
||||
public bool Equals(Matrix transform, IRef<IBitmapImpl> 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;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Render(IDrawingContextImpl context)
|
||||
{
|
||||
context.DrawBitmap(Source, Opacity, SourceRect, DestRect, BitmapInterpolationMode);
|
||||
context.DrawBitmap(Source, Opacity, SourceRect, DestRect);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
||||
@@ -248,11 +248,11 @@ public partial class Dispatcher
|
||||
/// An operation representing the queued delegate to be invoked.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// Note that the default priority is DispatcherPriority.Normal.
|
||||
/// Note that the default priority is DispatcherPriority.Default.
|
||||
/// </remarks>
|
||||
public DispatcherOperation InvokeAsync(Action callback)
|
||||
{
|
||||
return InvokeAsync(callback, DispatcherPriority.Normal, CancellationToken.None);
|
||||
return InvokeAsync(callback, default, CancellationToken.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -326,11 +326,11 @@ public partial class Dispatcher
|
||||
/// An operation representing the queued delegate to be invoked.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// Note that the default priority is DispatcherPriority.Normal.
|
||||
/// Note that the default priority is DispatcherPriority.Default.
|
||||
/// </remarks>
|
||||
public DispatcherOperation<TResult> InvokeAsync<TResult>(Func<TResult> callback)
|
||||
{
|
||||
return InvokeAsync(callback, DispatcherPriority.Normal, CancellationToken.None);
|
||||
return InvokeAsync(callback, DispatcherPriority.Default, CancellationToken.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -541,6 +541,18 @@ public partial class Dispatcher
|
||||
InvokeAsyncImpl(new DispatcherOperation(this, priority, action, true), CancellationToken.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the specified Func<Task> asynchronously on the
|
||||
/// thread that the Dispatcher was created on
|
||||
/// </summary>
|
||||
/// <param name="callback">
|
||||
/// A Func<Task> delegate to invoke through the dispatcher.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// An task that completes after the task returned from callback finishes.
|
||||
/// </returns>
|
||||
public Task InvokeAsync(Func<Task> callback) => InvokeAsync(callback, DispatcherPriority.Default);
|
||||
|
||||
/// <summary>
|
||||
/// Executes the specified Func<Task> asynchronously on the
|
||||
/// thread that the Dispatcher was created on
|
||||
@@ -556,11 +568,29 @@ public partial class Dispatcher
|
||||
/// <returns>
|
||||
/// An task that completes after the task returned from callback finishes
|
||||
/// </returns>
|
||||
public Task InvokeAsync(Func<Task> callback, DispatcherPriority priority = default)
|
||||
public Task InvokeAsync(Func<Task> callback, DispatcherPriority priority)
|
||||
{
|
||||
_ = callback ?? throw new ArgumentNullException(nameof(callback));
|
||||
return InvokeAsync<Task>(callback, priority).GetTask().Unwrap();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the specified Func<Task<TResult>> asynchronously on the
|
||||
/// thread that the Dispatcher was created on
|
||||
/// </summary>
|
||||
/// <param name="action">
|
||||
/// A Func<Task<TResult>> delegate to invoke through the dispatcher.
|
||||
/// </param>
|
||||
/// <param name="priority">
|
||||
/// The priority that determines in what order the specified
|
||||
/// callback is invoked relative to the other pending operations
|
||||
/// in the Dispatcher.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// An task that completes after the task returned from callback finishes
|
||||
/// </returns>
|
||||
public Task<TResult> InvokeAsync<TResult>(Func<Task<TResult>> action) =>
|
||||
InvokeAsync(action, DispatcherPriority.Default);
|
||||
|
||||
/// <summary>
|
||||
/// Executes the specified Func<Task<TResult>> asynchronously on the
|
||||
@@ -577,7 +607,7 @@ public partial class Dispatcher
|
||||
/// <returns>
|
||||
/// An task that completes after the task returned from callback finishes
|
||||
/// </returns>
|
||||
public Task<TResult> InvokeAsync<TResult>(Func<Task<TResult>> action, DispatcherPriority priority = default)
|
||||
public Task<TResult> InvokeAsync<TResult>(Func<Task<TResult>> action, DispatcherPriority priority)
|
||||
{
|
||||
_ = action ?? throw new ArgumentNullException(nameof(action));
|
||||
return InvokeAsync<Task<TResult>>(action, priority).GetTask().Unwrap();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
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)
|
||||
{
|
||||
// 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");
|
||||
|
||||
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
|
||||
{
|
||||
_isRunning = false;
|
||||
_cancellationTokenSource?.Cancel();
|
||||
_cancellationTokenSource = null;
|
||||
_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()
|
||||
{
|
||||
|
||||
@@ -331,6 +331,8 @@ public class DispatcherOperation<T> : DispatcherOperation
|
||||
|
||||
private TaskCompletionSource<T> TaskCompletionSource => (TaskCompletionSource<T>)TaskSource!;
|
||||
|
||||
public new TaskAwaiter<T> GetAwaiter() => GetTask().GetAwaiter();
|
||||
|
||||
public new Task<T> GetTask() => TaskCompletionSource!.Task;
|
||||
|
||||
protected override Task GetTaskCore() => GetTask();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Avalonia.Threading
|
||||
{
|
||||
@@ -100,7 +101,7 @@ namespace Avalonia.Threading
|
||||
/// <summary>
|
||||
/// The job will be processed with the same priority as data binding.
|
||||
/// </summary>
|
||||
[Obsolete("WPF compatibility")] public static readonly DispatcherPriority DataBind = new(Layout);
|
||||
[Obsolete("WPF compatibility"), EditorBrowsable(EditorBrowsableState.Never)] public static readonly DispatcherPriority DataBind = new(Layout);
|
||||
|
||||
/// <summary>
|
||||
/// The job will be processed with normal priority.
|
||||
|
||||
@@ -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; }
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value indicating the corner radius of control's clip bounds
|
||||
/// </summary>
|
||||
[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; }
|
||||
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
<Property Name="AdornerIsClipped" Type="bool" Internal="true" />
|
||||
<Property Name="OpacityMaskBrush" Type="Avalonia.Media.IImmutableBrush?" Internal="true" />
|
||||
<Property Name="Effect" Type="Avalonia.Media.IImmutableEffect?" Internal="true" />
|
||||
<Property Name="RenderOptions" Type="Avalonia.Media.RenderOptions" />
|
||||
</Object>
|
||||
<Object Name="CompositionContainerVisual" Inherits="CompositionVisual"/>
|
||||
<Object Name="CompositionSolidColorVisual" Inherits="CompositionContainerVisual">
|
||||
|
||||
@@ -729,6 +729,8 @@ namespace Avalonia.Controls
|
||||
RowDetailsTemplateProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnRowDetailsTemplateChanged(e));
|
||||
RowDetailsVisibilityModeProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnRowDetailsVisibilityModeChanged(e));
|
||||
AutoGenerateColumnsProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnAutoGenerateColumnsChanged(e));
|
||||
|
||||
FocusableProperty.OverrideDefaultValue<DataGrid>(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -2478,7 +2480,7 @@ namespace Avalonia.Controls
|
||||
|
||||
if (_hScrollBar != null)
|
||||
{
|
||||
//_hScrollBar.IsTabStop = false;
|
||||
_hScrollBar.IsTabStop = false;
|
||||
_hScrollBar.Maximum = 0.0;
|
||||
_hScrollBar.Orientation = Orientation.Horizontal;
|
||||
_hScrollBar.IsVisible = false;
|
||||
@@ -2494,7 +2496,7 @@ namespace Avalonia.Controls
|
||||
|
||||
if (_vScrollBar != null)
|
||||
{
|
||||
//_vScrollBar.IsTabStop = false;
|
||||
_vScrollBar.IsTabStop = false;
|
||||
_vScrollBar.Maximum = 0.0;
|
||||
_vScrollBar.Orientation = Orientation.Vertical;
|
||||
_vScrollBar.IsVisible = false;
|
||||
@@ -3734,7 +3736,7 @@ namespace Avalonia.Controls
|
||||
if (sender is Control editingElement)
|
||||
{
|
||||
editingElement.LostFocus -= EditingElement_LostFocus;
|
||||
if (EditingRow != null && EditingColumnIndex != -1)
|
||||
if (EditingRow != null && _editingColumnIndex != -1)
|
||||
{
|
||||
FocusEditingCell(true);
|
||||
}
|
||||
@@ -4039,18 +4041,22 @@ namespace Avalonia.Controls
|
||||
return true;
|
||||
}
|
||||
|
||||
Debug.Assert(EditingRow != null);
|
||||
var editingRow = EditingRow;
|
||||
if (editingRow is null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
Debug.Assert(_editingColumnIndex >= 0);
|
||||
Debug.Assert(_editingColumnIndex < ColumnsItemsInternal.Count);
|
||||
Debug.Assert(_editingColumnIndex == CurrentColumnIndex);
|
||||
Debug.Assert(EditingRow != null && EditingRow.Slot == CurrentSlot);
|
||||
|
||||
// Cache these to see if they change later
|
||||
int currentSlot = CurrentSlot;
|
||||
int currentColumnIndex = CurrentColumnIndex;
|
||||
|
||||
// We're ready to start ending, so raise the event
|
||||
DataGridCell editingCell = EditingRow.Cells[_editingColumnIndex];
|
||||
DataGridCell editingCell = editingRow.Cells[_editingColumnIndex];
|
||||
var editingElement = editingCell.Content as Control;
|
||||
if (editingElement == null)
|
||||
{
|
||||
@@ -4058,7 +4064,7 @@ namespace Avalonia.Controls
|
||||
}
|
||||
if (raiseEvents)
|
||||
{
|
||||
DataGridCellEditEndingEventArgs e = new DataGridCellEditEndingEventArgs(CurrentColumn, EditingRow, editingElement, editAction);
|
||||
DataGridCellEditEndingEventArgs e = new DataGridCellEditEndingEventArgs(CurrentColumn, editingRow, editingElement, editAction);
|
||||
OnCellEditEnding(e);
|
||||
if (e.Cancel)
|
||||
{
|
||||
@@ -4112,7 +4118,7 @@ namespace Avalonia.Controls
|
||||
}
|
||||
else
|
||||
{
|
||||
if (EditingRow != null)
|
||||
if (editingRow != null)
|
||||
{
|
||||
if (editingCell.IsValid)
|
||||
{
|
||||
@@ -4120,10 +4126,10 @@ namespace Avalonia.Controls
|
||||
editingCell.UpdatePseudoClasses();
|
||||
}
|
||||
|
||||
if (EditingRow.IsValid)
|
||||
if (editingRow.IsValid)
|
||||
{
|
||||
EditingRow.IsValid = false;
|
||||
EditingRow.UpdatePseudoClasses();
|
||||
editingRow.IsValid = false;
|
||||
editingRow.UpdatePseudoClasses();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4169,22 +4175,22 @@ namespace Avalonia.Controls
|
||||
PopulateCellContent(
|
||||
isCellEdited: !exitEditingMode,
|
||||
dataGridColumn: CurrentColumn,
|
||||
dataGridRow: EditingRow,
|
||||
dataGridRow: editingRow,
|
||||
dataGridCell: editingCell);
|
||||
|
||||
EditingRow.InvalidateDesiredHeight();
|
||||
editingRow.InvalidateDesiredHeight();
|
||||
var column = editingCell.OwningColumn;
|
||||
if (column.Width.IsSizeToCells || column.Width.IsAuto)
|
||||
{// Invalidate desired width and force recalculation
|
||||
column.SetWidthDesiredValue(0);
|
||||
EditingRow.OwningGrid.AutoSizeColumn(column, editingCell.DesiredSize.Width);
|
||||
editingRow.OwningGrid.AutoSizeColumn(column, editingCell.DesiredSize.Width);
|
||||
}
|
||||
}
|
||||
|
||||
// We're done, so raise the CellEditEnded event
|
||||
if (raiseEvents)
|
||||
{
|
||||
OnCellEditEnded(new DataGridCellEditEndedEventArgs(CurrentColumn, EditingRow, editAction));
|
||||
OnCellEditEnded(new DataGridCellEditEndedEventArgs(CurrentColumn, editingRow, editAction));
|
||||
}
|
||||
|
||||
// There's a chance that somebody reopened this cell for edit within the CellEditEnded handler,
|
||||
@@ -4427,8 +4433,7 @@ namespace Avalonia.Controls
|
||||
dataGridCell.Focus();
|
||||
success = dataGridCell.ContainsFocusedElement();
|
||||
}
|
||||
//TODO Check
|
||||
//success = dataGridCell.ContainsFocusedElement() ? true : dataGridCell.Focus();
|
||||
|
||||
_focusEditingControl = !success;
|
||||
}
|
||||
return success;
|
||||
|
||||
@@ -33,6 +33,8 @@ namespace Avalonia.Controls
|
||||
{
|
||||
PointerPressedEvent.AddClassHandler<DataGridCell>(
|
||||
(x,e) => x.DataGridCell_PointerPressed(e), handledEventsToo: true);
|
||||
FocusableProperty.OverrideDefaultValue<DataGridCell>(true);
|
||||
IsTabStopProperty.OverrideDefaultValue<DataGridCell>(false);
|
||||
}
|
||||
public DataGridCell()
|
||||
{ }
|
||||
@@ -169,8 +171,7 @@ namespace Avalonia.Controls
|
||||
OwningGrid.OnCellPointerPressed(new DataGridCellPointerPressedEventArgs(this, OwningRow, OwningColumn, e));
|
||||
if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
|
||||
{
|
||||
if (!e.Handled)
|
||||
//if (!e.Handled && OwningGrid.IsTabStop)
|
||||
if (!e.Handled && OwningGrid.IsTabStop)
|
||||
{
|
||||
OwningGrid.Focus();
|
||||
}
|
||||
@@ -190,8 +191,7 @@ namespace Avalonia.Controls
|
||||
}
|
||||
else if (e.GetCurrentPoint(this).Properties.IsRightButtonPressed)
|
||||
{
|
||||
if (!e.Handled)
|
||||
//if (!e.Handled && OwningGrid.IsTabStop)
|
||||
if (!e.Handled && OwningGrid.IsTabStop)
|
||||
{
|
||||
OwningGrid.Focus();
|
||||
}
|
||||
|
||||
@@ -72,6 +72,7 @@ namespace Avalonia.Controls
|
||||
{
|
||||
AreSeparatorsVisibleProperty.Changed.AddClassHandler<DataGridColumnHeader>((x, e) => x.OnAreSeparatorsVisibleChanged(e));
|
||||
PressedMixin.Attach<DataGridColumnHeader>();
|
||||
IsTabStopProperty.OverrideDefaultValue<DataGridColumnHeader>(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -12,6 +12,7 @@ using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using Avalonia.Layout;
|
||||
|
||||
namespace Avalonia.Controls
|
||||
{
|
||||
@@ -489,7 +490,7 @@ namespace Avalonia.Controls
|
||||
{
|
||||
DataGridFillerColumn fillerColumn = ColumnsInternal.FillerColumn;
|
||||
double totalColumnsWidth = ColumnsInternal.VisibleEdgedColumnsWidth;
|
||||
if (finalWidth > totalColumnsWidth)
|
||||
if (finalWidth - totalColumnsWidth > LayoutHelper.LayoutEpsilon)
|
||||
{
|
||||
fillerColumn.FillerWidth = finalWidth - totalColumnsWidth;
|
||||
}
|
||||
@@ -971,6 +972,12 @@ namespace Avalonia.Controls
|
||||
{
|
||||
cx += _negHorizontalOffset;
|
||||
_horizontalOffset -= _negHorizontalOffset;
|
||||
if (_horizontalOffset < LayoutHelper.LayoutEpsilon)
|
||||
{
|
||||
// Snap to zero to avoid trying to partially scroll in first scrolled off column below
|
||||
_horizontalOffset = 0;
|
||||
}
|
||||
|
||||
_negHorizontalOffset = 0;
|
||||
}
|
||||
else
|
||||
@@ -979,6 +986,11 @@ namespace Avalonia.Controls
|
||||
_negHorizontalOffset -= displayWidth - cx;
|
||||
cx = displayWidth;
|
||||
}
|
||||
|
||||
// Make sure the HorizontalAdjustment is not greater than the new HorizontalOffset
|
||||
// since it would cause an assertion failure in DataGridCellsPresenter.ShouldDisplayCell
|
||||
// called by DataGridCellsPresenter.MeasureOverride.
|
||||
HorizontalAdjustment = Math.Min(HorizontalAdjustment, _horizontalOffset);
|
||||
}
|
||||
// second try to scroll entire columns
|
||||
if (cx < displayWidth && _horizontalOffset > 0)
|
||||
|
||||
@@ -128,6 +128,7 @@ namespace Avalonia.Controls
|
||||
DetailsTemplateProperty.Changed.AddClassHandler<DataGridRow>((x, e) => x.OnDetailsTemplateChanged(e));
|
||||
AreDetailsVisibleProperty.Changed.AddClassHandler<DataGridRow>((x, e) => x.OnAreDetailsVisibleChanged(e));
|
||||
PointerPressedEvent.AddClassHandler<DataGridRow>((x, e) => x.DataGridRow_PointerPressed(e), handledEventsToo: true);
|
||||
IsTabStopProperty.OverrideDefaultValue<DataGridRow>(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -106,6 +106,7 @@ namespace Avalonia.Controls
|
||||
{
|
||||
SublevelIndentProperty.Changed.AddClassHandler<DataGridRowGroupHeader>((x,e) => x.OnSublevelIndentChanged(e));
|
||||
PressedMixin.Attach<DataGridRowGroupHeader>();
|
||||
IsTabStopProperty.OverrideDefaultValue<DataGridRowGroupHeader>(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -301,8 +302,7 @@ namespace Avalonia.Controls
|
||||
}
|
||||
else
|
||||
{
|
||||
//if (!e.Handled && OwningGrid.IsTabStop)
|
||||
if (!e.Handled)
|
||||
if (!e.Handled && OwningGrid.IsTabStop)
|
||||
{
|
||||
OwningGrid.Focus();
|
||||
}
|
||||
|
||||
@@ -1589,6 +1589,23 @@ namespace Avalonia.Controls
|
||||
CorrectSlotsAfterDeletion(slot, isRow);
|
||||
|
||||
OnRemovedElement(slot, item);
|
||||
|
||||
// Synchronize CurrentCellCoordinates, CurrentColumn, CurrentColumnIndex, CurrentItem
|
||||
// and CurrentSlot with the currently edited cell, since OnRemovingElement called
|
||||
// SetCurrentCellCore(-1, -1) to temporarily reset the current cell.
|
||||
if (_temporarilyResetCurrentCell &&
|
||||
_editingColumnIndex != -1 &&
|
||||
_previousCurrentItem != null &&
|
||||
EditingRow != null &&
|
||||
EditingRow.Slot != -1)
|
||||
{
|
||||
ProcessSelectionAndCurrency(
|
||||
columnIndex: _editingColumnIndex,
|
||||
item: _previousCurrentItem,
|
||||
backupSlot: this.EditingRow.Slot,
|
||||
action: DataGridSelectionAction.None,
|
||||
scrollIntoView: false);
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveNonDisplayedRows(int newFirstDisplayedSlot, int newLastDisplayedSlot)
|
||||
|
||||
@@ -82,7 +82,6 @@
|
||||
<Setter Property="VerticalContentAlignment" Value="Stretch" />
|
||||
<Setter Property="FontSize" Value="15" />
|
||||
<Setter Property="MinHeight" Value="32" />
|
||||
<Setter Property="Focusable" Value="False" />
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate>
|
||||
<Border x:Name="CellBorder"
|
||||
@@ -157,7 +156,6 @@
|
||||
<Setter Property="Background" Value="{DynamicResource DataGridColumnHeaderBackgroundBrush}" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
||||
<Setter Property="VerticalContentAlignment" Value="Center" />
|
||||
<Setter Property="Focusable" Value="False" />
|
||||
<Setter Property="SeparatorBrush" Value="{DynamicResource DataGridGridLinesBrush}" />
|
||||
<Setter Property="Padding" Value="12,0,0,0" />
|
||||
<Setter Property="FontSize" Value="12" />
|
||||
@@ -268,7 +266,6 @@
|
||||
</ControlTheme>
|
||||
|
||||
<ControlTheme x:Key="{x:Type DataGridRowHeader}" TargetType="DataGridRowHeader">
|
||||
<Setter Property="Focusable" Value="False" />
|
||||
<Setter Property="SeparatorBrush" Value="{DynamicResource DataGridGridLinesBrush}" />
|
||||
<Setter Property="AreSeparatorsVisible" Value="False" />
|
||||
<Setter Property="Template">
|
||||
@@ -310,7 +307,6 @@
|
||||
</ControlTheme>
|
||||
|
||||
<ControlTheme x:Key="{x:Type DataGridRow}" TargetType="DataGridRow">
|
||||
<Setter Property="Focusable" Value="False" />
|
||||
<Setter Property="Background" Value="{Binding $parent[DataGrid].RowBackground}" />
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate>
|
||||
@@ -408,7 +404,6 @@
|
||||
</ControlTheme>
|
||||
|
||||
<ControlTheme x:Key="{x:Type DataGridRowGroupHeader}" TargetType="DataGridRowGroupHeader">
|
||||
<Setter Property="Focusable" Value="False" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource DataGridRowGroupHeaderForegroundBrush}" />
|
||||
<Setter Property="Background" Value="{DynamicResource DataGridRowGroupHeaderBackgroundBrush}" />
|
||||
<Setter Property="FontSize" Value="15" />
|
||||
@@ -433,7 +428,7 @@
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
Background="{TemplateBinding Background}"
|
||||
CornerRadius="{TemplateBinding CornerRadius}"
|
||||
Focusable="False"
|
||||
IsTabStop="False"
|
||||
Foreground="{TemplateBinding Foreground}" />
|
||||
|
||||
<StackPanel Grid.Column="3"
|
||||
@@ -503,6 +498,7 @@
|
||||
<Setter Property="GridLinesVisibility" Value="None" />
|
||||
<Setter Property="HorizontalGridLinesBrush" Value="{DynamicResource DataGridGridLinesBrush}" />
|
||||
<Setter Property="VerticalGridLinesBrush" Value="{DynamicResource DataGridGridLinesBrush}" />
|
||||
<Setter Property="FocusAdorner" Value="{x:Null}" />
|
||||
<Setter Property="DropLocationIndicatorTemplate">
|
||||
<Template>
|
||||
<Rectangle Fill="{DynamicResource DataGridDropLocationIndicatorBackground}"
|
||||
|
||||
@@ -126,7 +126,6 @@
|
||||
|
||||
<ControlTheme x:Key="{x:Type DataGridRowHeader}"
|
||||
TargetType="DataGridRowHeader">
|
||||
<Setter Property="Focusable" Value="False" />
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate>
|
||||
<Grid x:Name="PART_Root"
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace Avalonia.Controls.Utils
|
||||
/// <returns>True if the currently focused element is within the visual tree of the parent</returns>
|
||||
internal static bool ContainsFocusedElement(this Visual element)
|
||||
{
|
||||
return (element == null) ? false : element.ContainsChild(FocusManager.Instance.Current as Visual);
|
||||
return element is InputElement { IsKeyboardFocusWithin: true };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,8 +170,15 @@ namespace Avalonia.Controls
|
||||
UpdateFlowDirection();
|
||||
}
|
||||
|
||||
protected internal override Control CreateContainerForItemOverride() => new ComboBoxItem();
|
||||
protected internal override bool IsItemItsOwnContainerOverride(Control item) => item is ComboBoxItem;
|
||||
protected internal override Control CreateContainerForItemOverride(object? item, int index, object? recycleKey)
|
||||
{
|
||||
return new ComboBoxItem();
|
||||
}
|
||||
|
||||
protected internal override bool NeedsContainerOverride(object? item, int index, out object? recycleKey)
|
||||
{
|
||||
return NeedsContainer<ComboBoxItem>(item, out recycleKey);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnKeyDown(KeyEventArgs e)
|
||||
@@ -443,7 +450,22 @@ namespace Avalonia.Controls
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectionBoxItem = item;
|
||||
if(ItemTemplate is null && DisplayMemberBinding is { } binding)
|
||||
{
|
||||
var template = new FuncDataTemplate<object?>((_, _) =>
|
||||
new TextBlock
|
||||
{
|
||||
[TextBlock.DataContextProperty] = item,
|
||||
[!TextBlock.TextProperty] = binding,
|
||||
});
|
||||
var text = template.Build(item);
|
||||
SelectionBoxItem = text;
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectionBoxItem = item;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ namespace Avalonia.Controls
|
||||
/// <summary>
|
||||
/// Defines the <see cref="PlacementMode"/> property.
|
||||
/// </summary>
|
||||
[Obsolete("Use the Placement property instead.")]
|
||||
[Obsolete("Use the Placement property instead."), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public static readonly StyledProperty<PlacementMode> PlacementModeProperty = PlacementProperty;
|
||||
|
||||
/// <summary>
|
||||
@@ -157,7 +157,7 @@ namespace Avalonia.Controls
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Placement"/>
|
||||
[Obsolete("Use the Placement property instead.")]
|
||||
[Obsolete("Use the Placement property instead."), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public PlacementMode PlacementMode
|
||||
{
|
||||
get => GetValue(PlacementProperty);
|
||||
|
||||
@@ -403,7 +403,9 @@ namespace Avalonia.Controls
|
||||
{
|
||||
if (_focusAdorner == null)
|
||||
{
|
||||
var template = GetValue(FocusAdornerProperty) ?? adornerLayer.DefaultFocusAdorner;
|
||||
var template = IsSet(FocusAdornerProperty)
|
||||
? GetValue(FocusAdornerProperty)
|
||||
: adornerLayer.DefaultFocusAdorner;
|
||||
|
||||
if (template != null)
|
||||
{
|
||||
|
||||
@@ -24,9 +24,8 @@ namespace Avalonia.Controls
|
||||
/// <summary>
|
||||
/// Defines the <see cref="ItemTemplate"/> property
|
||||
/// </summary>
|
||||
public static readonly DirectProperty<MenuFlyout, IDataTemplate?> ItemTemplateProperty =
|
||||
AvaloniaProperty.RegisterDirect<MenuFlyout, IDataTemplate?>(nameof(ItemTemplate),
|
||||
x => x.ItemTemplate, (x, v) => x.ItemTemplate = v);
|
||||
public static readonly StyledProperty<IDataTemplate?> ItemTemplateProperty =
|
||||
AvaloniaProperty.Register<MenuFlyout, IDataTemplate?>(nameof(ItemTemplate));
|
||||
|
||||
/// <summary>
|
||||
/// Defines the <see cref="ItemContainerTheme"/> property.
|
||||
@@ -59,8 +58,8 @@ namespace Avalonia.Controls
|
||||
/// </summary>
|
||||
public IDataTemplate? ItemTemplate
|
||||
{
|
||||
get => _itemTemplate;
|
||||
set => SetAndRaise(ItemTemplateProperty, ref _itemTemplate, value);
|
||||
get => GetValue(ItemTemplateProperty);
|
||||
set => SetValue(ItemTemplateProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -82,7 +81,6 @@ namespace Avalonia.Controls
|
||||
}
|
||||
|
||||
private Classes? _classes;
|
||||
private IDataTemplate? _itemTemplate;
|
||||
|
||||
protected override Control CreatePresenter()
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Avalonia.Controls.Generators
|
||||
{
|
||||
@@ -9,34 +10,44 @@ namespace Avalonia.Controls.Generators
|
||||
/// When creating a container for an item from a <see cref="VirtualizingPanel"/>, the following
|
||||
/// process should be followed:
|
||||
///
|
||||
/// - <see cref="IsItemItsOwnContainer(Control)"/> should first be called if the item is
|
||||
/// derived from the <see cref="Control"/> class. If this method returns true then the
|
||||
/// item itself should be used as the container.
|
||||
/// - If <see cref="IsItemItsOwnContainer(Control)"/> returns false then
|
||||
/// <see cref="CreateContainer"/> should be called to create a new container.
|
||||
/// - <see cref="NeedsContainer(object, int, out object?)"/> should first be called to
|
||||
/// determine whether the item needs a container. This method will return true if the item
|
||||
/// should be wrapped in a container control, or false if the item itself can be used as a
|
||||
/// container.
|
||||
/// - If <see cref="NeedsContainer(object, int, out object?)"/> returns true then the
|
||||
/// <see cref="CreateContainer"/> method should be called to create a new container, passing
|
||||
/// the recycle key returned from <see cref="NeedsContainer(object, int, out object?)"/>.
|
||||
/// - If the panel supports recycling and the recycle key is non-null then the recycle key
|
||||
/// should be recorded for the container (e.g. in an attached property or the realized
|
||||
/// container list).
|
||||
/// - <see cref="PrepareItemContainer(Control, object?, int)"/> method should be called for the
|
||||
/// container.
|
||||
/// - The container should then be added to the panel using
|
||||
/// <see cref="VirtualizingPanel.AddInternalChild(Control)"/>
|
||||
/// - Finally, <see cref="ItemContainerPrepared(Control, object?, int)"/> should be called.
|
||||
///
|
||||
/// NOTE: If <see cref="IsItemItsOwnContainer(Control)"/> in the first step above returns true
|
||||
/// then the above steps should be carried out a single time; the first time the item is
|
||||
/// displayed. Otherwise the steps should be carried out each time a new container is realized
|
||||
/// for an item.
|
||||
/// NOTE: If <see cref="NeedsContainer(object, int, out object?)"/> in the first step above
|
||||
/// returns false then the above steps should be carried out a single time: the first time the
|
||||
/// item is displayed. Otherwise the steps should be carried out each time a new container is
|
||||
/// realized for an item.
|
||||
///
|
||||
/// When unrealizing a container, the following process should be followed:
|
||||
///
|
||||
/// - If <see cref="IsItemItsOwnContainer(Control)"/> for the item returned true then the item
|
||||
/// cannot be unrealized or recycled.
|
||||
/// - If <see cref="NeedsContainer(object, int, out object?)"/> for the item returned false
|
||||
/// then the item cannot be unrealized or recycled.
|
||||
/// - Otherwise, <see cref="ClearItemContainer(Control)"/> should be called for the container
|
||||
/// - If recycling is supported then the container should be added to a recycle pool.
|
||||
/// - It is assumed that recyclable containers will not be removed from the panel but instead
|
||||
/// hidden from view using e.g. `container.IsVisible = false`.
|
||||
/// - If recycling is supported by the panel and the container then the container should be
|
||||
/// added to a recycle pool keyed on the recycle key returned from
|
||||
/// <see cref="NeedsContainer(object, int, out object?)"/>. It is assumed that recycled
|
||||
/// containers will not be removed from the panel but instead hidden from view using
|
||||
/// e.g. `container.IsVisible = false`.
|
||||
/// - If recycling is not supported then the container should be removed from the panel.
|
||||
///
|
||||
/// When recycling an unrealized container, the following process should be followed:
|
||||
///
|
||||
/// - An element should be taken from the recycle pool.
|
||||
/// - <see cref="NeedsContainer(object, int, out object?)"/> should be called to determine
|
||||
/// whether the item needs a container, and if so, the recycle key.
|
||||
/// - A container should be taken from the recycle pool keyed on the returned recycle key.
|
||||
/// - The container should be made visible.
|
||||
/// - <see cref="PrepareItemContainer(Control, object?, int)"/> method should be called for the
|
||||
/// container.
|
||||
@@ -54,28 +65,43 @@ namespace Avalonia.Controls.Generators
|
||||
internal ItemContainerGenerator(ItemsControl owner) => _owner = owner;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new container control.
|
||||
/// Determines whether the specified item needs to be wrapped in a container control.
|
||||
/// </summary>
|
||||
/// <returns>The newly created container control.</returns>
|
||||
/// <remarks>
|
||||
/// Before calling this method, <see cref="IsItemItsOwnContainer(Control)"/> should be
|
||||
/// called to determine whether the item itself should be used as a container. After
|
||||
/// calling this method, <see cref="PrepareItemContainer(Control, object, int)"/> should
|
||||
/// be called to prepare the container to display the specified item.
|
||||
/// </remarks>
|
||||
public Control CreateContainer() => _owner.CreateContainerForItemOverride();
|
||||
/// <param name="item">The item to display.</param>
|
||||
/// <param name="index">The index of the item.</param>
|
||||
/// <param name="recycleKey">
|
||||
/// When the method returns, contains a key that can be used to locate a previously
|
||||
/// recycled container of the correct type, or null if the item cannot be recycled.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// true if the item needs a container; otherwise false if the item can itself be used
|
||||
/// as a container.
|
||||
/// </returns>
|
||||
public bool NeedsContainer(object? item, int index, out object? recycleKey) =>
|
||||
_owner.NeedsContainerOverride(item, index, out recycleKey);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified item is (or is eligible to be) its own container.
|
||||
/// Creates a new container control.
|
||||
/// </summary>
|
||||
/// <param name="container">The item.</param>
|
||||
/// <returns>true if the item is its own container, otherwise false.</returns>
|
||||
/// <param name="item">The item to display.</param>
|
||||
/// <param name="index">The index of the item.</param>
|
||||
/// <param name="recycleKey">
|
||||
/// The recycle key returned from <see cref="NeedsContainer(object, int, out object?)"/>
|
||||
/// </param>
|
||||
/// <returns>The newly created container control.</returns>
|
||||
/// <remarks>
|
||||
/// Whereas in WPF/UWP, non-control items can be their own container, in Avalonia only
|
||||
/// control items may be; the caller is responsible for checking if each item is a control
|
||||
/// and calling this method before creating a new container.
|
||||
/// Before calling this method, <see cref="NeedsContainer(object, int, out object?)"/>
|
||||
/// should be called to determine whether the item itself should be used as a container.
|
||||
/// After calling this method, <see cref="PrepareItemContainer(Control, object, int)"/>
|
||||
/// must be called to prepare the container to display the specified item.
|
||||
///
|
||||
/// If the panel supports recycling then the returned recycle key should be stored alongside
|
||||
/// the container and when container becomes eligible for recycling the container should
|
||||
/// be placed in a recycle pool using this key. If the returned recycle key is null then
|
||||
/// the container cannot be recycled.
|
||||
/// </remarks>
|
||||
public bool IsItemItsOwnContainer(Control container) => _owner.IsItemItsOwnContainerOverride(container);
|
||||
public Control CreateContainer(object? item, int index, object? recycleKey)
|
||||
=> _owner.CreateContainerForItemOverride(item, index, recycleKey);
|
||||
|
||||
/// <summary>
|
||||
/// Prepares the specified element as the container for the corresponding item.
|
||||
@@ -84,10 +110,10 @@ namespace Avalonia.Controls.Generators
|
||||
/// <param name="item">The item to display.</param>
|
||||
/// <param name="index">The index of the item to display.</param>
|
||||
/// <remarks>
|
||||
/// If <see cref="IsItemItsOwnContainer(Control)"/> is true for an item, then this method
|
||||
/// must only be called a single time, otherwise this method must be called after the
|
||||
/// container is created, and each subsequent time the container is recycled to display a
|
||||
/// new item.
|
||||
/// If <see cref="NeedsContainer(object, int, out object?)"/> is false for an
|
||||
/// item, then this method must only be called a single time; otherwise this method must
|
||||
/// be called after the container is created, and each subsequent time the container is
|
||||
/// recycled to display a new item.
|
||||
/// </remarks>
|
||||
public void PrepareItemContainer(Control container, object? item, int index) =>
|
||||
_owner.PrepareItemContainer(container, item, index);
|
||||
@@ -103,8 +129,8 @@ namespace Avalonia.Controls.Generators
|
||||
/// This method must be called when a container has been fully prepared and added
|
||||
/// to the logical and visual trees, but may be called before a layout pass has completed.
|
||||
/// It must be called regardless of the result of
|
||||
/// <see cref="IsItemItsOwnContainer(Control)"/> but if that method returned true then
|
||||
/// must be called only a single time.
|
||||
/// <see cref="NeedsContainer(object, int, out object?)"/> but if that method returned
|
||||
/// false then must be called only a single time.
|
||||
/// </remarks>
|
||||
public void ItemContainerPrepared(Control container, object? item, int index) =>
|
||||
_owner.ItemContainerPrepared(container, item, index);
|
||||
@@ -127,14 +153,14 @@ namespace Avalonia.Controls.Generators
|
||||
/// This method must be called when a container is unrealized. The container must have
|
||||
/// already have been removed from the virtualizing panel's list of realized containers before
|
||||
/// this method is called. This method must not be called if
|
||||
/// <see cref="IsItemItsOwnContainer"/> returned true for the item.
|
||||
/// <see cref="NeedsContainer(object, int, out object?)"/> returned false for the item.
|
||||
/// </remarks>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Control> 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
@@ -306,6 +307,12 @@ namespace Avalonia.Controls
|
||||
set => SetValue(AreVerticalSnapPointsRegularProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a default recycle key that can be used when an <see cref="ItemsControl"/> supports
|
||||
/// a single container type.
|
||||
/// </summary>
|
||||
protected static object DefaultRecycleKey { get; } = new object();
|
||||
|
||||
/// <summary>
|
||||
/// Returns the container for the item at the specified index.
|
||||
/// </summary>
|
||||
@@ -361,7 +368,10 @@ namespace Avalonia.Controls
|
||||
/// <summary>
|
||||
/// Creates or a container that can be used to display an item.
|
||||
/// </summary>
|
||||
protected internal virtual Control CreateContainerForItemOverride() => new ContentPresenter();
|
||||
protected internal virtual Control CreateContainerForItemOverride(object? item, int index, object? recycleKey)
|
||||
{
|
||||
return new ContentPresenter();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prepares the specified element to display the specified item.
|
||||
@@ -494,11 +504,52 @@ namespace Avalonia.Controls
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified item is (or is eligible to be) its own container.
|
||||
/// Determines whether the specified item can be its own container.
|
||||
/// </summary>
|
||||
/// <param name="item">The item to check.</param>
|
||||
/// <returns>true if the item is (or is eligible to be) its own container; otherwise, false.</returns>
|
||||
protected internal virtual bool IsItemItsOwnContainerOverride(Control item) => true;
|
||||
/// <param name="index">The index of the item.</param>
|
||||
/// <param name="recycleKey">
|
||||
/// When the method returns, contains a key that can be used to locate a previously
|
||||
/// recycled container of the correct type, or null if the item cannot be recycled.
|
||||
/// If the item is its own container then by definition it cannot be recycled, so
|
||||
/// <paramref name="recycleKey"/> shoud be set to null.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// true if the item needs a container; otherwise false if the item can itself be used
|
||||
/// as a container.
|
||||
/// </returns>
|
||||
protected internal virtual bool NeedsContainerOverride(object? item, int index, out object? recycleKey)
|
||||
{
|
||||
return NeedsContainer<Control>(item, out recycleKey);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A default implementation of <see cref="NeedsContainerOverride(object, int, out object?)"/>
|
||||
/// that returns true and sets the recycle key to <see cref="DefaultRecycleKey"/> if the item
|
||||
/// is not a <typeparamref name="T"/> .
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The container type.</typeparam>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <param name="recycleKey">
|
||||
/// When the method returns, contains <see cref="DefaultRecycleKey"/> if
|
||||
/// <paramref name="item"/> is not of type <typeparamref name="T"/>; otherwise null.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// true if <paramref name="item"/> is of type <typeparamref name="T"/>; otherwise false.
|
||||
/// </returns>
|
||||
protected bool NeedsContainer<T>(object? item, out object? recycleKey) where T : Control
|
||||
{
|
||||
if (item is T)
|
||||
{
|
||||
recycleKey = null;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
recycleKey = DefaultRecycleKey;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||
@@ -626,7 +677,7 @@ namespace Avalonia.Controls
|
||||
/// TreeView to be able to create a <see cref="TreeItemContainerGenerator"/>. Can be
|
||||
/// removed in 12.0.
|
||||
/// </remarks>
|
||||
[Obsolete]
|
||||
[Obsolete, EditorBrowsable(EditorBrowsableState.Never)]
|
||||
private protected virtual ItemContainerGenerator CreateItemContainerGenerator()
|
||||
{
|
||||
return new ItemContainerGenerator(this);
|
||||
|
||||
@@ -108,8 +108,15 @@ namespace Avalonia.Controls
|
||||
/// </summary>
|
||||
public void UnselectAll() => Selection.Clear();
|
||||
|
||||
protected internal override Control CreateContainerForItemOverride() => new ListBoxItem();
|
||||
protected internal override bool IsItemItsOwnContainerOverride(Control item) => item is ListBoxItem;
|
||||
protected internal override Control CreateContainerForItemOverride(object? item, int index, object? recycleKey)
|
||||
{
|
||||
return new ListBoxItem();
|
||||
}
|
||||
|
||||
protected internal override bool NeedsContainerOverride(object? item, int index, out object? recycleKey)
|
||||
{
|
||||
return NeedsContainer<ListBoxItem>(item, out recycleKey);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnGotFocus(GotFocusEventArgs e)
|
||||
|
||||
@@ -133,8 +133,22 @@ namespace Avalonia.Controls
|
||||
/// <inheritdoc/>
|
||||
bool IMenuElement.MoveSelection(NavigationDirection direction, bool wrap) => MoveSelection(direction, wrap);
|
||||
|
||||
protected internal override Control CreateContainerForItemOverride() => new MenuItem();
|
||||
protected internal override bool IsItemItsOwnContainerOverride(Control item) => item is MenuItem or Separator;
|
||||
protected internal override Control CreateContainerForItemOverride(object? item, int index, object? recycleKey)
|
||||
{
|
||||
return new MenuItem();
|
||||
}
|
||||
|
||||
protected internal override bool NeedsContainerOverride(object? item, int index, out object? recycleKey)
|
||||
{
|
||||
if (item is MenuItem or Separator)
|
||||
{
|
||||
recycleKey = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
recycleKey = DefaultRecycleKey;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnKeyDown(KeyEventArgs e)
|
||||
|
||||
@@ -339,8 +339,22 @@ namespace Avalonia.Controls
|
||||
/// <inheritdoc/>
|
||||
void IMenuItem.RaiseClick() => RaiseEvent(new RoutedEventArgs(ClickEvent));
|
||||
|
||||
protected internal override Control CreateContainerForItemOverride() => new MenuItem();
|
||||
protected internal override bool IsItemItsOwnContainerOverride(Control item) => item is MenuItem or Separator;
|
||||
protected internal override Control CreateContainerForItemOverride(object? item, int index, object? recycleKey)
|
||||
{
|
||||
return new MenuItem();
|
||||
}
|
||||
|
||||
protected internal override bool NeedsContainerOverride(object? item, int index, out object? recycleKey)
|
||||
{
|
||||
if (item is MenuItem or Separator)
|
||||
{
|
||||
recycleKey = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
recycleKey = DefaultRecycleKey;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void OnPointerReleased(PointerReleasedEventArgs e)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia.Metadata;
|
||||
|
||||
@@ -7,7 +8,7 @@ namespace Avalonia.Controls.Platform
|
||||
/// <summary>
|
||||
/// Defines a platform-specific system dialog implementation.
|
||||
/// </summary>
|
||||
[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
|
||||
{
|
||||
|
||||
@@ -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
|
||||
/// <summary>
|
||||
/// Defines a platform-specific system dialog implementation.
|
||||
/// </summary>
|
||||
[Obsolete]
|
||||
[Obsolete, EditorBrowsable(EditorBrowsableState.Never)]
|
||||
internal class SystemDialogImpl : ISystemDialogImpl
|
||||
{
|
||||
public async Task<string[]?> ShowFileDialogAsync(FileDialog dialog, Window parent)
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Avalonia.Platform
|
||||
{
|
||||
@@ -17,7 +18,7 @@ namespace Avalonia.Platform
|
||||
public double Scaling { get; }
|
||||
|
||||
/// <inheritdoc cref="Scaling"/>
|
||||
[Obsolete("Use the Scaling property instead.")]
|
||||
[Obsolete("Use the Scaling property instead."), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public double PixelDensity => Scaling;
|
||||
|
||||
/// <summary>
|
||||
@@ -43,7 +44,7 @@ namespace Avalonia.Platform
|
||||
public bool IsPrimary { get; }
|
||||
|
||||
/// <inheritdoc cref="IsPrimary"/>
|
||||
[Obsolete("Use the IsPrimary property instead.")]
|
||||
[Obsolete("Use the IsPrimary property instead."), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public bool Primary => IsPrimary;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -113,14 +113,14 @@ namespace Avalonia.Controls.Presenters
|
||||
var generator = itemsControl.ItemContainerGenerator;
|
||||
Control container;
|
||||
|
||||
if (item is Control c && generator.IsItemItsOwnContainer(c))
|
||||
if (generator.NeedsContainer(item, index, out var recycleKey))
|
||||
{
|
||||
container = c;
|
||||
container.SetValue(ItemIsOwnContainerProperty, true);
|
||||
container = generator.CreateContainer(item, index, recycleKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
container = generator.CreateContainer();
|
||||
container = (Control)item!;
|
||||
container.SetValue(ItemIsOwnContainerProperty, true);
|
||||
}
|
||||
|
||||
generator.PrepareItemContainer(container, item, index);
|
||||
|
||||
@@ -74,7 +74,7 @@ namespace Avalonia.Controls.Primitives
|
||||
/// <summary>
|
||||
/// Defines the <see cref="PlacementMode"/> property.
|
||||
/// </summary>
|
||||
[Obsolete("Use the Placement property instead.")]
|
||||
[Obsolete("Use the Placement property instead."), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public static readonly StyledProperty<PlacementMode> PlacementModeProperty = PlacementProperty;
|
||||
|
||||
/// <summary>
|
||||
@@ -241,7 +241,7 @@ namespace Avalonia.Controls.Primitives
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Placement"/>
|
||||
[Obsolete("Use the Placement property instead.")]
|
||||
[Obsolete("Use the Placement property instead."), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public PlacementMode PlacementMode
|
||||
{
|
||||
get => GetValue(PlacementProperty);
|
||||
|
||||
@@ -16,8 +16,15 @@ namespace Avalonia.Controls.Primitives
|
||||
ItemsPanelProperty.OverrideDefaultValue<TabStrip>(DefaultPanel);
|
||||
}
|
||||
|
||||
protected internal override Control CreateContainerForItemOverride() => new TabStripItem();
|
||||
protected internal override bool IsItemItsOwnContainerOverride(Control item) => item is TabStripItem;
|
||||
protected internal override Control CreateContainerForItemOverride(object? item, int index, object? recycleKey)
|
||||
{
|
||||
return new TabStripItem();
|
||||
}
|
||||
|
||||
protected internal override bool NeedsContainerOverride(object? item, int index, out object? recycleKey)
|
||||
{
|
||||
return NeedsContainer<TabStripItem>(item, out recycleKey);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnGotFocus(GotFocusEventArgs e)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
/// <summary>
|
||||
/// Defines the <see cref="Checked"/> event.
|
||||
/// </summary>
|
||||
[Obsolete("Use IsCheckedChangedEvent instead.")]
|
||||
[Obsolete("Use IsCheckedChangedEvent instead."), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public static readonly RoutedEvent<RoutedEventArgs> CheckedEvent =
|
||||
RoutedEvent.Register<ToggleButton, RoutedEventArgs>(
|
||||
nameof(Checked),
|
||||
@@ -37,7 +38,7 @@ namespace Avalonia.Controls.Primitives
|
||||
/// <summary>
|
||||
/// Defines the <see cref="Unchecked"/> event.
|
||||
/// </summary>
|
||||
[Obsolete("Use IsCheckedChangedEvent instead.")]
|
||||
[Obsolete("Use IsCheckedChangedEvent instead."), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public static readonly RoutedEvent<RoutedEventArgs> UncheckedEvent =
|
||||
RoutedEvent.Register<ToggleButton, RoutedEventArgs>(
|
||||
nameof(Unchecked),
|
||||
@@ -46,7 +47,7 @@ namespace Avalonia.Controls.Primitives
|
||||
/// <summary>
|
||||
/// Defines the <see cref="Unchecked"/> event.
|
||||
/// </summary>
|
||||
[Obsolete("Use IsCheckedChangedEvent instead.")]
|
||||
[Obsolete("Use IsCheckedChangedEvent instead."), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public static readonly RoutedEvent<RoutedEventArgs> IndeterminateEvent =
|
||||
RoutedEvent.Register<ToggleButton, RoutedEventArgs>(
|
||||
nameof(Indeterminate),
|
||||
@@ -72,7 +73,7 @@ namespace Avalonia.Controls.Primitives
|
||||
/// <summary>
|
||||
/// Raised when a <see cref="ToggleButton"/> is checked.
|
||||
/// </summary>
|
||||
[Obsolete("Use IsCheckedChanged instead.")]
|
||||
[Obsolete("Use IsCheckedChanged instead."), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public event EventHandler<RoutedEventArgs>? Checked
|
||||
{
|
||||
add => AddHandler(CheckedEvent, value);
|
||||
@@ -82,7 +83,7 @@ namespace Avalonia.Controls.Primitives
|
||||
/// <summary>
|
||||
/// Raised when a <see cref="ToggleButton"/> is unchecked.
|
||||
/// </summary>
|
||||
[Obsolete("Use IsCheckedChanged instead.")]
|
||||
[Obsolete("Use IsCheckedChanged instead."), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public event EventHandler<RoutedEventArgs>? Unchecked
|
||||
{
|
||||
add => AddHandler(UncheckedEvent, value);
|
||||
@@ -92,7 +93,7 @@ namespace Avalonia.Controls.Primitives
|
||||
/// <summary>
|
||||
/// Raised when a <see cref="ToggleButton"/> is neither checked nor unchecked.
|
||||
/// </summary>
|
||||
[Obsolete("Use IsCheckedChanged instead.")]
|
||||
[Obsolete("Use IsCheckedChanged instead."), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public event EventHandler<RoutedEventArgs>? Indeterminate
|
||||
{
|
||||
add => AddHandler(IndeterminateEvent, value);
|
||||
@@ -168,7 +169,7 @@ namespace Avalonia.Controls.Primitives
|
||||
/// Called when <see cref="IsChecked"/> becomes true.
|
||||
/// </summary>
|
||||
/// <param name="e">Event arguments for the routed event that is raised by the default implementation of this method.</param>
|
||||
[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 <see cref="IsChecked"/> becomes false.
|
||||
/// </summary>
|
||||
/// <param name="e">Event arguments for the routed event that is raised by the default implementation of this method.</param>
|
||||
[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 <see cref="IsChecked"/> becomes null.
|
||||
/// </summary>
|
||||
/// <param name="e">Event arguments for the routed event that is raised by the default implementation of this method.</param>
|
||||
[Obsolete("Use OnIsCheckedChanged instead.")]
|
||||
[Obsolete("Use OnIsCheckedChanged instead."), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
protected virtual void OnIndeterminate(RoutedEventArgs e)
|
||||
{
|
||||
RaiseEvent(e);
|
||||
|
||||
@@ -45,6 +45,8 @@ namespace Avalonia.Controls.Primitives
|
||||
public static readonly StyledProperty<bool> IgnoreThumbDragProperty =
|
||||
AvaloniaProperty.Register<Track, bool>(nameof(IgnoreThumbDrag));
|
||||
|
||||
private Vector _lastDrag;
|
||||
|
||||
static Track()
|
||||
{
|
||||
ThumbProperty.Changed.AddClassHandler<Track>((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)
|
||||
|
||||
@@ -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
|
||||
/// </summary>
|
||||
/// <param name="window">The window impl for which to retrieve the Screen.</param>
|
||||
/// <returns>The <see cref="Screen"/>.</returns>
|
||||
[Obsolete("Use ScreenFromWindow(WindowBase) overload.")]
|
||||
[Obsolete("Use ScreenFromWindow(WindowBase) overload."), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public Screen? ScreenFromWindow(IWindowBaseImpl window)
|
||||
{
|
||||
return _iScreenImpl.ScreenFromWindow(window);
|
||||
|
||||
@@ -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
|
||||
/// <summary>
|
||||
/// Base class for system file dialogs.
|
||||
/// </summary>
|
||||
[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
|
||||
{
|
||||
/// <summary>
|
||||
@@ -29,7 +30,7 @@ namespace Avalonia.Controls
|
||||
/// <summary>
|
||||
/// Base class for system file and directory dialogs.
|
||||
/// </summary>
|
||||
[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
|
||||
{
|
||||
/// <summary>
|
||||
@@ -42,7 +43,7 @@ namespace Avalonia.Controls
|
||||
/// <summary>
|
||||
/// Represents a system dialog that prompts the user to select a location for saving a file.
|
||||
/// </summary>
|
||||
[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
|
||||
{
|
||||
/// <summary>
|
||||
@@ -91,7 +92,7 @@ namespace Avalonia.Controls
|
||||
/// <summary>
|
||||
/// Represents a system dialog that allows the user to select one or more files to open.
|
||||
/// </summary>
|
||||
[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
|
||||
{
|
||||
/// <summary>
|
||||
@@ -132,7 +133,7 @@ namespace Avalonia.Controls
|
||||
/// <summary>
|
||||
/// Represents a system dialog that allows the user to select a directory.
|
||||
/// </summary>
|
||||
[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
|
||||
{
|
||||
/// <summary>
|
||||
@@ -167,7 +168,7 @@ namespace Avalonia.Controls
|
||||
/// <summary>
|
||||
/// Base class for system dialogs.
|
||||
/// </summary>
|
||||
[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
|
||||
/// <summary>
|
||||
/// Represents a filter in an <see cref="OpenFileDialog"/> or an <see cref="SaveFileDialog"/>.
|
||||
/// </summary>
|
||||
[Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API")]
|
||||
[Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API"), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public class FileDialogFilter
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -148,8 +148,15 @@ namespace Avalonia.Controls
|
||||
return RegisterContentPresenter(presenter);
|
||||
}
|
||||
|
||||
protected internal override Control CreateContainerForItemOverride() => new TabItem();
|
||||
protected internal override bool IsItemItsOwnContainerOverride(Control item) => item is TabItem;
|
||||
protected internal override Control CreateContainerForItemOverride(object? item, int index, object? recycleKey)
|
||||
{
|
||||
return new TabItem();
|
||||
}
|
||||
|
||||
protected internal override bool NeedsContainerOverride(object? item, int index, out object? recycleKey)
|
||||
{
|
||||
return NeedsContainer<TabItem>(item, out recycleKey);
|
||||
}
|
||||
|
||||
protected internal override void PrepareContainerForItemOverride(Control element, object? item, int index)
|
||||
{
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user