From 747bfc46955ae831b5b09208b7d67f377e6a16c0 Mon Sep 17 00:00:00 2001 From: rabbitism Date: Fri, 6 Sep 2024 23:28:05 +0800 Subject: [PATCH 1/3] feat: make nav menu item focusable. --- src/Ursa/Controls/NavMenu/NavMenuItem.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Ursa/Controls/NavMenu/NavMenuItem.cs b/src/Ursa/Controls/NavMenu/NavMenuItem.cs index e90e1cd..8e921f0 100644 --- a/src/Ursa/Controls/NavMenu/NavMenuItem.cs +++ b/src/Ursa/Controls/NavMenu/NavMenuItem.cs @@ -142,6 +142,7 @@ public class NavMenuItem: HeaderedItemsControl { // SelectableMixin.Attach(IsSelectedProperty); PressedMixin.Attach(); + FocusableProperty.OverrideDefaultValue(true); LevelProperty.Changed.AddClassHandler((item, args) => item.OnLevelChange(args)); IsHighlightedProperty.AffectsPseudoClass(PC_Highlighted); IsHorizontalCollapsedProperty.AffectsPseudoClass(PC_HorizontalCollapsed); From c678d82d6b75ca9f11643bca665b61e42422d38f Mon Sep 17 00:00:00 2001 From: rabbitism Date: Fri, 6 Sep 2024 23:31:34 +0800 Subject: [PATCH 2/3] misc: re-organize code. --- src/Ursa/Controls/NavMenu/NavMenu.cs | 186 +++++++------ src/Ursa/Controls/NavMenu/NavMenuItem.cs | 251 ++++++++---------- .../Controls/NavMenu/OverflowStackPanel.cs | 13 +- 3 files changed, 207 insertions(+), 243 deletions(-) diff --git a/src/Ursa/Controls/NavMenu/NavMenu.cs b/src/Ursa/Controls/NavMenu/NavMenu.cs index 7c1e866..d2194a1 100644 --- a/src/Ursa/Controls/NavMenu/NavMenu.cs +++ b/src/Ursa/Controls/NavMenu/NavMenu.cs @@ -13,22 +13,77 @@ using Irihi.Avalonia.Shared.Helpers; namespace Ursa.Controls; [PseudoClasses(PC_HorizontalCollapsed)] -public class NavMenu: ItemsControl +public class NavMenu : ItemsControl { public const string PC_HorizontalCollapsed = ":horizontal-collapsed"; - + public static readonly StyledProperty SelectedItemProperty = AvaloniaProperty.Register( nameof(SelectedItem), defaultBindingMode: BindingMode.TwoWay); + public static readonly StyledProperty IconBindingProperty = + AvaloniaProperty.Register( + nameof(IconBinding)); + + public static readonly StyledProperty HeaderBindingProperty = + AvaloniaProperty.Register( + nameof(HeaderBinding)); + + public static readonly StyledProperty SubMenuBindingProperty = + AvaloniaProperty.Register( + nameof(SubMenuBinding)); + + public static readonly StyledProperty CommandBindingProperty = + AvaloniaProperty.Register( + nameof(CommandBinding)); + + public static readonly StyledProperty HeaderTemplateProperty = + AvaloniaProperty.Register( + nameof(HeaderTemplate)); + + public static readonly StyledProperty IconTemplateProperty = + AvaloniaProperty.Register( + nameof(IconTemplate)); + + public static readonly StyledProperty SubMenuIndentProperty = AvaloniaProperty.Register( + nameof(SubMenuIndent)); + + public static readonly StyledProperty IsHorizontalCollapsedProperty = + AvaloniaProperty.Register( + nameof(IsHorizontalCollapsed)); + + public static readonly StyledProperty HeaderProperty = + HeaderedContentControl.HeaderProperty.AddOwner(); + + public static readonly StyledProperty FooterProperty = AvaloniaProperty.Register( + nameof(Footer)); + + public static readonly StyledProperty ExpandWidthProperty = AvaloniaProperty.Register( + nameof(ExpandWidth), double.NaN); + + public static readonly StyledProperty CollapseWidthProperty = AvaloniaProperty.Register( + nameof(CollapseWidth), double.NaN); + + public static readonly AttachedProperty CanToggleProperty = + AvaloniaProperty.RegisterAttached("CanToggle"); + + public static readonly RoutedEvent SelectionChangedEvent = + RoutedEvent.Register(nameof(SelectionChanged), RoutingStrategies.Bubble); + + private bool _updateFromUI; + + static NavMenu() + { + SelectedItemProperty.Changed.AddClassHandler((o, e) => o.OnSelectedItemChange(e)); + IsHorizontalCollapsedProperty.AffectsPseudoClass(PC_HorizontalCollapsed); + CanToggleProperty.Changed.AddClassHandler(OnInputRegisteredAsToggle); + } + public object? SelectedItem { get => GetValue(SelectedItemProperty); set => SetValue(SelectedItemProperty, value); } - public static readonly StyledProperty IconBindingProperty = AvaloniaProperty.Register( - nameof(IconBinding)); - [AssignBinding] [InheritDataTypeFromItems(nameof(ItemsSource))] public IBinding? IconBinding @@ -37,9 +92,6 @@ public class NavMenu: ItemsControl set => SetValue(IconBindingProperty, value); } - public static readonly StyledProperty HeaderBindingProperty = AvaloniaProperty.Register( - nameof(HeaderBinding)); - [AssignBinding] [InheritDataTypeFromItems(nameof(ItemsSource))] public IBinding? HeaderBinding @@ -48,9 +100,6 @@ public class NavMenu: ItemsControl set => SetValue(HeaderBindingProperty, value); } - public static readonly StyledProperty SubMenuBindingProperty = AvaloniaProperty.Register( - nameof(SubMenuBinding)); - [AssignBinding] [InheritDataTypeFromItems(nameof(ItemsSource))] public IBinding? SubMenuBinding @@ -59,9 +108,6 @@ public class NavMenu: ItemsControl set => SetValue(SubMenuBindingProperty, value); } - public static readonly StyledProperty CommandBindingProperty = AvaloniaProperty.Register( - nameof(CommandBinding)); - [AssignBinding] [InheritDataTypeFromItems(nameof(ItemsSource))] public IBinding? CommandBinding @@ -70,11 +116,8 @@ public class NavMenu: ItemsControl set => SetValue(CommandBindingProperty, value); } - public static readonly StyledProperty HeaderTemplateProperty = AvaloniaProperty.Register( - nameof(HeaderTemplate)); - /// - /// Header Template is used for MenuItem headers, not menu header. + /// Header Template is used for MenuItem headers, not menu header. /// public IDataTemplate? HeaderTemplate { @@ -82,126 +125,98 @@ public class NavMenu: ItemsControl set => SetValue(HeaderTemplateProperty, value); } - public static readonly StyledProperty IconTemplateProperty = AvaloniaProperty.Register( - nameof(IconTemplate)); - public IDataTemplate? IconTemplate { get => GetValue(IconTemplateProperty); set => SetValue(IconTemplateProperty, value); } - public static readonly StyledProperty SubMenuIndentProperty = AvaloniaProperty.Register( - nameof(SubMenuIndent)); - public double SubMenuIndent { get => GetValue(SubMenuIndentProperty); set => SetValue(SubMenuIndentProperty, value); } - public static readonly StyledProperty IsHorizontalCollapsedProperty = AvaloniaProperty.Register( - nameof(IsHorizontalCollapsed)); - public bool IsHorizontalCollapsed { get => GetValue(IsHorizontalCollapsedProperty); set => SetValue(IsHorizontalCollapsedProperty, value); } - public static readonly StyledProperty HeaderProperty = - HeaderedContentControl.HeaderProperty.AddOwner(); - public object? Header { get => GetValue(HeaderProperty); set => SetValue(HeaderProperty, value); } - public static readonly StyledProperty FooterProperty = AvaloniaProperty.Register( - nameof(Footer)); - public object? Footer { get => GetValue(FooterProperty); set => SetValue(FooterProperty, value); } - public static readonly StyledProperty ExpandWidthProperty = AvaloniaProperty.Register( - nameof(ExpandWidth), double.NaN); - public double ExpandWidth { get => GetValue(ExpandWidthProperty); set => SetValue(ExpandWidthProperty, value); } - public static readonly StyledProperty CollapseWidthProperty = AvaloniaProperty.Register( - nameof(CollapseWidth), double.NaN); - public double CollapseWidth { get => GetValue(CollapseWidthProperty); set => SetValue(CollapseWidthProperty, value); } - public static readonly AttachedProperty CanToggleProperty = - AvaloniaProperty.RegisterAttached("CanToggle"); + public static void SetCanToggle(InputElement obj, bool value) + { + obj.SetValue(CanToggleProperty, value); + } + + public static bool GetCanToggle(InputElement obj) + { + return obj.GetValue(CanToggleProperty); + } - public static void SetCanToggle(InputElement obj, bool value) => obj.SetValue(CanToggleProperty, value); - public static bool GetCanToggle(InputElement obj) => obj.GetValue(CanToggleProperty); - - public static readonly RoutedEvent SelectionChangedEvent = RoutedEvent.Register(nameof(SelectionChanged), RoutingStrategies.Bubble); - public event EventHandler? SelectionChanged { add => AddHandler(SelectionChangedEvent, value); remove => RemoveHandler(SelectionChangedEvent, value); } - - static NavMenu() - { - SelectedItemProperty.Changed.AddClassHandler((o, e) => o.OnSelectedItemChange(e)); - IsHorizontalCollapsedProperty.AffectsPseudoClass(PC_HorizontalCollapsed); - CanToggleProperty.Changed.AddClassHandler(OnInputRegisteredAsToggle); - } private static void OnInputRegisteredAsToggle(InputElement input, AvaloniaPropertyChangedEventArgs e) { if (e.NewValue.Value) - { input.AddHandler(PointerPressedEvent, OnElementToggle); - } else - { input.RemoveHandler(PointerPressedEvent, OnElementToggle); - } } private static void OnElementToggle(object? sender, RoutedEventArgs args) { if (sender is not InputElement input) return; var nav = input.FindLogicalAncestorOfType(); - if(nav is null) return; - bool collapsed = nav.IsHorizontalCollapsed; + if (nav is null) return; + var collapsed = nav.IsHorizontalCollapsed; nav.IsHorizontalCollapsed = !collapsed; } /// - /// this implementation only works in the case that only leaf menu item is allowed to select. It will be changed if we introduce parent level selection in the future. + /// this implementation only works in the case that only leaf menu item is allowed to select. It will be changed if we + /// introduce parent level selection in the future. /// /// private void OnSelectedItemChange(AvaloniaPropertyChangedEventArgs args) { - SelectionChangedEventArgs a = new SelectionChangedEventArgs( + var a = new SelectionChangedEventArgs( SelectionChangedEvent, - new [] { args.OldValue.Value }, - new [] { args.NewValue.Value }); + new[] { args.OldValue.Value }, + new[] { args.NewValue.Value }); if (_updateFromUI) { RaiseEvent(a); return; } + var newValue = args.NewValue.Value; if (newValue is null) { @@ -209,20 +224,17 @@ public class NavMenu: ItemsControl RaiseEvent(a); return; } + var leaves = GetLeafMenus(); - bool found = false; + var found = false; foreach (var leaf in leaves) - { if (leaf == newValue || leaf.DataContext == newValue) { leaf.SelectItem(leaf); found = true; } - } - if (!found) - { - ClearAll(); - } + + if (!found) ClearAll(); RaiseEvent(a); } @@ -236,57 +248,37 @@ public class NavMenu: ItemsControl return new NavMenuItem(); } - private bool _updateFromUI; - internal void SelectItem(NavMenuItem item, NavMenuItem parent) { _updateFromUI = true; foreach (var child in LogicalChildren) { - if (Equals(child, parent)) - { - continue; - } - if (child is NavMenuItem navMenuItem) - { - navMenuItem.ClearSelection(); - } + if (Equals(child, parent)) continue; + if (child is NavMenuItem navMenuItem) navMenuItem.ClearSelection(); } - if (item.DataContext is not null && item.DataContext != this.DataContext) - { + + if (item.DataContext is not null && item.DataContext != DataContext) SelectedItem = item.DataContext; - } else - { SelectedItem = item; - } item.BringIntoView(); _updateFromUI = false; } private IEnumerable GetLeafMenus() { - foreach (var child in LogicalChildren) - { + foreach (var child in LogicalChildren) if (child is NavMenuItem item) { var leafs = item.GetLeafMenus(); - foreach (var leaf in leafs) - { - yield return leaf; - } + foreach (var leaf in leafs) yield return leaf; } - } } private void ClearAll() { - foreach (var child in LogicalChildren) - { + foreach (var child in LogicalChildren) if (child is NavMenuItem item) - { item.ClearSelection(); - } - } } } \ No newline at end of file diff --git a/src/Ursa/Controls/NavMenu/NavMenuItem.cs b/src/Ursa/Controls/NavMenu/NavMenuItem.cs index 8e921f0..5112ef6 100644 --- a/src/Ursa/Controls/NavMenu/NavMenuItem.cs +++ b/src/Ursa/Controls/NavMenu/NavMenuItem.cs @@ -16,127 +16,66 @@ using Irihi.Avalonia.Shared.Helpers; namespace Ursa.Controls; /// -/// Navigation Menu Item +/// Navigation Menu Item /// [PseudoClasses(PC_Highlighted, PC_HorizontalCollapsed, PC_VerticalCollapsed, PC_FirstLevel, PC_Selector)] -public class NavMenuItem: HeaderedItemsControl +public class NavMenuItem : HeaderedItemsControl { public const string PC_Highlighted = ":highlighted"; public const string PC_FirstLevel = ":first-level"; public const string PC_HorizontalCollapsed = ":horizontal-collapsed"; public const string PC_VerticalCollapsed = ":vertical-collapsed"; public const string PC_Selector = ":selector"; - - private NavMenu? _rootMenu; - private Popup? _popup; - private Panel? _overflowPanel; - - private static readonly Point InvalidPoint = new (double.NaN, double.NaN); - private Point _pointerDownPoint = InvalidPoint; - + + private static readonly Point InvalidPoint = new(double.NaN, double.NaN); + public static readonly StyledProperty IconProperty = AvaloniaProperty.Register( nameof(Icon)); - public object? Icon - { - get => GetValue(IconProperty); - set => SetValue(IconProperty, value); - } - - public static readonly StyledProperty IconTemplateProperty = AvaloniaProperty.Register( - nameof(IconTemplate)); - - public IDataTemplate? IconTemplate - { - get => GetValue(IconTemplateProperty); - set => SetValue(IconTemplateProperty, value); - } + public static readonly StyledProperty IconTemplateProperty = + AvaloniaProperty.Register( + nameof(IconTemplate)); public static readonly StyledProperty CommandProperty = Button.CommandProperty.AddOwner(); - public ICommand? Command - { - get => GetValue(CommandProperty); - set => SetValue(CommandProperty, value); - } - public static readonly StyledProperty CommandParameterProperty = Button.CommandParameterProperty.AddOwner(); - public object? CommandParameter - { - get => GetValue(CommandParameterProperty); - set => SetValue(CommandParameterProperty, value); - } - public static readonly StyledProperty IsSelectedProperty = SelectingItemsControl.IsSelectedProperty.AddOwner(); - public bool IsSelected - { - get => GetValue(IsSelectedProperty); - set => SetValue(IsSelectedProperty, value); - } - - public static readonly RoutedEvent IsSelectedChangedEvent = RoutedEvent.Register("IsSelectedChanged", RoutingStrategies.Bubble); - - private bool _isHighlighted; + public static readonly RoutedEvent IsSelectedChangedEvent = + RoutedEvent.Register("IsSelectedChanged", RoutingStrategies.Bubble); public static readonly DirectProperty IsHighlightedProperty = AvaloniaProperty.RegisterDirect( nameof(IsHighlighted), o => o.IsHighlighted, (o, v) => o.IsHighlighted = v, defaultBindingMode: BindingMode.TwoWay); - public bool IsHighlighted - { - get => _isHighlighted; - private set => SetAndRaise(IsHighlightedProperty, ref _isHighlighted, value); - } - public static readonly StyledProperty IsHorizontalCollapsedProperty = NavMenu.IsHorizontalCollapsedProperty.AddOwner(); - public bool IsHorizontalCollapsed - { - get => GetValue(IsHorizontalCollapsedProperty); - set => SetValue(IsHorizontalCollapsedProperty, value); - } - - public static readonly StyledProperty IsVerticalCollapsedProperty = AvaloniaProperty.Register( - nameof(IsVerticalCollapsed)); - - public bool IsVerticalCollapsed - { - get => GetValue(IsVerticalCollapsedProperty); - set => SetValue(IsVerticalCollapsedProperty, value); - } + public static readonly StyledProperty IsVerticalCollapsedProperty = + AvaloniaProperty.Register( + nameof(IsVerticalCollapsed)); public static readonly StyledProperty SubMenuIndentProperty = NavMenu.SubMenuIndentProperty.AddOwner(); - public double SubMenuIndent - { - get => GetValue(SubMenuIndentProperty); - set => SetValue(SubMenuIndentProperty, value); - } - - internal static readonly DirectProperty LevelProperty = AvaloniaProperty.RegisterDirect( - nameof(Level), o => o.Level, (o, v) => o.Level = v); - private int _level; - public int Level - { - get => _level; - set => SetAndRaise(LevelProperty, ref _level, value); - } + internal static readonly DirectProperty LevelProperty = + AvaloniaProperty.RegisterDirect( + nameof(Level), o => o.Level, (o, v) => o.Level = v); public static readonly StyledProperty IsSeparatorProperty = AvaloniaProperty.Register( nameof(IsSeparator)); - public bool IsSeparator - { - get => GetValue(IsSeparatorProperty); - set => SetValue(IsSeparatorProperty, value); - } + private bool _isHighlighted; + private int _level; + private Panel? _overflowPanel; + private Point _pointerDownPoint = InvalidPoint; + private Popup? _popup; + + private NavMenu? _rootMenu; static NavMenuItem() { @@ -152,21 +91,81 @@ public class NavMenuItem: HeaderedItemsControl item.OnIsHorizontalCollapsedChanged(args)); } + public object? Icon + { + get => GetValue(IconProperty); + set => SetValue(IconProperty, value); + } + + public IDataTemplate? IconTemplate + { + get => GetValue(IconTemplateProperty); + set => SetValue(IconTemplateProperty, value); + } + + public ICommand? Command + { + get => GetValue(CommandProperty); + set => SetValue(CommandProperty, value); + } + + public object? CommandParameter + { + get => GetValue(CommandParameterProperty); + set => SetValue(CommandParameterProperty, value); + } + + public bool IsSelected + { + get => GetValue(IsSelectedProperty); + set => SetValue(IsSelectedProperty, value); + } + + public bool IsHighlighted + { + get => _isHighlighted; + private set => SetAndRaise(IsHighlightedProperty, ref _isHighlighted, value); + } + + public bool IsHorizontalCollapsed + { + get => GetValue(IsHorizontalCollapsedProperty); + set => SetValue(IsHorizontalCollapsedProperty, value); + } + + public bool IsVerticalCollapsed + { + get => GetValue(IsVerticalCollapsedProperty); + set => SetValue(IsVerticalCollapsedProperty, value); + } + + public double SubMenuIndent + { + get => GetValue(SubMenuIndentProperty); + set => SetValue(SubMenuIndentProperty, value); + } + + public int Level + { + get => _level; + set => SetAndRaise(LevelProperty, ref _level, value); + } + + public bool IsSeparator + { + get => GetValue(IsSeparatorProperty); + set => SetValue(IsSeparatorProperty, value); + } + private void OnIsHorizontalCollapsedChanged(AvaloniaPropertyChangedEventArgs args) { if (args.NewValue.Value) { - if (this.ItemsPanelRoot is OverflowStackPanel s) - { - s.MoveChildrenToOverflowPanel(); - } + if (ItemsPanelRoot is OverflowStackPanel s) s.MoveChildrenToOverflowPanel(); } else { - if (this.ItemsPanelRoot is OverflowStackPanel s) - { - s.MoveChildrenToMainPanel(); - } + if (ItemsPanelRoot is OverflowStackPanel s) s.MoveChildrenToMainPanel(); } } @@ -184,7 +183,7 @@ public class NavMenuItem: HeaderedItemsControl { return new NavMenuItem(); } - + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) { base.OnAttachedToVisualTree(e); @@ -194,7 +193,7 @@ public class NavMenuItem: HeaderedItemsControl protected override void OnApplyTemplate(TemplateAppliedEventArgs e) { base.OnApplyTemplate(e); - SetCurrentValue(LevelProperty,CalculateDistanceFromLogicalParent(this)); + SetCurrentValue(LevelProperty, CalculateDistanceFromLogicalParent(this)); _popup = e.NameScope.Find("PART_Popup"); _overflowPanel = e.NameScope.Find("PART_OverflowPanel"); if (_rootMenu is not null) @@ -213,11 +212,8 @@ public class NavMenuItem: HeaderedItemsControl protected override void OnLoaded(RoutedEventArgs e) { base.OnLoaded(e); - var root = this.ItemsPanelRoot; - if (root is OverflowStackPanel stack) - { - stack.OverflowPanel = _overflowPanel; - } + var root = ItemsPanelRoot; + if (root is OverflowStackPanel stack) stack.OverflowPanel = _overflowPanel; } protected override void OnPointerPressed(PointerPressedEventArgs e) @@ -227,6 +223,7 @@ public class NavMenuItem: HeaderedItemsControl e.Handled = true; return; } + base.OnPointerPressed(e); if (e.Handled) return; @@ -235,7 +232,7 @@ public class NavMenuItem: HeaderedItemsControl or PointerUpdateKind.RightButtonPressed)) return; if (p.Pointer.Type == PointerType.Mouse) { - if (this.ItemCount == 0) + if (ItemCount == 0) { SelectItem(this); Command?.Execute(CommandParameter); @@ -243,7 +240,7 @@ public class NavMenuItem: HeaderedItemsControl } else { - if (!IsHorizontalCollapsed) + if (!IsHorizontalCollapsed) { SetCurrentValue(IsVerticalCollapsedProperty, !IsVerticalCollapsed); e.Handled = true; @@ -252,13 +249,9 @@ public class NavMenuItem: HeaderedItemsControl { if (_popup is null || e.Source is not Visual v || _popup.IsInsidePopup(v)) return; if (_popup.IsOpen) - { _popup.Close(); - } else - { _popup.Open(); - } } } } @@ -276,7 +269,7 @@ public class NavMenuItem: HeaderedItemsControl { var point = e.GetCurrentPoint(this); if (!new Rect(Bounds.Size).ContainsExclusive(point.Position) || e.Pointer.Type != PointerType.Touch) return; - if (this.ItemCount == 0) + if (ItemCount == 0) { SelectItem(this); Command?.Execute(CommandParameter); @@ -284,7 +277,7 @@ public class NavMenuItem: HeaderedItemsControl } else { - if (!IsHorizontalCollapsed) + if (!IsHorizontalCollapsed) { SetCurrentValue(IsVerticalCollapsedProperty, !IsVerticalCollapsed); e.Handled = true; @@ -293,13 +286,9 @@ public class NavMenuItem: HeaderedItemsControl { if (_popup is null || e.Source is not Visual v || _popup.IsInsidePopup(v)) return; if (_popup.IsOpen) - { _popup.Close(); - } else - { _popup.Open(); - } } } } @@ -317,26 +306,21 @@ public class NavMenuItem: HeaderedItemsControl SetCurrentValue(IsSelectedProperty, false); SetCurrentValue(IsHighlightedProperty, true); } - if (this.Parent is NavMenuItem menuItem) + + if (Parent is NavMenuItem menuItem) { menuItem.SelectItem(item); var items = menuItem.LogicalChildren.OfType(); foreach (var child in items) - { if (child != this) - { child.ClearSelection(); - } - } } - else if (this.Parent is NavMenu menu) + else if (Parent is NavMenu menu) { menu.SelectItem(item, this); } - if(_popup is not null) - { - _popup.Close(); - } + + if (_popup is not null) _popup.Close(); } internal void ClearSelection() @@ -344,12 +328,8 @@ public class NavMenuItem: HeaderedItemsControl SetCurrentValue(IsHighlightedProperty, false); SetCurrentValue(IsSelectedProperty, false); foreach (var child in LogicalChildren) - { if (child is NavMenuItem item) - { item.ClearSelection(); - } - } } private NavMenu? GetRootMenu() @@ -357,40 +337,33 @@ public class NavMenuItem: HeaderedItemsControl var root = this.FindAncestorOfType() ?? this.FindLogicalAncestorOfType(); return root; } - + private static int CalculateDistanceFromLogicalParent(ILogical? logical, int @default = -1) where T : class { var result = 0; while (logical != null && !(logical is T)) { - if (logical is NavMenuItem) - { - result++; - } + if (logical is NavMenuItem) result++; logical = logical.LogicalParent; } return logical != null ? result : @default; } - + internal IEnumerable GetLeafMenus() { - if (this.ItemCount == 0) + if (ItemCount == 0) { yield return this; yield break; } - foreach (var child in LogicalChildren) - { + + foreach (var child in LogicalChildren) if (child is NavMenuItem item) { var items = item.GetLeafMenus(); - foreach (var i in items) - { - yield return i; - } + foreach (var i in items) yield return i; } - } } } \ No newline at end of file diff --git a/src/Ursa/Controls/NavMenu/OverflowStackPanel.cs b/src/Ursa/Controls/NavMenu/OverflowStackPanel.cs index bfa7ad1..48d5163 100644 --- a/src/Ursa/Controls/NavMenu/OverflowStackPanel.cs +++ b/src/Ursa/Controls/NavMenu/OverflowStackPanel.cs @@ -2,29 +2,28 @@ using Avalonia.Controls; namespace Ursa.Controls; -public class OverflowStackPanel: StackPanel +public class OverflowStackPanel : StackPanel { public Panel? OverflowPanel { get; set; } + public void MoveChildrenToOverflowPanel() { - var children = this.Children.ToList(); + var children = Children.ToList(); foreach (var child in children) { Children.Remove(child); OverflowPanel?.Children.Add(child); } } - + public void MoveChildrenToMainPanel() { - var children = this.OverflowPanel?.Children.ToList(); - if (children != null && children.Count > 0) - { + var children = OverflowPanel?.Children.ToList(); + if (children is not null && children.Count > 0) foreach (var child in children) { OverflowPanel?.Children.Remove(child); Children.Add(child); } - } } } \ No newline at end of file From fb6ecc72acf1b96d86cbf7d5dcd245bb4f0003d3 Mon Sep 17 00:00:00 2001 From: rabbitism Date: Sat, 7 Sep 2024 00:35:51 +0800 Subject: [PATCH 3/3] feat: make sure initialize selection is handled visually. --- src/Ursa/Controls/NavMenu/NavMenu.cs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/Ursa/Controls/NavMenu/NavMenu.cs b/src/Ursa/Controls/NavMenu/NavMenu.cs index d2194a1..893a22e 100644 --- a/src/Ursa/Controls/NavMenu/NavMenu.cs +++ b/src/Ursa/Controls/NavMenu/NavMenu.cs @@ -200,6 +200,12 @@ public class NavMenu : ItemsControl nav.IsHorizontalCollapsed = !collapsed; } + protected override void OnLoaded(RoutedEventArgs e) + { + base.OnLoaded(e); + TryToSelectItem(SelectedItem); + } + /// /// this implementation only works in the case that only leaf menu item is allowed to select. It will be changed if we /// introduce parent level selection in the future. @@ -224,18 +230,24 @@ public class NavMenu : ItemsControl RaiseEvent(a); return; } - + var found = TryToSelectItem(newValue); + if (!found) ClearAll(); + RaiseEvent(a); + } + + private bool TryToSelectItem(object? item) + { + if (item is null) return false; var leaves = GetLeafMenus(); var found = false; foreach (var leaf in leaves) - if (leaf == newValue || leaf.DataContext == newValue) + if (leaf == item || leaf.DataContext == item) { leaf.SelectItem(leaf); found = true; } - if (!found) ClearAll(); - RaiseEvent(a); + return found; } protected override bool NeedsContainerOverride(object? item, int index, out object? recycleKey)