From a26059630f19e5ec8ceb5d65aff29193ee74a036 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 1 Feb 2022 14:57:08 +0100 Subject: [PATCH 001/151] Add failing test for control template value priority. Add tests to check that template values are assigned with Style priority and move other XAML control template tests alongside it in a new test class. --- .../Xaml/BasicTests.cs | 66 +------- .../Xaml/ControlTemplateTests.cs | 143 ++++++++++++++++++ 2 files changed, 144 insertions(+), 65 deletions(-) create mode 100644 tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlTemplateTests.cs diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs index f20faa2287..16685c5d2d 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs @@ -281,70 +281,6 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml Assert.Equal(expected4, grid.RowDefinitions[3].Height); } - [Fact] - public void ControlTemplate_With_Nested_Child_Is_Operational() - { - var xaml = @" - - - - - -"; - var template = AvaloniaRuntimeXamlLoader.Parse(xaml); - - var parent = (ContentControl)template.Build(new ContentControl()).Control; - - Assert.Equal("parent", parent.Name); - - var child = parent.Content as ContentControl; - - Assert.NotNull(child); - - Assert.Equal("child", child.Name); - } - - [Fact] - public void ControlTemplate_With_TargetType_Is_Operational() - { - var xaml = @" - - - -"; - var template = AvaloniaRuntimeXamlLoader.Parse(xaml); - - Assert.Equal(typeof(ContentControl), template.TargetType); - - Assert.IsType(typeof(ContentPresenter), template.Build(new ContentControl()).Control); - } - - [Fact] - public void ControlTemplate_With_Panel_Children_Are_Added() - { - var xaml = @" - - - - - - -"; - var template = AvaloniaRuntimeXamlLoader.Parse(xaml); - - var panel = (Panel)template.Build(new ContentControl()).Control; - - Assert.Equal(2, panel.Children.Count); - - var foo = panel.Children[0]; - var bar = panel.Children[1]; - - Assert.Equal("Foo", foo.Name); - Assert.Equal("Bar", bar.Name); - } - [Fact] public void Named_x_Control_Is_Added_To_NameScope_Simple() { @@ -361,7 +297,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml } [Fact] - public void Standart_TypeConverter_Is_Used() + public void Standard_TypeConverter_Is_Used() { var xaml = @""; diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlTemplateTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlTemplateTests.cs new file mode 100644 index 0000000000..fbb16d9f2e --- /dev/null +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlTemplateTests.cs @@ -0,0 +1,143 @@ +using Avalonia.Controls; +using Avalonia.Controls.Presenters; +using Avalonia.Data; +using Avalonia.Diagnostics; +using Avalonia.Markup.Xaml.Templates; +using Avalonia.Media; +using Avalonia.UnitTests; +using Xunit; + +namespace Avalonia.Markup.Xaml.UnitTests.Xaml +{ + public class ControlTemplateTests : XamlTestBase + { + [Fact] + public void Inline_ControlTemplate_Values_Are_Set_With_Style_Priority() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + +"; + var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); + var button = (Button)window.Content; + + window.ApplyTemplate(); + button.ApplyTemplate(); + + var presenter = (ContentPresenter)button.Presenter; + Assert.Equal(Brushes.Red, presenter.Background); + + var diagnostic = presenter.GetDiagnostic(Button.BackgroundProperty); + Assert.Equal(BindingPriority.Style, diagnostic.Priority); + } + } + + [Fact] + public void Style_ControlTemplate_Values_Are_Set_With_Style_Priority() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + + + + +"; + var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); + var button = (Button)window.Content; + + window.ApplyTemplate(); + button.ApplyTemplate(); + + var presenter = (ContentPresenter)button.Presenter; + Assert.Equal(Dock.Top, DockPanel.GetDock(presenter)); + + var diagnostic = presenter.GetDiagnostic(DockPanel.DockProperty); + Assert.Equal(BindingPriority.Style, diagnostic.Priority); + } + } + + [Fact] + public void ControlTemplate_TemplateBindings_Are_Set_With_TemplatedParent_Priority() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + +"; + var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); + var button = (Button)window.Content; + + window.ApplyTemplate(); + button.ApplyTemplate(); + + var presenter = (ContentPresenter)button.Presenter; + Assert.Equal("Foo", presenter.Content); + + var diagnostic = presenter.GetDiagnostic(ContentPresenter.ContentProperty); + Assert.Equal(BindingPriority.TemplatedParent, diagnostic.Priority); + } + } + [Fact] public void ControlTemplate_With_Nested_Child_Is_Operational() { From f7ff0a02893396f5ed28a033005d9907b97af096 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sun, 20 Feb 2022 23:48:49 +0100 Subject: [PATCH 006/151] More failing tests for ControlTemplate resources. --- .../Xaml/ControlTemplateTests.cs | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlTemplateTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlTemplateTests.cs index f74aaaed76..2c38a82031 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlTemplateTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlTemplateTests.cs @@ -107,6 +107,74 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml } } + [Fact] + public void ControlTemplate_StaticResources_Are_Set_With_Style_Priority() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + + Red + + +"; + var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); + var button = (Button)window.Content; + + window.ApplyTemplate(); + button.ApplyTemplate(); + + var presenter = (ContentPresenter)button.Presenter; + Assert.Equal(Brushes.Red, presenter.Background); + + var diagnostic = presenter.GetDiagnostic(Button.BackgroundProperty); + Assert.Equal(BindingPriority.Style, diagnostic.Priority); + } + } + + [Fact] + public void ControlTemplate_DynamicResources_Are_Set_With_Style_Priority() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + + Red + + +"; + var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); + var button = (Button)window.Content; + + window.ApplyTemplate(); + button.ApplyTemplate(); + + var presenter = (ContentPresenter)button.Presenter; + Assert.Equal(Brushes.Red, presenter.Background); + + var diagnostic = presenter.GetDiagnostic(Button.BackgroundProperty); + Assert.Equal(BindingPriority.Style, diagnostic.Priority); + } + } + [Fact] public void ControlTemplate_TemplateBindings_Are_Set_With_TemplatedParent_Priority() { From cb7488baa7ed96fedd0308f8363c7003f4484893 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 21 Feb 2022 22:54:58 +0100 Subject: [PATCH 007/151] Use Style priority for StaticResource in ControlTemplate. --- ...amlIlControlTemplatePriorityTransformer.cs | 33 +++++++++++-------- .../XamlIlAvaloniaPropertyHelper.cs | 32 ++++++++++++++++-- .../Avalonia.Markup.Xaml.Loader/xamlil.github | 2 +- 3 files changed, 50 insertions(+), 17 deletions(-) diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlControlTemplatePriorityTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlControlTemplatePriorityTransformer.cs index eede21396d..e0090eb2e8 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlControlTemplatePriorityTransformer.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlControlTemplatePriorityTransformer.cs @@ -18,26 +18,31 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers // The node is a candidate for transformation if: // - It's a property assignment to an Avalonia property // - There's a ControlTemplate ancestor - // - There's just a direct call setter available + // - The property has a single value if (node is XamlPropertyAssignmentNode prop && prop.Property is XamlIlAvaloniaProperty avaloniaProperty && context.ParentNodes().Any(IsControlTemplate) && - prop.PossibleSetters.Count == 1 && - prop.PossibleSetters[0] is XamlDirectCallPropertySetter) + prop.Values.Count == 1) { - // Check if there are any setters on the property which accept a binding priority - - // this filters the candidates down to styled and attached properties. If so, then - // use this setter with BindingPriority.Style. - var setPriorityValueSetter = - avaloniaProperty.Setters.FirstOrDefault(x => x.Parameters[0] == bindingPriorityType); - - if(setPriorityValueSetter != null - && prop.Values.Count == 1 - && setPriorityValueSetter.Parameters[1].IsAssignableFrom(prop.Values[0].Type.GetClrType())) + var priorityValueSetters = new List(); + + // Iterate through the possible setters, trying to find a setter on the property + // which has a BindingPriority parameter followed by the parameter of the existing + // setter. + foreach (var setter in prop.PossibleSetters) + { + var s = avaloniaProperty.Setters.FirstOrDefault(x => + x.Parameters[0] == bindingPriorityType && + x.Parameters[1] == setter.Parameters[0]); + if (s != null) + priorityValueSetters.Add(s); + } + + // If any BindingPriority setters were found, use those. + if (priorityValueSetters.Count > 0) { - prop.PossibleSetters = new List { setPriorityValueSetter }; + prop.PossibleSetters = priorityValueSetters; prop.Values.Insert(0, new XamlConstantNode(node, bindingPriorityType, (int)BindingPriority.Style)); - return node; } } diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs index bb439b8816..5c7a80e680 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs @@ -185,9 +185,11 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions AvaloniaXamlIlWellKnownTypes types) :base(original, original.Name, original.DeclaringType, original.Getter, original.Setters) { + var assignBinding = original.CustomAttributes.Any(ca => ca.Type.Equals(types.AssignBindingAttribute)); + AvaloniaProperty = field; CustomAttributes = original.CustomAttributes; - if (!original.CustomAttributes.Any(ca => ca.Type.Equals(types.AssignBindingAttribute))) + if (!assignBinding) Setters.Insert(0, new BindingSetter(types, original.DeclaringType, field)); // Styled and attached properties can be set with a BindingPriority when they're @@ -196,7 +198,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions field.FieldType.GenericTypeDefinition == types.AvaloniaAttachedPropertyT) { var propertyType = field.FieldType.GenericArguments[0]; - Setters.Insert(1, new SetValueWithPrioritySetter(types, original.DeclaringType, field, propertyType)); + Setters.Insert(0, new SetValueWithPrioritySetter(types, original.DeclaringType, field, propertyType)); + if (!assignBinding) + Setters.Insert(1, new BindingWithPrioritySetter(types, original.DeclaringType, field)); } Setters.Insert(0, new UnsetValueSetter(types, original.DeclaringType, field)); @@ -249,6 +253,30 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions } } + class BindingWithPrioritySetter : AvaloniaPropertyCustomSetter + { + public BindingWithPrioritySetter(AvaloniaXamlIlWellKnownTypes types, + IXamlType declaringType, + IXamlField avaloniaProperty) : base(types, declaringType, avaloniaProperty) + { + Parameters = new[] { types.BindingPriority, types.IBinding }; + } + + public override void Emit(IXamlILEmitter emitter) + { + using (var bloc = emitter.LocalsPool.GetLocal(Types.IBinding)) + using (var priorityLocal = emitter.LocalsPool.GetLocal(Types.Int)) + emitter + .Stloc(bloc.Local) + .Stloc(priorityLocal.Local) + .Ldsfld(AvaloniaProperty) + .Ldloc(bloc.Local) + // TODO: provide anchor? + .Ldnull(); + emitter.EmitCall(Types.AvaloniaObjectBindMethod, true); + } + } + class SetValueWithPrioritySetter : AvaloniaPropertyCustomSetter { public SetValueWithPrioritySetter(AvaloniaXamlIlWellKnownTypes types, IXamlType declaringType, IXamlField avaloniaProperty, diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github b/src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github index e0cfcd0ef6..a4897d581c 160000 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github @@ -1 +1 @@ -Subproject commit e0cfcd0ef687e613ef8905e8d0891974b68c565d +Subproject commit a4897d581c7f155543102f0d744a47863e1fbfea From a142b7ea04578e2b4cc5e521a38288fe9b3cd790 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 22 Feb 2022 23:14:46 +0100 Subject: [PATCH 008/151] Detect control template from markup extensions. Allow markup extensions to detect whether they're in a control template and use that info in `DynamicResourceExtension` to use `Style` priority. --- .../Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj | 1 + src/Markup/Avalonia.Markup.Xaml/Extensions.cs | 4 ++-- .../MarkupExtensions/DynamicResourceExtension.cs | 8 ++++++-- .../Runtime/IAvaloniaXamlIlControlTemplateProvider.cs | 8 ++++++++ .../XamlIl/Runtime/XamlIlRuntimeHelpers.cs | 5 ++++- 5 files changed, 21 insertions(+), 5 deletions(-) create mode 100644 src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/IAvaloniaXamlIlControlTemplateProvider.cs diff --git a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj index 86132c5d27..4d9e216916 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj +++ b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj @@ -46,6 +46,7 @@ + diff --git a/src/Markup/Avalonia.Markup.Xaml/Extensions.cs b/src/Markup/Avalonia.Markup.Xaml/Extensions.cs index 263750c316..d937a83010 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Extensions.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Extensions.cs @@ -24,10 +24,10 @@ namespace Avalonia.Markup.Xaml public static IEnumerable GetParents(this IServiceProvider sp) { return sp.GetService().Parents.OfType(); - - } + public static bool IsInControlTemplate(this IServiceProvider sp) => sp.GetService() != null; + public static Type ResolveType(this IServiceProvider ctx, string namespacePrefix, string type) { var tr = ctx.GetService(); diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs index 087611bd59..65e38508bc 100644 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs +++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs @@ -11,6 +11,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions public class DynamicResourceExtension : IBinding { private object? _anchor; + private BindingPriority _priority; public DynamicResourceExtension() { @@ -25,6 +26,9 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions public IBinding ProvideValue(IServiceProvider serviceProvider) { + if (serviceProvider.IsInControlTemplate()) + _priority = BindingPriority.Style; + var provideTarget = serviceProvider.GetService(); if (!(provideTarget.TargetObject is IStyledElement)) @@ -53,12 +57,12 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions if (control != null) { var source = control.GetResourceObservable(ResourceKey, GetConverter(targetProperty)); - return InstancedBinding.OneWay(source); + return InstancedBinding.OneWay(source, _priority); } else if (_anchor is IResourceProvider resourceProvider) { var source = resourceProvider.GetResourceObservable(ResourceKey, GetConverter(targetProperty)); - return InstancedBinding.OneWay(source); + return InstancedBinding.OneWay(source, _priority); } return null; diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/IAvaloniaXamlIlControlTemplateProvider.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/IAvaloniaXamlIlControlTemplateProvider.cs new file mode 100644 index 0000000000..ed3f5bfdff --- /dev/null +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/IAvaloniaXamlIlControlTemplateProvider.cs @@ -0,0 +1,8 @@ +using System.Collections.Generic; + +namespace Avalonia.Markup.Xaml.XamlIl.Runtime +{ + public interface IAvaloniaXamlIlControlTemplateProvider + { + } +} diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs index c48f386ffd..a0e7fd7dcf 100644 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs @@ -42,7 +42,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime class DeferredParentServiceProvider : IAvaloniaXamlIlParentStackProvider, IServiceProvider, - IRootObjectProvider + IRootObjectProvider, + IAvaloniaXamlIlControlTemplateProvider { private readonly IServiceProvider _parentProvider; private readonly List _parentResourceNodes; @@ -75,6 +76,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime return this; if (serviceType == typeof(IRootObjectProvider)) return this; + if (serviceType == typeof(IAvaloniaXamlIlControlTemplateProvider)) + return this; return _parentProvider?.GetService(serviceType); } From beaf956af72b4ad38a56f92b6d9ec048fedb2815 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 22 Feb 2022 23:23:15 +0100 Subject: [PATCH 009/151] Use TemplatedParent priority in ControlTemplates. --- ...AvaloniaXamlIlControlTemplatePriorityTransformer.cs | 2 +- .../MarkupExtensions/DynamicResourceExtension.cs | 2 +- .../Xaml/ControlTemplateTests.cs | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlControlTemplatePriorityTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlControlTemplatePriorityTransformer.cs index e0090eb2e8..6cab68e756 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlControlTemplatePriorityTransformer.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlControlTemplatePriorityTransformer.cs @@ -42,7 +42,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers if (priorityValueSetters.Count > 0) { prop.PossibleSetters = priorityValueSetters; - prop.Values.Insert(0, new XamlConstantNode(node, bindingPriorityType, (int)BindingPriority.Style)); + prop.Values.Insert(0, new XamlConstantNode(node, bindingPriorityType, (int)BindingPriority.TemplatedParent)); } } diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs index 65e38508bc..f13ecab4e1 100644 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs +++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs @@ -27,7 +27,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions public IBinding ProvideValue(IServiceProvider serviceProvider) { if (serviceProvider.IsInControlTemplate()) - _priority = BindingPriority.Style; + _priority = BindingPriority.TemplatedParent; var provideTarget = serviceProvider.GetService(); diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlTemplateTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlTemplateTests.cs index 2c38a82031..2d6ed1dc62 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlTemplateTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlTemplateTests.cs @@ -38,7 +38,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml Assert.Equal(Brushes.Red, presenter.Background); var diagnostic = presenter.GetDiagnostic(Button.BackgroundProperty); - Assert.Equal(BindingPriority.Style, diagnostic.Priority); + Assert.Equal(BindingPriority.TemplatedParent, diagnostic.Priority); } } @@ -72,7 +72,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml Assert.Equal(Brushes.Red, presenter.Background); var diagnostic = presenter.GetDiagnostic(Button.BackgroundProperty); - Assert.Equal(BindingPriority.Style, diagnostic.Priority); + Assert.Equal(BindingPriority.TemplatedParent, diagnostic.Priority); } } @@ -103,7 +103,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml Assert.Equal(Dock.Top, DockPanel.GetDock(presenter)); var diagnostic = presenter.GetDiagnostic(DockPanel.DockProperty); - Assert.Equal(BindingPriority.Style, diagnostic.Priority); + Assert.Equal(BindingPriority.TemplatedParent, diagnostic.Priority); } } @@ -137,7 +137,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml Assert.Equal(Brushes.Red, presenter.Background); var diagnostic = presenter.GetDiagnostic(Button.BackgroundProperty); - Assert.Equal(BindingPriority.Style, diagnostic.Priority); + Assert.Equal(BindingPriority.TemplatedParent, diagnostic.Priority); } } @@ -171,7 +171,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml Assert.Equal(Brushes.Red, presenter.Background); var diagnostic = presenter.GetDiagnostic(Button.BackgroundProperty); - Assert.Equal(BindingPriority.Style, diagnostic.Priority); + Assert.Equal(BindingPriority.TemplatedParent, diagnostic.Priority); } } From c02439aaaf5c1984f8aae75c777173c775c89257 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 30 May 2022 17:10:09 +0200 Subject: [PATCH 010/151] Refactored most of Style into StyleBase. Ready for `ControlTheme` class, which is a style without a selector. --- src/Avalonia.Base/Styling/Style.cs | 148 ++------------------- src/Avalonia.Base/Styling/StyleBase.cs | 137 +++++++++++++++++++ src/Avalonia.Base/Styling/StyleChildren.cs | 10 +- 3 files changed, 151 insertions(+), 144 deletions(-) create mode 100644 src/Avalonia.Base/Styling/StyleBase.cs diff --git a/src/Avalonia.Base/Styling/Style.cs b/src/Avalonia.Base/Styling/Style.cs index 000e588bad..c85c85fe21 100644 --- a/src/Avalonia.Base/Styling/Style.cs +++ b/src/Avalonia.Base/Styling/Style.cs @@ -1,23 +1,12 @@ using System; -using System.Collections.Generic; -using Avalonia.Animation; -using Avalonia.Controls; -using Avalonia.Metadata; namespace Avalonia.Styling { /// /// Defines a style. /// - public class Style : AvaloniaObject, IStyle, IResourceProvider + public class Style : StyleBase { - private IResourceHost? _owner; - private StyleChildren? _children; - private IResourceDictionary? _resources; - private List? _setters; - private List? _animations; - private StyleCache? _childCache; - /// /// Initializes a new instance of the class. /// @@ -34,114 +23,11 @@ namespace Avalonia.Styling Selector = selector(null); } - /// - /// Gets the children of the style. - /// - public IList Children => _children ??= new(this); - - /// - /// Gets the or Application that hosts the style. - /// - public IResourceHost? Owner - { - get => _owner; - private set - { - if (_owner != value) - { - _owner = value; - OwnerChanged?.Invoke(this, EventArgs.Empty); - } - } - } - - /// - /// Gets the parent style if this style is hosted in a collection. - /// - public Style? Parent { get; private set; } - - /// - /// Gets or sets a dictionary of style resources. - /// - public IResourceDictionary Resources - { - get => _resources ?? (Resources = new ResourceDictionary()); - set - { - value = value ?? throw new ArgumentNullException(nameof(value)); - - var hadResources = _resources?.HasResources ?? false; - - _resources = value; - - if (Owner is object) - { - _resources.AddOwner(Owner); - - if (hadResources || _resources.HasResources) - { - Owner.NotifyHostedResourcesChanged(ResourcesChangedEventArgs.Empty); - } - } - } - } - /// /// Gets or sets the style's selector. /// public Selector? Selector { get; set; } - /// - /// Gets the style's setters. - /// - public IList Setters => _setters ??= new List(); - - /// - /// Gets the style's animations. - /// - public IList Animations => _animations ??= new List(); - - bool IResourceNode.HasResources => _resources?.Count > 0; - IReadOnlyList IStyle.Children => (IReadOnlyList?)_children ?? Array.Empty(); - - public event EventHandler? OwnerChanged; - - public void Add(ISetter setter) => Setters.Add(setter); - public void Add(IStyle style) => Children.Add(style); - - public SelectorMatchResult TryAttach(IStyleable target, IStyleHost? host) - { - target = target ?? throw new ArgumentNullException(nameof(target)); - - var match = Selector is object ? Selector.Match(target, Parent) : - target == host ? SelectorMatch.AlwaysThisInstance : SelectorMatch.NeverThisInstance; - - if (match.IsMatch && (_setters is object || _animations is object)) - { - var instance = new StyleInstance(this, target, _setters, _animations, match.Activator); - target.StyleApplied(instance); - instance.Start(); - } - - var result = match.Result; - - if (_children is not null) - { - _childCache ??= new StyleCache(); - var childResult = _childCache.TryAttach(_children, target, host); - if (childResult > result) - result = childResult; - } - - return result; - } - - public bool TryGetResource(object key, out object? result) - { - result = null; - return _resources?.TryGetResource(key, out result) ?? false; - } - /// /// Returns a string representation of the style. /// @@ -158,33 +44,17 @@ namespace Avalonia.Styling } } - void IResourceProvider.AddOwner(IResourceHost owner) - { - owner = owner ?? throw new ArgumentNullException(nameof(owner)); - - if (Owner != null) - { - throw new InvalidOperationException("The Style already has a parent."); - } - - Owner = owner; - _resources?.AddOwner(owner); - } - - void IResourceProvider.RemoveOwner(IResourceHost owner) + protected override SelectorMatch Matches(IStyleable target, IStyleHost? host) { - owner = owner ?? throw new ArgumentNullException(nameof(owner)); - - if (Owner == owner) - { - Owner = null; - _resources?.RemoveOwner(owner); - } + return Selector?.Match(target, Parent) ?? + (target == host ? + SelectorMatch.AlwaysThisInstance : + SelectorMatch.NeverThisInstance); } - internal void SetParent(Style? parent) + internal override void SetParent(StyleBase? parent) { - if (parent?.Selector is not null) + if (parent is Style parentStyle && parentStyle.Selector is not null) { if (Selector is null) throw new InvalidOperationException("Child styles must have a selector."); @@ -192,7 +62,7 @@ namespace Avalonia.Styling throw new InvalidOperationException("Child styles must have a nesting selector."); } - Parent = parent; + base.SetParent(parent); } } } diff --git a/src/Avalonia.Base/Styling/StyleBase.cs b/src/Avalonia.Base/Styling/StyleBase.cs new file mode 100644 index 0000000000..0fc57da728 --- /dev/null +++ b/src/Avalonia.Base/Styling/StyleBase.cs @@ -0,0 +1,137 @@ +using System; +using System.Collections.Generic; +using Avalonia.Animation; +using Avalonia.Controls; +using Avalonia.Metadata; + +namespace Avalonia.Styling +{ + /// + /// Base class for and . + /// + public abstract class StyleBase : AvaloniaObject, IStyle, IResourceProvider + { + private IResourceHost? _owner; + private StyleChildren? _children; + private IResourceDictionary? _resources; + private List? _setters; + private List? _animations; + private StyleCache? _childCache; + + public IList Children => _children ??= new(this); + + public IResourceHost? Owner + { + get => _owner; + private set + { + if (_owner != value) + { + _owner = value; + OwnerChanged?.Invoke(this, EventArgs.Empty); + } + } + } + + public IStyle? Parent { get; private set; } + + public IResourceDictionary Resources + { + get => _resources ?? (Resources = new ResourceDictionary()); + set + { + value = value ?? throw new ArgumentNullException(nameof(value)); + + var hadResources = _resources?.HasResources ?? false; + + _resources = value; + + if (Owner is object) + { + _resources.AddOwner(Owner); + + if (hadResources || _resources.HasResources) + { + Owner.NotifyHostedResourcesChanged(ResourcesChangedEventArgs.Empty); + } + } + } + } + + public IList Setters => _setters ??= new List(); + public IList Animations => _animations ??= new List(); + + bool IResourceNode.HasResources => _resources?.Count > 0; + IReadOnlyList IStyle.Children => (IReadOnlyList?)_children ?? Array.Empty(); + + public void Add(ISetter setter) => Setters.Add(setter); + public void Add(IStyle style) => Children.Add(style); + + public event EventHandler? OwnerChanged; + + public SelectorMatchResult TryAttach(IStyleable target, IStyleHost? host) + { + target = target ?? throw new ArgumentNullException(nameof(target)); + + var result = SelectorMatchResult.NeverThisType; + + if (_setters?.Count > 0 || _animations?.Count > 0) + { + var match = Matches(target, host); + + if (match.IsMatch) + { + var instance = new StyleInstance(this, target, _setters, _animations, match.Activator); + target.StyleApplied(instance); + instance.Start(); + } + + result = match.Result; + } + + if (_children is not null) + { + _childCache ??= new StyleCache(); + var childResult = _childCache.TryAttach(_children, target, host); + if (childResult > result) + result = childResult; + } + + return result; + } + + public bool TryGetResource(object key, out object? result) + { + result = null; + return _resources?.TryGetResource(key, out result) ?? false; + } + + protected abstract SelectorMatch Matches(IStyleable target, IStyleHost? host); + + internal virtual void SetParent(StyleBase? parent) => Parent = parent; + + void IResourceProvider.AddOwner(IResourceHost owner) + { + owner = owner ?? throw new ArgumentNullException(nameof(owner)); + + if (Owner != null) + { + throw new InvalidOperationException("The Style already has a parent."); + } + + Owner = owner; + _resources?.AddOwner(owner); + } + + void IResourceProvider.RemoveOwner(IResourceHost owner) + { + owner = owner ?? throw new ArgumentNullException(nameof(owner)); + + if (Owner == owner) + { + Owner = null; + _resources?.RemoveOwner(owner); + } + } + } +} diff --git a/src/Avalonia.Base/Styling/StyleChildren.cs b/src/Avalonia.Base/Styling/StyleChildren.cs index 5f8635f155..42b0a331ee 100644 --- a/src/Avalonia.Base/Styling/StyleChildren.cs +++ b/src/Avalonia.Base/Styling/StyleChildren.cs @@ -5,20 +5,20 @@ namespace Avalonia.Styling { internal class StyleChildren : Collection { - private readonly Style _owner; + private readonly StyleBase _owner; - public StyleChildren(Style owner) => _owner = owner; + public StyleChildren(StyleBase owner) => _owner = owner; protected override void InsertItem(int index, IStyle item) { - (item as Style)?.SetParent(_owner); + (item as StyleBase)?.SetParent(_owner); base.InsertItem(index, item); } protected override void RemoveItem(int index) { var item = Items[index]; - (item as Style)?.SetParent(null); + (item as StyleBase)?.SetParent(null); if (_owner.Owner is IResourceHost host) (item as IResourceProvider)?.RemoveOwner(host); base.RemoveItem(index); @@ -26,7 +26,7 @@ namespace Avalonia.Styling protected override void SetItem(int index, IStyle item) { - (item as Style)?.SetParent(_owner); + (item as StyleBase)?.SetParent(_owner); base.SetItem(index, item); if (_owner.Owner is IResourceHost host) (item as IResourceProvider)?.AddOwner(host); From 088d8cfc5c147da723e7641cf77c8dc67646e786 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 1 Jun 2022 13:21:52 +0200 Subject: [PATCH 011/151] Initial implementation of control themes. --- src/Avalonia.Base/Styling/ControlTheme.cs | 27 ++++ src/Avalonia.Base/Styling/IStyle.cs | 2 +- src/Avalonia.Base/Styling/IThemed.cs | 13 ++ src/Avalonia.Base/Styling/NestingSelector.cs | 4 +- src/Avalonia.Base/Styling/Style.cs | 8 +- src/Avalonia.Base/Styling/StyleBase.cs | 20 ++- src/Avalonia.Base/Styling/StyleCache.cs | 2 +- src/Avalonia.Base/Styling/Styler.cs | 14 +++ src/Avalonia.Base/Styling/Styles.cs | 2 +- .../Primitives/TemplatedControl.cs | 25 +++- src/Avalonia.Themes.Default/SimpleTheme.cs | 2 +- src/Avalonia.Themes.Fluent/FluentTheme.cs | 2 +- .../Styling/StyleInclude.cs | 2 +- .../TemplatedControlTests_Theming.cs | 119 ++++++++++++++++++ 14 files changed, 227 insertions(+), 15 deletions(-) create mode 100644 src/Avalonia.Base/Styling/ControlTheme.cs create mode 100644 src/Avalonia.Base/Styling/IThemed.cs create mode 100644 tests/Avalonia.Controls.UnitTests/Primitives/TemplatedControlTests_Theming.cs diff --git a/src/Avalonia.Base/Styling/ControlTheme.cs b/src/Avalonia.Base/Styling/ControlTheme.cs new file mode 100644 index 0000000000..54fc972c31 --- /dev/null +++ b/src/Avalonia.Base/Styling/ControlTheme.cs @@ -0,0 +1,27 @@ +using System; + +namespace Avalonia.Styling +{ + /// + /// Defines a switchable theme for a control. + /// + public class ControlTheme : StyleBase + { + /// + /// Gets or sets the type for which this control theme is intended. + /// + public Type? TargetType { get; set; } + + internal override bool HasSelector => TargetType is not null; + + internal override SelectorMatch Match(IStyleable control, object? host, bool subscribe) + { + if (TargetType is null) + throw new InvalidOperationException("ControlTheme has no TargetType."); + + return control.StyleKey == TargetType ? + SelectorMatch.AlwaysThisType : + SelectorMatch.NeverThisType; + } + } +} diff --git a/src/Avalonia.Base/Styling/IStyle.cs b/src/Avalonia.Base/Styling/IStyle.cs index e9faf82c07..417739fb28 100644 --- a/src/Avalonia.Base/Styling/IStyle.cs +++ b/src/Avalonia.Base/Styling/IStyle.cs @@ -23,6 +23,6 @@ namespace Avalonia.Styling /// /// A describing how the style matches the control. /// - SelectorMatchResult TryAttach(IStyleable target, IStyleHost? host); + SelectorMatchResult TryAttach(IStyleable target, object? host); } } diff --git a/src/Avalonia.Base/Styling/IThemed.cs b/src/Avalonia.Base/Styling/IThemed.cs new file mode 100644 index 0000000000..32ae515bcb --- /dev/null +++ b/src/Avalonia.Base/Styling/IThemed.cs @@ -0,0 +1,13 @@ +namespace Avalonia.Styling +{ + /// + /// Represents a themed element. + /// + public interface IThemed + { + /// + /// Gets the theme style for the element. + /// + public ControlTheme? Theme { get; } + } +} diff --git a/src/Avalonia.Base/Styling/NestingSelector.cs b/src/Avalonia.Base/Styling/NestingSelector.cs index 481a937867..6d31f7cb18 100644 --- a/src/Avalonia.Base/Styling/NestingSelector.cs +++ b/src/Avalonia.Base/Styling/NestingSelector.cs @@ -15,9 +15,9 @@ namespace Avalonia.Styling protected override SelectorMatch Evaluate(IStyleable control, IStyle? parent, bool subscribe) { - if (parent is Style s && s.Selector is Selector selector) + if (parent is StyleBase s && s.HasSelector) { - return selector.Match(control, (parent as Style)?.Parent, subscribe); + return s.Match(control, null, subscribe); } throw new InvalidOperationException( diff --git a/src/Avalonia.Base/Styling/Style.cs b/src/Avalonia.Base/Styling/Style.cs index c85c85fe21..ca20ff2b4b 100644 --- a/src/Avalonia.Base/Styling/Style.cs +++ b/src/Avalonia.Base/Styling/Style.cs @@ -28,6 +28,8 @@ namespace Avalonia.Styling /// public Selector? Selector { get; set; } + internal override bool HasSelector => Selector is not null; + /// /// Returns a string representation of the style. /// @@ -44,10 +46,10 @@ namespace Avalonia.Styling } } - protected override SelectorMatch Matches(IStyleable target, IStyleHost? host) + internal override SelectorMatch Match(IStyleable control, object? host, bool subscribe) { - return Selector?.Match(target, Parent) ?? - (target == host ? + return Selector?.Match(control, Parent, subscribe) ?? + (control == host ? SelectorMatch.AlwaysThisInstance : SelectorMatch.NeverThisInstance); } diff --git a/src/Avalonia.Base/Styling/StyleBase.cs b/src/Avalonia.Base/Styling/StyleBase.cs index 0fc57da728..b6bfec62bd 100644 --- a/src/Avalonia.Base/Styling/StyleBase.cs +++ b/src/Avalonia.Base/Styling/StyleBase.cs @@ -64,12 +64,14 @@ namespace Avalonia.Styling bool IResourceNode.HasResources => _resources?.Count > 0; IReadOnlyList IStyle.Children => (IReadOnlyList?)_children ?? Array.Empty(); + internal abstract bool HasSelector { get; } + public void Add(ISetter setter) => Setters.Add(setter); public void Add(IStyle style) => Children.Add(style); public event EventHandler? OwnerChanged; - public SelectorMatchResult TryAttach(IStyleable target, IStyleHost? host) + public SelectorMatchResult TryAttach(IStyleable target, object? host) { target = target ?? throw new ArgumentNullException(nameof(target)); @@ -77,7 +79,7 @@ namespace Avalonia.Styling if (_setters?.Count > 0 || _animations?.Count > 0) { - var match = Matches(target, host); + var match = Match(target, host, subscribe: true); if (match.IsMatch) { @@ -106,7 +108,19 @@ namespace Avalonia.Styling return _resources?.TryGetResource(key, out result) ?? false; } - protected abstract SelectorMatch Matches(IStyleable target, IStyleHost? host); + /// + /// Evaluates the style's selector against the specified target element. + /// + /// The control. + /// The element that hosts the style. + /// + /// Whether the match should subscribe to changes in order to track the match over time, + /// or simply return an immediate result. + /// + /// + /// A describing how the style matches the control. + /// + internal abstract SelectorMatch Match(IStyleable control, object? host, bool subscribe); internal virtual void SetParent(StyleBase? parent) => Parent = parent; diff --git a/src/Avalonia.Base/Styling/StyleCache.cs b/src/Avalonia.Base/Styling/StyleCache.cs index 3285476880..81196f6a27 100644 --- a/src/Avalonia.Base/Styling/StyleCache.cs +++ b/src/Avalonia.Base/Styling/StyleCache.cs @@ -12,7 +12,7 @@ namespace Avalonia.Styling /// internal class StyleCache : Dictionary?> { - public SelectorMatchResult TryAttach(IList styles, IStyleable target, IStyleHost? host) + public SelectorMatchResult TryAttach(IList styles, IStyleable target, object? host) { if (TryGetValue(target.StyleKey, out var cached)) { diff --git a/src/Avalonia.Base/Styling/Styler.cs b/src/Avalonia.Base/Styling/Styler.cs index 74cf77ea40..b9359b3329 100644 --- a/src/Avalonia.Base/Styling/Styler.cs +++ b/src/Avalonia.Base/Styling/Styler.cs @@ -10,6 +10,20 @@ namespace Avalonia.Styling { target = target ?? throw new ArgumentNullException(nameof(target)); + // If the control has a themed templated parent then first apply the styles from + // the templated parent theme. + if (target.TemplatedParent is IThemed themedTemplatedParent) + { + themedTemplatedParent.Theme?.TryAttach(target, themedTemplatedParent); + } + + // If the control itself is themed, then next apply the control theme. + if (target is IThemed themed) + { + themed.Theme?.TryAttach(target, target); + } + + // Apply styles from the rest of the tree. if (target is IStyleHost styleHost) { ApplyStyles(target, styleHost); diff --git a/src/Avalonia.Base/Styling/Styles.cs b/src/Avalonia.Base/Styling/Styles.cs index 7c0bc4ad7f..4c011f1b0d 100644 --- a/src/Avalonia.Base/Styling/Styles.cs +++ b/src/Avalonia.Base/Styling/Styles.cs @@ -109,7 +109,7 @@ namespace Avalonia.Styling set => _styles[index] = value; } - public SelectorMatchResult TryAttach(IStyleable target, IStyleHost? host) + public SelectorMatchResult TryAttach(IStyleable target, object? host) { _cache ??= new StyleCache(); return _cache.TryAttach(this, target, host); diff --git a/src/Avalonia.Controls/Primitives/TemplatedControl.cs b/src/Avalonia.Controls/Primitives/TemplatedControl.cs index db029d38c0..e1f42b6eb0 100644 --- a/src/Avalonia.Controls/Primitives/TemplatedControl.cs +++ b/src/Avalonia.Controls/Primitives/TemplatedControl.cs @@ -12,7 +12,7 @@ namespace Avalonia.Controls.Primitives /// /// A lookless control whose visual appearance is defined by its . /// - public class TemplatedControl : Control, ITemplatedControl + public class TemplatedControl : Control, IThemed, ITemplatedControl { /// /// Defines the property. @@ -86,6 +86,12 @@ namespace Avalonia.Controls.Primitives public static readonly StyledProperty TemplateProperty = AvaloniaProperty.Register(nameof(Template)); + /// + /// Defines the property. + /// + public static readonly StyledProperty ThemeProperty = + AvaloniaProperty.Register(nameof(Theme)); + /// /// Defines the IsTemplateFocusTarget attached property. /// @@ -228,6 +234,15 @@ namespace Avalonia.Controls.Primitives set { SetValue(TemplateProperty, value); } } + /// + /// Gets or sets the theme to be applied to the control. + /// + public ControlTheme? Theme + { + get { return GetValue(ThemeProperty); } + set { SetValue(ThemeProperty, value); } + } + /// /// Gets the value of the IsTemplateFocusTargetProperty attached property on a control. /// @@ -365,6 +380,14 @@ namespace Avalonia.Controls.Primitives { } + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property == ThemeProperty) + InvalidateStyles(); + } + /// /// Called when the control's template is applied. /// diff --git a/src/Avalonia.Themes.Default/SimpleTheme.cs b/src/Avalonia.Themes.Default/SimpleTheme.cs index 6929660757..d7939a68c1 100644 --- a/src/Avalonia.Themes.Default/SimpleTheme.cs +++ b/src/Avalonia.Themes.Default/SimpleTheme.cs @@ -103,7 +103,7 @@ namespace Avalonia.Themes.Default void IResourceProvider.RemoveOwner(IResourceHost owner) => (Loaded as IResourceProvider)?.RemoveOwner(owner); - public SelectorMatchResult TryAttach(IStyleable target, IStyleHost? host) => Loaded.TryAttach(target, host); + public SelectorMatchResult TryAttach(IStyleable target, object? host) => Loaded.TryAttach(target, host); public bool TryGetResource(object key, out object? value) { diff --git a/src/Avalonia.Themes.Fluent/FluentTheme.cs b/src/Avalonia.Themes.Fluent/FluentTheme.cs index f6b47a5466..befe669029 100644 --- a/src/Avalonia.Themes.Fluent/FluentTheme.cs +++ b/src/Avalonia.Themes.Fluent/FluentTheme.cs @@ -164,7 +164,7 @@ namespace Avalonia.Themes.Fluent } } - public SelectorMatchResult TryAttach(IStyleable target, IStyleHost? host) => Loaded.TryAttach(target, host); + public SelectorMatchResult TryAttach(IStyleable target, object? host) => Loaded.TryAttach(target, host); public bool TryGetResource(object key, out object? value) { diff --git a/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs b/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs index fa4a27fc50..109e85f1a4 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs @@ -82,7 +82,7 @@ namespace Avalonia.Markup.Xaml.Styling } } - public SelectorMatchResult TryAttach(IStyleable target, IStyleHost? host) => Loaded.TryAttach(target, host); + public SelectorMatchResult TryAttach(IStyleable target, object? host) => Loaded.TryAttach(target, host); public bool TryGetResource(object key, out object? value) { diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/TemplatedControlTests_Theming.cs b/tests/Avalonia.Controls.UnitTests/Primitives/TemplatedControlTests_Theming.cs new file mode 100644 index 0000000000..b24adfe7ab --- /dev/null +++ b/tests/Avalonia.Controls.UnitTests/Primitives/TemplatedControlTests_Theming.cs @@ -0,0 +1,119 @@ +using System.Linq; +using Avalonia.Controls.Primitives; +using Avalonia.Controls.Templates; +using Avalonia.Media; +using Avalonia.Styling; +using Avalonia.UnitTests; +using Avalonia.VisualTree; +using Xunit; + +#nullable enable + +namespace Avalonia.Controls.UnitTests.Primitives +{ + public class TemplatedControlTests_Theming + { + [Fact] + public void Theme_Is_Applied_When_Attached_To_Logical_Tree() + { + using var app = UnitTestApplication.Start(TestServices.RealStyler); + var target = CreateTarget(); + + Assert.Null(target.Template); + + var root = CreateRoot(target); + + Assert.NotNull(target.Template); + var border = Assert.IsType(target.VisualChild); + + Assert.Equal(border.Background, Brushes.Red); + + target.Classes.Add("foo"); + Assert.Equal(border.Background, Brushes.Green); + } + + [Fact] + public void Theme_Is_Detached_When_Theme_Property_Cleared() + { + using var app = UnitTestApplication.Start(TestServices.RealStyler); + var target = CreateTarget(); + var root = CreateRoot(target); + + Assert.NotNull(target.Template); + + target.Theme = null; + Assert.Null(target.Template); + } + + [Fact] + public void Theme_Is_Applied_On_Layout_After_Theme_Property_Changes() + { + using var app = UnitTestApplication.Start(TestServices.RealStyler); + var target = new ThemedControl(); + var root = CreateRoot(target); + + Assert.Null(target.Template); + + target.Theme = CreateTheme(); + Assert.Null(target.Template); + + root.LayoutManager.ExecuteLayoutPass(); + + var border = Assert.IsType(target.VisualChild); + Assert.NotNull(target.Template); + Assert.Equal(border.Background, Brushes.Red); + } + + private static ThemedControl CreateTarget() + { + return new ThemedControl + { + Theme = CreateTheme(), + }; + } + + private static ControlTheme CreateTheme() + { + var template = new FuncControlTemplate((o, n) => + new Border { Name = "PART_Border" }); + + return new ControlTheme + { + TargetType = typeof(ThemedControl), + Setters = + { + new Setter(ThemedControl.TemplateProperty, template), + }, + Children = + { + new Style(x => x.Nesting().Template().OfType()) + { + Setters = + { + new Setter(Border.BackgroundProperty, Brushes.Red), + } + }, + new Style(x => x.Nesting().Class("foo").Template().OfType()) + { + Setters = + { + new Setter(Border.BackgroundProperty, Brushes.Green), + } + }, + } + }; + } + + private static TestRoot CreateRoot(IControl child) + { + var result = new TestRoot(child); + result.LayoutManager.ExecuteInitialLayoutPass(); + return result; + } + + private class ThemedControl : TemplatedControl + { + public IVisual? VisualChild => VisualChildren?.SingleOrDefault(); + } + } +} From dee353bb9640278ab2364b1d9b4624d5cbe7a215 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 1 Jun 2022 15:24:15 +0200 Subject: [PATCH 012/151] Support ControlTheme in XAML compiler. --- .../AvaloniaXamlIlCompiler.cs | 1 + .../AvaloniaXamlIlControlThemeTransformer.cs | 39 ++++++++++ .../AvaloniaXamlIlSetterTransformer.cs | 75 +++++++++++++------ .../Xaml/ControlThemeTests.cs | 53 +++++++++++++ .../Xaml/TestTemplatedControl.cs | 8 ++ 5 files changed, 155 insertions(+), 21 deletions(-) create mode 100644 src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlControlThemeTransformer.cs create mode 100644 tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlThemeTests.cs create mode 100644 tests/Avalonia.Markup.Xaml.UnitTests/Xaml/TestTemplatedControl.cs diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs index 1ca7be67a7..20e035f8ff 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs @@ -48,6 +48,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions InsertBefore( new AvaloniaXamlIlBindingPathParser(), + new AvaloniaXamlIlControlThemeTransformer(), new AvaloniaXamlIlSelectorTransformer(), new AvaloniaXamlIlControlTemplateTargetTypeMetadataTransformer(), new AvaloniaXamlIlPropertyPathTransformer(), diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlControlThemeTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlControlThemeTransformer.cs new file mode 100644 index 0000000000..1338dc7248 --- /dev/null +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlControlThemeTransformer.cs @@ -0,0 +1,39 @@ +using System.Linq; +using XamlX; +using XamlX.Ast; +using XamlX.Transform; +using XamlX.Transform.Transformers; +using XamlX.TypeSystem; + +namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers +{ + class AvaloniaXamlIlControlThemeTransformer : IXamlAstTransformer + { + public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node) + { + if (!(node is XamlAstObjectNode on && on.Type.GetClrType().FullName == "Avalonia.Styling.ControlTheme")) + return node; + + // Check if we've already transformed this node. + if (context.ParentNodes().FirstOrDefault() is AvaloniaXamlIlTargetTypeMetadataNode) + return node; + + var targetTypeNode = on.Children.OfType() + .FirstOrDefault(p => p.Property.GetClrProperty().Name == "TargetType") ?? + throw new XamlParseException("ControlTheme must have a TargetType.", node); + + IXamlType targetType; + + if (targetTypeNode.Values[0] is XamlTypeExtensionNode extension) + targetType = extension.Value.GetClrType(); + else if (targetTypeNode.Values[0] is XamlAstTextNode text) + targetType = TypeReferenceResolver.ResolveType(context, text.Text, false, text, true).GetClrType(); + else + throw new XamlParseException("Could not determine TargetType for ControlTheme.", targetTypeNode); + + return new AvaloniaXamlIlTargetTypeMetadataNode(on, + new XamlAstClrTypeReference(targetTypeNode, targetType, false), + AvaloniaXamlIlTargetTypeMetadataNode.ScopeTypes.Style); + } + } +} diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs index e816265422..06e34a85a2 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs @@ -1,19 +1,14 @@ -using System; using System.Collections.Generic; using System.Linq; -using Avalonia.Data.Core; -using XamlX; using XamlX.Ast; using XamlX.Emit; using XamlX.IL; using XamlX.Transform; -using XamlX.Transform.Transformers; using XamlX.TypeSystem; namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers { using XamlParseException = XamlX.XamlParseException; - using XamlLoadException = XamlX.XamlLoadException; class AvaloniaXamlIlSetterTransformer : IXamlAstTransformer { public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node) @@ -22,21 +17,10 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers && on.Type.GetClrType().FullName == "Avalonia.Styling.Setter")) return node; - var parent = context.ParentNodes().OfType() - .FirstOrDefault(p => p.Type.GetClrType().FullName == "Avalonia.Styling.Style"); - - if (parent == null) - throw new XamlParseException( - "Avalonia.Styling.Setter is only valid inside Avalonia.Styling.Style", node); - var selectorProperty = parent.Children.OfType() - .FirstOrDefault(p => p.Property.GetClrProperty().Name == "Selector"); - if (selectorProperty == null) - throw new XamlParseException( - "Can not find parent Style Selector", node); - var selector = selectorProperty.Values.FirstOrDefault() as XamlIlSelectorNode; - if (selector?.TargetType == null) - throw new XamlParseException( - "Can not resolve parent Style Selector type", node); + var targetTypeNode = context.ParentNodes() + .OfType() + .FirstOrDefault(x => x.ScopeType == AvaloniaXamlIlTargetTypeMetadataNode.ScopeTypes.Style) ?? + throw new XamlParseException("Can not find parent Style Selector or ControlTemplate TargetType", node); IXamlType propType = null; var property = @on.Children.OfType() @@ -50,7 +34,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers var avaloniaPropertyNode = XamlIlAvaloniaPropertyHelper.CreateNode(context, propertyName, - new XamlAstClrTypeReference(selector, selector.TargetType, false), property.Values[0]); + new XamlAstClrTypeReference(targetTypeNode, targetTypeNode.TargetType.GetClrType(), false), property.Values[0]); property.Values = new List {avaloniaPropertyNode}; propType = avaloniaPropertyNode.AvaloniaPropertyType; } @@ -84,6 +68,55 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers return node; } + private (IXamlLineInfo, IXamlType) GetTargetType(AstTransformationContext context, IXamlAstNode node) + { + foreach (var n in context.ParentNodes()) + { + if (n is XamlAstObjectNode parent) + { + switch (parent.Type.GetClrType().FullName) + { + case "Avalonia.Styling.Style": + var selectorProperty = parent.Children.OfType() + .FirstOrDefault(p => p.Property.GetClrProperty().Name == "Selector"); + if (selectorProperty == null) + throw new XamlParseException("Can not find parent Style Selector.", node); + var selector = selectorProperty.Values.FirstOrDefault() as XamlIlSelectorNode; + if (selector?.TargetType != null) + return (selector, selector.TargetType); + throw new XamlParseException( + "Can not resolve parent Style Selector type", node); + + case "Avalonia.Styling.ControlTheme": + var targetTypeProperty = parent.Children.OfType() + .FirstOrDefault(p => p.Property.GetClrProperty().Name == "TargetType"); + if (targetTypeProperty == null) + throw new XamlParseException("ControlTemplate has no TargetType.", parent); + break; + } + } + } + + throw new XamlParseException("'Setter' is only valid inside a 'Style' or 'ControlTheme'.", node); + //var parent = context.ParentNodes().OfType() + // .FirstOrDefault(p => p.Type.GetClrType().FullName == "Avalonia.Styling.Style" || + // p.Type.GetClrType().FullName == "Avalonia.Styling.ControlTheme"); + + //if (parent == null) + // throw new XamlParseException( + // "Avalonia.Styling.Setter is only valid inside Avalonia.Styling.Style", node); + //var selectorProperty = parent.Children.OfType() + // .FirstOrDefault(p => p.Property.GetClrProperty().Name == "Selector" || + // p.Property.GetClrProperty().Name == "TargetType"); + //if (selectorProperty == null) + // throw new XamlParseException( + // "Can not find parent Style Selector or ControlTemplate TargetType", node); + //var selector = selectorProperty.Values.FirstOrDefault() as XamlIlSelectorNode; + //if (selector?.TargetType == null) + // throw new XamlParseException( + // "Can not resolve parent Style Selector type", node); + } + class SetterValueProperty : XamlAstClrProperty { public SetterValueProperty(IXamlLineInfo line, IXamlType setterType, IXamlType targetType, diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlThemeTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlThemeTests.cs new file mode 100644 index 0000000000..05083537cd --- /dev/null +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlThemeTests.cs @@ -0,0 +1,53 @@ +using Avalonia.Controls; +using Avalonia.Media; +using Avalonia.UnitTests; +using Avalonia.VisualTree; +using Xunit; + +namespace Avalonia.Markup.Xaml.UnitTests.Xaml +{ + public class ControlThemeTests : XamlTestBase + { + [Fact] + public void ControlTheme_Can_Be_StaticResource() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = $@" + + + {ControlThemeXaml} + + + +"; + + var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); + var button = Assert.IsType(window.Content); + + window.LayoutManager.ExecuteInitialLayoutPass(); + + Assert.NotNull(button.Template); + + var child = Assert.Single(button.GetVisualChildren()); + var border = Assert.IsType(child); + + Assert.Equal(Brushes.Red, border.Background); + } + } + + private const string ControlThemeXaml = @" + + + + + + + +"; + } +} diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/TestTemplatedControl.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/TestTemplatedControl.cs new file mode 100644 index 0000000000..0c862bb66a --- /dev/null +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/TestTemplatedControl.cs @@ -0,0 +1,8 @@ +using Avalonia.Controls.Primitives; + +namespace Avalonia.Markup.Xaml.UnitTests.Xaml +{ + public class TestTemplatedControl : TemplatedControl + { + } +} From a6dc6b1c887c8a5139be7bf1abba1315c26af0d7 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 1 Jun 2022 21:45:45 +0200 Subject: [PATCH 013/151] Prevent ControlTheme as a nested style. --- src/Avalonia.Base/Styling/ControlTheme.cs | 16 +++++++++++ .../Styling/ControlThemeTests.cs | 28 +++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 tests/Avalonia.Base.UnitTests/Styling/ControlThemeTests.cs diff --git a/src/Avalonia.Base/Styling/ControlTheme.cs b/src/Avalonia.Base/Styling/ControlTheme.cs index 54fc972c31..9dcbd7d2c4 100644 --- a/src/Avalonia.Base/Styling/ControlTheme.cs +++ b/src/Avalonia.Base/Styling/ControlTheme.cs @@ -7,6 +7,17 @@ namespace Avalonia.Styling /// public class ControlTheme : StyleBase { + /// + /// Initializes a new instance of the class. + /// + public ControlTheme() { } + + /// + /// Initializes a new instance of the class. + /// + /// The value for . + public ControlTheme(Type targetType) => TargetType = targetType; + /// /// Gets or sets the type for which this control theme is intended. /// @@ -23,5 +34,10 @@ namespace Avalonia.Styling SelectorMatch.AlwaysThisType : SelectorMatch.NeverThisType; } + + internal override void SetParent(StyleBase? parent) + { + throw new InvalidOperationException("ControlThemes cannot be added as a nested style."); + } } } diff --git a/tests/Avalonia.Base.UnitTests/Styling/ControlThemeTests.cs b/tests/Avalonia.Base.UnitTests/Styling/ControlThemeTests.cs new file mode 100644 index 0000000000..93a0e6c2fd --- /dev/null +++ b/tests/Avalonia.Base.UnitTests/Styling/ControlThemeTests.cs @@ -0,0 +1,28 @@ +using System; +using Avalonia.Controls; +using Avalonia.Styling; +using Xunit; + +namespace Avalonia.Base.UnitTests.Styling +{ + public class ControlThemeTests + { + [Fact] + public void ControlTheme_Cannot_Be_Added_To_Style_Children() + { + var target = new ControlTheme(typeof(Button)); + var style = new Style(); + + Assert.Throws(() => style.Children.Add(target)); + } + + [Fact] + public void ControlTheme_Cannot_Be_Added_To_ControlTheme_Children() + { + var target = new ControlTheme(typeof(Button)); + var other = new ControlTheme(typeof(CheckBox)); + + Assert.Throws(() => other.Children.Add(target)); + } + } +} From fc3c036b02afce41d8faca7e4c1e8219fb0c4ceb Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 1 Jun 2022 22:48:34 +0200 Subject: [PATCH 014/151] Move Theme to StyledElement. The WPF equivalent (`Style`) is in `FrameworkElement` so it would make sense. Will also make stuff a lot easier and removes the need for an `IThemed` interface. --- src/Avalonia.Base/StyledElement.cs | 25 +++++++++++++++++-- src/Avalonia.Base/Styling/IStyleable.cs | 7 ++++-- src/Avalonia.Base/Styling/IThemed.cs | 13 ---------- src/Avalonia.Base/Styling/Styler.cs | 23 ++++------------- .../Primitives/TemplatedControl.cs | 25 +------------------ .../AvaloniaPropertyConverterTest.cs | 5 ++++ 6 files changed, 39 insertions(+), 59 deletions(-) delete mode 100644 src/Avalonia.Base/Styling/IThemed.cs diff --git a/src/Avalonia.Base/StyledElement.cs b/src/Avalonia.Base/StyledElement.cs index f98d2cdbcc..4ead2470d7 100644 --- a/src/Avalonia.Base/StyledElement.cs +++ b/src/Avalonia.Base/StyledElement.cs @@ -12,8 +12,6 @@ using Avalonia.Logging; using Avalonia.LogicalTree; using Avalonia.Styling; -#nullable enable - namespace Avalonia { /// @@ -55,6 +53,12 @@ namespace Avalonia nameof(TemplatedParent), o => o.TemplatedParent, (o ,v) => o.TemplatedParent = v); + + /// + /// Defines the property. + /// + public static readonly StyledProperty ThemeProperty = + AvaloniaProperty.Register(nameof(Theme)); private int _initCount; private string? _name; @@ -230,6 +234,15 @@ namespace Avalonia internal set => SetAndRaise(TemplatedParentProperty, ref _templatedParent, value); } + /// + /// Gets or sets the theme to be applied to the element. + /// + public ControlTheme? Theme + { + get { return GetValue(ThemeProperty); } + set { SetValue(ThemeProperty, value); } + } + /// /// Gets the styled element's logical children. /// @@ -590,6 +603,14 @@ namespace Avalonia { } + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property == ThemeProperty) + InvalidateStyles(); + } + private static void DataContextNotifying(IAvaloniaObject o, bool updateStarted) { if (o is StyledElement element) diff --git a/src/Avalonia.Base/Styling/IStyleable.cs b/src/Avalonia.Base/Styling/IStyleable.cs index 5bc972e7ab..61fcbdf850 100644 --- a/src/Avalonia.Base/Styling/IStyleable.cs +++ b/src/Avalonia.Base/Styling/IStyleable.cs @@ -3,8 +3,6 @@ using System.Collections.Generic; using Avalonia.Collections; using Avalonia.Metadata; -#nullable enable - namespace Avalonia.Styling { /// @@ -28,6 +26,11 @@ namespace Avalonia.Styling /// ITemplatedControl? TemplatedParent { get; } + /// + /// Gets the theme to be applied to the control. + /// + public ControlTheme? Theme { get; } + /// /// Notifies the element that a style has been applied. /// diff --git a/src/Avalonia.Base/Styling/IThemed.cs b/src/Avalonia.Base/Styling/IThemed.cs deleted file mode 100644 index 32ae515bcb..0000000000 --- a/src/Avalonia.Base/Styling/IThemed.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Avalonia.Styling -{ - /// - /// Represents a themed element. - /// - public interface IThemed - { - /// - /// Gets the theme style for the element. - /// - public ControlTheme? Theme { get; } - } -} diff --git a/src/Avalonia.Base/Styling/Styler.cs b/src/Avalonia.Base/Styling/Styler.cs index b9359b3329..c9ea123bdc 100644 --- a/src/Avalonia.Base/Styling/Styler.cs +++ b/src/Avalonia.Base/Styling/Styler.cs @@ -1,33 +1,24 @@ using System; -#nullable enable - namespace Avalonia.Styling { public class Styler : IStyler { public void ApplyStyles(IStyleable target) { - target = target ?? throw new ArgumentNullException(nameof(target)); + _ = target ?? throw new ArgumentNullException(nameof(target)); // If the control has a themed templated parent then first apply the styles from // the templated parent theme. - if (target.TemplatedParent is IThemed themedTemplatedParent) - { - themedTemplatedParent.Theme?.TryAttach(target, themedTemplatedParent); - } + if (target.TemplatedParent is IStyleable styleableParent) + styleableParent.Theme?.TryAttach(target, styleableParent); - // If the control itself is themed, then next apply the control theme. - if (target is IThemed themed) - { - themed.Theme?.TryAttach(target, target); - } + // Next apply the control theme. + target.Theme?.TryAttach(target, target); // Apply styles from the rest of the tree. if (target is IStyleHost styleHost) - { ApplyStyles(target, styleHost); - } } private void ApplyStyles(IStyleable target, IStyleHost host) @@ -35,14 +26,10 @@ namespace Avalonia.Styling var parent = host.StylingParent; if (parent != null) - { ApplyStyles(target, parent); - } if (host.IsStylesInitialized) - { host.Styles.TryAttach(target, host); - } } } } diff --git a/src/Avalonia.Controls/Primitives/TemplatedControl.cs b/src/Avalonia.Controls/Primitives/TemplatedControl.cs index e1f42b6eb0..db029d38c0 100644 --- a/src/Avalonia.Controls/Primitives/TemplatedControl.cs +++ b/src/Avalonia.Controls/Primitives/TemplatedControl.cs @@ -12,7 +12,7 @@ namespace Avalonia.Controls.Primitives /// /// A lookless control whose visual appearance is defined by its . /// - public class TemplatedControl : Control, IThemed, ITemplatedControl + public class TemplatedControl : Control, ITemplatedControl { /// /// Defines the property. @@ -86,12 +86,6 @@ namespace Avalonia.Controls.Primitives public static readonly StyledProperty TemplateProperty = AvaloniaProperty.Register(nameof(Template)); - /// - /// Defines the property. - /// - public static readonly StyledProperty ThemeProperty = - AvaloniaProperty.Register(nameof(Theme)); - /// /// Defines the IsTemplateFocusTarget attached property. /// @@ -234,15 +228,6 @@ namespace Avalonia.Controls.Primitives set { SetValue(TemplateProperty, value); } } - /// - /// Gets or sets the theme to be applied to the control. - /// - public ControlTheme? Theme - { - get { return GetValue(ThemeProperty); } - set { SetValue(ThemeProperty, value); } - } - /// /// Gets the value of the IsTemplateFocusTargetProperty attached property on a control. /// @@ -380,14 +365,6 @@ namespace Avalonia.Controls.Primitives { } - protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) - { - base.OnPropertyChanged(change); - - if (change.Property == ThemeProperty) - InvalidateStyles(); - } - /// /// Called when the control's template is applied. /// diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Converters/AvaloniaPropertyConverterTest.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Converters/AvaloniaPropertyConverterTest.cs index 33bf72014c..ca59fe8480 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Converters/AvaloniaPropertyConverterTest.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Converters/AvaloniaPropertyConverterTest.cs @@ -137,6 +137,11 @@ namespace Avalonia.Markup.Xaml.UnitTests.Converters get { throw new NotImplementedException(); } } + public ControlTheme Theme + { + get { throw new NotImplementedException(); } + } + public void DetachStyles() { throw new NotImplementedException(); From 8c61f25188afe50b0785150e2e1fbf605c4652c5 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 2 Jun 2022 09:23:17 +0200 Subject: [PATCH 015/151] Promote theme to LocalValue if applied from style. --- src/Avalonia.Base/StyledElement.cs | 25 ++- .../TemplatedControlTests_Theming.cs | 146 ++++++++++++------ .../Xaml/ControlThemeTests.cs | 36 +++++ 3 files changed, 157 insertions(+), 50 deletions(-) diff --git a/src/Avalonia.Base/StyledElement.cs b/src/Avalonia.Base/StyledElement.cs index 4ead2470d7..75c4b94174 100644 --- a/src/Avalonia.Base/StyledElement.cs +++ b/src/Avalonia.Base/StyledElement.cs @@ -71,6 +71,7 @@ namespace Avalonia private List? _appliedStyles; private ITemplatedControl? _templatedParent; private bool _dataContextUpdating; + private bool _hasPromotedTheme; /// /// Initializes static members of the class. @@ -239,8 +240,8 @@ namespace Avalonia /// public ControlTheme? Theme { - get { return GetValue(ThemeProperty); } - set { SetValue(ThemeProperty, value); } + get => GetValue(ThemeProperty); + set => SetValue(ThemeProperty, value); } /// @@ -315,6 +316,7 @@ namespace Avalonia /// IStyleHost? IStyleHost.StylingParent => (IStyleHost?)InheritanceParent; + /// public virtual void BeginInit() { @@ -354,10 +356,15 @@ namespace Avalonia } finally { + _styled = true; EndBatchUpdate(); } - _styled = true; + if (_hasPromotedTheme) + { + _hasPromotedTheme = false; + ClearValue(ThemeProperty); + } } return _styled; @@ -608,7 +615,19 @@ namespace Avalonia base.OnPropertyChanged(change); if (change.Property == ThemeProperty) + { + // Changing the theme detaches all styles, meaning that if the theme property was + // set via a style, it will get cleared! To work around this, if the value was + // applied at less than local value priority then promote the value to local value + // priority until styling is re-applied. + if (change.Priority > BindingPriority.LocalValue) + { + Theme = change.GetNewValue(); + _hasPromotedTheme = true; + } + InvalidateStyles(); + } } private static void DataContextNotifying(IAvaloniaObject o, bool updateStarted) diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/TemplatedControlTests_Theming.cs b/tests/Avalonia.Controls.UnitTests/Primitives/TemplatedControlTests_Theming.cs index b24adfe7ab..74d75ff056 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/TemplatedControlTests_Theming.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/TemplatedControlTests_Theming.cs @@ -13,63 +13,122 @@ namespace Avalonia.Controls.UnitTests.Primitives { public class TemplatedControlTests_Theming { - [Fact] - public void Theme_Is_Applied_When_Attached_To_Logical_Tree() + public class InlineTheme { - using var app = UnitTestApplication.Start(TestServices.RealStyler); - var target = CreateTarget(); + [Fact] + public void Theme_Is_Applied_When_Attached_To_Logical_Tree() + { + using var app = UnitTestApplication.Start(TestServices.RealStyler); + var target = CreateTarget(); - Assert.Null(target.Template); + Assert.Null(target.Template); - var root = CreateRoot(target); + var root = CreateRoot(target); + Assert.NotNull(target.Template); - Assert.NotNull(target.Template); - var border = Assert.IsType(target.VisualChild); - - Assert.Equal(border.Background, Brushes.Red); + var border = Assert.IsType(target.VisualChild); + Assert.Equal(border.Background, Brushes.Red); - target.Classes.Add("foo"); - Assert.Equal(border.Background, Brushes.Green); - } + target.Classes.Add("foo"); + Assert.Equal(border.Background, Brushes.Green); + } - [Fact] - public void Theme_Is_Detached_When_Theme_Property_Cleared() - { - using var app = UnitTestApplication.Start(TestServices.RealStyler); - var target = CreateTarget(); - var root = CreateRoot(target); + [Fact] + public void Theme_Is_Detached_When_Theme_Property_Cleared() + { + using var app = UnitTestApplication.Start(TestServices.RealStyler); + var target = CreateTarget(); + var root = CreateRoot(target); - Assert.NotNull(target.Template); + Assert.NotNull(target.Template); - target.Theme = null; - Assert.Null(target.Template); - } + target.Theme = null; + Assert.Null(target.Template); + } - [Fact] - public void Theme_Is_Applied_On_Layout_After_Theme_Property_Changes() - { - using var app = UnitTestApplication.Start(TestServices.RealStyler); - var target = new ThemedControl(); - var root = CreateRoot(target); + [Fact] + public void Theme_Is_Applied_On_Layout_After_Theme_Property_Changes() + { + using var app = UnitTestApplication.Start(TestServices.RealStyler); + var target = new ThemedControl(); + var root = CreateRoot(target); + + Assert.Null(target.Template); - Assert.Null(target.Template); + target.Theme = CreateTheme(); + Assert.Null(target.Template); - target.Theme = CreateTheme(); - Assert.Null(target.Template); + root.LayoutManager.ExecuteLayoutPass(); - root.LayoutManager.ExecuteLayoutPass(); + var border = Assert.IsType(target.VisualChild); + Assert.NotNull(target.Template); + Assert.Equal(border.Background, Brushes.Red); + } - var border = Assert.IsType(target.VisualChild); - Assert.NotNull(target.Template); - Assert.Equal(border.Background, Brushes.Red); + private static ThemedControl CreateTarget() + { + return new ThemedControl + { + Theme = CreateTheme(), + }; + } + + private static TestRoot CreateRoot(IControl child) + { + var result = new TestRoot(child); + result.LayoutManager.ExecuteInitialLayoutPass(); + return result; + } } - private static ThemedControl CreateTarget() + public class ThemeFromStyle { - return new ThemedControl + [Fact] + public void Theme_Is_Applied_When_Attached_To_Logical_Tree() { - Theme = CreateTheme(), - }; + using var app = UnitTestApplication.Start(TestServices.RealStyler); + var target = CreateTarget(); + + Assert.Null(target.Theme); + Assert.Null(target.Template); + + var root = CreateRoot(target); + + Assert.NotNull(target.Theme); + Assert.NotNull(target.Template); + + var border = Assert.IsType(target.VisualChild); + Assert.Equal(border.Background, Brushes.Red); + + target.Classes.Add("foo"); + Assert.Equal(border.Background, Brushes.Green); + } + + private static ThemedControl CreateTarget() + { + return new ThemedControl(); + } + + private static TestRoot CreateRoot(IControl child) + { + var result = new TestRoot() + { + Styles = + { + new Style(x => x.OfType()) + { + Setters = + { + new Setter(TemplatedControl.ThemeProperty, CreateTheme()) + } + } + } + }; + + result.Child = child; + result.LayoutManager.ExecuteInitialLayoutPass(); + return result; + } } private static ControlTheme CreateTheme() @@ -104,13 +163,6 @@ namespace Avalonia.Controls.UnitTests.Primitives }; } - private static TestRoot CreateRoot(IControl child) - { - var result = new TestRoot(child); - result.LayoutManager.ExecuteInitialLayoutPass(); - return result; - } - private class ThemedControl : TemplatedControl { public IVisual? VisualChild => VisualChildren?.SingleOrDefault(); diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlThemeTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlThemeTests.cs index 05083537cd..9eb48311df 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlThemeTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlThemeTests.cs @@ -38,6 +38,42 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml } } + [Fact] + public void ControlTheme_Can_Be_Set_In_Style() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = $@" + + + {ControlThemeXaml} + + + + + + + +"; + + var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); + var button = Assert.IsType(window.Content); + + window.LayoutManager.ExecuteInitialLayoutPass(); + + Assert.NotNull(button.Template); + + var child = Assert.Single(button.GetVisualChildren()); + var border = Assert.IsType(child); + + Assert.Equal(Brushes.Red, border.Background); + } + } + private const string ControlThemeXaml = @" From 5cd95320128fa97fff7af8223cf55b9d043086f8 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 2 Jun 2022 09:36:53 +0200 Subject: [PATCH 016/151] Move tests to correct place. --- .../Styling/StyledElementTests_Theming.cs | 169 +++++++++++++++++ .../TemplatedControlTests_Theming.cs | 171 ------------------ 2 files changed, 169 insertions(+), 171 deletions(-) create mode 100644 tests/Avalonia.Base.UnitTests/Styling/StyledElementTests_Theming.cs delete mode 100644 tests/Avalonia.Controls.UnitTests/Primitives/TemplatedControlTests_Theming.cs diff --git a/tests/Avalonia.Base.UnitTests/Styling/StyledElementTests_Theming.cs b/tests/Avalonia.Base.UnitTests/Styling/StyledElementTests_Theming.cs new file mode 100644 index 0000000000..539f9e6576 --- /dev/null +++ b/tests/Avalonia.Base.UnitTests/Styling/StyledElementTests_Theming.cs @@ -0,0 +1,169 @@ +using System.Linq; +using Avalonia.Controls; +using Avalonia.Controls.Primitives; +using Avalonia.Controls.Templates; +using Avalonia.Media; +using Avalonia.Styling; +using Avalonia.UnitTests; +using Avalonia.VisualTree; +using Xunit; + +namespace Avalonia.Base.UnitTests.Styling; + +public class StyledElementTests_Theming +{ + public class InlineTheme + { + [Fact] + public void Theme_Is_Applied_When_Attached_To_Logical_Tree() + { + using var app = UnitTestApplication.Start(TestServices.RealStyler); + var target = CreateTarget(); + + Assert.Null(target.Template); + + var root = CreateRoot(target); + Assert.NotNull(target.Template); + + var border = Assert.IsType(target.VisualChild); + Assert.Equal(border.Background, Brushes.Red); + + target.Classes.Add("foo"); + Assert.Equal(border.Background, Brushes.Green); + } + + [Fact] + public void Theme_Is_Detached_When_Theme_Property_Cleared() + { + using var app = UnitTestApplication.Start(TestServices.RealStyler); + var target = CreateTarget(); + var root = CreateRoot(target); + + Assert.NotNull(target.Template); + + target.Theme = null; + Assert.Null(target.Template); + } + + [Fact] + public void Theme_Is_Applied_On_Layout_After_Theme_Property_Changes() + { + using var app = UnitTestApplication.Start(TestServices.RealStyler); + var target = new ThemedControl(); + var root = CreateRoot(target); + + Assert.Null(target.Template); + + target.Theme = CreateTheme(); + Assert.Null(target.Template); + + root.LayoutManager.ExecuteLayoutPass(); + + var border = Assert.IsType(target.VisualChild); + Assert.NotNull(target.Template); + Assert.Equal(border.Background, Brushes.Red); + } + + private static ThemedControl CreateTarget() + { + return new ThemedControl + { + Theme = CreateTheme(), + }; + } + + private static TestRoot CreateRoot(IControl child) + { + var result = new TestRoot(child); + result.LayoutManager.ExecuteInitialLayoutPass(); + return result; + } + } + + public class ThemeFromStyle + { + [Fact] + public void Theme_Is_Applied_When_Attached_To_Logical_Tree() + { + using var app = UnitTestApplication.Start(TestServices.RealStyler); + var target = CreateTarget(); + + Assert.Null(target.Theme); + Assert.Null(target.Template); + + var root = CreateRoot(target); + + Assert.NotNull(target.Theme); + Assert.NotNull(target.Template); + + var border = Assert.IsType(target.VisualChild); + Assert.Equal(border.Background, Brushes.Red); + + target.Classes.Add("foo"); + Assert.Equal(border.Background, Brushes.Green); + } + + private static ThemedControl CreateTarget() + { + return new ThemedControl(); + } + + private static TestRoot CreateRoot(IControl child) + { + var result = new TestRoot() + { + Styles = + { + new Style(x => x.OfType()) + { + Setters = + { + new Setter(TemplatedControl.ThemeProperty, CreateTheme()) + } + } + } + }; + + result.Child = child; + result.LayoutManager.ExecuteInitialLayoutPass(); + return result; + } + } + + private static ControlTheme CreateTheme() + { + var template = new FuncControlTemplate((o, n) => + new Border { Name = "PART_Border" }); + + return new ControlTheme + { + TargetType = typeof(ThemedControl), + Setters = + { + new Setter(ThemedControl.TemplateProperty, template), + }, + Children = + { + new Style(x => x.Nesting().Template().OfType()) + { + Setters = + { + new Setter(Border.BackgroundProperty, Brushes.Red), + } + }, + new Style(x => x.Nesting().Class("foo").Template().OfType()) + { + Setters = + { + new Setter(Border.BackgroundProperty, Brushes.Green), + } + }, + } + }; + } + + private class ThemedControl : TemplatedControl + { + public IVisual? VisualChild => VisualChildren?.SingleOrDefault(); + } +} diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/TemplatedControlTests_Theming.cs b/tests/Avalonia.Controls.UnitTests/Primitives/TemplatedControlTests_Theming.cs deleted file mode 100644 index 74d75ff056..0000000000 --- a/tests/Avalonia.Controls.UnitTests/Primitives/TemplatedControlTests_Theming.cs +++ /dev/null @@ -1,171 +0,0 @@ -using System.Linq; -using Avalonia.Controls.Primitives; -using Avalonia.Controls.Templates; -using Avalonia.Media; -using Avalonia.Styling; -using Avalonia.UnitTests; -using Avalonia.VisualTree; -using Xunit; - -#nullable enable - -namespace Avalonia.Controls.UnitTests.Primitives -{ - public class TemplatedControlTests_Theming - { - public class InlineTheme - { - [Fact] - public void Theme_Is_Applied_When_Attached_To_Logical_Tree() - { - using var app = UnitTestApplication.Start(TestServices.RealStyler); - var target = CreateTarget(); - - Assert.Null(target.Template); - - var root = CreateRoot(target); - Assert.NotNull(target.Template); - - var border = Assert.IsType(target.VisualChild); - Assert.Equal(border.Background, Brushes.Red); - - target.Classes.Add("foo"); - Assert.Equal(border.Background, Brushes.Green); - } - - [Fact] - public void Theme_Is_Detached_When_Theme_Property_Cleared() - { - using var app = UnitTestApplication.Start(TestServices.RealStyler); - var target = CreateTarget(); - var root = CreateRoot(target); - - Assert.NotNull(target.Template); - - target.Theme = null; - Assert.Null(target.Template); - } - - [Fact] - public void Theme_Is_Applied_On_Layout_After_Theme_Property_Changes() - { - using var app = UnitTestApplication.Start(TestServices.RealStyler); - var target = new ThemedControl(); - var root = CreateRoot(target); - - Assert.Null(target.Template); - - target.Theme = CreateTheme(); - Assert.Null(target.Template); - - root.LayoutManager.ExecuteLayoutPass(); - - var border = Assert.IsType(target.VisualChild); - Assert.NotNull(target.Template); - Assert.Equal(border.Background, Brushes.Red); - } - - private static ThemedControl CreateTarget() - { - return new ThemedControl - { - Theme = CreateTheme(), - }; - } - - private static TestRoot CreateRoot(IControl child) - { - var result = new TestRoot(child); - result.LayoutManager.ExecuteInitialLayoutPass(); - return result; - } - } - - public class ThemeFromStyle - { - [Fact] - public void Theme_Is_Applied_When_Attached_To_Logical_Tree() - { - using var app = UnitTestApplication.Start(TestServices.RealStyler); - var target = CreateTarget(); - - Assert.Null(target.Theme); - Assert.Null(target.Template); - - var root = CreateRoot(target); - - Assert.NotNull(target.Theme); - Assert.NotNull(target.Template); - - var border = Assert.IsType(target.VisualChild); - Assert.Equal(border.Background, Brushes.Red); - - target.Classes.Add("foo"); - Assert.Equal(border.Background, Brushes.Green); - } - - private static ThemedControl CreateTarget() - { - return new ThemedControl(); - } - - private static TestRoot CreateRoot(IControl child) - { - var result = new TestRoot() - { - Styles = - { - new Style(x => x.OfType()) - { - Setters = - { - new Setter(TemplatedControl.ThemeProperty, CreateTheme()) - } - } - } - }; - - result.Child = child; - result.LayoutManager.ExecuteInitialLayoutPass(); - return result; - } - } - - private static ControlTheme CreateTheme() - { - var template = new FuncControlTemplate((o, n) => - new Border { Name = "PART_Border" }); - - return new ControlTheme - { - TargetType = typeof(ThemedControl), - Setters = - { - new Setter(ThemedControl.TemplateProperty, template), - }, - Children = - { - new Style(x => x.Nesting().Template().OfType()) - { - Setters = - { - new Setter(Border.BackgroundProperty, Brushes.Red), - } - }, - new Style(x => x.Nesting().Class("foo").Template().OfType()) - { - Setters = - { - new Setter(Border.BackgroundProperty, Brushes.Green), - } - }, - } - }; - } - - private class ThemedControl : TemplatedControl - { - public IVisual? VisualChild => VisualChildren?.SingleOrDefault(); - } - } -} From 4bdcb8eeeaab2f790245ddb70a5cfca3df7886f8 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 2 Jun 2022 10:34:34 +0200 Subject: [PATCH 017/151] Invalidate template control styles when Theme changes. --- .../Primitives/TemplatedControl.cs | 11 +++ .../Styling/StyledElementTests_Theming.cs | 75 ++++++++++++++----- 2 files changed, 66 insertions(+), 20 deletions(-) diff --git a/src/Avalonia.Controls/Primitives/TemplatedControl.cs b/src/Avalonia.Controls/Primitives/TemplatedControl.cs index db029d38c0..a07dd9ae27 100644 --- a/src/Avalonia.Controls/Primitives/TemplatedControl.cs +++ b/src/Avalonia.Controls/Primitives/TemplatedControl.cs @@ -365,6 +365,17 @@ namespace Avalonia.Controls.Primitives { } + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property == ThemeProperty) + { + foreach (var child in this.GetTemplateChildren()) + child.InvalidateStyles(); + } + } + /// /// Called when the control's template is applied. /// diff --git a/tests/Avalonia.Base.UnitTests/Styling/StyledElementTests_Theming.cs b/tests/Avalonia.Base.UnitTests/Styling/StyledElementTests_Theming.cs index 539f9e6576..0c0808987a 100644 --- a/tests/Avalonia.Base.UnitTests/Styling/StyledElementTests_Theming.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/StyledElementTests_Theming.cs @@ -8,6 +8,8 @@ using Avalonia.UnitTests; using Avalonia.VisualTree; using Xunit; +#nullable enable + namespace Avalonia.Base.UnitTests.Styling; public class StyledElementTests_Theming @@ -45,6 +47,40 @@ public class StyledElementTests_Theming Assert.Null(target.Template); } + [Fact] + public void Theme_Is_Detached_From_Template_Controls_When_Theme_Property_Cleared() + { + using var app = UnitTestApplication.Start(TestServices.RealStyler); + + var theme = new ControlTheme + { + TargetType = typeof(ThemedControl), + Children = + { + new Style(x => x.Nesting().Template().OfType()) + { + Setters = + { + new Setter(Canvas.BackgroundProperty, Brushes.Red), + } + }, + } + }; + + var target = CreateTarget(theme); + target.Template = new FuncControlTemplate((o, n) => new Canvas()); + + var root = CreateRoot(target); + + var canvas = Assert.IsType(target.VisualChild); + Assert.Equal(canvas.Background, Brushes.Red); + + target.Theme = null; + + Assert.IsType(target.VisualChild); + Assert.Null(canvas.Background); + } + [Fact] public void Theme_Is_Applied_On_Layout_After_Theme_Property_Changes() { @@ -64,11 +100,11 @@ public class StyledElementTests_Theming Assert.Equal(border.Background, Brushes.Red); } - private static ThemedControl CreateTarget() + private static ThemedControl CreateTarget(ControlTheme? theme = null) { return new ThemedControl { - Theme = CreateTheme(), + Theme = theme ?? CreateTheme(), }; } @@ -132,33 +168,32 @@ public class StyledElementTests_Theming private static ControlTheme CreateTheme() { - var template = new FuncControlTemplate((o, n) => - new Border { Name = "PART_Border" }); + var template = new FuncControlTemplate((o, n) => new Border()); return new ControlTheme { TargetType = typeof(ThemedControl), Setters = - { - new Setter(ThemedControl.TemplateProperty, template), - }, - Children = - { - new Style(x => x.Nesting().Template().OfType()) { - Setters = - { - new Setter(Border.BackgroundProperty, Brushes.Red), - } + new Setter(ThemedControl.TemplateProperty, template), }, - new Style(x => x.Nesting().Class("foo").Template().OfType()) + Children = { - Setters = + new Style(x => x.Nesting().Template().OfType()) { - new Setter(Border.BackgroundProperty, Brushes.Green), - } - }, - } + Setters = + { + new Setter(Border.BackgroundProperty, Brushes.Red), + } + }, + new Style(x => x.Nesting().Class("foo").Template().OfType()) + { + Setters = + { + new Setter(Border.BackgroundProperty, Brushes.Green), + } + }, + } }; } From 49613c7bceb5244444df97862b59285915a8e4ee Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 3 Jun 2022 12:04:37 +0200 Subject: [PATCH 018/151] Add accent button to control catalog. --- samples/ControlCatalog/Pages/ButtonsPage.xaml | 1 + 1 file changed, 1 insertion(+) diff --git a/samples/ControlCatalog/Pages/ButtonsPage.xaml b/samples/ControlCatalog/Pages/ButtonsPage.xaml index 059b4d9788..8a474a203d 100644 --- a/samples/ControlCatalog/Pages/ButtonsPage.xaml +++ b/samples/ControlCatalog/Pages/ButtonsPage.xaml @@ -90,6 +90,7 @@ + Date: Fri, 3 Jun 2022 14:32:19 +0200 Subject: [PATCH 019/151] Fix nested :not selector. --- src/Avalonia.Base/Styling/NotSelector.cs | 2 +- .../Styling/SelectorTests_Nesting.cs | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Styling/NotSelector.cs b/src/Avalonia.Base/Styling/NotSelector.cs index cdc3254d38..76a0690e96 100644 --- a/src/Avalonia.Base/Styling/NotSelector.cs +++ b/src/Avalonia.Base/Styling/NotSelector.cs @@ -67,6 +67,6 @@ namespace Avalonia.Styling } protected override Selector? MovePrevious() => _previous; - internal override bool HasValidNestingSelector() => _argument.HasValidNestingSelector(); + internal override bool HasValidNestingSelector() => _previous?.HasValidNestingSelector() ?? false; } } diff --git a/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Nesting.cs b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Nesting.cs index d49fcf03a2..1520dc329d 100644 --- a/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Nesting.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Nesting.cs @@ -257,6 +257,30 @@ namespace Avalonia.Base.UnitTests.Styling parent.Children.Add(child); } + + [Fact] + public void Nesting_Not_Class_Matches() + { + var control = new Control1 { Classes = { "foo" } }; + Style nested; + var parent = new Style(x => x.OfType()) + { + Children = + { + (nested = new Style(x => x.Nesting().Not(y => y.Class("foo")))), + } + }; + + var match = nested.Selector.Match(control, parent); + Assert.Equal(SelectorMatchResult.Sometimes, match.Result); + + var sink = new ActivatorSink(match.Activator); + + Assert.False(sink.Active); + control.Classes.Clear(); + Assert.True(sink.Active); + } + public class Control1 : Control { } From 05fdc0446416a285c9067b56d858c018e4f7104d Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 3 Jun 2022 23:43:55 +0200 Subject: [PATCH 020/151] Add ControlTheme.BasedOn. --- src/Avalonia.Base/Styling/ControlTheme.cs | 26 ++++++-- src/Avalonia.Base/Styling/NestingSelector.cs | 12 +++- src/Avalonia.Base/Styling/Style.cs | 35 ++++++++--- src/Avalonia.Base/Styling/StyleBase.cs | 61 ++++++------------- .../Styling/StyledElementTests_Theming.cs | 59 ++++++++++++++++-- 5 files changed, 129 insertions(+), 64 deletions(-) diff --git a/src/Avalonia.Base/Styling/ControlTheme.cs b/src/Avalonia.Base/Styling/ControlTheme.cs index 9dcbd7d2c4..aff6fad990 100644 --- a/src/Avalonia.Base/Styling/ControlTheme.cs +++ b/src/Avalonia.Base/Styling/ControlTheme.cs @@ -23,16 +23,32 @@ namespace Avalonia.Styling /// public Type? TargetType { get; set; } - internal override bool HasSelector => TargetType is not null; + /// + /// Gets or sets a control theme that is the basis of the current theme. + /// + public ControlTheme? BasedOn { get; set; } - internal override SelectorMatch Match(IStyleable control, object? host, bool subscribe) + public override SelectorMatchResult TryAttach(IStyleable target, object? host) { + _ = target ?? throw new ArgumentNullException(nameof(target)); + if (TargetType is null) throw new InvalidOperationException("ControlTheme has no TargetType."); - return control.StyleKey == TargetType ? - SelectorMatch.AlwaysThisType : - SelectorMatch.NeverThisType; + var result = BasedOn?.TryAttach(target, host) ?? SelectorMatchResult.NeverThisType; + + if (HasSettersOrAnimations && target.StyleKey == TargetType) + { + Attach(target, null); + result = SelectorMatchResult.AlwaysThisType; + } + + var childResult = TryAttachChildren(target, host); + + if (childResult > result) + result = childResult; + + return result; } internal override void SetParent(StyleBase? parent) diff --git a/src/Avalonia.Base/Styling/NestingSelector.cs b/src/Avalonia.Base/Styling/NestingSelector.cs index 6d31f7cb18..c8945a713d 100644 --- a/src/Avalonia.Base/Styling/NestingSelector.cs +++ b/src/Avalonia.Base/Styling/NestingSelector.cs @@ -15,9 +15,17 @@ namespace Avalonia.Styling protected override SelectorMatch Evaluate(IStyleable control, IStyle? parent, bool subscribe) { - if (parent is StyleBase s && s.HasSelector) + if (parent is Style s && s.Selector is not null) { - return s.Match(control, null, subscribe); + return s.Selector.Match(control, s.Parent, subscribe); + } + else if (parent is ControlTheme theme) + { + if (theme.TargetType is null) + throw new InvalidOperationException("ControlTheme has no TargetType."); + return control.StyleKey == theme.TargetType ? + SelectorMatch.AlwaysThisType : + SelectorMatch.NeverThisType; } throw new InvalidOperationException( diff --git a/src/Avalonia.Base/Styling/Style.cs b/src/Avalonia.Base/Styling/Style.cs index ca20ff2b4b..7a6b746488 100644 --- a/src/Avalonia.Base/Styling/Style.cs +++ b/src/Avalonia.Base/Styling/Style.cs @@ -28,7 +28,32 @@ namespace Avalonia.Styling /// public Selector? Selector { get; set; } - internal override bool HasSelector => Selector is not null; + public override SelectorMatchResult TryAttach(IStyleable target, object? host) + { + _ = target ?? throw new ArgumentNullException(nameof(target)); + + var result = SelectorMatchResult.NeverThisType; + + if (HasSettersOrAnimations) + { + var match = Selector?.Match(target, Parent, true) ?? + (target == host ? + SelectorMatch.AlwaysThisInstance : + SelectorMatch.NeverThisInstance); + + if (match.IsMatch) + Attach(target, match.Activator); + + result = match.Result; + } + + var childResult = TryAttachChildren(target, host); + + if (childResult > result) + result = childResult; + + return result; + } /// /// Returns a string representation of the style. @@ -46,14 +71,6 @@ namespace Avalonia.Styling } } - internal override SelectorMatch Match(IStyleable control, object? host, bool subscribe) - { - return Selector?.Match(control, Parent, subscribe) ?? - (control == host ? - SelectorMatch.AlwaysThisInstance : - SelectorMatch.NeverThisInstance); - } - internal override void SetParent(StyleBase? parent) { if (parent is Style parentStyle && parentStyle.Selector is not null) diff --git a/src/Avalonia.Base/Styling/StyleBase.cs b/src/Avalonia.Base/Styling/StyleBase.cs index b6bfec62bd..306a4cf010 100644 --- a/src/Avalonia.Base/Styling/StyleBase.cs +++ b/src/Avalonia.Base/Styling/StyleBase.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using Avalonia.Animation; using Avalonia.Controls; using Avalonia.Metadata; +using Avalonia.Styling.Activators; namespace Avalonia.Styling { @@ -64,43 +65,14 @@ namespace Avalonia.Styling bool IResourceNode.HasResources => _resources?.Count > 0; IReadOnlyList IStyle.Children => (IReadOnlyList?)_children ?? Array.Empty(); - internal abstract bool HasSelector { get; } + internal bool HasSettersOrAnimations => _setters?.Count > 0 || _animations?.Count > 0; public void Add(ISetter setter) => Setters.Add(setter); public void Add(IStyle style) => Children.Add(style); public event EventHandler? OwnerChanged; - public SelectorMatchResult TryAttach(IStyleable target, object? host) - { - target = target ?? throw new ArgumentNullException(nameof(target)); - - var result = SelectorMatchResult.NeverThisType; - - if (_setters?.Count > 0 || _animations?.Count > 0) - { - var match = Match(target, host, subscribe: true); - - if (match.IsMatch) - { - var instance = new StyleInstance(this, target, _setters, _animations, match.Activator); - target.StyleApplied(instance); - instance.Start(); - } - - result = match.Result; - } - - if (_children is not null) - { - _childCache ??= new StyleCache(); - var childResult = _childCache.TryAttach(_children, target, host); - if (childResult > result) - result = childResult; - } - - return result; - } + public abstract SelectorMatchResult TryAttach(IStyleable target, object? host); public bool TryGetResource(object key, out object? result) { @@ -108,19 +80,20 @@ namespace Avalonia.Styling return _resources?.TryGetResource(key, out result) ?? false; } - /// - /// Evaluates the style's selector against the specified target element. - /// - /// The control. - /// The element that hosts the style. - /// - /// Whether the match should subscribe to changes in order to track the match over time, - /// or simply return an immediate result. - /// - /// - /// A describing how the style matches the control. - /// - internal abstract SelectorMatch Match(IStyleable control, object? host, bool subscribe); + internal void Attach(IStyleable target, IStyleActivator? activator) + { + var instance = new StyleInstance(this, target, _setters, _animations, activator); + target.StyleApplied(instance); + instance.Start(); + } + + internal SelectorMatchResult TryAttachChildren(IStyleable target, object? host) + { + if (_children is null || _children.Count == 0) + return SelectorMatchResult.NeverThisType; + _childCache ??= new StyleCache(); + return _childCache.TryAttach(_children, target, host); + } internal virtual void SetParent(StyleBase? parent) => Parent = parent; diff --git a/tests/Avalonia.Base.UnitTests/Styling/StyledElementTests_Theming.cs b/tests/Avalonia.Base.UnitTests/Styling/StyledElementTests_Theming.cs index 0c0808987a..737cf1e048 100644 --- a/tests/Avalonia.Base.UnitTests/Styling/StyledElementTests_Theming.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/StyledElementTests_Theming.cs @@ -28,10 +28,10 @@ public class StyledElementTests_Theming Assert.NotNull(target.Template); var border = Assert.IsType(target.VisualChild); - Assert.Equal(border.Background, Brushes.Red); + Assert.Equal(Brushes.Red, border.Background); target.Classes.Add("foo"); - Assert.Equal(border.Background, Brushes.Green); + Assert.Equal(Brushes.Green, border.Background); } [Fact] @@ -73,7 +73,7 @@ public class StyledElementTests_Theming var root = CreateRoot(target); var canvas = Assert.IsType(target.VisualChild); - Assert.Equal(canvas.Background, Brushes.Red); + Assert.Equal(Brushes.Red, canvas.Background); target.Theme = null; @@ -97,7 +97,28 @@ public class StyledElementTests_Theming var border = Assert.IsType(target.VisualChild); Assert.NotNull(target.Template); - Assert.Equal(border.Background, Brushes.Red); + Assert.Equal(Brushes.Red, border.Background); + } + + [Fact] + public void BasedOn_Theme_Is_Applied_When_Attached_To_Logical_Tree() + { + using var app = UnitTestApplication.Start(TestServices.RealStyler); + var target = CreateTarget(CreateDerivedTheme()); + + Assert.Null(target.Template); + + var root = CreateRoot(target); + Assert.NotNull(target.Template); + Assert.Equal(Brushes.Blue, target.BorderBrush); + + var border = Assert.IsType(target.VisualChild); + Assert.Equal(Brushes.Red, border.Background); + Assert.Equal(Brushes.Yellow, border.BorderBrush); + + target.Classes.Add("foo"); + Assert.Equal(Brushes.Green, border.Background); + Assert.Equal(Brushes.Cyan, border.BorderBrush); } private static ThemedControl CreateTarget(ControlTheme? theme = null) @@ -197,6 +218,36 @@ public class StyledElementTests_Theming }; } + private static ControlTheme CreateDerivedTheme() + { + return new ControlTheme + { + TargetType = typeof(ThemedControl), + BasedOn = CreateTheme(), + Setters = + { + new Setter(Border.BorderBrushProperty, Brushes.Blue), + }, + Children = + { + new Style(x => x.Nesting().Template().OfType()) + { + Setters = + { + new Setter(Border.BorderBrushProperty, Brushes.Yellow), + } + }, + new Style(x => x.Nesting().Class("foo").Template().OfType()) + { + Setters = + { + new Setter(Border.BorderBrushProperty, Brushes.Cyan), + } + }, + } + }; + } + private class ThemedControl : TemplatedControl { public IVisual? VisualChild => VisualChildren?.SingleOrDefault(); From 1d1ef5ca9fdb42d2fbf029f122530dcca9b51a59 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 4 Jun 2022 12:45:19 +0200 Subject: [PATCH 021/151] Display control themes in devtools. --- src/Avalonia.Base/Styling/ControlTheme.cs | 8 ++++++++ .../Diagnostics/ViewModels/ControlDetailsViewModel.cs | 11 +++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Base/Styling/ControlTheme.cs b/src/Avalonia.Base/Styling/ControlTheme.cs index aff6fad990..399eb9ae59 100644 --- a/src/Avalonia.Base/Styling/ControlTheme.cs +++ b/src/Avalonia.Base/Styling/ControlTheme.cs @@ -51,6 +51,14 @@ namespace Avalonia.Styling return result; } + public override string ToString() + { + if (TargetType is not null) + return "ControlTheme: " + TargetType.Name; + else + return "ControlTheme"; + } + internal override void SetParent(StyleBase? parent) { throw new InvalidOperationException("ControlThemes cannot be added as a nested style."); diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs index e383c160e3..795826e4f6 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs @@ -67,8 +67,15 @@ namespace Avalonia.Diagnostics.ViewModels var setters = new List(); - if (styleSource is Style style) + if (styleSource is StyleBase style) { + var selector = style switch + { + Style s => s.Selector?.ToString(), + ControlTheme t => t.TargetType?.Name.ToString(), + _ => null, + }; + foreach (var setter in style.Setters) { if (setter is Setter regularSetter @@ -105,7 +112,7 @@ namespace Avalonia.Diagnostics.ViewModels } } - AppliedStyles.Add(new StyleViewModel(appliedStyle, style.Selector?.ToString() ?? "No selector", setters)); + AppliedStyles.Add(new StyleViewModel(appliedStyle, selector ?? "No selector", setters)); } } From 95f70143ca319e5e6f49601d1e2de60bdbb39759 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 4 Jun 2022 14:53:31 +0200 Subject: [PATCH 022/151] Can apply control theme to derived types. --- src/Avalonia.Base/Styling/ControlTheme.cs | 2 +- src/Avalonia.Base/Styling/NestingSelector.cs | 2 +- .../Styling/StyledElementTests_Theming.cs | 25 +++++++++++++++++++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Base/Styling/ControlTheme.cs b/src/Avalonia.Base/Styling/ControlTheme.cs index 399eb9ae59..644e8b32d4 100644 --- a/src/Avalonia.Base/Styling/ControlTheme.cs +++ b/src/Avalonia.Base/Styling/ControlTheme.cs @@ -37,7 +37,7 @@ namespace Avalonia.Styling var result = BasedOn?.TryAttach(target, host) ?? SelectorMatchResult.NeverThisType; - if (HasSettersOrAnimations && target.StyleKey == TargetType) + if (HasSettersOrAnimations && TargetType.IsAssignableFrom(target.StyleKey)) { Attach(target, null); result = SelectorMatchResult.AlwaysThisType; diff --git a/src/Avalonia.Base/Styling/NestingSelector.cs b/src/Avalonia.Base/Styling/NestingSelector.cs index c8945a713d..4393d3239f 100644 --- a/src/Avalonia.Base/Styling/NestingSelector.cs +++ b/src/Avalonia.Base/Styling/NestingSelector.cs @@ -23,7 +23,7 @@ namespace Avalonia.Styling { if (theme.TargetType is null) throw new InvalidOperationException("ControlTheme has no TargetType."); - return control.StyleKey == theme.TargetType ? + return theme.TargetType.IsAssignableFrom(control.StyleKey) ? SelectorMatch.AlwaysThisType : SelectorMatch.NeverThisType; } diff --git a/tests/Avalonia.Base.UnitTests/Styling/StyledElementTests_Theming.cs b/tests/Avalonia.Base.UnitTests/Styling/StyledElementTests_Theming.cs index 737cf1e048..ab6c239393 100644 --- a/tests/Avalonia.Base.UnitTests/Styling/StyledElementTests_Theming.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/StyledElementTests_Theming.cs @@ -34,6 +34,27 @@ public class StyledElementTests_Theming Assert.Equal(Brushes.Green, border.Background); } + [Fact] + public void Theme_Is_Applied_To_Derived_Class_When_Attached_To_Logical_Tree() + { + using var app = UnitTestApplication.Start(TestServices.RealStyler); + var target = new DerivedThemedControl + { + Theme = CreateTheme(), + }; + + Assert.Null(target.Template); + + var root = CreateRoot(target); + Assert.NotNull(target.Template); + + var border = Assert.IsType(target.VisualChild); + Assert.Equal(Brushes.Red, border.Background); + + target.Classes.Add("foo"); + Assert.Equal(Brushes.Green, border.Background); + } + [Fact] public void Theme_Is_Detached_When_Theme_Property_Cleared() { @@ -252,4 +273,8 @@ public class StyledElementTests_Theming { public IVisual? VisualChild => VisualChildren?.SingleOrDefault(); } + + private class DerivedThemedControl : ThemedControl + { + } } From d21e634ab308c1b63d1e2f2105de29c8af236b2a Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 8 Jun 2022 12:00:14 +0200 Subject: [PATCH 023/151] Added support for implicit themes. If no `Theme` property is provided, try to look up a resource keyed with the control's `StyleKey`. --- src/Avalonia.Base/StyledElement.cs | 28 ++++++++++++ src/Avalonia.Base/Styling/IStyleable.cs | 4 +- src/Avalonia.Base/Styling/Styler.cs | 4 +- .../Styling/StyledElementTests_Theming.cs | 43 +++++++++++++++++++ .../AvaloniaPropertyConverterTest.cs | 4 +- .../DynamicResourceExtensionTests.cs | 6 ++- 6 files changed, 81 insertions(+), 8 deletions(-) diff --git a/src/Avalonia.Base/StyledElement.cs b/src/Avalonia.Base/StyledElement.cs index 75c4b94174..f377eb848c 100644 --- a/src/Avalonia.Base/StyledElement.cs +++ b/src/Avalonia.Base/StyledElement.cs @@ -60,6 +60,7 @@ namespace Avalonia public static readonly StyledProperty ThemeProperty = AvaloniaProperty.Register(nameof(Theme)); + private static readonly ControlTheme s_invalidTheme = new ControlTheme(); private int _initCount; private string? _name; private readonly Classes _classes = new Classes(); @@ -72,6 +73,7 @@ namespace Avalonia private ITemplatedControl? _templatedParent; private bool _dataContextUpdating; private bool _hasPromotedTheme; + private ControlTheme? _implicitTheme; /// /// Initializes static members of the class. @@ -495,6 +497,31 @@ namespace Avalonia }; } + ControlTheme? IStyleable.GetEffectiveTheme() + { + var theme = Theme; + + // Explitly set Theme property takes precedence. + if (theme is not null) + return theme; + + // If the Theme property is not set, try to find a ControlTheme resource with our StyleKey. + if (_implicitTheme is null) + { + var key = ((IStyleable)this).StyleKey; + + if (this.TryFindResource(key, out var value) && value is ControlTheme t) + _implicitTheme = t; + else + _implicitTheme = s_invalidTheme; + } + + if (_implicitTheme != s_invalidTheme) + return _implicitTheme; + + return null; + } + void IStyleable.StyleApplied(IStyleInstance instance) { instance = instance ?? throw new ArgumentNullException(nameof(instance)); @@ -736,6 +763,7 @@ namespace Avalonia if (_logicalRoot != null) { _logicalRoot = null; + _implicitTheme = null; DetachStyles(); OnDetachedFromLogicalTree(e); DetachedFromLogicalTree?.Invoke(this, e); diff --git a/src/Avalonia.Base/Styling/IStyleable.cs b/src/Avalonia.Base/Styling/IStyleable.cs index 61fcbdf850..254da4d85c 100644 --- a/src/Avalonia.Base/Styling/IStyleable.cs +++ b/src/Avalonia.Base/Styling/IStyleable.cs @@ -27,9 +27,9 @@ namespace Avalonia.Styling ITemplatedControl? TemplatedParent { get; } /// - /// Gets the theme to be applied to the control. + /// Gets the effective theme for the control as used by the syling system. /// - public ControlTheme? Theme { get; } + ControlTheme? GetEffectiveTheme(); /// /// Notifies the element that a style has been applied. diff --git a/src/Avalonia.Base/Styling/Styler.cs b/src/Avalonia.Base/Styling/Styler.cs index c9ea123bdc..6ac2e8d372 100644 --- a/src/Avalonia.Base/Styling/Styler.cs +++ b/src/Avalonia.Base/Styling/Styler.cs @@ -11,10 +11,10 @@ namespace Avalonia.Styling // If the control has a themed templated parent then first apply the styles from // the templated parent theme. if (target.TemplatedParent is IStyleable styleableParent) - styleableParent.Theme?.TryAttach(target, styleableParent); + styleableParent.GetEffectiveTheme()?.TryAttach(target, styleableParent); // Next apply the control theme. - target.Theme?.TryAttach(target, target); + target.GetEffectiveTheme()?.TryAttach(target, target); // Apply styles from the rest of the tree. if (target is IStyleHost styleHost) diff --git a/tests/Avalonia.Base.UnitTests/Styling/StyledElementTests_Theming.cs b/tests/Avalonia.Base.UnitTests/Styling/StyledElementTests_Theming.cs index ab6c239393..522937b669 100644 --- a/tests/Avalonia.Base.UnitTests/Styling/StyledElementTests_Theming.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/StyledElementTests_Theming.cs @@ -158,6 +158,49 @@ public class StyledElementTests_Theming } } + public class ImplicitTheme + { + [Fact] + public void Implicit_Theme_Is_Applied_When_Attached_To_Logical_Tree() + { + using var app = UnitTestApplication.Start(TestServices.RealStyler); + var target = CreateTarget(); + var root = CreateRoot(target); + Assert.NotNull(target.Template); + + var border = Assert.IsType(target.VisualChild); + Assert.Equal(Brushes.Red, border.Background); + + target.Classes.Add("foo"); + Assert.Equal(Brushes.Green, border.Background); + } + + [Fact] + public void Implicit_Theme_Is_Cleared_When_Removed_From_Logical_Tree() + { + using var app = UnitTestApplication.Start(TestServices.RealStyler); + var target = CreateTarget(); + var root = CreateRoot(target); + + Assert.NotNull(((IStyleable)target).GetEffectiveTheme()); + + root.Child = null; + + Assert.Null(((IStyleable)target).GetEffectiveTheme()); + } + + private static ThemedControl CreateTarget() => new ThemedControl(); + + private static TestRoot CreateRoot(IControl child) + { + var result = new TestRoot(); + result.Resources.Add(typeof(ThemedControl), CreateTheme()); + result.Child = child; + result.LayoutManager.ExecuteInitialLayoutPass(); + return result; + } + } + public class ThemeFromStyle { [Fact] diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Converters/AvaloniaPropertyConverterTest.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Converters/AvaloniaPropertyConverterTest.cs index ca59fe8480..75e21a7138 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Converters/AvaloniaPropertyConverterTest.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Converters/AvaloniaPropertyConverterTest.cs @@ -137,9 +137,9 @@ namespace Avalonia.Markup.Xaml.UnitTests.Converters get { throw new NotImplementedException(); } } - public ControlTheme Theme + public ControlTheme GetEffectiveTheme() { - get { throw new NotImplementedException(); } + throw new NotImplementedException(); } public void DetachStyles() diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs index 592dbfc0d1..987725c314 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs @@ -845,7 +845,8 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions Assert.Equal("bar", border.Tag); var resourceProvider = (TrackingResourceProvider)window.Resources.MergedDictionaries[0]; - Assert.Equal(new[] { "bar" }, resourceProvider.RequestedResources); + Assert.Contains("bar", resourceProvider.RequestedResources); + Assert.DoesNotContain("foo", resourceProvider.RequestedResources); } [Fact] @@ -883,7 +884,8 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions Assert.Equal("bar", border.Tag); var resourceProvider = (TrackingResourceProvider)window.Resources.MergedDictionaries[0]; - Assert.Equal(new[] { "bar" }, resourceProvider.RequestedResources); + Assert.Contains("bar", resourceProvider.RequestedResources); + Assert.DoesNotContain("foo", resourceProvider.RequestedResources); } private IDisposable StyledWindow(params (string, string)[] assets) From 8b4cf63be3ffbf29427bb16d15ff514c595d348b Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 10 Jun 2022 11:18:22 +0200 Subject: [PATCH 024/151] Additional validation for ControlTheme children. --- src/Avalonia.Base/Styling/ChildSelector.cs | 2 +- .../Styling/DescendentSelector.cs | 2 +- src/Avalonia.Base/Styling/NestingSelector.cs | 2 +- src/Avalonia.Base/Styling/NotSelector.cs | 2 +- src/Avalonia.Base/Styling/NthChildSelector.cs | 2 +- src/Avalonia.Base/Styling/OrSelector.cs | 12 +--- .../Styling/PropertyEqualsSelector.cs | 2 +- src/Avalonia.Base/Styling/Selector.cs | 31 ++++++++- src/Avalonia.Base/Styling/Style.cs | 9 ++- src/Avalonia.Base/Styling/Styles.cs | 5 ++ src/Avalonia.Base/Styling/TemplateSelector.cs | 2 +- .../Styling/TypeNameAndClassSelector.cs | 2 +- .../Styling/ControlThemeTests.cs | 64 +++++++++++++++++++ 13 files changed, 117 insertions(+), 20 deletions(-) diff --git a/src/Avalonia.Base/Styling/ChildSelector.cs b/src/Avalonia.Base/Styling/ChildSelector.cs index 34f3a76b61..9512dc34df 100644 --- a/src/Avalonia.Base/Styling/ChildSelector.cs +++ b/src/Avalonia.Base/Styling/ChildSelector.cs @@ -65,6 +65,6 @@ namespace Avalonia.Styling } protected override Selector? MovePrevious() => null; - internal override bool HasValidNestingSelector() => _parent.HasValidNestingSelector(); + protected override Selector? MovePreviousOrParent() => _parent; } } diff --git a/src/Avalonia.Base/Styling/DescendentSelector.cs b/src/Avalonia.Base/Styling/DescendentSelector.cs index 4ffaff6861..677a924189 100644 --- a/src/Avalonia.Base/Styling/DescendentSelector.cs +++ b/src/Avalonia.Base/Styling/DescendentSelector.cs @@ -70,6 +70,6 @@ namespace Avalonia.Styling } protected override Selector? MovePrevious() => null; - internal override bool HasValidNestingSelector() => _parent.HasValidNestingSelector(); + protected override Selector? MovePreviousOrParent() => _parent; } } diff --git a/src/Avalonia.Base/Styling/NestingSelector.cs b/src/Avalonia.Base/Styling/NestingSelector.cs index 4393d3239f..77c5b719c6 100644 --- a/src/Avalonia.Base/Styling/NestingSelector.cs +++ b/src/Avalonia.Base/Styling/NestingSelector.cs @@ -33,6 +33,6 @@ namespace Avalonia.Styling } protected override Selector? MovePrevious() => null; - internal override bool HasValidNestingSelector() => true; + protected override Selector? MovePreviousOrParent() => null; } } diff --git a/src/Avalonia.Base/Styling/NotSelector.cs b/src/Avalonia.Base/Styling/NotSelector.cs index 76a0690e96..c7727bb6b8 100644 --- a/src/Avalonia.Base/Styling/NotSelector.cs +++ b/src/Avalonia.Base/Styling/NotSelector.cs @@ -67,6 +67,6 @@ namespace Avalonia.Styling } protected override Selector? MovePrevious() => _previous; - internal override bool HasValidNestingSelector() => _previous?.HasValidNestingSelector() ?? false; + protected override Selector? MovePreviousOrParent() => _previous; } } diff --git a/src/Avalonia.Base/Styling/NthChildSelector.cs b/src/Avalonia.Base/Styling/NthChildSelector.cs index 047bf434da..f473791664 100644 --- a/src/Avalonia.Base/Styling/NthChildSelector.cs +++ b/src/Avalonia.Base/Styling/NthChildSelector.cs @@ -105,7 +105,7 @@ namespace Avalonia.Styling } protected override Selector? MovePrevious() => _previous; - internal override bool HasValidNestingSelector() => _previous?.HasValidNestingSelector() ?? false; + protected override Selector? MovePreviousOrParent() => _previous; public override string ToString() { diff --git a/src/Avalonia.Base/Styling/OrSelector.cs b/src/Avalonia.Base/Styling/OrSelector.cs index 913c27bf0c..af9249864f 100644 --- a/src/Avalonia.Base/Styling/OrSelector.cs +++ b/src/Avalonia.Base/Styling/OrSelector.cs @@ -103,18 +103,12 @@ namespace Avalonia.Styling } protected override Selector? MovePrevious() => null; + protected override Selector? MovePreviousOrParent() => null; - internal override bool HasValidNestingSelector() + internal override void ValidateNestingSelector(bool inControlTheme) { foreach (var selector in _selectors) - { - if (!selector.HasValidNestingSelector()) - { - return false; - } - } - - return true; + selector.ValidateNestingSelector(inControlTheme); } private Type? EvaluateTargetType() diff --git a/src/Avalonia.Base/Styling/PropertyEqualsSelector.cs b/src/Avalonia.Base/Styling/PropertyEqualsSelector.cs index 7a37daf087..48136ba2de 100644 --- a/src/Avalonia.Base/Styling/PropertyEqualsSelector.cs +++ b/src/Avalonia.Base/Styling/PropertyEqualsSelector.cs @@ -90,7 +90,7 @@ namespace Avalonia.Styling } protected override Selector? MovePrevious() => _previous; - internal override bool HasValidNestingSelector() => _previous?.HasValidNestingSelector() ?? false; + protected override Selector? MovePreviousOrParent() => _previous; internal static bool Compare(Type propertyType, object? propertyValue, object? value) { diff --git a/src/Avalonia.Base/Styling/Selector.cs b/src/Avalonia.Base/Styling/Selector.cs index 1e06f3d375..7ce17518dd 100644 --- a/src/Avalonia.Base/Styling/Selector.cs +++ b/src/Avalonia.Base/Styling/Selector.cs @@ -86,7 +86,36 @@ namespace Avalonia.Styling /// protected abstract Selector? MovePrevious(); - internal abstract bool HasValidNestingSelector(); + /// + /// Moves to the previous selector or the parent selector. + /// + protected abstract Selector? MovePreviousOrParent(); + + internal virtual void ValidateNestingSelector(bool inControlTheme) + { + var s = this; + var templateCount = 0; + + do + { + if (inControlTheme) + { + if (!s.InTemplate && s.IsCombinator) + throw new InvalidOperationException( + "ControlTheme style may not directly contain a child or descendent selector."); + if (s is TemplateSelector && templateCount++ > 0) + throw new InvalidOperationException( + "ControlTemplate styles cannot contain multiple template selectors."); + } + + var previous = s.MovePreviousOrParent(); + + if (previous is null && s is not NestingSelector) + throw new InvalidOperationException("Child styles must have a nesting selector."); + + s = previous; + } while (s is not null); + } private static SelectorMatch MatchUntilCombinator( IStyleable control, diff --git a/src/Avalonia.Base/Styling/Style.cs b/src/Avalonia.Base/Styling/Style.cs index 7a6b746488..77c4e62d29 100644 --- a/src/Avalonia.Base/Styling/Style.cs +++ b/src/Avalonia.Base/Styling/Style.cs @@ -77,8 +77,13 @@ namespace Avalonia.Styling { if (Selector is null) throw new InvalidOperationException("Child styles must have a selector."); - if (!Selector.HasValidNestingSelector()) - throw new InvalidOperationException("Child styles must have a nesting selector."); + Selector.ValidateNestingSelector(false); + } + else if (parent is ControlTheme) + { + if (Selector is null) + throw new InvalidOperationException("Child styles must have a selector."); + Selector.ValidateNestingSelector(true); } base.SetParent(parent); diff --git a/src/Avalonia.Base/Styling/Styles.cs b/src/Avalonia.Base/Styling/Styles.cs index 4c011f1b0d..3a27275438 100644 --- a/src/Avalonia.Base/Styling/Styles.cs +++ b/src/Avalonia.Base/Styling/Styles.cs @@ -26,6 +26,11 @@ namespace Avalonia.Styling { _styles.ResetBehavior = ResetBehavior.Remove; _styles.CollectionChanged += OnCollectionChanged; + _styles.Validate = i => + { + if (i is ControlTheme) + throw new InvalidOperationException("ControlThemes cannot be added to a Styles collection."); + }; } public Styles(IResourceHost owner) diff --git a/src/Avalonia.Base/Styling/TemplateSelector.cs b/src/Avalonia.Base/Styling/TemplateSelector.cs index b0a2dae8d6..278e24a203 100644 --- a/src/Avalonia.Base/Styling/TemplateSelector.cs +++ b/src/Avalonia.Base/Styling/TemplateSelector.cs @@ -49,6 +49,6 @@ namespace Avalonia.Styling } protected override Selector? MovePrevious() => null; - internal override bool HasValidNestingSelector() => _parent?.HasValidNestingSelector() ?? false; + protected override Selector? MovePreviousOrParent() => _parent; } } diff --git a/src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs b/src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs index 24d5d6bbbf..6681a7da36 100644 --- a/src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs +++ b/src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs @@ -140,7 +140,7 @@ namespace Avalonia.Styling } protected override Selector? MovePrevious() => _previous; - internal override bool HasValidNestingSelector() => _previous?.HasValidNestingSelector() ?? false; + protected override Selector? MovePreviousOrParent() => _previous; private string BuildSelectorString() { diff --git a/tests/Avalonia.Base.UnitTests/Styling/ControlThemeTests.cs b/tests/Avalonia.Base.UnitTests/Styling/ControlThemeTests.cs index 93a0e6c2fd..7a27a02fc4 100644 --- a/tests/Avalonia.Base.UnitTests/Styling/ControlThemeTests.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/ControlThemeTests.cs @@ -1,5 +1,6 @@ using System; using Avalonia.Controls; +using Avalonia.Controls.Primitives; using Avalonia.Styling; using Xunit; @@ -7,6 +8,15 @@ namespace Avalonia.Base.UnitTests.Styling { public class ControlThemeTests { + [Fact] + public void ControlTheme_Cannot_Be_Added_To_Styles() + { + var target = new ControlTheme(typeof(Button)); + var styles = new Styles(); + + Assert.Throws(() => styles.Add(target)); + } + [Fact] public void ControlTheme_Cannot_Be_Added_To_Style_Children() { @@ -24,5 +34,59 @@ namespace Avalonia.Base.UnitTests.Styling Assert.Throws(() => other.Children.Add(target)); } + + [Fact] + public void Style_Without_Selector_Cannot_Be_Added_To_Children() + { + var target = new ControlTheme(typeof(Button)); + var child = new Style(); + + Assert.Throws(() => target.Children.Add(child)); + } + + [Fact] + public void Style_Without_Nesting_Selector_Cannot_Be_Added_To_Children() + { + var target = new ControlTheme(typeof(Button)); + var child = new Style(x => x.OfType IEnumerable Containers { get; } + /// + /// Gets or sets the theme to be applied to the items in the control. + /// + ControlTheme? ItemContainerTheme { get; set; } + /// /// Gets or sets the data template used to display the items in the control. /// diff --git a/src/Avalonia.Controls/Generators/ItemContainerGenerator.cs b/src/Avalonia.Controls/Generators/ItemContainerGenerator.cs index a76dcbe9c8..8b36b07cec 100644 --- a/src/Avalonia.Controls/Generators/ItemContainerGenerator.cs +++ b/src/Avalonia.Controls/Generators/ItemContainerGenerator.cs @@ -4,6 +4,7 @@ using System.Linq; using Avalonia.Controls.Presenters; using Avalonia.Controls.Templates; using Avalonia.Data; +using Avalonia.Styling; namespace Avalonia.Controls.Generators { @@ -35,6 +36,11 @@ namespace Avalonia.Controls.Generators /// public event EventHandler? Recycled; + /// + /// Gets or sets the theme to be applied to the items in the control. + /// + public ControlTheme? ItemContainerTheme { get; set; } + /// /// Gets or sets the data template used to display the items in the control. /// @@ -190,10 +196,18 @@ namespace Avalonia.Controls.Generators result.SetValue( ContentPresenter.ContentTemplateProperty, ItemTemplate, - BindingPriority.TemplatedParent); + BindingPriority.Style); } } + if (ItemContainerTheme != null) + { + result.SetValue( + StyledElement.ThemeProperty, + ItemContainerTheme, + BindingPriority.TemplatedParent); + } + return result; } diff --git a/src/Avalonia.Controls/Generators/ItemContainerGenerator`1.cs b/src/Avalonia.Controls/Generators/ItemContainerGenerator`1.cs index 635f3a7d37..3ff1b0702d 100644 --- a/src/Avalonia.Controls/Generators/ItemContainerGenerator`1.cs +++ b/src/Avalonia.Controls/Generators/ItemContainerGenerator`1.cs @@ -44,28 +44,29 @@ namespace Avalonia.Controls.Generators { var container = item as T; - if (container != null) + if (container is null) { - return container; - } - else - { - var result = new T(); + container = new T(); if (ContentTemplateProperty != null) { - result.SetValue(ContentTemplateProperty, ItemTemplate, BindingPriority.Style); + container.SetValue(ContentTemplateProperty, ItemTemplate, BindingPriority.Style); } - result.SetValue(ContentProperty, item, BindingPriority.Style); + container.SetValue(ContentProperty, item, BindingPriority.Style); if (!(item is IControl)) { - result.DataContext = item; + container.DataContext = item; } + } - return result; + if (ItemContainerTheme != null) + { + container.SetValue(StyledElement.ThemeProperty, ItemContainerTheme, BindingPriority.Style); } + + return container; } /// diff --git a/src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs b/src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs index 536a5fdd06..4e3deb5552 100644 --- a/src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs +++ b/src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs @@ -71,6 +71,11 @@ namespace Avalonia.Controls.Generators var template = GetTreeDataTemplate(item, ItemTemplate); var result = new T(); + if (ItemContainerTheme != null) + { + result.SetValue(Control.ThemeProperty, ItemContainerTheme, BindingPriority.Style); + } + result.SetValue(ContentProperty, template.Build(item), BindingPriority.Style); var itemsSelector = template.ItemsSelector(item); diff --git a/src/Avalonia.Controls/ItemsControl.cs b/src/Avalonia.Controls/ItemsControl.cs index 56b0014c05..427f5c8a9b 100644 --- a/src/Avalonia.Controls/ItemsControl.cs +++ b/src/Avalonia.Controls/ItemsControl.cs @@ -15,6 +15,7 @@ using Avalonia.Input; using Avalonia.LogicalTree; using Avalonia.Metadata; using Avalonia.VisualTree; +using Avalonia.Styling; namespace Avalonia.Controls { @@ -36,6 +37,12 @@ namespace Avalonia.Controls public static readonly DirectProperty ItemsProperty = AvaloniaProperty.RegisterDirect(nameof(Items), o => o.Items, (o, v) => o.Items = v); + /// + /// Defines the property. + /// + public static readonly StyledProperty ItemContainerThemeProperty = + AvaloniaProperty.Register(nameof(ItemContainerTheme)); + /// /// Defines the property. /// @@ -88,6 +95,7 @@ namespace Avalonia.Controls { _itemContainerGenerator = CreateItemContainerGenerator(); + _itemContainerGenerator.ItemContainerTheme = ItemContainerTheme; _itemContainerGenerator.ItemTemplate = ItemTemplate; _itemContainerGenerator.Materialized += (_, e) => OnContainersMaterialized(e); _itemContainerGenerator.Dematerialized += (_, e) => OnContainersDematerialized(e); @@ -108,6 +116,15 @@ namespace Avalonia.Controls set { SetAndRaise(ItemsProperty, ref _items, value); } } + /// + /// Gets or sets the that is applied to the container element generated for each item. + /// + public ControlTheme? ItemContainerTheme + { + get { return GetValue(ItemContainerThemeProperty); } + set { SetValue(ItemContainerThemeProperty, value); } + } + /// /// Gets the number of items in . /// @@ -349,6 +366,10 @@ namespace Avalonia.Controls { UpdatePseudoClasses(change.GetNewValue()); } + else if (change.Property == ItemContainerThemeProperty && _itemContainerGenerator is not null) + { + _itemContainerGenerator.ItemContainerTheme = change.GetNewValue(); + } } /// diff --git a/src/Avalonia.Controls/TreeViewItem.cs b/src/Avalonia.Controls/TreeViewItem.cs index 490b0b3ce3..2e3aa037c2 100644 --- a/src/Avalonia.Controls/TreeViewItem.cs +++ b/src/Avalonia.Controls/TreeViewItem.cs @@ -121,6 +121,11 @@ namespace Avalonia.Controls { ItemTemplate = _treeView.ItemTemplate; } + + if (ItemContainerTheme == null && _treeView?.ItemContainerTheme != null) + { + ItemContainerTheme = _treeView.ItemContainerTheme; + } } protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e) diff --git a/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs b/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs index bfece7871c..f08653a4f8 100644 --- a/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs @@ -1,15 +1,14 @@ +using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Linq; using Avalonia.Collections; using Avalonia.Controls.Presenters; using Avalonia.Controls.Templates; +using Avalonia.Input; using Avalonia.LogicalTree; -using Avalonia.VisualTree; -using Xunit; -using System.Collections.ObjectModel; +using Avalonia.Styling; using Avalonia.UnitTests; -using Avalonia.Input; -using System.Collections.Generic; +using Xunit; namespace Avalonia.Controls.UnitTests { @@ -62,6 +61,25 @@ namespace Avalonia.Controls.UnitTests Assert.Null(container.TemplatedParent); } + [Fact] + public void Container_Should_Have_Theme_Set_To_ItemContainerTheme() + { + var theme = new ControlTheme(); + var target = new ItemsControl + { + ItemContainerTheme = theme, + }; + + target.Template = GetTemplate(); + target.Items = new[] { "Foo" }; + target.ApplyTemplate(); + target.Presenter.ApplyTemplate(); + + var container = (ContentPresenter)target.Presenter.Panel.Children[0]; + + Assert.Same(container.Theme, theme); + } + [Fact] public void Container_Should_Have_LogicalParent_Set_To_ItemsControl() { diff --git a/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs b/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs index e87990ebb1..afa153a593 100644 --- a/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs @@ -94,6 +94,50 @@ namespace Avalonia.Controls.UnitTests } } + [Fact] + public void Container_Should_Have_Theme_Set_To_ItemContainerTheme() + { + using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface)) + { + var items = new[] { "Foo", "Bar", "Baz " }; + var theme = new ControlTheme(); + var target = new ListBox + { + Template = ListBoxTemplate(), + Items = items, + ItemContainerTheme = theme, + }; + + Prepare(target); + + var container = (ListBoxItem)target.Presenter.Panel.Children[0]; + + Assert.Same(container.Theme, theme); + } + } + + [Fact] + public void Inline_Item_Should_Have_Theme_Set_To_ItemContainerTheme() + { + using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface)) + { + var items = new[] { "Foo", "Bar", "Baz " }; + var theme = new ControlTheme(); + var target = new ListBox + { + Template = ListBoxTemplate(), + Items = new[] { new ListBoxItem() }, + ItemContainerTheme = theme, + }; + + Prepare(target); + + var container = (ListBoxItem)target.Presenter.Panel.Children[0]; + + Assert.Same(container.Theme, theme); + } + } + [Fact] public void LogicalChildren_Should_Be_Set_For_DataTemplate_Generated_Items() { diff --git a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs index 2169b15cad..d784caf2db 100644 --- a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs @@ -72,6 +72,35 @@ namespace Avalonia.Controls.UnitTests Assert.All(items, x => Assert.IsType(x.HeaderPresenter.Child)); } + [Fact] + public void Items_Should_Be_Created_Using_ItemConatinerTheme_If_Present() + { + TreeView target; + var theme = new ControlTheme(); + + var root = new TestRoot + { + Child = target = new TreeView + { + Template = CreateTreeViewTemplate(), + Items = CreateTestTreeData(), + ItemContainerTheme = theme, + ItemTemplate = new FuncTreeDataTemplate( + (_, __) => new Canvas(), + x => x.Children), + } + }; + + ApplyTemplates(target); + + var items = target.ItemContainerGenerator.Index.Containers + .OfType() + .ToList(); + + Assert.Equal(5, items.Count); + Assert.All(items, x => Assert.Same(theme, x.ItemContainerTheme)); + } + [Fact] public void Root_ItemContainerGenerator_Containers_Should_Be_Root_Containers() { From 7cbeb7ad3f4aac3412efd662d5dac4f6f984cdbb Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Fri, 8 Jul 2022 18:56:29 +0200 Subject: [PATCH 040/151] Rework TextLineImpl hit testing --- .../ControlCatalog/Pages/TextBlockPage.xaml | 2 +- samples/Sandbox/MainWindow.axaml | 1 + samples/Sandbox/Sandbox.csproj | 1 + src/Avalonia.Base/Media/GlyphRun.cs | 109 ++++- .../Media/TextFormatting/TextLineImpl.cs | 371 +++++++++--------- .../Media/TextFormatting/TextLayoutTests.cs | 84 +++- 6 files changed, 366 insertions(+), 202 deletions(-) diff --git a/samples/ControlCatalog/Pages/TextBlockPage.xaml b/samples/ControlCatalog/Pages/TextBlockPage.xaml index cb49ba96c6..32914428ed 100644 --- a/samples/ControlCatalog/Pages/TextBlockPage.xaml +++ b/samples/ControlCatalog/Pages/TextBlockPage.xaml @@ -118,7 +118,7 @@ - + This is a TextBlock with several diff --git a/samples/Sandbox/MainWindow.axaml b/samples/Sandbox/MainWindow.axaml index 6929f192c7..0c5a7a11e3 100644 --- a/samples/Sandbox/MainWindow.axaml +++ b/samples/Sandbox/MainWindow.axaml @@ -1,4 +1,5 @@ + diff --git a/samples/Sandbox/Sandbox.csproj b/samples/Sandbox/Sandbox.csproj index eab654acb6..20c7f29201 100644 --- a/samples/Sandbox/Sandbox.csproj +++ b/samples/Sandbox/Sandbox.csproj @@ -10,6 +10,7 @@ + diff --git a/src/Avalonia.Base/Media/GlyphRun.cs b/src/Avalonia.Base/Media/GlyphRun.cs index ac87d521a5..703b56b0e8 100644 --- a/src/Avalonia.Base/Media/GlyphRun.cs +++ b/src/Avalonia.Base/Media/GlyphRun.cs @@ -614,17 +614,29 @@ namespace Avalonia.Media private GlyphRunMetrics CreateGlyphRunMetrics() { + var firstCluster = 0; + var lastCluster = Characters.Length - 1; + + if (!IsLeftToRight) + { + var cluster = firstCluster; + firstCluster = lastCluster; + lastCluster = cluster; + } + if (GlyphClusters != null && GlyphClusters.Count > 0) { - var firstCluster = GlyphClusters[0]; + firstCluster = GlyphClusters[0]; + lastCluster = GlyphClusters[GlyphClusters.Count - 1]; _offsetToFirstCharacter = Math.Max(0, Characters.Start - firstCluster); } + var isReversed = firstCluster > lastCluster; var height = (GlyphTypeface.Descent - GlyphTypeface.Ascent + GlyphTypeface.LineGap) * Scale; var widthIncludingTrailingWhitespace = 0d; - var trailingWhitespaceLength = GetTrailingWhitespaceLength(out var newLineLength, out var glyphCount); + var trailingWhitespaceLength = GetTrailingWhitespaceLength(isReversed, out var newLineLength, out var glyphCount); for (var index = 0; index < GlyphIndices.Count; index++) { @@ -635,16 +647,16 @@ namespace Avalonia.Media var width = widthIncludingTrailingWhitespace; - if (IsLeftToRight) + if (isReversed) { - for (var index = GlyphIndices.Count - glyphCount; index < GlyphIndices.Count; index++) + for (var index = 0; index < glyphCount; index++) { width -= GetGlyphAdvance(index, out _); - } + } } else { - for (var index = 0; index < glyphCount; index++) + for (var index = GlyphIndices.Count - glyphCount; index < GlyphIndices.Count; index++) { width -= GetGlyphAdvance(index, out _); } @@ -654,16 +666,15 @@ namespace Avalonia.Media height); } - private int GetTrailingWhitespaceLength(out int newLineLength, out int glyphCount) - { - glyphCount = 0; - newLineLength = 0; - - if (Characters.IsEmpty) + private int GetTrailingWhitespaceLength(bool isReversed, out int newLineLength, out int glyphCount) + { + if (isReversed) { - return 0; + return GetTralingWhitespaceLengthRightToLeft(out newLineLength, out glyphCount); } + glyphCount = 0; + newLineLength = 0; var trailingWhitespaceLength = 0; if (GlyphClusters == null) @@ -732,6 +743,78 @@ namespace Avalonia.Media return trailingWhitespaceLength; } + private int GetTralingWhitespaceLengthRightToLeft(out int newLineLength, out int glyphCount) + { + glyphCount = 0; + newLineLength = 0; + var trailingWhitespaceLength = 0; + + if (GlyphClusters == null) + { + for (var i = 0; i < Characters.Length;) + { + var codepoint = Codepoint.ReadAt(_characters, i, out var count); + + if (!codepoint.IsWhiteSpace) + { + break; + } + + if (codepoint.IsBreakChar) + { + newLineLength++; + } + + trailingWhitespaceLength++; + + i += count; + glyphCount++; + } + } + else + { + for (var i = 0; i < GlyphClusters.Count; i++) + { + var currentCluster = GlyphClusters[i]; + var characterIndex = Math.Max(0, currentCluster - _characters.BufferOffset); + var codepoint = Codepoint.ReadAt(_characters, characterIndex, out _); + + if (!codepoint.IsWhiteSpace) + { + break; + } + + var clusterLength = 1; + + while (i - 1 >= 0) + { + var nextCluster = GlyphClusters[i - 1]; + + if (currentCluster == nextCluster) + { + clusterLength++; + i--; + + continue; + } + + break; + } + + if (codepoint.IsBreakChar) + { + newLineLength += clusterLength; + } + + trailingWhitespaceLength += clusterLength; + + glyphCount++; + } + } + + return trailingWhitespaceLength; + } + private void Set(ref T field, T value) { _glyphRunImpl?.Dispose(); diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs index 7c686358e2..f4a0324d90 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs @@ -166,58 +166,74 @@ namespace Avalonia.Media.TextFormatting if (distance <= 0) { - // hit happens before the line, return the first position var firstRun = _textRuns[0]; - if (firstRun is ShapedTextCharacters shapedTextCharacters) - { - return shapedTextCharacters.GlyphRun.GetCharacterHitFromDistance(distance, out _); - } + return GetRunCharacterHit(firstRun, FirstTextSourceIndex, 0); + } - return _resolvedFlowDirection == FlowDirection.LeftToRight ? - new CharacterHit(FirstTextSourceIndex) : - new CharacterHit(FirstTextSourceIndex + Length); + if (distance > WidthIncludingTrailingWhitespace) + { + var lastRun = _textRuns[_textRuns.Count - 1]; + + return GetRunCharacterHit(lastRun, FirstTextSourceIndex + Length - lastRun.TextSourceLength, lastRun.Size.Width); } // process hit that happens within the line var characterHit = new CharacterHit(); var currentPosition = FirstTextSourceIndex; + var currentDistance = 0.0; foreach (var currentRun in _textRuns) { - switch (currentRun) + if (currentDistance + currentRun.Size.Width < distance) { - case ShapedTextCharacters shapedRun: - { - characterHit = shapedRun.GlyphRun.GetCharacterHitFromDistance(distance, out _); + currentDistance += currentRun.Size.Width; + currentPosition += currentRun.TextSourceLength; - var offset = Math.Max(0, currentPosition - shapedRun.Text.Start); + continue; + } - characterHit = new CharacterHit(characterHit.FirstCharacterIndex + offset, characterHit.TrailingLength); + characterHit = GetRunCharacterHit(currentRun, currentPosition, distance - currentDistance); - break; - } - default: + break; + } + + return characterHit; + } + + private static CharacterHit GetRunCharacterHit(DrawableTextRun run, int currentPosition, double distance) + { + CharacterHit characterHit; + + switch (run) + { + case ShapedTextCharacters shapedRun: + { + characterHit = shapedRun.GlyphRun.GetCharacterHitFromDistance(distance, out _); + + var offset = Math.Max(0, currentPosition - shapedRun.Text.Start); + + if (!shapedRun.GlyphRun.IsLeftToRight) { - if (distance < currentRun.Size.Width / 2) - { - characterHit = new CharacterHit(currentPosition); - } - else - { - characterHit = new CharacterHit(currentPosition, currentRun.TextSourceLength); - } - break; + offset = Math.Max(0, offset - shapedRun.Text.End); } - } - if (distance <= currentRun.Size.Width) - { - break; - } + characterHit = new CharacterHit(characterHit.FirstCharacterIndex + offset, characterHit.TrailingLength); - distance -= currentRun.Size.Width; - currentPosition += currentRun.TextSourceLength; + break; + } + default: + { + if (distance < run.Size.Width / 2) + { + characterHit = new CharacterHit(currentPosition); + } + else + { + characterHit = new CharacterHit(currentPosition, run.TextSourceLength); + } + break; + } } return characterHit; @@ -226,136 +242,122 @@ namespace Avalonia.Media.TextFormatting /// public override double GetDistanceFromCharacterHit(CharacterHit characterHit) { - var isTrailingHit = characterHit.TrailingLength > 0; + var flowDirection = _paragraphProperties.FlowDirection; var characterIndex = characterHit.FirstCharacterIndex + characterHit.TrailingLength; - var currentDistance = Start; var currentPosition = FirstTextSourceIndex; var remainingLength = characterIndex - FirstTextSourceIndex; - GlyphRun? lastRun = null; + var currentDistance = Start; - for (var index = 0; index < _textRuns.Count; index++) + if (flowDirection == FlowDirection.LeftToRight) + { + for (var index = 0; index < _textRuns.Count; index++) + { + var currentRun = _textRuns[index]; + + if (TryGetDistanceFromCharacterHit(currentRun, characterHit, currentPosition, remainingLength, + flowDirection, out var distance, out _)) + { + return currentDistance + distance; + } + + //No hit hit found so we add the full width + currentDistance += currentRun.Size.Width; + currentPosition += currentRun.TextSourceLength; + remainingLength -= currentRun.TextSourceLength; + } + } + else { - var textRun = _textRuns[index]; + currentDistance += WidthIncludingTrailingWhitespace; - switch (textRun) + for (var index = _textRuns.Count - 1; index >= 0; index--) { - case ShapedTextCharacters shapedTextCharacters: + var currentRun = _textRuns[index]; + + if (TryGetDistanceFromCharacterHit(currentRun, characterHit, currentPosition, remainingLength, + flowDirection, out var distance, out var currentGlyphRun)) + { + if (currentGlyphRun != null) { - var currentRun = shapedTextCharacters.GlyphRun; + distance = currentGlyphRun.Size.Width - distance; + } - if (lastRun != null) - { - if (!lastRun.IsLeftToRight && currentRun.IsLeftToRight && - currentRun.Characters.Start == characterHit.FirstCharacterIndex && - characterHit.TrailingLength == 0) - { - return currentDistance; - } - } + return currentDistance - distance; + } - //Look for a hit in within the current run - if (currentPosition + remainingLength <= currentPosition + textRun.Text.Length) - { - characterHit = new CharacterHit(textRun.Text.Start + remainingLength); + //No hit hit found so we add the full width + currentDistance -= currentRun.Size.Width; + currentPosition += currentRun.TextSourceLength; + remainingLength -= currentRun.TextSourceLength; + } + } - var distance = currentRun.GetDistanceFromCharacterHit(characterHit); + return currentDistance; + } - return currentDistance + distance; - } + private static bool TryGetDistanceFromCharacterHit( + DrawableTextRun currentRun, + CharacterHit characterHit, + int currentPosition, + int remainingLength, + FlowDirection flowDirection, + out double distance, + out GlyphRun? currentGlyphRun) + { + var characterIndex = characterHit.FirstCharacterIndex + characterHit.TrailingLength; + var isTrailingHit = characterHit.TrailingLength > 0; - //Look at the left and right edge of the current run - if (currentRun.IsLeftToRight) - { - if (_resolvedFlowDirection == FlowDirection.LeftToRight && (lastRun == null || lastRun.IsLeftToRight)) - { - if (characterIndex <= currentPosition) - { - return currentDistance; - } - } - else - { - if (characterIndex == currentPosition) - { - return currentDistance; - } - } + distance = 0; + currentGlyphRun = null; - if (characterIndex == currentPosition + textRun.Text.Length && isTrailingHit) - { - return currentDistance + currentRun.Size.Width; - } - } - else - { - if (characterIndex == currentPosition) - { - return currentDistance + currentRun.Size.Width; - } - - var nextRun = index + 1 < _textRuns.Count ? - _textRuns[index + 1] as ShapedTextCharacters : - null; + switch (currentRun) + { + case ShapedTextCharacters shapedTextCharacters: + { + currentGlyphRun = shapedTextCharacters.GlyphRun; - if (nextRun != null) - { - if (nextRun.ShapedBuffer.IsLeftToRight) - { - if (characterIndex == currentPosition + textRun.Text.Length) - { - return currentDistance; - } - } - else - { - if (currentPosition + nextRun.Text.Length == characterIndex) - { - return currentDistance; - } - } - } - else - { - if (characterIndex > currentPosition + textRun.Text.Length) - { - return currentDistance; - } - } - } + if (currentPosition + remainingLength < currentPosition + currentRun.Text.Length) + { + characterHit = new CharacterHit(currentRun.Text.Start + remainingLength); - lastRun = currentRun; + distance = currentGlyphRun.GetDistanceFromCharacterHit(characterHit); - break; + return true; } - default: + + if (currentPosition + remainingLength == currentPosition + currentRun.Text.Length && isTrailingHit) { - if (characterIndex == currentPosition) + if (currentGlyphRun.IsLeftToRight || flowDirection == FlowDirection.RightToLeft) { - return currentDistance; + distance = currentGlyphRun.Size.Width; } - if (characterIndex == currentPosition + textRun.TextSourceLength) - { - return currentDistance + textRun.Size.Width; - } + return true; + } - break; + break; + } + default: + { + if (characterIndex == currentPosition) + { + return true; } - } - //No hit hit found so we add the full width - currentDistance += textRun.Size.Width; - currentPosition += textRun.TextSourceLength; - remainingLength -= textRun.TextSourceLength; + if (characterIndex == currentPosition + currentRun.TextSourceLength) + { + distance = currentRun.Size.Width; - if (remainingLength <= 0) - { - break; - } + return true; + + } + + break; + } } - return currentDistance; + return false; } /// @@ -460,20 +462,33 @@ namespace Avalonia.Media.TextFormatting var startIndex = currentRun.Text.Start + offset; - var endOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit( - currentShapedRun.ShapedBuffer.IsLeftToRight ? - new CharacterHit(startIndex + remainingLength) : - new CharacterHit(startIndex)); + double startOffset; + double endOffset; - endX += endOffset; + if (currentShapedRun.ShapedBuffer.IsLeftToRight) + { + startOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit(new CharacterHit(startIndex)); + + endOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit(new CharacterHit(startIndex + remainingLength)); + } + else + { + endOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit(new CharacterHit(startIndex)); - var startOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit( - currentShapedRun.ShapedBuffer.IsLeftToRight ? - new CharacterHit(startIndex) : - new CharacterHit(startIndex + remainingLength)); + if (currentPosition < startIndex) + { + startOffset = endOffset; + } + else + { + startOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit(new CharacterHit(startIndex + remainingLength)); + } + } startX += startOffset; + endX += endOffset; + var endHit = currentShapedRun.GlyphRun.GetCharacterHitFromDistance(endOffset, out _); var startHit = currentShapedRun.GlyphRun.GetCharacterHitFromDistance(startOffset, out _); @@ -504,7 +519,7 @@ namespace Avalonia.Media.TextFormatting } //Lines that only contain a linebreak need to be covered here - if(characterLength == 0) + if (characterLength == 0) { characterLength = NewLineLength; } @@ -532,19 +547,9 @@ namespace Avalonia.Media.TextFormatting currentWidth += runwidth; currentPosition += characterLength; - if (currentDirection == FlowDirection.LeftToRight) + if (currentPosition > characterIndex) { - if (currentPosition > characterIndex) - { - break; - } - } - else - { - if (currentPosition <= firstTextSourceIndex) - { - break; - } + break; } startX = endX; @@ -571,7 +576,7 @@ namespace Avalonia.Media.TextFormatting var currentPosition = FirstTextSourceIndex; var remainingLength = textLength; - var startX = Start + WidthIncludingTrailingWhitespace; + var startX = WidthIncludingTrailingWhitespace; double currentWidth = 0; var currentRect = Rect.Empty; @@ -582,7 +587,7 @@ namespace Avalonia.Media.TextFormatting continue; } - if (currentPosition + currentRun.TextSourceLength <= firstTextSourceIndex) + if (currentPosition + currentRun.TextSourceLength < firstTextSourceIndex) { startX -= currentRun.Size.Width; @@ -601,20 +606,31 @@ namespace Avalonia.Media.TextFormatting currentPosition += offset; var startIndex = currentRun.Text.Start + offset; + double startOffset; + double endOffset; - var endOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit( - currentShapedRun.ShapedBuffer.IsLeftToRight ? - new CharacterHit(startIndex + remainingLength) : - new CharacterHit(startIndex)); + if (currentShapedRun.ShapedBuffer.IsLeftToRight) + { + if (currentPosition < startIndex) + { + startOffset = endOffset = 0; + } + else + { + endOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit(new CharacterHit(startIndex + remainingLength)); - endX += endOffset - currentShapedRun.Size.Width; + startOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit(new CharacterHit(startIndex)); + } + } + else + { + endOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit(new CharacterHit(startIndex)); - var startOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit( - currentShapedRun.ShapedBuffer.IsLeftToRight ? - new CharacterHit(startIndex) : - new CharacterHit(startIndex + remainingLength)); + startOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit(new CharacterHit(startIndex + remainingLength)); + } - startX += startOffset - currentShapedRun.Size.Width; + startX -= currentRun.Size.Width - startOffset; + endX -= currentRun.Size.Width - endOffset; var endHit = currentShapedRun.GlyphRun.GetCharacterHitFromDistance(endOffset, out _); var startHit = currentShapedRun.GlyphRun.GetCharacterHitFromDistance(startOffset, out _); @@ -652,9 +668,10 @@ namespace Avalonia.Media.TextFormatting } var runWidth = endX - startX; - var currentRunBounds = new TextRunBounds(new Rect(startX, 0, runWidth, Height), currentPosition, characterLength, currentRun); - if (lastDirection == currentDirection && result.Count > 0 && MathUtilities.AreClose(currentRect.Right, startX)) + var currentRunBounds = new TextRunBounds(new Rect(Start + startX, 0, runWidth, Height), currentPosition, characterLength, currentRun); + + if (lastDirection == currentDirection && result.Count > 0 && MathUtilities.AreClose(currentRect.Right, Start + startX)) { currentRect = currentRect.WithWidth(currentWidth + runWidth); @@ -674,19 +691,9 @@ namespace Avalonia.Media.TextFormatting currentWidth += runWidth; currentPosition += characterLength; - if (currentDirection == FlowDirection.LeftToRight) + if (currentPosition > characterIndex) { - if (currentPosition > characterIndex) - { - break; - } - } - else - { - if (currentPosition <= firstTextSourceIndex) - { - break; - } + break; } lastDirection = currentDirection; diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs index 7e1103d624..631d0881b0 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs @@ -154,7 +154,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting { j += inner.Current.Text.Length; - if(j + i > text.Length) + if (j + i > text.Length) { break; } @@ -738,7 +738,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting var textLine = layout.TextLines[0]; var start = textLine.GetDistanceFromCharacterHit(new CharacterHit(5, 1)); - + var end = textLine.GetDistanceFromCharacterHit(new CharacterHit(6, 1)); var rects = layout.HitTestTextRange(0, 7).ToArray(); @@ -746,7 +746,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting Assert.Equal(1, rects.Length); var expected = rects[0]; - + Assert.Equal(expected.Left, start); Assert.Equal(expected.Right, end); } @@ -818,11 +818,11 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting var expected = text.Substring(textLine.FirstTextSourceIndex, textLine.Length); Assert.Equal(expected, actual); - } + } } } } - + [Fact] public void Should_Layout_Empty_String() { @@ -833,11 +833,83 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting Typeface.Default, 12, Brushes.Black); - + Assert.True(layout.Bounds.Height > 0); } } + [Fact] + public void Should_HitTestPoint_RightToLeft() + { + using (Start()) + { + var text = "אאא AAA"; + + var layout = new TextLayout( + text, + Typeface.Default, + 12, + Brushes.Black, + flowDirection: FlowDirection.RightToLeft); + + var firstRun = layout.TextLines[0].TextRuns[0] as ShapedTextCharacters; + + var hit = layout.HitTestPoint(new Point()); + + Assert.Equal(4, hit.TextPosition); + + var currentX = 0.0; + + for (var i = 0; i < firstRun.GlyphRun.GlyphClusters.Count; i++) + { + var cluster = firstRun.GlyphRun.GlyphClusters[i]; + var advance = firstRun.GlyphRun.GlyphAdvances[i]; + + hit = layout.HitTestPoint(new Point(currentX, 0)); + + Assert.Equal(cluster, hit.TextPosition); + + var hitRange = layout.HitTestTextRange(hit.TextPosition, 1); + + var distance = hitRange.First().Left; + + Assert.Equal(currentX, distance); + + currentX += advance; + } + + var secondRun = layout.TextLines[0].TextRuns[1] as ShapedTextCharacters; + + hit = layout.HitTestPoint(new Point(firstRun.Size.Width, 0)); + + Assert.Equal(7, hit.TextPosition); + + hit = layout.HitTestPoint(new Point(layout.TextLines[0].WidthIncludingTrailingWhitespace, 0)); + + Assert.Equal(0, hit.TextPosition); + + currentX = firstRun.Size.Width + 0.5; + + for (var i = 0; i < secondRun.GlyphRun.GlyphClusters.Count; i++) + { + var cluster = secondRun.GlyphRun.GlyphClusters[i]; + var advance = secondRun.GlyphRun.GlyphAdvances[i]; + + hit = layout.HitTestPoint(new Point(currentX, 0)); + + Assert.Equal(cluster, hit.CharacterHit.FirstCharacterIndex); + + var hitRange = layout.HitTestTextRange(hit.CharacterHit.FirstCharacterIndex, hit.CharacterHit.TrailingLength); + + var distance = hitRange.First().Left + 0.5; + + Assert.Equal(currentX, distance); + + currentX += advance; + } + } + } + private static IDisposable Start() { var disposable = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface From 2b3608078ea33439364d5ff61cf1868e2026b3bf Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 7 Jul 2022 11:03:55 +0200 Subject: [PATCH 041/151] WIP: Porting fluent theme to use control themes. Co-Authored-By: Takoooooo --- .../DateTimePickers/DateTimePickerPanel.cs | 2 +- .../Controls/AutoCompleteBox.xaml | 110 ++-- .../Controls/Button.xaml | 41 +- .../Controls/ButtonSpinner.xaml | 229 +++---- .../Controls/Calendar.xaml | 56 +- .../Controls/CalendarButton.xaml | 204 +++--- .../Controls/CalendarDatePicker.xaml | 346 +++++----- .../Controls/CalendarDayButton.xaml | 194 +++--- .../Controls/CalendarItem.xaml | 275 ++++---- .../Controls/CaptionButtons.xaml | 129 ++-- .../Controls/Carousel.xaml | 37 +- .../Controls/CheckBox.xaml | 572 ++++++++-------- .../Controls/ComboBox.xaml | 440 ++++++------- .../Controls/ComboBoxItem.xaml | 171 +++-- .../Controls/Common.xaml | 6 - .../Controls/ContentControl.xaml | 35 +- .../Controls/ContextMenu.xaml | 136 ++-- .../Controls/DataValidationErrors.xaml | 207 +++--- .../Controls/DatePicker.xaml | 622 +++++++++--------- .../Controls/DropDownButton.xaml | 198 +++--- .../Controls/EmbeddableControlRoot.xaml | 42 +- .../Controls/Expander.xaml | 354 +++++----- .../Controls/FluentControls.xaml | 146 ++-- .../Controls/FlyoutPresenter.xaml | 14 +- .../Controls/FocusAdorner.xaml | 2 +- .../Controls/GridSplitter.xaml | 46 +- .../Controls/ItemsControl.xaml | 35 +- .../Controls/Label.xaml | 40 +- .../Controls/ListBox.xaml | 92 +-- .../Controls/ListBoxItem.xaml | 155 ++--- .../Controls/ManagedFileChooser.xaml | 608 ++++++++--------- src/Avalonia.Themes.Fluent/Controls/Menu.xaml | 65 +- .../Controls/MenuFlyoutPresenter.xaml | 69 +- .../Controls/MenuItem.xaml | 4 +- .../Controls/NativeMenuBar.xaml | 59 +- .../Controls/NotificationCard.xaml | 37 +- .../Controls/NumericUpDown.xaml | 135 ++-- .../Controls/OverlayPopupHost.xaml | 43 +- .../Controls/PathIcon.xaml | 55 +- .../Controls/PopupRoot.xaml | 56 +- .../Controls/ProgressBar.xaml | 343 +++++----- .../Controls/RadioButton.xaml | 286 ++++---- .../Controls/RepeatButton.xaml | 118 ++-- .../Controls/ScrollBar.xaml | 2 +- .../Controls/ScrollViewer.xaml | 245 +++---- .../Controls/Separator.xaml | 15 +- .../Controls/Slider.xaml | 603 ++++++++++------- .../Controls/SplitButton.xaml | 1 - .../Controls/SplitView.xaml | 2 +- .../Controls/TabControl.xaml | 122 ++-- .../Controls/TabItem.xaml | 237 +++---- .../Controls/TabStrip.xaml | 66 +- .../Controls/TabStripItem.xaml | 193 +++--- .../Controls/TextBox.xaml | 62 +- .../Controls/TimePicker.xaml | 409 ++++-------- .../Controls/TitleBar.xaml | 94 +-- .../Controls/ToggleButton.xaml | 189 +++--- .../Controls/ToggleSwitch.xaml | 585 ++++++++-------- .../Controls/ToolTip.xaml | 146 ++-- .../Controls/TransitioningContentControl.xaml | 39 +- .../Controls/TreeView.xaml | 59 +- .../Controls/TreeViewItem.xaml | 3 +- .../Controls/UserControl.xaml | 38 +- .../Controls/Window.xaml | 57 +- .../Controls/WindowNotificationManager.xaml | 97 +-- 65 files changed, 4976 insertions(+), 5102 deletions(-) delete mode 100644 src/Avalonia.Themes.Fluent/Controls/Common.xaml diff --git a/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs b/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs index 667f994a1d..3fdfbee54d 100644 --- a/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs +++ b/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs @@ -454,7 +454,7 @@ namespace Avalonia.Controls.Primitives children.Add(new ListBoxItem { Height = ItemHeight, - Classes = new Classes("DateTimePickerItem", $"{PanelType}Item"), + Classes = new Classes($"{PanelType}Item"), VerticalContentAlignment = Avalonia.Layout.VerticalAlignment.Center, Focusable = false }); diff --git a/src/Avalonia.Themes.Fluent/Controls/AutoCompleteBox.xaml b/src/Avalonia.Themes.Fluent/Controls/AutoCompleteBox.xaml index 58295d024f..6004d42120 100644 --- a/src/Avalonia.Themes.Fluent/Controls/AutoCompleteBox.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/AutoCompleteBox.xaml @@ -1,5 +1,5 @@ - + @@ -16,56 +16,56 @@ - - - + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/Button.xaml b/src/Avalonia.Themes.Fluent/Controls/Button.xaml index 3fb0d43342..b8fea84ae0 100644 --- a/src/Avalonia.Themes.Fluent/Controls/Button.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/Button.xaml @@ -1,4 +1,5 @@ - + @@ -7,14 +8,11 @@ - - - 8,5,8,6 - - - - - - - - + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/ButtonSpinner.xaml b/src/Avalonia.Themes.Fluent/Controls/ButtonSpinner.xaml index 836cc27db3..a5234f5771 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ButtonSpinner.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ButtonSpinner.xaml @@ -1,28 +1,26 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + + + - @@ -32,118 +30,79 @@ M0,9 L10,0 20,9 19,10 10,2 1,10 z M0,1 L10,10 20,1 19,0 10,8 1,0 z - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/Calendar.xaml b/src/Avalonia.Themes.Fluent/Controls/Calendar.xaml index 43e4a1fcea..3434f9b6e5 100644 --- a/src/Avalonia.Themes.Fluent/Controls/Calendar.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/Calendar.xaml @@ -1,38 +1,40 @@ - - - + + - - + CornerRadius="{TemplateBinding CornerRadius}" + HeaderBackground="{TemplateBinding HeaderBackground}" /> + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/CalendarButton.xaml b/src/Avalonia.Themes.Fluent/Controls/CalendarButton.xaml index 3a6af60983..0381022910 100644 --- a/src/Avalonia.Themes.Fluent/Controls/CalendarButton.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/CalendarButton.xaml @@ -1,121 +1,109 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - + + + + - + - - + + + - - - - - - + + + + + - - + + + + - - - - - - - + - - - - - - - - - - - - + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml b/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml index c6f70d05e1..83a254be56 100644 --- a/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml @@ -1,182 +1,180 @@ - - - - - - - - + + + + + + + 12 32 - - - - - - - - + Width="3" + Height="3" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/CaptionButtons.xaml b/src/Avalonia.Themes.Fluent/Controls/CaptionButtons.xaml index 62874f4884..3c45de18c6 100644 --- a/src/Avalonia.Themes.Fluent/Controls/CaptionButtons.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/CaptionButtons.xaml @@ -1,72 +1,57 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/Carousel.xaml b/src/Avalonia.Themes.Fluent/Controls/Carousel.xaml index baba0649aa..6c05a62250 100644 --- a/src/Avalonia.Themes.Fluent/Controls/Carousel.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/Carousel.xaml @@ -1,17 +1,20 @@ - + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/CheckBox.xaml b/src/Avalonia.Themes.Fluent/Controls/CheckBox.xaml index e9830fb228..2eee425cb0 100644 --- a/src/Avalonia.Themes.Fluent/Controls/CheckBox.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/CheckBox.xaml @@ -1,297 +1,293 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + - + + + + + + - + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + diff --git a/src/Avalonia.Themes.Fluent/Controls/ComboBox.xaml b/src/Avalonia.Themes.Fluent/Controls/ComboBox.xaml index 93ecc438eb..2dc7439001 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ComboBox.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ComboBox.xaml @@ -1,232 +1,228 @@ - - - - - - Item 1 - Item 2 - - - Item 1 - Item 2 - - - - - + + + + + + Item 1 + Item 2 + + + Item 1 + Item 2 + + + + + 0,0,0,4 15 7 - + 12,5,0,7 11,5,32,6 32 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Background="Transparent" + Margin="0,1,1,1" + Width="30" + IsVisible="False" + HorizontalAlignment="Right" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + diff --git a/src/Avalonia.Themes.Fluent/Controls/ComboBoxItem.xaml b/src/Avalonia.Themes.Fluent/Controls/ComboBoxItem.xaml index 0debe87445..88f9c0ea15 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ComboBoxItem.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ComboBoxItem.xaml @@ -1,90 +1,85 @@ - - - - - - Item 1 - Item 2 long - Item 3 - Item 4 - + + + + + + Item 1 + Item 2 long + Item 3 + Item 4 + + - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/Common.xaml b/src/Avalonia.Themes.Fluent/Controls/Common.xaml deleted file mode 100644 index e09e39d7cb..0000000000 --- a/src/Avalonia.Themes.Fluent/Controls/Common.xaml +++ /dev/null @@ -1,6 +0,0 @@ - - - diff --git a/src/Avalonia.Themes.Fluent/Controls/ContentControl.xaml b/src/Avalonia.Themes.Fluent/Controls/ContentControl.xaml index d32bc399b6..5777603074 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ContentControl.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ContentControl.xaml @@ -1,16 +1,19 @@ - + + + + + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/ContextMenu.xaml b/src/Avalonia.Themes.Fluent/Controls/ContextMenu.xaml index df800b4a06..fe0f0f3f6c 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ContextMenu.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ContextMenu.xaml @@ -1,72 +1,72 @@ - + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/DataValidationErrors.xaml b/src/Avalonia.Themes.Fluent/Controls/DataValidationErrors.xaml index 649a186c7e..6ed73148a7 100644 --- a/src/Avalonia.Themes.Fluent/Controls/DataValidationErrors.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/DataValidationErrors.xaml @@ -1,104 +1,103 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/DatePicker.xaml b/src/Avalonia.Themes.Fluent/Controls/DatePicker.xaml index 11d6b9fdfc..a7093c1341 100644 --- a/src/Avalonia.Themes.Fluent/Controls/DatePicker.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/DatePicker.xaml @@ -5,17 +5,16 @@ // All other rights reserved. --> - - - - - - - - + + + + + + 0,0,0,4 40 40 @@ -27,328 +26,327 @@ 0,3,0,6 9,3,0,6 1 - - - - - - - - + + + + + + + + + + + - - + + + + + - + - + + + + + + + + + + + + - - + + + + + + + + + + - - - + + + + - + - + + + - + - - - - - + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + + + + + - - - - + diff --git a/src/Avalonia.Themes.Fluent/Controls/DropDownButton.xaml b/src/Avalonia.Themes.Fluent/Controls/DropDownButton.xaml index b96c689ab6..3206cb8cb1 100644 --- a/src/Avalonia.Themes.Fluent/Controls/DropDownButton.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/DropDownButton.xaml @@ -1,103 +1,101 @@ - - - - - - - - - - - - - 32 - + + + + + + + + + - - - - - - - - - - + 32 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + - + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/EmbeddableControlRoot.xaml b/src/Avalonia.Themes.Fluent/Controls/EmbeddableControlRoot.xaml index 79560be933..89eaec70ad 100644 --- a/src/Avalonia.Themes.Fluent/Controls/EmbeddableControlRoot.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/EmbeddableControlRoot.xaml @@ -1,19 +1,23 @@ - + + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/Expander.xaml b/src/Avalonia.Themes.Fluent/Controls/Expander.xaml index 33d502772e..36f6008782 100644 --- a/src/Avalonia.Themes.Fluent/Controls/Expander.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/Expander.xaml @@ -1,6 +1,6 @@ - + @@ -34,72 +34,34 @@ - - 16 - 16 + 16 + 16 - 1 + 1 - 1,1,0,1 - 1,1,1,0 - 0,1,1,1 - 1,0,1,1 + 1,1,0,1 + 1,1,1,0 + 0,1,1,1 + 1,0,1,1 - - + + - - + + - + - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml b/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml index 5b217e4764..286b3e3fcd 100644 --- a/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml @@ -1,70 +1,78 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/FlyoutPresenter.xaml b/src/Avalonia.Themes.Fluent/Controls/FlyoutPresenter.xaml index 92f8177ead..2dca5b0770 100644 --- a/src/Avalonia.Themes.Fluent/Controls/FlyoutPresenter.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/FlyoutPresenter.xaml @@ -1,9 +1,7 @@ - - - 1 - - - - + + diff --git a/src/Avalonia.Themes.Fluent/Controls/FocusAdorner.xaml b/src/Avalonia.Themes.Fluent/Controls/FocusAdorner.xaml index 91bf71ed4d..c3f489da80 100644 --- a/src/Avalonia.Themes.Fluent/Controls/FocusAdorner.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/FocusAdorner.xaml @@ -1,4 +1,4 @@ - 0 diff --git a/src/Avalonia.Themes.Fluent/Controls/GridSplitter.xaml b/src/Avalonia.Themes.Fluent/Controls/GridSplitter.xaml index e3a7b04f33..ca57eccd13 100644 --- a/src/Avalonia.Themes.Fluent/Controls/GridSplitter.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/GridSplitter.xaml @@ -1,24 +1,22 @@ - - - - - + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/ItemsControl.xaml b/src/Avalonia.Themes.Fluent/Controls/ItemsControl.xaml index 19d13b6399..b8d7c2c4ef 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ItemsControl.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ItemsControl.xaml @@ -1,16 +1,19 @@ - + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/Label.xaml b/src/Avalonia.Themes.Fluent/Controls/Label.xaml index d41e4e2166..ad57239648 100644 --- a/src/Avalonia.Themes.Fluent/Controls/Label.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/Label.xaml @@ -1,18 +1,22 @@ - + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/ListBox.xaml b/src/Avalonia.Themes.Fluent/Controls/ListBox.xaml index 8011ed9daf..b1d4b77a3f 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ListBox.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ListBox.xaml @@ -1,45 +1,47 @@ - - - - - Test - Test - Test - Test - - - - - + + + + + Test + Test + Test + Test + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/ListBoxItem.xaml b/src/Avalonia.Themes.Fluent/Controls/ListBoxItem.xaml index 11f3c12772..c00e920f75 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ListBoxItem.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ListBoxItem.xaml @@ -1,94 +1,79 @@ - - - - - Disabled - - Test - - Test - - - - + + + + + Disabled + + Test + + Test + + + 12,9,12,12 - - + + + + + + + + - + - - + + - - - + + - - - + + - + - - - + + - - - + + - - - - + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/ManagedFileChooser.xaml b/src/Avalonia.Themes.Fluent/Controls/ManagedFileChooser.xaml index 60e16fcff2..dc454e4fdb 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ManagedFileChooser.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ManagedFileChooser.xaml @@ -1,324 +1,324 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + - - - + + - - + + + + - - + + diff --git a/src/Avalonia.Themes.Fluent/Controls/Menu.xaml b/src/Avalonia.Themes.Fluent/Controls/Menu.xaml index 4eb724a926..d42e413f00 100644 --- a/src/Avalonia.Themes.Fluent/Controls/Menu.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/Menu.xaml @@ -1,36 +1,35 @@ - + + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/MenuFlyoutPresenter.xaml b/src/Avalonia.Themes.Fluent/Controls/MenuFlyoutPresenter.xaml index ff50acab5e..7769f0a5c9 100644 --- a/src/Avalonia.Themes.Fluent/Controls/MenuFlyoutPresenter.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/MenuFlyoutPresenter.xaml @@ -1,35 +1,34 @@ - - - - + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml b/src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml index 33cf6bfdde..b7a9435749 100644 --- a/src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml @@ -1,4 +1,4 @@ - - -4 + -4 0,0,12,0 24,0,0,0 M 1,0 10,10 l -9,10 -1,-1 L 8,10 -0,1 Z diff --git a/src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml b/src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml index d40ba0cc1d..753c03992a 100644 --- a/src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml @@ -1,30 +1,29 @@ - - - - - - + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/NotificationCard.xaml b/src/Avalonia.Themes.Fluent/Controls/NotificationCard.xaml index 924d977eb5..e55fcb90e8 100644 --- a/src/Avalonia.Themes.Fluent/Controls/NotificationCard.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/NotificationCard.xaml @@ -1,11 +1,11 @@ - + - + - - - - - - - - - - + + diff --git a/src/Avalonia.Themes.Fluent/Controls/NumericUpDown.xaml b/src/Avalonia.Themes.Fluent/Controls/NumericUpDown.xaml index 36ab07e3e3..74ffff0f5c 100644 --- a/src/Avalonia.Themes.Fluent/Controls/NumericUpDown.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/NumericUpDown.xaml @@ -1,68 +1,67 @@ - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/OverlayPopupHost.xaml b/src/Avalonia.Themes.Fluent/Controls/OverlayPopupHost.xaml index 31b43c39cd..0b587e6037 100644 --- a/src/Avalonia.Themes.Fluent/Controls/OverlayPopupHost.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/OverlayPopupHost.xaml @@ -1,20 +1,23 @@ - + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/PathIcon.xaml b/src/Avalonia.Themes.Fluent/Controls/PathIcon.xaml index d4952b3571..966c6c2632 100644 --- a/src/Avalonia.Themes.Fluent/Controls/PathIcon.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/PathIcon.xaml @@ -1,29 +1,26 @@ - - - - - M14 9.50006C11.5147 9.50006 9.5 11.5148 9.5 14.0001C9.5 16.4853 11.5147 18.5001 14 18.5001C15.3488 18.5001 16.559 17.9066 17.3838 16.9666C18.0787 16.1746 18.5 15.1365 18.5 14.0001C18.5 13.5401 18.431 13.0963 18.3028 12.6784C17.7382 10.8381 16.0253 9.50006 14 9.50006ZM11 14.0001C11 12.3432 12.3431 11.0001 14 11.0001C15.6569 11.0001 17 12.3432 17 14.0001C17 15.6569 15.6569 17.0001 14 17.0001C12.3431 17.0001 11 15.6569 11 14.0001Z M21.7093 22.3948L19.9818 21.6364C19.4876 21.4197 18.9071 21.4515 18.44 21.7219C17.9729 21.9924 17.675 22.4693 17.6157 23.0066L17.408 24.8855C17.3651 25.273 17.084 25.5917 16.7055 25.682C14.9263 26.1061 13.0725 26.1061 11.2933 25.682C10.9148 25.5917 10.6336 25.273 10.5908 24.8855L10.3834 23.0093C10.3225 22.4731 10.0112 21.9976 9.54452 21.7281C9.07783 21.4586 8.51117 21.4269 8.01859 21.6424L6.29071 22.4009C5.93281 22.558 5.51493 22.4718 5.24806 22.1859C4.00474 20.8536 3.07924 19.2561 2.54122 17.5137C2.42533 17.1384 2.55922 16.7307 2.8749 16.4977L4.40219 15.3703C4.83721 15.0501 5.09414 14.5415 5.09414 14.0007C5.09414 13.4598 4.83721 12.9512 4.40162 12.6306L2.87529 11.5051C2.55914 11.272 2.42513 10.8638 2.54142 10.4882C3.08038 8.74734 4.00637 7.15163 5.24971 5.82114C5.51684 5.53528 5.93492 5.44941 6.29276 5.60691L8.01296 6.36404C8.50793 6.58168 9.07696 6.54881 9.54617 6.27415C10.0133 6.00264 10.3244 5.52527 10.3844 4.98794L10.5933 3.11017C10.637 2.71803 10.9245 2.39704 11.3089 2.31138C12.19 2.11504 13.0891 2.01071 14.0131 2.00006C14.9147 2.01047 15.8128 2.11485 16.6928 2.31149C17.077 2.39734 17.3643 2.71823 17.4079 3.11017L17.617 4.98937C17.7116 5.85221 18.4387 6.50572 19.3055 6.50663C19.5385 6.507 19.769 6.45838 19.9843 6.36294L21.7048 5.60568C22.0626 5.44818 22.4807 5.53405 22.7478 5.81991C23.9912 7.1504 24.9172 8.74611 25.4561 10.487C25.5723 10.8623 25.4386 11.2703 25.1228 11.5035L23.5978 12.6297C23.1628 12.95 22.9 13.4586 22.9 13.9994C22.9 14.5403 23.1628 15.0489 23.5988 15.3698L25.1251 16.4965C25.441 16.7296 25.5748 17.1376 25.4586 17.5131C24.9198 19.2536 23.9944 20.8492 22.7517 22.1799C22.4849 22.4657 22.0671 22.5518 21.7093 22.3948ZM16.263 22.1966C16.4982 21.4685 16.9889 20.8288 17.6884 20.4238C18.5702 19.9132 19.6536 19.8547 20.5841 20.2627L21.9281 20.8526C22.791 19.8538 23.4593 18.7013 23.8981 17.4552L22.7095 16.5778L22.7086 16.5771C21.898 15.98 21.4 15.0277 21.4 13.9994C21.4 12.9719 21.8974 12.0195 22.7073 11.4227L22.7085 11.4218L23.8957 10.545C23.4567 9.2988 22.7881 8.14636 21.9248 7.1477L20.5922 7.73425L20.5899 7.73527C20.1844 7.91463 19.7472 8.00722 19.3039 8.00663C17.6715 8.00453 16.3046 6.77431 16.1261 5.15465L16.1259 5.15291L15.9635 3.69304C15.3202 3.57328 14.6677 3.50872 14.013 3.50017C13.3389 3.50891 12.6821 3.57367 12.0377 3.69328L11.8751 5.15452C11.7625 6.16272 11.1793 7.05909 10.3019 7.56986C9.41937 8.0856 8.34453 8.14844 7.40869 7.73694L6.07273 7.14893C5.20949 8.14751 4.54092 9.29983 4.10196 10.5459L5.29181 11.4233C6.11115 12.0269 6.59414 12.9837 6.59414 14.0007C6.59414 15.0173 6.11142 15.9742 5.29237 16.5776L4.10161 17.4566C4.54002 18.7044 5.2085 19.8585 6.07205 20.8587L7.41742 20.2682C8.34745 19.8613 9.41573 19.9215 10.2947 20.4292C11.174 20.937 11.7593 21.832 11.8738 22.84L11.8744 22.8445L12.0362 24.3088C13.3326 24.5638 14.6662 24.5638 15.9626 24.3088L16.1247 22.8418C16.1491 22.6217 16.1955 22.4055 16.263 22.1966Z - - - - - - + + + + + M14 9.50006C11.5147 9.50006 9.5 11.5148 9.5 14.0001C9.5 16.4853 11.5147 18.5001 14 18.5001C15.3488 18.5001 16.559 17.9066 17.3838 16.9666C18.0787 16.1746 18.5 15.1365 18.5 14.0001C18.5 13.5401 18.431 13.0963 18.3028 12.6784C17.7382 10.8381 16.0253 9.50006 14 9.50006ZM11 14.0001C11 12.3432 12.3431 11.0001 14 11.0001C15.6569 11.0001 17 12.3432 17 14.0001C17 15.6569 15.6569 17.0001 14 17.0001C12.3431 17.0001 11 15.6569 11 14.0001Z M21.7093 22.3948L19.9818 21.6364C19.4876 21.4197 18.9071 21.4515 18.44 21.7219C17.9729 21.9924 17.675 22.4693 17.6157 23.0066L17.408 24.8855C17.3651 25.273 17.084 25.5917 16.7055 25.682C14.9263 26.1061 13.0725 26.1061 11.2933 25.682C10.9148 25.5917 10.6336 25.273 10.5908 24.8855L10.3834 23.0093C10.3225 22.4731 10.0112 21.9976 9.54452 21.7281C9.07783 21.4586 8.51117 21.4269 8.01859 21.6424L6.29071 22.4009C5.93281 22.558 5.51493 22.4718 5.24806 22.1859C4.00474 20.8536 3.07924 19.2561 2.54122 17.5137C2.42533 17.1384 2.55922 16.7307 2.8749 16.4977L4.40219 15.3703C4.83721 15.0501 5.09414 14.5415 5.09414 14.0007C5.09414 13.4598 4.83721 12.9512 4.40162 12.6306L2.87529 11.5051C2.55914 11.272 2.42513 10.8638 2.54142 10.4882C3.08038 8.74734 4.00637 7.15163 5.24971 5.82114C5.51684 5.53528 5.93492 5.44941 6.29276 5.60691L8.01296 6.36404C8.50793 6.58168 9.07696 6.54881 9.54617 6.27415C10.0133 6.00264 10.3244 5.52527 10.3844 4.98794L10.5933 3.11017C10.637 2.71803 10.9245 2.39704 11.3089 2.31138C12.19 2.11504 13.0891 2.01071 14.0131 2.00006C14.9147 2.01047 15.8128 2.11485 16.6928 2.31149C17.077 2.39734 17.3643 2.71823 17.4079 3.11017L17.617 4.98937C17.7116 5.85221 18.4387 6.50572 19.3055 6.50663C19.5385 6.507 19.769 6.45838 19.9843 6.36294L21.7048 5.60568C22.0626 5.44818 22.4807 5.53405 22.7478 5.81991C23.9912 7.1504 24.9172 8.74611 25.4561 10.487C25.5723 10.8623 25.4386 11.2703 25.1228 11.5035L23.5978 12.6297C23.1628 12.95 22.9 13.4586 22.9 13.9994C22.9 14.5403 23.1628 15.0489 23.5988 15.3698L25.1251 16.4965C25.441 16.7296 25.5748 17.1376 25.4586 17.5131C24.9198 19.2536 23.9944 20.8492 22.7517 22.1799C22.4849 22.4657 22.0671 22.5518 21.7093 22.3948ZM16.263 22.1966C16.4982 21.4685 16.9889 20.8288 17.6884 20.4238C18.5702 19.9132 19.6536 19.8547 20.5841 20.2627L21.9281 20.8526C22.791 19.8538 23.4593 18.7013 23.8981 17.4552L22.7095 16.5778L22.7086 16.5771C21.898 15.98 21.4 15.0277 21.4 13.9994C21.4 12.9719 21.8974 12.0195 22.7073 11.4227L22.7085 11.4218L23.8957 10.545C23.4567 9.2988 22.7881 8.14636 21.9248 7.1477L20.5922 7.73425L20.5899 7.73527C20.1844 7.91463 19.7472 8.00722 19.3039 8.00663C17.6715 8.00453 16.3046 6.77431 16.1261 5.15465L16.1259 5.15291L15.9635 3.69304C15.3202 3.57328 14.6677 3.50872 14.013 3.50017C13.3389 3.50891 12.6821 3.57367 12.0377 3.69328L11.8751 5.15452C11.7625 6.16272 11.1793 7.05909 10.3019 7.56986C9.41937 8.0856 8.34453 8.14844 7.40869 7.73694L6.07273 7.14893C5.20949 8.14751 4.54092 9.29983 4.10196 10.5459L5.29181 11.4233C6.11115 12.0269 6.59414 12.9837 6.59414 14.0007C6.59414 15.0173 6.11142 15.9742 5.29237 16.5776L4.10161 17.4566C4.54002 18.7044 5.2085 19.8585 6.07205 20.8587L7.41742 20.2682C8.34745 19.8613 9.41573 19.9215 10.2947 20.4292C11.174 20.937 11.7593 21.832 11.8738 22.84L11.8744 22.8445L12.0362 24.3088C13.3326 24.5638 14.6662 24.5638 15.9626 24.3088L16.1247 22.8418C16.1491 22.6217 16.1955 22.4055 16.263 22.1966Z + + + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/PopupRoot.xaml b/src/Avalonia.Themes.Fluent/Controls/PopupRoot.xaml index f608cf55f5..f57ecd2013 100644 --- a/src/Avalonia.Themes.Fluent/Controls/PopupRoot.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/PopupRoot.xaml @@ -1,28 +1,28 @@ - - - + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/ProgressBar.xaml b/src/Avalonia.Themes.Fluent/Controls/ProgressBar.xaml index a463334a76..3c7d22e13f 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ProgressBar.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ProgressBar.xaml @@ -1,164 +1,179 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/RadioButton.xaml b/src/Avalonia.Themes.Fluent/Controls/RadioButton.xaml index e967dc8807..a1c4ba4f2d 100644 --- a/src/Avalonia.Themes.Fluent/Controls/RadioButton.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/RadioButton.xaml @@ -1,4 +1,4 @@ - + @@ -9,7 +9,8 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/RepeatButton.xaml b/src/Avalonia.Themes.Fluent/Controls/RepeatButton.xaml index 7fa515d3f7..670212c4b1 100644 --- a/src/Avalonia.Themes.Fluent/Controls/RepeatButton.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/RepeatButton.xaml @@ -1,63 +1,65 @@ - - - - - - - - - + + + + + + + + + 8,5,8,6 - - - + + + + + + + + + + + + + + + + - + - + - - + + + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/ScrollBar.xaml b/src/Avalonia.Themes.Fluent/Controls/ScrollBar.xaml index 4727ff72b9..7d87a3e35a 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ScrollBar.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ScrollBar.xaml @@ -1,4 +1,4 @@ - + - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/Separator.xaml b/src/Avalonia.Themes.Fluent/Controls/Separator.xaml index 5d95ccc404..afbc3770c1 100644 --- a/src/Avalonia.Themes.Fluent/Controls/Separator.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/Separator.xaml @@ -1,7 +1,7 @@ - - - - - + + diff --git a/src/Avalonia.Themes.Fluent/Controls/Slider.xaml b/src/Avalonia.Themes.Fluent/Controls/Slider.xaml index cd2c02c567..b468f7f4ad 100644 --- a/src/Avalonia.Themes.Fluent/Controls/Slider.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/Slider.xaml @@ -1,19 +1,20 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + 0,0,0,4 15 15 @@ -24,252 +25,364 @@ 20 20 20 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - + + + + + + - + - + + + + - + - - - - - - - - - + - - - - - - - - - - - + - - - - - - - - + - - + - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/SplitButton.xaml b/src/Avalonia.Themes.Fluent/Controls/SplitButton.xaml index 91c901f567..fb0460d9a1 100644 --- a/src/Avalonia.Themes.Fluent/Controls/SplitButton.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/SplitButton.xaml @@ -2,7 +2,6 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:CompileBindings="True" xmlns:converters="using:Avalonia.Controls.Converters"> - diff --git a/src/Avalonia.Themes.Fluent/Controls/SplitView.xaml b/src/Avalonia.Themes.Fluent/Controls/SplitView.xaml index 55d46e32a1..6b9b94852f 100644 --- a/src/Avalonia.Themes.Fluent/Controls/SplitView.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/SplitView.xaml @@ -1,4 +1,4 @@ - diff --git a/src/Avalonia.Themes.Fluent/Controls/TabControl.xaml b/src/Avalonia.Themes.Fluent/Controls/TabControl.xaml index 322f6ce89e..1cc31f8e15 100644 --- a/src/Avalonia.Themes.Fluent/Controls/TabControl.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/TabControl.xaml @@ -1,65 +1,63 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - 0 0 0 2 - - - - - - - + + + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/TabItem.xaml b/src/Avalonia.Themes.Fluent/Controls/TabItem.xaml index ebe6f82917..110ffc2f48 100644 --- a/src/Avalonia.Themes.Fluent/Controls/TabItem.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/TabItem.xaml @@ -1,132 +1,133 @@ - - - - - - - - - - + + + + + + + + + + 48 24 2 - - + + + + + + + + + + + + + + + + + + + - - - + + + - - - - + + + + - - + + - - + + - - + + - - + + - - + + - - - + + + - + - - - - + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/TabStrip.xaml b/src/Avalonia.Themes.Fluent/Controls/TabStrip.xaml index 681ac48850..15c3ba2f37 100644 --- a/src/Avalonia.Themes.Fluent/Controls/TabStrip.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/TabStrip.xaml @@ -1,32 +1,34 @@ - - - - - Item 1 - Item 2 - Disabled - - - - - + + + + + Item 1 + Item 2 + Disabled + + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/TabStripItem.xaml b/src/Avalonia.Themes.Fluent/Controls/TabStripItem.xaml index 59f68a1547..5ccd430d33 100644 --- a/src/Avalonia.Themes.Fluent/Controls/TabStripItem.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/TabStripItem.xaml @@ -1,107 +1,108 @@ - - - - - Leaf - Arch - - - - + + + + + Leaf + Arch + + + + 48 2 - - + + + + + + + + + + + + + + + + + + + - + - - - - + + + + - - - - + + + + - - + + - - + + - - + + - - + + - - - + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/TextBox.xaml b/src/Avalonia.Themes.Fluent/Controls/TextBox.xaml index 40d9b11f7c..0fd3758d77 100644 --- a/src/Avalonia.Themes.Fluent/Controls/TextBox.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/TextBox.xaml @@ -1,4 +1,4 @@ - @@ -10,7 +10,7 @@ TextAlignment="Center"/> - + 0,0,0,4 M 11.416016,10 20,1.4160156 18.583984,0 10,8.5839846 1.4160156,0 0,1.4160156 8.5839844,10 0,18.583985 1.4160156,20 10,11.416015 18.583984,20 20,18.583985 Z @@ -36,7 +36,7 @@ - + @@ -73,32 +73,32 @@ VerticalScrollBarVisibility="{TemplateBinding (ScrollViewer.VerticalScrollBarVisibility)}" IsScrollChainingEnabled="{TemplateBinding (ScrollViewer.IsScrollChainingEnabled)}" AllowAutoHide="{TemplateBinding (ScrollViewer.AllowAutoHide)}"> - - - - - + + + + + @@ -159,7 +159,7 @@ - + @@ -237,7 +237,7 @@ - + - - - - + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - diff --git a/src/Avalonia.Themes.Fluent/Controls/TitleBar.xaml b/src/Avalonia.Themes.Fluent/Controls/TitleBar.xaml index 4dba5b4ba4..2b97df020f 100644 --- a/src/Avalonia.Themes.Fluent/Controls/TitleBar.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/TitleBar.xaml @@ -1,53 +1,55 @@ - - - - - - - + + - + - + - + - + - + - - + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/ToggleButton.xaml b/src/Avalonia.Themes.Fluent/Controls/ToggleButton.xaml index e0d54d7232..b53980b792 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ToggleButton.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ToggleButton.xaml @@ -1,5 +1,5 @@ - + @@ -10,104 +10,111 @@ - 8,5,8,6 - - - - - + + + + + + + + + + + + + + + + - + - + - + - + - + - + - + - + + - + - - + + + + + + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/ToggleSwitch.xaml b/src/Avalonia.Themes.Fluent/Controls/ToggleSwitch.xaml index a2b50a859d..533903bec7 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ToggleSwitch.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ToggleSwitch.xaml @@ -1,288 +1,307 @@ - - + + + + + + + + + + + + + + + + + 0,0,0,6 6 6 0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/ToolTip.xaml b/src/Avalonia.Themes.Fluent/Controls/ToolTip.xaml index 2d18be91cb..1070f3b68c 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ToolTip.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ToolTip.xaml @@ -1,87 +1,71 @@ - - - - - - Hover Here - - + + + + Hover Here + + + - - - - ToolTip - A control which pops up a hint when a control is hovered - - - ToolTip bottom placement - - - + Padding="50" + ToolTip.Placement="Bottom"> + + + ToolTip + A control which pops up a hint when a control is hovered + + + ToolTip bottom placement + + + - 320 - - - - + + + + + + + + + + + + + + + + + + + + + + - + - - + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/TransitioningContentControl.xaml b/src/Avalonia.Themes.Fluent/Controls/TransitioningContentControl.xaml index 6a4d56ccb7..f28dc66a5f 100644 --- a/src/Avalonia.Themes.Fluent/Controls/TransitioningContentControl.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/TransitioningContentControl.xaml @@ -1,20 +1,19 @@ - - - + + + + + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/TreeView.xaml b/src/Avalonia.Themes.Fluent/Controls/TreeView.xaml index 656b72e07b..4fcec79f25 100644 --- a/src/Avalonia.Themes.Fluent/Controls/TreeView.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/TreeView.xaml @@ -1,28 +1,31 @@ - + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/TreeViewItem.xaml b/src/Avalonia.Themes.Fluent/Controls/TreeViewItem.xaml index f86b67bb6c..6510832eb3 100644 --- a/src/Avalonia.Themes.Fluent/Controls/TreeViewItem.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/TreeViewItem.xaml @@ -1,4 +1,4 @@ - @@ -53,7 +53,6 @@ diff --git a/src/Avalonia.Themes.Fluent/Controls/Window.xaml b/src/Avalonia.Themes.Fluent/Controls/Window.xaml index 90963c606c..d78fd76a37 100644 --- a/src/Avalonia.Themes.Fluent/Controls/Window.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/Window.xaml @@ -1,27 +1,30 @@ - + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/WindowNotificationManager.xaml b/src/Avalonia.Themes.Fluent/Controls/WindowNotificationManager.xaml index 8d14c2d972..d5d5114c1b 100644 --- a/src/Avalonia.Themes.Fluent/Controls/WindowNotificationManager.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/WindowNotificationManager.xaml @@ -1,47 +1,58 @@ - - + + + + + + + + + + + + + + + + + + + + - + - + - + - - + + + From afe3486e9f3ea7a493c7e66ca3e5b59682357dcb Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 7 Jul 2022 11:15:41 +0200 Subject: [PATCH 042/151] Remove trailing /template/ selectors. --- .../Controls/ButtonSpinner.xaml | 12 +-- .../Controls/CalendarDatePicker.xaml | 12 +-- .../Controls/CalendarDayButton.xaml | 6 +- .../Controls/CheckBox.xaml | 88 +++++++++---------- .../Controls/ComboBox.xaml | 50 +++++------ .../Controls/Slider.xaml | 56 ++++++------ .../Controls/TimePicker.xaml | 6 +- 7 files changed, 113 insertions(+), 117 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/ButtonSpinner.xaml b/src/Avalonia.Themes.Fluent/Controls/ButtonSpinner.xaml index a5234f5771..f99f1040e5 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ButtonSpinner.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ButtonSpinner.xaml @@ -87,20 +87,20 @@ - - - - diff --git a/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml b/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml index 83a254be56..eefd9e9b51 100644 --- a/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml @@ -106,13 +106,13 @@ - - @@ -152,13 +152,13 @@ - - diff --git a/src/Avalonia.Themes.Fluent/Controls/CalendarDayButton.xaml b/src/Avalonia.Themes.Fluent/Controls/CalendarDayButton.xaml index b119dd1355..9f476d51bc 100644 --- a/src/Avalonia.Themes.Fluent/Controls/CalendarDayButton.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/CalendarDayButton.xaml @@ -84,11 +84,11 @@ - - diff --git a/src/Avalonia.Themes.Fluent/Controls/CheckBox.xaml b/src/Avalonia.Themes.Fluent/Controls/CheckBox.xaml index 2eee425cb0..44e1afefa4 100644 --- a/src/Avalonia.Themes.Fluent/Controls/CheckBox.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/CheckBox.xaml @@ -63,64 +63,64 @@ - - - - - - - - - - - - @@ -145,64 +145,64 @@ - - - - - - - - - - - - @@ -227,64 +227,64 @@ - - - - - - - - - - - - diff --git a/src/Avalonia.Themes.Fluent/Controls/ComboBox.xaml b/src/Avalonia.Themes.Fluent/Controls/ComboBox.xaml index 2dc7439001..8ef8388cf1 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ComboBox.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ComboBox.xaml @@ -144,17 +144,15 @@ - - - + + + @@ -175,51 +173,51 @@ - - - - - - - - - - - - diff --git a/src/Avalonia.Themes.Fluent/Controls/Slider.xaml b/src/Avalonia.Themes.Fluent/Controls/Slider.xaml index b468f7f4ad..c74878e8be 100644 --- a/src/Avalonia.Themes.Fluent/Controls/Slider.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/Slider.xaml @@ -283,17 +283,15 @@ - - - + + + @@ -316,70 +314,70 @@ - - - - - - - - - - - - - - diff --git a/src/Avalonia.Themes.Fluent/Controls/TimePicker.xaml b/src/Avalonia.Themes.Fluent/Controls/TimePicker.xaml index 69b2d32dda..dc09032a36 100644 --- a/src/Avalonia.Themes.Fluent/Controls/TimePicker.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/TimePicker.xaml @@ -82,11 +82,11 @@ --> - - From 671b7ea83e75a2b442a4ee0d5877c0e269dfbc48 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 7 Jul 2022 11:50:33 +0200 Subject: [PATCH 043/151] WIP: HamburgerMenu. --- samples/ControlCatalog/App.xaml | 8 +- .../HamburgerMenu/HamburgerMenu.xaml | 260 +++++++++--------- 2 files changed, 139 insertions(+), 129 deletions(-) diff --git a/samples/ControlCatalog/App.xaml b/samples/ControlCatalog/App.xaml index d0e1bd885e..c7ad72d2c5 100644 --- a/samples/ControlCatalog/App.xaml +++ b/samples/ControlCatalog/App.xaml @@ -5,6 +5,13 @@ x:CompileBindings="True" Name="Avalonia ControlCatalog" x:Class="ControlCatalog.App"> + + + + + + + - diff --git a/samples/SampleControls/HamburgerMenu/HamburgerMenu.xaml b/samples/SampleControls/HamburgerMenu/HamburgerMenu.xaml index 1d58c465a0..5f8bfe4fb5 100644 --- a/samples/SampleControls/HamburgerMenu/HamburgerMenu.xaml +++ b/samples/SampleControls/HamburgerMenu/HamburgerMenu.xaml @@ -1,6 +1,6 @@ - + @@ -20,21 +20,84 @@ - - 40 - 220 - 36 - 36 - 32 - 12,0,0,0 - 52,0,0,0 - 212,0,0,0 - 1 1 1 1 #2000, 0 0 1 1 #2fff - 0 0 1 1 #2000 - + 40 + 220 + 36 + 36 + 32 + 12,0,0,0 + 52,0,0,0 + 212,0,0,0 + 1 1 1 1 #2000, 0 0 1 1 #2fff + 0 0 1 1 #2000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + IEnumerable Containers { get; } + /// + /// Gets or sets the theme to be applied to the items in the control. + /// + ControlTheme? ItemContainerTheme { get; set; } + /// /// Gets or sets the data template used to display the items in the control. /// diff --git a/src/Avalonia.Controls/Generators/ItemContainerGenerator.cs b/src/Avalonia.Controls/Generators/ItemContainerGenerator.cs index a76dcbe9c8..8b36b07cec 100644 --- a/src/Avalonia.Controls/Generators/ItemContainerGenerator.cs +++ b/src/Avalonia.Controls/Generators/ItemContainerGenerator.cs @@ -4,6 +4,7 @@ using System.Linq; using Avalonia.Controls.Presenters; using Avalonia.Controls.Templates; using Avalonia.Data; +using Avalonia.Styling; namespace Avalonia.Controls.Generators { @@ -35,6 +36,11 @@ namespace Avalonia.Controls.Generators /// public event EventHandler? Recycled; + /// + /// Gets or sets the theme to be applied to the items in the control. + /// + public ControlTheme? ItemContainerTheme { get; set; } + /// /// Gets or sets the data template used to display the items in the control. /// @@ -190,10 +196,18 @@ namespace Avalonia.Controls.Generators result.SetValue( ContentPresenter.ContentTemplateProperty, ItemTemplate, - BindingPriority.TemplatedParent); + BindingPriority.Style); } } + if (ItemContainerTheme != null) + { + result.SetValue( + StyledElement.ThemeProperty, + ItemContainerTheme, + BindingPriority.TemplatedParent); + } + return result; } diff --git a/src/Avalonia.Controls/Generators/ItemContainerGenerator`1.cs b/src/Avalonia.Controls/Generators/ItemContainerGenerator`1.cs index 635f3a7d37..3ff1b0702d 100644 --- a/src/Avalonia.Controls/Generators/ItemContainerGenerator`1.cs +++ b/src/Avalonia.Controls/Generators/ItemContainerGenerator`1.cs @@ -44,28 +44,29 @@ namespace Avalonia.Controls.Generators { var container = item as T; - if (container != null) + if (container is null) { - return container; - } - else - { - var result = new T(); + container = new T(); if (ContentTemplateProperty != null) { - result.SetValue(ContentTemplateProperty, ItemTemplate, BindingPriority.Style); + container.SetValue(ContentTemplateProperty, ItemTemplate, BindingPriority.Style); } - result.SetValue(ContentProperty, item, BindingPriority.Style); + container.SetValue(ContentProperty, item, BindingPriority.Style); if (!(item is IControl)) { - result.DataContext = item; + container.DataContext = item; } + } - return result; + if (ItemContainerTheme != null) + { + container.SetValue(StyledElement.ThemeProperty, ItemContainerTheme, BindingPriority.Style); } + + return container; } /// diff --git a/src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs b/src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs index 536a5fdd06..4e3deb5552 100644 --- a/src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs +++ b/src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs @@ -71,6 +71,11 @@ namespace Avalonia.Controls.Generators var template = GetTreeDataTemplate(item, ItemTemplate); var result = new T(); + if (ItemContainerTheme != null) + { + result.SetValue(Control.ThemeProperty, ItemContainerTheme, BindingPriority.Style); + } + result.SetValue(ContentProperty, template.Build(item), BindingPriority.Style); var itemsSelector = template.ItemsSelector(item); diff --git a/src/Avalonia.Controls/ItemsControl.cs b/src/Avalonia.Controls/ItemsControl.cs index 56b0014c05..427f5c8a9b 100644 --- a/src/Avalonia.Controls/ItemsControl.cs +++ b/src/Avalonia.Controls/ItemsControl.cs @@ -15,6 +15,7 @@ using Avalonia.Input; using Avalonia.LogicalTree; using Avalonia.Metadata; using Avalonia.VisualTree; +using Avalonia.Styling; namespace Avalonia.Controls { @@ -36,6 +37,12 @@ namespace Avalonia.Controls public static readonly DirectProperty ItemsProperty = AvaloniaProperty.RegisterDirect(nameof(Items), o => o.Items, (o, v) => o.Items = v); + /// + /// Defines the property. + /// + public static readonly StyledProperty ItemContainerThemeProperty = + AvaloniaProperty.Register(nameof(ItemContainerTheme)); + /// /// Defines the property. /// @@ -88,6 +95,7 @@ namespace Avalonia.Controls { _itemContainerGenerator = CreateItemContainerGenerator(); + _itemContainerGenerator.ItemContainerTheme = ItemContainerTheme; _itemContainerGenerator.ItemTemplate = ItemTemplate; _itemContainerGenerator.Materialized += (_, e) => OnContainersMaterialized(e); _itemContainerGenerator.Dematerialized += (_, e) => OnContainersDematerialized(e); @@ -108,6 +116,15 @@ namespace Avalonia.Controls set { SetAndRaise(ItemsProperty, ref _items, value); } } + /// + /// Gets or sets the that is applied to the container element generated for each item. + /// + public ControlTheme? ItemContainerTheme + { + get { return GetValue(ItemContainerThemeProperty); } + set { SetValue(ItemContainerThemeProperty, value); } + } + /// /// Gets the number of items in . /// @@ -349,6 +366,10 @@ namespace Avalonia.Controls { UpdatePseudoClasses(change.GetNewValue()); } + else if (change.Property == ItemContainerThemeProperty && _itemContainerGenerator is not null) + { + _itemContainerGenerator.ItemContainerTheme = change.GetNewValue(); + } } /// diff --git a/src/Avalonia.Controls/TreeViewItem.cs b/src/Avalonia.Controls/TreeViewItem.cs index 490b0b3ce3..2e3aa037c2 100644 --- a/src/Avalonia.Controls/TreeViewItem.cs +++ b/src/Avalonia.Controls/TreeViewItem.cs @@ -121,6 +121,11 @@ namespace Avalonia.Controls { ItemTemplate = _treeView.ItemTemplate; } + + if (ItemContainerTheme == null && _treeView?.ItemContainerTheme != null) + { + ItemContainerTheme = _treeView.ItemContainerTheme; + } } protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e) diff --git a/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs b/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs index bfece7871c..f08653a4f8 100644 --- a/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs @@ -1,15 +1,14 @@ +using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Linq; using Avalonia.Collections; using Avalonia.Controls.Presenters; using Avalonia.Controls.Templates; +using Avalonia.Input; using Avalonia.LogicalTree; -using Avalonia.VisualTree; -using Xunit; -using System.Collections.ObjectModel; +using Avalonia.Styling; using Avalonia.UnitTests; -using Avalonia.Input; -using System.Collections.Generic; +using Xunit; namespace Avalonia.Controls.UnitTests { @@ -62,6 +61,25 @@ namespace Avalonia.Controls.UnitTests Assert.Null(container.TemplatedParent); } + [Fact] + public void Container_Should_Have_Theme_Set_To_ItemContainerTheme() + { + var theme = new ControlTheme(); + var target = new ItemsControl + { + ItemContainerTheme = theme, + }; + + target.Template = GetTemplate(); + target.Items = new[] { "Foo" }; + target.ApplyTemplate(); + target.Presenter.ApplyTemplate(); + + var container = (ContentPresenter)target.Presenter.Panel.Children[0]; + + Assert.Same(container.Theme, theme); + } + [Fact] public void Container_Should_Have_LogicalParent_Set_To_ItemsControl() { diff --git a/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs b/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs index e87990ebb1..afa153a593 100644 --- a/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs @@ -94,6 +94,50 @@ namespace Avalonia.Controls.UnitTests } } + [Fact] + public void Container_Should_Have_Theme_Set_To_ItemContainerTheme() + { + using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface)) + { + var items = new[] { "Foo", "Bar", "Baz " }; + var theme = new ControlTheme(); + var target = new ListBox + { + Template = ListBoxTemplate(), + Items = items, + ItemContainerTheme = theme, + }; + + Prepare(target); + + var container = (ListBoxItem)target.Presenter.Panel.Children[0]; + + Assert.Same(container.Theme, theme); + } + } + + [Fact] + public void Inline_Item_Should_Have_Theme_Set_To_ItemContainerTheme() + { + using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface)) + { + var items = new[] { "Foo", "Bar", "Baz " }; + var theme = new ControlTheme(); + var target = new ListBox + { + Template = ListBoxTemplate(), + Items = new[] { new ListBoxItem() }, + ItemContainerTheme = theme, + }; + + Prepare(target); + + var container = (ListBoxItem)target.Presenter.Panel.Children[0]; + + Assert.Same(container.Theme, theme); + } + } + [Fact] public void LogicalChildren_Should_Be_Set_For_DataTemplate_Generated_Items() { diff --git a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs index 2169b15cad..d784caf2db 100644 --- a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs @@ -72,6 +72,35 @@ namespace Avalonia.Controls.UnitTests Assert.All(items, x => Assert.IsType(x.HeaderPresenter.Child)); } + [Fact] + public void Items_Should_Be_Created_Using_ItemConatinerTheme_If_Present() + { + TreeView target; + var theme = new ControlTheme(); + + var root = new TestRoot + { + Child = target = new TreeView + { + Template = CreateTreeViewTemplate(), + Items = CreateTestTreeData(), + ItemContainerTheme = theme, + ItemTemplate = new FuncTreeDataTemplate( + (_, __) => new Canvas(), + x => x.Children), + } + }; + + ApplyTemplates(target); + + var items = target.ItemContainerGenerator.Index.Containers + .OfType() + .ToList(); + + Assert.Equal(5, items.Count); + Assert.All(items, x => Assert.Same(theme, x.ItemContainerTheme)); + } + [Fact] public void Root_ItemContainerGenerator_Containers_Should_Be_Root_Containers() { From 209797e5e1375c2c20a335d3bc2cf66810d9b129 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 7 Jul 2022 23:32:23 +0200 Subject: [PATCH 045/151] Ported HamburgerMenu to ControlTheme. --- .../HamburgerMenu/HamburgerMenu.xaml | 89 +++++++++++-------- 1 file changed, 54 insertions(+), 35 deletions(-) diff --git a/samples/SampleControls/HamburgerMenu/HamburgerMenu.xaml b/samples/SampleControls/HamburgerMenu/HamburgerMenu.xaml index 5f8bfe4fb5..73f8e480f5 100644 --- a/samples/SampleControls/HamburgerMenu/HamburgerMenu.xaml +++ b/samples/SampleControls/HamburgerMenu/HamburgerMenu.xaml @@ -62,9 +62,28 @@ TextElement.FontWeight="{TemplateBinding FontWeight}" /> + + + + + + + + + + + + + + + + Background="{DynamicResource TabItemHeaderSelectedPipeFill}" + IsVisible="False" + CornerRadius="{DynamicResource ControlCornerRadius}"/> + + + + + + + + - + + + @@ -109,7 +157,8 @@ CompactPaneLength="{StaticResource PaneCompactWidth}" DisplayMode="Inline" IsPaneOpen="True" - OpenPaneLength="{StaticResource PaneExpandWidth}"> + OpenPaneLength="{StaticResource PaneExpandWidth}" + PaneBackground="Transparent"> @@ -123,11 +172,6 @@ ItemTemplate="{TemplateBinding ItemTemplate}" Items="{TemplateBinding Items}" ItemsPanel="{TemplateBinding ItemsPanel}"> - - - @@ -187,7 +232,7 @@ HorizontalAlignment="Left" VerticalAlignment="Top" HorizontalContentAlignment="Center" - Classes="NavigationButton" + Theme="{StaticResource NavigationButton}" CornerRadius="4" IsChecked="{Binding #PART_NavigationPane.IsPaneOpen, Mode=TwoWay}"> @@ -196,18 +241,12 @@ - - @@ -221,26 +260,6 @@ - - From 59c58e567242f9641759ef1f1d5dc8380792e03b Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 8 Jul 2022 11:47:26 +0200 Subject: [PATCH 046/151] Don't set Foreground in PathIcon theme. It is more useful if this property inherits by default. --- src/Avalonia.Themes.Fluent/Controls/PathIcon.xaml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/PathIcon.xaml b/src/Avalonia.Themes.Fluent/Controls/PathIcon.xaml index 966c6c2632..b9967a1b44 100644 --- a/src/Avalonia.Themes.Fluent/Controls/PathIcon.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/PathIcon.xaml @@ -9,7 +9,6 @@ - From 052cf699d00e30a51347845789bf20f1cd18965d Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 8 Jul 2022 11:47:44 +0200 Subject: [PATCH 047/151] Ported ScrollBar to ControlTheme. --- .../Controls/FluentControls.xaml | 2 +- .../Controls/ScrollBar.xaml | 521 +++++++++--------- 2 files changed, 252 insertions(+), 271 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml b/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml index 286b3e3fcd..d344fedf0a 100644 --- a/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml @@ -34,6 +34,7 @@ + @@ -64,7 +65,6 @@ - diff --git a/src/Avalonia.Themes.Fluent/Controls/ScrollBar.xaml b/src/Avalonia.Themes.Fluent/Controls/ScrollBar.xaml index 7d87a3e35a..4544fbe339 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ScrollBar.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ScrollBar.xaml @@ -1,302 +1,283 @@ - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - - + - - - - - - - - + + + - + + + - + + + + - - - - - - - - - - - - - - - - - + + + + + + - + - + + + + + + + + + + From 9d0132dc24535b89aed7e04fc8da2cfecad061f6 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 8 Jul 2022 11:56:21 +0200 Subject: [PATCH 048/151] Remove unneeded normal state. --- .../Controls/ComboBox.xaml | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/ComboBox.xaml b/src/Avalonia.Themes.Fluent/Controls/ComboBox.xaml index 8ef8388cf1..b3da7735eb 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ComboBox.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ComboBox.xaml @@ -72,7 +72,8 @@ Background="{DynamicResource ComboBoxBackgroundUnfocused}" BorderBrush="{DynamicResource ComboBoxBackgroundBorderBrushUnfocused}" BorderThickness="{TemplateBinding BorderThickness}" - CornerRadius="{TemplateBinding CornerRadius}" /> + CornerRadius="{TemplateBinding CornerRadius}" + IsVisible="False"/> + VerticalAlignment="Center" + Foreground="{DynamicResource ComboBoxDropDownGlyphForeground}" + Data="M1939 486L2029 576L1024 1581L19 576L109 486L1024 1401L1939 486Z"/> - - - - - + + - - diff --git a/src/Avalonia.Themes.Fluent/Controls/ToggleButton.xaml b/src/Avalonia.Themes.Fluent/Controls/ToggleButton.xaml index b53980b792..617f3e9c45 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ToggleButton.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ToggleButton.xaml @@ -10,111 +10,117 @@ - 8,5,8,6 + 8,5,8,6 - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - + - - - - - - - - - + + + - + + From c15837b743264282fa070eea4fbc63ebacd3d5c1 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 8 Jul 2022 13:55:18 +0200 Subject: [PATCH 050/151] Ported ButtonSpinner to ControlTheme. --- .../Controls/ButtonSpinner.xaml | 228 ++++++++++-------- 1 file changed, 133 insertions(+), 95 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/ButtonSpinner.xaml b/src/Avalonia.Themes.Fluent/Controls/ButtonSpinner.xaml index f99f1040e5..980ea02a73 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ButtonSpinner.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ButtonSpinner.xaml @@ -3,106 +3,144 @@ xmlns:sys="clr-namespace:System;assembly=netstandard" xmlns:converters="clr-namespace:Avalonia.Controls.Converters;assembly=Avalonia.Controls" x:CompileBindings="True"> - - - - - - - - - - - - - + + + + + + + + + + + + + + + Error + + + + + + + - - + + + M0,9 L10,0 20,9 19,10 10,2 1,10 z + M0,1 L10,10 20,1 19,0 10,8 1,0 z - M0,9 L10,0 20,9 19,10 10,2 1,10 z - M0,1 L10,10 20,1 19,0 10,8 1,0 z + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - + - - - + + + + - - + + + + + + + + - + + + + From ddbc9fbfcaf7524fcd7ed03a3f481dab7ecff1bf Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 8 Jul 2022 14:01:56 +0200 Subject: [PATCH 051/151] Tweaked CheckBox ControlTheme. --- .../Controls/CheckBox.xaml | 451 +++++++++--------- 1 file changed, 228 insertions(+), 223 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/CheckBox.xaml b/src/Avalonia.Themes.Fluent/Controls/CheckBox.xaml index 44e1afefa4..9abcf5d32b 100644 --- a/src/Avalonia.Themes.Fluent/Controls/CheckBox.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/CheckBox.xaml @@ -1,293 +1,298 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + Unchecked + Checked + Indeterminate + Checkbox should wrap its text + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + + - + - + + + - - - - - - - - + - - - - - - - - + - - - - - - - - + - + + + - - - - - - - - + + + + - - - - - - - - + - - - - - - - - + - + + From 47215816a22a68fd5c7ba863e78843127ba6e603 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 8 Jul 2022 14:08:03 +0200 Subject: [PATCH 052/151] Validated ComboBox/Item ControlTheme. --- .../Controls/ComboBox.xaml | 416 +++++++++--------- .../Controls/ComboBoxItem.xaml | 153 +++---- 2 files changed, 290 insertions(+), 279 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/ComboBox.xaml b/src/Avalonia.Themes.Fluent/Controls/ComboBox.xaml index b3da7735eb..ec78bf6ab4 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ComboBox.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ComboBox.xaml @@ -1,217 +1,227 @@  - - - - - Item 1 - Item 2 - - - Item 1 - Item 2 - - - - - - 0,0,0,4 - 15 - 7 - - 12,5,0,7 - 11,5,32,6 - 32 - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + Item 1 + Item 2 + + + Item 1 + Item 2 + + + + + + Error + + + + + + + + + 0,0,0,4 + 15 + 7 + + 12,5,0,7 + 11,5,32,6 + 32 + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + - - - - - - - - - - - - - - + UseLayoutRounding="False" + IsHitTestVisible="False" + Height="12" + Width="12" + Margin="0,0,10,0" + HorizontalAlignment="Right" + VerticalAlignment="Center" + Foreground="{DynamicResource ComboBoxDropDownGlyphForeground}" + Data="M1939 486L2029 576L1024 1581L19 576L109 486L1024 1401L1939 486Z"/> + + + + + + + + + + + + + + + + + + - - + + + - - - - - - + - - - - + + - - - - - - + + + + - - - - - - + + + - - + + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/ComboBoxItem.xaml b/src/Avalonia.Themes.Fluent/Controls/ComboBoxItem.xaml index 88f9c0ea15..581cbaf80a 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ComboBoxItem.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ComboBoxItem.xaml @@ -1,85 +1,86 @@  - - - - - Item 1 - Item 2 long - Item 3 - Item 4 - - + + + + + Item 1 + Item 2 long + Item 3 + Item 4 + Item 5 + - - - - - - - - - - - - - - - + + + + + + + + + - - + - - - - - - - - - - - - + + - + + From 100978a5bf491b75d89fa0669fcce52a212d021d Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 8 Jul 2022 14:09:41 +0200 Subject: [PATCH 053/151] Formatted ContentControl ControlTheme. --- .../Controls/ContentControl.xaml | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/ContentControl.xaml b/src/Avalonia.Themes.Fluent/Controls/ContentControl.xaml index 5777603074..4caf156366 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ContentControl.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ContentControl.xaml @@ -1,19 +1,19 @@ - - - - - - - + + + + + + + From e89060fbc3c655340fc254ca26744ab9ddcd3ab9 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 8 Jul 2022 14:18:48 +0200 Subject: [PATCH 054/151] Formatted DataValidationErrors ControlTheme. --- .../Controls/DataValidationErrors.xaml | 198 +++++++++--------- 1 file changed, 99 insertions(+), 99 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/DataValidationErrors.xaml b/src/Avalonia.Themes.Fluent/Controls/DataValidationErrors.xaml index 6ed73148a7..cc55f84d80 100644 --- a/src/Avalonia.Themes.Fluent/Controls/DataValidationErrors.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/DataValidationErrors.xaml @@ -1,103 +1,103 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From ebe9d6b086c3da15678aea2408412df18ce520e7 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 8 Jul 2022 14:29:30 +0200 Subject: [PATCH 055/151] Tidied up DropDownButton ControlTheme. --- .../Controls/DropDownButton.xaml | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/DropDownButton.xaml b/src/Avalonia.Themes.Fluent/Controls/DropDownButton.xaml index 3206cb8cb1..96fd575f4f 100644 --- a/src/Avalonia.Themes.Fluent/Controls/DropDownButton.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/DropDownButton.xaml @@ -57,26 +57,26 @@ Margin="0,0,10,0" Data="M1939 486L2029 576L1024 1581L19 576L109 486L1024 1401L1939 486Z" HorizontalAlignment="Right" - VerticalAlignment="Center" /> + VerticalAlignment="Center" + Foreground="{DynamicResource ComboBoxDropDownGlyphForeground}"/> - - - - + + + - - From 6f797a9c56f643cc724521e75d5ba9ba9a3882bf Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 8 Jul 2022 14:30:04 +0200 Subject: [PATCH 056/151] Tidied up EmbeddableControlRoot ControlTheme. --- .../Controls/EmbeddableControlRoot.xaml | 39 +++++++++---------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/EmbeddableControlRoot.xaml b/src/Avalonia.Themes.Fluent/Controls/EmbeddableControlRoot.xaml index 89eaec70ad..f8b4854553 100644 --- a/src/Avalonia.Themes.Fluent/Controls/EmbeddableControlRoot.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/EmbeddableControlRoot.xaml @@ -1,23 +1,22 @@ - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + From 4f3fafc9dfa6835ed1707715eabd0665dda9bb52 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 8 Jul 2022 14:56:36 +0200 Subject: [PATCH 057/151] Tweaked Expander ControlTheme. And added "Rounded" option to ControlCatalog for testing. --- .../ControlCatalog/Pages/ExpanderPage.xaml | 15 ++-- .../ControlCatalog/Pages/ExpanderPage.xaml.cs | 2 + .../ViewModels/ExpanderPageViewModel.cs | 27 +++++++ .../Controls/Expander.xaml | 71 +++++++++---------- 4 files changed, 72 insertions(+), 43 deletions(-) create mode 100644 samples/ControlCatalog/ViewModels/ExpanderPageViewModel.cs diff --git a/samples/ControlCatalog/Pages/ExpanderPage.xaml b/samples/ControlCatalog/Pages/ExpanderPage.xaml index cef473af04..f9ae1c7a3c 100644 --- a/samples/ControlCatalog/Pages/ExpanderPage.xaml +++ b/samples/ControlCatalog/Pages/ExpanderPage.xaml @@ -8,26 +8,31 @@ Margin="0,16,0,0" HorizontalAlignment="Center" Spacing="16"> - + Expanded content - + Expanded content - + Expanded content - + Expanded content - + Rounded + diff --git a/samples/ControlCatalog/Pages/ExpanderPage.xaml.cs b/samples/ControlCatalog/Pages/ExpanderPage.xaml.cs index 52166d1a5f..e8a080899a 100644 --- a/samples/ControlCatalog/Pages/ExpanderPage.xaml.cs +++ b/samples/ControlCatalog/Pages/ExpanderPage.xaml.cs @@ -1,5 +1,6 @@ using Avalonia.Controls; using Avalonia.Markup.Xaml; +using ControlCatalog.ViewModels; namespace ControlCatalog.Pages { @@ -8,6 +9,7 @@ namespace ControlCatalog.Pages public ExpanderPage() { this.InitializeComponent(); + DataContext = new ExpanderPageViewModel(); } private void InitializeComponent() diff --git a/samples/ControlCatalog/ViewModels/ExpanderPageViewModel.cs b/samples/ControlCatalog/ViewModels/ExpanderPageViewModel.cs new file mode 100644 index 0000000000..18511a6071 --- /dev/null +++ b/samples/ControlCatalog/ViewModels/ExpanderPageViewModel.cs @@ -0,0 +1,27 @@ +using Avalonia; +using MiniMvvm; + +namespace ControlCatalog.ViewModels +{ + public class ExpanderPageViewModel : ViewModelBase + { + private CornerRadius _cornerRadius; + private bool _rounded; + + public CornerRadius CornerRadius + { + get => _cornerRadius; + private set => RaiseAndSetIfChanged(ref _cornerRadius, value); + } + + public bool Rounded + { + get => _rounded; + set + { + if (RaiseAndSetIfChanged(ref _rounded, value)) + CornerRadius = _rounded ? new CornerRadius(25) : default; + } + } + } +} diff --git a/src/Avalonia.Themes.Fluent/Controls/Expander.xaml b/src/Avalonia.Themes.Fluent/Controls/Expander.xaml index 36f6008782..7d5bb48333 100644 --- a/src/Avalonia.Themes.Fluent/Controls/Expander.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/Expander.xaml @@ -3,29 +3,39 @@ x:CompileBindings="True"> - - - - - - - - + + Expanded content - + Expanded content - + Expanded content - + + + Expanded content + + + + + + + + + + + Expanded content + + + Expanded content @@ -36,25 +46,19 @@ 16 16 - 1 - 1,1,0,1 1,1,1,0 0,1,1,1 1,0,1,1 - - - - - + - - @@ -186,28 +190,28 @@ - - - - @@ -244,7 +240,6 @@ - From c246d76a206df3b8325006d88ef8aa7891772e1a Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 8 Jul 2022 14:58:15 +0200 Subject: [PATCH 058/151] Formated ItemsControl ControlTheme. --- .../Controls/ItemsControl.xaml | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/ItemsControl.xaml b/src/Avalonia.Themes.Fluent/Controls/ItemsControl.xaml index b8d7c2c4ef..fc85b7b9ff 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ItemsControl.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ItemsControl.xaml @@ -1,19 +1,19 @@ - - - - - - - - - + + + + + + + + + From 4d73e2b3a946fa65db1df2c7b7c222e0c1b6410a Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 8 Jul 2022 14:58:40 +0200 Subject: [PATCH 059/151] Formatted Label ControlTheme. --- .../Controls/Label.xaml | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/Label.xaml b/src/Avalonia.Themes.Fluent/Controls/Label.xaml index ad57239648..ff6909ee87 100644 --- a/src/Avalonia.Themes.Fluent/Controls/Label.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/Label.xaml @@ -1,22 +1,22 @@ - - - - - - - - + + + + + + + + From 901026a187d03fa020c7c503efb443f855cc32f8 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 8 Jul 2022 15:01:03 +0200 Subject: [PATCH 060/151] Formatted ListBox/Item ControlTheme. --- .../Controls/ListBox.xaml | 88 ++++++------ .../Controls/ListBoxItem.xaml | 132 +++++++++--------- 2 files changed, 110 insertions(+), 110 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/ListBox.xaml b/src/Avalonia.Themes.Fluent/Controls/ListBox.xaml index b1d4b77a3f..c17dc65cca 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ListBox.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ListBox.xaml @@ -1,47 +1,47 @@  - - - - Test - Test - Test - Test - - - - - - - - - - - - - - - - - - - - - - - + + + + Test + Test + Test + Test + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/ListBoxItem.xaml b/src/Avalonia.Themes.Fluent/Controls/ListBoxItem.xaml index c00e920f75..fc1d0c5ff1 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ListBoxItem.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ListBoxItem.xaml @@ -1,79 +1,79 @@  - - - - Disabled - - Test - - Test - - - - 12,9,12,12 - - - - - - - - + + + + Disabled + + Test + + Test + + + + 12,9,12,12 + + + + + + + + - + - - + + + + + + - - - - - - - - - - + + - - + + - + + From 800967c62742ca9934f47d6e68be94095b1241c1 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 8 Jul 2022 15:02:52 +0200 Subject: [PATCH 061/151] Formatted Menu/FlyoutPresenter ControlTheme. --- src/Avalonia.Themes.Fluent/Controls/Menu.xaml | 62 ++++++++--------- .../Controls/MenuFlyoutPresenter.xaml | 67 ++++++++++--------- 2 files changed, 65 insertions(+), 64 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/Menu.xaml b/src/Avalonia.Themes.Fluent/Controls/Menu.xaml index d42e413f00..11d21b6b23 100644 --- a/src/Avalonia.Themes.Fluent/Controls/Menu.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/Menu.xaml @@ -1,35 +1,35 @@ - - - - - - - - + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> + + + + + + + + - 32 + 32 - - - - - - - - - - - + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/MenuFlyoutPresenter.xaml b/src/Avalonia.Themes.Fluent/Controls/MenuFlyoutPresenter.xaml index 7769f0a5c9..264438390d 100644 --- a/src/Avalonia.Themes.Fluent/Controls/MenuFlyoutPresenter.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/MenuFlyoutPresenter.xaml @@ -1,34 +1,35 @@ - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + From f82541a90d4916f192e432c76fa329d4d3dd3dbf Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 8 Jul 2022 15:09:47 +0200 Subject: [PATCH 062/151] Tweaked NotificationCard ControlTheme. --- .../Accents/FluentControlResourcesDark.xaml | 1 + .../Accents/FluentControlResourcesLight.xaml | 1 + .../Controls/NotificationCard.xaml | 82 +++++++++---------- 3 files changed, 43 insertions(+), 41 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml b/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml index c5bb70bed3..9d04009aed 100644 --- a/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml +++ b/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml @@ -308,6 +308,7 @@ + diff --git a/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesLight.xaml b/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesLight.xaml index 8d38d39bd5..7b673767b8 100644 --- a/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesLight.xaml +++ b/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesLight.xaml @@ -304,6 +304,7 @@ + diff --git a/src/Avalonia.Themes.Fluent/Controls/NotificationCard.xaml b/src/Avalonia.Themes.Fluent/Controls/NotificationCard.xaml index e55fcb90e8..51dcdd42e3 100644 --- a/src/Avalonia.Themes.Fluent/Controls/NotificationCard.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/NotificationCard.xaml @@ -9,7 +9,7 @@ - + @@ -53,47 +53,47 @@ - + - + - - - - + + + + From db954bfd0ebf0401a9bc1cfc6dc517cd1e2d5a08 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 8 Jul 2022 15:11:10 +0200 Subject: [PATCH 063/151] Formatted NumericUpDown ControlTheme. --- .../Controls/NumericUpDown.xaml | 126 +++++++++--------- 1 file changed, 62 insertions(+), 64 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/NumericUpDown.xaml b/src/Avalonia.Themes.Fluent/Controls/NumericUpDown.xaml index 74ffff0f5c..872367c6da 100644 --- a/src/Avalonia.Themes.Fluent/Controls/NumericUpDown.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/NumericUpDown.xaml @@ -1,67 +1,65 @@  - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + From 474df23c0715253fe3be730fc1cdd59d093a2749 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 8 Jul 2022 15:11:35 +0200 Subject: [PATCH 064/151] Formatted OverlayPopupHost ControlTheme. --- .../Controls/OverlayPopupHost.xaml | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/OverlayPopupHost.xaml b/src/Avalonia.Themes.Fluent/Controls/OverlayPopupHost.xaml index 0b587e6037..28d8fb508c 100644 --- a/src/Avalonia.Themes.Fluent/Controls/OverlayPopupHost.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/OverlayPopupHost.xaml @@ -1,23 +1,23 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + From 0d7bb472b38e042e44b1200f0f08e0c405f6c353 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 8 Jul 2022 15:12:20 +0200 Subject: [PATCH 065/151] Formatted PathIcon ControlTheme. --- .../Controls/PathIcon.xaml | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/PathIcon.xaml b/src/Avalonia.Themes.Fluent/Controls/PathIcon.xaml index b9967a1b44..5584eb3ab9 100644 --- a/src/Avalonia.Themes.Fluent/Controls/PathIcon.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/PathIcon.xaml @@ -1,25 +1,25 @@  - - - - M14 9.50006C11.5147 9.50006 9.5 11.5148 9.5 14.0001C9.5 16.4853 11.5147 18.5001 14 18.5001C15.3488 18.5001 16.559 17.9066 17.3838 16.9666C18.0787 16.1746 18.5 15.1365 18.5 14.0001C18.5 13.5401 18.431 13.0963 18.3028 12.6784C17.7382 10.8381 16.0253 9.50006 14 9.50006ZM11 14.0001C11 12.3432 12.3431 11.0001 14 11.0001C15.6569 11.0001 17 12.3432 17 14.0001C17 15.6569 15.6569 17.0001 14 17.0001C12.3431 17.0001 11 15.6569 11 14.0001Z M21.7093 22.3948L19.9818 21.6364C19.4876 21.4197 18.9071 21.4515 18.44 21.7219C17.9729 21.9924 17.675 22.4693 17.6157 23.0066L17.408 24.8855C17.3651 25.273 17.084 25.5917 16.7055 25.682C14.9263 26.1061 13.0725 26.1061 11.2933 25.682C10.9148 25.5917 10.6336 25.273 10.5908 24.8855L10.3834 23.0093C10.3225 22.4731 10.0112 21.9976 9.54452 21.7281C9.07783 21.4586 8.51117 21.4269 8.01859 21.6424L6.29071 22.4009C5.93281 22.558 5.51493 22.4718 5.24806 22.1859C4.00474 20.8536 3.07924 19.2561 2.54122 17.5137C2.42533 17.1384 2.55922 16.7307 2.8749 16.4977L4.40219 15.3703C4.83721 15.0501 5.09414 14.5415 5.09414 14.0007C5.09414 13.4598 4.83721 12.9512 4.40162 12.6306L2.87529 11.5051C2.55914 11.272 2.42513 10.8638 2.54142 10.4882C3.08038 8.74734 4.00637 7.15163 5.24971 5.82114C5.51684 5.53528 5.93492 5.44941 6.29276 5.60691L8.01296 6.36404C8.50793 6.58168 9.07696 6.54881 9.54617 6.27415C10.0133 6.00264 10.3244 5.52527 10.3844 4.98794L10.5933 3.11017C10.637 2.71803 10.9245 2.39704 11.3089 2.31138C12.19 2.11504 13.0891 2.01071 14.0131 2.00006C14.9147 2.01047 15.8128 2.11485 16.6928 2.31149C17.077 2.39734 17.3643 2.71823 17.4079 3.11017L17.617 4.98937C17.7116 5.85221 18.4387 6.50572 19.3055 6.50663C19.5385 6.507 19.769 6.45838 19.9843 6.36294L21.7048 5.60568C22.0626 5.44818 22.4807 5.53405 22.7478 5.81991C23.9912 7.1504 24.9172 8.74611 25.4561 10.487C25.5723 10.8623 25.4386 11.2703 25.1228 11.5035L23.5978 12.6297C23.1628 12.95 22.9 13.4586 22.9 13.9994C22.9 14.5403 23.1628 15.0489 23.5988 15.3698L25.1251 16.4965C25.441 16.7296 25.5748 17.1376 25.4586 17.5131C24.9198 19.2536 23.9944 20.8492 22.7517 22.1799C22.4849 22.4657 22.0671 22.5518 21.7093 22.3948ZM16.263 22.1966C16.4982 21.4685 16.9889 20.8288 17.6884 20.4238C18.5702 19.9132 19.6536 19.8547 20.5841 20.2627L21.9281 20.8526C22.791 19.8538 23.4593 18.7013 23.8981 17.4552L22.7095 16.5778L22.7086 16.5771C21.898 15.98 21.4 15.0277 21.4 13.9994C21.4 12.9719 21.8974 12.0195 22.7073 11.4227L22.7085 11.4218L23.8957 10.545C23.4567 9.2988 22.7881 8.14636 21.9248 7.1477L20.5922 7.73425L20.5899 7.73527C20.1844 7.91463 19.7472 8.00722 19.3039 8.00663C17.6715 8.00453 16.3046 6.77431 16.1261 5.15465L16.1259 5.15291L15.9635 3.69304C15.3202 3.57328 14.6677 3.50872 14.013 3.50017C13.3389 3.50891 12.6821 3.57367 12.0377 3.69328L11.8751 5.15452C11.7625 6.16272 11.1793 7.05909 10.3019 7.56986C9.41937 8.0856 8.34453 8.14844 7.40869 7.73694L6.07273 7.14893C5.20949 8.14751 4.54092 9.29983 4.10196 10.5459L5.29181 11.4233C6.11115 12.0269 6.59414 12.9837 6.59414 14.0007C6.59414 15.0173 6.11142 15.9742 5.29237 16.5776L4.10161 17.4566C4.54002 18.7044 5.2085 19.8585 6.07205 20.8587L7.41742 20.2682C8.34745 19.8613 9.41573 19.9215 10.2947 20.4292C11.174 20.937 11.7593 21.832 11.8738 22.84L11.8744 22.8445L12.0362 24.3088C13.3326 24.5638 14.6662 24.5638 15.9626 24.3088L16.1247 22.8418C16.1491 22.6217 16.1955 22.4055 16.263 22.1966Z - - - - - - - - - - - - - - - - - - + + + + M14 9.50006C11.5147 9.50006 9.5 11.5148 9.5 14.0001C9.5 16.4853 11.5147 18.5001 14 18.5001C15.3488 18.5001 16.559 17.9066 17.3838 16.9666C18.0787 16.1746 18.5 15.1365 18.5 14.0001C18.5 13.5401 18.431 13.0963 18.3028 12.6784C17.7382 10.8381 16.0253 9.50006 14 9.50006ZM11 14.0001C11 12.3432 12.3431 11.0001 14 11.0001C15.6569 11.0001 17 12.3432 17 14.0001C17 15.6569 15.6569 17.0001 14 17.0001C12.3431 17.0001 11 15.6569 11 14.0001Z M21.7093 22.3948L19.9818 21.6364C19.4876 21.4197 18.9071 21.4515 18.44 21.7219C17.9729 21.9924 17.675 22.4693 17.6157 23.0066L17.408 24.8855C17.3651 25.273 17.084 25.5917 16.7055 25.682C14.9263 26.1061 13.0725 26.1061 11.2933 25.682C10.9148 25.5917 10.6336 25.273 10.5908 24.8855L10.3834 23.0093C10.3225 22.4731 10.0112 21.9976 9.54452 21.7281C9.07783 21.4586 8.51117 21.4269 8.01859 21.6424L6.29071 22.4009C5.93281 22.558 5.51493 22.4718 5.24806 22.1859C4.00474 20.8536 3.07924 19.2561 2.54122 17.5137C2.42533 17.1384 2.55922 16.7307 2.8749 16.4977L4.40219 15.3703C4.83721 15.0501 5.09414 14.5415 5.09414 14.0007C5.09414 13.4598 4.83721 12.9512 4.40162 12.6306L2.87529 11.5051C2.55914 11.272 2.42513 10.8638 2.54142 10.4882C3.08038 8.74734 4.00637 7.15163 5.24971 5.82114C5.51684 5.53528 5.93492 5.44941 6.29276 5.60691L8.01296 6.36404C8.50793 6.58168 9.07696 6.54881 9.54617 6.27415C10.0133 6.00264 10.3244 5.52527 10.3844 4.98794L10.5933 3.11017C10.637 2.71803 10.9245 2.39704 11.3089 2.31138C12.19 2.11504 13.0891 2.01071 14.0131 2.00006C14.9147 2.01047 15.8128 2.11485 16.6928 2.31149C17.077 2.39734 17.3643 2.71823 17.4079 3.11017L17.617 4.98937C17.7116 5.85221 18.4387 6.50572 19.3055 6.50663C19.5385 6.507 19.769 6.45838 19.9843 6.36294L21.7048 5.60568C22.0626 5.44818 22.4807 5.53405 22.7478 5.81991C23.9912 7.1504 24.9172 8.74611 25.4561 10.487C25.5723 10.8623 25.4386 11.2703 25.1228 11.5035L23.5978 12.6297C23.1628 12.95 22.9 13.4586 22.9 13.9994C22.9 14.5403 23.1628 15.0489 23.5988 15.3698L25.1251 16.4965C25.441 16.7296 25.5748 17.1376 25.4586 17.5131C24.9198 19.2536 23.9944 20.8492 22.7517 22.1799C22.4849 22.4657 22.0671 22.5518 21.7093 22.3948ZM16.263 22.1966C16.4982 21.4685 16.9889 20.8288 17.6884 20.4238C18.5702 19.9132 19.6536 19.8547 20.5841 20.2627L21.9281 20.8526C22.791 19.8538 23.4593 18.7013 23.8981 17.4552L22.7095 16.5778L22.7086 16.5771C21.898 15.98 21.4 15.0277 21.4 13.9994C21.4 12.9719 21.8974 12.0195 22.7073 11.4227L22.7085 11.4218L23.8957 10.545C23.4567 9.2988 22.7881 8.14636 21.9248 7.1477L20.5922 7.73425L20.5899 7.73527C20.1844 7.91463 19.7472 8.00722 19.3039 8.00663C17.6715 8.00453 16.3046 6.77431 16.1261 5.15465L16.1259 5.15291L15.9635 3.69304C15.3202 3.57328 14.6677 3.50872 14.013 3.50017C13.3389 3.50891 12.6821 3.57367 12.0377 3.69328L11.8751 5.15452C11.7625 6.16272 11.1793 7.05909 10.3019 7.56986C9.41937 8.0856 8.34453 8.14844 7.40869 7.73694L6.07273 7.14893C5.20949 8.14751 4.54092 9.29983 4.10196 10.5459L5.29181 11.4233C6.11115 12.0269 6.59414 12.9837 6.59414 14.0007C6.59414 15.0173 6.11142 15.9742 5.29237 16.5776L4.10161 17.4566C4.54002 18.7044 5.2085 19.8585 6.07205 20.8587L7.41742 20.2682C8.34745 19.8613 9.41573 19.9215 10.2947 20.4292C11.174 20.937 11.7593 21.832 11.8738 22.84L11.8744 22.8445L12.0362 24.3088C13.3326 24.5638 14.6662 24.5638 15.9626 24.3088L16.1247 22.8418C16.1491 22.6217 16.1955 22.4055 16.263 22.1966Z + + + + + + + + + + + + + + + + + + From 8b69e083e8968df9361378454ddad87b834159f2 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 8 Jul 2022 15:12:54 +0200 Subject: [PATCH 066/151] Formatted PopupRoot ControlTheme. --- .../Controls/PopupRoot.xaml | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/PopupRoot.xaml b/src/Avalonia.Themes.Fluent/Controls/PopupRoot.xaml index f57ecd2013..5a924830b1 100644 --- a/src/Avalonia.Themes.Fluent/Controls/PopupRoot.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/PopupRoot.xaml @@ -1,28 +1,28 @@  - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + From 3dc70c0725526db91f6f9c765ba212d363ef1514 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 8 Jul 2022 15:17:15 +0200 Subject: [PATCH 067/151] Checked ProgressBar ControlTheme. --- .../Controls/ProgressBar.xaml | 348 +++++++++--------- 1 file changed, 176 insertions(+), 172 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/ProgressBar.xaml b/src/Avalonia.Themes.Fluent/Controls/ProgressBar.xaml index 3c7d22e13f..6bf813385d 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ProgressBar.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ProgressBar.xaml @@ -2,178 +2,182 @@ xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:CompileBindings="True"> - - + + + - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From d284822731e941224c76f0bf9eda3fa1349f3470 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 8 Jul 2022 15:20:16 +0200 Subject: [PATCH 068/151] Tweaked RepeatButton ControlTheme. --- .../Controls/RepeatButton.xaml | 110 +++++++++--------- 1 file changed, 54 insertions(+), 56 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/RepeatButton.xaml b/src/Avalonia.Themes.Fluent/Controls/RepeatButton.xaml index 670212c4b1..5e789e7e10 100644 --- a/src/Avalonia.Themes.Fluent/Controls/RepeatButton.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/RepeatButton.xaml @@ -1,65 +1,63 @@  - - - - - - - - - 8,5,8,6 + + + + + + + + + - - - - - - - - - - - - - - - - + 8,5,8,6 - + + + + + + + + + + + + + + + + - + - + - + - - + + From 250b5b91ef8c5d4d8fefd66c14029ee0720fe0ec Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 8 Jul 2022 16:02:03 +0200 Subject: [PATCH 069/151] Tweaked Slider ControlTheme. --- .../Controls/Slider.xaml | 534 +++++++++--------- 1 file changed, 269 insertions(+), 265 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/Slider.xaml b/src/Avalonia.Themes.Fluent/Controls/Slider.xaml index c74878e8be..9378640d64 100644 --- a/src/Avalonia.Themes.Fluent/Controls/Slider.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/Slider.xaml @@ -1,57 +1,71 @@ - - - - - - - - - - - - - - - - 0,0,0,4 - 15 - 15 - 32 - 32 - 10 - 20 - 20 - 20 - 20 - + + + + + + + + + + + + + Error + + + + + + + + + + + + + + + 0,0,0,4 + 15 + 15 + 32 + 32 + 10 + 20 + 20 + 20 + 20 - + - - + + - - - - - - - - - + + + + + + + + + + @@ -65,225 +79,215 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - @@ -326,7 +330,7 @@ - @@ -344,18 +348,18 @@ - - + @@ -370,14 +374,14 @@ - From e5f70deab03fea424de8b06a98c4e50f23eecde6 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 8 Jul 2022 16:04:37 +0200 Subject: [PATCH 070/151] Formatted TabControl ControlTheme. --- .../Controls/TabControl.xaml | 111 +++++++++--------- 1 file changed, 54 insertions(+), 57 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/TabControl.xaml b/src/Avalonia.Themes.Fluent/Controls/TabControl.xaml index 1cc31f8e15..17d179e0cd 100644 --- a/src/Avalonia.Themes.Fluent/Controls/TabControl.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/TabControl.xaml @@ -1,63 +1,60 @@  - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - 0 0 0 2 + 0 0 0 2 - - - - - - - - - - - - - - + + + + + + + + + + + + + + - - - - + + + + From 27abe8200987ac1ab627549f3642d1f9353e9e18 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 8 Jul 2022 16:15:06 +0200 Subject: [PATCH 071/151] Twaked TabItem ControlTheme. --- .../Controls/TabItem.xaml | 230 +++++++++--------- 1 file changed, 113 insertions(+), 117 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/TabItem.xaml b/src/Avalonia.Themes.Fluent/Controls/TabItem.xaml index 110ffc2f48..e323be72e2 100644 --- a/src/Avalonia.Themes.Fluent/Controls/TabItem.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/TabItem.xaml @@ -2,132 +2,128 @@ xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:CompileBindings="True"> - - - - - - - - - - 48 - 24 - 2 + + + + + + + + + - - - - - - - - - - - - - - - - - - - + 48 + 24 + 2 - - - + + + + + + + + + + + + + + + + + + + - - - - + + + + - - + + - - + + - - + + - - + + - - + + - - - + + + - + - - - - + + + + From 1af445893abf5d66af80bedd05d932e8e0febe44 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 8 Jul 2022 16:20:33 +0200 Subject: [PATCH 072/151] Tweaked TabStrip/Item ControlTemplates. --- .../Controls/TabStrip.xaml | 62 +++--- .../Controls/TabStripItem.xaml | 180 +++++++++--------- 2 files changed, 116 insertions(+), 126 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/TabStrip.xaml b/src/Avalonia.Themes.Fluent/Controls/TabStrip.xaml index 15c3ba2f37..59cb7f6752 100644 --- a/src/Avalonia.Themes.Fluent/Controls/TabStrip.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/TabStrip.xaml @@ -1,34 +1,34 @@  - - - - Item 1 - Item 2 - Disabled - - - - - - - - - - - - - - - - - - + + + + Item 1 + Item 2 + Disabled + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/TabStripItem.xaml b/src/Avalonia.Themes.Fluent/Controls/TabStripItem.xaml index 5ccd430d33..fab8ba87aa 100644 --- a/src/Avalonia.Themes.Fluent/Controls/TabStripItem.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/TabStripItem.xaml @@ -2,107 +2,97 @@ xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:CompileBindings="True"> - - - - Leaf - Arch - - - - - 48 - 2 + + + + Leaf + Arch + Background + + + - - - - - - - - - - - - - - - - - - - + 48 + 2 - + + + + + + + + + + + + + + + + + + + - - - - + + + + - - - - + + - - + + - - + + - - + + - - - - - - + + + From c27f5e798e0bb1a15491c931abc3330fa3e17d4e Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 8 Jul 2022 16:24:51 +0200 Subject: [PATCH 073/151] Tweaked ToggleButton ControlTheme. --- src/Avalonia.Themes.Fluent/Controls/ToggleButton.xaml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/ToggleButton.xaml b/src/Avalonia.Themes.Fluent/Controls/ToggleButton.xaml index 617f3e9c45..36c0c50064 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ToggleButton.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ToggleButton.xaml @@ -45,10 +45,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + 0,0,0,6 + 6 + 6 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From e8091cfe80759251c7fe35f834c91fe606b8576c Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 8 Jul 2022 16:48:04 +0200 Subject: [PATCH 075/151] Tweaked ToolTip ControlTheme. --- .../Controls/ToolTip.xaml | 113 ++++++++---------- 1 file changed, 50 insertions(+), 63 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/ToolTip.xaml b/src/Avalonia.Themes.Fluent/Controls/ToolTip.xaml index 1070f3b68c..bc9dceb545 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ToolTip.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ToolTip.xaml @@ -2,70 +2,57 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:sys="clr-namespace:System;assembly=netstandard" x:CompileBindings="True"> - - - - Hover Here - - - - - - ToolTip - A control which pops up a hint when a control is hovered - - - ToolTip bottom placement - - - + + + + Text Content + Very long text content which should exceed the maximum with of the tooltip and wrap. + + + Multi-line + Control Content + + + + + - 320 + 320 - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - + + From 433325e7463376756de70912a9fac103c958d54c Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 8 Jul 2022 16:48:44 +0200 Subject: [PATCH 076/151] Formatted TransitioningContentControl ControlTheme. --- .../Controls/TransitioningContentControl.xaml | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/TransitioningContentControl.xaml b/src/Avalonia.Themes.Fluent/Controls/TransitioningContentControl.xaml index f28dc66a5f..f1971fe236 100644 --- a/src/Avalonia.Themes.Fluent/Controls/TransitioningContentControl.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/TransitioningContentControl.xaml @@ -1,19 +1,19 @@  - - - - - - - + + + + + + + From 1bbff64d1fffd557c031e5fc4e78b003fdfe2c35 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 8 Jul 2022 16:50:39 +0200 Subject: [PATCH 077/151] Added preview to TreeView ControlTheme. --- .../Controls/TreeView.xaml | 66 +++++++++++-------- 1 file changed, 39 insertions(+), 27 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/TreeView.xaml b/src/Avalonia.Themes.Fluent/Controls/TreeView.xaml index 4fcec79f25..c340599000 100644 --- a/src/Avalonia.Themes.Fluent/Controls/TreeView.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/TreeView.xaml @@ -1,31 +1,43 @@ + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + From 42da6cde7c1d237161768939c032bbf8a54b3f53 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 8 Jul 2022 16:56:49 +0200 Subject: [PATCH 078/151] Formatted Window ControlTheme. --- .../Controls/Window.xaml | 54 +++++++++---------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/Window.xaml b/src/Avalonia.Themes.Fluent/Controls/Window.xaml index d78fd76a37..35cc81663f 100644 --- a/src/Avalonia.Themes.Fluent/Controls/Window.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/Window.xaml @@ -1,30 +1,30 @@ - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + From fa65cae7d82d6d3769d9a74f46a0033ea9852967 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 8 Jul 2022 16:58:05 +0200 Subject: [PATCH 079/151] Formatted WindowNotificationManager ControlTheme. --- .../Controls/WindowNotificationManager.xaml | 105 +++++++++--------- 1 file changed, 52 insertions(+), 53 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/WindowNotificationManager.xaml b/src/Avalonia.Themes.Fluent/Controls/WindowNotificationManager.xaml index d5d5114c1b..77f49941f0 100644 --- a/src/Avalonia.Themes.Fluent/Controls/WindowNotificationManager.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/WindowNotificationManager.xaml @@ -1,58 +1,57 @@ - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - + - + - + - - + + From 5229cf57336c771ecda45dfea704340a7d1141e3 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 8 Jul 2022 17:00:12 +0200 Subject: [PATCH 080/151] Formatted Calendar ControlTheme. --- .../Controls/Calendar.xaml | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/Calendar.xaml b/src/Avalonia.Themes.Fluent/Controls/Calendar.xaml index 3434f9b6e5..8d54cb93ca 100644 --- a/src/Avalonia.Themes.Fluent/Controls/Calendar.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/Calendar.xaml @@ -5,36 +5,36 @@ // All other rights reserved. --> - - + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + From 7af2eb9a29e0ea5a2b8b08b3a00b72518b34b6ef Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 8 Jul 2022 17:08:40 +0200 Subject: [PATCH 081/151] Formatted CalendarButton ControlTheme. --- .../Controls/CalendarButton.xaml | 178 +++++++++--------- 1 file changed, 93 insertions(+), 85 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/CalendarButton.xaml b/src/Avalonia.Themes.Fluent/Controls/CalendarButton.xaml index 0381022910..d1aee7ee9a 100644 --- a/src/Avalonia.Themes.Fluent/Controls/CalendarButton.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/CalendarButton.xaml @@ -4,106 +4,114 @@ // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. --> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + - + + + + + - - - - - - + - - - - + + - - - + - - - - + - - - - + + + + + + From f66d9aca7a7c034ecdc6ec1565176790677c1c39 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 8 Jul 2022 17:10:48 +0200 Subject: [PATCH 082/151] Formatted CalendarDayButton ControlTheme. --- .../Controls/CalendarDayButton.xaml | 172 +++++++++--------- 1 file changed, 90 insertions(+), 82 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/CalendarDayButton.xaml b/src/Avalonia.Themes.Fluent/Controls/CalendarDayButton.xaml index 9f476d51bc..dd2692cc5a 100644 --- a/src/Avalonia.Themes.Fluent/Controls/CalendarDayButton.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/CalendarDayButton.xaml @@ -6,99 +6,107 @@ --> - - - - - - - - - - - - - - - - - - - - + + + + + + + - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - + - - - - - - + + - - - - + - - + - - + + + + + + + From 1e642fb3218d990cc8bbbe84e22c8f8d336ca52b Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 8 Jul 2022 17:23:24 +0200 Subject: [PATCH 083/151] Tweaked CalendarItem ControlTheme. --- .../Controls/CalendarItem.xaml | 276 +++++++++--------- 1 file changed, 143 insertions(+), 133 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/CalendarItem.xaml b/src/Avalonia.Themes.Fluent/Controls/CalendarItem.xaml index f8fec58ef7..6914a0db08 100644 --- a/src/Avalonia.Themes.Fluent/Controls/CalendarItem.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/CalendarItem.xaml @@ -6,145 +6,155 @@ --> - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + 2000-01-01 + 2000-01-05 + + + + + + - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - + + + + + + + + From f6e8dda027e159820f744a03ebff8cca80d1c998 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 8 Jul 2022 17:26:17 +0200 Subject: [PATCH 084/151] Formatted CalendarDatePicker ControlTheme. --- .../Controls/CalendarDatePicker.xaml | 328 +++++++++--------- 1 file changed, 163 insertions(+), 165 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml b/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml index eefd9e9b51..54f6a200b8 100644 --- a/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml @@ -2,179 +2,177 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:sys="clr-namespace:System;assembly=netstandard" x:CompileBindings="True"> - - - - - - - 12 - 32 + + + + + - - - - - - - - - - - 12 + 32 + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - + - - + - - - + + + + + + + + + + + From 83140fe427b8ddcb691b7dc063e5af965de40180 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 9 Jul 2022 00:18:28 +0200 Subject: [PATCH 089/151] Reset button animations correctly. Otherwise they keep on animating way after the interaction. --- src/Avalonia.Themes.Fluent/Controls/Button.xaml | 1 + src/Avalonia.Themes.Fluent/Controls/RepeatButton.xaml | 1 + src/Avalonia.Themes.Fluent/Controls/ToggleButton.xaml | 1 + 3 files changed, 3 insertions(+) diff --git a/src/Avalonia.Themes.Fluent/Controls/Button.xaml b/src/Avalonia.Themes.Fluent/Controls/Button.xaml index 7ac5c4351a..7828fd52ed 100644 --- a/src/Avalonia.Themes.Fluent/Controls/Button.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/Button.xaml @@ -20,6 +20,7 @@ + diff --git a/src/Avalonia.Themes.Fluent/Controls/RepeatButton.xaml b/src/Avalonia.Themes.Fluent/Controls/RepeatButton.xaml index 5e789e7e10..a54187104b 100644 --- a/src/Avalonia.Themes.Fluent/Controls/RepeatButton.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/RepeatButton.xaml @@ -23,6 +23,7 @@ + + From 0d5b10ec3aa041b21d5cd4a785b37201dec03f7a Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 9 Jul 2022 00:25:04 +0200 Subject: [PATCH 090/151] Tweaked DatePicker ControlTheme. --- .../Controls/DatePicker.xaml | 629 +++++++++--------- 1 file changed, 317 insertions(+), 312 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/DatePicker.xaml b/src/Avalonia.Themes.Fluent/Controls/DatePicker.xaml index a7093c1341..0537feb60b 100644 --- a/src/Avalonia.Themes.Fluent/Controls/DatePicker.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/DatePicker.xaml @@ -9,344 +9,349 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:sys="clr-namespace:System;assembly=netstandard" x:CompileBindings="True"> - - - - - + + + + + - 0,0,0,4 - 40 - 40 - 41 - 296 - 456 - 0,3,0,6 - 9,3,0,6 - 0,3,0,6 - 9,3,0,6 - 1 + 0,0,0,4 + 40 + 40 + 41 + 296 + 456 + 0,3,0,6 + 9,3,0,6 + 0,3,0,6 + 9,3,0,6 + 1 - - - - - - - - + + + + + + + - - + + + - - - - - - - - - + + + + + + + + + - - - - - + + + + + - + - - + - - - - - - - - - - + + - - - - - - - - - - + + + + + + + + + + - - - - - - - - - - - - - - + + + + + + + + + + - + + + + + + + + + + + + + + - - - + - - - - + + + - + + + + - - - + - + + + - + - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + - + - - - + + + From c4541e27db84734a963dc785060ca023a2606283 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 9 Jul 2022 00:48:00 +0200 Subject: [PATCH 091/151] Ported ManagedFileChooser to ControlTheme. --- .../Controls/FluentControls.xaml | 4 +- .../Controls/ManagedFileChooser.xaml | 156 +++++++++--------- 2 files changed, 82 insertions(+), 78 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml b/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml index 0b5f6a4a72..1c936ba097 100644 --- a/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml @@ -59,6 +59,8 @@ + + @@ -72,7 +74,5 @@ - - diff --git a/src/Avalonia.Themes.Fluent/Controls/ManagedFileChooser.xaml b/src/Avalonia.Themes.Fluent/Controls/ManagedFileChooser.xaml index dc454e4fdb..55f4893057 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ManagedFileChooser.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ManagedFileChooser.xaml @@ -1,8 +1,14 @@ - - + - + + + + - - - - - - - - - - + + + - - - + + + From da264ac4d0587b7cd6bd36cf0181caafacd83642 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 9 Jul 2022 02:20:14 +0200 Subject: [PATCH 092/151] WIP: Menu/Item. Something strange is happening when you open the menu a 3rd time. --- .../Controls/FluentControls.xaml | 2 +- src/Avalonia.Themes.Fluent/Controls/Menu.xaml | 82 ++++- .../Controls/MenuItem.xaml | 303 +++++++----------- 3 files changed, 191 insertions(+), 196 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml b/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml index 1c936ba097..6d51dac17f 100644 --- a/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml @@ -26,6 +26,7 @@ + @@ -67,7 +68,6 @@ - diff --git a/src/Avalonia.Themes.Fluent/Controls/Menu.xaml b/src/Avalonia.Themes.Fluent/Controls/Menu.xaml index 11d21b6b23..3e0e81f870 100644 --- a/src/Avalonia.Themes.Fluent/Controls/Menu.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/Menu.xaml @@ -3,17 +3,95 @@ - - + + + 32 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - + + + + + + + 😊 + - - - - + + + + - - + + + + + - - + + - + - - - -4 - 0,0,12,0 - 24,0,0,0 - M 1,0 10,10 l -9,10 -1,-1 L 8,10 -0,1 Z - + + -4 + 0,0,12,0 + 24,0,0,0 + M 1,0 10,10 l -9,10 -1,-1 L 8,10 -0,1 Z - - - - - - - - - - - - - - - - - + - - - - - + + + + + - - - - + + + + + - + + + + + - + - + - - + + + From d2e4d58f178113d500ca5dffc06734be751ce403 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 9 Jul 2022 15:17:15 +0200 Subject: [PATCH 093/151] Formatted NativeMenuBar ControlTheme. --- src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml b/src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml index 753c03992a..4f51ebfa7f 100644 --- a/src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml @@ -25,5 +25,5 @@ - + From d902c3eb0ca5a7559d1e7843abd93f567fe065bc Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 9 Jul 2022 15:53:41 +0200 Subject: [PATCH 094/151] Port ScrollViewer to ControlTheme. Including splitting out the menu scroller into a separate xaml file for separate preview. --- .../Controls/FluentControls.xaml | 1 + .../Controls/MenuItem.xaml | 28 ++- .../Controls/MenuScrollViewer.xaml | 98 +++++++++++ .../Controls/ScrollViewer.xaml | 164 +++++++++--------- 4 files changed, 204 insertions(+), 87 deletions(-) create mode 100644 src/Avalonia.Themes.Fluent/Controls/MenuScrollViewer.xaml diff --git a/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml b/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml index 6d51dac17f..3e4361465c 100644 --- a/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml @@ -25,6 +25,7 @@ + diff --git a/src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml b/src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml index 3ec65c4936..8f77d1bc1b 100644 --- a/src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml @@ -5,8 +5,8 @@ x:DataType="MenuItem" x:CompileBindings="True"> - - + + @@ -22,8 +22,26 @@ - - + + + + + + + + + + + + + + + + + + + + @@ -113,7 +131,7 @@ MinHeight="{DynamicResource MenuFlyoutThemeMinHeight}" HorizontalAlignment="Stretch" CornerRadius="{DynamicResource OverlayCornerRadius}"> - + + + + + + Item 1 + Item 2 + Item 3 + Item 4 + Item 5 + Item 6 + Item 7 + Item 8 + Item 9 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/ScrollViewer.xaml b/src/Avalonia.Themes.Fluent/Controls/ScrollViewer.xaml index a6e02d2769..55adb54d9b 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ScrollViewer.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ScrollViewer.xaml @@ -1,87 +1,87 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + Item 1 + Item 2 + Item 3 + Item 4 + Item 5 + Item 6 + Item 7 + Item 8 + Item 9 + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + From 1d15d1b51343137a24171002b6f07ac98517b1af Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sun, 10 Jul 2022 00:04:29 +0200 Subject: [PATCH 095/151] Ported SplitView to ControlTheme. --- .../Controls/FluentControls.xaml | 2 +- .../Controls/SplitView.xaml | 444 +++++++++--------- 2 files changed, 231 insertions(+), 215 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml b/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml index 3e4361465c..fc89d7c21e 100644 --- a/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml @@ -39,6 +39,7 @@ + @@ -72,7 +73,6 @@ - diff --git a/src/Avalonia.Themes.Fluent/Controls/SplitView.xaml b/src/Avalonia.Themes.Fluent/Controls/SplitView.xaml index 6b9b94852f..9feb6c7fc6 100644 --- a/src/Avalonia.Themes.Fluent/Controls/SplitView.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/SplitView.xaml @@ -1,4 +1,4 @@ - @@ -15,219 +15,235 @@ - - 320 - 48 - 00:00:00.2 - 00:00:00.1 - 0.1,0.9,0.2,1.0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From b1038f115b8abdc48ae884ec531a3ae385731010 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sun, 10 Jul 2022 01:31:23 +0200 Subject: [PATCH 096/151] Ported TreeViewItem to ControlTheme. --- .../Controls/FluentControls.xaml | 4 +- .../Controls/TreeViewItem.xaml | 178 +++++++++--------- 2 files changed, 87 insertions(+), 95 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml b/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml index fc89d7c21e..58c33bf26f 100644 --- a/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml @@ -51,6 +51,7 @@ + @@ -68,10 +69,7 @@ - - - diff --git a/src/Avalonia.Themes.Fluent/Controls/TreeViewItem.xaml b/src/Avalonia.Themes.Fluent/Controls/TreeViewItem.xaml index 6510832eb3..83abe7848c 100644 --- a/src/Avalonia.Themes.Fluent/Controls/TreeViewItem.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/TreeViewItem.xaml @@ -1,4 +1,4 @@ - @@ -6,32 +6,29 @@ - - + + - + - + - - 16 - 12 - 12, 0, 12, 0 - M 1,0 10,10 l -9,10 -1,-1 L 8,10 -0,1 Z - M0,1 L10,10 20,1 19,0 10,8 1,0 Z - - + 16 + 12 + 12, 0, 12, 0 + M 1,0 10,10 l -9,10 -1,-1 L 8,10 -0,1 Z + M0,1 L10,10 20,1 19,0 10,8 1,0 Z + - - + + + @@ -78,7 +80,7 @@ @@ -98,83 +100,75 @@ - - - - - - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - - - + + + - + + + + From 7fcd73836940e0bf3654fe4e0a86493eb75e8386 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sun, 10 Jul 2022 02:12:56 +0200 Subject: [PATCH 097/151] Tweaked TitleBar ControlTheme. --- src/Avalonia.Controls/Chrome/TitleBar.cs | 15 ++++++------ .../Controls/TitleBar.xaml | 24 +++++++++++-------- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/src/Avalonia.Controls/Chrome/TitleBar.cs b/src/Avalonia.Controls/Chrome/TitleBar.cs index b152a31587..428bf8f9b0 100644 --- a/src/Avalonia.Controls/Chrome/TitleBar.cs +++ b/src/Avalonia.Controls/Chrome/TitleBar.cs @@ -35,7 +35,8 @@ namespace Avalonia.Controls.Chrome } } - IsVisible = window.PlatformImpl?.NeedsManagedDecorations ?? false; + if (!Design.IsDesignMode) + IsVisible = window.PlatformImpl?.NeedsManagedDecorations ?? false; } } @@ -44,13 +45,13 @@ namespace Avalonia.Controls.Chrome base.OnApplyTemplate(e); _captionButtons?.Detach(); - + _captionButtons = e.NameScope.Get("PART_CaptionButtons"); - if (VisualRoot is Window window) + if (VisualRoot is Window window && !Design.IsDesignMode) { - _captionButtons?.Attach(window); - + _captionButtons?.Attach(window); + UpdateSize(window); } } @@ -59,7 +60,7 @@ namespace Avalonia.Controls.Chrome { base.OnAttachedToVisualTree(e); - if (VisualRoot is Window window) + if (VisualRoot is Window window && !Design.IsDesignMode) { _disposables = new CompositeDisposable { @@ -90,7 +91,7 @@ namespace Avalonia.Controls.Chrome base.OnDetachedFromVisualTree(e); _disposables?.Dispose(); - + _captionButtons?.Detach(); _captionButtons = null; } diff --git a/src/Avalonia.Themes.Fluent/Controls/TitleBar.xaml b/src/Avalonia.Themes.Fluent/Controls/TitleBar.xaml index 2b97df020f..5730b19e78 100644 --- a/src/Avalonia.Themes.Fluent/Controls/TitleBar.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/TitleBar.xaml @@ -1,8 +1,8 @@  - - + + @@ -13,11 +13,19 @@ - - + + - - + + @@ -27,10 +35,6 @@ - - From 5864452d75a596bdd7bd0d0cdd6de8f3805d8159 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sun, 10 Jul 2022 15:19:40 +0200 Subject: [PATCH 098/151] Undo changed to TitleBar. It was causing the title bar to be shown in all designer previews. --- src/Avalonia.Controls/Chrome/TitleBar.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Controls/Chrome/TitleBar.cs b/src/Avalonia.Controls/Chrome/TitleBar.cs index 428bf8f9b0..1bf13111a9 100644 --- a/src/Avalonia.Controls/Chrome/TitleBar.cs +++ b/src/Avalonia.Controls/Chrome/TitleBar.cs @@ -35,8 +35,7 @@ namespace Avalonia.Controls.Chrome } } - if (!Design.IsDesignMode) - IsVisible = window.PlatformImpl?.NeedsManagedDecorations ?? false; + IsVisible = window.PlatformImpl?.NeedsManagedDecorations ?? false; } } @@ -48,7 +47,7 @@ namespace Avalonia.Controls.Chrome _captionButtons = e.NameScope.Get("PART_CaptionButtons"); - if (VisualRoot is Window window && !Design.IsDesignMode) + if (VisualRoot is Window window) { _captionButtons?.Attach(window); @@ -60,7 +59,7 @@ namespace Avalonia.Controls.Chrome { base.OnAttachedToVisualTree(e); - if (VisualRoot is Window window && !Design.IsDesignMode) + if (VisualRoot is Window window) { _disposables = new CompositeDisposable { From 07cce63b8bd4b009bd384e727caaab16d339bc4a Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sun, 10 Jul 2022 16:57:55 +0200 Subject: [PATCH 099/151] Ported TimePicker to ControlTheme. And refactored shared date/time picker components into a separate file. --- .../Controls/DatePicker.xaml | 207 +++----- .../Controls/DateTimePickerShared.xaml | 116 +++++ .../Controls/FluentControls.xaml | 3 +- .../Controls/TimePicker.xaml | 465 ++++++++++++------ 4 files changed, 501 insertions(+), 290 deletions(-) create mode 100644 src/Avalonia.Themes.Fluent/Controls/DateTimePickerShared.xaml diff --git a/src/Avalonia.Themes.Fluent/Controls/DatePicker.xaml b/src/Avalonia.Themes.Fluent/Controls/DatePicker.xaml index 0537feb60b..0441415128 100644 --- a/src/Avalonia.Themes.Fluent/Controls/DatePicker.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/DatePicker.xaml @@ -10,8 +10,21 @@ xmlns:sys="clr-namespace:System;assembly=netstandard" x:CompileBindings="True"> - - + + + + + + + + + + Error + + + + + @@ -27,105 +40,45 @@ 9,3,0,6 1 - - - - - - - - - - - - - - - - - - + - - - - + - - - + - - - - - - - - - - - - - - - - - - - - - - - + - + @@ -148,7 +101,9 @@ HorizontalAlignment="Stretch" VerticalAlignment="Top"/> - - diff --git a/src/Avalonia.Themes.Fluent/Controls/DateTimePickerShared.xaml b/src/Avalonia.Themes.Fluent/Controls/DateTimePickerShared.xaml new file mode 100644 index 0000000000..77271a149f --- /dev/null +++ b/src/Avalonia.Themes.Fluent/Controls/DateTimePickerShared.xaml @@ -0,0 +1,116 @@ + + + + + + + Standard Item + Month Item + Button + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml b/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml index 58c33bf26f..262007082f 100644 --- a/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml @@ -59,7 +59,9 @@ + + @@ -72,6 +74,5 @@ - diff --git a/src/Avalonia.Themes.Fluent/Controls/TimePicker.xaml b/src/Avalonia.Themes.Fluent/Controls/TimePicker.xaml index dc09032a36..b5f177b985 100644 --- a/src/Avalonia.Themes.Fluent/Controls/TimePicker.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/TimePicker.xaml @@ -1,163 +1,320 @@ - - - - - 40 - 1 - 1 - 0,0,0,4 - 40 - 41 - 242 - 456 - 0,3,0,6 - 0,3,0,6 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + Error + + + + + + + + + 40 + 1 + 1 + 0,0,0,4 + 40 + 41 + 242 + 456 + 0,3,0,6 + 0,3,0,6 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + + + + + + + + + + - - - - - + + From 2dda677fc8099a484806be21574e1a5bbbf721b8 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sun, 10 Jul 2022 22:26:28 +0200 Subject: [PATCH 100/151] Ported SplitButton to ControlTheme. Not happy with the Tag hack but no better way to add additional states to a control currently. --- .../Controls/FluentControls.xaml | 2 +- .../Controls/SplitButton.xaml | 388 ++++++++---------- 2 files changed, 173 insertions(+), 217 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml b/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml index 262007082f..bc2352d5d0 100644 --- a/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml @@ -39,6 +39,7 @@ + @@ -72,7 +73,6 @@ - diff --git a/src/Avalonia.Themes.Fluent/Controls/SplitButton.xaml b/src/Avalonia.Themes.Fluent/Controls/SplitButton.xaml index fb0460d9a1..9a93b2625b 100644 --- a/src/Avalonia.Themes.Fluent/Controls/SplitButton.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/SplitButton.xaml @@ -1,36 +1,120 @@ - - - + + Hello - + + + Hello + + + Disabled + + + Hello + + + Hello + - - 32 - 32 - 1 - 32 + 32 + 32 + 1 + 32 - - - - + + + - + + + + + + + + + + + + + + + + + + + + @@ -40,211 +124,83 @@ - - - - - - - - - - - @@ -147,9 +155,6 @@ - diff --git a/src/Avalonia.Themes.Fluent/Controls/DatePicker.xaml b/src/Avalonia.Themes.Fluent/Controls/DatePicker.xaml index 0441415128..bc47f3892e 100644 --- a/src/Avalonia.Themes.Fluent/Controls/DatePicker.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/DatePicker.xaml @@ -280,10 +280,6 @@ - diff --git a/src/Avalonia.Themes.Fluent/Controls/DateTimePickerShared.xaml b/src/Avalonia.Themes.Fluent/Controls/DateTimePickerShared.xaml index 77271a149f..be664b375d 100644 --- a/src/Avalonia.Themes.Fluent/Controls/DateTimePickerShared.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/DateTimePickerShared.xaml @@ -15,8 +15,8 @@ Standard Item Month Item Button - - + + @@ -90,6 +90,7 @@ +