diff --git a/samples/AtomUI.Demo.Desktop/ShowCase/MenuShowCase.axaml b/samples/AtomUI.Demo.Desktop/ShowCase/MenuShowCase.axaml
index 8b09b25..aaa647d 100644
--- a/samples/AtomUI.Demo.Desktop/ShowCase/MenuShowCase.axaml
+++ b/samples/AtomUI.Demo.Desktop/ShowCase/MenuShowCase.axaml
@@ -187,28 +187,28 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -220,7 +220,7 @@
-
+
diff --git a/src/AtomUI.Controls/GeneratedFiles/AtomUI.Generator/AtomUI.Generator.TokenResourceKeyGenerator/TokenResourceConst.g.cs b/src/AtomUI.Controls/GeneratedFiles/AtomUI.Generator/AtomUI.Generator.TokenResourceKeyGenerator/TokenResourceConst.g.cs
index 2a40933..2790c34 100644
--- a/src/AtomUI.Controls/GeneratedFiles/AtomUI.Generator/AtomUI.Generator.TokenResourceKeyGenerator/TokenResourceConst.g.cs
+++ b/src/AtomUI.Controls/GeneratedFiles/AtomUI.Generator/AtomUI.Generator.TokenResourceKeyGenerator/TokenResourceConst.g.cs
@@ -411,6 +411,7 @@ namespace AtomUI.Theme.Styling
public static readonly TokenResourceKey HorizontalItemHoverBg = new TokenResourceKey("NavMenu.HorizontalItemHoverBg", "AtomUI.Token");
public static readonly TokenResourceKey HorizontalItemBorderRadius = new TokenResourceKey("NavMenu.HorizontalItemBorderRadius", "AtomUI.Token");
public static readonly TokenResourceKey ItemHeight = new TokenResourceKey("NavMenu.ItemHeight", "AtomUI.Token");
+ public static readonly TokenResourceKey InlineItemIndentUnit = new TokenResourceKey("NavMenu.InlineItemIndentUnit", "AtomUI.Token");
public static readonly TokenResourceKey CollapsedWidth = new TokenResourceKey("NavMenu.CollapsedWidth", "AtomUI.Token");
public static readonly TokenResourceKey MenuPopupBg = new TokenResourceKey("NavMenu.MenuPopupBg", "AtomUI.Token");
public static readonly TokenResourceKey HorizontalLineHeight = new TokenResourceKey("NavMenu.HorizontalLineHeight", "AtomUI.Token");
diff --git a/src/AtomUI.Controls/NavMenu/BaseNavMenuItemTheme.cs b/src/AtomUI.Controls/NavMenu/BaseNavMenuItemTheme.cs
index 8a873b5..c7a4a49 100644
--- a/src/AtomUI.Controls/NavMenu/BaseNavMenuItemTheme.cs
+++ b/src/AtomUI.Controls/NavMenu/BaseNavMenuItemTheme.cs
@@ -38,14 +38,14 @@ internal class BaseNavMenuItemTheme : BaseControlTheme
BuildInstanceStyles(item);
// 仅仅为了把 Popup 包进来,没有其他什么作用
var layoutWrapper = new Panel();
- var header = BuildMenuItemContent(scope);
+ var header = BuildMenuItemContent(item, scope);
BuildExtraItem(layoutWrapper, scope);
layoutWrapper.Children.Add(header);
return layoutWrapper;
});
}
- protected virtual Control BuildMenuItemContent(INameScope scope)
+ protected virtual Control BuildMenuItemContent(NavMenuItem navMenuItem, INameScope scope)
{
var headerFrame = new Border
{
@@ -55,7 +55,13 @@ internal class BaseNavMenuItemTheme : BaseControlTheme
var transitions = new Transitions();
transitions.Add(AnimationUtils.CreateTransition(Border.BackgroundProperty));
headerFrame.Transitions = transitions;
+
+ headerFrame.Child = BuildMenuItemInfoGrid(navMenuItem, scope);
+ return headerFrame;
+ }
+ protected virtual Grid BuildMenuItemInfoGrid(NavMenuItem navMenuItem, INameScope scope)
+ {
var layout = new Grid
{
Name = MainContainerPart,
@@ -132,24 +138,21 @@ internal class BaseNavMenuItemTheme : BaseControlTheme
inputGestureText.RegisterInNameScope(scope);
- var menuIndicatorIcon = BuildMenuIndicatorIcon();
+ var menuIndicatorIcon = BuildMenuIndicatorIcon(scope);
Grid.SetColumn(menuIndicatorIcon, 3);
- menuIndicatorIcon.RegisterInNameScope(scope);
layout.Children.Add(iconPresenter);
layout.Children.Add(itemTextPresenter);
layout.Children.Add(inputGestureText);
layout.Children.Add(menuIndicatorIcon);
-
- headerFrame.Child = layout;
- return headerFrame;
+ return layout;
}
protected virtual void BuildExtraItem(Panel layout, INameScope scope)
{
}
- protected virtual Control BuildMenuIndicatorIcon()
+ protected virtual Control BuildMenuIndicatorIcon(INameScope scope)
{
var menuIndicatorIcon = new PathIcon
{
@@ -158,12 +161,23 @@ internal class BaseNavMenuItemTheme : BaseControlTheme
VerticalAlignment = VerticalAlignment.Center,
Kind = "RightOutlined"
};
-
+
+
+ CreateTemplateParentBinding(menuIndicatorIcon, PathIcon.IsEnabledProperty, NavMenuItem.IsEnabledProperty);
+
+ TokenResourceBinder.CreateGlobalTokenBinding(menuIndicatorIcon, PathIcon.NormalFilledBrushProperty,
+ NavMenuTokenResourceKey.ItemColor);
+ TokenResourceBinder.CreateGlobalTokenBinding(menuIndicatorIcon, PathIcon.SelectedFilledBrushProperty,
+ NavMenuTokenResourceKey.ItemSelectedColor);
+ TokenResourceBinder.CreateGlobalTokenBinding(menuIndicatorIcon, PathIcon.DisabledFilledBrushProperty,
+ NavMenuTokenResourceKey.ItemDisabledColor);
+
TokenResourceBinder.CreateGlobalTokenBinding(menuIndicatorIcon, Layoutable.WidthProperty,
NavMenuTokenResourceKey.MenuArrowSize);
TokenResourceBinder.CreateGlobalTokenBinding(menuIndicatorIcon, Layoutable.HeightProperty,
NavMenuTokenResourceKey.MenuArrowSize);
-
+ menuIndicatorIcon.RegisterInNameScope(scope);
+
return menuIndicatorIcon;
}
@@ -195,31 +209,63 @@ internal class BaseNavMenuItemTheme : BaseControlTheme
}
// Hover 状态
- var hoverStyle = new Style(selector => selector.Nesting().Class(StdPseudoClass.PointerOver));
+ var hoverStyle = new Style(selector => selector.Nesting().Template().Name(ItemDecoratorPart).Class(StdPseudoClass.PointerOver));
hoverStyle.Add(TemplatedControl.ForegroundProperty, NavMenuTokenResourceKey.ItemHoverColor);
- {
- var borderStyle = new Style(selector => selector.Nesting().Template().Name(ItemDecoratorPart));
- borderStyle.Add(Border.BackgroundProperty, NavMenuTokenResourceKey.ItemHoverBg);
- hoverStyle.Add(borderStyle);
- }
+ hoverStyle.Add(Border.BackgroundProperty, NavMenuTokenResourceKey.ItemHoverBg);
commonStyle.Add(hoverStyle);
+
+ // 选中分两种,一种是有子菜单一种是没有子菜单
+ var hasNoSubMenuStyle = new Style(selector => selector.Nesting().PropertyEquals(NavMenuItem.HasSubMenuProperty, false));
+ {
+ var selectedStyle = new Style(selector => selector.Nesting().Class(StdPseudoClass.Selected));
+ {
+ var itemDecoratorStyle = new Style(selector => selector.Nesting().Template().Name(ItemDecoratorPart));
+ itemDecoratorStyle.Add(Border.BackgroundProperty, NavMenuTokenResourceKey.ItemSelectedBg);
+ itemDecoratorStyle.Add(TemplatedControl.ForegroundProperty, NavMenuTokenResourceKey.ItemSelectedColor);
+ selectedStyle.Add(itemDecoratorStyle);
+ }
+ hasNoSubMenuStyle.Add(selectedStyle);
+ }
+ commonStyle.Add(hasNoSubMenuStyle);
+
+ var hasSubMenuStyle = new Style(selector => selector.Nesting().PropertyEquals(NavMenuItem.HasSubMenuProperty, true));
+ {
+ var selectedStyle = new Style(selector => selector.Nesting().Class(StdPseudoClass.Selected));
+ {
+ var itemDecoratorStyle = new Style(selector => selector.Nesting().Template().Name(ItemDecoratorPart));
+ itemDecoratorStyle.Add(TemplatedControl.ForegroundProperty, NavMenuTokenResourceKey.ItemSelectedColor);
+ selectedStyle.Add(itemDecoratorStyle);
+ }
+ hasSubMenuStyle.Add(selectedStyle);
+ }
+ commonStyle.Add(hasSubMenuStyle);
Add(commonStyle);
}
private void BuildMenuIndicatorStyle()
{
{
- var menuIndicatorStyle = new Style(selector => selector.Nesting().Template().Name(MenuIndicatorIconPart));
- menuIndicatorStyle.Add(Visual.IsVisibleProperty, true);
- Add(menuIndicatorStyle);
+ {
+ var menuIndicatorStyle = new Style(selector => selector.Nesting().Template().Name(MenuIndicatorIconPart));
+ menuIndicatorStyle.Add(Visual.IsVisibleProperty, true);
+ Add(menuIndicatorStyle);
+ }
+
+ var selectedStyle = new Style(selector => selector.Nesting().Class(StdPseudoClass.Selected));
+ {
+ var menuIndicatorStyle = new Style(selector => selector.Nesting().Template().Name(MenuIndicatorIconPart));
+ menuIndicatorStyle.Add(PathIcon.IconModeProperty, IconMode.Selected);
+ selectedStyle.Add(menuIndicatorStyle);
+ }
+ Add(selectedStyle);
}
- var hasSubMenuStyle = new Style(selector => selector.Nesting().Class(StdPseudoClass.Empty));
+ var hasNoSubMenuStyle = new Style(selector => selector.Nesting().PropertyEquals(NavMenuItem.HasSubMenuProperty, false));
{
var menuIndicatorStyle = new Style(selector => selector.Nesting().Template().Name(MenuIndicatorIconPart));
menuIndicatorStyle.Add(Visual.IsVisibleProperty, false);
- hasSubMenuStyle.Add(menuIndicatorStyle);
+ hasNoSubMenuStyle.Add(menuIndicatorStyle);
}
- Add(hasSubMenuStyle);
+ Add(hasNoSubMenuStyle);
}
private void BuildMenuIconStyle()
@@ -248,13 +294,15 @@ internal class BaseNavMenuItemTheme : BaseControlTheme
protected override void BuildInstanceStyles(Control control)
{
- var iconStyle = new Style(selector => selector.Name(ThemeConstants.ItemIconPart));
- iconStyle.Add(PathIcon.WidthProperty, NavMenuTokenResourceKey.ItemIconSize);
- iconStyle.Add(PathIcon.HeightProperty, NavMenuTokenResourceKey.ItemIconSize);
- iconStyle.Add(PathIcon.NormalFilledBrushProperty, GlobalTokenResourceKey.ColorText);
- iconStyle.Add(PathIcon.DisabledFilledBrushProperty, NavMenuTokenResourceKey.ItemDisabledColor);
- iconStyle.Add(PathIcon.SelectedFilledBrushProperty, GlobalTokenResourceKey.ColorPrimary);
- control.Styles.Add(iconStyle);
+ {
+ var iconStyle = new Style(selector => selector.Name(ThemeConstants.ItemIconPart));
+ iconStyle.Add(PathIcon.WidthProperty, NavMenuTokenResourceKey.ItemIconSize);
+ iconStyle.Add(PathIcon.HeightProperty, NavMenuTokenResourceKey.ItemIconSize);
+ iconStyle.Add(PathIcon.NormalFilledBrushProperty, GlobalTokenResourceKey.ColorText);
+ iconStyle.Add(PathIcon.DisabledFilledBrushProperty, NavMenuTokenResourceKey.ItemDisabledColor);
+ iconStyle.Add(PathIcon.SelectedFilledBrushProperty, GlobalTokenResourceKey.ColorPrimary);
+ control.Styles.Add(iconStyle);
+ }
var disabledIconStyle = new Style(selector => selector.OfType().Class(StdPseudoClass.Disabled));
disabledIconStyle.Add(PathIcon.IconModeProperty, IconMode.Disabled);
diff --git a/src/AtomUI.Controls/NavMenu/InlineNavMenuInteractionHandler.cs b/src/AtomUI.Controls/NavMenu/InlineNavMenuInteractionHandler.cs
index 8bd814e..7d2ba21 100644
--- a/src/AtomUI.Controls/NavMenu/InlineNavMenuInteractionHandler.cs
+++ b/src/AtomUI.Controls/NavMenu/InlineNavMenuInteractionHandler.cs
@@ -53,8 +53,9 @@ internal class InlineNavMenuInteractionHandler : INavMenuInteractionHandler
{
if (NavMenu is NavMenu navMenu)
{
- navMenu.UpdateSelectionFromItem(item);
+ navMenu.ClearSelection();
}
+ item?.SelectItemRecursively();
}
e.Handled = true;
@@ -66,7 +67,7 @@ internal class InlineNavMenuInteractionHandler : INavMenuInteractionHandler
item.Open();
}
- internal static INavMenuItem? GetMenuItemCore(StyledElement? item)
+ internal static NavMenuItem? GetMenuItemCore(StyledElement? item)
{
while (true)
{
@@ -75,7 +76,7 @@ internal class InlineNavMenuInteractionHandler : INavMenuInteractionHandler
return null;
}
- if (item is INavMenuItem menuItem)
+ if (item is NavMenuItem menuItem)
{
return menuItem;
}
diff --git a/src/AtomUI.Controls/NavMenu/InlineNavMenuItemTheme.cs b/src/AtomUI.Controls/NavMenu/InlineNavMenuItemTheme.cs
index 023b313..1ee4eab 100644
--- a/src/AtomUI.Controls/NavMenu/InlineNavMenuItemTheme.cs
+++ b/src/AtomUI.Controls/NavMenu/InlineNavMenuItemTheme.cs
@@ -4,6 +4,7 @@ using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Controls;
+using Avalonia.Controls.Converters;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Templates;
using Avalonia.Data;
@@ -30,10 +31,10 @@ internal class InlineNavMenuItemTheme : BaseNavMenuItemTheme
return ID;
}
- protected override Control BuildMenuIndicatorIcon()
+ protected override Control BuildMenuIndicatorIcon(INameScope scope)
{
- var indicatorIcon = base.BuildMenuIndicatorIcon();
- var menuIndicatorIconPresenter = new ContentPresenter()
+ var indicatorIcon = base.BuildMenuIndicatorIcon(scope);
+ var menuIndicatorIconPresenter = new Border()
{
Name = MenuIndicatorIconLayoutPart,
Transitions = new Transitions()
@@ -41,18 +42,20 @@ internal class InlineNavMenuItemTheme : BaseNavMenuItemTheme
AnimationUtils.CreateTransition(ContentPresenter.RenderTransformProperty)
}
};
- menuIndicatorIconPresenter.Content = indicatorIcon;
+ menuIndicatorIconPresenter.Child = indicatorIcon;
+ menuIndicatorIconPresenter.RegisterInNameScope(scope);
return menuIndicatorIconPresenter;
}
- protected override Control BuildMenuItemContent(INameScope scope)
+ protected override Control BuildMenuItemContent(NavMenuItem navMenuItem, INameScope scope)
{
var rootLayout = new StackPanel()
{
Orientation = Orientation.Vertical
};
- var headerContent = base.BuildMenuItemContent(scope);
-
+
+ var headerContent = base.BuildMenuItemContent(navMenuItem, scope);
+
TokenResourceBinder.CreateTokenBinding(headerContent, Control.MarginProperty, NavMenuTokenResourceKey.VerticalItemsPanelSpacing, BindingPriority.Template,
(v) =>
{
@@ -84,6 +87,21 @@ internal class InlineNavMenuItemTheme : BaseNavMenuItemTheme
return rootLayout;
}
+ protected override Grid BuildMenuItemInfoGrid(NavMenuItem navMenuItem, INameScope scope)
+ {
+ var infoGrid = base.BuildMenuItemInfoGrid(navMenuItem, scope);
+ var indentConverter = new MarginMultiplierConverter
+ {
+ Left = true,
+ Indent = navMenuItem.InlineItemIndentUnit
+ };
+ CreateTemplateParentBinding(infoGrid, Grid.MarginProperty,
+ NavMenuItem.LevelProperty, BindingMode.OneWay,
+ indentConverter);
+
+ return infoGrid;
+ }
+
protected override void BuildStyles()
{
base.BuildStyles();
@@ -100,7 +118,7 @@ internal class InlineNavMenuItemTheme : BaseNavMenuItemTheme
var menuIndicatorStyle = new Style(selector => selector.Nesting().Template().Name(MenuIndicatorIconLayoutPart));
var transformOptions = new TransformOperations.Builder(1);
transformOptions.AppendRotate(MathUtils.Deg2Rad(90));
- menuIndicatorStyle.Add(ContentPresenter.RenderTransformProperty, transformOptions.Build());
+ menuIndicatorStyle.Add(Border.RenderTransformProperty, transformOptions.Build());
menuIndicatorStyle.Add(Visual.IsVisibleProperty, true);
Add(menuIndicatorStyle);
}
@@ -109,7 +127,7 @@ internal class InlineNavMenuItemTheme : BaseNavMenuItemTheme
var menuIndicatorStyle = new Style(selector => selector.Nesting().Template().Name(MenuIndicatorIconLayoutPart));
var transformOptions = new TransformOperations.Builder(1);
transformOptions.AppendRotate(MathUtils.Deg2Rad(-90));
- menuIndicatorStyle.Add(ContentPresenter.RenderTransformProperty, transformOptions.Build());
+ menuIndicatorStyle.Add(Border.RenderTransformProperty, transformOptions.Build());
openSubMenuStyle.Add(menuIndicatorStyle);
}
Add(openSubMenuStyle);
@@ -121,40 +139,4 @@ internal class InlineNavMenuItemTheme : BaseNavMenuItemTheme
}
Add(emptySubMenuStyle);
}
-
- // private void BuildAnimationStyle()
- // {
- // var closeSubMenuStyle = new Style(selector => selector.Nesting().Not(selector.Nesting().Class(StdPseudoClass.Open)));
- // {
- // var layoutTransformStyle =
- // new Style(selector => selector.Nesting().Template().Name(ChildItemsLayoutTransformPart));
- // var slideDownInMotionConfig = MotionFactory.BuildSlideUpOutMotion(TimeSpan.FromMilliseconds(300), new QuadraticEaseIn(),
- // FillMode.Forward);
- // foreach (var animation in slideDownInMotionConfig.Animations)
- // {
- // layoutTransformStyle.Animations.Add(animation);
- // }
- // layoutTransformStyle.Add(LayoutTransformControl.RenderTransformOriginProperty, slideDownInMotionConfig.RenderTransformOrigin);
- // layoutTransformStyle.Add(LayoutTransformControl.IsVisibleProperty, false);
- // closeSubMenuStyle.Add(layoutTransformStyle);
- // }
- // Add(closeSubMenuStyle);
- //
- // var openSubMenuStyle = new Style(selector => selector.Nesting().Class(StdPseudoClass.Open));
- // {
- // var layoutTransformStyle =
- // new Style(selector => selector.Nesting().Template().Name(ChildItemsLayoutTransformPart));
- // var slideDownInMotionConfig = MotionFactory.BuildSlideUpInMotion(TimeSpan.FromMilliseconds(300), new QuadraticEaseIn(),
- // FillMode.Forward);
- // foreach (var animation in slideDownInMotionConfig.Animations)
- // {
- // layoutTransformStyle.Animations.Add(animation);
- // }
- //
- // layoutTransformStyle.Add(LayoutTransformControl.RenderTransformOriginProperty, slideDownInMotionConfig.RenderTransformOrigin);
- // layoutTransformStyle.Add(LayoutTransformControl.IsVisibleProperty, true);
- // openSubMenuStyle.Add(layoutTransformStyle);
- // }
- // Add(openSubMenuStyle);
- // }
}
\ No newline at end of file
diff --git a/src/AtomUI.Controls/NavMenu/NavMenu.cs b/src/AtomUI.Controls/NavMenu/NavMenu.cs
index 0fcbf6f..48335ab 100644
--- a/src/AtomUI.Controls/NavMenu/NavMenu.cs
+++ b/src/AtomUI.Controls/NavMenu/NavMenu.cs
@@ -254,8 +254,26 @@ public class NavMenu : NavMenuBase
}
}
- internal void UpdateSelectionFromItem(INavMenuItem? item)
+ internal void ClearSelection()
{
- UpdateSelectionFromEventSource(item);
+ foreach (var item in Items)
+ {
+ if (item is NavMenuItem navMenuItem)
+ {
+ ClearSelectionRecursively(navMenuItem);
+ }
+ }
+ }
+
+ private void ClearSelectionRecursively(NavMenuItem item)
+ {
+ item.IsSelected = false;
+ foreach (var childItem in item.Items)
+ {
+ if (childItem is NavMenuItem navMenuItem)
+ {
+ ClearSelectionRecursively(navMenuItem);
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/AtomUI.Controls/NavMenu/NavMenuItem.cs b/src/AtomUI.Controls/NavMenu/NavMenuItem.cs
index 22b95a5..3f528c1 100644
--- a/src/AtomUI.Controls/NavMenu/NavMenuItem.cs
+++ b/src/AtomUI.Controls/NavMenu/NavMenuItem.cs
@@ -1,6 +1,7 @@
using System.Windows.Input;
using AtomUI.Controls.Utils;
using AtomUI.Data;
+using AtomUI.Icon;
using AtomUI.Input;
using AtomUI.Theme.Styling;
using AtomUI.Utils;
@@ -84,6 +85,13 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
///
public static readonly StyledProperty IsCheckedProperty =
AvaloniaProperty.Register(nameof(IsChecked));
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly DirectProperty LevelProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(Level), o => o.Level);
///
/// Gets or sets the command associated with the menu item.
@@ -171,10 +179,25 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
set => SetValue(IsCheckedProperty, value);
}
+ private bool _hasSubMenu;
///
/// Gets or sets a value that indicates whether the has a submenu.
///
- public bool HasSubMenu => !Classes.Contains(StdPseudoClass.Empty);
+ public bool HasSubMenu
+ {
+ get => _hasSubMenu;
+ set => SetAndRaise(HasSubMenuProperty, ref _hasSubMenu, value);
+ }
+
+ private int _level;
+ ///
+ /// Gets the level/indentation of the item.
+ ///
+ public int Level
+ {
+ get => _level;
+ private set => SetAndRaise(LevelProperty, ref _level, value);
+ }
///
/// Gets a value that indicates whether the is a top-level main menu item.
@@ -250,6 +273,16 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
o => o.OpenCloseMotionDuration,
(o, v) => o.OpenCloseMotionDuration = v);
+ internal static readonly DirectProperty HasSubMenuProperty =
+ AvaloniaProperty.RegisterDirect(nameof(HasSubMenu),
+ o => o.HasSubMenu,
+ (o, v) => o.HasSubMenu = v);
+
+ internal static readonly DirectProperty InlineItemIndentUnitProperty =
+ AvaloniaProperty.RegisterDirect(nameof(InlineItemIndentUnit),
+ o => o.InlineItemIndentUnit,
+ (o, v) => o.InlineItemIndentUnit = v);
+
internal double ActiveBarWidth
{
get => GetValue(ActiveBarWidthProperty);
@@ -297,7 +330,14 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
set => SetAndRaise(ModeProperty, ref _mode, value);
}
+ private double _inlineItemIndentUnit;
+ internal double InlineItemIndentUnit
+ {
+ get => _inlineItemIndentUnit;
+ set => SetAndRaise(InlineItemIndentUnitProperty, ref _inlineItemIndentUnit, value);
+ }
+
#endregion
#region 公共事件定义
@@ -428,31 +468,6 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
///
public void Open()
{
- if (Mode == NavMenuMode.Inline)
- {
- if (_childItemsLayoutTransform is not null)
- {
- if (_animating)
- {
- return;
- }
-
- _animating = true;
- var slideDownInMotionConfig = MotionFactory.BuildSlideUpInMotion(_openCloseMotionDuration, new QuinticEaseOut(),
- FillMode.Forward);
- SetCurrentValue(IsSubMenuOpenProperty, true);
- _childItemsLayoutTransform.RenderTransformOrigin = slideDownInMotionConfig.RenderTransformOrigin;
- MotionInvoker.Invoke(_childItemsLayoutTransform, slideDownInMotionConfig, () =>
- {
- _childItemsLayoutTransform.SetCurrentValue(IsVisibleProperty, true);
- }, () =>
- {
- _animating = false;
- });
- return;
- }
- }
-
SetCurrentValue(IsSubMenuOpenProperty, true);
}
@@ -464,27 +479,6 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
///
public void Close()
{
- if (Mode == NavMenuMode.Inline)
- {
- if (_childItemsLayoutTransform is not null)
- {
- if (_animating)
- {
- return;
- }
-
- _animating = true;
- SetCurrentValue(IsSubMenuOpenProperty, false);
- var slideDownOutMotionConfig = MotionFactory.BuildSlideUpOutMotion(_openCloseMotionDuration, new QuinticEaseOut(),
- FillMode.Forward);
- MotionInvoker.Invoke(_childItemsLayoutTransform, slideDownOutMotionConfig, null, () =>
- {
- _childItemsLayoutTransform.SetCurrentValue(IsVisibleProperty, false);
- _animating = false;
- });
- return;
- }
- }
SetCurrentValue(IsSubMenuOpenProperty, false);
}
@@ -528,6 +522,8 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
}
base.OnAttachedToLogicalTree(e);
+
+ Level = CalculateDistanceFromLogicalParent(this) - 1;
(var command, var parameter) = (Command, CommandParameter);
if (command is not null)
@@ -545,6 +541,7 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
}
_isEmbeddedInMenu = parent?.FindLogicalAncestorOfType(true) != null;
+ TokenResourceBinder.CreateTokenBinding(this, InlineItemIndentUnitProperty, NavMenuTokenResourceKey.InlineItemIndentUnit);
}
///
@@ -590,7 +587,10 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
protected override void OnGotFocus(GotFocusEventArgs e)
{
base.OnGotFocus(e);
- e.Handled = UpdateSelectionFromEventSource(e.Source, true);
+ if (Mode != NavMenuMode.Inline || !HasSubMenu)
+ {
+ e.Handled = UpdateSelectionFromEventSource(e.Source, true);
+ }
}
///
@@ -620,15 +620,19 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
protected virtual void OnSubmenuOpened(RoutedEventArgs e)
{
var menuItem = e.Source as NavMenuItem;
-
+
if (menuItem != null && menuItem.Parent == this)
{
- foreach (var child in ((INavMenuItem)this).SubItems)
+ // TODO 我们在这里对模式做一个区分, Inline 暂时不互斥关闭,后面有时间看是否加一个互斥的标记
+ if (Mode != NavMenuMode.Inline)
{
- if (child != menuItem && child.IsSubMenuOpen)
+ foreach (var child in ((INavMenuItem)this).SubItems)
{
- child.IsSubMenuOpen = false;
- }
+ if (child != menuItem && child.IsSubMenuOpen)
+ {
+ child.IsSubMenuOpen = false;
+ }
+ }
}
}
}
@@ -819,6 +823,9 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
} else if (change.Property == ModeProperty)
{
SetupItemContainerTheme(true);
+ } else if (change.Property == ItemCountProperty)
+ {
+ HasSubMenu = ItemCount > 0;
}
if (change.Property == BoundsProperty ||
@@ -917,11 +924,23 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
private void IsSelectedChanged(AvaloniaPropertyChangedEventArgs e)
{
var parentMenu = Parent as NavMenu;
-
- if ((bool)e.NewValue! && (parentMenu is null || parentMenu.IsOpen))
+ var isSelected = e.GetNewValue();
+ if (isSelected && (parentMenu is null || parentMenu.IsOpen))
{
Focus();
}
+
+ if (Icon is not null && Icon is PathIcon menuIcon)
+ {
+ if (isSelected)
+ {
+ menuIcon.SetValue(PathIcon.IconModeProperty, IconMode.Selected);
+ }
+ else
+ {
+ menuIcon.SetValue(PathIcon.IconModeProperty, IconMode.Normal);
+ }
+ }
}
///
@@ -932,22 +951,87 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
{
var value = (bool)e.NewValue!;
- if (value)
+ if (Mode == NavMenuMode.Inline)
{
- foreach (var item in ItemsView.OfType())
+ // 在这里我们有一个动画的效果
+ if (value)
{
- item.TryUpdateCanExecute();
- }
+ foreach (var item in ItemsView.OfType())
+ {
+ item.TryUpdateCanExecute();
+ }
- RaiseEvent(new RoutedEventArgs(SubmenuOpenedEvent));
- SetCurrentValue(IsSelectedProperty, true);
- PseudoClasses.Add(StdPseudoClass.Open);
+ RaiseEvent(new RoutedEventArgs(SubmenuOpenedEvent));
+ PseudoClasses.Add(StdPseudoClass.Open);
+ OpenInlineItem();
+ } else
+ {
+ PseudoClasses.Remove(StdPseudoClass.Open);
+ CloseInlineItem();
+ }
}
else
{
- CloseSubmenus();
- SelectedIndex = -1;
- PseudoClasses.Remove(StdPseudoClass.Open);
+ if (value)
+ {
+ foreach (var item in ItemsView.OfType())
+ {
+ item.TryUpdateCanExecute();
+ }
+ RaiseEvent(new RoutedEventArgs(SubmenuOpenedEvent));
+ SetCurrentValue(IsSelectedProperty, true);
+ PseudoClasses.Add(StdPseudoClass.Open);
+ }
+ else
+ {
+ CloseSubmenus();
+ SelectedIndex = -1;
+ PseudoClasses.Remove(StdPseudoClass.Open);
+ }
+ }
+ }
+
+ private void OpenInlineItem()
+ {
+ if (_childItemsLayoutTransform is not null)
+ {
+ if (_animating)
+ {
+ return;
+ }
+
+ _animating = true;
+ var slideDownInMotionConfig = MotionFactory.BuildSlideUpInMotion(_openCloseMotionDuration, new QuinticEaseOut(),
+ FillMode.Forward);
+ _childItemsLayoutTransform.RenderTransformOrigin = slideDownInMotionConfig.RenderTransformOrigin;
+ MotionInvoker.Invoke(_childItemsLayoutTransform, slideDownInMotionConfig, () =>
+ {
+ _childItemsLayoutTransform.SetCurrentValue(IsVisibleProperty, true);
+ }, () =>
+ {
+ _animating = false;
+ });
+ }
+ }
+
+ private void CloseInlineItem()
+ {
+ if (_childItemsLayoutTransform is not null)
+ {
+ if (_animating)
+ {
+ return;
+ }
+
+ _animating = true;
+ SetCurrentValue(IsSubMenuOpenProperty, false);
+ var slideDownOutMotionConfig = MotionFactory.BuildSlideUpOutMotion(_openCloseMotionDuration, new QuinticEaseOut(),
+ FillMode.Forward);
+ MotionInvoker.Invoke(_childItemsLayoutTransform, slideDownOutMotionConfig, null, () =>
+ {
+ _childItemsLayoutTransform.SetCurrentValue(IsVisibleProperty, false);
+ _animating = false;
+ });
}
}
@@ -1038,4 +1122,29 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
BindUtils.RelayBind(this, ModeProperty, navMenuItem, ModeProperty);
}
}
+
+ internal void SelectItemRecursively()
+ {
+ IsSelected = true;
+ if (!IsTopLevel)
+ {
+ if (Parent is NavMenuItem parent)
+ {
+ parent.SelectItemRecursively();
+ }
+ }
+ }
+
+ private static int CalculateDistanceFromLogicalParent(ILogical? logical, int @default = -1) where T : class
+ {
+ var result = 0;
+
+ while (logical != null && !(logical is T))
+ {
+ ++result;
+ logical = logical.LogicalParent;
+ }
+
+ return logical != null ? result : @default;
+ }
}
\ No newline at end of file
diff --git a/src/AtomUI.Controls/NavMenu/NavMenuToken.cs b/src/AtomUI.Controls/NavMenu/NavMenuToken.cs
index 51c4862..56f7c8e 100644
--- a/src/AtomUI.Controls/NavMenu/NavMenuToken.cs
+++ b/src/AtomUI.Controls/NavMenu/NavMenuToken.cs
@@ -215,6 +215,11 @@ internal class NavMenuToken : AbstractControlDesignToken
///
public double ItemHeight { get; set; }
+ ///
+ /// 内联菜单项的缩进单位
+ ///
+ public double InlineItemIndentUnit { get; set; }
+
///
/// 收起后的宽度
///
@@ -446,5 +451,7 @@ internal class NavMenuToken : AbstractControlDesignToken
MenuPopupContentPadding = new Thickness(_globalToken.PaddingXXS, MenuPopupBorderRadius.TopLeft / 2);
MenuPopupBoxShadows = _globalToken.BoxShadowsSecondary;
VerticalItemsPanelSpacing = _globalToken.MarginXXS;
+
+ InlineItemIndentUnit = ItemHeight / 2;
}
}
\ No newline at end of file
diff --git a/src/AtomUI.Controls/PathIcon/PathIcon.cs b/src/AtomUI.Controls/PathIcon/PathIcon.cs
index 0cf8fb9..cea5997 100644
--- a/src/AtomUI.Controls/PathIcon/PathIcon.cs
+++ b/src/AtomUI.Controls/PathIcon/PathIcon.cs
@@ -206,6 +206,14 @@ public sealed class PathIcon : Control, ICustomHitTest
BuildSourceRenderData();
}
}
+ else if (change.Property == IsEnabledProperty)
+ {
+ // TODO 这个地方需要优化一点,是否需要保存老的,当状态为 Enabled 的时候进行还原
+ if (!IsEnabled)
+ {
+ IconMode = IconMode.Disabled;
+ }
+ }
else if (change.Property == NormalFilledBrushProperty ||
change.Property == ActiveFilledBrushProperty ||
change.Property == SelectedFilledBrushProperty ||