From 79b79bbc707f4172ffe7d92d84211cf4a6195d58 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 15 Dec 2023 15:15:00 +0100 Subject: [PATCH 01/33] Use ShowNoActivate in Win32NativeControlHost. (#13966) This prevents a `NativeControlHost` sealing focus on win32. --- src/Windows/Avalonia.Win32/Win32NativeControlHost.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Windows/Avalonia.Win32/Win32NativeControlHost.cs b/src/Windows/Avalonia.Win32/Win32NativeControlHost.cs index 620cd64e79..a04e52f410 100644 --- a/src/Windows/Avalonia.Win32/Win32NativeControlHost.cs +++ b/src/Windows/Avalonia.Win32/Win32NativeControlHost.cs @@ -137,7 +137,7 @@ namespace Avalonia.Win32 _holder = holder; _child = child; UnmanagedMethods.SetParent(child.Handle, _holder.Handle); - UnmanagedMethods.ShowWindow(child.Handle, UnmanagedMethods.ShowWindowCommand.Show); + UnmanagedMethods.ShowWindow(child.Handle, UnmanagedMethods.ShowWindowCommand.ShowNoActivate); } [MemberNotNull(nameof(_holder))] From 2ba8ac8a738d7081e795d6c28511bd5517138664 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 15 Dec 2023 19:15:56 +0100 Subject: [PATCH 02/33] Don't share style instances with Or selector. (#13969) * Added failing test for #13910. * Don't share style instances with Or selector. Fixes #13910. --- src/Avalonia.Base/Styling/ControlTheme.cs | 2 +- src/Avalonia.Base/Styling/Style.cs | 2 +- src/Avalonia.Base/Styling/StyleBase.cs | 10 +++-- .../Styling/StyleTests.cs | 39 +++++++++++++++++++ 4 files changed, 48 insertions(+), 5 deletions(-) diff --git a/src/Avalonia.Base/Styling/ControlTheme.cs b/src/Avalonia.Base/Styling/ControlTheme.cs index 75a3beb907..6cd09e1808 100644 --- a/src/Avalonia.Base/Styling/ControlTheme.cs +++ b/src/Avalonia.Base/Styling/ControlTheme.cs @@ -48,7 +48,7 @@ namespace Avalonia.Styling if (HasSettersOrAnimations && TargetType.IsAssignableFrom(StyledElement.GetStyleKey(target))) { - Attach(target, null, type); + Attach(target, null, type, true); return SelectorMatchResult.AlwaysThisType; } diff --git a/src/Avalonia.Base/Styling/Style.cs b/src/Avalonia.Base/Styling/Style.cs index a5d89392e9..44ffc22e91 100644 --- a/src/Avalonia.Base/Styling/Style.cs +++ b/src/Avalonia.Base/Styling/Style.cs @@ -74,7 +74,7 @@ namespace Avalonia.Styling if (match.IsMatch) { - Attach(target, match.Activator, type); + Attach(target, match.Activator, type, Selector is not OrSelector); } result = match.Result; diff --git a/src/Avalonia.Base/Styling/StyleBase.cs b/src/Avalonia.Base/Styling/StyleBase.cs index 318e8d6890..bc3457cb48 100644 --- a/src/Avalonia.Base/Styling/StyleBase.cs +++ b/src/Avalonia.Base/Styling/StyleBase.cs @@ -92,20 +92,24 @@ namespace Avalonia.Styling return false; } - internal ValueFrame Attach(StyledElement target, IStyleActivator? activator, FrameType type) + internal ValueFrame Attach( + StyledElement target, + IStyleActivator? activator, + FrameType type, + bool canShareInstance) { if (target is not AvaloniaObject ao) throw new InvalidOperationException("Styles can only be applied to AvaloniaObjects."); StyleInstance instance; - if (_sharedInstance is not null) + if (_sharedInstance is not null && canShareInstance) { instance = _sharedInstance; } else { - var canShareInstance = activator is null; + canShareInstance &= activator is null; instance = new StyleInstance(this, activator, type); diff --git a/tests/Avalonia.Base.UnitTests/Styling/StyleTests.cs b/tests/Avalonia.Base.UnitTests/Styling/StyleTests.cs index 52bee42d9d..b44078caeb 100644 --- a/tests/Avalonia.Base.UnitTests/Styling/StyleTests.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/StyleTests.cs @@ -1029,6 +1029,28 @@ namespace Avalonia.Base.UnitTests.Styling Assert.Equal(Brushes.Blue, border.Background); } + [Fact] + public void Should_Not_Share_Instance_When_Or_Selector_Is_Present() + { + // Issue #13910 + Style style = new Style(x => Selectors.Or(x.OfType(), x.OfType().Class("bar"))) + { + Setters = + { + new Setter(Class1.FooProperty, "Foo"), + }, + }; + + var target1 = new Class1 { Classes = { "foo" } }; + var target2 = new Class2(); + + StyleHelpers.TryAttach(style, target1); + StyleHelpers.TryAttach(style, target2); + + Assert.Equal("Foo", target1.Foo); + Assert.Equal("foodefault", target2.Foo); + } + private class Class1 : Control { public static readonly StyledProperty FooProperty = @@ -1063,5 +1085,22 @@ namespace Avalonia.Base.UnitTests.Styling throw new NotImplementedException(); } } + + private class Class2 : Control + { + public static readonly StyledProperty FooProperty = + Class1.FooProperty.AddOwner(); + + public string Foo + { + get { return GetValue(FooProperty); } + set { SetValue(FooProperty, value); } + } + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + } + } } } From 645ca8555fc676d821f7dc06779da334e75ccd82 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 15 Dec 2023 21:26:26 +0100 Subject: [PATCH 03/33] Don't override Placement when Open called in code. (#13967) #6059 changed `ContextMenu.Open` to open the context menu at the bottom of the control when the `ContextRequestedEventArgs` doesn't contain a position, however it also had the side-effect of preventing the `Placement` property from being respected when opening a `ContextMenu` from code. Change the private `Open` method to accept a `PlacementMode` instead of a boolean flag, and only pass `Bottom` here when `ContextRequestedEventArgs` has no requested position. Fixes #12504 --- src/Avalonia.Controls/ContextMenu.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Avalonia.Controls/ContextMenu.cs b/src/Avalonia.Controls/ContextMenu.cs index b3dabd8ab9..7bfe283d40 100644 --- a/src/Avalonia.Controls/ContextMenu.cs +++ b/src/Avalonia.Controls/ContextMenu.cs @@ -265,7 +265,7 @@ namespace Avalonia.Controls } control ??= _attachedControls![0]; - Open(control, PlacementTarget ?? control, false); + Open(control, PlacementTarget ?? control, Placement); } /// @@ -303,7 +303,7 @@ namespace Avalonia.Controls remove => _popupHostChangedHandler -= value; } - private void Open(Control control, Control placementTarget, bool requestedByPointer) + private void Open(Control control, Control placementTarget, PlacementMode placement) { if (IsOpen) { @@ -330,9 +330,7 @@ namespace Avalonia.Controls ((ISetLogicalParent)_popup).SetParent(control); } - _popup.Placement = !requestedByPointer && Placement == PlacementMode.Pointer - ? PlacementMode.Bottom - : Placement; + _popup.Placement = placement; //Position of the line below is really important. //All styles are being applied only when control has logical parent. @@ -420,7 +418,10 @@ namespace Avalonia.Controls && !contextMenu.CancelOpening()) { var requestedByPointer = e.TryGetPosition(null, out _); - contextMenu.Open(control, e.Source as Control ?? control, requestedByPointer); + contextMenu.Open( + control, + e.Source as Control ?? control, + requestedByPointer ? contextMenu.Placement : PlacementMode.Bottom); e.Handled = true; } } From bfe1a7b4dc4b396ad5ca3fc8cf6d5b0cb321a72e Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Fri, 15 Dec 2023 21:35:21 +0000 Subject: [PATCH 04/33] Replace fluent menu item icon presenter with a control theme (#13964) --- .../Controls/MenuItem.xaml | 33 +++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml b/src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml index 3cf6261683..1052a20ee9 100644 --- a/src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml @@ -81,15 +81,11 @@ SharedSizeGroup="MenuItemChevron" /> - - - + -