Browse Source

Merge remote-tracking branch 'upstream/master' into feature/TextRenderingOptions

pull/9584/head
Benedikt Stebner 3 years ago
parent
commit
6ea98c8e6c
  1. 6
      Avalonia.Desktop.slnf
  2. 22
      Avalonia.sln
  3. 2
      Directory.Build.targets
  4. 104
      native/Avalonia.Native/src/OSX/AvnView.mm
  5. 3
      nukebuild/Build.cs
  6. 24
      nukebuild/Helpers.cs
  7. 171
      nukebuild/RefAssemblyGenerator.cs
  8. 13
      nukebuild/_build.csproj
  9. 2
      samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj
  10. 6
      samples/ControlCatalog.NetCore/Properties/launchSettings.json
  11. 2
      samples/ControlCatalog/App.xaml
  12. 48
      samples/ControlCatalog/MainView.xaml.cs
  13. 17
      samples/IntegrationTestApp/MainWindow.axaml
  14. 2
      samples/IntegrationTestApp/MainWindow.axaml.cs
  15. 2
      samples/MobileSandbox.Desktop/MobileSandbox.Desktop.csproj
  16. 6
      src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
  17. 2
      src/Avalonia.Base/Collections/AvaloniaDictionaryExtensions.cs
  18. 2
      src/Avalonia.Base/Controls/IResourceDictionary.cs
  19. 22
      src/Avalonia.Base/Controls/IThemeVariantProvider.cs
  20. 12
      src/Avalonia.Base/Controls/ResourceDictionary.cs
  21. 46
      src/Avalonia.Base/Controls/ResourceNodeExtensions.cs
  22. 2
      src/Avalonia.Base/Input/AccessKeyHandler.cs
  23. 4
      src/Avalonia.Base/Input/PointerEventArgs.cs
  24. 9
      src/Avalonia.Base/Layout/LayoutManager.cs
  25. 2
      src/Avalonia.Base/Media/Color.cs
  26. 10
      src/Avalonia.Base/Media/PolyLineSegment.cs
  27. 18
      src/Avalonia.Base/Media/PolylineGeometry.cs
  28. 2
      src/Avalonia.Base/Media/TextFormatting/TextLayout.cs
  29. 9
      src/Avalonia.Base/Metadata/PrivateApiAttribute.cs
  30. 2
      src/Avalonia.Base/Platform/ICursorFactory.cs
  31. 4
      src/Avalonia.Base/Platform/IPlatformRenderInterface.cs
  32. 27
      src/Avalonia.Base/Platform/Storage/NoopStorageProvider.cs
  33. 14
      src/Avalonia.Base/Points.cs
  34. 1
      src/Avalonia.Base/Styling/IThemeVariantHost.cs
  35. 8
      src/Avalonia.Base/Threading/IDispatcherImpl.cs
  36. 5
      src/Avalonia.Base/Utilities/SpanHelpers.cs
  37. 1
      src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
  38. 26
      src/Avalonia.Build.Tasks/SpanCompat.cs
  39. 39
      src/Avalonia.Controls/AppBuilder.cs
  40. 12
      src/Avalonia.Controls/Automation/Peers/ThumbAutomationPeer.cs
  41. 6
      src/Avalonia.Controls/Avalonia.Controls.csproj
  42. 3
      src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.cs
  43. 4
      src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs
  44. 2
      src/Avalonia.Controls/Platform/IPlatformIconLoader.cs
  45. 36
      src/Avalonia.Controls/Platform/ITopLevelImpl.cs
  46. 2
      src/Avalonia.Controls/Platform/IWindowImpl.cs
  47. 2
      src/Avalonia.Controls/Platform/IWindowingPlatform.cs
  48. 3
      src/Avalonia.Controls/Platform/ScreenHelper.cs
  49. 10
      src/Avalonia.Controls/Primitives/Thumb.cs
  50. 2
      src/Avalonia.Controls/PullToRefresh/RefreshVisualizer.cs
  51. 9
      src/Avalonia.Controls/Shapes/Polygon.cs
  52. 9
      src/Avalonia.Controls/Shapes/Polyline.cs
  53. 4
      src/Avalonia.Controls/Templates/FuncControlTemplate.cs
  54. 18
      src/Avalonia.Controls/Templates/IControlTemplate.cs
  55. 7
      src/Avalonia.Controls/TopLevel.cs
  56. 14
      src/Avalonia.Controls/Window.cs
  57. 24
      src/Avalonia.Controls/WindowBase.cs
  58. 61
      src/Avalonia.Controls/WindowResizedEventArgs.cs
  59. 2
      src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs
  60. 12
      src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs
  61. 6
      src/Avalonia.DesignerSupport/Remote/Stubs.cs
  62. 78
      src/Avalonia.FreeDesktop/DBusPlatformSettings.cs
  63. 13
      src/Avalonia.Headless/Avalonia.Headless.csproj
  64. 4
      src/Avalonia.Native/Avalonia.Native.csproj
  65. 3
      src/Avalonia.Native/PopupImpl.cs
  66. 8
      src/Avalonia.Native/WindowImplBase.cs
  67. 12
      src/Avalonia.Themes.Fluent/Accents/AccentColors.xaml
  68. 69
      src/Avalonia.Themes.Fluent/Accents/BaseColorsPalette.xaml
  69. 158
      src/Avalonia.Themes.Fluent/Accents/BaseResources.xaml
  70. 114
      src/Avalonia.Themes.Fluent/Accents/FluentControlResources.xaml
  71. 163
      src/Avalonia.Themes.Fluent/Accents/SystemAccentColors.cs
  72. 158
      src/Avalonia.Themes.Fluent/ColorPaletteResources.Properties.cs
  73. 118
      src/Avalonia.Themes.Fluent/ColorPaletteResources.cs
  74. 65
      src/Avalonia.Themes.Fluent/ColorPaletteResourcesCollection.cs
  75. 2
      src/Avalonia.Themes.Fluent/Controls/EmbeddableControlRoot.xaml
  76. 2
      src/Avalonia.Themes.Fluent/Controls/Window.xaml
  77. 14
      src/Avalonia.Themes.Fluent/FluentTheme.xaml
  78. 6
      src/Avalonia.Themes.Fluent/FluentTheme.xaml.cs
  79. 14
      src/Avalonia.X11/X11Window.cs
  80. 4
      src/Browser/Avalonia.Browser/BrowserTopLevelImpl.cs
  81. 6
      src/Headless/Avalonia.Headless.Vnc/Avalonia.Headless.Vnc.csproj
  82. 36
      src/Headless/Avalonia.Headless.Vnc/HeadlessVncFramebufferSource.cs
  83. 5
      src/Headless/Avalonia.Headless.Vnc/HeadlessVncPlatformExtensions.cs
  84. 19
      src/Headless/Avalonia.Headless.XUnit/Avalonia.Headless.XUnit.csproj
  85. 35
      src/Headless/Avalonia.Headless.XUnit/AvaloniaTestFramework.cs
  86. 45
      src/Headless/Avalonia.Headless.XUnit/AvaloniaTestFrameworkAttribute.cs
  87. 61
      src/Headless/Avalonia.Headless.XUnit/AvaloniaTestRunner.cs
  88. 18
      src/Headless/Avalonia.Headless/Avalonia.Headless.csproj
  89. 20
      src/Headless/Avalonia.Headless/AvaloniaHeadlessPlatform.cs
  90. 46
      src/Headless/Avalonia.Headless/HeadlessPlatformRenderInterface.cs
  91. 73
      src/Headless/Avalonia.Headless/HeadlessPlatformStubs.cs
  92. 101
      src/Headless/Avalonia.Headless/HeadlessWindowExtensions.cs
  93. 128
      src/Headless/Avalonia.Headless/HeadlessWindowImpl.cs
  94. 10
      src/Headless/Avalonia.Headless/IHeadlessWindow.cs
  95. 2
      src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs
  96. 71
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlArrayConstantAstNode.cs
  97. 3
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs
  98. 138
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs
  99. 14
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlMergeResourceGroupTransformer.cs
  100. 31
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlThemeVariantProviderTransformer.cs

6
Avalonia.Desktop.slnf

@ -23,8 +23,6 @@
"src\\Avalonia.Dialogs\\Avalonia.Dialogs.csproj", "src\\Avalonia.Dialogs\\Avalonia.Dialogs.csproj",
"src\\Avalonia.Fonts.Inter\\Avalonia.Fonts.Inter.csproj", "src\\Avalonia.Fonts.Inter\\Avalonia.Fonts.Inter.csproj",
"src\\Avalonia.FreeDesktop\\Avalonia.FreeDesktop.csproj", "src\\Avalonia.FreeDesktop\\Avalonia.FreeDesktop.csproj",
"src\\Avalonia.Headless.Vnc\\Avalonia.Headless.Vnc.csproj",
"src\\Avalonia.Headless\\Avalonia.Headless.csproj",
"src\\Avalonia.MicroCom\\Avalonia.MicroCom.csproj", "src\\Avalonia.MicroCom\\Avalonia.MicroCom.csproj",
"src\\Avalonia.Native\\Avalonia.Native.csproj", "src\\Avalonia.Native\\Avalonia.Native.csproj",
"src\\Avalonia.OpenGL\\Avalonia.OpenGL.csproj", "src\\Avalonia.OpenGL\\Avalonia.OpenGL.csproj",
@ -33,6 +31,8 @@
"src\\Avalonia.Themes.Fluent\\Avalonia.Themes.Fluent.csproj", "src\\Avalonia.Themes.Fluent\\Avalonia.Themes.Fluent.csproj",
"src\\Avalonia.Themes.Simple\\Avalonia.Themes.Simple.csproj", "src\\Avalonia.Themes.Simple\\Avalonia.Themes.Simple.csproj",
"src\\Avalonia.X11\\Avalonia.X11.csproj", "src\\Avalonia.X11\\Avalonia.X11.csproj",
"src\\Headless\\Avalonia.Headless.Vnc\\Avalonia.Headless.Vnc.csproj",
"src\\Headless\\Avalonia.Headless\\Avalonia.Headless.csproj",
"src\\Linux\\Avalonia.LinuxFramebuffer\\Avalonia.LinuxFramebuffer.csproj", "src\\Linux\\Avalonia.LinuxFramebuffer\\Avalonia.LinuxFramebuffer.csproj",
"src\\Markup\\Avalonia.Markup.Xaml.Loader\\Avalonia.Markup.Xaml.Loader.csproj", "src\\Markup\\Avalonia.Markup.Xaml.Loader\\Avalonia.Markup.Xaml.Loader.csproj",
"src\\Markup\\Avalonia.Markup.Xaml\\Avalonia.Markup.Xaml.csproj", "src\\Markup\\Avalonia.Markup.Xaml\\Avalonia.Markup.Xaml.csproj",
@ -65,4 +65,4 @@
"tests\\Avalonia.UnitTests\\Avalonia.UnitTests.csproj" "tests\\Avalonia.UnitTests\\Avalonia.UnitTests.csproj"
] ]
} }
} }

22
Avalonia.sln

@ -181,9 +181,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls.DataGrid.
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Themes.Fluent", "src\Avalonia.Themes.Fluent\Avalonia.Themes.Fluent.csproj", "{C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Themes.Fluent", "src\Avalonia.Themes.Fluent\Avalonia.Themes.Fluent.csproj", "{C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Headless", "src\Avalonia.Headless\Avalonia.Headless.csproj", "{8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Headless", "src\Headless\Avalonia.Headless\Avalonia.Headless.csproj", "{8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Headless.Vnc", "src\Avalonia.Headless.Vnc\Avalonia.Headless.Vnc.csproj", "{B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Headless.Vnc", "src\Headless\Avalonia.Headless.Vnc\Avalonia.Headless.Vnc.csproj", "{B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Markup.Xaml.Loader", "src\Markup\Avalonia.Markup.Xaml.Loader\Avalonia.Markup.Xaml.Loader.csproj", "{909A8CBD-7D0E-42FD-B841-022AD8925820}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Markup.Xaml.Loader", "src\Markup\Avalonia.Markup.Xaml.Loader\Avalonia.Markup.Xaml.Loader.csproj", "{909A8CBD-7D0E-42FD-B841-022AD8925820}"
EndProject EndProject
@ -260,6 +260,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SafeAreaDemo.Desktop", "sam
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SafeAreaDemo.iOS", "samples\SafeAreaDemo.iOS\SafeAreaDemo.iOS.csproj", "{FC956F9A-4C3A-4A1A-ACDD-BB54DCB661DD}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SafeAreaDemo.iOS", "samples\SafeAreaDemo.iOS\SafeAreaDemo.iOS.csproj", "{FC956F9A-4C3A-4A1A-ACDD-BB54DCB661DD}"
EndProject 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}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Headless.UnitTests", "tests\Avalonia.Headless.UnitTests\Avalonia.Headless.UnitTests.csproj", "{3B2405E8-9E7A-46D1-8E2D-EF9ED124C9F2}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -599,6 +605,14 @@ Global
{13F1135D-BA1A-435C-9C5B-A368D1D63DE4}.Debug|Any CPU.Build.0 = Debug|Any CPU {13F1135D-BA1A-435C-9C5B-A368D1D63DE4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{13F1135D-BA1A-435C-9C5B-A368D1D63DE4}.Release|Any CPU.ActiveCfg = Release|Any CPU {13F1135D-BA1A-435C-9C5B-A368D1D63DE4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{13F1135D-BA1A-435C-9C5B-A368D1D63DE4}.Release|Any CPU.Build.0 = Release|Any CPU {13F1135D-BA1A-435C-9C5B-A368D1D63DE4}.Release|Any CPU.Build.0 = Release|Any CPU
{F47F8316-4D4B-4026-8EF3-16B2CFDA8119}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F47F8316-4D4B-4026-8EF3-16B2CFDA8119}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F47F8316-4D4B-4026-8EF3-16B2CFDA8119}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F47F8316-4D4B-4026-8EF3-16B2CFDA8119}.Release|Any CPU.Build.0 = Release|Any CPU
{3B2405E8-9E7A-46D1-8E2D-EF9ED124C9F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3B2405E8-9E7A-46D1-8E2D-EF9ED124C9F2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3B2405E8-9E7A-46D1-8E2D-EF9ED124C9F2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3B2405E8-9E7A-46D1-8E2D-EF9ED124C9F2}.Release|Any CPU.Build.0 = Release|Any CPU
{A82AD1BC-EBE6-4FC3-A13B-D52A50297533}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A82AD1BC-EBE6-4FC3-A13B-D52A50297533}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A82AD1BC-EBE6-4FC3-A13B-D52A50297533}.Debug|Any CPU.Build.0 = Debug|Any CPU {A82AD1BC-EBE6-4FC3-A13B-D52A50297533}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A82AD1BC-EBE6-4FC3-A13B-D52A50297533}.Release|Any CPU.ActiveCfg = Release|Any CPU {A82AD1BC-EBE6-4FC3-A13B-D52A50297533}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -690,6 +704,10 @@ Global
{C810060E-3809-4B74-A125-F11533AF9C1B} = {9B9E3891-2366-4253-A952-D08BCEB71098} {C810060E-3809-4B74-A125-F11533AF9C1B} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{C692FE73-43DB-49CE-87FC-F03ED61F25C9} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637} {C692FE73-43DB-49CE-87FC-F03ED61F25C9} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637}
{F4E36AA8-814E-4704-BC07-291F70F45193} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {F4E36AA8-814E-4704-BC07-291F70F45193} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC} = {FF237916-7150-496B-89ED-6CA3292896E7}
{B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E} = {FF237916-7150-496B-89ED-6CA3292896E7}
{F47F8316-4D4B-4026-8EF3-16B2CFDA8119} = {FF237916-7150-496B-89ED-6CA3292896E7}
{3B2405E8-9E7A-46D1-8E2D-EF9ED124C9F2} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{DDA28789-C21A-4654-86CE-D01E81F095C5} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637} {DDA28789-C21A-4654-86CE-D01E81F095C5} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637}
{2D7C812B-7E73-4252-8EFD-BC8A4D5CCB9F} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {2D7C812B-7E73-4252-8EFD-BC8A4D5CCB9F} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{A82AD1BC-EBE6-4FC3-A13B-D52A50297533} = {9B9E3891-2366-4253-A952-D08BCEB71098} {A82AD1BC-EBE6-4FC3-A13B-D52A50297533} = {9B9E3891-2366-4253-A952-D08BCEB71098}

2
Directory.Build.targets

@ -1,5 +1,5 @@
<Project> <Project>
<PropertyGroup Condition="$(NETCoreSdkVersion.StartsWith('7.0'))"> <PropertyGroup Condition="$([MSBuild]::VersionGreaterThanOrEquals($(NETCoreSdkVersion), '7.0'))">
<DefineConstants>$(DefineConstants);NET7SDK</DefineConstants> <DefineConstants>$(DefineConstants);NET7SDK</DefineConstants>
</PropertyGroup> </PropertyGroup>
</Project> </Project>

104
native/Avalonia.Native/src/OSX/AvnView.mm

@ -12,7 +12,6 @@
{ {
ComPtr<WindowBaseImpl> _parent; ComPtr<WindowBaseImpl> _parent;
NSTrackingArea* _area; NSTrackingArea* _area;
NSMutableAttributedString* _markedText;
bool _isLeftPressed, _isMiddlePressed, _isRightPressed, _isXButton1Pressed, _isXButton2Pressed; bool _isLeftPressed, _isMiddlePressed, _isRightPressed, _isXButton1Pressed, _isXButton2Pressed;
AvnInputModifiers _modifierState; AvnInputModifiers _modifierState;
NSEvent* _lastMouseDownEvent; NSEvent* _lastMouseDownEvent;
@ -22,8 +21,9 @@
AvnPlatformResizeReason _resizeReason; AvnPlatformResizeReason _resizeReason;
AvnAccessibilityElement* _accessibilityChild; AvnAccessibilityElement* _accessibilityChild;
NSRect _cursorRect; NSRect _cursorRect;
NSMutableString* _text; NSMutableAttributedString* _text;
NSRange _selection; NSRange _selectedRange;
NSRange _markedRange;
} }
- (void)onClosed - (void)onClosed
@ -59,6 +59,11 @@
[self registerForDraggedTypes: @[@"public.data", GetAvnCustomDataType()]]; [self registerForDraggedTypes: @[@"public.data", GetAvnCustomDataType()]];
_modifierState = AvnInputModifiersNone; _modifierState = AvnInputModifiersNone;
_text = [[NSMutableAttributedString alloc] initWithString:@""];
_markedRange = NSMakeRange(0, 0);
_selectedRange = NSMakeRange(0, 0);
return self; return self;
} }
@ -521,9 +526,13 @@
- (void)keyDown:(NSEvent *)event - (void)keyDown:(NSEvent *)event
{ {
[self keyboardEvent:event withType:KeyDown]; _lastKeyHandled = false;
_lastKeyHandled = [[self inputContext] handleEvent:event];
[super keyDown:event]; [[self inputContext] handleEvent:event];
if(!_lastKeyHandled){
[self keyboardEvent:event withType:KeyDown];
}
} }
- (void)keyUp:(NSEvent *)event - (void)keyUp:(NSEvent *)event
@ -532,6 +541,10 @@
[super keyUp:event]; [super keyUp:event];
} }
- (void) doCommandBySelector:(SEL)selector{
}
- (AvnInputModifiers)getModifiers:(NSEventModifierFlags)mod - (AvnInputModifiers)getModifiers:(NSEventModifierFlags)mod
{ {
unsigned int rv = 0; unsigned int rv = 0;
@ -561,50 +574,52 @@
- (BOOL)hasMarkedText - (BOOL)hasMarkedText
{ {
return [_markedText length] > 0; return _markedRange.length > 0;
} }
- (NSRange)markedRange - (NSRange)markedRange
{ {
if([_markedText length] > 0) return _markedRange;
return NSMakeRange(0, [_markedText length] - 1);
return NSMakeRange(NSNotFound, 0);
} }
- (NSRange)selectedRange - (NSRange)selectedRange
{ {
return _selection; return _selectedRange;
} }
- (void)setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange - (void)setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
{ {
_lastKeyHandled = true;
NSString* markedText;
if([string isKindOfClass:[NSAttributedString class]]) if([string isKindOfClass:[NSAttributedString class]])
{ {
_markedText = [[NSMutableAttributedString alloc] initWithAttributedString:string]; markedText = [string string];
} }
else else
{ {
_markedText = [[NSMutableAttributedString alloc] initWithString:string]; markedText = (NSString*) string;
} }
if(!_parent->InputMethod->IsActive()){ _markedRange = NSMakeRange(_selectedRange.location, [markedText length]);
return;
if(_parent->InputMethod->IsActive()){
_parent->InputMethod->Client->SetPreeditText((char*)[markedText UTF8String]);
} }
_parent->InputMethod->Client->SetPreeditText((char*)[_markedText.string UTF8String]);
} }
- (void)unmarkText - (void)unmarkText
{ {
[[_markedText mutableString] setString:@""]; if(_parent->InputMethod->IsActive()){
_parent->InputMethod->Client->SetPreeditText(nullptr);
}
[[self inputContext] discardMarkedText]; _markedRange = NSMakeRange(_selectedRange.location, 0);
if(!_parent->InputMethod->IsActive()){ if([self inputContext]) {
return; [[self inputContext] discardMarkedText];
} }
_parent->InputMethod->Client->SetPreeditText(nullptr);
} }
- (NSArray<NSString *> *)validAttributesForMarkedText - (NSArray<NSString *> *)validAttributesForMarkedText
@ -614,19 +629,38 @@
- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)range actualRange:(NSRangePointer)actualRange - (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)range actualRange:(NSRangePointer)actualRange
{ {
return nullptr; if(actualRange){
range = *actualRange;
}
NSAttributedString* subString = [_text attributedSubstringFromRange:range];
return subString;
} }
- (void)insertText:(id)string replacementRange:(NSRange)replacementRange - (void)insertText:(id)string replacementRange:(NSRange)replacementRange
{ {
[self unmarkText]; if(_parent == nullptr){
return;
if(_parent != nullptr) }
NSString* text;
if([string isKindOfClass:[NSAttributedString class]])
{ {
_lastKeyHandled = _parent->BaseEvents->RawTextInputEvent(0, [string UTF8String]); text = [string string];
}
else
{
text = (NSString*) string;
} }
[[self inputContext] invalidateCharacterCoordinates]; [self unmarkText];
uint32_t timestamp = static_cast<uint32_t>([NSDate timeIntervalSinceReferenceDate] * 1000);
_lastKeyHandled = _parent->BaseEvents->RawTextInputEvent(timestamp, [text UTF8String]);
} }
- (NSUInteger)characterIndexForPoint:(NSPoint)point - (NSUInteger)characterIndexForPoint:(NSPoint)point
@ -746,15 +780,11 @@
} }
- (void) setText:(NSString *)text{ - (void) setText:(NSString *)text{
[_text setString:text]; [[_text mutableString] setString:text];
[[self inputContext] discardMarkedText];
} }
- (void) setSelection:(int)start :(int)end{ - (void) setSelection:(int)start :(int)end{
_selection = NSMakeRange(start, end - start); _selectedRange = NSMakeRange(start, end - start);
[[self inputContext] invalidateCharacterCoordinates];
} }
- (void) setCursorRect:(AvnRect)rect{ - (void) setCursorRect:(AvnRect)rect{
@ -766,7 +796,9 @@
_cursorRect = windowRectOnScreen; _cursorRect = windowRectOnScreen;
[[self inputContext] invalidateCharacterCoordinates]; if([self inputContext]) {
[[self inputContext] invalidateCharacterCoordinates];
}
} }
@end @end

3
nukebuild/Build.cs

@ -212,6 +212,7 @@ partial class Build : NukeBuild
RunCoreTest("Avalonia.Markup.Xaml.UnitTests"); RunCoreTest("Avalonia.Markup.Xaml.UnitTests");
RunCoreTest("Avalonia.Skia.UnitTests"); RunCoreTest("Avalonia.Skia.UnitTests");
RunCoreTest("Avalonia.ReactiveUI.UnitTests"); RunCoreTest("Avalonia.ReactiveUI.UnitTests");
RunCoreTest("Avalonia.Headless.UnitTests");
}); });
Target RunRenderTests => _ => _ Target RunRenderTests => _ => _
@ -273,6 +274,8 @@ partial class Build : NukeBuild
if(!Numerge.NugetPackageMerger.Merge(Parameters.NugetIntermediateRoot, Parameters.NugetRoot, config, if(!Numerge.NugetPackageMerger.Merge(Parameters.NugetIntermediateRoot, Parameters.NugetRoot, config,
new NumergeNukeLogger())) new NumergeNukeLogger()))
throw new Exception("Package merge failed"); throw new Exception("Package merge failed");
RefAssemblyGenerator.GenerateRefAsmsInPackage(Parameters.NugetRoot / "Avalonia." +
Parameters.Version + ".nupkg");
}); });
Target RunTests => _ => _ Target RunTests => _ => _

24
nukebuild/Helpers.cs

@ -0,0 +1,24 @@
using System;
using System.IO;
using Nuke.Common.Utilities;
class Helpers
{
public static IDisposable UseTempDir(out string dir)
{
var path = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
Directory.CreateDirectory(path);
dir = path;
return DelegateDisposable.CreateBracket(null, () =>
{
try
{
Directory.Delete(path, true);
}
catch
{
// ignore
}
});
}
}

171
nukebuild/RefAssemblyGenerator.cs

@ -0,0 +1,171 @@
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using ILRepacking;
using Mono.Cecil;
using Mono.Cecil.Cil;
public class RefAssemblyGenerator
{
class Resolver : DefaultAssemblyResolver, IAssemblyResolver
{
private readonly string _dir;
Dictionary<string, AssemblyDefinition> _cache = new();
public Resolver(string dir)
{
_dir = dir;
}
public override AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters)
{
if (_cache.TryGetValue(name.Name, out var asm))
return asm;
var path = Path.Combine(_dir, name.Name + ".dll");
if (File.Exists(path))
return _cache[name.Name] = AssemblyDefinition.ReadAssembly(path, parameters);
return base.Resolve(name, parameters);
}
}
public static void PatchRefAssembly(string file)
{
var reader = typeof(RefAssemblyGenerator).Assembly.GetManifestResourceStream("avalonia.snk");
var snk = new byte[reader.Length];
reader.Read(snk, 0, snk.Length);
var def = AssemblyDefinition.ReadAssembly(file, new ReaderParameters
{
ReadWrite = true,
InMemory = true,
ReadSymbols = true,
SymbolReaderProvider = new DefaultSymbolReaderProvider(false),
AssemblyResolver = new Resolver(Path.GetDirectoryName(file))
});
var obsoleteAttribute = def.MainModule.ImportReference(new TypeReference("System", "ObsoleteAttribute", def.MainModule,
def.MainModule.TypeSystem.CoreLibrary));
var obsoleteCtor = def.MainModule.ImportReference(new MethodReference(".ctor",
def.MainModule.TypeSystem.Void, obsoleteAttribute)
{
Parameters = { new ParameterDefinition(def.MainModule.TypeSystem.String) }
});
foreach(var t in def.MainModule.Types)
ProcessType(t, obsoleteCtor);
def.Write(file, new WriterParameters()
{
StrongNameKeyBlob = snk,
WriteSymbols = def.MainModule.HasSymbols,
SymbolWriterProvider = new EmbeddedPortablePdbWriterProvider(),
DeterministicMvid = def.MainModule.HasSymbols
});
}
static void ProcessType(TypeDefinition type, MethodReference obsoleteCtor)
{
foreach (var nested in type.NestedTypes)
ProcessType(nested, obsoleteCtor);
if (type.IsInterface)
{
var hideMethods = type.Name.EndsWith("Impl")
|| (type.HasCustomAttributes && type.CustomAttributes.Any(a =>
a.AttributeType.FullName == "Avalonia.Metadata.PrivateApiAttribute"));
var injectMethod = hideMethods
|| type.CustomAttributes.Any(a =>
a.AttributeType.FullName == "Avalonia.Metadata.NotClientImplementableAttribute");
if (hideMethods)
{
foreach (var m in type.Methods)
{
var dflags = MethodAttributes.Public | MethodAttributes.Family | MethodAttributes.FamORAssem |
MethodAttributes.FamANDAssem | MethodAttributes.Assembly;
m.Attributes = ((m.Attributes | dflags) ^ dflags) | MethodAttributes.Assembly;
}
}
if(injectMethod)
{
type.Methods.Add(new MethodDefinition("NotClientImplementable",
MethodAttributes.Assembly
| MethodAttributes.Abstract
| MethodAttributes.NewSlot
| MethodAttributes.HideBySig, type.Module.TypeSystem.Void));
}
var forceUnstable = type.CustomAttributes.Any(a =>
a.AttributeType.FullName == "Avalonia.Metadata.UnstableAttribute");
foreach (var m in type.Methods)
MarkAsUnstable(m, obsoleteCtor, forceUnstable);
foreach (var m in type.Properties)
MarkAsUnstable(m, obsoleteCtor, forceUnstable);
foreach (var m in type.Events)
MarkAsUnstable(m, obsoleteCtor, forceUnstable);
}
}
static void MarkAsUnstable(IMemberDefinition def, MethodReference obsoleteCtor, bool force)
{
if (!force && (
def.HasCustomAttributes == false
|| def.CustomAttributes.All(a => a.AttributeType.FullName != "Avalonia.Metadata.UnstableAttribute")))
return;
if (def.CustomAttributes.Any(a => a.AttributeType.FullName == "System.ObsoleteAttribute"))
return;
def.CustomAttributes.Add(new CustomAttribute(obsoleteCtor)
{
ConstructorArguments =
{
new CustomAttributeArgument(obsoleteCtor.Module.TypeSystem.String,
"This is a part of unstable API and can be changed in minor releases. You have been warned")
}
});
}
public static void GenerateRefAsmsInPackage(string packagePath)
{
using (var archive = new ZipArchive(File.Open(packagePath, FileMode.Open, FileAccess.ReadWrite),
ZipArchiveMode.Update))
{
foreach (var entry in archive.Entries.ToList())
{
if (entry.FullName.StartsWith("ref/"))
entry.Delete();
}
foreach (var entry in archive.Entries.ToList())
{
if (entry.FullName.StartsWith("lib/") && entry.Name.EndsWith(".xml"))
{
var newEntry = archive.CreateEntry("ref/" + entry.FullName.Substring(4),
CompressionLevel.Optimal);
using (var src = entry.Open())
using (var dst = newEntry.Open())
src.CopyTo(dst);
}
}
var libs = archive.Entries.Where(e => e.FullName.StartsWith("lib/") && e.FullName.EndsWith(".dll"))
.Select((e => new { s = e.FullName.Split('/'), e = e }))
.Select(e => new { Tfm = e.s[1], Name = e.s[2], Entry = e.e })
.GroupBy(x => x.Tfm);
foreach(var tfm in libs)
using (Helpers.UseTempDir(out var temp))
{
foreach (var l in tfm)
l.Entry.ExtractToFile(Path.Combine(temp, l.Name));
foreach (var l in tfm)
PatchRefAssembly(Path.Combine(temp, l.Name));
foreach (var l in tfm)
archive.CreateEntryFromFile(Path.Combine(temp, l.Name), $"ref/{l.Tfm}/{l.Name}");
}
}
}
}

13
nukebuild/_build.csproj

@ -31,18 +31,11 @@
<!-- Common build related files --> <!-- Common build related files -->
<Compile Remove="Numerge/**/*.*" /> <Compile Remove="Numerge/**/*.*" />
<Compile Include="Numerge/Numerge/**/*.cs" /> <Compile Include="Numerge/Numerge/**/*.cs" />
</ItemGroup> <EmbeddedResource Include="$(NuGetPackageRoot)sourcelink/1.1.0/tools/pdbstr.exe"></EmbeddedResource>
<EmbeddedResource Include="../build/avalonia.snk"></EmbeddedResource>
<ItemGroup>
<EmbeddedResource Include="$(NuGetPackageRoot)sourcelink/1.1.0/tools/pdbstr.exe"></EmbeddedResource>
</ItemGroup>
<ItemGroup>
<Compile Remove="il-repack\ILRepack\Application.cs" /> <Compile Remove="il-repack\ILRepack\Application.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Folder Include="Numerge\Numerge.Console\" />
</ItemGroup>
</Project> </Project>

2
samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj

@ -26,7 +26,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" /> <ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Headless.Vnc\Avalonia.Headless.Vnc.csproj" /> <ProjectReference Include="..\..\src\Headless\Avalonia.Headless.Vnc\Avalonia.Headless.Vnc.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Dialogs\Avalonia.Dialogs.csproj" /> <ProjectReference Include="..\..\src\Avalonia.Dialogs\Avalonia.Dialogs.csproj" />
<ProjectReference Include="..\..\src\Linux\Avalonia.LinuxFramebuffer\Avalonia.LinuxFramebuffer.csproj" /> <ProjectReference Include="..\..\src\Linux\Avalonia.LinuxFramebuffer\Avalonia.LinuxFramebuffer.csproj" />
<ProjectReference Include="..\ControlCatalog\ControlCatalog.csproj" /> <ProjectReference Include="..\ControlCatalog\ControlCatalog.csproj" />

6
samples/ControlCatalog.NetCore/Properties/launchSettings.json

@ -6,6 +6,10 @@
"Dxgi": { "Dxgi": {
"commandName": "Project", "commandName": "Project",
"commandLineArgs": "--dxgi" "commandLineArgs": "--dxgi"
},
"VNC": {
"commandName": "Project",
"commandLineArgs": "--vnc"
} }
} }
} }

2
samples/ControlCatalog/App.xaml

@ -26,8 +26,6 @@
<Color x:Key="CatalogBaseHighColor">#FFFFFFFF</Color> <Color x:Key="CatalogBaseHighColor">#FFFFFFFF</Color>
</ResourceDictionary> </ResourceDictionary>
</ResourceDictionary.ThemeDictionaries> </ResourceDictionary.ThemeDictionaries>
<Color x:Key="SystemAccentColor">#FF0078D7</Color>
<Color x:Key="SystemAccentColorDark1">#FF005A9E</Color>
<!-- Styles attached dynamically depending on current theme (simple or fluent) --> <!-- Styles attached dynamically depending on current theme (simple or fluent) -->
<StyleInclude x:Key="DataGridFluent" Source="avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml" /> <StyleInclude x:Key="DataGridFluent" Source="avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml" />

48
samples/ControlCatalog/MainView.xaml.cs

@ -19,13 +19,9 @@ namespace ControlCatalog
{ {
public class MainView : UserControl public class MainView : UserControl
{ {
private readonly IPlatformSettings _platformSettings;
public MainView() public MainView()
{ {
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);
_platformSettings = AvaloniaLocator.Current.GetRequiredService<IPlatformSettings>();
PlatformSettingsOnColorValuesChanged(_platformSettings, _platformSettings.GetColorValues());
var sideBar = this.Get<TabControl>("Sidebar"); var sideBar = this.Get<TabControl>("Sidebar");
@ -141,50 +137,6 @@ namespace ControlCatalog
ViewModel.IsSystemBarVisible = insets.IsSystemBarVisible ?? true; ViewModel.IsSystemBarVisible = insets.IsSystemBarVisible ?? true;
}; };
} }
_platformSettings.ColorValuesChanged += PlatformSettingsOnColorValuesChanged;
PlatformSettingsOnColorValuesChanged(_platformSettings, _platformSettings.GetColorValues());
}
protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
{
base.OnDetachedFromLogicalTree(e);
_platformSettings.ColorValuesChanged -= PlatformSettingsOnColorValuesChanged;
}
private void PlatformSettingsOnColorValuesChanged(object? sender, PlatformColorValues e)
{
Application.Current!.Resources["SystemAccentColor"] = e.AccentColor1;
Application.Current.Resources["SystemAccentColorDark1"] = ChangeColorLuminosity(e.AccentColor1, -0.3);
Application.Current.Resources["SystemAccentColorDark2"] = ChangeColorLuminosity(e.AccentColor1, -0.5);
Application.Current.Resources["SystemAccentColorDark3"] = ChangeColorLuminosity(e.AccentColor1, -0.7);
Application.Current.Resources["SystemAccentColorLight1"] = ChangeColorLuminosity(e.AccentColor1, 0.3);
Application.Current.Resources["SystemAccentColorLight2"] = ChangeColorLuminosity(e.AccentColor1, 0.5);
Application.Current.Resources["SystemAccentColorLight3"] = ChangeColorLuminosity(e.AccentColor1, 0.7);
static Color ChangeColorLuminosity(Color color, double luminosityFactor)
{
var red = (double)color.R;
var green = (double)color.G;
var blue = (double)color.B;
if (luminosityFactor < 0)
{
luminosityFactor = 1 + luminosityFactor;
red *= luminosityFactor;
green *= luminosityFactor;
blue *= luminosityFactor;
}
else if (luminosityFactor >= 0)
{
red = (255 - red) * luminosityFactor + red;
green = (255 - green) * luminosityFactor + green;
blue = (255 - blue) * luminosityFactor + blue;
}
return new Color(color.A, (byte)red, (byte)green, (byte)blue);
}
} }
} }
} }

17
samples/IntegrationTestApp/MainWindow.axaml

@ -170,10 +170,21 @@
</StackPanel> </StackPanel>
</Grid> </Grid>
</TabItem> </TabItem>
<TabItem Header="SliderTab">
<Slider VerticalAlignment="Top" Name="Slider" Value="30"/> <TabItem Header="Slider">
<DockPanel>
<DockPanel DockPanel.Dock="Top">
<TextBox Name="HorizontalSliderValue"
DockPanel.Dock="Right"
Text="{Binding #HorizontalSlider.Value, Mode=OneWay, StringFormat=\{0:0\}}"
VerticalAlignment="Top"/>
<Slider Name="HorizontalSlider" Value="50"/>
</DockPanel>
<Button Name="ResetSliders">Reset</Button>
</DockPanel>
</TabItem> </TabItem>
<TabItem Header="ScrollBarTab">
<TabItem Header="ScrollBar">
<ScrollBar Name="MyScrollBar" Orientation="Horizontal" AllowAutoHide="False" Width="200" Height="30" Value="20"/> <ScrollBar Name="MyScrollBar" Orientation="Horizontal" AllowAutoHide="False" Width="200" Height="30" Value="20"/>
</TabItem> </TabItem>
</TabControl> </TabControl>

2
samples/IntegrationTestApp/MainWindow.axaml.cs

@ -270,6 +270,8 @@ namespace IntegrationTestApp
this.Get<ListBox>("BasicListBox").SelectedIndex = -1; this.Get<ListBox>("BasicListBox").SelectedIndex = -1;
if (source?.Name == "MenuClickedMenuItemReset") if (source?.Name == "MenuClickedMenuItemReset")
this.Get<TextBlock>("ClickedMenuItem").Text = "None"; this.Get<TextBlock>("ClickedMenuItem").Text = "None";
if (source?.Name == "ResetSliders")
this.Get<Slider>("HorizontalSlider").Value = 50;
if (source?.Name == "ShowTransparentWindow") if (source?.Name == "ShowTransparentWindow")
ShowTransparentWindow(); ShowTransparentWindow();
if (source?.Name == "ShowTransparentPopup") if (source?.Name == "ShowTransparentPopup")

2
samples/MobileSandbox.Desktop/MobileSandbox.Desktop.csproj

@ -19,7 +19,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" /> <ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Headless.Vnc\Avalonia.Headless.Vnc.csproj" /> <ProjectReference Include="..\..\src\Headless\Avalonia.Headless.Vnc\Avalonia.Headless.Vnc.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Dialogs\Avalonia.Dialogs.csproj" /> <ProjectReference Include="..\..\src\Avalonia.Dialogs\Avalonia.Dialogs.csproj" />
<ProjectReference Include="..\..\src\Linux\Avalonia.LinuxFramebuffer\Avalonia.LinuxFramebuffer.csproj" /> <ProjectReference Include="..\..\src\Linux\Avalonia.LinuxFramebuffer\Avalonia.LinuxFramebuffer.csproj" />
<ProjectReference Include="..\MobileSandbox\MobileSandbox.csproj" /> <ProjectReference Include="..\MobileSandbox\MobileSandbox.csproj" />

6
src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs

@ -91,7 +91,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
public Action<Rect> Paint { get; set; } public Action<Rect> Paint { get; set; }
public Action<Size, PlatformResizeReason> Resized { get; set; } public Action<Size, WindowResizeReason> Resized { get; set; }
public Action<double> ScalingChanged { get; set; } public Action<double> ScalingChanged { get; set; }
@ -156,12 +156,12 @@ namespace Avalonia.Android.Platform.SkiaPlatform
protected virtual void OnResized(Size size) protected virtual void OnResized(Size size)
{ {
Resized?.Invoke(size, PlatformResizeReason.Unspecified); Resized?.Invoke(size, WindowResizeReason.Unspecified);
} }
internal void Resize(Size size) internal void Resize(Size size)
{ {
Resized?.Invoke(size, PlatformResizeReason.Layout); Resized?.Invoke(size, WindowResizeReason.Layout);
} }
class ViewImpl : InvalidationAwareSurfaceView, ISurfaceHolderCallback, IInitEditorInfo class ViewImpl : InvalidationAwareSurfaceView, ISurfaceHolderCallback, IInitEditorInfo

2
src/Avalonia.Base/Collections/AvaloniaDictionaryExtensions.cs

@ -35,7 +35,7 @@ namespace Avalonia.Collections
/// Indicates if a weak subscription should be used to track changes to the collection. /// Indicates if a weak subscription should be used to track changes to the collection.
/// </param> /// </param>
/// <returns>A disposable used to terminate the subscription.</returns> /// <returns>A disposable used to terminate the subscription.</returns>
internal static IDisposable ForEachItem<TKey, TValue>( public static IDisposable ForEachItem<TKey, TValue>(
this IAvaloniaReadOnlyDictionary<TKey, TValue> collection, this IAvaloniaReadOnlyDictionary<TKey, TValue> collection,
Action<TKey, TValue> added, Action<TKey, TValue> added,
Action<TKey, TValue> removed, Action<TKey, TValue> removed,

2
src/Avalonia.Base/Controls/IResourceDictionary.cs

@ -18,6 +18,6 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Gets a collection of merged resource dictionaries that are specifically keyed and composed to address theme scenarios. /// Gets a collection of merged resource dictionaries that are specifically keyed and composed to address theme scenarios.
/// </summary> /// </summary>
IDictionary<ThemeVariant, IResourceProvider> ThemeDictionaries { get; } IDictionary<ThemeVariant, IThemeVariantProvider> ThemeDictionaries { get; }
} }
} }

22
src/Avalonia.Base/Controls/IThemeVariantProvider.cs

@ -0,0 +1,22 @@
using Avalonia.Metadata;
using Avalonia.Styling;
namespace Avalonia.Controls;
/// <summary>
/// Resource provider with theme variant awareness.
/// Can be used with <see cref="IResourceDictionary.ThemeDictionaries"/>.
/// </summary>
/// <remarks>
/// This is a helper interface for the XAML compiler to make Key property accessibly by the markup extensions.
/// Which means, it can only be used with ResourceDictionaries and markup extensions in the XAML code.
/// This API might be removed in the future minor updates.
/// </remarks>
[Unstable]
public interface IThemeVariantProvider : IResourceProvider
{
/// <summary>
/// Key property set by the compiler.
/// </summary>
ThemeVariant? Key { get; set; }
}

12
src/Avalonia.Base/Controls/ResourceDictionary.cs

@ -13,13 +13,13 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// An indexed dictionary of resources. /// An indexed dictionary of resources.
/// </summary> /// </summary>
public class ResourceDictionary : IResourceDictionary public class ResourceDictionary : IResourceDictionary, IThemeVariantProvider
{ {
private object? lastDeferredItemKey; private object? lastDeferredItemKey;
private Dictionary<object, object?>? _inner; private Dictionary<object, object?>? _inner;
private IResourceHost? _owner; private IResourceHost? _owner;
private AvaloniaList<IResourceProvider>? _mergedDictionaries; private AvaloniaList<IResourceProvider>? _mergedDictionaries;
private AvaloniaDictionary<ThemeVariant, IResourceProvider>? _themeDictionary; private AvaloniaDictionary<ThemeVariant, IThemeVariantProvider>? _themeDictionary;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ResourceDictionary"/> class. /// Initializes a new instance of the <see cref="ResourceDictionary"/> class.
@ -93,13 +93,13 @@ namespace Avalonia.Controls
} }
} }
public IDictionary<ThemeVariant, IResourceProvider> ThemeDictionaries public IDictionary<ThemeVariant, IThemeVariantProvider> ThemeDictionaries
{ {
get get
{ {
if (_themeDictionary == null) if (_themeDictionary == null)
{ {
_themeDictionary = new AvaloniaDictionary<ThemeVariant, IResourceProvider>(2); _themeDictionary = new AvaloniaDictionary<ThemeVariant, IThemeVariantProvider>(2);
_themeDictionary.ForEachItem( _themeDictionary.ForEachItem(
(_, x) => (_, x) =>
{ {
@ -120,6 +120,8 @@ namespace Avalonia.Controls
return _themeDictionary; return _themeDictionary;
} }
} }
ThemeVariant? IThemeVariantProvider.Key { get; set; }
bool IResourceNode.HasResources bool IResourceNode.HasResources
{ {
@ -192,7 +194,7 @@ namespace Avalonia.Controls
if (_themeDictionary is not null) if (_themeDictionary is not null)
{ {
IResourceProvider? themeResourceProvider; IThemeVariantProvider? themeResourceProvider;
if (theme is not null && theme != ThemeVariant.Default) if (theme is not null && theme != ThemeVariant.Default)
{ {
if (_themeDictionary.TryGetValue(theme, out themeResourceProvider) if (_themeDictionary.TryGetValue(theme, out themeResourceProvider)

46
src/Avalonia.Base/Controls/ResourceNodeExtensions.cs

@ -119,7 +119,19 @@ namespace Avalonia.Controls
resourceProvider = resourceProvider ?? throw new ArgumentNullException(nameof(resourceProvider)); resourceProvider = resourceProvider ?? throw new ArgumentNullException(nameof(resourceProvider));
key = key ?? throw new ArgumentNullException(nameof(key)); key = key ?? throw new ArgumentNullException(nameof(key));
return new FloatingResourceObservable(resourceProvider, key, converter); return new FloatingResourceObservable(resourceProvider, key, null, converter);
}
public static IObservable<object?> GetResourceObservable(
this IResourceProvider resourceProvider,
object key,
ThemeVariant? defaultThemeVariant,
Func<object?, object?>? converter = null)
{
resourceProvider = resourceProvider ?? throw new ArgumentNullException(nameof(resourceProvider));
key = key ?? throw new ArgumentNullException(nameof(key));
return new FloatingResourceObservable(resourceProvider, key, defaultThemeVariant, converter);
} }
private class ResourceObservable : LightweightObservableBase<object?> private class ResourceObservable : LightweightObservableBase<object?>
@ -128,7 +140,10 @@ namespace Avalonia.Controls
private readonly object _key; private readonly object _key;
private readonly Func<object?, object?>? _converter; private readonly Func<object?, object?>? _converter;
public ResourceObservable(IResourceHost target, object key, Func<object?, object?>? converter) public ResourceObservable(
IResourceHost target,
object key,
Func<object?, object?>? converter)
{ {
_target = target; _target = target;
_key = key; _key = key;
@ -170,11 +185,8 @@ namespace Avalonia.Controls
private object? GetValue() private object? GetValue()
{ {
if (_target is not IThemeVariantHost themeVariantHost var theme = (_target as IThemeVariantHost)?.ActualThemeVariant;
|| !_target.TryFindResource(_key, themeVariantHost.ActualThemeVariant, out var value)) var value = _target.FindResource(theme, _key) ?? AvaloniaProperty.UnsetValue;
{
value = _target.FindResource(_key) ?? AvaloniaProperty.UnsetValue;
}
return _converter?.Invoke(value) ?? value; return _converter?.Invoke(value) ?? value;
} }
@ -183,14 +195,20 @@ namespace Avalonia.Controls
private class FloatingResourceObservable : LightweightObservableBase<object?> private class FloatingResourceObservable : LightweightObservableBase<object?>
{ {
private readonly IResourceProvider _target; private readonly IResourceProvider _target;
private readonly ThemeVariant? _overrideThemeVariant;
private readonly object _key; private readonly object _key;
private readonly Func<object?, object?>? _converter; private readonly Func<object?, object?>? _converter;
private IResourceHost? _owner; private IResourceHost? _owner;
public FloatingResourceObservable(IResourceProvider target, object key, Func<object?, object?>? converter) public FloatingResourceObservable(
IResourceProvider target,
object key,
ThemeVariant? overrideThemeVariant,
Func<object?, object?>? converter)
{ {
_target = target; _target = target;
_key = key; _key = key;
_overrideThemeVariant = overrideThemeVariant;
_converter = converter; _converter = converter;
} }
@ -233,7 +251,7 @@ namespace Avalonia.Controls
{ {
_owner.ResourcesChanged -= ResourcesChanged; _owner.ResourcesChanged -= ResourcesChanged;
} }
if (_owner is IThemeVariantHost themeVariantHost) if (_overrideThemeVariant is null && _owner is IThemeVariantHost themeVariantHost)
{ {
themeVariantHost.ActualThemeVariantChanged += ActualThemeVariantChanged; themeVariantHost.ActualThemeVariantChanged += ActualThemeVariantChanged;
} }
@ -244,12 +262,11 @@ namespace Avalonia.Controls
{ {
_owner.ResourcesChanged += ResourcesChanged; _owner.ResourcesChanged += ResourcesChanged;
} }
if (_owner is IThemeVariantHost themeVariantHost2) if (_overrideThemeVariant is null && _owner is IThemeVariantHost themeVariantHost2)
{ {
themeVariantHost2.ActualThemeVariantChanged -= ActualThemeVariantChanged; themeVariantHost2.ActualThemeVariantChanged -= ActualThemeVariantChanged;
} }
PublishNext(); PublishNext();
} }
@ -265,11 +282,8 @@ namespace Avalonia.Controls
private object? GetValue() private object? GetValue()
{ {
if (!(_target.Owner is IThemeVariantHost themeVariantHost) var theme = _overrideThemeVariant ?? (_target.Owner as IThemeVariantHost)?.ActualThemeVariant;
|| !_target.Owner.TryFindResource(_key, themeVariantHost.ActualThemeVariant, out var value)) var value = _target.Owner?.FindResource(theme, _key) ?? AvaloniaProperty.UnsetValue;
{
value = _target.Owner?.FindResource(_key) ?? AvaloniaProperty.UnsetValue;
}
return _converter?.Invoke(value) ?? value; return _converter?.Invoke(value) ?? value;
} }

2
src/Avalonia.Base/Input/AccessKeyHandler.cs

@ -176,7 +176,7 @@ namespace Avalonia.Input
{ {
bool menuIsOpen = MainMenu?.IsOpen == true; bool menuIsOpen = MainMenu?.IsOpen == true;
if (e.KeyModifiers.HasAllFlags(KeyModifiers.Alt) || menuIsOpen) if (e.KeyModifiers.HasAllFlags(KeyModifiers.Alt) && !e.KeyModifiers.HasAllFlags(KeyModifiers.Control) || menuIsOpen)
{ {
// If any other key is pressed with the Alt key held down, or the main menu is open, // If any other key is pressed with the Alt key held down, or the main menu is open,
// find all controls who have registered that access key. // find all controls who have registered that access key.

4
src/Avalonia.Base/Input/PointerEventArgs.cs

@ -77,14 +77,14 @@ namespace Avalonia.Input
/// <summary> /// <summary>
/// Gets the pointer position relative to a control. /// Gets the pointer position relative to a control.
/// </summary> /// </summary>
/// <param name="relativeTo">The control.</param> /// <param name="relativeTo">The visual whose coordinate system to use. Pass null for toplevel coordinate system</param>
/// <returns>The pointer position in the control's coordinates.</returns> /// <returns>The pointer position in the control's coordinates.</returns>
public Point GetPosition(Visual? relativeTo) => GetPosition(_rootVisualPosition, relativeTo); public Point GetPosition(Visual? relativeTo) => GetPosition(_rootVisualPosition, relativeTo);
/// <summary> /// <summary>
/// Returns the PointerPoint associated with the current event /// Returns the PointerPoint associated with the current event
/// </summary> /// </summary>
/// <param name="relativeTo">The visual which coordinate system to use. Pass null for toplevel coordinate system</param> /// <param name="relativeTo">The visual whose coordinate system to use. Pass null for toplevel coordinate system</param>
/// <returns></returns> /// <returns></returns>
public PointerPoint GetCurrentPoint(Visual? relativeTo) public PointerPoint GetCurrentPoint(Visual? relativeTo)
=> new PointerPoint(Pointer, GetPosition(relativeTo), _properties); => new PointerPoint(Pointer, GetPosition(relativeTo), _properties);

9
src/Avalonia.Base/Layout/LayoutManager.cs

@ -2,6 +2,7 @@ using System;
using System.Buffers; using System.Buffers;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq;
using Avalonia.Logging; using Avalonia.Logging;
using Avalonia.Rendering; using Avalonia.Rendering;
using Avalonia.Threading; using Avalonia.Threading;
@ -64,7 +65,6 @@ namespace Avalonia.Layout
} }
_toMeasure.Enqueue(control); _toMeasure.Enqueue(control);
_toArrange.Enqueue(control);
QueueLayoutPass(); QueueLayoutPass();
} }
@ -297,6 +297,8 @@ namespace Avalonia.Layout
{ {
control.Measure(control.PreviousMeasure.Value); control.Measure(control.PreviousMeasure.Value);
} }
_toArrange.Enqueue(control);
} }
return true; return true;
@ -313,7 +315,10 @@ namespace Avalonia.Layout
return false; return false;
} }
if (control.IsMeasureValid && !control.IsArrangeValid) if (!control.IsMeasureValid)
return false;
if (!control.IsArrangeValid)
{ {
if (control is IEmbeddedLayoutRoot embeddedRoot) if (control is IEmbeddedLayoutRoot embeddedRoot)
control.Arrange(new Rect(embeddedRoot.AllocatedSize)); control.Arrange(new Rect(embeddedRoot.AllocatedSize));

2
src/Avalonia.Base/Media/Color.cs

@ -9,8 +9,8 @@ using System;
using System.Globalization; using System.Globalization;
#if !BUILDTASK #if !BUILDTASK
using Avalonia.Animation.Animators; using Avalonia.Animation.Animators;
using static Avalonia.Utilities.SpanHelpers;
#endif #endif
using static Avalonia.Utilities.SpanHelpers;
namespace Avalonia.Media namespace Avalonia.Media
{ {

10
src/Avalonia.Base/Media/PolyLineSegment.cs

@ -10,8 +10,8 @@ namespace Avalonia.Media
/// <summary> /// <summary>
/// Defines the <see cref="Points"/> property. /// Defines the <see cref="Points"/> property.
/// </summary> /// </summary>
public static readonly StyledProperty<Points> PointsProperty public static readonly StyledProperty<IList<Point>> PointsProperty
= AvaloniaProperty.Register<PolyLineSegment, Points>(nameof(Points)); = AvaloniaProperty.Register<PolyLineSegment, IList<Point>>(nameof(Points));
/// <summary> /// <summary>
/// Gets or sets the points. /// Gets or sets the points.
@ -19,7 +19,7 @@ namespace Avalonia.Media
/// <value> /// <value>
/// The points. /// The points.
/// </value> /// </value>
public Points Points public IList<Point> Points
{ {
get => GetValue(PointsProperty); get => GetValue(PointsProperty);
set => SetValue(PointsProperty, value); set => SetValue(PointsProperty, value);
@ -37,9 +37,9 @@ namespace Avalonia.Media
/// Initializes a new instance of the <see cref="PolyLineSegment"/> class. /// Initializes a new instance of the <see cref="PolyLineSegment"/> class.
/// </summary> /// </summary>
/// <param name="points">The points.</param> /// <param name="points">The points.</param>
public PolyLineSegment(IEnumerable<Point> points) : this() public PolyLineSegment(IEnumerable<Point> points)
{ {
Points.AddRange(points); Points = new Points(points);
} }
protected internal override void ApplyTo(StreamGeometryContext ctx) protected internal override void ApplyTo(StreamGeometryContext ctx)

18
src/Avalonia.Base/Media/PolylineGeometry.cs

@ -14,8 +14,8 @@ namespace Avalonia.Media
/// <summary> /// <summary>
/// Defines the <see cref="Points"/> property. /// Defines the <see cref="Points"/> property.
/// </summary> /// </summary>
public static readonly DirectProperty<PolylineGeometry, Points> PointsProperty = public static readonly DirectProperty<PolylineGeometry, IList<Point>> PointsProperty =
AvaloniaProperty.RegisterDirect<PolylineGeometry, Points>(nameof(Points), g => g.Points, (g, f) => g.Points = f); AvaloniaProperty.RegisterDirect<PolylineGeometry, IList<Point>>(nameof(Points), g => g.Points, (g, f) => g.Points = f);
/// <summary> /// <summary>
/// Defines the <see cref="IsFilled"/> property. /// Defines the <see cref="IsFilled"/> property.
@ -23,13 +23,13 @@ namespace Avalonia.Media
public static readonly StyledProperty<bool> IsFilledProperty = public static readonly StyledProperty<bool> IsFilledProperty =
AvaloniaProperty.Register<PolylineGeometry, bool>(nameof(IsFilled)); AvaloniaProperty.Register<PolylineGeometry, bool>(nameof(IsFilled));
private Points _points; private IList<Point> _points;
private IDisposable? _pointsObserver; private IDisposable? _pointsObserver;
static PolylineGeometry() static PolylineGeometry()
{ {
AffectsGeometry(IsFilledProperty); AffectsGeometry(IsFilledProperty);
PointsProperty.Changed.AddClassHandler<PolylineGeometry>((s, e) => s.OnPointsChanged(e.NewValue as Points)); PointsProperty.Changed.AddClassHandler<PolylineGeometry>((s, e) => s.OnPointsChanged(e.NewValue as IList<Point>));
} }
/// <summary> /// <summary>
@ -43,9 +43,9 @@ namespace Avalonia.Media
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="PolylineGeometry"/> class. /// Initializes a new instance of the <see cref="PolylineGeometry"/> class.
/// </summary> /// </summary>
public PolylineGeometry(IEnumerable<Point> points, bool isFilled) : this() public PolylineGeometry(IEnumerable<Point> points, bool isFilled)
{ {
Points.AddRange(points); _points = new Points(points);
IsFilled = isFilled; IsFilled = isFilled;
} }
@ -56,7 +56,7 @@ namespace Avalonia.Media
/// The points. /// The points.
/// </value> /// </value>
[Content] [Content]
public Points Points public IList<Point> Points
{ {
get => _points; get => _points;
set => SetAndRaise(PointsProperty, ref _points, value); set => SetAndRaise(PointsProperty, ref _points, value);
@ -97,10 +97,10 @@ namespace Avalonia.Media
return geometry; return geometry;
} }
private void OnPointsChanged(Points? newValue) private void OnPointsChanged(IList<Point>? newValue)
{ {
_pointsObserver?.Dispose(); _pointsObserver?.Dispose();
_pointsObserver = newValue?.ForEachItem( _pointsObserver = (newValue as IAvaloniaList<Point>)?.ForEachItem(
_ => InvalidateGeometry(), _ => InvalidateGeometry(),
_ => InvalidateGeometry(), _ => InvalidateGeometry(),
InvalidateGeometry); InvalidateGeometry);

2
src/Avalonia.Base/Media/TextFormatting/TextLayout.cs

@ -174,7 +174,7 @@ namespace Avalonia.Media.TextFormatting
foreach (var textLine in _textLines) foreach (var textLine in _textLines)
{ {
textLine.Draw(context, new Point(currentX + textLine.Start, currentY)); textLine.Draw(context, new Point(currentX, currentY));
currentY += textLine.Height; currentY += textLine.Height;
} }

9
src/Avalonia.Base/Metadata/PrivateApiAttribute.cs

@ -0,0 +1,9 @@
using System;
namespace Avalonia.Metadata;
[AttributeUsage(AttributeTargets.Interface)]
public sealed class PrivateApiAttribute : Attribute
{
}

2
src/Avalonia.Base/Platform/ICursorFactory.cs

@ -1,9 +1,11 @@
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Metadata;
#nullable enable #nullable enable
namespace Avalonia.Platform namespace Avalonia.Platform
{ {
[PrivateApi]
public interface ICursorFactory public interface ICursorFactory
{ {
ICursorImpl GetCursor(StandardCursorType cursorType); ICursorImpl GetCursor(StandardCursorType cursorType);

4
src/Avalonia.Base/Platform/IPlatformRenderInterface.cs

@ -11,7 +11,7 @@ namespace Avalonia.Platform
/// <summary> /// <summary>
/// Defines the main platform-specific interface for the rendering subsystem. /// Defines the main platform-specific interface for the rendering subsystem.
/// </summary> /// </summary>
[Unstable] [Unstable, PrivateApi]
public interface IPlatformRenderInterface public interface IPlatformRenderInterface
{ {
/// <summary> /// <summary>
@ -202,7 +202,7 @@ namespace Avalonia.Platform
bool IsSupportedBitmapPixelFormat(PixelFormat format); bool IsSupportedBitmapPixelFormat(PixelFormat format);
} }
[Unstable] [Unstable, PrivateApi]
public interface IPlatformRenderInterfaceContext : IOptionalFeatureProvider, IDisposable public interface IPlatformRenderInterfaceContext : IOptionalFeatureProvider, IDisposable
{ {
/// <summary> /// <summary>

27
src/Avalonia.Base/Platform/Storage/NoopStorageProvider.cs

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Avalonia.Platform.Storage.FileIO;
namespace Avalonia.Platform.Storage;
internal class NoopStorageProvider : BclStorageProvider
{
public override bool CanOpen => false;
public override Task<IReadOnlyList<IStorageFile>> OpenFilePickerAsync(FilePickerOpenOptions options)
{
return Task.FromResult<IReadOnlyList<IStorageFile>>(Array.Empty<IStorageFile>());
}
public override bool CanSave => false;
public override Task<IStorageFile?> SaveFilePickerAsync(FilePickerSaveOptions options)
{
return Task.FromResult<IStorageFile?>(null);
}
public override bool CanPickFolder => false;
public override Task<IReadOnlyList<IStorageFolder>> OpenFolderPickerAsync(FolderPickerOpenOptions options)
{
return Task.FromResult<IReadOnlyList<IStorageFolder>>(Array.Empty<IStorageFolder>());
}
}

14
src/Avalonia.Base/Points.cs

@ -1,6 +1,18 @@
using System.Collections.Generic;
using Avalonia.Collections; using Avalonia.Collections;
namespace Avalonia namespace Avalonia
{ {
public sealed class Points : AvaloniaList<Point> { } public sealed class Points : AvaloniaList<Point>
{
public Points()
{
}
public Points(IEnumerable<Point> points) : base(points)
{
}
}
} }

1
src/Avalonia.Base/Styling/IThemeVariantHost.cs

@ -7,7 +7,6 @@ namespace Avalonia.Styling;
/// <summary> /// <summary>
/// Interface for the host element with a theme variant. /// Interface for the host element with a theme variant.
/// </summary> /// </summary>
[Unstable]
public interface IThemeVariantHost : IResourceHost public interface IThemeVariantHost : IResourceHost
{ {
/// <summary> /// <summary>

8
src/Avalonia.Base/Threading/IDispatcherImpl.cs

@ -6,7 +6,7 @@ using Avalonia.Platform;
namespace Avalonia.Threading; namespace Avalonia.Threading;
[Unstable] [PrivateApi]
public interface IDispatcherImpl public interface IDispatcherImpl
{ {
bool CurrentThreadIsLoopThread { get; } bool CurrentThreadIsLoopThread { get; }
@ -19,7 +19,7 @@ public interface IDispatcherImpl
void UpdateTimer(long? dueTimeInMs); void UpdateTimer(long? dueTimeInMs);
} }
[Unstable] [PrivateApi]
public interface IDispatcherImplWithPendingInput : IDispatcherImpl public interface IDispatcherImplWithPendingInput : IDispatcherImpl
{ {
// Checks if dispatcher implementation can // Checks if dispatcher implementation can
@ -28,14 +28,14 @@ public interface IDispatcherImplWithPendingInput : IDispatcherImpl
bool HasPendingInput { get; } bool HasPendingInput { get; }
} }
[Unstable] [PrivateApi]
public interface IDispatcherImplWithExplicitBackgroundProcessing : IDispatcherImpl public interface IDispatcherImplWithExplicitBackgroundProcessing : IDispatcherImpl
{ {
event Action ReadyForBackgroundProcessing; event Action ReadyForBackgroundProcessing;
void RequestBackgroundProcessing(); void RequestBackgroundProcessing();
} }
[Unstable] [PrivateApi]
public interface IControlledDispatcherImpl : IDispatcherImplWithPendingInput public interface IControlledDispatcherImpl : IDispatcherImplWithPendingInput
{ {
// Runs the event loop // Runs the event loop

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

@ -4,7 +4,10 @@ using System.Runtime.CompilerServices;
namespace Avalonia.Utilities namespace Avalonia.Utilities
{ {
public static class SpanHelpers #if !BUILDTASK
public
#endif
static class SpanHelpers
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryParseUInt(this ReadOnlySpan<char> span, NumberStyles style, IFormatProvider provider, out uint value) public static bool TryParseUInt(this ReadOnlySpan<char> span, NumberStyles style, IFormatProvider provider, out uint value)

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

@ -115,6 +115,7 @@
</Compile> </Compile>
<Compile Include="..\Avalonia.Base\Metadata\NullableAttributes.cs" Link="NullableAttributes.cs" /> <Compile Include="..\Avalonia.Base\Metadata\NullableAttributes.cs" Link="NullableAttributes.cs" />
<Compile Include="..\Avalonia.Base\Compatibility\TrimmingAttributes.cs" Link="TrimmingAttributes.cs" Visible="False" /> <Compile Include="..\Avalonia.Base\Compatibility\TrimmingAttributes.cs" Link="TrimmingAttributes.cs" Visible="False" />
<Compile Include="..\Avalonia.Base\Utilities\SpanHelpers.cs" Link="Utilities\SpanHelpers.cs" />
<Compile Remove="../Markup/Avalonia.Markup.Xaml.Loader\xamlil.github\**\obj\**\*.cs" /> <Compile Remove="../Markup/Avalonia.Markup.Xaml.Loader\xamlil.github\**\obj\**\*.cs" />
<Compile Remove="../Markup/Avalonia.Markup.Xaml.Loader\xamlil.github\src\XamlX\IL\SreTypeSystem.cs" /> <Compile Remove="../Markup/Avalonia.Markup.Xaml.Loader\xamlil.github\src\XamlX\IL\SreTypeSystem.cs" />
<PackageReference Include="Mono.Cecil" Version="0.11.4" /> <PackageReference Include="Mono.Cecil" Version="0.11.4" />

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

@ -85,31 +85,7 @@ namespace System
{ {
return TrimStart().TrimEnd(); return TrimStart().TrimEnd();
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryParseUInt(NumberStyles style, IFormatProvider provider, out uint value)
{
return uint.TryParse(ToString(), style, provider, out value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryParseInt(out int value)
{
return int.TryParse(ToString(), out value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryParseDouble(NumberStyles style, IFormatProvider provider, out double value)
{
return double.TryParse(ToString(), style, provider, out value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryParseByte(NumberStyles style, IFormatProvider provider, out byte value)
{
return byte.TryParse(ToString(), style, provider, out value);
}
public override string ToString() => _length == 0 ? string.Empty : _s.Substring(_start, _length); public override string ToString() => _length == 0 ? string.Empty : _s.Substring(_start, _length);
internal int IndexOf(ReadOnlySpan<char> v, StringComparison ordinal, int start = 0) internal int IndexOf(ReadOnlySpan<char> v, StringComparison ordinal, int start = 0)

39
src/Avalonia.Controls/AppBuilder.cs

@ -118,6 +118,43 @@ namespace Avalonia
}; };
} }
/// <summary>
/// Begin configuring an <see cref="Application"/>.
/// Should only be used for testing and design purposes, as it relies on dynamic code.
/// </summary>
/// <param name="entryPointType">
/// Parameter from which <see cref="AppBuilder"/> should be created.
/// It either needs to have BuildAvaloniaApp -> AppBuilder method or inherit Application.
/// </param>
/// <returns>An <see cref="AppBuilder"/> instance. If can't be created, thrown an exception.</returns>
internal static AppBuilder Configure(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
Type entryPointType)
{
var appBuilderObj = entryPointType
.GetMethod(
"BuildAvaloniaApp",
BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy,
null,
Array.Empty<Type>(),
null)?
.Invoke(null, Array.Empty<object?>());
if (appBuilderObj is AppBuilder appBuilder)
{
return appBuilder;
}
if (typeof(Application).IsAssignableFrom(entryPointType))
{
return Configure(() => (Application)Activator.CreateInstance(entryPointType)!);
}
throw new InvalidOperationException(
$"Unable to create AppBuilder from type {entryPointType.Name}." +
$"Input type either needs to have BuildAvaloniaApp -> AppBuilder method or inherit Application type.");
}
protected AppBuilder Self => this; protected AppBuilder Self => this;
public AppBuilder AfterSetup(Action<AppBuilder> callback) public AppBuilder AfterSetup(Action<AppBuilder> callback)
@ -206,7 +243,7 @@ namespace Avalonia
_optionsInitializers += () => { AvaloniaLocator.CurrentMutable.Bind<T>().ToFunc(options); }; _optionsInitializers += () => { AvaloniaLocator.CurrentMutable.Bind<T>().ToFunc(options); };
return Self; return Self;
} }
/// <summary> /// <summary>
/// Registers an action that is executed with the current font manager. /// Registers an action that is executed with the current font manager.
/// </summary> /// </summary>

12
src/Avalonia.Controls/Automation/Peers/ThumbAutomationPeer.cs

@ -0,0 +1,12 @@
using Avalonia.Automation.Peers;
using Avalonia.Controls.Primitives;
namespace Avalonia.Controls.Automation.Peers
{
public class ThumbAutomationPeer : ControlAutomationPeer
{
public ThumbAutomationPeer(Thumb owner) : base(owner) { }
protected override AutomationControlType GetAutomationControlTypeCore() => AutomationControlType.Thumb;
protected override bool IsContentElementCore() => false;
}
}

6
src/Avalonia.Controls/Avalonia.Controls.csproj

@ -16,5 +16,11 @@
<InternalsVisibleTo Include="Avalonia.Controls.UnitTests, PublicKey=$(AvaloniaPublicKey)" /> <InternalsVisibleTo Include="Avalonia.Controls.UnitTests, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.DesignerSupport, PublicKey=$(AvaloniaPublicKey)" /> <InternalsVisibleTo Include="Avalonia.DesignerSupport, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.LeakTests, PublicKey=$(AvaloniaPublicKey)" /> <InternalsVisibleTo Include="Avalonia.LeakTests, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Headless, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Headless.XUnit, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Native, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.X11, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.DesignerSupport.Remote, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Browser, PublicKey=$(AvaloniaPublicKey)" />
</ItemGroup> </ItemGroup>
</Project> </Project>

3
src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.cs

@ -875,10 +875,11 @@ namespace Avalonia.Controls
{ {
if (_textBox != null) if (_textBox != null)
{ {
SetCurrentValue(TextProperty, String.Empty);
if (string.IsNullOrEmpty(Watermark) && !UseFloatingWatermark) if (string.IsNullOrEmpty(Watermark) && !UseFloatingWatermark)
{ {
DateTimeFormatInfo dtfi = DateTimeHelper.GetCurrentDateFormat(); DateTimeFormatInfo dtfi = DateTimeHelper.GetCurrentDateFormat();
SetCurrentValue(TextProperty, string.Empty);
_defaultText = string.Empty; _defaultText = string.Empty;
var watermarkFormat = "<{0}>"; var watermarkFormat = "<{0}>";
string watermarkText; string watermarkText;

4
src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs

@ -47,7 +47,7 @@ namespace Avalonia.Controls.Embedding.Offscreen
set set
{ {
_clientSize = value; _clientSize = value;
Resized?.Invoke(value, PlatformResizeReason.Unspecified); Resized?.Invoke(value, WindowResizeReason.Unspecified);
} }
} }
@ -65,7 +65,7 @@ namespace Avalonia.Controls.Embedding.Offscreen
public Action<RawInputEventArgs>? Input { get; set; } public Action<RawInputEventArgs>? Input { get; set; }
public Action<Rect>? Paint { get; set; } public Action<Rect>? Paint { get; set; }
public Action<Size, PlatformResizeReason>? Resized { get; set; } public Action<Size, WindowResizeReason>? Resized { get; set; }
public Action<double>? ScalingChanged { get; set; } public Action<double>? ScalingChanged { get; set; }
public Action<WindowTransparencyLevel>? TransparencyLevelChanged { get; set; } public Action<WindowTransparencyLevel>? TransparencyLevelChanged { get; set; }

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

@ -3,7 +3,7 @@ using Avalonia.Metadata;
namespace Avalonia.Platform namespace Avalonia.Platform
{ {
[Unstable] [Unstable, PrivateApi]
public interface IPlatformIconLoader public interface IPlatformIconLoader
{ {
IWindowIconImpl LoadIcon(string fileName); IWindowIconImpl LoadIcon(string fileName);

36
src/Avalonia.Controls/Platform/ITopLevelImpl.cs

@ -9,40 +9,6 @@ using Avalonia.Rendering;
namespace Avalonia.Platform namespace Avalonia.Platform
{ {
/// <summary>
/// Describes the reason for a <see cref="ITopLevelImpl.Resized"/> message.
/// </summary>
public enum PlatformResizeReason
{
/// <summary>
/// The resize reason is unknown or unspecified.
/// </summary>
Unspecified,
/// <summary>
/// The resize was due to the user resizing the window, for example by dragging the
/// window frame.
/// </summary>
User,
/// <summary>
/// The resize was initiated by the application, for example by setting one of the sizing-
/// related properties on <see cref="Window"/> such as <see cref="Layoutable.Width"/> or
/// <see cref="Layoutable.Height"/>.
/// </summary>
Application,
/// <summary>
/// The resize was initiated by the layout system.
/// </summary>
Layout,
/// <summary>
/// The resize was due to a change in DPI.
/// </summary>
DpiChange,
}
/// <summary> /// <summary>
/// Defines a platform-specific top-level window implementation. /// Defines a platform-specific top-level window implementation.
/// </summary> /// </summary>
@ -93,7 +59,7 @@ namespace Avalonia.Platform
/// <summary> /// <summary>
/// Gets or sets a method called when the toplevel is resized. /// Gets or sets a method called when the toplevel is resized.
/// </summary> /// </summary>
Action<Size, PlatformResizeReason>? Resized { get; set; } Action<Size, WindowResizeReason>? Resized { get; set; }
/// <summary> /// <summary>
/// Gets or sets a method called when the toplevel's scaling changes. /// Gets or sets a method called when the toplevel's scaling changes.

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

@ -114,7 +114,7 @@ namespace Avalonia.Platform
/// </summary> /// </summary>
/// <param name="clientSize">The new client size.</param> /// <param name="clientSize">The new client size.</param>
/// <param name="reason">The reason for the resize.</param> /// <param name="reason">The reason for the resize.</param>
void Resize(Size clientSize, PlatformResizeReason reason = PlatformResizeReason.Application); void Resize(Size clientSize, WindowResizeReason reason = WindowResizeReason.Application);
/// <summary> /// <summary>
/// Sets the client size of the top level. /// Sets the client size of the top level.

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

@ -2,7 +2,7 @@ using Avalonia.Metadata;
namespace Avalonia.Platform namespace Avalonia.Platform
{ {
[Unstable] [Unstable, PrivateApi]
public interface IWindowingPlatform public interface IWindowingPlatform
{ {
IWindowImpl CreateWindow(); IWindowImpl CreateWindow();

3
src/Avalonia.Controls/Platform/ScreenHelper.cs

@ -1,11 +1,12 @@
using System.Collections.Generic; using System.Collections.Generic;
using Avalonia.Controls;
using Avalonia.Utilities; using Avalonia.Utilities;
#nullable enable #nullable enable
namespace Avalonia.Platform namespace Avalonia.Platform
{ {
public static class ScreenHelper internal static class ScreenHelper
{ {
public static Screen? ScreenFromPoint(PixelPoint point, IReadOnlyList<Screen> screens) public static Screen? ScreenFromPoint(PixelPoint point, IReadOnlyList<Screen> screens)
{ {

10
src/Avalonia.Controls/Primitives/Thumb.cs

@ -1,4 +1,6 @@
using System; using System;
using Avalonia.Automation.Peers;
using Avalonia.Controls.Automation.Peers;
using Avalonia.Controls.Metadata; using Avalonia.Controls.Metadata;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
@ -45,6 +47,8 @@ namespace Avalonia.Controls.Primitives
remove { RemoveHandler(DragCompletedEvent, value); } remove { RemoveHandler(DragCompletedEvent, value); }
} }
protected override AutomationPeer OnCreateAutomationPeer() => new ThumbAutomationPeer(this);
protected virtual void OnDragStarted(VectorEventArgs e) protected virtual void OnDragStarted(VectorEventArgs e)
{ {
} }
@ -81,7 +85,7 @@ namespace Avalonia.Controls.Primitives
{ {
if (_lastPoint.HasValue) if (_lastPoint.HasValue)
{ {
var point = e.GetPosition(this.GetVisualParent()); var point = e.GetPosition(null);
var ev = new VectorEventArgs var ev = new VectorEventArgs
{ {
RoutedEvent = DragDeltaEvent, RoutedEvent = DragDeltaEvent,
@ -96,7 +100,7 @@ namespace Avalonia.Controls.Primitives
protected override void OnPointerPressed(PointerPressedEventArgs e) protected override void OnPointerPressed(PointerPressedEventArgs e)
{ {
e.Handled = true; e.Handled = true;
_lastPoint = e.GetPosition(this.GetVisualParent()); _lastPoint = e.GetPosition(null);
var ev = new VectorEventArgs var ev = new VectorEventArgs
{ {
@ -119,7 +123,7 @@ namespace Avalonia.Controls.Primitives
var ev = new VectorEventArgs var ev = new VectorEventArgs
{ {
RoutedEvent = DragCompletedEvent, RoutedEvent = DragCompletedEvent,
Vector = (Vector)e.GetPosition(this.GetVisualParent()), Vector = (Vector)e.GetPosition(null),
}; };
RaiseEvent(ev); RaiseEvent(ev);

2
src/Avalonia.Controls/PullToRefresh/RefreshVisualizer.cs

@ -38,7 +38,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Defines the <see cref="PullDirection"/> property. /// Defines the <see cref="PullDirection"/> property.
/// </summary> /// </summary>
public static readonly StyledProperty<PullDirection> PullDirectionProperty = internal static readonly StyledProperty<PullDirection> PullDirectionProperty =
AvaloniaProperty.Register<RefreshVisualizer, PullDirection>(nameof(PullDirection), PullDirection.TopToBottom); AvaloniaProperty.Register<RefreshVisualizer, PullDirection>(nameof(PullDirection), PullDirection.TopToBottom);
/// <summary> /// <summary>

9
src/Avalonia.Controls/Shapes/Polygon.cs

@ -13,10 +13,15 @@ namespace Avalonia.Controls.Shapes
AffectsGeometry<Polygon>(PointsProperty); AffectsGeometry<Polygon>(PointsProperty);
} }
public Polygon()
{
Points = new Points();
}
public IList<Point> Points public IList<Point> Points
{ {
get { return GetValue(PointsProperty); } get => GetValue(PointsProperty);
set { SetValue(PointsProperty, value); } set => SetValue(PointsProperty, value);
} }
protected override Geometry CreateDefiningGeometry() protected override Geometry CreateDefiningGeometry()

9
src/Avalonia.Controls/Shapes/Polyline.cs

@ -14,10 +14,15 @@ namespace Avalonia.Controls.Shapes
AffectsGeometry<Polyline>(PointsProperty); AffectsGeometry<Polyline>(PointsProperty);
} }
public Polyline()
{
Points = new Points();
}
public IList<Point> Points public IList<Point> Points
{ {
get { return GetValue(PointsProperty); } get => GetValue(PointsProperty);
set { SetValue(PointsProperty, value); } set => SetValue(PointsProperty, value);
} }
protected override Geometry CreateDefiningGeometry() protected override Geometry CreateDefiningGeometry()

4
src/Avalonia.Controls/Templates/FuncControlTemplate.cs

@ -18,10 +18,10 @@ namespace Avalonia.Controls.Templates
{ {
} }
public new ControlTemplateResult Build(TemplatedControl param) public new TemplateResult<Control> Build(TemplatedControl param)
{ {
var (control, scope) = BuildWithNameScope(param); var (control, scope) = BuildWithNameScope(param);
return new ControlTemplateResult(control, scope); return new(control, scope);
} }
} }
} }

18
src/Avalonia.Controls/Templates/IControlTemplate.cs

@ -5,23 +5,7 @@ namespace Avalonia.Controls.Templates
/// <summary> /// <summary>
/// Interface representing a template used to build a <see cref="TemplatedControl"/>. /// Interface representing a template used to build a <see cref="TemplatedControl"/>.
/// </summary> /// </summary>
public interface IControlTemplate : ITemplate<TemplatedControl, ControlTemplateResult?> public interface IControlTemplate : ITemplate<TemplatedControl, TemplateResult<Control>?>
{ {
} }
public class ControlTemplateResult : TemplateResult<Control>
{
public Control Control { get; }
public ControlTemplateResult(Control control, INameScope nameScope) : base(control, nameScope)
{
Control = control;
}
public new void Deconstruct(out Control control, out INameScope scope)
{
control = Control;
scope = NameScope;
}
}
} }

7
src/Avalonia.Controls/TopLevel.cs

@ -432,10 +432,13 @@ namespace Avalonia.Controls
IStyleHost IStyleHost.StylingParent => _globalStyles!; IStyleHost IStyleHost.StylingParent => _globalStyles!;
/// <summary>
/// File System storage service used for file pickers and bookmarks.
/// </summary>
public IStorageProvider StorageProvider => _storageProvider public IStorageProvider StorageProvider => _storageProvider
??= AvaloniaLocator.Current.GetService<IStorageProviderFactory>()?.CreateProvider(this) ??= AvaloniaLocator.Current.GetService<IStorageProviderFactory>()?.CreateProvider(this)
?? PlatformImpl?.TryGetFeature<IStorageProvider>() ?? PlatformImpl?.TryGetFeature<IStorageProvider>()
?? throw new InvalidOperationException("StorageProvider platform implementation is not available."); ?? new NoopStorageProvider();
public IInsetsManager? InsetsManager => PlatformImpl?.TryGetFeature<IInsetsManager>(); public IInsetsManager? InsetsManager => PlatformImpl?.TryGetFeature<IInsetsManager>();
@ -569,7 +572,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
/// <param name="clientSize">The new client size.</param> /// <param name="clientSize">The new client size.</param>
/// <param name="reason">The reason for the resize.</param> /// <param name="reason">The reason for the resize.</param>
protected virtual void HandleResized(Size clientSize, PlatformResizeReason reason) internal virtual void HandleResized(Size clientSize, WindowResizeReason reason)
{ {
ClientSize = clientSize; ClientSize = clientSize;
FrameSize = PlatformImpl!.FrameSize; FrameSize = PlatformImpl!.FrameSize;

14
src/Avalonia.Controls/Window.cs

@ -227,7 +227,7 @@ namespace Avalonia.Controls
impl.WindowStateChanged = HandleWindowStateChanged; impl.WindowStateChanged = HandleWindowStateChanged;
_maxPlatformClientSize = PlatformImpl?.MaxAutoSizeHint ?? default(Size); _maxPlatformClientSize = PlatformImpl?.MaxAutoSizeHint ?? default(Size);
impl.ExtendClientAreaToDecorationsChanged = ExtendClientAreaToDecorationsChanged; impl.ExtendClientAreaToDecorationsChanged = ExtendClientAreaToDecorationsChanged;
this.GetObservable(ClientSizeProperty).Skip(1).Subscribe(x => PlatformImpl?.Resize(x, PlatformResizeReason.Application)); this.GetObservable(ClientSizeProperty).Skip(1).Subscribe(x => PlatformImpl?.Resize(x, WindowResizeReason.Application));
PlatformImpl?.ShowTaskbarIcon(ShowInTaskbar); PlatformImpl?.ShowTaskbarIcon(ShowInTaskbar);
} }
@ -700,7 +700,7 @@ namespace Avalonia.Controls
if (initialSize != ClientSize) if (initialSize != ClientSize)
{ {
PlatformImpl?.Resize(initialSize, PlatformResizeReason.Layout); PlatformImpl?.Resize(initialSize, WindowResizeReason.Layout);
} }
LayoutManager.ExecuteInitialLayoutPass(); LayoutManager.ExecuteInitialLayoutPass();
@ -778,7 +778,7 @@ namespace Avalonia.Controls
if (initialSize != ClientSize) if (initialSize != ClientSize)
{ {
PlatformImpl?.Resize(initialSize, PlatformResizeReason.Layout); PlatformImpl?.Resize(initialSize, WindowResizeReason.Layout);
} }
LayoutManager.ExecuteInitialLayoutPass(); LayoutManager.ExecuteInitialLayoutPass();
@ -975,7 +975,7 @@ namespace Avalonia.Controls
protected sealed override Size ArrangeSetBounds(Size size) protected sealed override Size ArrangeSetBounds(Size size)
{ {
PlatformImpl?.Resize(size, PlatformResizeReason.Layout); PlatformImpl?.Resize(size, WindowResizeReason.Layout);
return ClientSize; return ClientSize;
} }
@ -994,7 +994,7 @@ namespace Avalonia.Controls
} }
/// <inheritdoc/> /// <inheritdoc/>
protected sealed override void HandleResized(Size clientSize, PlatformResizeReason reason) internal override void HandleResized(Size clientSize, WindowResizeReason reason)
{ {
if (ClientSize != clientSize || double.IsNaN(Width) || double.IsNaN(Height)) if (ClientSize != clientSize || double.IsNaN(Width) || double.IsNaN(Height))
{ {
@ -1005,8 +1005,8 @@ namespace Avalonia.Controls
// to the requested size. // to the requested size.
if (sizeToContent != SizeToContent.Manual && if (sizeToContent != SizeToContent.Manual &&
CanResize && CanResize &&
reason == PlatformResizeReason.Unspecified || reason == WindowResizeReason.Unspecified ||
reason == PlatformResizeReason.User) reason == WindowResizeReason.User)
{ {
if (clientSize.Width != ClientSize.Width) if (clientSize.Width != ClientSize.Width)
sizeToContent &= ~SizeToContent.Width; sizeToContent &= ~SizeToContent.Width;

24
src/Avalonia.Controls/WindowBase.cs

@ -80,6 +80,11 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
public event EventHandler<PixelPointEventArgs>? PositionChanged; public event EventHandler<PixelPointEventArgs>? PositionChanged;
/// <summary>
/// Occurs when the window is resized.
/// </summary>
public event EventHandler<WindowResizedEventArgs>? Resized;
public new IWindowBaseImpl? PlatformImpl => (IWindowBaseImpl?) base.PlatformImpl; public new IWindowBaseImpl? PlatformImpl => (IWindowBaseImpl?) base.PlatformImpl;
/// <summary> /// <summary>
@ -155,6 +160,15 @@ namespace Avalonia.Controls
} }
} }
/// <summary>
/// Trys to get the platform handle for the window.
/// </summary>
/// <returns>
/// An <see cref="IPlatformHandle"/> describing the window handle, or null if the handle
/// could not be retrieved.
/// </returns>
public IPlatformHandle? TryGetPlatformHandle() => PlatformImpl?.Handle;
/// <summary> /// <summary>
/// Ensures that the window is initialized. /// Ensures that the window is initialized.
/// </summary> /// </summary>
@ -188,6 +202,12 @@ namespace Avalonia.Controls
base.OnOpened(e); base.OnOpened(e);
} }
/// <summary>
/// Raises the <see cref="Resized"/> event.
/// </summary>
/// <param name="e">An <see cref="EventArgs"/> that contains the event data.</param>
protected virtual void OnResized(WindowResizedEventArgs e) => Resized?.Invoke(this, e);
protected override void HandleClosed() protected override void HandleClosed()
{ {
using (FreezeVisibilityChangeHandling()) using (FreezeVisibilityChangeHandling())
@ -208,7 +228,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
/// <param name="clientSize">The new client size.</param> /// <param name="clientSize">The new client size.</param>
/// <param name="reason">The reason for the resize.</param> /// <param name="reason">The reason for the resize.</param>
protected override void HandleResized(Size clientSize, PlatformResizeReason reason) internal override void HandleResized(Size clientSize, WindowResizeReason reason)
{ {
FrameSize = PlatformImpl?.FrameSize; FrameSize = PlatformImpl?.FrameSize;
@ -218,6 +238,8 @@ namespace Avalonia.Controls
LayoutManager.ExecuteLayoutPass(); LayoutManager.ExecuteLayoutPass();
Renderer.Resized(clientSize); Renderer.Resized(clientSize);
} }
OnResized(new WindowResizedEventArgs(clientSize, reason));
} }
/// <summary> /// <summary>

61
src/Avalonia.Controls/WindowResizedEventArgs.cs

@ -0,0 +1,61 @@
using System;
using Avalonia.Layout;
namespace Avalonia.Controls
{
/// <summary>
/// Describes the reason for a <see cref="WindowBase.Resized"/> event.
/// </summary>
public enum WindowResizeReason
{
/// <summary>
/// The resize reason is unknown or unspecified.
/// </summary>
Unspecified,
/// <summary>
/// The resize was due to the user resizing the window, for example by dragging the
/// window frame.
/// </summary>
User,
/// <summary>
/// The resize was initiated by the application, for example by setting one of the sizing-
/// related properties on <see cref="Window"/> such as <see cref="Layoutable.Width"/> or
/// <see cref="Layoutable.Height"/>.
/// </summary>
Application,
/// <summary>
/// The resize was initiated by the layout system.
/// </summary>
Layout,
/// <summary>
/// The resize was due to a change in DPI.
/// </summary>
DpiChange,
}
/// <summary>
/// Provides data for the <see cref="WindowBase.Resized"/> event.
/// </summary>
public class WindowResizedEventArgs : EventArgs
{
internal WindowResizedEventArgs(Size clientSize, WindowResizeReason reason)
{
ClientSize = clientSize;
Reason = reason;
}
/// <summary>
/// Gets the new client size of the window in device-independent pixels.
/// </summary>
public Size ClientSize { get; }
/// <summary>
/// Gets the reason for the resize.
/// </summary>
public WindowResizeReason Reason { get; }
}
}

2
src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs

@ -59,7 +59,7 @@ namespace Avalonia.DesignerSupport.Remote
base.OnMessage(transport, obj); base.OnMessage(transport, obj);
} }
public void Resize(Size clientSize, PlatformResizeReason reason) public void Resize(Size clientSize, WindowResizeReason reason)
{ {
_transport.Send(new RequestViewportResizeMessage _transport.Send(new RequestViewportResizeMessage
{ {

12
src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs

@ -179,17 +179,9 @@ namespace Avalonia.DesignerSupport.Remote
var entryPoint = asm.EntryPoint; var entryPoint = asm.EntryPoint;
if (entryPoint == null) if (entryPoint == null)
throw Die($"Assembly {args.AppPath} doesn't have an entry point"); throw Die($"Assembly {args.AppPath} doesn't have an entry point");
var builderMethod = entryPoint.DeclaringType.GetMethod( Log($"Obtaining AppBuilder instance from {entryPoint.DeclaringType!.FullName}");
BuilderMethodName, var appBuilder = AppBuilder.Configure(entryPoint.DeclaringType);
BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy,
null,
Array.Empty<Type>(),
null);
if (builderMethod == null)
throw Die($"{entryPoint.DeclaringType.FullName} doesn't have a method named {BuilderMethodName}");
Design.IsDesignMode = true; Design.IsDesignMode = true;
Log($"Obtaining AppBuilder instance from {builderMethod.DeclaringType.FullName}.{builderMethod.Name}");
var appBuilder = builderMethod.Invoke(null, null);
Log($"Initializing application in design mode"); Log($"Initializing application in design mode");
var initializer =(IAppInitializer)Activator.CreateInstance(typeof(AppInitializer)); var initializer =(IAppInitializer)Activator.CreateInstance(typeof(AppInitializer));
transport = initializer.ConfigureApp(transport, args, appBuilder); transport = initializer.ConfigureApp(transport, args, appBuilder);

6
src/Avalonia.DesignerSupport/Remote/Stubs.cs

@ -32,7 +32,7 @@ namespace Avalonia.DesignerSupport.Remote
public IEnumerable<object> Surfaces { get; } public IEnumerable<object> Surfaces { get; }
public Action<RawInputEventArgs> Input { get; set; } public Action<RawInputEventArgs> Input { get; set; }
public Action<Rect> Paint { get; set; } public Action<Rect> Paint { get; set; }
public Action<Size, PlatformResizeReason> Resized { get; set; } public Action<Size, WindowResizeReason> Resized { get; set; }
public Action<double> ScalingChanged { get; set; } public Action<double> ScalingChanged { get; set; }
public Func<WindowCloseReason, bool> Closing { get; set; } public Func<WindowCloseReason, bool> Closing { get; set; }
public Action Closed { get; set; } public Action Closed { get; set; }
@ -59,7 +59,7 @@ namespace Avalonia.DesignerSupport.Remote
PopupPositioner = new ManagedPopupPositioner(new ManagedPopupPositionerPopupImplHelper(parent, PopupPositioner = new ManagedPopupPositioner(new ManagedPopupPositionerPopupImplHelper(parent,
(_, size, __) => (_, size, __) =>
{ {
Resize(size, PlatformResizeReason.Unspecified); Resize(size, WindowResizeReason.Unspecified);
})); }));
} }
@ -112,7 +112,7 @@ namespace Avalonia.DesignerSupport.Remote
{ {
} }
public void Resize(Size clientSize, PlatformResizeReason reason) public void Resize(Size clientSize, WindowResizeReason reason)
{ {
} }

78
src/Avalonia.FreeDesktop/DBusPlatformSettings.cs

@ -1,6 +1,7 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia.Logging; using Avalonia.Logging;
using Avalonia.Media;
using Avalonia.Platform; using Avalonia.Platform;
using Tmds.DBus.SourceGenerator; using Tmds.DBus.SourceGenerator;
@ -9,7 +10,10 @@ namespace Avalonia.FreeDesktop
internal class DBusPlatformSettings : DefaultPlatformSettings internal class DBusPlatformSettings : DefaultPlatformSettings
{ {
private readonly OrgFreedesktopPortalSettings? _settings; private readonly OrgFreedesktopPortalSettings? _settings;
private PlatformColorValues? _lastColorValues; private PlatformColorValues? _lastColorValues;
private PlatformThemeVariant? _themeVariant;
private Color? _accentColor;
public DBusPlatformSettings() public DBusPlatformSettings()
{ {
@ -21,24 +25,33 @@ namespace Avalonia.FreeDesktop
_ = TryGetInitialValueAsync(); _ = TryGetInitialValueAsync();
} }
public override PlatformColorValues GetColorValues() public override PlatformColorValues GetColorValues() => _lastColorValues ?? base.GetColorValues();
{
return _lastColorValues ?? base.GetColorValues();
}
private async Task TryGetInitialValueAsync() private async Task TryGetInitialValueAsync()
{ {
try try
{ {
var value = await _settings!.ReadAsync("org.freedesktop.appearance", "color-scheme"); var value = await _settings!.ReadAsync("org.freedesktop.appearance", "color-scheme");
_lastColorValues = GetColorValuesFromSetting(value); _themeVariant = ReadAsColorScheme(value);
OnColorValuesChanged(_lastColorValues); }
catch (Exception ex)
{
Logger.TryGet(LogEventLevel.Error, LogArea.FreeDesktopPlatform)?.Log(this, "Unable to get org.freedesktop.appearance.color-scheme value", ex);
}
try
{
var value = await _settings!.ReadAsync("org.kde.kdeglobals.General", "AccentColor");
_accentColor = ReadAsAccentColor(value);
} }
catch (Exception ex) catch (Exception ex)
{ {
_lastColorValues = base.GetColorValues(); Logger.TryGet(LogEventLevel.Error, LogArea.FreeDesktopPlatform)?.Log(this, "Unable to get org.kde.kdeglobals.General.AccentColor value", ex);
Logger.TryGet(LogEventLevel.Error, LogArea.FreeDesktopPlatform)?.Log(this, "Unable to get setting value", ex);
} }
_lastColorValues = BuildPlatformColorValues();
if (_lastColorValues is not null)
OnColorValuesChanged(_lastColorValues);
} }
private void SettingsChangedHandler(Exception? exception, (string @namespace, string key, DBusVariantItem value) valueTuple) private void SettingsChangedHandler(Exception? exception, (string @namespace, string key, DBusVariantItem value) valueTuple)
@ -46,25 +59,48 @@ namespace Avalonia.FreeDesktop
if (exception is not null) if (exception is not null)
return; return;
if (valueTuple is ("org.freedesktop.appearance", "color-scheme", { } value)) switch (valueTuple)
{ {
/* case ("org.freedesktop.appearance", "color-scheme", { } colorScheme):
<member>0: No preference</member> _themeVariant = ReadAsColorScheme(colorScheme);
<member>1: Prefer dark appearance</member> _lastColorValues = BuildPlatformColorValues();
<member>2: Prefer light appearance</member> OnColorValuesChanged(_lastColorValues!);
*/ break;
_lastColorValues = GetColorValuesFromSetting(value); case ("org.kde.kdeglobals.General", "AccentColor", { } accentColor):
OnColorValuesChanged(_lastColorValues); _accentColor = ReadAsAccentColor(accentColor);
_lastColorValues = BuildPlatformColorValues();
OnColorValuesChanged(_lastColorValues!);
break;
} }
} }
private static PlatformColorValues GetColorValuesFromSetting(DBusVariantItem value) private PlatformColorValues? BuildPlatformColorValues()
{
if (_themeVariant is { } themeVariant && _accentColor is { } accentColor)
return new PlatformColorValues { ThemeVariant = themeVariant, AccentColor1 = accentColor };
if (_themeVariant is { } themeVariant1)
return new PlatformColorValues { ThemeVariant = themeVariant1 };
if (_accentColor is { } accentColor1)
return new PlatformColorValues { AccentColor1 = accentColor1 };
return null;
}
private static PlatformThemeVariant ReadAsColorScheme(DBusVariantItem value)
{ {
/*
<member>0: No preference</member>
<member>1: Prefer dark appearance</member>
<member>2: Prefer light appearance</member>
*/
var isDark = ((value.Value as DBusVariantItem)!.Value as DBusUInt32Item)!.Value == 1; var isDark = ((value.Value as DBusVariantItem)!.Value as DBusUInt32Item)!.Value == 1;
return new PlatformColorValues return isDark ? PlatformThemeVariant.Dark : PlatformThemeVariant.Light;
{ }
ThemeVariant = isDark ? PlatformThemeVariant.Dark : PlatformThemeVariant.Light
}; private static Color ReadAsAccentColor(DBusVariantItem value)
{
var colorStr = ((value.Value as DBusVariantItem)!.Value as DBusStringItem)!.Value;
var rgb = colorStr.Split(',');
return new Color(255, byte.Parse(rgb[0]), byte.Parse(rgb[1]), byte.Parse(rgb[2]));
} }
} }
} }

13
src/Avalonia.Headless/Avalonia.Headless.csproj

@ -1,13 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net6.0;netstandard2.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\packages\Avalonia\Avalonia.csproj" />
</ItemGroup>
<Import Project="..\..\build\ApiDiff.props" />
<Import Project="..\..\build\DevAnalyzers.props" />
<Import Project="..\..\build\TrimmingEnable.props" />
</Project>

4
src/Avalonia.Native/Avalonia.Native.csproj

@ -26,8 +26,4 @@
<Import Project="..\..\build\DevAnalyzers.props" /> <Import Project="..\..\build\DevAnalyzers.props" />
<Import Project="..\..\build\TrimmingEnable.props" /> <Import Project="..\..\build\TrimmingEnable.props" />
<ItemGroup>
<Compile Remove="..\Shared\ModuleInitializer.cs" />
</ItemGroup>
</Project> </Project>

3
src/Avalonia.Native/PopupImpl.cs

@ -1,4 +1,5 @@
using System; using System;
using Avalonia.Controls;
using Avalonia.Controls.Primitives.PopupPositioning; using Avalonia.Controls.Primitives.PopupPositioning;
using Avalonia.Native.Interop; using Avalonia.Native.Interop;
using Avalonia.Platform; using Avalonia.Platform;
@ -29,7 +30,7 @@ namespace Avalonia.Native
private void MoveResize(PixelPoint position, Size size, double scaling) private void MoveResize(PixelPoint position, Size size, double scaling)
{ {
Position = position; Position = position;
Resize(size, PlatformResizeReason.Layout); Resize(size, WindowResizeReason.Layout);
//TODO: We ignore the scaling override for now //TODO: We ignore the scaling override for now
} }

8
src/Avalonia.Native/WindowImplBase.cs

@ -95,7 +95,7 @@ namespace Avalonia.Native
var monitor = Screen.AllScreens.OrderBy(x => x.Scaling) var monitor = Screen.AllScreens.OrderBy(x => x.Scaling)
.FirstOrDefault(m => m.Bounds.Contains(Position)); .FirstOrDefault(m => m.Bounds.Contains(Position));
Resize(new Size(monitor.WorkingArea.Width * 0.75d, monitor.WorkingArea.Height * 0.7d), PlatformResizeReason.Layout); Resize(new Size(monitor.WorkingArea.Width * 0.75d, monitor.WorkingArea.Height * 0.7d), WindowResizeReason.Layout);
} }
public IAvnWindowBase Native => _native; public IAvnWindowBase Native => _native;
@ -160,7 +160,7 @@ namespace Avalonia.Native
public Action LostFocus { get; set; } public Action LostFocus { get; set; }
public Action<Rect> Paint { get; set; } public Action<Rect> Paint { get; set; }
public Action<Size, PlatformResizeReason> Resized { get; set; } public Action<Size, WindowResizeReason> Resized { get; set; }
public Action Closed { get; set; } public Action Closed { get; set; }
public IMouseDevice MouseDevice => _mouse; public IMouseDevice MouseDevice => _mouse;
public abstract IPopupImpl CreatePopup(); public abstract IPopupImpl CreatePopup();
@ -211,7 +211,7 @@ namespace Avalonia.Native
{ {
var s = new Size(size->Width, size->Height); var s = new Size(size->Width, size->Height);
_parent._savedLogicalSize = s; _parent._savedLogicalSize = s;
_parent.Resized?.Invoke(s, (PlatformResizeReason)reason); _parent.Resized?.Invoke(s, (WindowResizeReason)reason);
} }
} }
@ -360,7 +360,7 @@ namespace Avalonia.Native
} }
} }
public void Resize(Size clientSize, PlatformResizeReason reason) public void Resize(Size clientSize, WindowResizeReason reason)
{ {
_native?.Resize(clientSize.Width, clientSize.Height, (AvnPlatformResizeReason)reason); _native?.Resize(clientSize.Width, clientSize.Height, (AvnPlatformResizeReason)reason);
} }

12
src/Avalonia.Themes.Fluent/Accents/AccentColors.xaml

@ -1,12 +0,0 @@
<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- Accent Colours -->
<!-- TODO pull accents from system... algorithm to generate shades -->
<Color x:Key="SystemAccentColor">#FF0078D7</Color>
<Color x:Key="SystemAccentColorDark1">#FF005A9E</Color>
<Color x:Key="SystemAccentColorDark2">#FF004275</Color>
<Color x:Key="SystemAccentColorDark3">#FF002642</Color>
<Color x:Key="SystemAccentColorLight1">#FF429CE3</Color>
<Color x:Key="SystemAccentColorLight2">#FF76B9ED</Color>
<Color x:Key="SystemAccentColorLight3">#FFA6D8FF</Color>
</ResourceDictionary>

69
src/Avalonia.Themes.Fluent/Accents/BaseColorsPalette.xaml

@ -0,0 +1,69 @@
<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- https://docs.microsoft.com/en-us/previous-versions/windows/apps/dn518235(v=win.10)?redirectedfrom=MSDN -->
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Default">
<Color x:Key="SystemAltHighColor">#FFFFFFFF</Color>
<Color x:Key="SystemAltLowColor">#33FFFFFF</Color>
<Color x:Key="SystemAltMediumColor">#99FFFFFF</Color>
<Color x:Key="SystemAltMediumHighColor">#CCFFFFFF</Color>
<Color x:Key="SystemAltMediumLowColor">#66FFFFFF</Color>
<Color x:Key="SystemBaseHighColor">#FF000000</Color>
<Color x:Key="SystemBaseLowColor">#33000000</Color>
<Color x:Key="SystemBaseMediumColor">#99000000</Color>
<Color x:Key="SystemBaseMediumHighColor">#CC000000</Color>
<Color x:Key="SystemBaseMediumLowColor">#66000000</Color>
<Color x:Key="SystemChromeAltLowColor">#FF171717</Color>
<Color x:Key="SystemChromeBlackHighColor">#FF000000</Color>
<Color x:Key="SystemChromeBlackLowColor">#33000000</Color>
<Color x:Key="SystemChromeBlackMediumLowColor">#66000000</Color>
<Color x:Key="SystemChromeBlackMediumColor">#CC000000</Color>
<Color x:Key="SystemChromeDisabledHighColor">#FFCCCCCC</Color>
<Color x:Key="SystemChromeDisabledLowColor">#FF7A7A7A</Color>
<Color x:Key="SystemChromeHighColor">#FFCCCCCC</Color>
<Color x:Key="SystemChromeLowColor">#FFF2F2F2</Color>
<Color x:Key="SystemChromeMediumColor">#FFE6E6E6</Color>
<Color x:Key="SystemChromeMediumLowColor">#FFF2F2F2</Color>
<Color x:Key="SystemChromeWhiteColor">#FFFFFFFF</Color>
<Color x:Key="SystemChromeGrayColor">#FF767676</Color>
<Color x:Key="SystemListLowColor">#19000000</Color>
<Color x:Key="SystemListMediumColor">#33000000</Color>
<Color x:Key="SystemErrorTextColor">#C50500</Color>
<Color x:Key="SystemRegionColor">#FFFFFFFF</Color>
<Color x:Key="SystemRevealListLowColor">#17000000</Color>
<Color x:Key="SystemRevealListMediumColor">#2E000000</Color>
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
<Color x:Key="SystemAltHighColor">#FF000000</Color>
<Color x:Key="SystemAltLowColor">#33000000</Color>
<Color x:Key="SystemAltMediumColor">#99000000</Color>
<Color x:Key="SystemAltMediumHighColor">#CC000000</Color>
<Color x:Key="SystemAltMediumLowColor">#66000000</Color>
<Color x:Key="SystemBaseHighColor">#FFFFFFFF</Color>
<Color x:Key="SystemBaseLowColor">#33FFFFFF</Color>
<Color x:Key="SystemBaseMediumColor">#99FFFFFF</Color>
<Color x:Key="SystemBaseMediumHighColor">#CCFFFFFF</Color>
<Color x:Key="SystemBaseMediumLowColor">#66FFFFFF</Color>
<Color x:Key="SystemChromeAltLowColor">#FFF2F2F2</Color>
<Color x:Key="SystemChromeBlackHighColor">#FF000000</Color>
<Color x:Key="SystemChromeBlackLowColor">#33000000</Color>
<Color x:Key="SystemChromeBlackMediumLowColor">#66000000</Color>
<Color x:Key="SystemChromeBlackMediumColor">#CC000000</Color>
<Color x:Key="SystemChromeDisabledHighColor">#FF333333</Color>
<Color x:Key="SystemChromeDisabledLowColor">#FF858585</Color>
<Color x:Key="SystemChromeHighColor">#FF767676</Color>
<Color x:Key="SystemChromeLowColor">#FF171717</Color>
<Color x:Key="SystemChromeMediumColor">#FF1F1F1F</Color>
<Color x:Key="SystemChromeMediumLowColor">#FF2B2B2B</Color>
<Color x:Key="SystemChromeWhiteColor">#FFFFFFFF</Color>
<Color x:Key="SystemChromeGrayColor">#FF767676</Color>
<Color x:Key="SystemListLowColor">#19FFFFFF</Color>
<Color x:Key="SystemListMediumColor">#33FFFFFF</Color>
<Color x:Key="SystemErrorTextColor">#FFF000</Color>
<Color x:Key="SystemRegionColor">#FF000000</Color>
<Color x:Key="SystemRevealListLowColor">#18FFFFFF</Color>
<Color x:Key="SystemRevealListMediumColor">#30FFFFFF</Color>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>

158
src/Avalonia.Themes.Fluent/Accents/Base.xaml → src/Avalonia.Themes.Fluent/Accents/BaseResources.xaml

@ -2,7 +2,6 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="using:System" xmlns:sys="using:System"
xmlns:converters="using:Avalonia.Controls.Converters"> xmlns:converters="using:Avalonia.Controls.Converters">
<!-- https://docs.microsoft.com/en-us/previous-versions/windows/apps/dn518235(v=win.10)?redirectedfrom=MSDN -->
<FontFamily x:Key="ContentControlThemeFontFamily">fonts:Inter#Inter, $Default</FontFamily> <FontFamily x:Key="ContentControlThemeFontFamily">fonts:Inter#Inter, $Default</FontFamily>
<sys:Double x:Key="ControlContentThemeFontSize">14</sys:Double> <sys:Double x:Key="ControlContentThemeFontSize">14</sys:Double>
@ -28,39 +27,33 @@
<converters:CornerRadiusFilterConverter x:Key="BottomCornerRadiusFilterConverter" Filter="BottomLeft, BottomRight" /> <converters:CornerRadiusFilterConverter x:Key="BottomCornerRadiusFilterConverter" Filter="BottomLeft, BottomRight" />
<converters:CornerRadiusFilterConverter x:Key="LeftCornerRadiusFilterConverter" Filter="TopLeft, BottomLeft" /> <converters:CornerRadiusFilterConverter x:Key="LeftCornerRadiusFilterConverter" Filter="TopLeft, BottomLeft" />
<ResourceDictionary.ThemeDictionaries> <x:Double x:Key="AutoCompleteListMaxHeight">374</x:Double>
<ResourceDictionary x:Key="Default"> <Thickness x:Key="AutoCompleteListMargin">0,2,0,2</Thickness>
<!-- System Control Colors --> <Thickness x:Key="AutoCompleteListBorderThemeThickness">1</Thickness>
<Color x:Key="SystemAltHighColor">#FFFFFFFF</Color> <Thickness x:Key="AutoCompleteListPadding">-1,0,-1,0</Thickness>
<Color x:Key="SystemAltLowColor">#33FFFFFF</Color> <x:Double x:Key="TextControlThemeMinHeight">32</x:Double>
<Color x:Key="SystemAltMediumColor">#99FFFFFF</Color> <x:Double x:Key="TextControlThemeMinWidth">64</x:Double>
<Color x:Key="SystemAltMediumHighColor">#CCFFFFFF</Color> <x:Double x:Key="FlyoutThemeMaxWidth">456</x:Double>
<Color x:Key="SystemAltMediumLowColor">#66FFFFFF</Color> <Thickness x:Key="DateTimeFlyoutButtonBorderThickness">0</Thickness>
<Color x:Key="SystemBaseHighColor">#FF000000</Color> <Thickness x:Key="DateTimeFlyoutBorderThickness">1</Thickness>
<Color x:Key="SystemBaseLowColor">#33000000</Color> <Thickness x:Key="DateTimeFlyoutBorderPadding">0</Thickness>
<Color x:Key="SystemBaseMediumColor">#99000000</Color>
<Color x:Key="SystemBaseMediumHighColor">#CC000000</Color> <Thickness x:Key="FlyoutContentThemePadding">12,11,12,12</Thickness>
<Color x:Key="SystemBaseMediumLowColor">#66000000</Color> <x:Double x:Key="FlyoutThemeMinWidth">96</x:Double>
<Color x:Key="SystemChromeAltLowColor">#FF171717</Color> <x:Double x:Key="FlyoutThemeMinHeight">40</x:Double>
<Color x:Key="SystemChromeBlackHighColor">#FF000000</Color> <x:Double x:Key="FlyoutThemeMaxHeight">758</x:Double>
<Color x:Key="SystemChromeBlackLowColor">#33000000</Color>
<Color x:Key="SystemChromeBlackMediumLowColor">#66000000</Color>
<Color x:Key="SystemChromeBlackMediumColor">#CC000000</Color>
<Color x:Key="SystemChromeDisabledHighColor">#FFCCCCCC</Color>
<Color x:Key="SystemChromeDisabledLowColor">#FF7A7A7A</Color>
<Color x:Key="SystemChromeHighColor">#FFCCCCCC</Color>
<Color x:Key="SystemChromeLowColor">#FFF2F2F2</Color>
<Color x:Key="SystemChromeMediumColor">#FFE6E6E6</Color>
<Color x:Key="SystemChromeMediumLowColor">#FFF2F2F2</Color>
<Color x:Key="SystemChromeWhiteColor">#FFFFFFFF</Color>
<Color x:Key="SystemChromeGrayColor">#FF767676</Color>
<Color x:Key="SystemListLowColor">#19000000</Color>
<Color x:Key="SystemListMediumColor">#33000000</Color>
<Color x:Key="SystemErrorTextColor">#C50500</Color>
<Color x:Key="SystemRevealListLowColor">#17000000</Color> <!-- Moved from FlyoutPresenter.xaml -->
<Color x:Key="SystemRevealListMediumColor">#2E000000</Color> <Thickness x:Key="FlyoutBorderThemePadding">0</Thickness>
<!-- Moved from MenuItem.xaml -->
<Thickness x:Key="MenuFlyoutScrollerMargin">0,4,0,4</Thickness>
<!-- Moved from Menu.xaml -->
<Thickness x:Key="MenuBarItemPadding">12,0,12,0</Thickness>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Default">
<SolidColorBrush x:Key="SystemControlBackgroundAccentBrush" Color="{DynamicResource SystemAccentColor}" /> <SolidColorBrush x:Key="SystemControlBackgroundAccentBrush" Color="{DynamicResource SystemAccentColor}" />
<SolidColorBrush x:Key="SystemControlBackgroundAltHighBrush" Color="{StaticResource SystemAltHighColor}" /> <SolidColorBrush x:Key="SystemControlBackgroundAltHighBrush" Color="{StaticResource SystemAltHighColor}" />
<SolidColorBrush x:Key="SystemControlBackgroundAltMediumHighBrush" <SolidColorBrush x:Key="SystemControlBackgroundAltMediumHighBrush"
@ -211,82 +204,17 @@
Color="{StaticResource SystemChromeWhiteColor}" /> Color="{StaticResource SystemChromeWhiteColor}" />
<SolidColorBrush x:Key="SystemControlHighlightAltTransparentRevealBorderBrush" Color="Transparent" /> <SolidColorBrush x:Key="SystemControlHighlightAltTransparentRevealBorderBrush" Color="Transparent" />
<SolidColorBrush x:Key="SystemControlBackgroundTransparentRevealBorderBrush" Color="Transparent" /> <SolidColorBrush x:Key="SystemControlBackgroundTransparentRevealBorderBrush" Color="Transparent" />
<!-- TODO implement AcrylicBrush -->
<!--<AcrylicBrush x:Key="SystemControlTransientBackgroundBrush" BackgroundSource="HostBackdrop" TintColor="{StaticResource SystemChromeAltHighColor}" TintOpacity="0.8" FallbackColor="{StaticResource SystemChromeMediumLowColor}" />-->
<SolidColorBrush x:Key="SystemControlTransientBackgroundBrush" <SolidColorBrush x:Key="SystemControlTransientBackgroundBrush"
Color="{StaticResource SystemChromeMediumLowColor}" /> Color="{StaticResource SystemChromeMediumLowColor}" />
<StaticResource x:Key="SystemControlDescriptionTextForegroundBrush" <StaticResource x:Key="SystemControlDescriptionTextForegroundBrush"
ResourceKey="SystemControlPageTextBaseMediumBrush" /> ResourceKey="SystemControlPageTextBaseMediumBrush" />
<!--<AcrylicBrush x:Key="SystemControlAcrylicWindowBrush" BackgroundSource="HostBackdrop" TintColor="{ThemeResource SystemChromeAltHighColor}" TintOpacity="0.8" FallbackColor="{ThemeResource SystemChromeMediumColor}" />-->
<!--<RevealBackgroundBrush x:Key="SystemControlTransparentRevealBackgroundBrush" TargetTheme="Dark" Color="Transparent" FallbackColor="Transparent" />-->
<SolidColorBrush x:Key="SystemControlTransparentRevealBackgroundBrush" Color="Transparent" /> <SolidColorBrush x:Key="SystemControlTransparentRevealBackgroundBrush" Color="Transparent" />
<!--<RevealBorderBrush x:Key="SystemControlTransparentRevealBorderBrush" TargetTheme="Dark" Color="Transparent" FallbackColor="Transparent" />-->
<SolidColorBrush x:Key="SystemControlTransparentRevealBorderBrush" Color="Transparent" /> <SolidColorBrush x:Key="SystemControlTransparentRevealBorderBrush" Color="Transparent" />
<SolidColorBrush x:Key="SystemRegionBrush" Color="{StaticResource SystemRegionColor}" />
<!--<RevealBackgroundBrush x:Key="SystemControlHighlightListLowRevealBackgroundBrush" TargetTheme="Light" Color="{ThemeResource SystemRevealListMediumColor}" FallbackColor="{ StaticResource SystemListMediumColor}" />-->
<Color x:Key="RegionColor">#FFFFFFFF</Color>
<SolidColorBrush x:Key="RegionBrush" Color="{StaticResource RegionColor}" />
<x:Double x:Key="AutoCompleteListMaxHeight">374</x:Double>
<Thickness x:Key="AutoCompleteListMargin">0,2,0,2</Thickness>
<Thickness x:Key="AutoCompleteListBorderThemeThickness">1</Thickness>
<Thickness x:Key="AutoCompleteListPadding">-1,0,-1,0</Thickness>
<x:Double x:Key="TextControlThemeMinHeight">32</x:Double>
<x:Double x:Key="TextControlThemeMinWidth">64</x:Double>
<x:Double x:Key="FlyoutThemeMaxWidth">456</x:Double>
<Thickness x:Key="DateTimeFlyoutButtonBorderThickness">0</Thickness>
<Thickness x:Key="DateTimeFlyoutBorderThickness">1</Thickness>
<Thickness x:Key="DateTimeFlyoutBorderPadding">0</Thickness>
<Thickness x:Key="FlyoutContentThemePadding">12,11,12,12</Thickness>
<x:Double x:Key="FlyoutThemeMinWidth">96</x:Double>
<x:Double x:Key="FlyoutThemeMinHeight">40</x:Double>
<x:Double x:Key="FlyoutThemeMaxHeight">758</x:Double>
<!-- Moved from FlyoutPresenter.xaml -->
<Thickness x:Key="FlyoutBorderThemePadding">0</Thickness>
<!-- Moved from MenuItem.xaml -->
<Thickness x:Key="MenuFlyoutScrollerMargin">0,4,0,4</Thickness>
<!-- Moved from Menu.xaml -->
<Thickness x:Key="MenuBarItemPadding">12,0,12,0</Thickness>
</ResourceDictionary> </ResourceDictionary>
<ResourceDictionary x:Key="Dark"> <ResourceDictionary x:Key="Dark">
<!-- System Control Colors -->
<Color x:Key="SystemAltHighColor">#FF000000</Color>
<Color x:Key="SystemAltLowColor">#33000000</Color>
<Color x:Key="SystemAltMediumColor">#99000000</Color>
<Color x:Key="SystemAltMediumHighColor">#CC000000</Color>
<Color x:Key="SystemAltMediumLowColor">#66000000</Color>
<Color x:Key="SystemBaseHighColor">#FFFFFFFF</Color>
<Color x:Key="SystemBaseLowColor">#33FFFFFF</Color>
<Color x:Key="SystemBaseMediumColor">#99FFFFFF</Color>
<Color x:Key="SystemBaseMediumHighColor">#CCFFFFFF</Color>
<Color x:Key="SystemBaseMediumLowColor">#66FFFFFF</Color>
<Color x:Key="SystemChromeAltLowColor">#FFF2F2F2</Color>
<Color x:Key="SystemChromeBlackHighColor">#FF000000</Color>
<Color x:Key="SystemChromeBlackLowColor">#33000000</Color>
<Color x:Key="SystemChromeBlackMediumLowColor">#66000000</Color>
<Color x:Key="SystemChromeBlackMediumColor">#CC000000</Color>
<Color x:Key="SystemChromeDisabledHighColor">#FF333333</Color>
<Color x:Key="SystemChromeDisabledLowColor">#FF858585</Color>
<Color x:Key="SystemChromeHighColor">#FF767676</Color>
<Color x:Key="SystemChromeLowColor">#FF171717</Color>
<Color x:Key="SystemChromeMediumColor">#FF1F1F1F</Color>
<Color x:Key="SystemChromeMediumLowColor">#FF2B2B2B</Color>
<Color x:Key="SystemChromeWhiteColor">#FFFFFFFF</Color>
<Color x:Key="SystemChromeGrayColor">#FF767676</Color>
<Color x:Key="SystemListLowColor">#19FFFFFF</Color>
<Color x:Key="SystemListMediumColor">#33FFFFFF</Color>
<Color x:Key="SystemErrorTextColor">#FFF000</Color>
<Color x:Key="SystemRevealListLowColor">#18FFFFFF</Color>
<Color x:Key="SystemRevealListMediumColor">#30FFFFFF</Color>
<SolidColorBrush x:Key="SystemControlBackgroundAccentBrush" Color="{DynamicResource SystemAccentColor}" /> <SolidColorBrush x:Key="SystemControlBackgroundAccentBrush" Color="{DynamicResource SystemAccentColor}" />
<SolidColorBrush x:Key="SystemControlBackgroundAltHighBrush" Color="{StaticResource SystemAltHighColor}" /> <SolidColorBrush x:Key="SystemControlBackgroundAltHighBrush" Color="{StaticResource SystemAltHighColor}" />
<SolidColorBrush x:Key="SystemControlBackgroundAltMediumHighBrush" <SolidColorBrush x:Key="SystemControlBackgroundAltMediumHighBrush"
@ -437,46 +365,14 @@
Color="{StaticResource SystemChromeWhiteColor}" /> Color="{StaticResource SystemChromeWhiteColor}" />
<SolidColorBrush x:Key="SystemControlHighlightAltTransparentRevealBorderBrush" Color="Transparent" /> <SolidColorBrush x:Key="SystemControlHighlightAltTransparentRevealBorderBrush" Color="Transparent" />
<SolidColorBrush x:Key="SystemControlBackgroundTransparentRevealBorderBrush" Color="Transparent" /> <SolidColorBrush x:Key="SystemControlBackgroundTransparentRevealBorderBrush" Color="Transparent" />
<!-- TODO implement AcrylicBrush -->
<!--<AcrylicBrush x:Key="SystemControlTransientBackgroundBrush" BackgroundSource="HostBackdrop" TintColor="{StaticResource SystemChromeAltHighColor}" TintOpacity="0.8" FallbackColor="{StaticResource SystemChromeMediumLowColor}" />-->
<SolidColorBrush x:Key="SystemControlTransientBackgroundBrush" <SolidColorBrush x:Key="SystemControlTransientBackgroundBrush"
Color="{StaticResource SystemChromeMediumLowColor}" /> Color="{StaticResource SystemChromeMediumLowColor}" />
<StaticResource x:Key="SystemControlDescriptionTextForegroundBrush" <StaticResource x:Key="SystemControlDescriptionTextForegroundBrush"
ResourceKey="SystemControlPageTextBaseMediumBrush" /> ResourceKey="SystemControlPageTextBaseMediumBrush" />
<!--<AcrylicBrush x:Key="SystemControlAcrylicWindowBrush" BackgroundSource="HostBackdrop" TintColor="{ThemeResource SystemChromeAltHighColor}" TintOpacity="0.8" FallbackColor="{ThemeResource SystemChromeMediumColor}" />-->
<!--<RevealBackgroundBrush x:Key="SystemControlTransparentRevealBackgroundBrush" TargetTheme="Dark" Color="Transparent" FallbackColor="Transparent" />-->
<SolidColorBrush x:Key="SystemControlTransparentRevealBackgroundBrush" Color="Transparent" /> <SolidColorBrush x:Key="SystemControlTransparentRevealBackgroundBrush" Color="Transparent" />
<!--<RevealBorderBrush x:Key="SystemControlTransparentRevealBorderBrush" TargetTheme="Dark" Color="Transparent" FallbackColor="Transparent" />-->
<SolidColorBrush x:Key="SystemControlTransparentRevealBorderBrush" Color="Transparent" /> <SolidColorBrush x:Key="SystemControlTransparentRevealBorderBrush" Color="Transparent" />
<Color x:Key="RegionColor">#FF000000</Color> <SolidColorBrush x:Key="SystemRegionBrush" Color="{StaticResource SystemRegionColor}" />
<SolidColorBrush x:Key="RegionBrush" Color="{StaticResource RegionColor}" />
<x:Double x:Key="AutoCompleteListMaxHeight">374</x:Double>
<Thickness x:Key="AutoCompleteListMargin">0,2,0,2</Thickness>
<Thickness x:Key="AutoCompleteListBorderThemeThickness">1</Thickness>
<Thickness x:Key="AutoCompleteListPadding">-1,0,-1,0</Thickness>
<x:Double x:Key="TextControlThemeMinHeight">32</x:Double>
<x:Double x:Key="TextControlThemeMinWidth">64</x:Double>
<x:Double x:Key="FlyoutThemeMaxWidth">456</x:Double>
<Thickness x:Key="DateTimeFlyoutButtonBorderThickness">0</Thickness>
<Thickness x:Key="DateTimeFlyoutBorderThickness">1</Thickness>
<Thickness x:Key="DateTimeFlyoutBorderPadding">0</Thickness>
<Thickness x:Key="FlyoutContentThemePadding">12,11,12,12</Thickness>
<x:Double x:Key="FlyoutThemeMinWidth">96</x:Double>
<x:Double x:Key="FlyoutThemeMinHeight">40</x:Double>
<x:Double x:Key="FlyoutThemeMaxHeight">758</x:Double>
<!-- Moved from FlyoutPresenter.xaml -->
<Thickness x:Key="FlyoutBorderThemePadding">0</Thickness>
<!-- Moved from MenuItem.xaml -->
<Thickness x:Key="MenuFlyoutScrollerMargin">0,4,0,4</Thickness>
<!-- Moved from Menu.xaml -->
<Thickness x:Key="MenuBarItemPadding">12,0,12,0</Thickness>
</ResourceDictionary> </ResourceDictionary>
</ResourceDictionary.ThemeDictionaries> </ResourceDictionary.ThemeDictionaries>
</ResourceDictionary> </ResourceDictionary>

114
src/Avalonia.Themes.Fluent/Accents/FluentControlResources.xaml

@ -4,8 +4,8 @@
<ResourceDictionary x:Key="Default"> <ResourceDictionary x:Key="Default">
<!-- Resources for Button.xaml --> <!-- Resources for Button.xaml -->
<StaticResource x:Key="AccentButtonBackground" ResourceKey="SystemControlBackgroundAccentBrush" /> <StaticResource x:Key="AccentButtonBackground" ResourceKey="SystemControlBackgroundAccentBrush" />
<StaticResource x:Key="AccentButtonBackgroundPointerOver" ResourceKey="SystemAccentColorLight1" /> <SolidColorBrush x:Key="AccentButtonBackgroundPointerOver" Color="{DynamicResource SystemAccentColorLight1}" />
<StaticResource x:Key="AccentButtonBackgroundPressed" ResourceKey="SystemAccentColorDark1" /> <SolidColorBrush x:Key="AccentButtonBackgroundPressed" Color="{DynamicResource SystemAccentColorDark1}" />
<StaticResource x:Key="AccentButtonBackgroundDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" /> <StaticResource x:Key="AccentButtonBackgroundDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<StaticResource x:Key="AccentButtonForeground" ResourceKey="SystemControlBackgroundChromeWhiteBrush" /> <StaticResource x:Key="AccentButtonForeground" ResourceKey="SystemControlBackgroundChromeWhiteBrush" />
<StaticResource x:Key="AccentButtonForegroundPointerOver" ResourceKey="SystemControlBackgroundChromeWhiteBrush" /> <StaticResource x:Key="AccentButtonForegroundPointerOver" ResourceKey="SystemControlBackgroundChromeWhiteBrush" />
@ -52,7 +52,8 @@
<StaticResource x:Key="ToggleButtonBackgroundPressed" ResourceKey="SystemControlBackgroundBaseMediumLowBrush" /> <StaticResource x:Key="ToggleButtonBackgroundPressed" ResourceKey="SystemControlBackgroundBaseMediumLowBrush" />
<StaticResource x:Key="ToggleButtonBackgroundDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" /> <StaticResource x:Key="ToggleButtonBackgroundDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<StaticResource x:Key="ToggleButtonBackgroundChecked" ResourceKey="SystemControlHighlightAccentBrush" /> <StaticResource x:Key="ToggleButtonBackgroundChecked" ResourceKey="SystemControlHighlightAccentBrush" />
<StaticResource x:Key="ToggleButtonBackgroundCheckedPointerOver" ResourceKey="SystemAccentColorLight1" /> <SolidColorBrush x:Key="ToggleButtonBackgroundCheckedPointerOver"
Color="{DynamicResource SystemAccentColorLight1}" />
<StaticResource x:Key="ToggleButtonBackgroundCheckedPressed" <StaticResource x:Key="ToggleButtonBackgroundCheckedPressed"
ResourceKey="SystemControlHighlightBaseMediumLowBrush" /> ResourceKey="SystemControlHighlightBaseMediumLowBrush" />
<StaticResource x:Key="ToggleButtonBackgroundCheckedDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" /> <StaticResource x:Key="ToggleButtonBackgroundCheckedDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" />
@ -291,15 +292,17 @@
ResourceKey="SystemControlHighlightBaseHighBrush" /> ResourceKey="SystemControlHighlightBaseHighBrush" />
<StaticResource x:Key="CheckBoxCheckBackgroundStrokeUncheckedDisabled" <StaticResource x:Key="CheckBoxCheckBackgroundStrokeUncheckedDisabled"
ResourceKey="SystemControlDisabledBaseMediumLowBrush" /> ResourceKey="SystemControlDisabledBaseMediumLowBrush" />
<StaticResource x:Key="CheckBoxCheckBackgroundStrokeCheckedPointerOver" ResourceKey="SystemAccentColorLight1" /> <SolidColorBrush x:Key="CheckBoxCheckBackgroundStrokeCheckedPointerOver"
Color="{DynamicResource SystemAccentColorLight1}" />
<StaticResource x:Key="CheckBoxCheckBackgroundStrokeCheckedPressed" <StaticResource x:Key="CheckBoxCheckBackgroundStrokeCheckedPressed"
ResourceKey="SystemControlHighlightTransparentBrush" /> ResourceKey="SystemControlHighlightTransparentBrush" />
<StaticResource x:Key="CheckBoxCheckBackgroundStrokeCheckedDisabled" ResourceKey="SystemControlTransparentBrush" /> <StaticResource x:Key="CheckBoxCheckBackgroundStrokeCheckedDisabled" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="CheckBoxCheckBackgroundStrokeIndeterminate" <StaticResource x:Key="CheckBoxCheckBackgroundStrokeIndeterminate"
ResourceKey="SystemControlHighlightAccentBrush" /> ResourceKey="SystemControlHighlightAccentBrush" />
<StaticResource x:Key="CheckBoxCheckBackgroundStrokeIndeterminatePointerOver" <SolidColorBrush x:Key="CheckBoxCheckBackgroundStrokeIndeterminatePointerOver"
ResourceKey="SystemAccentColorLight1" /> Color="{DynamicResource SystemAccentColorLight1}" />
<StaticResource x:Key="CheckBoxCheckBackgroundStrokeIndeterminatePressed" ResourceKey="SystemAccentColorDark1" /> <SolidColorBrush x:Key="CheckBoxCheckBackgroundStrokeIndeterminatePressed"
Color="{DynamicResource SystemAccentColorDark1}" />
<StaticResource x:Key="CheckBoxCheckBackgroundStrokeIndeterminateDisabled" <StaticResource x:Key="CheckBoxCheckBackgroundStrokeIndeterminateDisabled"
ResourceKey="SystemControlTransparentBrush" /> ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="CheckBoxCheckBackgroundFillUnchecked" ResourceKey="SystemControlTransparentBrush" /> <StaticResource x:Key="CheckBoxCheckBackgroundFillUnchecked" ResourceKey="SystemControlTransparentBrush" />
@ -309,13 +312,17 @@
ResourceKey="SystemControlBackgroundBaseMediumLowBrush" /> ResourceKey="SystemControlBackgroundBaseMediumLowBrush" />
<StaticResource x:Key="CheckBoxCheckBackgroundFillUncheckedDisabled" ResourceKey="SystemControlTransparentBrush" /> <StaticResource x:Key="CheckBoxCheckBackgroundFillUncheckedDisabled" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="CheckBoxCheckBackgroundFillChecked" ResourceKey="SystemControlHighlightAccentBrush" /> <StaticResource x:Key="CheckBoxCheckBackgroundFillChecked" ResourceKey="SystemControlHighlightAccentBrush" />
<StaticResource x:Key="CheckBoxCheckBackgroundFillCheckedPointerOver" ResourceKey="SystemAccentColorLight1" /> <SolidColorBrush x:Key="CheckBoxCheckBackgroundFillCheckedPointerOver"
<StaticResource x:Key="CheckBoxCheckBackgroundFillCheckedPressed" ResourceKey="SystemAccentColorDark1" /> Color="{DynamicResource SystemAccentColorLight1}" />
<SolidColorBrush x:Key="CheckBoxCheckBackgroundFillCheckedPressed"
Color="{DynamicResource SystemAccentColorDark1}" />
<StaticResource x:Key="CheckBoxCheckBackgroundFillCheckedDisabled" <StaticResource x:Key="CheckBoxCheckBackgroundFillCheckedDisabled"
ResourceKey="SystemControlBackgroundBaseLowBrush" /> ResourceKey="SystemControlBackgroundBaseLowBrush" />
<StaticResource x:Key="CheckBoxCheckBackgroundFillIndeterminate" ResourceKey="SystemControlHighlightAccentBrush" /> <StaticResource x:Key="CheckBoxCheckBackgroundFillIndeterminate" ResourceKey="SystemControlHighlightAccentBrush" />
<StaticResource x:Key="CheckBoxCheckBackgroundFillIndeterminatePointerOver" ResourceKey="SystemAccentColorLight1" /> <SolidColorBrush x:Key="CheckBoxCheckBackgroundFillIndeterminatePointerOver"
<StaticResource x:Key="CheckBoxCheckBackgroundFillIndeterminatePressed" ResourceKey="SystemAccentColorDark1" /> Color="{DynamicResource SystemAccentColorLight1}" />
<SolidColorBrush x:Key="CheckBoxCheckBackgroundFillIndeterminatePressed"
Color="{DynamicResource SystemAccentColorDark1}" />
<StaticResource x:Key="CheckBoxCheckBackgroundFillIndeterminateDisabled" <StaticResource x:Key="CheckBoxCheckBackgroundFillIndeterminateDisabled"
ResourceKey="SystemControlBackgroundBaseLowBrush" /> ResourceKey="SystemControlBackgroundBaseLowBrush" />
<StaticResource x:Key="CheckBoxCheckGlyphForegroundUnchecked" <StaticResource x:Key="CheckBoxCheckGlyphForegroundUnchecked"
@ -442,13 +449,17 @@
ResourceKey="SystemControlBackgroundBaseMediumLowBrush" /> ResourceKey="SystemControlBackgroundBaseMediumLowBrush" />
<StaticResource x:Key="RadioButtonOuterEllipseFillDisabled" ResourceKey="SystemControlTransparentBrush" /> <StaticResource x:Key="RadioButtonOuterEllipseFillDisabled" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="RadioButtonOuterEllipseCheckedStroke" ResourceKey="SystemControlHighlightAccentBrush" /> <StaticResource x:Key="RadioButtonOuterEllipseCheckedStroke" ResourceKey="SystemControlHighlightAccentBrush" />
<StaticResource x:Key="RadioButtonOuterEllipseCheckedStrokePointerOver" ResourceKey="SystemAccentColorLight1" /> <SolidColorBrush x:Key="RadioButtonOuterEllipseCheckedStrokePointerOver"
<StaticResource x:Key="RadioButtonOuterEllipseCheckedStrokePressed" ResourceKey="SystemAccentColorDark1" /> Color="{DynamicResource SystemAccentColorLight1}" />
<SolidColorBrush x:Key="RadioButtonOuterEllipseCheckedStrokePressed"
Color="{DynamicResource SystemAccentColorDark1}" />
<StaticResource x:Key="RadioButtonOuterEllipseCheckedStrokeDisabled" <StaticResource x:Key="RadioButtonOuterEllipseCheckedStrokeDisabled"
ResourceKey="SystemControlDisabledBaseMediumLowBrush" /> ResourceKey="SystemControlDisabledBaseMediumLowBrush" />
<StaticResource x:Key="RadioButtonOuterEllipseCheckedFill" ResourceKey="SystemControlHighlightAccentBrush" /> <StaticResource x:Key="RadioButtonOuterEllipseCheckedFill" ResourceKey="SystemControlHighlightAccentBrush" />
<StaticResource x:Key="RadioButtonOuterEllipseCheckedFillPointerOver" ResourceKey="SystemAccentColorLight1" /> <SolidColorBrush x:Key="RadioButtonOuterEllipseCheckedFillPointerOver"
<StaticResource x:Key="RadioButtonOuterEllipseCheckedFillPressed" ResourceKey="SystemAccentColorDark1" /> Color="{DynamicResource SystemAccentColorLight1}" />
<SolidColorBrush x:Key="RadioButtonOuterEllipseCheckedFillPressed"
Color="{DynamicResource SystemAccentColorDark1}" />
<StaticResource x:Key="RadioButtonOuterEllipseCheckedFillDisabled" <StaticResource x:Key="RadioButtonOuterEllipseCheckedFillDisabled"
ResourceKey="SystemControlBackgroundBaseMediumLowBrush" /> ResourceKey="SystemControlBackgroundBaseMediumLowBrush" />
<StaticResource x:Key="RadioButtonCheckGlyphFill" ResourceKey="SystemControlForegroundChromeWhiteBrush" /> <StaticResource x:Key="RadioButtonCheckGlyphFill" ResourceKey="SystemControlForegroundChromeWhiteBrush" />
@ -470,8 +481,8 @@
<StaticResource x:Key="SliderContainerBackgroundPressed" ResourceKey="SystemControlTransparentBrush" /> <StaticResource x:Key="SliderContainerBackgroundPressed" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="SliderContainerBackgroundDisabled" ResourceKey="SystemControlTransparentBrush" /> <StaticResource x:Key="SliderContainerBackgroundDisabled" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="SliderThumbBackground" ResourceKey="SystemControlForegroundAccentBrush" /> <StaticResource x:Key="SliderThumbBackground" ResourceKey="SystemControlForegroundAccentBrush" />
<StaticResource x:Key="SliderThumbBackgroundPointerOver" ResourceKey="SystemAccentColorLight1" /> <SolidColorBrush x:Key="SliderThumbBackgroundPointerOver" Color="{DynamicResource SystemAccentColorLight1}" />
<StaticResource x:Key="SliderThumbBackgroundPressed" ResourceKey="SystemAccentColorDark1" /> <SolidColorBrush x:Key="SliderThumbBackgroundPressed" Color="{DynamicResource SystemAccentColorDark1}" />
<StaticResource x:Key="SliderThumbBackgroundDisabled" ResourceKey="SystemControlDisabledChromeDisabledHighBrush" /> <StaticResource x:Key="SliderThumbBackgroundDisabled" ResourceKey="SystemControlDisabledChromeDisabledHighBrush" />
<StaticResource x:Key="SliderTrackFill" ResourceKey="SystemControlForegroundBaseMediumLowBrush" /> <StaticResource x:Key="SliderTrackFill" ResourceKey="SystemControlForegroundBaseMediumLowBrush" />
<StaticResource x:Key="SliderTrackFillPointerOver" ResourceKey="SystemControlForegroundBaseMediumBrush" /> <StaticResource x:Key="SliderTrackFillPointerOver" ResourceKey="SystemControlForegroundBaseMediumBrush" />
@ -502,8 +513,8 @@
<StaticResource x:Key="ToggleSwitchStrokeOffPressed" ResourceKey="SystemControlForegroundBaseHighBrush" /> <StaticResource x:Key="ToggleSwitchStrokeOffPressed" ResourceKey="SystemControlForegroundBaseHighBrush" />
<StaticResource x:Key="ToggleSwitchStrokeOffDisabled" ResourceKey="SystemControlDisabledBaseMediumLowBrush" /> <StaticResource x:Key="ToggleSwitchStrokeOffDisabled" ResourceKey="SystemControlDisabledBaseMediumLowBrush" />
<StaticResource x:Key="ToggleSwitchFillOn" ResourceKey="SystemControlHighlightAccentBrush" /> <StaticResource x:Key="ToggleSwitchFillOn" ResourceKey="SystemControlHighlightAccentBrush" />
<StaticResource x:Key="ToggleSwitchFillOnPointerOver" ResourceKey="SystemAccentColorLight1" /> <SolidColorBrush x:Key="ToggleSwitchFillOnPointerOver" Color="{DynamicResource SystemAccentColorLight1}" />
<StaticResource x:Key="ToggleSwitchFillOnPressed" ResourceKey="SystemAccentColorDark1" /> <SolidColorBrush x:Key="ToggleSwitchFillOnPressed" Color="{DynamicResource SystemAccentColorDark1}" />
<StaticResource x:Key="ToggleSwitchFillOnDisabled" ResourceKey="SystemControlDisabledBaseLowBrush" /> <StaticResource x:Key="ToggleSwitchFillOnDisabled" ResourceKey="SystemControlDisabledBaseLowBrush" />
<StaticResource x:Key="ToggleSwitchStrokeOn" ResourceKey="SystemControlHighlightBaseHighBrush" /> <StaticResource x:Key="ToggleSwitchStrokeOn" ResourceKey="SystemControlHighlightBaseHighBrush" />
<StaticResource x:Key="ToggleSwitchStrokeOnPointerOver" ResourceKey="SystemControlHighlightListAccentHighBrush" /> <StaticResource x:Key="ToggleSwitchStrokeOnPointerOver" ResourceKey="SystemControlHighlightListAccentHighBrush" />
@ -701,8 +712,9 @@
<StaticResource x:Key="SplitButtonBackgroundPressed" ResourceKey="SystemControlBackgroundBaseMediumLowBrush" /> <StaticResource x:Key="SplitButtonBackgroundPressed" ResourceKey="SystemControlBackgroundBaseMediumLowBrush" />
<StaticResource x:Key="SplitButtonBackgroundDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" /> <StaticResource x:Key="SplitButtonBackgroundDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<StaticResource x:Key="SplitButtonBackgroundChecked" ResourceKey="SystemControlHighlightAccentBrush" /> <StaticResource x:Key="SplitButtonBackgroundChecked" ResourceKey="SystemControlHighlightAccentBrush" />
<StaticResource x:Key="SplitButtonBackgroundCheckedPointerOver" ResourceKey="SystemAccentColorLight1" /> <SolidColorBrush x:Key="SplitButtonBackgroundCheckedPointerOver"
<StaticResource x:Key="SplitButtonBackgroundCheckedPressed" ResourceKey="SystemAccentColorDark1" /> Color="{DynamicResource SystemAccentColorLight1}" />
<SolidColorBrush x:Key="SplitButtonBackgroundCheckedPressed" Color="{DynamicResource SystemAccentColorDark1}" />
<StaticResource x:Key="SplitButtonBackgroundCheckedDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" /> <StaticResource x:Key="SplitButtonBackgroundCheckedDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<StaticResource x:Key="SplitButtonForeground" ResourceKey="SystemControlForegroundBaseHighBrush" /> <StaticResource x:Key="SplitButtonForeground" ResourceKey="SystemControlForegroundBaseHighBrush" />
<StaticResource x:Key="SplitButtonForegroundPointerOver" ResourceKey="SystemControlHighlightBaseHighBrush" /> <StaticResource x:Key="SplitButtonForegroundPointerOver" ResourceKey="SystemControlHighlightBaseHighBrush" />
@ -775,8 +787,8 @@
<ResourceDictionary x:Key="Dark"> <ResourceDictionary x:Key="Dark">
<!-- Resources for Button.xaml --> <!-- Resources for Button.xaml -->
<StaticResource x:Key="AccentButtonBackground" ResourceKey="SystemControlBackgroundAccentBrush" /> <StaticResource x:Key="AccentButtonBackground" ResourceKey="SystemControlBackgroundAccentBrush" />
<StaticResource x:Key="AccentButtonBackgroundPointerOver" ResourceKey="SystemAccentColorLight1" /> <SolidColorBrush x:Key="AccentButtonBackgroundPointerOver" Color="{DynamicResource SystemAccentColorLight1}" />
<StaticResource x:Key="AccentButtonBackgroundPressed" ResourceKey="SystemAccentColorDark1" /> <SolidColorBrush x:Key="AccentButtonBackgroundPressed" Color="{DynamicResource SystemAccentColorDark1}" />
<StaticResource x:Key="AccentButtonBackgroundDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" /> <StaticResource x:Key="AccentButtonBackgroundDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<StaticResource x:Key="AccentButtonForeground" ResourceKey="SystemControlBackgroundChromeWhiteBrush" /> <StaticResource x:Key="AccentButtonForeground" ResourceKey="SystemControlBackgroundChromeWhiteBrush" />
<StaticResource x:Key="AccentButtonForegroundPointerOver" ResourceKey="SystemControlBackgroundChromeWhiteBrush" /> <StaticResource x:Key="AccentButtonForegroundPointerOver" ResourceKey="SystemControlBackgroundChromeWhiteBrush" />
@ -823,7 +835,8 @@
<StaticResource x:Key="ToggleButtonBackgroundPressed" ResourceKey="SystemControlBackgroundBaseMediumLowBrush" /> <StaticResource x:Key="ToggleButtonBackgroundPressed" ResourceKey="SystemControlBackgroundBaseMediumLowBrush" />
<StaticResource x:Key="ToggleButtonBackgroundDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" /> <StaticResource x:Key="ToggleButtonBackgroundDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<StaticResource x:Key="ToggleButtonBackgroundChecked" ResourceKey="SystemControlHighlightAccentBrush" /> <StaticResource x:Key="ToggleButtonBackgroundChecked" ResourceKey="SystemControlHighlightAccentBrush" />
<StaticResource x:Key="ToggleButtonBackgroundCheckedPointerOver" ResourceKey="SystemAccentColorDark1" /> <SolidColorBrush x:Key="ToggleButtonBackgroundCheckedPointerOver"
Color="{DynamicResource SystemAccentColorDark1}" />
<StaticResource x:Key="ToggleButtonBackgroundCheckedPressed" <StaticResource x:Key="ToggleButtonBackgroundCheckedPressed"
ResourceKey="SystemControlHighlightBaseMediumLowBrush" /> ResourceKey="SystemControlHighlightBaseMediumLowBrush" />
<StaticResource x:Key="ToggleButtonBackgroundCheckedDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" /> <StaticResource x:Key="ToggleButtonBackgroundCheckedDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" />
@ -1065,14 +1078,17 @@
ResourceKey="SystemControlHighlightBaseHighBrush" /> ResourceKey="SystemControlHighlightBaseHighBrush" />
<StaticResource x:Key="CheckBoxCheckBackgroundStrokeUncheckedDisabled" <StaticResource x:Key="CheckBoxCheckBackgroundStrokeUncheckedDisabled"
ResourceKey="SystemControlDisabledBaseMediumLowBrush" /> ResourceKey="SystemControlDisabledBaseMediumLowBrush" />
<StaticResource x:Key="CheckBoxCheckBackgroundStrokeCheckedPointerOver" ResourceKey="SystemAccentColorDark1" /> <SolidColorBrush x:Key="CheckBoxCheckBackgroundStrokeCheckedPointerOver"
<StaticResource x:Key="CheckBoxCheckBackgroundStrokeCheckedPressed" ResourceKey="SystemAccentColorLight1" /> Color="{DynamicResource SystemAccentColorDark1}" />
<SolidColorBrush x:Key="CheckBoxCheckBackgroundStrokeCheckedPressed"
Color="{DynamicResource SystemAccentColorLight1}" />
<StaticResource x:Key="CheckBoxCheckBackgroundStrokeCheckedDisabled" ResourceKey="SystemControlTransparentBrush" /> <StaticResource x:Key="CheckBoxCheckBackgroundStrokeCheckedDisabled" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="CheckBoxCheckBackgroundStrokeIndeterminate" <StaticResource x:Key="CheckBoxCheckBackgroundStrokeIndeterminate"
ResourceKey="SystemControlForegroundAccentBrush" /> ResourceKey="SystemControlForegroundAccentBrush" />
<StaticResource x:Key="CheckBoxCheckBackgroundStrokeIndeterminatePointerOver" <SolidColorBrush x:Key="CheckBoxCheckBackgroundStrokeIndeterminatePointerOver"
ResourceKey="SystemAccentColorLight1" /> Color="{DynamicResource SystemAccentColorLight1}" />
<StaticResource x:Key="CheckBoxCheckBackgroundStrokeIndeterminatePressed" ResourceKey="SystemAccentColorDark1" /> <SolidColorBrush x:Key="CheckBoxCheckBackgroundStrokeIndeterminatePressed"
Color="{DynamicResource SystemAccentColorDark1}" />
<StaticResource x:Key="CheckBoxCheckBackgroundStrokeIndeterminateDisabled" <StaticResource x:Key="CheckBoxCheckBackgroundStrokeIndeterminateDisabled"
ResourceKey="SystemControlTransparentBrush" /> ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="CheckBoxCheckBackgroundFillUnchecked" ResourceKey="SystemControlTransparentBrush" /> <StaticResource x:Key="CheckBoxCheckBackgroundFillUnchecked" ResourceKey="SystemControlTransparentBrush" />
@ -1082,13 +1098,17 @@
ResourceKey="SystemControlBackgroundBaseMediumLowBrush" /> ResourceKey="SystemControlBackgroundBaseMediumLowBrush" />
<StaticResource x:Key="CheckBoxCheckBackgroundFillUncheckedDisabled" ResourceKey="SystemControlTransparentBrush" /> <StaticResource x:Key="CheckBoxCheckBackgroundFillUncheckedDisabled" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="CheckBoxCheckBackgroundFillChecked" ResourceKey="SystemControlHighlightAccentBrush" /> <StaticResource x:Key="CheckBoxCheckBackgroundFillChecked" ResourceKey="SystemControlHighlightAccentBrush" />
<StaticResource x:Key="CheckBoxCheckBackgroundFillCheckedPointerOver" ResourceKey="SystemAccentColorLight1" /> <SolidColorBrush x:Key="CheckBoxCheckBackgroundFillCheckedPointerOver"
<StaticResource x:Key="CheckBoxCheckBackgroundFillCheckedPressed" ResourceKey="SystemAccentColorDark1" /> Color="{DynamicResource SystemAccentColorLight1}" />
<SolidColorBrush x:Key="CheckBoxCheckBackgroundFillCheckedPressed"
Color="{DynamicResource SystemAccentColorDark1}" />
<StaticResource x:Key="CheckBoxCheckBackgroundFillCheckedDisabled" <StaticResource x:Key="CheckBoxCheckBackgroundFillCheckedDisabled"
ResourceKey="SystemControlBackgroundBaseMediumLowBrush" /> ResourceKey="SystemControlBackgroundBaseMediumLowBrush" />
<StaticResource x:Key="CheckBoxCheckBackgroundFillIndeterminate" ResourceKey="SystemControlHighlightAccentBrush" /> <StaticResource x:Key="CheckBoxCheckBackgroundFillIndeterminate" ResourceKey="SystemControlHighlightAccentBrush" />
<StaticResource x:Key="CheckBoxCheckBackgroundFillIndeterminatePointerOver" ResourceKey="SystemAccentColorLight1" /> <SolidColorBrush x:Key="CheckBoxCheckBackgroundFillIndeterminatePointerOver"
<StaticResource x:Key="CheckBoxCheckBackgroundFillIndeterminatePressed" ResourceKey="SystemAccentColorDark1" /> Color="{DynamicResource SystemAccentColorLight1}" />
<SolidColorBrush x:Key="CheckBoxCheckBackgroundFillIndeterminatePressed"
Color="{DynamicResource SystemAccentColorDark1}" />
<StaticResource x:Key="CheckBoxCheckBackgroundFillIndeterminateDisabled" <StaticResource x:Key="CheckBoxCheckBackgroundFillIndeterminateDisabled"
ResourceKey="SystemControlBackgroundBaseMediumLowBrush" /> ResourceKey="SystemControlBackgroundBaseMediumLowBrush" />
<StaticResource x:Key="CheckBoxCheckGlyphForegroundUnchecked" <StaticResource x:Key="CheckBoxCheckGlyphForegroundUnchecked"
@ -1215,13 +1235,17 @@
ResourceKey="SystemControlBackgroundBaseMediumLowBrush" /> ResourceKey="SystemControlBackgroundBaseMediumLowBrush" />
<StaticResource x:Key="RadioButtonOuterEllipseFillDisabled" ResourceKey="SystemControlTransparentBrush" /> <StaticResource x:Key="RadioButtonOuterEllipseFillDisabled" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="RadioButtonOuterEllipseCheckedStroke" ResourceKey="SystemControlHighlightAccentBrush" /> <StaticResource x:Key="RadioButtonOuterEllipseCheckedStroke" ResourceKey="SystemControlHighlightAccentBrush" />
<StaticResource x:Key="RadioButtonOuterEllipseCheckedStrokePointerOver" ResourceKey="SystemAccentColorLight1" /> <SolidColorBrush x:Key="RadioButtonOuterEllipseCheckedStrokePointerOver"
<StaticResource x:Key="RadioButtonOuterEllipseCheckedStrokePressed" ResourceKey="SystemAccentColorDark1" /> Color="{DynamicResource SystemAccentColorLight1}" />
<SolidColorBrush x:Key="RadioButtonOuterEllipseCheckedStrokePressed"
Color="{DynamicResource SystemAccentColorDark1}" />
<StaticResource x:Key="RadioButtonOuterEllipseCheckedStrokeDisabled" <StaticResource x:Key="RadioButtonOuterEllipseCheckedStrokeDisabled"
ResourceKey="SystemControlDisabledBaseMediumLowBrush" /> ResourceKey="SystemControlDisabledBaseMediumLowBrush" />
<StaticResource x:Key="RadioButtonOuterEllipseCheckedFill" ResourceKey="SystemControlHighlightAccentBrush" /> <StaticResource x:Key="RadioButtonOuterEllipseCheckedFill" ResourceKey="SystemControlHighlightAccentBrush" />
<StaticResource x:Key="RadioButtonOuterEllipseCheckedFillPointerOver" ResourceKey="SystemAccentColorLight1" /> <SolidColorBrush x:Key="RadioButtonOuterEllipseCheckedFillPointerOver"
<StaticResource x:Key="RadioButtonOuterEllipseCheckedFillPressed" ResourceKey="SystemAccentColorDark1" /> Color="{DynamicResource SystemAccentColorLight1}" />
<SolidColorBrush x:Key="RadioButtonOuterEllipseCheckedFillPressed"
Color="{DynamicResource SystemAccentColorDark1}" />
<StaticResource x:Key="RadioButtonOuterEllipseCheckedFillDisabled" <StaticResource x:Key="RadioButtonOuterEllipseCheckedFillDisabled"
ResourceKey="SystemControlBackgroundBaseMediumLowBrush" /> ResourceKey="SystemControlBackgroundBaseMediumLowBrush" />
<StaticResource x:Key="RadioButtonCheckGlyphFill" ResourceKey="SystemControlForegroundChromeWhiteBrush" /> <StaticResource x:Key="RadioButtonCheckGlyphFill" ResourceKey="SystemControlForegroundChromeWhiteBrush" />
@ -1243,8 +1267,8 @@
<StaticResource x:Key="SliderContainerBackgroundPressed" ResourceKey="SystemControlTransparentBrush" /> <StaticResource x:Key="SliderContainerBackgroundPressed" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="SliderContainerBackgroundDisabled" ResourceKey="SystemControlTransparentBrush" /> <StaticResource x:Key="SliderContainerBackgroundDisabled" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="SliderThumbBackground" ResourceKey="SystemControlForegroundAccentBrush" /> <StaticResource x:Key="SliderThumbBackground" ResourceKey="SystemControlForegroundAccentBrush" />
<StaticResource x:Key="SliderThumbBackgroundPointerOver" ResourceKey="SystemAccentColorLight1" /> <SolidColorBrush x:Key="SliderThumbBackgroundPointerOver" Color="{DynamicResource SystemAccentColorLight1}" />
<StaticResource x:Key="SliderThumbBackgroundPressed" ResourceKey="SystemAccentColorDark1" /> <SolidColorBrush x:Key="SliderThumbBackgroundPressed" Color="{DynamicResource SystemAccentColorDark1}" />
<StaticResource x:Key="SliderThumbBackgroundDisabled" ResourceKey="SystemControlDisabledChromeDisabledHighBrush" /> <StaticResource x:Key="SliderThumbBackgroundDisabled" ResourceKey="SystemControlDisabledChromeDisabledHighBrush" />
<StaticResource x:Key="SliderTrackFill" ResourceKey="SystemControlForegroundBaseMediumLowBrush" /> <StaticResource x:Key="SliderTrackFill" ResourceKey="SystemControlForegroundBaseMediumLowBrush" />
<StaticResource x:Key="SliderTrackFillPointerOver" ResourceKey="SystemControlForegroundBaseMediumBrush" /> <StaticResource x:Key="SliderTrackFillPointerOver" ResourceKey="SystemControlForegroundBaseMediumBrush" />
@ -1275,12 +1299,12 @@
<StaticResource x:Key="ToggleSwitchStrokeOffPressed" ResourceKey="SystemControlForegroundBaseHighBrush" /> <StaticResource x:Key="ToggleSwitchStrokeOffPressed" ResourceKey="SystemControlForegroundBaseHighBrush" />
<StaticResource x:Key="ToggleSwitchStrokeOffDisabled" ResourceKey="SystemControlDisabledBaseMediumLowBrush" /> <StaticResource x:Key="ToggleSwitchStrokeOffDisabled" ResourceKey="SystemControlDisabledBaseMediumLowBrush" />
<StaticResource x:Key="ToggleSwitchFillOn" ResourceKey="SystemControlHighlightAccentBrush" /> <StaticResource x:Key="ToggleSwitchFillOn" ResourceKey="SystemControlHighlightAccentBrush" />
<StaticResource x:Key="ToggleSwitchFillOnPointerOver" ResourceKey="SystemAccentColorLight1" /> <SolidColorBrush x:Key="ToggleSwitchFillOnPointerOver" Color="{DynamicResource SystemAccentColorLight1}" />
<StaticResource x:Key="ToggleSwitchFillOnPressed" ResourceKey="SystemAccentColorDark1" /> <SolidColorBrush x:Key="ToggleSwitchFillOnPressed" Color="{DynamicResource SystemAccentColorDark1}" />
<StaticResource x:Key="ToggleSwitchFillOnDisabled" ResourceKey="SystemControlDisabledBaseLowBrush" /> <StaticResource x:Key="ToggleSwitchFillOnDisabled" ResourceKey="SystemControlDisabledBaseLowBrush" />
<StaticResource x:Key="ToggleSwitchStrokeOn" ResourceKey="SystemControlHighlightBaseHighBrush" /> <StaticResource x:Key="ToggleSwitchStrokeOn" ResourceKey="SystemControlHighlightBaseHighBrush" />
<StaticResource x:Key="ToggleSwitchStrokeOnPointerOver" ResourceKey="SystemAccentColorLight1" /> <SolidColorBrush x:Key="ToggleSwitchStrokeOnPointerOver" Color="{DynamicResource SystemAccentColorLight1}" />
<StaticResource x:Key="ToggleSwitchStrokeOnPressed" ResourceKey="SystemAccentColorDark1" /> <SolidColorBrush x:Key="ToggleSwitchStrokeOnPressed" Color="{DynamicResource SystemAccentColorDark1}" />
<StaticResource x:Key="ToggleSwitchStrokeOnDisabled" ResourceKey="SystemControlDisabledBaseMediumLowBrush" /> <StaticResource x:Key="ToggleSwitchStrokeOnDisabled" ResourceKey="SystemControlDisabledBaseMediumLowBrush" />
<StaticResource x:Key="ToggleSwitchKnobFillOff" ResourceKey="SystemControlHighlightBaseHighBrush" /> <StaticResource x:Key="ToggleSwitchKnobFillOff" ResourceKey="SystemControlHighlightBaseHighBrush" />
<StaticResource x:Key="ToggleSwitchKnobFillOffPointerOver" ResourceKey="SystemControlHighlightBaseHighBrush" /> <StaticResource x:Key="ToggleSwitchKnobFillOffPointerOver" ResourceKey="SystemControlHighlightBaseHighBrush" />
@ -1476,8 +1500,8 @@
<StaticResource x:Key="SplitButtonBackgroundPressed" ResourceKey="SystemControlBackgroundBaseMediumLowBrush" /> <StaticResource x:Key="SplitButtonBackgroundPressed" ResourceKey="SystemControlBackgroundBaseMediumLowBrush" />
<StaticResource x:Key="SplitButtonBackgroundDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" /> <StaticResource x:Key="SplitButtonBackgroundDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<StaticResource x:Key="SplitButtonBackgroundChecked" ResourceKey="SystemControlHighlightAccentBrush" /> <StaticResource x:Key="SplitButtonBackgroundChecked" ResourceKey="SystemControlHighlightAccentBrush" />
<StaticResource x:Key="SplitButtonBackgroundCheckedPointerOver" ResourceKey="SystemAccentColorDark1" /> <SolidColorBrush x:Key="SplitButtonBackgroundCheckedPointerOver" Color="{DynamicResource SystemAccentColorDark1}" />
<StaticResource x:Key="SplitButtonBackgroundCheckedPressed" ResourceKey="SystemAccentColorLight1" /> <SolidColorBrush x:Key="SplitButtonBackgroundCheckedPressed" Color="{DynamicResource SystemAccentColorLight1}" />
<StaticResource x:Key="SplitButtonBackgroundCheckedDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" /> <StaticResource x:Key="SplitButtonBackgroundCheckedDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<StaticResource x:Key="SplitButtonForeground" ResourceKey="SystemControlForegroundBaseHighBrush" /> <StaticResource x:Key="SplitButtonForeground" ResourceKey="SystemControlForegroundBaseHighBrush" />
<StaticResource x:Key="SplitButtonForegroundPointerOver" ResourceKey="SystemControlHighlightBaseHighBrush" /> <StaticResource x:Key="SplitButtonForegroundPointerOver" ResourceKey="SystemControlHighlightBaseHighBrush" />

163
src/Avalonia.Themes.Fluent/Accents/SystemAccentColors.cs

@ -0,0 +1,163 @@
using System;
using Avalonia.Controls;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.Styling;
namespace Avalonia.Themes.Fluent.Accents;
internal class SystemAccentColors : IResourceProvider
{
public const string AccentKey = "SystemAccentColor";
public const string AccentDark1Key = "SystemAccentColorDark1";
public const string AccentDark2Key = "SystemAccentColorDark2";
public const string AccentDark3Key = "SystemAccentColorDark3";
public const string AccentLight1Key = "SystemAccentColorLight1";
public const string AccentLight2Key = "SystemAccentColorLight2";
public const string AccentLight3Key = "SystemAccentColorLight3";
private static readonly Color s_defaultSystemAccentColor = Color.FromRgb(0, 120, 215);
private readonly IPlatformSettings? _platformSettings;
private bool _invalidateColors = true;
private Color _systemAccentColor;
private Color _systemAccentColorDark1, _systemAccentColorDark2, _systemAccentColorDark3;
private Color _systemAccentColorLight1, _systemAccentColorLight2, _systemAccentColorLight3;
public SystemAccentColors()
{
_platformSettings = AvaloniaLocator.Current.GetService<IPlatformSettings>();
}
public bool HasResources => true;
public bool TryGetResource(object key, ThemeVariant? theme, out object? value)
{
if (key is string strKey)
{
if (strKey.Equals(AccentKey, StringComparison.InvariantCulture))
{
EnsureColors();
value = _systemAccentColor;
return true;
}
if (strKey.Equals(AccentDark1Key, StringComparison.InvariantCulture))
{
EnsureColors();
value = _systemAccentColorDark1;
return true;
}
if (strKey.Equals(AccentDark2Key, StringComparison.InvariantCulture))
{
EnsureColors();
value = _systemAccentColorDark2;
return true;
}
if (strKey.Equals(AccentDark3Key, StringComparison.InvariantCulture))
{
EnsureColors();
value = _systemAccentColorDark3;
return true;
}
if (strKey.Equals(AccentLight1Key, StringComparison.InvariantCulture))
{
EnsureColors();
value = _systemAccentColorLight1;
return true;
}
if (strKey.Equals(AccentLight2Key, StringComparison.InvariantCulture))
{
EnsureColors();
value = _systemAccentColorLight2;
return true;
}
if (strKey.Equals(AccentLight3Key, StringComparison.InvariantCulture))
{
EnsureColors();
value = _systemAccentColorLight3;
return true;
}
}
value = null;
return false;
}
public IResourceHost? Owner { get; private set; }
public event EventHandler? OwnerChanged;
public void AddOwner(IResourceHost owner)
{
if (Owner != owner)
{
Owner = owner;
OwnerChanged?.Invoke(this, EventArgs.Empty);
if (_platformSettings is not null)
{
_platformSettings.ColorValuesChanged += PlatformSettingsOnColorValuesChanged;
}
}
}
public void RemoveOwner(IResourceHost owner)
{
if (Owner == owner)
{
Owner = null;
OwnerChanged?.Invoke(this, EventArgs.Empty);
if (_platformSettings is not null)
{
_platformSettings.ColorValuesChanged -= PlatformSettingsOnColorValuesChanged;
}
}
}
private void EnsureColors()
{
if (_invalidateColors)
{
_invalidateColors = false;
_systemAccentColor = _platformSettings?.GetColorValues().AccentColor1 ?? s_defaultSystemAccentColor;
(_systemAccentColorDark1,_systemAccentColorDark2, _systemAccentColorDark3,
_systemAccentColorLight1, _systemAccentColorLight2, _systemAccentColorLight3) = CalculateAccentShades(_systemAccentColor);
}
}
public static (Color d1, Color d2, Color d3, Color l1, Color l2, Color l3) CalculateAccentShades(Color accentColor)
{
// dark1step = (hslAccent.L - SystemAccentColorDark1.L) * 255
const double dark1step = 28.5 / 255d;
const double dark2step = 49 / 255d;
const double dark3step = 74.5 / 255d;
// light1step = (SystemAccentColorLight1.L - hslAccent.L) * 255
const double light1step = 39 / 255d;
const double light2step = 70 / 255d;
const double light3step = 103 / 255d;
var hslAccent = accentColor.ToHsl();
return (
// Darker shades
new HslColor(hslAccent.A, hslAccent.H, hslAccent.S, hslAccent.L - dark1step).ToRgb(),
new HslColor(hslAccent.A, hslAccent.H, hslAccent.S, hslAccent.L - dark2step).ToRgb(),
new HslColor(hslAccent.A, hslAccent.H, hslAccent.S, hslAccent.L - dark3step).ToRgb(),
// Lighter shades
new HslColor(hslAccent.A, hslAccent.H, hslAccent.S, hslAccent.L + light1step).ToRgb(),
new HslColor(hslAccent.A, hslAccent.H, hslAccent.S, hslAccent.L + light2step).ToRgb(),
new HslColor(hslAccent.A, hslAccent.H, hslAccent.S, hslAccent.L + light3step).ToRgb()
);
}
private void PlatformSettingsOnColorValuesChanged(object? sender, PlatformColorValues e)
{
_invalidateColors = true;
Owner?.NotifyHostedResourcesChanged(ResourcesChangedEventArgs.Empty);
}
}

158
src/Avalonia.Themes.Fluent/ColorPaletteResources.Properties.cs

@ -0,0 +1,158 @@
using Avalonia.Media;
namespace Avalonia.Themes.Fluent;
public partial class ColorPaletteResources
{
private bool _hasAccentColor;
private Color _accentColor;
private Color _accentColorDark1, _accentColorDark2, _accentColorDark3;
private Color _accentColorLight1, _accentColorLight2, _accentColorLight3;
public static readonly DirectProperty<ColorPaletteResources, Color> AccentProperty
= AvaloniaProperty.RegisterDirect<ColorPaletteResources, Color>(nameof(Accent), r => r.Accent, (r, v) => r.Accent = v);
/// <summary>
/// Gets or sets the Accent color value.
/// </summary>
public Color Accent
{
get => _accentColor;
set => SetAndRaise(AccentProperty, ref _accentColor, value);
}
/// <summary>
/// Gets or sets the AltHigh color value.
/// </summary>
public Color AltHigh { get => GetColor("SystemAltHighColor"); set => SetColor("SystemAltHighColor", value); }
/// <summary>
/// Gets or sets the AltLow color value.
/// </summary>
public Color AltLow { get => GetColor("SystemAltLowColor"); set => SetColor("SystemAltLowColor", value); }
/// <summary>
/// Gets or sets the AltMedium color value.
/// </summary>
public Color AltMedium { get => GetColor("SystemAltMediumColor"); set => SetColor("SystemAltMediumColor", value); }
/// <summary>
/// Gets or sets the AltMediumHigh color value.
/// </summary>
public Color AltMediumHigh { get => GetColor("SystemAltMediumHighColor"); set => SetColor("SystemAltMediumHighColor", value); }
/// <summary>
/// Gets or sets the AltMediumLow color value.
/// </summary>
public Color AltMediumLow { get => GetColor("SystemAltMediumLowColor"); set => SetColor("SystemAltMediumLowColor", value); }
/// <summary>
/// Gets or sets the BaseHigh color value.
/// </summary>
public Color BaseHigh { get => GetColor("SystemBaseHighColor"); set => SetColor("SystemBaseHighColor", value); }
/// <summary>
/// Gets or sets the BaseLow color value.
/// </summary>
public Color BaseLow { get => GetColor("SystemBaseLowColor"); set => SetColor("SystemBaseLowColor", value); }
/// <summary>
/// Gets or sets the BaseMedium color value.
/// </summary>
public Color BaseMedium { get => GetColor("SystemBaseMediumColor"); set => SetColor("SystemBaseMediumColor", value); }
/// <summary>
/// Gets or sets the BaseMediumHigh color value.
/// </summary>
public Color BaseMediumHigh { get => GetColor("SystemBaseMediumHighColor"); set => SetColor("SystemBaseMediumHighColor", value); }
/// <summary>
/// Gets or sets the BaseMediumLow color value.
/// </summary>
public Color BaseMediumLow { get => GetColor("SystemBaseMediumLowColor"); set => SetColor("SystemBaseMediumLowColor", value); }
/// <summary>
/// Gets or sets the ChromeAltLow color value.
/// </summary>
public Color ChromeAltLow { get => GetColor("SystemChromeAltLowColor"); set => SetColor("SystemChromeAltLowColor", value); }
/// <summary>
/// Gets or sets the ChromeBlackHigh color value.
/// </summary>
public Color ChromeBlackHigh { get => GetColor("SystemChromeBlackHighColor"); set => SetColor("SystemChromeBlackHighColor", value); }
/// <summary>
/// Gets or sets the ChromeBlackLow color value.
/// </summary>
public Color ChromeBlackLow { get => GetColor("SystemChromeBlackLowColor"); set => SetColor("SystemChromeBlackLowColor", value); }
/// <summary>
/// Gets or sets the ChromeBlackMedium color value.
/// </summary>
public Color ChromeBlackMedium { get => GetColor("SystemChromeBlackMediumColor"); set => SetColor("SystemChromeBlackMediumColor", value); }
/// <summary>
/// Gets or sets the ChromeBlackMediumLow color value.
/// </summary>
public Color ChromeBlackMediumLow { get => GetColor("SystemChromeBlackMediumLowColor"); set => SetColor("SystemChromeBlackMediumLowColor", value); }
/// <summary>
/// Gets or sets the ChromeDisabledHigh color value.
/// </summary>
public Color ChromeDisabledHigh { get => GetColor("SystemChromeDisabledHighColor"); set => SetColor("SystemChromeDisabledHighColor", value); }
/// <summary>
/// Gets or sets the ChromeDisabledLow color value.
/// </summary>
public Color ChromeDisabledLow { get => GetColor("SystemChromeDisabledLowColor"); set => SetColor("SystemChromeDisabledLowColor", value); }
/// <summary>
/// Gets or sets the ChromeGray color value.
/// </summary>
public Color ChromeGray { get => GetColor("SystemChromeGrayColor"); set => SetColor("SystemChromeGrayColor", value); }
/// <summary>
/// Gets or sets the ChromeHigh color value.
/// </summary>
public Color ChromeHigh { get => GetColor("SystemChromeHighColor"); set => SetColor("SystemChromeHighColor", value); }
/// <summary>
/// Gets or sets the ChromeLow color value.
/// </summary>
public Color ChromeLow { get => GetColor("SystemChromeLowColor"); set => SetColor("SystemChromeLowColor", value); }
/// <summary>
/// Gets or sets the ChromeMedium color value.
/// </summary>
public Color ChromeMedium { get => GetColor("SystemChromeMediumColor"); set => SetColor("SystemChromeMediumColor", value); }
/// <summary>
/// Gets or sets the ChromeMediumLow color value.
/// </summary>
public Color ChromeMediumLow { get => GetColor("SystemChromeMediumLowColor"); set => SetColor("SystemChromeMediumLowColor", value); }
/// <summary>
/// Gets or sets the ChromeWhite color value.
/// </summary>
public Color ChromeWhite { get => GetColor("SystemChromeWhiteColor"); set => SetColor("SystemChromeWhiteColor", value); }
/// <summary>
/// Gets or sets the ErrorText color value.
/// </summary>
public Color ErrorText { get => GetColor("SystemErrorTextColor"); set => SetColor("SystemErrorTextColor", value); }
/// <summary>
/// Gets or sets the ListLow color value.
/// </summary>
public Color ListLow { get => GetColor("SystemListLowColor"); set => SetColor("SystemListLowColor", value); }
/// <summary>
/// Gets or sets the ListMedium color value.
/// </summary>
public Color ListMedium { get => GetColor("SystemListMediumColor"); set => SetColor("SystemListMediumColor", value); }
/// <summary>
/// Gets or sets the RegionColor color value.
/// </summary>
public Color RegionColor { get => GetColor("SystemRegionColor"); set => SetColor("SystemRegionColor", value); }
}

118
src/Avalonia.Themes.Fluent/ColorPaletteResources.cs

@ -0,0 +1,118 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Avalonia.Controls;
using Avalonia.Media;
using Avalonia.Styling;
using Avalonia.Themes.Fluent.Accents;
namespace Avalonia.Themes.Fluent;
/// <summary>
/// Represents a specialized resource dictionary that contains color resources used by FluentTheme elements.
/// </summary>
/// <remarks>
/// This class can only be used in <see cref="FluentTheme.Palettes"/>.
/// </remarks>
public partial class ColorPaletteResources : AvaloniaObject, IResourceNode
{
private readonly Dictionary<string, Color> _colors = new(StringComparer.InvariantCulture);
public bool HasResources => _hasAccentColor || _colors.Count > 0;
public bool TryGetResource(object key, ThemeVariant? theme, out object? value)
{
if (key is string strKey)
{
if (strKey.Equals(SystemAccentColors.AccentKey, StringComparison.InvariantCulture))
{
value = _accentColor;
return _hasAccentColor;
}
if (strKey.Equals(SystemAccentColors.AccentDark1Key, StringComparison.InvariantCulture))
{
value = _accentColorDark1;
return _hasAccentColor;
}
if (strKey.Equals(SystemAccentColors.AccentDark2Key, StringComparison.InvariantCulture))
{
value = _accentColorDark2;
return _hasAccentColor;
}
if (strKey.Equals(SystemAccentColors.AccentDark3Key, StringComparison.InvariantCulture))
{
value = _accentColorDark3;
return _hasAccentColor;
}
if (strKey.Equals(SystemAccentColors.AccentLight1Key, StringComparison.InvariantCulture))
{
value = _accentColorLight1;
return _hasAccentColor;
}
if (strKey.Equals(SystemAccentColors.AccentLight2Key, StringComparison.InvariantCulture))
{
value = _accentColorLight2;
return _hasAccentColor;
}
if (strKey.Equals(SystemAccentColors.AccentLight3Key, StringComparison.InvariantCulture))
{
value = _accentColorLight3;
return _hasAccentColor;
}
if (_colors.TryGetValue(strKey, out var color))
{
value = color;
return true;
}
}
value = null;
return false;
}
private Color GetColor(string key)
{
if (_colors.TryGetValue(key, out var color))
{
return color;
}
return default;
}
private void SetColor(string key, Color value)
{
if (value == default)
{
_colors.Remove(key);
}
else
{
_colors[key] = value;
}
}
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
if (change.Property == AccentProperty)
{
_hasAccentColor = _accentColor != default;
if (_hasAccentColor)
{
(_accentColorDark1, _accentColorDark2, _accentColorDark3,
_accentColorLight1, _accentColorLight2, _accentColorLight3) =
SystemAccentColors.CalculateAccentShades(_accentColor);
}
}
}
}

65
src/Avalonia.Themes.Fluent/ColorPaletteResourcesCollection.cs

@ -0,0 +1,65 @@
using System;
using Avalonia.Collections;
using Avalonia.Controls;
using Avalonia.Styling;
namespace Avalonia.Themes.Fluent;
internal class ColorPaletteResourcesCollection : AvaloniaDictionary<ThemeVariant, ColorPaletteResources>, IResourceProvider
{
public ColorPaletteResourcesCollection() : base(2)
{
this.ForEachItem(
(_, x) =>
{
if (Owner is not null)
{
x.PropertyChanged += Palette_PropertyChanged;
}
},
(_, x) =>
{
if (Owner is not null)
{
x.PropertyChanged -= Palette_PropertyChanged;
}
},
() => throw new NotSupportedException("Dictionary reset not supported"));
}
public bool HasResources => Count > 0;
public bool TryGetResource(object key, ThemeVariant? theme, out object? value)
{
theme ??= ThemeVariant.Default;
if (base.TryGetValue(theme, out var paletteResources)
&& paletteResources.TryGetResource(key, theme, out value))
{
return true;
}
value = null;
return false;
}
public IResourceHost? Owner { get; private set; }
public event EventHandler? OwnerChanged;
public void AddOwner(IResourceHost owner)
{
Owner = owner;
OwnerChanged?.Invoke(this, EventArgs.Empty);
}
public void RemoveOwner(IResourceHost owner)
{
Owner = null;
OwnerChanged?.Invoke(this, EventArgs.Empty);
}
private void Palette_PropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
{
if (e.Property == ColorPaletteResources.AccentProperty)
{
Owner?.NotifyHostedResourcesChanged(ResourcesChangedEventArgs.Empty);
}
}
}

2
src/Avalonia.Themes.Fluent/Controls/EmbeddableControlRoot.xaml

@ -2,7 +2,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ControlTheme x:Key="{x:Type EmbeddableControlRoot}" TargetType="EmbeddableControlRoot"> <ControlTheme x:Key="{x:Type EmbeddableControlRoot}" TargetType="EmbeddableControlRoot">
<Setter Property="Foreground" Value="{DynamicResource SystemControlForegroundBaseHighBrush}"/> <Setter Property="Foreground" Value="{DynamicResource SystemControlForegroundBaseHighBrush}"/>
<Setter Property="Background" Value="{DynamicResource SystemControlBackgroundAltHighBrush}"/> <Setter Property="Background" Value="{DynamicResource SystemRegionBrush}"/>
<Setter Property="TopLevel.SystemBarColor" Value="{DynamicResource SystemControlBackgroundAltHighBrush}"/> <Setter Property="TopLevel.SystemBarColor" Value="{DynamicResource SystemControlBackgroundAltHighBrush}"/>
<Setter Property="FontSize" Value="{DynamicResource ControlContentThemeFontSize}"/> <Setter Property="FontSize" Value="{DynamicResource ControlContentThemeFontSize}"/>
<Setter Property="FontFamily" Value="{DynamicResource ContentControlThemeFontFamily}" /> <Setter Property="FontFamily" Value="{DynamicResource ContentControlThemeFontFamily}" />

2
src/Avalonia.Themes.Fluent/Controls/Window.xaml

@ -1,7 +1,7 @@
<ResourceDictionary xmlns="https://github.com/avaloniaui" <ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ControlTheme x:Key="{x:Type Window}" TargetType="Window"> <ControlTheme x:Key="{x:Type Window}" TargetType="Window">
<Setter Property="Background" Value="{DynamicResource SystemControlBackgroundAltHighBrush}"/> <Setter Property="Background" Value="{DynamicResource SystemRegionBrush}"/>
<Setter Property="TransparencyBackgroundFallback" Value="{DynamicResource SystemControlBackgroundAltHighBrush}" /> <Setter Property="TransparencyBackgroundFallback" Value="{DynamicResource SystemControlBackgroundAltHighBrush}" />
<Setter Property="TopLevel.SystemBarColor" Value="{DynamicResource SystemControlBackgroundAltHighBrush}"/> <Setter Property="TopLevel.SystemBarColor" Value="{DynamicResource SystemControlBackgroundAltHighBrush}"/>
<Setter Property="Foreground" Value="{DynamicResource SystemControlForegroundBaseHighBrush}"/> <Setter Property="Foreground" Value="{DynamicResource SystemControlForegroundBaseHighBrush}"/>

14
src/Avalonia.Themes.Fluent/FluentTheme.xaml

@ -1,11 +1,19 @@
<Styles x:Class="Avalonia.Themes.Fluent.FluentTheme" <Styles x:Class="Avalonia.Themes.Fluent.FluentTheme"
xmlns="https://github.com/avaloniaui" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:fluent="using:Avalonia.Themes.Fluent"
xmlns:accents="clr-namespace:Avalonia.Themes.Fluent.Accents">
<Styles.Resources> <Styles.Resources>
<ResourceDictionary> <ResourceDictionary>
<ResourceDictionary.MergedDictionaries> <ResourceDictionary.MergedDictionaries>
<MergeResourceInclude Source="/Accents/AccentColors.xaml" /> <!-- Keep custom palettes higher priority than default BaseColorsPalette and SystemAccentColors
<MergeResourceInclude Source="/Accents/Base.xaml" /> As that's an only place for user to redefine palette in a good way -->
<ResourceInclude Source="/Accents/BaseColorsPalette.xaml" />
<accents:SystemAccentColors />
<fluent:ColorPaletteResourcesCollection />
<!-- Resources and brushes will be merged into current dictionary for slightly better performance and possible optimizations -->
<MergeResourceInclude Source="/Accents/BaseResources.xaml" />
<MergeResourceInclude Source="/Accents/FluentControlResources.xaml" /> <MergeResourceInclude Source="/Accents/FluentControlResources.xaml" />
</ResourceDictionary.MergedDictionaries> </ResourceDictionary.MergedDictionaries>

6
src/Avalonia.Themes.Fluent/FluentTheme.xaml.cs

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.Styling; using Avalonia.Styling;
@ -31,6 +32,9 @@ namespace Avalonia.Themes.Fluent
EnsureCompactStyles(); EnsureCompactStyles();
Palettes = Resources.MergedDictionaries.OfType<ColorPaletteResourcesCollection>().FirstOrDefault()
?? throw new InvalidOperationException("FluentTheme was initialized with missing ColorPaletteResourcesCollection.");
object GetAndRemove(string key) object GetAndRemove(string key)
{ {
var val = Resources[key] var val = Resources[key]
@ -52,6 +56,8 @@ namespace Avalonia.Themes.Fluent
set => SetValue(DensityStyleProperty, value); set => SetValue(DensityStyleProperty, value);
} }
public IDictionary<ThemeVariant, ColorPaletteResources> Palettes { get; }
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
base.OnPropertyChanged(change); base.OnPropertyChanged(change);

14
src/Avalonia.X11/X11Window.cs

@ -352,7 +352,7 @@ namespace Avalonia.X11
public IEnumerable<object> Surfaces { get; } public IEnumerable<object> Surfaces { get; }
public Action<RawInputEventArgs>? Input { get; set; } public Action<RawInputEventArgs>? Input { get; set; }
public Action<Rect>? Paint { get; set; } public Action<Rect>? Paint { get; set; }
public Action<Size, PlatformResizeReason>? Resized { get; set; } public Action<Size, WindowResizeReason>? Resized { get; set; }
//TODO //TODO
public Action<double>? ScalingChanged { get; set; } public Action<double>? ScalingChanged { get; set; }
public Action? Deactivated { get; set; } public Action? Deactivated { get; set; }
@ -509,7 +509,7 @@ namespace Avalonia.X11
UpdateImePosition(); UpdateImePosition();
if (changedSize && !updatedSizeViaScaling && !_popup) if (changedSize && !updatedSizeViaScaling && !_popup)
Resized?.Invoke(ClientSize, PlatformResizeReason.Unspecified); Resized?.Invoke(ClientSize, WindowResizeReason.Unspecified);
}, DispatcherPriority.Layout); }, DispatcherPriority.Layout);
if (_useRenderWindow) if (_useRenderWindow)
@ -590,7 +590,7 @@ namespace Avalonia.X11
UpdateImePosition(); UpdateImePosition();
SetMinMaxSize(_scaledMinMaxSize.minSize, _scaledMinMaxSize.maxSize); SetMinMaxSize(_scaledMinMaxSize.minSize, _scaledMinMaxSize.maxSize);
if(!skipResize) if(!skipResize)
Resize(oldScaledSize, true, PlatformResizeReason.DpiChange); Resize(oldScaledSize, true, WindowResizeReason.DpiChange);
return true; return true;
} }
@ -642,7 +642,7 @@ namespace Avalonia.X11
{ {
// Occurs once the window has been mapped, which is the earliest the extents // Occurs once the window has been mapped, which is the earliest the extents
// can be retrieved, so invoke event to force update of TopLevel.FrameSize. // can be retrieved, so invoke event to force update of TopLevel.FrameSize.
Resized?.Invoke(ClientSize, PlatformResizeReason.Unspecified); Resized?.Invoke(ClientSize, WindowResizeReason.Unspecified);
} }
if (atom == _x11.Atoms._NET_WM_STATE) if (atom == _x11.Atoms._NET_WM_STATE)
@ -959,19 +959,19 @@ namespace Avalonia.X11
} }
public void Resize(Size clientSize, PlatformResizeReason reason) => Resize(clientSize, false, reason); public void Resize(Size clientSize, WindowResizeReason reason) => Resize(clientSize, false, reason);
public void Move(PixelPoint point) => Position = point; public void Move(PixelPoint point) => Position = point;
private void MoveResize(PixelPoint position, Size size, double scaling) private void MoveResize(PixelPoint position, Size size, double scaling)
{ {
Move(position); Move(position);
_scalingOverride = scaling; _scalingOverride = scaling;
UpdateScaling(true); UpdateScaling(true);
Resize(size, true, PlatformResizeReason.Layout); Resize(size, true, WindowResizeReason.Layout);
} }
private PixelSize ToPixelSize(Size size) => new PixelSize((int)(size.Width * RenderScaling), (int)(size.Height * RenderScaling)); private PixelSize ToPixelSize(Size size) => new PixelSize((int)(size.Width * RenderScaling), (int)(size.Height * RenderScaling));
private void Resize(Size clientSize, bool force, PlatformResizeReason reason) private void Resize(Size clientSize, bool force, WindowResizeReason reason)
{ {
if (!force && clientSize == ClientSize) if (!force && clientSize == ClientSize)
return; return;

4
src/Browser/Avalonia.Browser/BrowserTopLevelImpl.cs

@ -74,7 +74,7 @@ namespace Avalonia.Browser
surface.Size = new PixelSize((int)newSize.Width, (int)newSize.Height); surface.Size = new PixelSize((int)newSize.Width, (int)newSize.Height);
} }
Resized?.Invoke(newSize, PlatformResizeReason.User); Resized?.Invoke(newSize, WindowResizeReason.User);
(_insetsManager as BrowserInsetsManager)?.NotifySafeAreaPaddingChanged(); (_insetsManager as BrowserInsetsManager)?.NotifySafeAreaPaddingChanged();
} }
@ -241,7 +241,7 @@ namespace Avalonia.Browser
public Action<string>? SetCssCursor { get; set; } public Action<string>? SetCssCursor { get; set; }
public Action<RawInputEventArgs>? Input { get; set; } public Action<RawInputEventArgs>? Input { get; set; }
public Action<Rect>? Paint { get; set; } public Action<Rect>? Paint { get; set; }
public Action<Size, PlatformResizeReason>? Resized { get; set; } public Action<Size, WindowResizeReason>? Resized { get; set; }
public Action<double>? ScalingChanged { get; set; } public Action<double>? ScalingChanged { get; set; }
public Action<WindowTransparencyLevel>? TransparencyLevelChanged { get; set; } public Action<WindowTransparencyLevel>? TransparencyLevelChanged { get; set; }
public Action? Closed { get; set; } public Action? Closed { get; set; }

6
src/Avalonia.Headless.Vnc/Avalonia.Headless.Vnc.csproj → src/Headless/Avalonia.Headless.Vnc/Avalonia.Headless.Vnc.csproj

@ -2,6 +2,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net6.0;netstandard2.0</TargetFrameworks> <TargetFrameworks>net6.0;netstandard2.0</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@ -9,5 +10,8 @@
<PackageReference Include="Quamotion.RemoteViewing" Version="1.1.21" /> <PackageReference Include="Quamotion.RemoteViewing" Version="1.1.21" />
</ItemGroup> </ItemGroup>
<Import Project="..\..\build\TrimmingEnable.props" /> <Import Project="..\..\..\build\ApiDiff.props" />
<Import Project="..\..\..\build\DevAnalyzers.props" />
<Import Project="..\..\..\build\TrimmingEnable.props" />
<Import Project="..\..\..\build\NullableEnable.props" />
</Project> </Project>

36
src/Avalonia.Headless.Vnc/HeadlessVncFramebufferSource.cs → src/Headless/Avalonia.Headless.Vnc/HeadlessVncFramebufferSource.cs

@ -2,6 +2,7 @@
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Platform;
using Avalonia.Threading; using Avalonia.Threading;
using RemoteViewing.Vnc; using RemoteViewing.Vnc;
using RemoteViewing.Vnc.Server; using RemoteViewing.Vnc.Server;
@ -10,22 +11,28 @@ namespace Avalonia.Headless.Vnc
{ {
public class HeadlessVncFramebufferSource : IVncFramebufferSource public class HeadlessVncFramebufferSource : IVncFramebufferSource
{ {
public IHeadlessWindow Window { get; set; } public Window Window { get; set; }
private object _lock = new object(); private object _lock = new object();
public VncFramebuffer _framebuffer = new VncFramebuffer("Avalonia", 1, 1, VncPixelFormat.RGB32); public VncFramebuffer _framebuffer = new VncFramebuffer("Avalonia", 1, 1, VncPixelFormat.RGB32);
private VncButton _previousButtons; private VncButton _previousButtons;
public HeadlessVncFramebufferSource(VncServerSession session, Window window) public HeadlessVncFramebufferSource(VncServerSession session, Window window)
{ {
Window = (IHeadlessWindow)window.PlatformImpl; Window = window;
session.PointerChanged += (_, args) => session.PointerChanged += (_, args) =>
{ {
var pt = new Point(args.X, args.Y); var pt = new Point(args.X, args.Y);
var buttons = (VncButton)args.PressedButtons; var buttons = (VncButton)args.PressedButtons;
int TranslateButton(VncButton vncButton) => MouseButton TranslateButton(VncButton vncButton) =>
vncButton == VncButton.Left ? 0 : vncButton == VncButton.Right ? 1 : 2; vncButton switch
{
VncButton.Left => MouseButton.Left,
VncButton.Middle => MouseButton.Middle,
VncButton.Right => MouseButton.Right,
_ => MouseButton.None
};
var modifiers = (RawInputModifiers)(((int)buttons & 7) << 4); var modifiers = (RawInputModifiers)(((int)buttons & 7) << 4);
@ -58,34 +65,25 @@ namespace Avalonia.Headless.Vnc
private static VncButton[] CheckedButtons = new[] {VncButton.Left, VncButton.Middle, VncButton.Right}; private static VncButton[] CheckedButtons = new[] {VncButton.Left, VncButton.Middle, VncButton.Right};
public VncFramebuffer Capture() public unsafe VncFramebuffer Capture()
{ {
lock (_lock) lock (_lock)
{ {
using (var bmpRef = Window.GetLastRenderedFrame()) using (var bmpRef = Window.GetLastRenderedFrame())
{ {
if (bmpRef?.Item == null) if (bmpRef == null)
return _framebuffer; return _framebuffer;
var bmp = bmpRef.Item; var bmp = bmpRef;
if (bmp.PixelSize.Width != _framebuffer.Width || bmp.PixelSize.Height != _framebuffer.Height) if (bmp.PixelSize.Width != _framebuffer.Width || bmp.PixelSize.Height != _framebuffer.Height)
{ {
_framebuffer = new VncFramebuffer("Avalonia", bmp.PixelSize.Width, bmp.PixelSize.Height, _framebuffer = new VncFramebuffer("Avalonia", bmp.PixelSize.Width, bmp.PixelSize.Height,
VncPixelFormat.RGB32); VncPixelFormat.RGB32);
} }
using (var fb = bmp.Lock()) var buffer = _framebuffer.GetBuffer();
fixed (byte* bufferPtr = buffer)
{ {
var buf = _framebuffer.GetBuffer(); bmp.CopyPixels(new PixelRect(default, bmp.PixelSize), (IntPtr)bufferPtr, buffer.Length, _framebuffer.Stride);
if (_framebuffer.Stride == fb.RowBytes)
Marshal.Copy(fb.Address, buf, 0, buf.Length);
else
for (var y = 0; y < fb.Size.Height; y++)
{
var sourceStart = fb.RowBytes * y;
var dstStart = _framebuffer.Stride * y;
var row = fb.Size.Width * 4;
Marshal.Copy(new IntPtr(sourceStart + fb.Address.ToInt64()), buf, dstStart, row);
}
} }
} }
} }

5
src/Avalonia.Headless.Vnc/HeadlessVncPlatformExtensions.cs → src/Headless/Avalonia.Headless.Vnc/HeadlessVncPlatformExtensions.cs

@ -1,3 +1,4 @@
using System;
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using Avalonia.Controls; using Avalonia.Controls;
@ -25,7 +26,7 @@ namespace Avalonia
}) })
.AfterSetup(_ => .AfterSetup(_ =>
{ {
var lt = ((IClassicDesktopStyleApplicationLifetime)builder.Instance.ApplicationLifetime); var lt = ((IClassicDesktopStyleApplicationLifetime)builder.Instance!.ApplicationLifetime!);
lt.Startup += async delegate lt.Startup += async delegate
{ {
while (true) while (true)
@ -38,7 +39,7 @@ namespace Avalonia
var session = new VncServerSession(); var session = new VncServerSession();
session.SetFramebufferSource(new HeadlessVncFramebufferSource( session.SetFramebufferSource(new HeadlessVncFramebufferSource(
session, lt.MainWindow)); session, lt.MainWindow ?? throw new InvalidOperationException("MainWindow wasn't initialized")));
session.Connect(client.GetStream(), options); session.Connect(client.GetStream(), options);
} }

19
src/Headless/Avalonia.Headless.XUnit/Avalonia.Headless.XUnit.csproj

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="xunit.core" Version="2.4.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Avalonia.Headless\Avalonia.Headless.csproj" />
</ItemGroup>
<Import Project="..\..\..\build\ApiDiff.props" />
<Import Project="..\..\..\build\DevAnalyzers.props" />
<Import Project="..\..\..\build\NullableEnable.props" />
</Project>

35
src/Headless/Avalonia.Headless.XUnit/AvaloniaTestFramework.cs

@ -0,0 +1,35 @@
using System.Reflection;
using Xunit.Abstractions;
using Xunit.Sdk;
namespace Avalonia.Headless.XUnit;
internal class AvaloniaTestFramework<TAppBuilderEntry> : XunitTestFramework
{
public AvaloniaTestFramework(IMessageSink messageSink) : base(messageSink)
{
}
protected override ITestFrameworkExecutor CreateExecutor(AssemblyName assemblyName)
=> new Executor(assemblyName, SourceInformationProvider, DiagnosticMessageSink);
private class Executor : XunitTestFrameworkExecutor
{
public Executor(AssemblyName assemblyName, ISourceInformationProvider sourceInformationProvider,
IMessageSink diagnosticMessageSink) : base(assemblyName, sourceInformationProvider,
diagnosticMessageSink)
{
}
protected override async void RunTestCases(IEnumerable<IXunitTestCase> testCases,
IMessageSink executionMessageSink,
ITestFrameworkExecutionOptions executionOptions)
{
executionOptions.SetValue("xunit.execution.DisableParallelization", false);
using (var assemblyRunner = new AvaloniaTestRunner<TAppBuilderEntry>(
TestAssembly, testCases, DiagnosticMessageSink, executionMessageSink,
executionOptions)) await assemblyRunner.RunAsync();
}
}
}

45
src/Headless/Avalonia.Headless.XUnit/AvaloniaTestFrameworkAttribute.cs

@ -0,0 +1,45 @@
using System.Diagnostics.CodeAnalysis;
using Xunit.Abstractions;
using Xunit.Sdk;
namespace Avalonia.Headless.XUnit;
/// <summary>
/// Sets up global avalonia test framework using avalonia application builder passed as a parameter.
/// </summary>
[TestFrameworkDiscoverer("Avalonia.Headless.XUnit.AvaloniaTestFrameworkTypeDiscoverer", "Avalonia.Headless.XUnit")]
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)]
public sealed class AvaloniaTestFrameworkAttribute : Attribute, ITestFrameworkAttribute
{
/// <summary>
/// Creates instance of <see cref="AvaloniaTestFrameworkAttribute"/>.
/// </summary>
/// <param name="appBuilderEntryPointType">
/// Parameter from which <see cref="AppBuilder"/> should be created.
/// It either needs to have BuildAvaloniaApp -> AppBuilder method or inherit Application.
/// </param>
public AvaloniaTestFrameworkAttribute(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
Type appBuilderEntryPointType) { }
}
/// <summary>
/// Discoverer implementation for the Avalonia testing framework.
/// </summary>
public class AvaloniaTestFrameworkTypeDiscoverer : ITestFrameworkTypeDiscoverer
{
/// <summary>
/// Creates instance of <see cref="AvaloniaTestFrameworkTypeDiscoverer"/>.
/// </summary>
public AvaloniaTestFrameworkTypeDiscoverer(IMessageSink _)
{
}
/// <inheritdoc/>
public Type GetTestFrameworkType(IAttributeInfo attribute)
{
var builderType = attribute.GetConstructorArguments().First() as Type
?? throw new InvalidOperationException("AppBuilderEntryPointType parameter must be defined on the AvaloniaTestFrameworkAttribute attribute.");
return typeof(AvaloniaTestFramework<>).MakeGenericType(builderType);
}
}

61
src/Headless/Avalonia.Headless.XUnit/AvaloniaTestRunner.cs

@ -0,0 +1,61 @@
using Avalonia.Threading;
using Xunit.Abstractions;
using Xunit.Sdk;
namespace Avalonia.Headless.XUnit;
internal class AvaloniaTestRunner<TAppBuilderEntry> : XunitTestAssemblyRunner
{
private CancellationTokenSource? _cancellationTokenSource;
public AvaloniaTestRunner(ITestAssembly testAssembly, IEnumerable<IXunitTestCase> testCases,
IMessageSink diagnosticMessageSink, IMessageSink executionMessageSink,
ITestFrameworkExecutionOptions executionOptions) : base(testAssembly, testCases, diagnosticMessageSink,
executionMessageSink, executionOptions)
{
}
protected override void SetupSyncContext(int maxParallelThreads)
{
_cancellationTokenSource?.Dispose();
_cancellationTokenSource = new CancellationTokenSource();
SynchronizationContext.SetSynchronizationContext(InitNewApplicationContext(_cancellationTokenSource.Token).Result);
}
public override void Dispose()
{
_cancellationTokenSource?.Cancel();
base.Dispose();
}
internal static Task<SynchronizationContext> InitNewApplicationContext(CancellationToken cancellationToken)
{
var tcs = new TaskCompletionSource<SynchronizationContext>();
new Thread(() =>
{
try
{
var appBuilder = AppBuilder.Configure(typeof(TAppBuilderEntry));
// If windowing subsystem wasn't initialized by user, force headless with default parameters.
if (appBuilder.WindowingSubsystemName != "Headless")
{
appBuilder = appBuilder.UseHeadless(new AvaloniaHeadlessPlatformOptions());
}
appBuilder.SetupWithoutStarting();
tcs.SetResult(SynchronizationContext.Current!);
}
catch (Exception e)
{
tcs.SetException(e);
}
Dispatcher.UIThread.MainLoop(cancellationToken);
}) { IsBackground = true }.Start();
return tcs.Task;
}
}

18
src/Headless/Avalonia.Headless/Avalonia.Headless.csproj

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net6.0;netstandard2.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\packages\Avalonia\Avalonia.csproj" />
</ItemGroup>
<Import Project="..\..\..\build\ApiDiff.props" />
<Import Project="..\..\..\build\DevAnalyzers.props" />
<Import Project="..\..\..\build\TrimmingEnable.props" />
<Import Project="..\..\..\build\NullableEnable.props" />
<ItemGroup Label="InternalsVisibleTo">
<InternalsVisibleTo Include="Avalonia.Headless.Vnc, PublicKey=$(AvaloniaPublicKey)" />
</ItemGroup>
</Project>

20
src/Avalonia.Headless/AvaloniaHeadlessPlatform.cs → src/Headless/Avalonia.Headless/AvaloniaHeadlessPlatform.cs

@ -1,8 +1,7 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using Avalonia.Reactive;
using Avalonia.Controls;
using Avalonia.Controls.Platform; using Avalonia.Controls.Platform;
using Avalonia.Reactive;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Input.Platform; using Avalonia.Input.Platform;
using Avalonia.Platform; using Avalonia.Platform;
@ -14,11 +13,12 @@ namespace Avalonia.Headless
{ {
public static class AvaloniaHeadlessPlatform public static class AvaloniaHeadlessPlatform
{ {
internal static Compositor Compositor { get; private set; } internal static Compositor? Compositor { get; private set; }
class RenderTimer : DefaultRenderTimer
private class RenderTimer : DefaultRenderTimer
{ {
private readonly int _framesPerSecond; private readonly int _framesPerSecond;
private Action _forceTick; private Action? _forceTick;
protected override IDisposable StartCore(Action<TimeSpan> tick) protected override IDisposable StartCore(Action<TimeSpan> tick)
{ {
bool cancelled = false; bool cancelled = false;
@ -48,7 +48,7 @@ namespace Avalonia.Headless
public void ForceTick() => _forceTick?.Invoke(); public void ForceTick() => _forceTick?.Invoke();
} }
class HeadlessWindowingPlatform : IWindowingPlatform private class HeadlessWindowingPlatform : IWindowingPlatform
{ {
public IWindowImpl CreateWindow() => new HeadlessWindowImpl(false); public IWindowImpl CreateWindow() => new HeadlessWindowImpl(false);
@ -56,7 +56,7 @@ namespace Avalonia.Headless
public IPopupImpl CreatePopup() => new HeadlessWindowImpl(true); public IPopupImpl CreatePopup() => new HeadlessWindowImpl(true);
public ITrayIconImpl CreateTrayIcon() => null; public ITrayIconImpl? CreateTrayIcon() => null;
} }
internal static void Initialize(AvaloniaHeadlessPlatformOptions opts) internal static void Initialize(AvaloniaHeadlessPlatformOptions opts)
@ -75,7 +75,11 @@ namespace Avalonia.Headless
Compositor = new Compositor(AvaloniaLocator.Current.GetRequiredService<IRenderLoop>(), null); Compositor = new Compositor(AvaloniaLocator.Current.GetRequiredService<IRenderLoop>(), null);
} }
/// <summary>
/// Forces renderer to process a rendering timer tick.
/// Use this method before calling <see cref="HeadlessWindowExtensions.GetLastRenderedFrame"/>.
/// </summary>
/// <param name="count">Count of frames to be ticked on the timer.</param>
public static void ForceRenderTimerTick(int count = 1) public static void ForceRenderTimerTick(int count = 1)
{ {
var timer = AvaloniaLocator.Current.GetService<IRenderTimer>() as RenderTimer; var timer = AvaloniaLocator.Current.GetService<IRenderTimer>() as RenderTimer;

46
src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs → src/Headless/Avalonia.Headless/HeadlessPlatformRenderInterface.cs

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO; using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Avalonia.Media; using Avalonia.Media;
@ -16,12 +17,13 @@ namespace Avalonia.Headless
public static void Initialize() public static void Initialize()
{ {
AvaloniaLocator.CurrentMutable AvaloniaLocator.CurrentMutable
.Bind<IPlatformRenderInterface>().ToConstant(new HeadlessPlatformRenderInterface()); .Bind<IPlatformRenderInterface>().ToConstant(new HeadlessPlatformRenderInterface())
.Bind<IFontManagerImpl>().ToConstant(new HeadlessFontManagerStub());
} }
public IEnumerable<string> InstalledFontNames { get; } = new[] { "Tahoma" }; public IEnumerable<string> InstalledFontNames { get; } = new[] { "Tahoma" };
public IPlatformRenderInterfaceContext CreateBackendContext(IPlatformGraphicsContext graphicsContext) => this; public IPlatformRenderInterfaceContext CreateBackendContext(IPlatformGraphicsContext? graphicsContext) => this;
public bool SupportsIndividualRoundRects => false; public bool SupportsIndividualRoundRects => false;
@ -50,7 +52,7 @@ namespace Avalonia.Headless
public IRenderTarget CreateRenderTarget(IEnumerable<object> surfaces) => new HeadlessRenderTarget(); public IRenderTarget CreateRenderTarget(IEnumerable<object> surfaces) => new HeadlessRenderTarget();
public bool IsLost => false; public bool IsLost => false;
public object TryGetFeature(Type featureType) => null; public object? TryGetFeature(Type featureType) => null;
public IRenderTargetBitmapImpl CreateRenderTargetBitmap(PixelSize size, Vector dpi) public IRenderTargetBitmapImpl CreateRenderTargetBitmap(PixelSize size, Vector dpi)
{ {
@ -129,7 +131,7 @@ namespace Avalonia.Headless
return new HeadlessGlyphRunStub(); return new HeadlessGlyphRunStub();
} }
class HeadlessGlyphRunStub : IGlyphRunImpl private class HeadlessGlyphRunStub : IGlyphRunImpl
{ {
public Rect Bounds => new Rect(new Size(8, 12)); public Rect Bounds => new Rect(new Size(8, 12));
@ -147,7 +149,7 @@ namespace Avalonia.Headless
=> Array.Empty<float>(); => Array.Empty<float>();
} }
class HeadlessGeometryStub : IGeometryImpl private class HeadlessGeometryStub : IGeometryImpl
{ {
public HeadlessGeometryStub(Rect bounds) public HeadlessGeometryStub(Rect bounds)
{ {
@ -160,7 +162,7 @@ namespace Avalonia.Headless
public virtual bool FillContains(Point point) => Bounds.Contains(point); public virtual bool FillContains(Point point) => Bounds.Contains(point);
public Rect GetRenderBounds(IPen pen) public Rect GetRenderBounds(IPen? pen)
{ {
if(pen is null) if(pen is null)
{ {
@ -170,7 +172,7 @@ namespace Avalonia.Headless
return Bounds.Inflate(pen.Thickness / 2); return Bounds.Inflate(pen.Thickness / 2);
} }
public bool StrokeContains(IPen pen, Point point) public bool StrokeContains(IPen? pen, Point point)
{ {
return false; return false;
} }
@ -194,21 +196,21 @@ namespace Avalonia.Headless
return false; return false;
} }
public bool TryGetSegment(double startDistance, double stopDistance, bool startOnBeginFigure, out IGeometryImpl segmentGeometry) public bool TryGetSegment(double startDistance, double stopDistance, bool startOnBeginFigure, [NotNullWhen(true)] out IGeometryImpl? segmentGeometry)
{ {
segmentGeometry = null; segmentGeometry = null;
return false; return false;
} }
} }
class HeadlessTransformedGeometryStub : HeadlessGeometryStub, ITransformedGeometryImpl private class HeadlessTransformedGeometryStub : HeadlessGeometryStub, ITransformedGeometryImpl
{ {
public HeadlessTransformedGeometryStub(IGeometryImpl b, Matrix transform) : this(Fix(b, transform)) public HeadlessTransformedGeometryStub(IGeometryImpl b, Matrix transform) : this(Fix(b, transform))
{ {
} }
static (IGeometryImpl, Matrix, Rect) Fix(IGeometryImpl b, Matrix transform) private static (IGeometryImpl, Matrix, Rect) Fix(IGeometryImpl b, Matrix transform)
{ {
if (b is HeadlessTransformedGeometryStub transformed) if (b is HeadlessTransformedGeometryStub transformed)
{ {
@ -230,7 +232,7 @@ namespace Avalonia.Headless
public Matrix Transform { get; } public Matrix Transform { get; }
} }
class HeadlessStreamingGeometryStub : HeadlessGeometryStub, IStreamGeometryImpl private class HeadlessStreamingGeometryStub : HeadlessGeometryStub, IStreamGeometryImpl
{ {
public HeadlessStreamingGeometryStub() : base(default) public HeadlessStreamingGeometryStub() : base(default)
{ {
@ -246,7 +248,7 @@ namespace Avalonia.Headless
return new HeadlessStreamingGeometryContextStub(this); return new HeadlessStreamingGeometryContextStub(this);
} }
class HeadlessStreamingGeometryContextStub : IStreamGeometryContextImpl private class HeadlessStreamingGeometryContextStub : IStreamGeometryContextImpl
{ {
private readonly HeadlessStreamingGeometryStub _parent; private readonly HeadlessStreamingGeometryStub _parent;
private double _x1, _y1, _x2, _y2; private double _x1, _y1, _x2, _y2;
@ -255,7 +257,7 @@ namespace Avalonia.Headless
_parent = parent; _parent = parent;
} }
void Track(Point pt) private void Track(Point pt)
{ {
if (_x1 > pt.X) if (_x1 > pt.X)
_x1 = pt.X; _x1 = pt.X;
@ -304,7 +306,7 @@ namespace Avalonia.Headless
} }
} }
class HeadlessBitmapStub : IBitmapImpl, IDrawingContextLayerImpl, IWriteableBitmapImpl private class HeadlessBitmapStub : IBitmapImpl, IDrawingContextLayerImpl, IWriteableBitmapImpl
{ {
public Size Size { get; } public Size Size { get; }
@ -366,7 +368,7 @@ namespace Avalonia.Headless
} }
} }
class HeadlessDrawingContextStub : IDrawingContextImpl private class HeadlessDrawingContextStub : IDrawingContextImpl
{ {
public void Dispose() public void Dispose()
{ {
@ -442,16 +444,16 @@ namespace Avalonia.Headless
} }
public object GetFeature(Type t) public object? GetFeature(Type t)
{ {
return null; return null;
} }
public void DrawLine(IPen pen, Point p1, Point p2) public void DrawLine(IPen? pen, Point p1, Point p2)
{ {
} }
public void DrawGeometry(IBrush brush, IPen pen, IGeometryImpl geometry) public void DrawGeometry(IBrush? brush, IPen? pen, IGeometryImpl geometry)
{ {
} }
@ -469,16 +471,16 @@ namespace Avalonia.Headless
} }
public void DrawRectangle(IBrush brush, IPen pen, RoundedRect rect, BoxShadows boxShadow = default) public void DrawRectangle(IBrush? brush, IPen? pen, RoundedRect rect, BoxShadows boxShadow = default)
{ {
} }
public void DrawEllipse(IBrush brush, IPen pen, Rect rect) public void DrawEllipse(IBrush? brush, IPen? pen, Rect rect)
{ {
} }
public void DrawGlyphRun(IBrush foreground, IRef<IGlyphRunImpl> glyphRun) public void DrawGlyphRun(IBrush? foreground, IRef<IGlyphRunImpl> glyphRun)
{ {
} }
@ -489,7 +491,7 @@ namespace Avalonia.Headless
} }
} }
class HeadlessRenderTarget : IRenderTarget private class HeadlessRenderTarget : IRenderTarget
{ {
public void Dispose() public void Dispose()
{ {

73
src/Avalonia.Headless/HeadlessPlatformStubs.cs → src/Headless/Avalonia.Headless/HeadlessPlatformStubs.cs

@ -18,17 +18,17 @@ using Avalonia.Utilities;
namespace Avalonia.Headless namespace Avalonia.Headless
{ {
class HeadlessClipboardStub : IClipboard internal class HeadlessClipboardStub : IClipboard
{ {
private string _text; private string? _text;
private IDataObject _data; private IDataObject? _data;
public Task<string> GetTextAsync() public Task<string?> GetTextAsync()
{ {
return Task.Run(() => _text); return Task.Run(() => _text);
} }
public Task SetTextAsync(string text) public Task SetTextAsync(string? text)
{ {
return Task.Run(() => _text = text); return Task.Run(() => _text = text);
} }
@ -45,16 +45,29 @@ namespace Avalonia.Headless
public Task<string[]> GetFormatsAsync() public Task<string[]> GetFormatsAsync()
{ {
throw new NotImplementedException(); return Task.Run(() =>
{
if (_data is not null)
{
return _data.GetDataFormats().ToArray();
}
if (_text is not null)
{
return new[] { DataFormats.Text };
}
return Array.Empty<string>();
});
} }
public async Task<object> GetDataAsync(string format) public async Task<object?> GetDataAsync(string format)
{ {
return await Task.Run(() => _data); return await Task.Run(() => _data);
} }
} }
class HeadlessCursorFactoryStub : ICursorFactory internal class HeadlessCursorFactoryStub : ICursorFactory
{ {
public ICursorImpl GetCursor(StandardCursorType cursorType) => new CursorStub(); public ICursorImpl GetCursor(StandardCursorType cursorType) => new CursorStub();
public ICursorImpl CreateCursor(IBitmapImpl cursor, PixelPoint hotSpot) => new CursorStub(); public ICursorImpl CreateCursor(IBitmapImpl cursor, PixelPoint hotSpot) => new CursorStub();
@ -65,7 +78,7 @@ namespace Avalonia.Headless
} }
} }
class HeadlessGlyphTypefaceImpl : IGlyphTypeface internal class HeadlessGlyphTypefaceImpl : IGlyphTypeface
{ {
public FontMetrics Metrics => new FontMetrics public FontMetrics Metrics => new FontMetrics
{ {
@ -125,7 +138,7 @@ namespace Avalonia.Headless
public bool TryGetTable(uint tag, out byte[] table) public bool TryGetTable(uint tag, out byte[] table)
{ {
table = null; table = null!;
return false; return false;
} }
@ -141,7 +154,7 @@ namespace Avalonia.Headless
} }
} }
class HeadlessTextShaperStub : ITextShaperImpl internal class HeadlessTextShaperStub : ITextShaperImpl
{ {
public ShapedBuffer ShapeText(ReadOnlyMemory<char> text, TextShaperOptions options) public ShapedBuffer ShapeText(ReadOnlyMemory<char> text, TextShaperOptions options)
{ {
@ -153,7 +166,7 @@ namespace Avalonia.Headless
} }
} }
class HeadlessFontManagerStub : IFontManagerImpl internal class HeadlessFontManagerStub : IFontManagerImpl
{ {
public string GetDefaultFontFamilyName() public string GetDefaultFontFamilyName()
{ {
@ -179,17 +192,16 @@ namespace Avalonia.Headless
return true; return true;
} }
public bool TryMatchCharacter(int codepoint, FontStyle fontStyle, FontWeight fontWeight, FontStretch fontStretch, CultureInfo culture, out Typeface typeface) public bool TryMatchCharacter(int codepoint, FontStyle fontStyle, FontWeight fontWeight, FontStretch fontStretch, CultureInfo? culture, out Typeface typeface)
{ {
typeface = new Typeface("Arial", fontStyle, fontWeight, fontStretch); typeface = new Typeface("Arial", fontStyle, fontWeight, fontStretch);
return true; return true;
} }
} }
class HeadlessIconLoaderStub : IPlatformIconLoader internal class HeadlessIconLoaderStub : IPlatformIconLoader
{ {
private class IconStub : IWindowIconImpl
class IconStub : IWindowIconImpl
{ {
public void Save(Stream outputStream) public void Save(Stream outputStream)
{ {
@ -212,7 +224,7 @@ namespace Avalonia.Headless
} }
} }
class HeadlessScreensStub : IScreenImpl internal class HeadlessScreensStub : IScreenImpl
{ {
public int ScreenCount { get; } = 1; public int ScreenCount { get; } = 1;
@ -222,40 +234,19 @@ namespace Avalonia.Headless
new PixelRect(0, 0, 1920, 1280), true), new PixelRect(0, 0, 1920, 1280), true),
}; };
public Screen ScreenFromPoint(PixelPoint point) public Screen? ScreenFromPoint(PixelPoint point)
{ {
return ScreenHelper.ScreenFromPoint(point, AllScreens); return ScreenHelper.ScreenFromPoint(point, AllScreens);
} }
public Screen ScreenFromRect(PixelRect rect) public Screen? ScreenFromRect(PixelRect rect)
{ {
return ScreenHelper.ScreenFromRect(rect, AllScreens); return ScreenHelper.ScreenFromRect(rect, AllScreens);
} }
public Screen ScreenFromWindow(IWindowBaseImpl window) public Screen? ScreenFromWindow(IWindowBaseImpl window)
{ {
return ScreenHelper.ScreenFromWindow(window, AllScreens); return ScreenHelper.ScreenFromWindow(window, AllScreens);
} }
} }
internal class NoopStorageProvider : BclStorageProvider
{
public override bool CanOpen => false;
public override Task<IReadOnlyList<IStorageFile>> OpenFilePickerAsync(FilePickerOpenOptions options)
{
return Task.FromResult<IReadOnlyList<IStorageFile>>(Array.Empty<IStorageFile>());
}
public override bool CanSave => false;
public override Task<IStorageFile> SaveFilePickerAsync(FilePickerSaveOptions options)
{
return Task.FromResult<IStorageFile>(null);
}
public override bool CanPickFolder => false;
public override Task<IReadOnlyList<IStorageFolder>> OpenFolderPickerAsync(FolderPickerOpenOptions options)
{
return Task.FromResult<IReadOnlyList<IStorageFolder>>(Array.Empty<IStorageFolder>());
}
}
} }

101
src/Headless/Avalonia.Headless/HeadlessWindowExtensions.cs

@ -0,0 +1,101 @@
using System;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Input.Raw;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
using Avalonia.Threading;
namespace Avalonia.Headless;
/// <summary>
/// Set of extension methods to simplify usage of Avalonia.Headless platform.
/// </summary>
public static class HeadlessWindowExtensions
{
/// <summary>
/// Triggers a renderer timer tick and captures last rendered frame.
/// </summary>
/// <returns>Bitmap with last rendered frame. Null, if nothing was rendered.</returns>
public static Bitmap? CaptureRenderedFrame(this TopLevel topLevel)
{
Dispatcher.UIThread.RunJobs();
AvaloniaHeadlessPlatform.ForceRenderTimerTick();
return topLevel.GetLastRenderedFrame();
}
/// <summary>
/// Reads last rendered frame.
/// Note, in order to trigger rendering timer, call <see cref="AvaloniaHeadlessPlatform.ForceRenderTimerTick"/> method.
/// </summary>
/// <returns>Bitmap with last rendered frame. Null, if nothing was rendered.</returns>
public static Bitmap? GetLastRenderedFrame(this TopLevel topLevel)
{
if (AvaloniaLocator.Current.GetService<IPlatformRenderInterface>() is HeadlessPlatformRenderInterface)
{
throw new NotSupportedException(
"To capture a rendered frame, make sure that headless application was initialized with '.UseSkia()' and disabled 'UseHeadlessDrawing' in the 'AvaloniaHeadlessPlatformOptions'.");
}
return GetImpl(topLevel).GetLastRenderedFrame();
}
/// <summary>
/// Simulates keyboard press on the headless window/toplevel.
/// </summary>
public static void KeyPress(this TopLevel topLevel, Key key, RawInputModifiers modifiers) =>
RunJobsAndGetImpl(topLevel).KeyPress(key, modifiers);
/// <summary>
/// Simulates keyboard release on the headless window/toplevel.
/// </summary>
public static void KeyRelease(this TopLevel topLevel, Key key, RawInputModifiers modifiers) =>
RunJobsAndGetImpl(topLevel).KeyRelease(key, modifiers);
/// <summary>
/// Simulates mouse down on the headless window/toplevel.
/// </summary>
public static void MouseDown(this TopLevel topLevel, Point point, MouseButton button,
RawInputModifiers modifiers = RawInputModifiers.None) =>
RunJobsAndGetImpl(topLevel).MouseDown(point, button, modifiers);
/// <summary>
/// Simulates mouse move on the headless window/toplevel.
/// </summary>
public static void MouseMove(this TopLevel topLevel, Point point,
RawInputModifiers modifiers = RawInputModifiers.None) =>
RunJobsAndGetImpl(topLevel).MouseMove(point, modifiers);
/// <summary>
/// Simulates mouse up on the headless window/toplevel.
/// </summary>
public static void MouseUp(this TopLevel topLevel, Point point, MouseButton button,
RawInputModifiers modifiers = RawInputModifiers.None) =>
RunJobsAndGetImpl(topLevel).MouseUp(point, button, modifiers);
/// <summary>
/// Simulates mouse wheel on the headless window/toplevel.
/// </summary>
public static void MouseWheel(this TopLevel topLevel, Point point, Vector delta,
RawInputModifiers modifiers = RawInputModifiers.None) =>
RunJobsAndGetImpl(topLevel).MouseWheel(point, delta, modifiers);
/// <summary>
/// Simulates drag'n'drop target on the headless window/toplevel.
/// </summary>
public static void DragDrop(this TopLevel topLevel, Point point, RawDragEventType type, IDataObject data,
DragDropEffects effects, RawInputModifiers modifiers = RawInputModifiers.None) =>
RunJobsAndGetImpl(topLevel).DragDrop(point, type, data, effects, modifiers);
private static IHeadlessWindow RunJobsAndGetImpl(this TopLevel topLevel)
{
Dispatcher.UIThread.RunJobs();
return GetImpl(topLevel);
}
private static IHeadlessWindow GetImpl(this TopLevel topLevel)
{
return topLevel.PlatformImpl as IHeadlessWindow ??
throw new InvalidOperationException("TopLevel must be a headless window.");
}
}

128
src/Avalonia.Headless/HeadlessWindowImpl.cs → src/Headless/Avalonia.Headless/HeadlessWindowImpl.cs

@ -17,20 +17,20 @@ using Avalonia.Utilities;
namespace Avalonia.Headless namespace Avalonia.Headless
{ {
class HeadlessWindowImpl : IWindowImpl, IPopupImpl, IFramebufferPlatformSurface, IHeadlessWindow internal class HeadlessWindowImpl : IWindowImpl, IPopupImpl, IFramebufferPlatformSurface, IHeadlessWindow
{ {
private IKeyboardDevice _keyboard; private readonly IKeyboardDevice _keyboard;
private Stopwatch _st = Stopwatch.StartNew(); private readonly Stopwatch _st = Stopwatch.StartNew();
private Pointer _mousePointer; private readonly Pointer _mousePointer;
private WriteableBitmap _lastRenderedFrame; private WriteableBitmap? _lastRenderedFrame;
private object _sync = new object(); private readonly object _sync = new object();
public bool IsPopup { get; } public bool IsPopup { get; }
public HeadlessWindowImpl(bool isPopup) public HeadlessWindowImpl(bool isPopup)
{ {
IsPopup = isPopup; IsPopup = isPopup;
Surfaces = new object[] { this }; Surfaces = new object[] { this };
_keyboard = AvaloniaLocator.Current.GetService<IKeyboardDevice>(); _keyboard = AvaloniaLocator.Current.GetRequiredService<IKeyboardDevice>();
_mousePointer = new Pointer(Pointer.GetNextFreeId(), PointerType.Mouse, true); _mousePointer = new Pointer(Pointer.GetNextFreeId(), PointerType.Mouse, true);
MouseDevice = new MouseDevice(_mousePointer); MouseDevice = new MouseDevice(_mousePointer);
ClientSize = new Size(1024, 768); ClientSize = new Size(1024, 768);
@ -48,13 +48,13 @@ namespace Avalonia.Headless
public double RenderScaling { get; } = 1; public double RenderScaling { get; } = 1;
public double DesktopScaling => RenderScaling; public double DesktopScaling => RenderScaling;
public IEnumerable<object> Surfaces { get; } public IEnumerable<object> Surfaces { get; }
public Action<RawInputEventArgs> Input { get; set; } public Action<RawInputEventArgs>? Input { get; set; }
public Action<Rect> Paint { get; set; } public Action<Rect>? Paint { get; set; }
public Action<Size, PlatformResizeReason> Resized { get; set; } public Action<Size, WindowResizeReason>? Resized { get; set; }
public Action<double> ScalingChanged { get; set; } public Action<double>? ScalingChanged { get; set; }
public IRenderer CreateRenderer(IRenderRoot root) => public IRenderer CreateRenderer(IRenderRoot root) =>
new CompositingRenderer(root, AvaloniaHeadlessPlatform.Compositor, () => Surfaces); new CompositingRenderer(root, AvaloniaHeadlessPlatform.Compositor!, () => Surfaces);
public void Invalidate(Rect rect) public void Invalidate(Rect rect)
{ {
@ -65,18 +65,18 @@ namespace Avalonia.Headless
InputRoot = inputRoot; InputRoot = inputRoot;
} }
public IInputRoot InputRoot { get; set; } public IInputRoot? InputRoot { get; set; }
public Point PointToClient(PixelPoint point) => point.ToPoint(RenderScaling); public Point PointToClient(PixelPoint point) => point.ToPoint(RenderScaling);
public PixelPoint PointToScreen(Point point) => PixelPoint.FromPoint(point, RenderScaling); public PixelPoint PointToScreen(Point point) => PixelPoint.FromPoint(point, RenderScaling);
public void SetCursor(ICursorImpl cursor) public void SetCursor(ICursorImpl? cursor)
{ {
} }
public Action Closed { get; set; } public Action? Closed { get; set; }
public IMouseDevice MouseDevice { get; } public IMouseDevice MouseDevice { get; }
public void Show(bool activate, bool isDialog) public void Show(bool activate, bool isDialog)
@ -101,17 +101,17 @@ namespace Avalonia.Headless
} }
public PixelPoint Position { get; set; } public PixelPoint Position { get; set; }
public Action<PixelPoint> PositionChanged { get; set; } public Action<PixelPoint>? PositionChanged { get; set; }
public void Activate() public void Activate()
{ {
Dispatcher.UIThread.Post(() => Activated?.Invoke(), DispatcherPriority.Input); Dispatcher.UIThread.Post(() => Activated?.Invoke(), DispatcherPriority.Input);
} }
public Action Deactivated { get; set; } public Action? Deactivated { get; set; }
public Action Activated { get; set; } public Action? Activated { get; set; }
public IPlatformHandle Handle { get; } = new PlatformHandle(IntPtr.Zero, "STUB"); public IPlatformHandle Handle { get; } = new PlatformHandle(IntPtr.Zero, "STUB");
public Size MaxClientSize { get; } = new Size(1920, 1280); public Size MaxClientSize { get; } = new Size(1920, 1280);
public void Resize(Size clientSize, PlatformResizeReason reason) public void Resize(Size clientSize, WindowResizeReason reason)
{ {
// Emulate X11 behavior here // Emulate X11 behavior here
if (IsPopup) if (IsPopup)
@ -123,13 +123,13 @@ namespace Avalonia.Headless
}); });
} }
void DoResize(Size clientSize) private void DoResize(Size clientSize)
{ {
// Uncomment this check and experience a weird bug in layout engine // Uncomment this check and experience a weird bug in layout engine
if (ClientSize != clientSize) if (ClientSize != clientSize)
{ {
ClientSize = clientSize; ClientSize = clientSize;
Resized?.Invoke(clientSize, PlatformResizeReason.Unspecified); Resized?.Invoke(clientSize, WindowResizeReason.Unspecified);
} }
} }
@ -145,8 +145,8 @@ namespace Avalonia.Headless
public IScreenImpl Screen { get; } = new HeadlessScreensStub(); public IScreenImpl Screen { get; } = new HeadlessScreensStub();
public WindowState WindowState { get; set; } public WindowState WindowState { get; set; }
public Action<WindowState> WindowStateChanged { get; set; } public Action<WindowState>? WindowStateChanged { get; set; }
public void SetTitle(string title) public void SetTitle(string? title)
{ {
} }
@ -156,7 +156,7 @@ namespace Avalonia.Headless
} }
public void SetIcon(IWindowIconImpl icon) public void SetIcon(IWindowIconImpl? icon)
{ {
} }
@ -171,9 +171,9 @@ namespace Avalonia.Headless
} }
public Func<WindowCloseReason, bool> Closing { get; set; } public Func<WindowCloseReason, bool>? Closing { get; set; }
class FramebufferProxy : ILockedFramebuffer private class FramebufferProxy : ILockedFramebuffer
{ {
private readonly ILockedFramebuffer _fb; private readonly ILockedFramebuffer _fb;
private readonly Action _onDispose; private readonly Action _onDispose;
@ -214,28 +214,37 @@ namespace Avalonia.Headless
}); });
} }
public IRef<IWriteableBitmapImpl> GetLastRenderedFrame() public Bitmap? GetLastRenderedFrame()
{ {
lock (_sync) lock (_sync)
return _lastRenderedFrame?.PlatformImpl?.CloneAs<IWriteableBitmapImpl>(); {
if (_lastRenderedFrame is null)
{
return null;
}
using var lockedFramebuffer = _lastRenderedFrame.Lock();
return new Bitmap(lockedFramebuffer.Format, AlphaFormat.Opaque, lockedFramebuffer.Address,
lockedFramebuffer.Size, lockedFramebuffer.Dpi, lockedFramebuffer.RowBytes);
}
} }
private ulong Timestamp => (ulong)_st.ElapsedMilliseconds; private ulong Timestamp => (ulong)_st.ElapsedMilliseconds;
// TODO: Hook recent Popup changes. // TODO: Hook recent Popup changes.
IPopupPositioner IPopupImpl.PopupPositioner => null; IPopupPositioner IPopupImpl.PopupPositioner => null!;
public Size MaxAutoSizeHint => new Size(1920, 1080); public Size MaxAutoSizeHint => new Size(1920, 1080);
public Action<WindowTransparencyLevel> TransparencyLevelChanged { get; set; } public Action<WindowTransparencyLevel>? TransparencyLevelChanged { get; set; }
public WindowTransparencyLevel TransparencyLevel => WindowTransparencyLevel.None; public WindowTransparencyLevel TransparencyLevel => WindowTransparencyLevel.None;
public Action GotInputWhenDisabled { get; set; } public Action? GotInputWhenDisabled { get; set; }
public bool IsClientAreaExtendedToDecorations => false; public bool IsClientAreaExtendedToDecorations => false;
public Action<bool> ExtendClientAreaToDecorationsChanged { get; set; } public Action<bool>? ExtendClientAreaToDecorationsChanged { get; set; }
public bool NeedsManagedDecorations => false; public bool NeedsManagedDecorations => false;
@ -243,17 +252,12 @@ namespace Avalonia.Headless
public Thickness OffScreenMargin => new Thickness(); public Thickness OffScreenMargin => new Thickness();
public Action LostFocus { get; set; } public Action? LostFocus { get; set; }
public AcrylicPlatformCompensationLevels AcrylicCompensationLevels => new AcrylicPlatformCompensationLevels(1, 1, 1); public AcrylicPlatformCompensationLevels AcrylicCompensationLevels => new AcrylicPlatformCompensationLevels(1, 1, 1);
public object TryGetFeature(Type featureType) public object? TryGetFeature(Type featureType)
{ {
if (featureType == typeof(IStorageProvider)) if(featureType == typeof(IClipboard))
{
return new NoopStorageProvider();
}
if(featureType == typeof(IClipboard))
{ {
return AvaloniaLocator.Current.GetRequiredService<IClipboard>(); return AvaloniaLocator.Current.GetRequiredService<IClipboard>();
} }
@ -263,46 +267,58 @@ namespace Avalonia.Headless
void IHeadlessWindow.KeyPress(Key key, RawInputModifiers modifiers) void IHeadlessWindow.KeyPress(Key key, RawInputModifiers modifiers)
{ {
Input?.Invoke(new RawKeyEventArgs(_keyboard, Timestamp, InputRoot, RawKeyEventType.KeyDown, key, modifiers)); Input?.Invoke(new RawKeyEventArgs(_keyboard, Timestamp, InputRoot!, RawKeyEventType.KeyDown, key, modifiers));
} }
void IHeadlessWindow.KeyRelease(Key key, RawInputModifiers modifiers) void IHeadlessWindow.KeyRelease(Key key, RawInputModifiers modifiers)
{ {
Input?.Invoke(new RawKeyEventArgs(_keyboard, Timestamp, InputRoot, RawKeyEventType.KeyUp, key, modifiers)); Input?.Invoke(new RawKeyEventArgs(_keyboard, Timestamp, InputRoot!, RawKeyEventType.KeyUp, key, modifiers));
} }
void IHeadlessWindow.MouseDown(Point point, int button, RawInputModifiers modifiers) void IHeadlessWindow.MouseDown(Point point, MouseButton button, RawInputModifiers modifiers)
{ {
Input?.Invoke(new RawPointerEventArgs(MouseDevice, Timestamp, InputRoot, Input?.Invoke(new RawPointerEventArgs(MouseDevice, Timestamp, InputRoot!,
button == 0 ? RawPointerEventType.LeftButtonDown : button switch
button == 1 ? RawPointerEventType.MiddleButtonDown : RawPointerEventType.RightButtonDown, {
point, modifiers)); MouseButton.Left => RawPointerEventType.LeftButtonDown,
MouseButton.Right => RawPointerEventType.RightButtonDown,
MouseButton.Middle => RawPointerEventType.MiddleButtonDown,
MouseButton.XButton1 => RawPointerEventType.XButton1Down,
MouseButton.XButton2 => RawPointerEventType.XButton2Down,
_ => RawPointerEventType.Move,
}, point, modifiers));
} }
void IHeadlessWindow.MouseMove(Point point, RawInputModifiers modifiers) void IHeadlessWindow.MouseMove(Point point, RawInputModifiers modifiers)
{ {
Input?.Invoke(new RawPointerEventArgs(MouseDevice, Timestamp, InputRoot, Input?.Invoke(new RawPointerEventArgs(MouseDevice, Timestamp, InputRoot!,
RawPointerEventType.Move, point, modifiers)); RawPointerEventType.Move, point, modifiers));
} }
void IHeadlessWindow.MouseUp(Point point, int button, RawInputModifiers modifiers) void IHeadlessWindow.MouseUp(Point point, MouseButton button, RawInputModifiers modifiers)
{ {
Input?.Invoke(new RawPointerEventArgs(MouseDevice, Timestamp, InputRoot, Input?.Invoke(new RawPointerEventArgs(MouseDevice, Timestamp, InputRoot!,
button == 0 ? RawPointerEventType.LeftButtonUp : button switch
button == 1 ? RawPointerEventType.MiddleButtonUp : RawPointerEventType.RightButtonUp, {
point, modifiers)); MouseButton.Left => RawPointerEventType.LeftButtonUp,
MouseButton.Right => RawPointerEventType.RightButtonUp,
MouseButton.Middle => RawPointerEventType.MiddleButtonUp,
MouseButton.XButton1 => RawPointerEventType.XButton1Up,
MouseButton.XButton2 => RawPointerEventType.XButton2Up,
_ => RawPointerEventType.Move,
}, point, modifiers));
} }
void IHeadlessWindow.MouseWheel(Point point, Vector delta, RawInputModifiers modifiers) void IHeadlessWindow.MouseWheel(Point point, Vector delta, RawInputModifiers modifiers)
{ {
Input?.Invoke(new RawMouseWheelEventArgs(MouseDevice, Timestamp, InputRoot, Input?.Invoke(new RawMouseWheelEventArgs(MouseDevice, Timestamp, InputRoot!,
point, delta, modifiers)); point, delta, modifiers));
} }
void IHeadlessWindow.DragDrop(Point point, RawDragEventType type, IDataObject data, DragDropEffects effects, RawInputModifiers modifiers) void IHeadlessWindow.DragDrop(Point point, RawDragEventType type, IDataObject data, DragDropEffects effects, RawInputModifiers modifiers)
{ {
var device = AvaloniaLocator.Current.GetRequiredService<IDragDropDevice>(); var device = AvaloniaLocator.Current.GetRequiredService<IDragDropDevice>();
Input?.Invoke(new RawDragEvent(device, type, InputRoot, point, data, effects, modifiers)); Input?.Invoke(new RawDragEvent(device, type, InputRoot!, point, data, effects, modifiers));
} }
void IWindowImpl.Move(PixelPoint point) void IWindowImpl.Move(PixelPoint point)
@ -310,7 +326,7 @@ namespace Avalonia.Headless
} }
public IPopupImpl CreatePopup() public IPopupImpl? CreatePopup()
{ {
// TODO: Hook recent Popup changes. // TODO: Hook recent Popup changes.
return null; return null;

10
src/Avalonia.Headless/IHeadlessWindow.cs → src/Headless/Avalonia.Headless/IHeadlessWindow.cs

@ -6,15 +6,15 @@ using Avalonia.Utilities;
namespace Avalonia.Headless namespace Avalonia.Headless
{ {
public interface IHeadlessWindow internal interface IHeadlessWindow
{ {
IRef<IWriteableBitmapImpl> GetLastRenderedFrame(); Bitmap? GetLastRenderedFrame();
void KeyPress(Key key, RawInputModifiers modifiers); void KeyPress(Key key, RawInputModifiers modifiers);
void KeyRelease(Key key, RawInputModifiers modifiers); void KeyRelease(Key key, RawInputModifiers modifiers);
void MouseDown(Point point, int button, RawInputModifiers modifiers = RawInputModifiers.None); void MouseDown(Point point, MouseButton button, RawInputModifiers modifiers = RawInputModifiers.None);
void MouseMove(Point point, RawInputModifiers modifiers = RawInputModifiers.None); void MouseMove(Point point, RawInputModifiers modifiers = RawInputModifiers.None);
void MouseUp(Point point, int button, RawInputModifiers modifiers = RawInputModifiers.None); void MouseUp(Point point, MouseButton button, RawInputModifiers modifiers = RawInputModifiers.None);
void MouseWheel(Point point, Vector delta, RawInputModifiers modifiers = RawInputModifiers.None); void MouseWheel(Point point, Vector delta, RawInputModifiers modifiers = RawInputModifiers.None);
void DragDrop(Point point, RawDragEventType type, IDataObject data, DragDropEffects effects, RawInputModifiers modifiers); void DragDrop(Point point, RawDragEventType type, IDataObject data, DragDropEffects effects, RawInputModifiers modifiers = RawInputModifiers.None);
} }
} }

2
src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs

@ -65,7 +65,7 @@ using Avalonia.Rendering.Composition;
public IEnumerable<object> Surfaces { get; } public IEnumerable<object> Surfaces { get; }
public Action<RawInputEventArgs> Input { get; set; } public Action<RawInputEventArgs> Input { get; set; }
public Action<Rect> Paint { get; set; } public Action<Rect> Paint { get; set; }
public Action<Size, PlatformResizeReason> Resized { get; set; } public Action<Size, WindowResizeReason> Resized { get; set; }
public Action<double> ScalingChanged { get; set; } public Action<double> ScalingChanged { get; set; }
public Action<WindowTransparencyLevel> TransparencyLevelChanged { get; set; } public Action<WindowTransparencyLevel> TransparencyLevelChanged { get; set; }

71
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlArrayConstantAstNode.cs

@ -0,0 +1,71 @@
using System.Collections.Generic;
using System.Reflection.Emit;
using Avalonia.Controls;
using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers;
using XamlX;
using XamlX.Ast;
using XamlX.Emit;
using XamlX.IL;
using XamlX.Transform;
using XamlX.TypeSystem;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.AstNodes
{
class AvaloniaXamlIlArrayConstantAstNode : XamlAstNode, IXamlAstValueNode, IXamlAstILEmitableNode
{
private readonly IXamlType _elementType;
private readonly IReadOnlyList<IXamlAstValueNode> _values;
public AvaloniaXamlIlArrayConstantAstNode(IXamlLineInfo lineInfo, IXamlType arrayType, IXamlType elementType, IReadOnlyList<IXamlAstValueNode> values) : base(lineInfo)
{
_elementType = elementType;
_values = values;
Type = new XamlAstClrTypeReference(lineInfo, arrayType, false);
foreach (var element in values)
{
if (!elementType.IsAssignableFrom(element.Type.GetClrType()))
{
throw new XamlParseException("x:Array element is not assignable to the array element type!", lineInfo);
}
}
}
public IXamlAstTypeReference Type { get; }
public XamlILNodeEmitResult Emit(XamlEmitContext<IXamlILEmitter, XamlILNodeEmitResult> context, IXamlILEmitter codeGen)
{
codeGen.Ldc_I4(_values.Count)
.Newarr(_elementType);
for (var index = 0; index < _values.Count; index++)
{
var value = _values[index];
codeGen
.Dup()
.Ldc_I4(index);
context.Emit(value, codeGen, _elementType);
if (value.Type.GetClrType() is { IsValueType: true } valTypeInObjArr)
{
if (!_elementType.IsValueType)
{
codeGen.Box(valTypeInObjArr);
}
// It seems like ASM codegen for "stelem valuetype" and "stelem.i4" is identical,
// so we don't need to try to optimize it here.
codeGen.Emit(OpCodes.Stelem, valTypeInObjArr);
}
else
{
codeGen.Stelem_ref();
}
}
return XamlILNodeEmitResult.Type(0, Type.GetClrType());
}
}
}

3
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs

@ -58,7 +58,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
new AvaloniaXamlIlSetterTransformer(), new AvaloniaXamlIlSetterTransformer(),
new AvaloniaXamlIlConstructorServiceProviderTransformer(), new AvaloniaXamlIlConstructorServiceProviderTransformer(),
new AvaloniaXamlIlTransitionsTypeMetadataTransformer(), new AvaloniaXamlIlTransitionsTypeMetadataTransformer(),
new AvaloniaXamlIlResolveByNameMarkupExtensionReplacer() new AvaloniaXamlIlResolveByNameMarkupExtensionReplacer(),
new AvaloniaXamlIlThemeVariantProviderTransformer()
); );
InsertBefore<ConvertPropertyValuesToAssignmentsTransformer>( InsertBefore<ConvertPropertyValuesToAssignmentsTransformer>(
new AvaloniaXamlIlOptionMarkupExtensionTransformer()); new AvaloniaXamlIlOptionMarkupExtensionTransformer());

138
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs

@ -198,6 +198,29 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a grid length", node); throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a grid length", node);
} }
} }
if (type.Equals(types.ColumnDefinition) || type.Equals(types.RowDefinition))
{
try
{
var gridLength = GridLength.Parse(text);
result = new AvaloniaXamlIlGridLengthAstNode(node, types, gridLength);
var definitionConstructorGridLength = type.GetConstructor(new List<IXamlType> {types.GridLength});
var lengthNode = new AvaloniaXamlIlGridLengthAstNode(node, types, gridLength);
var definitionTypeRef = new XamlAstClrTypeReference(node, type, false);
result = new XamlAstNewClrObjectNode(node, definitionTypeRef,
definitionConstructorGridLength, new List<IXamlAstValueNode> {lengthNode});
return true;
}
catch
{
throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a grid length", node);
}
}
if (type.Equals(types.Cursor)) if (type.Equals(types.Cursor))
{ {
@ -211,16 +234,6 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
} }
} }
if (type.Equals(types.ColumnDefinitions))
{
return ConvertDefinitionList(node, text, types, types.ColumnDefinitions, types.ColumnDefinition, "column definitions", out result);
}
if (type.Equals(types.RowDefinitions))
{
return ConvertDefinitionList(node, text, types, types.RowDefinitions, types.RowDefinition, "row definitions", out result);
}
if (types.IBrush.IsAssignableFrom(type)) if (types.IBrush.IsAssignableFrom(type))
{ {
if (Color.TryParse(text, out Color color)) if (Color.TryParse(text, out Color color))
@ -295,46 +308,89 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
} }
} }
result = null; // Keep it in the end, so more specific parsers can be applied.
return false; var elementType = GetElementType(type, context.Configuration.WellKnownTypes);
} if (elementType is not null)
private static bool ConvertDefinitionList(
IXamlAstValueNode node,
string text,
AvaloniaXamlIlWellKnownTypes types,
IXamlType listType,
IXamlType elementType,
string errorDisplayName,
out IXamlAstValueNode result)
{
try
{ {
var lengths = GridLength.ParseLengths(text); string[] items;
// Normalize special case of Points collection.
var definitionTypeRef = new XamlAstClrTypeReference(node, elementType, false); if (elementType == types.Point)
{
var pointParts = text.Split(new[] { ",", " " }, StringSplitOptions.RemoveEmptyEntries);
if (pointParts.Length % 2 == 0)
{
items = new string[pointParts.Length / 2];
for (int i = 0; i < pointParts.Length; i += 2)
{
items[i / 2] = string.Format(CultureInfo.InvariantCulture, "{0} {1}", pointParts[i],
pointParts[i + 1]);
}
}
else
{
throw new XamlX.XamlLoadException($"Invalid PointsList.", node);
}
}
else
{
const StringSplitOptions trimOption = (StringSplitOptions)2; // StringSplitOptions.TrimEntries
var separators = new[] { "," };
var splitOptions = StringSplitOptions.RemoveEmptyEntries | trimOption;
var definitionConstructorGridLength = elementType.GetConstructor(new List<IXamlType> {types.GridLength}); items = text.Split(separators, splitOptions ^ trimOption);
// Compiler targets netstandard, so we need to emulate StringSplitOptions.TrimEntries, if it was requested.
if (splitOptions.HasFlag(trimOption))
{
items = items.Select(i => i.Trim()).ToArray();
}
}
IXamlAstValueNode CreateDefinitionNode(GridLength length) var nodes = new IXamlAstValueNode[items.Length];
for (var index = 0; index < items.Length; index++)
{ {
var lengthNode = new AvaloniaXamlIlGridLengthAstNode(node, types, length); var success = XamlTransformHelpers.TryGetCorrectlyTypedValue(
context,
new XamlAstTextNode(node, items[index], true, context.Configuration.WellKnownTypes.String),
elementType, out var itemNode);
if (!success)
{
result = null;
return false;
}
return new XamlAstNewClrObjectNode(node, definitionTypeRef, nodes[index] = itemNode;
definitionConstructorGridLength, new List<IXamlAstValueNode> {lengthNode});
} }
var definitionNodes = if (types.AvaloniaList.MakeGenericType(elementType).IsAssignableFrom(type))
new List<IXamlAstValueNode>(lengths.Select(CreateDefinitionNode)); {
result = new AvaloniaXamlIlAvaloniaListConstantAstNode(node, types, type, elementType, nodes);
result = new AvaloniaXamlIlAvaloniaListConstantAstNode(node, types, listType, elementType, definitionNodes); return true;
}
else if (type.IsArray)
{
result = new AvaloniaXamlIlArrayConstantAstNode(node, elementType.MakeArrayType(1), elementType, nodes);
return true;
}
else if (type == context.Configuration.WellKnownTypes.IListOfT.MakeGenericType(elementType))
{
var listType = context.Configuration.WellKnownTypes.IListOfT.MakeGenericType(elementType);
result = new AvaloniaXamlIlArrayConstantAstNode(node, listType, elementType, nodes);
return true;
}
return true; result = null;
} return false;
catch
{
throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a {errorDisplayName}", node);
} }
result = null;
return false;
}
private static IXamlType GetElementType(IXamlType type, XamlTypeWellKnownTypes types)
{
return type.GetAllInterfaces().FirstOrDefault(i =>
i.FullName.StartsWith(types.IEnumerableT.FullName))?
.GenericArguments[0];
} }
} }
} }

14
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlMergeResourceGroupTransformer.cs

@ -24,7 +24,7 @@ internal class XamlMergeResourceGroupTransformer : IXamlAstGroupTransformer
var mergeResourceIncludeType = context.GetAvaloniaTypes().MergeResourceInclude; var mergeResourceIncludeType = context.GetAvaloniaTypes().MergeResourceInclude;
var mergeSourceNodes = new List<XamlPropertyAssignmentNode>(); var mergeSourceNodes = new List<XamlPropertyAssignmentNode>();
var hasAnyNonMergedResource = false; var mergedResourceWasAdded = false;
foreach (var manipulationNode in resourceDictionaryManipulation.Children.ToArray()) foreach (var manipulationNode in resourceDictionaryManipulation.Children.ToArray())
{ {
void ProcessXamlPropertyAssignmentNode(XamlManipulationGroupNode parent, XamlPropertyAssignmentNode assignmentNode) void ProcessXamlPropertyAssignmentNode(XamlManipulationGroupNode parent, XamlPropertyAssignmentNode assignmentNode)
@ -38,7 +38,8 @@ internal class XamlMergeResourceGroupTransformer : IXamlAstGroupTransformer
&& objectInitialization.Manipulation is XamlPropertyAssignmentNode sourceAssignmentNode) && objectInitialization.Manipulation is XamlPropertyAssignmentNode sourceAssignmentNode)
{ {
parent.Children.Remove(assignmentNode); parent.Children.Remove(assignmentNode);
mergeSourceNodes.Add(sourceAssignmentNode); mergeSourceNodes.Add(sourceAssignmentNode);
mergedResourceWasAdded = true;
} }
else else
{ {
@ -47,15 +48,10 @@ internal class XamlMergeResourceGroupTransformer : IXamlAstGroupTransformer
valueNode); valueNode);
} }
} }
else else if (mergeSourceNodes.Any())
{
hasAnyNonMergedResource = true;
}
if (hasAnyNonMergedResource && mergeSourceNodes.Any())
{ {
throw new XamlDocumentParseException(context.CurrentDocument, throw new XamlDocumentParseException(context.CurrentDocument,
"Mix of MergeResourceInclude and other dictionaries inside of the ResourceDictionary.MergedDictionaries is not allowed", "MergeResourceInclude should always be included last when mixing with other dictionaries inside of the ResourceDictionary.MergedDictionaries.",
valueNode); valueNode);
} }
} }

31
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlThemeVariantProviderTransformer.cs

@ -0,0 +1,31 @@
using System.Linq;
using XamlX;
using XamlX.Ast;
using XamlX.Transform;
using XamlX.TypeSystem;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers;
internal class AvaloniaXamlIlThemeVariantProviderTransformer : IXamlAstTransformer
{
public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
{
var type = context.GetAvaloniaTypes().IThemeVariantProvider;
if (!(node is XamlAstObjectNode on
&& type.IsAssignableFrom(on.Type.GetClrType())))
return node;
var keyDirective = on.Children.FirstOrDefault(n => n is XamlAstXmlDirective d
&& d.Namespace == XamlNamespaces.Xaml2006 &&
d.Name == "Key") as XamlAstXmlDirective;
if (keyDirective is null)
return node;
var keyProp = type.Properties.First(p => p.Name == "Key");
on.Children.Add(new XamlAstXamlPropertyValueNode(keyDirective,
new XamlAstClrProperty(keyDirective, keyProp, context.Configuration),
keyDirective.Values, true));
return node;
}
}

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

Loading…
Cancel
Save