NavMenu dark mode completed

This commit is contained in:
polarboy 2024-09-25 14:23:18 +08:00
parent 003cb21847
commit 8710ccbd48
15 changed files with 426 additions and 121 deletions

View File

@ -63,6 +63,7 @@ Welcome to communicate and give suggestions to AtomUI, thank you for giving the
| Breadcrumb | TODO | | Breadcrumb | TODO |
| Dropdown | Completed ✅ | | Dropdown | Completed ✅ |
| Menu | Completed ✅ | | Menu | Completed ✅ |
| NavMenu | Completed ✅ |
| Pagination | TODO | | Pagination | TODO |
| Steps | TODO | | Steps | TODO |

View File

@ -90,7 +90,8 @@ PS: AtomUI 目前仅在 Windows 11 平台测试<br>
|:---------------|:-------| |:---------------|:-------|
| Breadcrumb 面包屑 | 未完成 | | Breadcrumb 面包屑 | 未完成 |
| Dropdown 下拉菜单 | 已完成 ✅ | | Dropdown 下拉菜单 | 已完成 ✅ |
| Menu 导航菜单 | 已完成 ✅ | | Menu 菜单 | 已完成 ✅ |
| NavMenu 导航菜单 | 已完成 ✅ |
| Pagination 分页 | 进行中 💪 | | Pagination 分页 | 进行中 💪 |
| Steps 步骤条 | 未完成 | | Steps 步骤条 | 未完成 |

View File

@ -241,7 +241,7 @@
<atom:NavMenuItem Header="Option 1" /> <atom:NavMenuItem Header="Option 1" />
<atom:NavMenuItem Header="Option 2" /> <atom:NavMenuItem Header="Option 2" />
</atom:NavMenuItem> </atom:NavMenuItem>
<atom:NavMenuItem Header="Item 2"> <atom:NavMenuItem Header="Item 2">
<atom:NavMenuItem Header="Option 3" /> <atom:NavMenuItem Header="Option 3" />
<atom:NavMenuItem Header="Option 4" /> <atom:NavMenuItem Header="Option 4" />
@ -250,6 +250,36 @@
<atom:NavMenuItem Header="Navigation Four"/> <atom:NavMenuItem Header="Navigation Four"/>
</atom:NavMenu> </atom:NavMenu>
</desktop:ShowCaseItem> </desktop:ShowCaseItem>
<desktop:ShowCaseItem
Title="Switch the menu type"
Description="Show the dynamic switching mode (between inline and vertical).">
<StackPanel Orientation="Vertical" Spacing="10">
<StackPanel Orientation="Horizontal" Spacing="5">
<atom:ToggleSwitch Name="ChangeModeSwitch"/>
<TextBlock>Change Mode</TextBlock>
<atom:ToggleSwitch Margin="10, 0, 0, 0" Name="ChangeStyleSwitch"/>
<TextBlock>Change Style</TextBlock>
</StackPanel>
<atom:NavMenu Mode="{Binding Mode}" Width="256" Margin="0, 0, 0, 20" IsDarkStyle="{Binding IsDark}">
<atom:NavMenuItem Header="Navigation One" Icon="{atom:IconProvider Kind=MailOutlined}" />
<atom:NavMenuItem Header="Navigation Two" Icon="{atom:IconProvider Kind=AppstoreOutlined}" IsEnabled="False"/>
<atom:NavMenuItem Header="Navigation Three - Submenu" Icon="{atom:IconProvider Kind=SettingOutlined}">
<atom:NavMenuItem Header="Item 1">
<atom:NavMenuItem Header="Option 1" />
<atom:NavMenuItem Header="Option 2" />
</atom:NavMenuItem>
<atom:NavMenuItem Header="Item 2">
<atom:NavMenuItem Header="Option 3" />
<atom:NavMenuItem Header="Option 4" />
</atom:NavMenuItem>
</atom:NavMenuItem>
<atom:NavMenuItem Header="Navigation Four"/>
</atom:NavMenu>
</StackPanel>
</desktop:ShowCaseItem>
</desktop:ShowCasePanel> </desktop:ShowCasePanel>
</UserControl> </UserControl>

View File

@ -1,11 +1,66 @@
using AtomUI.Controls;
using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Interactivity;
namespace AtomUI.Demo.Desktop.ShowCase; namespace AtomUI.Demo.Desktop.ShowCase;
public partial class MenuShowCase : UserControl public partial class MenuShowCase : UserControl
{ {
public static readonly StyledProperty<bool> IsDarkProperty =
AvaloniaProperty.Register<ButtonShowCase, bool>(nameof(IsDark), false);
public static readonly StyledProperty<NavMenuMode> ModeProperty =
AvaloniaProperty.Register<ButtonShowCase, NavMenuMode>(nameof(Mode), NavMenuMode.Inline);
public bool IsDark
{
get => GetValue(IsDarkProperty);
set => SetValue(IsDarkProperty, value);
}
public NavMenuMode Mode
{
get => GetValue(ModeProperty);
set => SetValue(ModeProperty, value);
}
public MenuShowCase() public MenuShowCase()
{ {
InitializeComponent(); InitializeComponent();
DataContext = this;
ChangeModeSwitch.IsCheckedChanged += HandleChangeModeCheckChanged;
ChangeStyleSwitch.IsCheckedChanged += HandleChangeStyleCheckChanged;
}
private void HandleChangeModeCheckChanged(object? sender, RoutedEventArgs? args)
{
if (ChangeModeSwitch.IsChecked.HasValue)
{
if (ChangeModeSwitch.IsChecked.Value)
{
Mode = NavMenuMode.Vertical;
}
else
{
Mode = NavMenuMode.Inline;
}
}
else
{
Mode = NavMenuMode.Inline;
}
}
private void HandleChangeStyleCheckChanged(object? sender, RoutedEventArgs? args)
{
if (ChangeStyleSwitch.IsChecked.HasValue)
{
IsDark = ChangeStyleSwitch.IsChecked.Value;
}
else
{
IsDark = false;
}
} }
} }

View File

@ -406,6 +406,7 @@ namespace AtomUI.Theme.Styling
public static readonly TokenResourceKey ItemContentMargin = new TokenResourceKey("NavMenu.ItemContentMargin", "AtomUI.Token"); public static readonly TokenResourceKey ItemContentMargin = new TokenResourceKey("NavMenu.ItemContentMargin", "AtomUI.Token");
public static readonly TokenResourceKey ItemContentPadding = new TokenResourceKey("NavMenu.ItemContentPadding", "AtomUI.Token"); public static readonly TokenResourceKey ItemContentPadding = new TokenResourceKey("NavMenu.ItemContentPadding", "AtomUI.Token");
public static readonly TokenResourceKey VerticalItemsPanelSpacing = new TokenResourceKey("NavMenu.VerticalItemsPanelSpacing", "AtomUI.Token"); public static readonly TokenResourceKey VerticalItemsPanelSpacing = new TokenResourceKey("NavMenu.VerticalItemsPanelSpacing", "AtomUI.Token");
public static readonly TokenResourceKey VerticalMenuContentPadding = new TokenResourceKey("NavMenu.VerticalMenuContentPadding", "AtomUI.Token");
public static readonly TokenResourceKey ItemMargin = new TokenResourceKey("NavMenu.ItemMargin", "AtomUI.Token"); public static readonly TokenResourceKey ItemMargin = new TokenResourceKey("NavMenu.ItemMargin", "AtomUI.Token");
public static readonly TokenResourceKey HorizontalItemMargin = new TokenResourceKey("NavMenu.HorizontalItemMargin", "AtomUI.Token"); public static readonly TokenResourceKey HorizontalItemMargin = new TokenResourceKey("NavMenu.HorizontalItemMargin", "AtomUI.Token");
public static readonly TokenResourceKey HorizontalItemHoverBg = new TokenResourceKey("NavMenu.HorizontalItemHoverBg", "AtomUI.Token"); public static readonly TokenResourceKey HorizontalItemHoverBg = new TokenResourceKey("NavMenu.HorizontalItemHoverBg", "AtomUI.Token");

View File

@ -20,7 +20,7 @@ namespace AtomUI.Controls;
internal class BaseNavMenuItemTheme : BaseControlTheme internal class BaseNavMenuItemTheme : BaseControlTheme
{ {
public const string ItemDecoratorPart = "PART_ItemDecorator"; public const string HeaderDecoratorPart = "PART_HeaderDecorator";
public const string MainContainerPart = "PART_MainContainer"; public const string MainContainerPart = "PART_MainContainer";
public const string ItemIconPresenterPart = "PART_ItemIconPresenter"; public const string ItemIconPresenterPart = "PART_ItemIconPresenter";
public const string ItemTextPresenterPart = "PART_ItemTextPresenter"; public const string ItemTextPresenterPart = "PART_ItemTextPresenter";
@ -35,7 +35,6 @@ internal class BaseNavMenuItemTheme : BaseControlTheme
{ {
return new FuncControlTemplate<NavMenuItem>((item, scope) => return new FuncControlTemplate<NavMenuItem>((item, scope) =>
{ {
BuildInstanceStyles(item);
// 仅仅为了把 Popup 包进来,没有其他什么作用 // 仅仅为了把 Popup 包进来,没有其他什么作用
var layoutWrapper = new Panel(); var layoutWrapper = new Panel();
var header = BuildMenuItemContent(item, scope); var header = BuildMenuItemContent(item, scope);
@ -49,13 +48,14 @@ internal class BaseNavMenuItemTheme : BaseControlTheme
{ {
var headerFrame = new Border var headerFrame = new Border
{ {
Name = ItemDecoratorPart Name = HeaderDecoratorPart,
Transitions = new Transitions()
{
AnimationUtils.CreateTransition<SolidColorBrushTransition>(Border.BackgroundProperty),
AnimationUtils.CreateTransition<SolidColorBrushTransition>(TemplatedControl.ForegroundProperty)
}
}; };
headerFrame.RegisterInNameScope(scope);
var transitions = new Transitions();
transitions.Add(AnimationUtils.CreateTransition<SolidColorBrushTransition>(Border.BackgroundProperty));
headerFrame.Transitions = transitions;
headerFrame.Child = BuildMenuItemInfoGrid(navMenuItem, scope); headerFrame.Child = BuildMenuItemInfoGrid(navMenuItem, scope);
return headerFrame; return headerFrame;
} }
@ -84,17 +84,16 @@ internal class BaseNavMenuItemTheme : BaseControlTheme
}; };
layout.RegisterInNameScope(scope); layout.RegisterInNameScope(scope);
var iconPresenter = new Viewbox var iconPresenter = new ContentPresenter()
{ {
Name = ItemIconPresenterPart, Name = ItemIconPresenterPart,
HorizontalAlignment = HorizontalAlignment.Center, HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center, VerticalAlignment = VerticalAlignment.Center,
Stretch = Stretch.Uniform
}; };
Grid.SetColumn(iconPresenter, 0); Grid.SetColumn(iconPresenter, 0);
iconPresenter.RegisterInNameScope(scope); iconPresenter.RegisterInNameScope(scope);
CreateTemplateParentBinding(iconPresenter, Viewbox.ChildProperty, NavMenuItem.IconProperty); CreateTemplateParentBinding(iconPresenter, ContentPresenter.ContentProperty, NavMenuItem.IconProperty);
TokenResourceBinder.CreateTokenBinding(iconPresenter, Layoutable.MarginProperty, TokenResourceBinder.CreateTokenBinding(iconPresenter, Layoutable.MarginProperty,
NavMenuTokenResourceKey.ItemMargin); NavMenuTokenResourceKey.ItemMargin);
TokenResourceBinder.CreateGlobalTokenBinding(iconPresenter, Layoutable.WidthProperty, TokenResourceBinder.CreateGlobalTokenBinding(iconPresenter, Layoutable.WidthProperty,
@ -161,17 +160,9 @@ internal class BaseNavMenuItemTheme : BaseControlTheme
VerticalAlignment = VerticalAlignment.Center, VerticalAlignment = VerticalAlignment.Center,
Kind = "RightOutlined" Kind = "RightOutlined"
}; };
CreateTemplateParentBinding(menuIndicatorIcon, PathIcon.IsEnabledProperty, NavMenuItem.IsEnabledProperty); 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, TokenResourceBinder.CreateGlobalTokenBinding(menuIndicatorIcon, Layoutable.WidthProperty,
NavMenuTokenResourceKey.MenuArrowSize); NavMenuTokenResourceKey.MenuArrowSize);
TokenResourceBinder.CreateGlobalTokenBinding(menuIndicatorIcon, Layoutable.HeightProperty, TokenResourceBinder.CreateGlobalTokenBinding(menuIndicatorIcon, Layoutable.HeightProperty,
@ -199,17 +190,17 @@ internal class BaseNavMenuItemTheme : BaseControlTheme
commonStyle.Add(keyGestureStyle); commonStyle.Add(keyGestureStyle);
} }
{ {
var borderStyle = new Style(selector => selector.Nesting().Template().Name(ItemDecoratorPart)); var borderStyle = new Style(selector => selector.Nesting().Template().Name(HeaderDecoratorPart));
borderStyle.Add(Border.CursorProperty, new Cursor(StandardCursorType.Hand)); borderStyle.Add(Border.CursorProperty, new Cursor(StandardCursorType.Hand));
borderStyle.Add(Layoutable.MinHeightProperty, NavMenuTokenResourceKey.ItemHeight); borderStyle.Add(Border.MinHeightProperty, NavMenuTokenResourceKey.ItemHeight);
borderStyle.Add(Decorator.PaddingProperty, NavMenuTokenResourceKey.ItemContentPadding); borderStyle.Add(Border.PaddingProperty, NavMenuTokenResourceKey.ItemContentPadding);
borderStyle.Add(Border.BackgroundProperty, NavMenuTokenResourceKey.ItemBg); borderStyle.Add(Border.BackgroundProperty, NavMenuTokenResourceKey.ItemBg);
borderStyle.Add(Border.CornerRadiusProperty, NavMenuTokenResourceKey.ItemBorderRadius); borderStyle.Add(Border.CornerRadiusProperty, NavMenuTokenResourceKey.ItemBorderRadius);
commonStyle.Add(borderStyle); commonStyle.Add(borderStyle);
} }
// Hover 状态 // Hover 状态
var hoverStyle = new Style(selector => selector.Nesting().Template().Name(ItemDecoratorPart).Class(StdPseudoClass.PointerOver)); var hoverStyle = new Style(selector => selector.Nesting().Template().Name(HeaderDecoratorPart).Class(StdPseudoClass.PointerOver));
hoverStyle.Add(TemplatedControl.ForegroundProperty, NavMenuTokenResourceKey.ItemHoverColor); hoverStyle.Add(TemplatedControl.ForegroundProperty, NavMenuTokenResourceKey.ItemHoverColor);
hoverStyle.Add(Border.BackgroundProperty, NavMenuTokenResourceKey.ItemHoverBg); hoverStyle.Add(Border.BackgroundProperty, NavMenuTokenResourceKey.ItemHoverBg);
commonStyle.Add(hoverStyle); commonStyle.Add(hoverStyle);
@ -219,7 +210,7 @@ internal class BaseNavMenuItemTheme : BaseControlTheme
{ {
var selectedStyle = new Style(selector => selector.Nesting().Class(StdPseudoClass.Selected)); var selectedStyle = new Style(selector => selector.Nesting().Class(StdPseudoClass.Selected));
{ {
var itemDecoratorStyle = new Style(selector => selector.Nesting().Template().Name(ItemDecoratorPart)); var itemDecoratorStyle = new Style(selector => selector.Nesting().Template().Name(HeaderDecoratorPart));
itemDecoratorStyle.Add(Border.BackgroundProperty, NavMenuTokenResourceKey.ItemSelectedBg); itemDecoratorStyle.Add(Border.BackgroundProperty, NavMenuTokenResourceKey.ItemSelectedBg);
itemDecoratorStyle.Add(TemplatedControl.ForegroundProperty, NavMenuTokenResourceKey.ItemSelectedColor); itemDecoratorStyle.Add(TemplatedControl.ForegroundProperty, NavMenuTokenResourceKey.ItemSelectedColor);
selectedStyle.Add(itemDecoratorStyle); selectedStyle.Add(itemDecoratorStyle);
@ -232,7 +223,7 @@ internal class BaseNavMenuItemTheme : BaseControlTheme
{ {
var selectedStyle = new Style(selector => selector.Nesting().Class(StdPseudoClass.Selected)); var selectedStyle = new Style(selector => selector.Nesting().Class(StdPseudoClass.Selected));
{ {
var itemDecoratorStyle = new Style(selector => selector.Nesting().Template().Name(ItemDecoratorPart)); var itemDecoratorStyle = new Style(selector => selector.Nesting().Template().Name(HeaderDecoratorPart));
itemDecoratorStyle.Add(TemplatedControl.ForegroundProperty, NavMenuTokenResourceKey.ItemSelectedColor); itemDecoratorStyle.Add(TemplatedControl.ForegroundProperty, NavMenuTokenResourceKey.ItemSelectedColor);
selectedStyle.Add(itemDecoratorStyle); selectedStyle.Add(itemDecoratorStyle);
} }
@ -240,25 +231,94 @@ internal class BaseNavMenuItemTheme : BaseControlTheme
} }
commonStyle.Add(hasSubMenuStyle); commonStyle.Add(hasSubMenuStyle);
Add(commonStyle); Add(commonStyle);
BuildDarkCommonStyle();
}
private void BuildDarkCommonStyle()
{
var darkCommonStyle = new Style(selector => selector.Nesting().PropertyEquals(NavMenuItem.IsDarkStyleProperty, true));
darkCommonStyle.Add(NavMenuItem.ForegroundProperty, NavMenuTokenResourceKey.DarkItemColor);
{
var borderStyle = new Style(selector => selector.Nesting().Template().Name(HeaderDecoratorPart));
borderStyle.Add(Border.BackgroundProperty, NavMenuTokenResourceKey.DarkItemBg);
darkCommonStyle.Add(borderStyle);
}
// 选中分两种,一种是有子菜单一种是没有子菜单
var hasNoSubMenuStyle = new Style(selector => selector.Nesting().PropertyEquals(NavMenuItem.HasSubMenuProperty, false));
{
// Hover 状态
var hoverStyle = new Style(selector => selector.Nesting().Template().Name(HeaderDecoratorPart).Class(StdPseudoClass.PointerOver));
hoverStyle.Add(TemplatedControl.ForegroundProperty, NavMenuTokenResourceKey.DarkItemHoverColor);
hoverStyle.Add(Border.BackgroundProperty, NavMenuTokenResourceKey.DarkItemHoverBg);
hasNoSubMenuStyle.Add(hoverStyle);
var selectedStyle = new Style(selector => selector.Nesting().Class(StdPseudoClass.Selected));
{
var itemDecoratorStyle = new Style(selector => selector.Nesting().Template().Name(HeaderDecoratorPart));
itemDecoratorStyle.Add(Border.BackgroundProperty, NavMenuTokenResourceKey.DarkItemSelectedBg);
itemDecoratorStyle.Add(TemplatedControl.ForegroundProperty, NavMenuTokenResourceKey.DarkItemSelectedColor);
selectedStyle.Add(itemDecoratorStyle);
}
hasNoSubMenuStyle.Add(selectedStyle);
}
darkCommonStyle.Add(hasNoSubMenuStyle);
var hasSubMenuStyle = new Style(selector => selector.Nesting().PropertyEquals(NavMenuItem.HasSubMenuProperty, true));
{
// Hover 状态
var hoverStyle = new Style(selector => selector.Nesting().Template().Name(HeaderDecoratorPart).Class(StdPseudoClass.PointerOver));
hoverStyle.Add(TemplatedControl.ForegroundProperty, NavMenuTokenResourceKey.DarkItemColor);
hoverStyle.Add(Border.BackgroundProperty, NavMenuTokenResourceKey.DarkItemBg);
hasSubMenuStyle.Add(hoverStyle);
var selectedStyle = new Style(selector => selector.Nesting().Class(StdPseudoClass.Selected));
{
var itemDecoratorStyle = new Style(selector => selector.Nesting().Template().Name(HeaderDecoratorPart));
itemDecoratorStyle.Add(TemplatedControl.ForegroundProperty, NavMenuTokenResourceKey.DarkItemSelectedColor);
selectedStyle.Add(itemDecoratorStyle);
}
hasSubMenuStyle.Add(selectedStyle);
}
darkCommonStyle.Add(hasSubMenuStyle);
Add(darkCommonStyle);
} }
private void BuildMenuIndicatorStyle() private void BuildMenuIndicatorStyle()
{ {
{ {
{ var menuIndicatorStyle = new Style(selector => selector.Nesting().Template().Name(MenuIndicatorIconPart));
var menuIndicatorStyle = new Style(selector => selector.Nesting().Template().Name(MenuIndicatorIconPart)); menuIndicatorStyle.Add(Visual.IsVisibleProperty, true);
menuIndicatorStyle.Add(Visual.IsVisibleProperty, true); menuIndicatorStyle.Add(PathIcon.NormalFilledBrushProperty, NavMenuTokenResourceKey.ItemColor);
Add(menuIndicatorStyle); menuIndicatorStyle.Add(PathIcon.SelectedFilledBrushProperty, NavMenuTokenResourceKey.ItemSelectedColor);
} menuIndicatorStyle.Add(PathIcon.DisabledFilledBrushProperty, NavMenuTokenResourceKey.ItemDisabledColor);
// 设置颜色
var selectedStyle = new Style(selector => selector.Nesting().Class(StdPseudoClass.Selected)); Add(menuIndicatorStyle);
}
{
var darkCommonStyle = new Style(selector => selector.Nesting().PropertyEquals(NavMenuItem.IsDarkStyleProperty, true));
{ {
var menuIndicatorStyle = new Style(selector => selector.Nesting().Template().Name(MenuIndicatorIconPart)); var menuIndicatorStyle = new Style(selector => selector.Nesting().Template().Name(MenuIndicatorIconPart));
menuIndicatorStyle.Add(PathIcon.IconModeProperty, IconMode.Selected); menuIndicatorStyle.Add(PathIcon.NormalFilledBrushProperty, NavMenuTokenResourceKey.DarkItemColor);
selectedStyle.Add(menuIndicatorStyle); menuIndicatorStyle.Add(PathIcon.SelectedFilledBrushProperty, NavMenuTokenResourceKey.DarkItemSelectedColor);
menuIndicatorStyle.Add(PathIcon.DisabledFilledBrushProperty, NavMenuTokenResourceKey.DarkItemDisabledColor);
darkCommonStyle.Add(menuIndicatorStyle);
} }
Add(selectedStyle); Add(darkCommonStyle);
} }
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 hasNoSubMenuStyle = new Style(selector => selector.Nesting().PropertyEquals(NavMenuItem.HasSubMenuProperty, false)); var hasNoSubMenuStyle = new Style(selector => selector.Nesting().PropertyEquals(NavMenuItem.HasSubMenuProperty, false));
{ {
var menuIndicatorStyle = new Style(selector => selector.Nesting().Template().Name(MenuIndicatorIconPart)); var menuIndicatorStyle = new Style(selector => selector.Nesting().Template().Name(MenuIndicatorIconPart));
@ -271,41 +331,34 @@ internal class BaseNavMenuItemTheme : BaseControlTheme
private void BuildMenuIconStyle() private void BuildMenuIconStyle()
{ {
{ {
var iconViewBoxStyle = new Style(selector => selector.Nesting().Template().Name(ItemIconPresenterPart)); var iconContentPresenterStyle = new Style(selector => selector.Nesting().Template().Name(ItemIconPresenterPart));
iconViewBoxStyle.Add(Visual.IsVisibleProperty, false); iconContentPresenterStyle.Add(Visual.IsVisibleProperty, false);
Add(iconViewBoxStyle); Add(iconContentPresenterStyle);
} }
var hasIconStyle = new Style(selector => selector.Nesting().Class(":icon")); var hasIconStyle = new Style(selector => selector.Nesting().Class(":icon"));
{ {
var iconViewBoxStyle = new Style(selector => selector.Nesting().Template().Name(ItemIconPresenterPart)); var iconContentPresenterStyle = new Style(selector => selector.Nesting().Template().Name(ItemIconPresenterPart));
iconViewBoxStyle.Add(Visual.IsVisibleProperty, true); iconContentPresenterStyle.Add(Visual.IsVisibleProperty, true);
hasIconStyle.Add(iconViewBoxStyle); hasIconStyle.Add(iconContentPresenterStyle);
} }
Add(hasIconStyle); Add(hasIconStyle);
} }
private void BuildDisabledStyle() private void BuildDisabledStyle()
{ {
var disabledStyle = new Style(selector => selector.Nesting().Class(StdPseudoClass.Disabled)); {
disabledStyle.Add(TemplatedControl.ForegroundProperty, NavMenuTokenResourceKey.ItemDisabledColor); var disabledStyle = new Style(selector => selector.Nesting().Class(StdPseudoClass.Disabled));
Add(disabledStyle); disabledStyle.Add(TemplatedControl.ForegroundProperty, NavMenuTokenResourceKey.ItemDisabledColor);
Add(disabledStyle);
}
var darkStyle = new Style(selector => selector.Nesting().PropertyEquals(NavMenuItem.IsDarkStyleProperty, true));
{
var disabledStyle = new Style(selector => selector.Nesting().Class(StdPseudoClass.Disabled));
disabledStyle.Add(TemplatedControl.ForegroundProperty, NavMenuTokenResourceKey.DarkItemDisabledColor);
darkStyle.Add(disabledStyle);
}
Add(darkStyle);
} }
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 disabledIconStyle = new Style(selector => selector.OfType<PathIcon>().Class(StdPseudoClass.Disabled));
disabledIconStyle.Add(PathIcon.IconModeProperty, IconMode.Disabled);
control.Styles.Add(disabledIconStyle);
}
} }

View File

@ -56,20 +56,21 @@ internal class InlineNavMenuItemTheme : BaseNavMenuItemTheme
var headerContent = base.BuildMenuItemContent(navMenuItem, scope); var headerContent = base.BuildMenuItemContent(navMenuItem, scope);
TokenResourceBinder.CreateTokenBinding(headerContent, Control.MarginProperty, NavMenuTokenResourceKey.VerticalItemsPanelSpacing, BindingPriority.Template,
(v) =>
{
if (v is double dval)
{
return new Thickness(0, 0, 0, dval);
}
return new Thickness();
});
var childItemsLayoutTransform = new LayoutTransformControl() var childItemsLayoutTransform = new LayoutTransformControl()
{ {
Name = ChildItemsLayoutTransformPart, Name = ChildItemsLayoutTransformPart,
}; };
TokenResourceBinder.CreateTokenBinding(childItemsLayoutTransform, LayoutTransformControl.MarginProperty,
NavMenuTokenResourceKey.VerticalItemsPanelSpacing, BindingPriority.Template,
(v) =>
{
if (v is double dval)
{
return new Thickness(0, dval, 0, 0);
}
return new Thickness();
});
childItemsLayoutTransform.RegisterInNameScope(scope); childItemsLayoutTransform.RegisterInNameScope(scope);
var itemsPresenter = new ItemsPresenter var itemsPresenter = new ItemsPresenter
@ -106,6 +107,10 @@ internal class InlineNavMenuItemTheme : BaseNavMenuItemTheme
{ {
base.BuildStyles(); base.BuildStyles();
BuildMenuIndicatorStyle(); BuildMenuIndicatorStyle();
var itemsPanelStyle = new Style(selector => selector.Nesting().Template().Name(ChildItemsPresenterPart).Child().OfType<StackPanel>());
itemsPanelStyle.Add(StackPanel.SpacingProperty, NavMenuTokenResourceKey.VerticalItemsPanelSpacing);
Add(itemsPanelStyle);
} }
private void BuildMenuIndicatorStyle() private void BuildMenuIndicatorStyle()

View File

@ -147,13 +147,20 @@ public class NavMenu : NavMenuBase
{ {
if (change.Property == ModeProperty) if (change.Property == ModeProperty)
{ {
SetupItemContainerTheme(true); HandleModeChanged();
SetupInteractionHandler(true);
} }
UpdatePseudoClasses(); UpdatePseudoClasses();
} }
} }
private void HandleModeChanged()
{
CloseChildItemsRecursively();
SetupItemContainerTheme(true);
RegenerateContainersRecursively();
SetupInteractionHandler(true);
}
protected override void PrepareContainerForItemOverride(Control element, object? item, int index) protected override void PrepareContainerForItemOverride(Control element, object? item, int index)
{ {
base.PrepareContainerForItemOverride(element, item, index); base.PrepareContainerForItemOverride(element, item, index);
@ -172,7 +179,9 @@ public class NavMenu : NavMenuBase
BindUtils.RelayBind(this, ActiveBarWidthProperty, navMenuItem, NavMenuItem.ActiveBarWidthProperty); BindUtils.RelayBind(this, ActiveBarWidthProperty, navMenuItem, NavMenuItem.ActiveBarWidthProperty);
} }
BindUtils.RelayBind(this, ModeProperty, navMenuItem, NavMenuItem.ModeProperty); BindUtils.RelayBind(this, ModeProperty, navMenuItem, NavMenuItem.ModeProperty);
BindUtils.RelayBind(this, IsDarkStyleProperty, navMenuItem, NavMenuItem.IsDarkStyleProperty);
} }
} }
private void UpdatePseudoClasses() private void UpdatePseudoClasses()
@ -276,4 +285,48 @@ public class NavMenu : NavMenuBase
} }
} }
} }
private void CloseChildItemsRecursively()
{
CloseInlineItems();
foreach (var i in ((INavMenu)this).SubItems)
{
i.Close();
}
}
private void RegenerateContainersRecursively()
{
foreach (var item in Items)
{
if (item is NavMenuItem navMenuItem)
{
navMenuItem.RegenerateContainers();
}
}
}
private void CloseInlineItems()
{
foreach (var item in Items)
{
if (item is NavMenuItem navMenuItem)
{
CloseInlineItemRecursively(navMenuItem);
}
}
}
private void CloseInlineItemRecursively(NavMenuItem navMenuItem)
{
foreach (var item in navMenuItem.Items)
{
if (item is NavMenuItem childNavMenuItem)
{
CloseInlineItemRecursively(childNavMenuItem);
}
}
navMenuItem.CloseInlineItem();
navMenuItem.IsSubMenuOpen = false;
}
} }

View File

@ -61,8 +61,8 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
/// <summary> /// <summary>
/// Defines the <see cref="Icon"/> property. /// Defines the <see cref="Icon"/> property.
/// </summary> /// </summary>
public static readonly StyledProperty<object?> IconProperty = public static readonly StyledProperty<PathIcon?> IconProperty =
AvaloniaProperty.Register<NavMenuItem, object?>(nameof(Icon)); AvaloniaProperty.Register<NavMenuItem, PathIcon?>(nameof(Icon));
/// <summary> /// <summary>
/// Defines the <see cref="InputGesture"/> property. /// Defines the <see cref="InputGesture"/> property.
@ -71,7 +71,7 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
AvaloniaProperty.Register<NavMenuItem, KeyGesture?>(nameof(InputGesture)); AvaloniaProperty.Register<NavMenuItem, KeyGesture?>(nameof(InputGesture));
/// <summary> /// <summary>
/// Defines the <see cref="IsSubMenuOpen"/> property. /// Defines the <see cref="IsSubMenuOpen"/> property.
/// </summary> /// </summary>
public static readonly StyledProperty<bool> IsSubMenuOpenProperty = public static readonly StyledProperty<bool> IsSubMenuOpenProperty =
AvaloniaProperty.Register<NavMenuItem, bool>(nameof(IsSubMenuOpen)); AvaloniaProperty.Register<NavMenuItem, bool>(nameof(IsSubMenuOpen));
@ -126,7 +126,7 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
/// <summary> /// <summary>
/// Gets or sets the icon that appears in a <see cref="NavMenuItem"/>. /// Gets or sets the icon that appears in a <see cref="NavMenuItem"/>.
/// </summary> /// </summary>
public object? Icon public PathIcon? Icon
{ {
get => GetValue(IconProperty); get => GetValue(IconProperty);
set => SetValue(IconProperty, value); set => SetValue(IconProperty, value);
@ -285,6 +285,11 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
o => o.InlineItemIndentUnit, o => o.InlineItemIndentUnit,
(o, v) => o.InlineItemIndentUnit = v); (o, v) => o.InlineItemIndentUnit = v);
internal static readonly DirectProperty<NavMenuItem, bool> IsDarkStyleProperty =
AvaloniaProperty.RegisterDirect<NavMenuItem, bool>(nameof(IsDarkStyle),
o => o.IsDarkStyle,
(o, v) => o.IsDarkStyle = v);
internal double ActiveBarWidth internal double ActiveBarWidth
{ {
get => GetValue(ActiveBarWidthProperty); get => GetValue(ActiveBarWidthProperty);
@ -340,6 +345,14 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
set => SetAndRaise(InlineItemIndentUnitProperty, ref _inlineItemIndentUnit, value); set => SetAndRaise(InlineItemIndentUnitProperty, ref _inlineItemIndentUnit, value);
} }
private bool _isDarkStyle;
internal bool IsDarkStyle
{
get => _isDarkStyle;
set => SetAndRaise(IsDarkStyleProperty, ref _isDarkStyle, value);
}
#endregion #endregion
#region #region
@ -444,6 +457,7 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
private Border? _horizontalFrame; private Border? _horizontalFrame;
private IDisposable? _itemContainerThemeDisposable; private IDisposable? _itemContainerThemeDisposable;
private LayoutTransformControl? _childItemsLayoutTransform; private LayoutTransformControl? _childItemsLayoutTransform;
private Border? _headerFrame;
private bool _animating = false; private bool _animating = false;
static NavMenuItem() static NavMenuItem()
@ -639,7 +653,13 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
_popup.DependencyResolver = null; _popup.DependencyResolver = null;
} }
_popup = e.NameScope.Find<Popup>("PART_Popup"); _popup = e.NameScope.Find<Popup>(ThemeConstants.PopupPart);
_headerFrame = e.NameScope.Find<Border>(BaseNavMenuItemTheme.HeaderDecoratorPart);
if (_headerFrame is not null)
{
_headerFrame.PointerEntered += HandleHeaderFrameEnter;
_headerFrame.PointerExited += HandleHeaderFrameExit;
}
if (_popup != null) if (_popup != null)
{ {
@ -670,6 +690,34 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
} }
} }
private void HandleHeaderFrameEnter(object? sender, PointerEventArgs args)
{
if (!IsDarkStyle || Icon is null || HasSubMenu)
{
return;
}
Icon.IconMode = IconMode.Active;
}
private void HandleHeaderFrameExit(object? sender, PointerEventArgs args)
{
if (!IsDarkStyle || Icon is null || HasSubMenu)
{
return;
}
if (IsSelected)
{
Icon.IconMode = IconMode.Selected;
}
else
{
Icon.IconMode = IconMode.Normal;
}
}
protected override void UpdateDataValidation( protected override void UpdateDataValidation(
AvaloniaProperty property, AvaloniaProperty property,
BindingValueType state, BindingValueType state,
@ -816,10 +864,7 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
{ {
SetupHorizontalEffectiveIndicatorWidth(); SetupHorizontalEffectiveIndicatorWidth();
} }
else if (change.Property == IconProperty) else if (change.Property == ModeProperty)
{
SetupItemIcon();
} else if (change.Property == ModeProperty)
{ {
SetupItemContainerTheme(true); SetupItemContainerTheme(true);
} else if (change.Property == ItemCountProperty) } else if (change.Property == ItemCountProperty)
@ -832,14 +877,36 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
{ {
SetupEffectivePopupMinWidth(); SetupEffectivePopupMinWidth();
} }
if (change.Property == IconProperty ||
change.Property == IsDarkStyleProperty)
{
SetupItemIcon();
}
} }
private void SetupItemIcon() private void SetupItemIcon()
{ {
if (Icon is not null && Icon is PathIcon menuItemIcon) if (Icon is not null && Icon is PathIcon menuItemIcon)
{ {
menuItemIcon.Name = ThemeConstants.ItemIconPart; BindUtils.RelayBind(this, IsEnabledProperty, menuItemIcon, PathIcon.IsEnabledProperty);
TokenResourceBinder.CreateTokenBinding(menuItemIcon, PathIcon.WidthProperty, NavMenuTokenResourceKey.ItemIconSize);
TokenResourceBinder.CreateTokenBinding(menuItemIcon, PathIcon.HeightProperty, NavMenuTokenResourceKey.ItemIconSize);
if (IsDarkStyle)
{
TokenResourceBinder.CreateTokenBinding(menuItemIcon, PathIcon.NormalFilledBrushProperty, NavMenuTokenResourceKey.DarkItemColor);
TokenResourceBinder.CreateTokenBinding(menuItemIcon, PathIcon.SelectedFilledBrushProperty, NavMenuTokenResourceKey.DarkItemSelectedColor);
TokenResourceBinder.CreateTokenBinding(menuItemIcon, PathIcon.ActiveFilledBrushProperty, NavMenuTokenResourceKey.DarkItemHoverColor);
TokenResourceBinder.CreateTokenBinding(menuItemIcon, PathIcon.DisabledFilledBrushProperty, NavMenuTokenResourceKey.DarkItemDisabledColor);
}
else
{
TokenResourceBinder.CreateTokenBinding(menuItemIcon, PathIcon.NormalFilledBrushProperty, NavMenuTokenResourceKey.ItemColor);
TokenResourceBinder.CreateTokenBinding(menuItemIcon, PathIcon.SelectedFilledBrushProperty, NavMenuTokenResourceKey.ItemSelectedColor);
TokenResourceBinder.CreateTokenBinding(menuItemIcon, PathIcon.ActiveFilledBrushProperty, NavMenuTokenResourceKey.ItemHoverColor);
TokenResourceBinder.CreateTokenBinding(menuItemIcon, PathIcon.DisabledFilledBrushProperty, NavMenuTokenResourceKey.ItemDisabledColor);
}
} }
} }
@ -901,7 +968,7 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
/// <param name="e">The property change event.</param> /// <param name="e">The property change event.</param>
private void IconChanged(AvaloniaPropertyChangedEventArgs e) private void IconChanged(AvaloniaPropertyChangedEventArgs e)
{ {
var (oldValue, newValue) = e.GetOldAndNewValue<object?>(); var (oldValue, newValue) = e.GetOldAndNewValue<PathIcon?>();
if (oldValue is ILogical oldLogical) if (oldValue is ILogical oldLogical)
{ {
@ -988,7 +1055,7 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
} }
} }
private void OpenInlineItem() internal void OpenInlineItem()
{ {
if (_childItemsLayoutTransform is not null) if (_childItemsLayoutTransform is not null)
{ {
@ -1011,7 +1078,7 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
} }
} }
private void CloseInlineItem() internal void CloseInlineItem()
{ {
if (_childItemsLayoutTransform is not null) if (_childItemsLayoutTransform is not null)
{ {
@ -1030,6 +1097,7 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
_animating = false; _animating = false;
}); });
} }
} }
/// <summary> /// <summary>
@ -1116,6 +1184,7 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
if (element is NavMenuItem navMenuItem) if (element is NavMenuItem navMenuItem)
{ {
BindUtils.RelayBind(this, ModeProperty, navMenuItem, ModeProperty); BindUtils.RelayBind(this, ModeProperty, navMenuItem, ModeProperty);
BindUtils.RelayBind(this, IsDarkStyleProperty, navMenuItem, IsDarkStyleProperty);
} }
} }
@ -1130,6 +1199,20 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
} }
} }
} }
internal void RegenerateContainers()
{
foreach (var item in Items)
{
if (item is NavMenuItem childNavMenuItem)
{
childNavMenuItem.RegenerateContainers();
}
}
ItemsPanel = new FuncTemplate<Panel?>(() => new StackPanel());
RefreshContainers();
}
private static int CalculateDistanceFromLogicalParent<T>(ILogical? logical, int @default = -1) where T : class private static int CalculateDistanceFromLogicalParent<T>(ILogical? logical, int @default = -1) where T : class
{ {
@ -1143,4 +1226,5 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
return logical != null ? result : @default; return logical != null ? result : @default;
} }
} }

View File

@ -13,6 +13,7 @@ namespace AtomUI.Controls;
internal class NavMenuItemTheme : BaseNavMenuItemTheme internal class NavMenuItemTheme : BaseNavMenuItemTheme
{ {
public const string ItemsPresenterPart = "PART_ItemsPresenter"; public const string ItemsPresenterPart = "PART_ItemsPresenter";
public const string PopupFramePart = "PART_PopupFrame";
public NavMenuItemTheme() public NavMenuItemTheme()
: base(typeof(NavMenuItem)) : base(typeof(NavMenuItem))
@ -37,12 +38,16 @@ internal class NavMenuItemTheme : BaseNavMenuItemTheme
Name = ThemeConstants.PopupPart, Name = ThemeConstants.PopupPart,
WindowManagerAddShadowHint = false, WindowManagerAddShadowHint = false,
IsLightDismissEnabled = false, IsLightDismissEnabled = false,
Placement = PlacementMode.RightEdgeAlignedTop Placement = PlacementMode.RightEdgeAlignedTop,
}; };
var border = new Border(); var border = new Border()
TokenResourceBinder.CreateTokenBinding(border, Border.BackgroundProperty, {
GlobalTokenResourceKey.ColorBgContainer); Name = PopupFramePart
};
TokenResourceBinder.CreateTokenBinding(popup, Popup.MarginToAnchorProperty,
NavMenuTokenResourceKey.TopLevelItemPopupMarginToAnchor);
TokenResourceBinder.CreateTokenBinding(border, Border.CornerRadiusProperty, TokenResourceBinder.CreateTokenBinding(border, Border.CornerRadiusProperty,
NavMenuTokenResourceKey.MenuPopupBorderRadius); NavMenuTokenResourceKey.MenuPopupBorderRadius);
TokenResourceBinder.CreateTokenBinding(border, Layoutable.MinWidthProperty, TokenResourceBinder.CreateTokenBinding(border, Layoutable.MinWidthProperty,
@ -70,7 +75,7 @@ internal class NavMenuItemTheme : BaseNavMenuItemTheme
NavMenuTokenResourceKey.TopLevelItemPopupMarginToAnchor); NavMenuTokenResourceKey.TopLevelItemPopupMarginToAnchor);
TokenResourceBinder.CreateTokenBinding(popup, Popup.MaskShadowsProperty, TokenResourceBinder.CreateTokenBinding(popup, Popup.MaskShadowsProperty,
NavMenuTokenResourceKey.MenuPopupBoxShadows); NavMenuTokenResourceKey.MenuPopupBoxShadows);
CreateTemplateParentBinding(popup, Avalonia.Controls.Primitives.Popup.IsOpenProperty, CreateTemplateParentBinding(popup, Popup.IsOpenProperty,
NavMenuItem.IsSubMenuOpenProperty, BindingMode.TwoWay); NavMenuItem.IsSubMenuOpenProperty, BindingMode.TwoWay);
return popup; return popup;
@ -82,5 +87,19 @@ internal class NavMenuItemTheme : BaseNavMenuItemTheme
var itemsPanelStyle = new Style(selector => selector.Nesting().Template().Name(ItemsPresenterPart).Child().OfType<StackPanel>()); var itemsPanelStyle = new Style(selector => selector.Nesting().Template().Name(ItemsPresenterPart).Child().OfType<StackPanel>());
itemsPanelStyle.Add(StackPanel.SpacingProperty, NavMenuTokenResourceKey.VerticalItemsPanelSpacing); itemsPanelStyle.Add(StackPanel.SpacingProperty, NavMenuTokenResourceKey.VerticalItemsPanelSpacing);
Add(itemsPanelStyle); Add(itemsPanelStyle);
{
var popupFrameStyle = new Style(selector => selector.Nesting().Template().Name(PopupFramePart));
popupFrameStyle.Add(Border.BackgroundProperty, GlobalTokenResourceKey.ColorBgContainer);
Add(popupFrameStyle);
}
var darkCommonStyle = new Style(selector => selector.Nesting().PropertyEquals(NavMenuItem.IsDarkStyleProperty, true));
{
var popupFrameStyle = new Style(selector => selector.Nesting().Template().Name(PopupFramePart));
popupFrameStyle.Add(Border.BackgroundProperty, NavMenuTokenResourceKey.DarkItemBg);
darkCommonStyle.Add(popupFrameStyle);
}
Add(darkCommonStyle);
} }
} }

View File

@ -72,7 +72,6 @@ internal class NavMenuTheme : BaseControlTheme
var commonStyle = new Style(selector => selector.Nesting()); var commonStyle = new Style(selector => selector.Nesting());
commonStyle.Add(TemplatedControl.BorderBrushProperty, GlobalTokenResourceKey.ColorBorderSecondary); commonStyle.Add(TemplatedControl.BorderBrushProperty, GlobalTokenResourceKey.ColorBorderSecondary);
commonStyle.Add(TemplatedControl.CornerRadiusProperty, GlobalTokenResourceKey.BorderRadius);
var horizontalStyle = new Style(selector => selector.Nesting().Class(NavMenu.HorizontalModePC)); var horizontalStyle = new Style(selector => selector.Nesting().Class(NavMenu.HorizontalModePC));
horizontalStyle.Add(NavMenu.BackgroundProperty, GlobalTokenResourceKey.ColorBgContainer); horizontalStyle.Add(NavMenu.BackgroundProperty, GlobalTokenResourceKey.ColorBgContainer);
@ -92,6 +91,7 @@ internal class NavMenuTheme : BaseControlTheme
var verticalOrInlineStyle = new Style(selector => Selectors.Or(selector.Nesting().Class(NavMenu.VerticalModePC), var verticalOrInlineStyle = new Style(selector => Selectors.Or(selector.Nesting().Class(NavMenu.VerticalModePC),
selector.Nesting().Class(NavMenu.InlineModePC))); selector.Nesting().Class(NavMenu.InlineModePC)));
verticalOrInlineStyle.Add(NavMenu.PaddingProperty, NavMenuTokenResourceKey.VerticalMenuContentPadding);
verticalOrInlineStyle.Add(NavMenu.HorizontalAlignmentProperty, HorizontalAlignment.Left); verticalOrInlineStyle.Add(NavMenu.HorizontalAlignmentProperty, HorizontalAlignment.Left);
verticalOrInlineStyle.Add(NavMenu.VerticalAlignmentProperty, VerticalAlignment.Stretch); verticalOrInlineStyle.Add(NavMenu.VerticalAlignmentProperty, VerticalAlignment.Stretch);
verticalOrInlineStyle.Add(NavMenu.BackgroundProperty, GlobalTokenResourceKey.ColorBgContainer); verticalOrInlineStyle.Add(NavMenu.BackgroundProperty, GlobalTokenResourceKey.ColorBgContainer);

View File

@ -190,6 +190,11 @@ internal class NavMenuToken : AbstractControlDesignToken
/// </summary> /// </summary>
public double VerticalItemsPanelSpacing { get; set; } public double VerticalItemsPanelSpacing { get; set; }
/// <summary>
/// 垂直面板的内容内间距
/// </summary>
public Thickness VerticalMenuContentPadding { get; set; }
/// <summary> /// <summary>
/// 菜单项内部元素边距 /// 菜单项内部元素边距
/// </summary> /// </summary>
@ -444,7 +449,7 @@ internal class NavMenuToken : AbstractControlDesignToken
MenuPopupMinWidth = 160d; MenuPopupMinWidth = 160d;
MenuPopupMaxWidth = 800d; MenuPopupMaxWidth = 800d;
MenuPopupMaxHeight = ItemHeight * 30; MenuPopupMaxHeight = ItemHeight * 30;
TopLevelItemPopupMarginToAnchor = _globalToken.MarginXXS; TopLevelItemPopupMarginToAnchor = _globalToken.MarginXS;
MenuPopupBg = _globalToken.ColorBgElevated; MenuPopupBg = _globalToken.ColorBgElevated;
MenuPopupBorderRadius = _globalToken.BorderRadiusLG; MenuPopupBorderRadius = _globalToken.BorderRadiusLG;
@ -452,6 +457,7 @@ internal class NavMenuToken : AbstractControlDesignToken
MenuPopupBoxShadows = _globalToken.BoxShadowsSecondary; MenuPopupBoxShadows = _globalToken.BoxShadowsSecondary;
VerticalItemsPanelSpacing = _globalToken.MarginXXS; VerticalItemsPanelSpacing = _globalToken.MarginXXS;
InlineItemIndentUnit = ItemHeight / 2; InlineItemIndentUnit = ItemHeight / 2;
VerticalMenuContentPadding = new Thickness(_globalToken.PaddingXXS);
} }
} }

View File

@ -471,16 +471,16 @@ public sealed class PathIcon : Control, ICustomHitTest
base.OnAttachedToLogicalTree(e); base.OnAttachedToLogicalTree(e);
SetupTransitions(); SetupTransitions();
SetupRotateAnimation(); SetupRotateAnimation();
if (_sourceGeometriesData.Count == 0)
{
BuildSourceRenderData();
SetupFilledBrush();
}
} }
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{ {
base.OnAttachedToVisualTree(e); base.OnAttachedToVisualTree(e);
if (_sourceGeometriesData.Count == 0)
{
BuildSourceRenderData();
SetupFilledBrush();
}
if (_animation is not null && _animationCancellationTokenSource is null) if (_animation is not null && _animationCancellationTokenSource is null)
{ {
_animationCancellationTokenSource = new CancellationTokenSource(); _animationCancellationTokenSource = new CancellationTokenSource();
@ -516,28 +516,25 @@ public sealed class PathIcon : Control, ICustomHitTest
protected override Size ArrangeOverride(Size finalSize) protected override Size ArrangeOverride(Size finalSize)
{ {
if (_sourceGeometriesData.Count != 0) _transforms.Clear();
// This should probably use GetRenderBounds(strokeThickness) but then the calculations
// will multiply the stroke thickness as well, which isn't correct.
for (var i = 0; i < _sourceGeometriesData.Count; i++)
{ {
// This should probably use GetRenderBounds(strokeThickness) but then the calculations var sourceGeometry = _sourceGeometriesData[i];
// will multiply the stroke thickness as well, which isn't correct. var (_, transform) = CalculateSizeAndTransform(finalSize, sourceGeometry.Bounds);
for (var i = 0; i < _sourceGeometriesData.Count; i++) _transforms.Insert(i, transform);
{
var sourceGeometry = _sourceGeometriesData[i];
var (_, transform) = CalculateSizeAndTransform(finalSize, sourceGeometry.Bounds);
_transforms.Insert(i, transform);
}
return finalSize;
} }
return default; return finalSize;
} }
public override void Render(DrawingContext context) public override void Render(DrawingContext context)
{ {
if (_sourceGeometriesData.Count > 0 && if (IsVisible &&
DesiredSize.Width > 0 && _sourceGeometriesData.Count > 0 &&
DesiredSize.Width > 0) Bounds.Width > 0 &&
Bounds.Width > 0)
{ {
for (var i = 0; i < _sourceGeometriesData.Count; i++) for (var i = 0; i < _sourceGeometriesData.Count; i++)
{ {
@ -577,7 +574,7 @@ public sealed class PathIcon : Control, ICustomHitTest
{ {
fillBrush = FilledBrush; fillBrush = FilledBrush;
} }
using var state = context.PushTransform(_transforms[i]); using var state = context.PushTransform(_transforms[i]);
context.DrawGeometry(fillBrush, null, renderedGeometry); context.DrawGeometry(fillBrush, null, renderedGeometry);
} }

View File

@ -70,8 +70,8 @@ public class Popup : AvaloniaPopup
private IDisposable? _selfLightDismissDisposable; private IDisposable? _selfLightDismissDisposable;
private bool _initialized; private bool _initialized;
private ManagedPopupPositionerInfo? _managedPopupPositioner; private ManagedPopupPositionerInfo? _managedPopupPositioner;
protected bool _animating;
private bool _isNeedFlip = true; private bool _isNeedFlip = true;
protected bool _animating;
// 当鼠标移走了,但是打开动画还没完成,我们需要记录下来这个信号 // 当鼠标移走了,但是打开动画还没完成,我们需要记录下来这个信号
internal bool RequestCloseWhereAnimationCompleted { get; set; } internal bool RequestCloseWhereAnimationCompleted { get; set; }

View File

@ -309,7 +309,7 @@ public class ToggleSwitch : ToggleButton,
_switchKnob.KnobSize = new Size(handleSize, handleSize); _switchKnob.KnobSize = new Size(handleSize, handleSize);
} }
CalculateElementsOffset(DesiredSize); CalculateElementsOffset(Bounds.Size);
} }
protected override void OnPointerPressed(PointerPressedEventArgs e) protected override void OnPointerPressed(PointerPressedEventArgs e)
@ -524,7 +524,7 @@ public class ToggleSwitch : ToggleButton,
CollectStyleState(); CollectStyleState();
if (e.Property == IsCheckedProperty) if (e.Property == IsCheckedProperty)
{ {
CalculateElementsOffset(DesiredSize); CalculateElementsOffset(Bounds.Size);
WaveSpiritAdorner.ShowWaveAdorner(this, WaveType.PillWave); WaveSpiritAdorner.ShowWaveAdorner(this, WaveType.PillWave);
} }
} }
@ -546,12 +546,12 @@ public class ToggleSwitch : ToggleButton,
private Rect GrooveRect() private Rect GrooveRect()
{ {
return new Rect(new Point(0, 0), DesiredSize); return new Rect(new Point(0, 0), Bounds.Size);
} }
private Rect HandleRect() private Rect HandleRect()
{ {
return HandleRect(IsChecked.HasValue && IsChecked.Value, DesiredSize); return HandleRect(IsChecked.HasValue && IsChecked.Value, Bounds.Size);
} }
private Rect HandleRect(bool isChecked, Size controlSize) private Rect HandleRect(bool isChecked, Size controlSize)