From 705152104030ee53c365f09f97fefbbeb0487490 Mon Sep 17 00:00:00 2001 From: rabbitism Date: Tue, 13 Feb 2024 22:51:38 +0800 Subject: [PATCH] feat: resolve highlight binding issue. --- demo/Ursa.Demo/Pages/NavMenuDemo.axaml | 18 ++-- demo/Ursa.Demo/Pages/NavMenuDemo.axaml.cs | 12 +++ src/Ursa.Themes.Semi/Controls/NavMenu.axaml | 82 +++++++++++-------- src/Ursa/Controls/NavMenu/NavMenuItem.cs | 67 ++++++++++----- .../Controls/NavMenu/OverflowStackPanel.cs | 30 +++++++ 5 files changed, 145 insertions(+), 64 deletions(-) create mode 100644 src/Ursa/Controls/NavMenu/OverflowStackPanel.cs diff --git a/demo/Ursa.Demo/Pages/NavMenuDemo.axaml b/demo/Ursa.Demo/Pages/NavMenuDemo.axaml index 59ade47..5a944cf 100644 --- a/demo/Ursa.Demo/Pages/NavMenuDemo.axaml +++ b/demo/Ursa.Demo/Pages/NavMenuDemo.axaml @@ -13,6 +13,7 @@ mc:Ignorable="d"> + Collapse + + SubMenuBinding="{Binding Children}"> + Width="10" + Height="10" + Classes.Active="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=u:NavMenuItem}, Path=IsHighlighted, Mode=TwoWay}"> diff --git a/demo/Ursa.Demo/Pages/NavMenuDemo.axaml.cs b/demo/Ursa.Demo/Pages/NavMenuDemo.axaml.cs index 635ab59..eac27af 100644 --- a/demo/Ursa.Demo/Pages/NavMenuDemo.axaml.cs +++ b/demo/Ursa.Demo/Pages/NavMenuDemo.axaml.cs @@ -1,5 +1,8 @@ using Avalonia; using Avalonia.Controls; +using Avalonia.Controls.Shapes; +using Avalonia.Input; +using Avalonia.LogicalTree; using Avalonia.Markup.Xaml; using Ursa.Demo.ViewModels; @@ -12,4 +15,13 @@ public partial class NavMenuDemo : UserControl InitializeComponent(); this.DataContext = new NavMenuDemoViewModel(); } + + private void InputElement_OnPointerPressed(object? sender, PointerPressedEventArgs e) + { + if (sender is Rectangle c) + { + c.ApplyStyling(); + var ancestors = c.GetLogicalAncestors(); + } + } } \ No newline at end of file diff --git a/src/Ursa.Themes.Semi/Controls/NavMenu.axaml b/src/Ursa.Themes.Semi/Controls/NavMenu.axaml index 8236e96..0909f21 100644 --- a/src/Ursa.Themes.Semi/Controls/NavMenu.axaml +++ b/src/Ursa.Themes.Semi/Controls/NavMenu.axaml @@ -26,6 +26,13 @@ Grid.Row="0" Background="{TemplateBinding u:NavMenuItem.Background}" CornerRadius="4"> + + + + + + + @@ -66,54 +73,47 @@ - - - + Grid.IsSharedSizeScope="True" + ItemsPanel="{Binding ItemsPanel, RelativeSource={RelativeSource TemplatedParent}}" + RenderTransformOrigin="0.5,0" > + - + + - - + + + CornerRadius="4"> - - - - - - + CornerRadius="4"> + @@ -137,16 +137,19 @@ Width="8" Height="8" Margin="12,0" - RenderTransform="rotate(-90deg)" Data="{DynamicResource NavigationMenuItemExpandIconGlyph}" - Foreground="{DynamicResource NavigationMenuItemExpandIconForeground}"> - - + - + @@ -154,14 +157,20 @@ - + + + + + + - - + diff --git a/src/Ursa/Controls/NavMenu/NavMenuItem.cs b/src/Ursa/Controls/NavMenu/NavMenuItem.cs index 20cda43..870ccff 100644 --- a/src/Ursa/Controls/NavMenu/NavMenuItem.cs +++ b/src/Ursa/Controls/NavMenu/NavMenuItem.cs @@ -5,7 +5,9 @@ using Avalonia.Controls.Metadata; using Avalonia.Controls.Mixins; using Avalonia.Controls.Primitives; using Avalonia.Controls.Templates; +using Avalonia.Data; using Avalonia.Input; +using Avalonia.Interactivity; using Avalonia.LogicalTree; using Avalonia.VisualTree; using Irihi.Avalonia.Shared.Helpers; @@ -27,6 +29,8 @@ public class NavMenuItem: HeaderedSelectingItemsControl private NavMenu? _rootMenu; private Panel? _popupPanel; private Popup? _popup; + private Panel? _overflowPanel; + private Border? _border; public static readonly StyledProperty IconProperty = AvaloniaProperty.Register( nameof(Icon)); @@ -74,8 +78,10 @@ public class NavMenuItem: HeaderedSelectingItemsControl private bool _isHighlighted; - public static readonly DirectProperty IsHighlightedProperty = AvaloniaProperty.RegisterDirect( - nameof(IsHighlighted), o => o.IsHighlighted, (o, v) => o.IsHighlighted = v); + public static readonly DirectProperty IsHighlightedProperty = + AvaloniaProperty.RegisterDirect( + nameof(IsHighlighted), o => o.IsHighlighted, (o, v) => o.IsHighlighted = v, + defaultBindingMode: BindingMode.TwoWay); public bool IsHighlighted { @@ -129,6 +135,26 @@ public class NavMenuItem: HeaderedSelectingItemsControl PropertyToPseudoClassMixin.Attach(IsHorizontalCollapsedProperty, PC_HorizontalCollapsed); PropertyToPseudoClassMixin.Attach(IsVerticalCollapsedProperty, PC_VerticalCollapsed); PropertyToPseudoClassMixin.Attach(IsSelectedProperty, ":selected", IsSelectedChangedEvent); + IsHorizontalCollapsedProperty.Changed.AddClassHandler((item, args) => + item.OnIsHorizontalCollapsedChanged(args)); + } + + private void OnIsHorizontalCollapsedChanged(AvaloniaPropertyChangedEventArgs args) + { + if (args.NewValue.Value) + { + if (this.ItemsPanelRoot is OverflowStackPanel s) + { + s.MoveChildrenToOverflowPanel(); + } + } + else + { + if (this.ItemsPanelRoot is OverflowStackPanel s) + { + s.MoveChildrenToMainPanel(); + } + } } private void OnLevelChange(AvaloniaPropertyChangedEventArgs args) @@ -150,6 +176,15 @@ public class NavMenuItem: HeaderedSelectingItemsControl { base.OnAttachedToVisualTree(e); _rootMenu = GetRootMenu(); + } + + protected override void OnApplyTemplate(TemplateAppliedEventArgs e) + { + base.OnApplyTemplate(e); + SetCurrentValue(LevelProperty,CalculateDistanceFromLogicalParent(this)); + _popup = e.NameScope.Find("PART_Popup"); + _overflowPanel = e.NameScope.Find("PART_OverflowPanel"); + _border = e.NameScope.Find("PART_Border"); if (_rootMenu is not null) { if (_rootMenu.IconBinding is not null) @@ -172,20 +207,17 @@ public class NavMenuItem: HeaderedSelectingItemsControl this[!HeaderTemplateProperty] = _rootMenu[!NavMenu.HeaderTemplateProperty]; this[!SubMenuIndentProperty] = _rootMenu[!NavMenu.SubMenuIndentProperty]; this[!IsHorizontalCollapsedProperty] = _rootMenu[!NavMenu.IsHorizontalCollapsedProperty]; - if (this == _rootMenu.SelectedItem || this.DataContext == _rootMenu.SelectedItem) - { - SelectItem(this); - } } - } - protected override void OnApplyTemplate(TemplateAppliedEventArgs e) + protected override void OnLoaded(RoutedEventArgs e) { - var children = this.ItemsPanelRoot?.Children.ToList(); - base.OnApplyTemplate(e); - SetCurrentValue(LevelProperty,CalculateDistanceFromLogicalParent(this)); - _popup = e.NameScope.Find("PART_Popup"); + base.OnLoaded(e); + var root = this.ItemsPanelRoot; + if (root is OverflowStackPanel stack) + { + stack.OverflowPanel = _overflowPanel; + } } protected override void OnPointerPressed(PointerPressedEventArgs e) @@ -203,16 +235,9 @@ public class NavMenuItem: HeaderedSelectingItemsControl } else { - if (_popup is not null) + if (_border?.ContextFlyout is not null) { - if (_popup.IsOpen) - { - _popup.Close(); - } - else - { - _popup.Open(); - } + _border.ContextFlyout.ShowAt(this); } } } diff --git a/src/Ursa/Controls/NavMenu/OverflowStackPanel.cs b/src/Ursa/Controls/NavMenu/OverflowStackPanel.cs new file mode 100644 index 0000000..b55877a --- /dev/null +++ b/src/Ursa/Controls/NavMenu/OverflowStackPanel.cs @@ -0,0 +1,30 @@ +using Avalonia.Controls; + +namespace Ursa.Controls; + +public class OverflowStackPanel: StackPanel +{ + public Panel? OverflowPanel { get; set; } + public void MoveChildrenToOverflowPanel() + { + var children = this.Children.ToList(); + foreach (var child in children) + { + this.Children.Remove(child); + this.OverflowPanel?.Children.Add(child); + } + } + + public void MoveChildrenToMainPanel() + { + var children = this.OverflowPanel?.Children.ToList(); + if (children != null && children.Count > 0) + { + foreach (var child in children) + { + this.OverflowPanel?.Children.Remove(child); + this.Children.Add(child); + } + } + } +} \ No newline at end of file