feat: use popup instead of flyout. improve demo.

This commit is contained in:
rabbitism
2024-02-14 00:22:09 +08:00
parent 7051521040
commit ec41a8228f
6 changed files with 167 additions and 60 deletions

View File

@@ -0,0 +1,36 @@
using System;
using System.Globalization;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Data.Converters;
using Avalonia.Media;
namespace Ursa.Demo.Converters;
public class IconNameToPathConverter: IValueConverter
{
private string[] paths = new[]
{
"M12,4A4,4 0 0,1 16,8A4,4 0 0,1 12,12A4,4 0 0,1 8,8A4,4 0 0,1 12,4M12,14C16.42,14 20,15.79 20,18V20H4V18C4,15.79 7.58,14 12,14Z",
"M16 12L9 2L2 12H3.86L0 18H7V22H11V18H18L14.14 12H16M20.14 12H22L15 2L12.61 5.41L17.92 13H15.97L19.19 18H24L20.14 12M13 19H17V22H13V19Z",
"M15,9H5V5H15M12,19A3,3 0 0,1 9,16A3,3 0 0,1 12,13A3,3 0 0,1 15,16A3,3 0 0,1 12,19M17,3H5C3.89,3 3,3.9 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V7L17,3Z",
"M5 21C3.9 21 3 20.1 3 19V5C3 3.9 3.9 3 5 3H19C20.1 3 21 3.9 21 5V19C21 20.1 20.1 21 19 21H5M15.3 16L13.2 13.9L17 10L14.2 7.2L10.4 11.1L8.2 8.9V16H15.3Z",
"M16,9V7H12V12.5C11.58,12.19 11.07,12 10.5,12A2.5,2.5 0 0,0 8,14.5A2.5,2.5 0 0,0 10.5,17A2.5,2.5 0 0,0 13,14.5V9H16M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2Z",
"M16.67,4H15V2H9V4H7.33A1.33,1.33 0 0,0 6,5.33V20.67C6,21.4 6.6,22 7.33,22H16.67A1.33,1.33 0 0,0 18,20.67V5.33C18,4.6 17.4,4 16.67,4Z",
"M12 2C6.5 2 2 6.5 2 12C2 17.5 6.5 22 12 22C17.5 22 22 17.5 22 12S17.5 2 12 2M12.5 13H11V7H12.5V11.3L16.2 9.2L17 10.5L12.5 13Z"
};
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value is int i)
{
string s = paths[i % paths.Length];
return StreamGeometry.Parse(s);
}
return AvaloniaProperty.UnsetValue;
}
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
return AvaloniaProperty.UnsetValue;
}
}

View File

@@ -6,14 +6,17 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:u="https://irihi.tech/ursa" xmlns:u="https://irihi.tech/ursa"
xmlns:vm="using:Ursa.Demo.ViewModels" xmlns:vm="using:Ursa.Demo.ViewModels"
xmlns:converters="clr-namespace:Ursa.Demo.Converters"
d:DesignHeight="450" d:DesignHeight="450"
d:DesignWidth="800" d:DesignWidth="800"
x:CompileBindings="True" x:CompileBindings="True"
x:DataType="vm:NavMenuDemoViewModel" x:DataType="vm:NavMenuDemoViewModel"
mc:Ignorable="d"> mc:Ignorable="d">
<UserControl.Resources>
<converters:IconNameToPathConverter x:Key="IconConverter"/>
</UserControl.Resources>
<ScrollViewer> <ScrollViewer>
<StackPanel HorizontalAlignment="Left"> <StackPanel HorizontalAlignment="Left">
<!--
<TextBlock Text="{ReflectionBinding #menu2.SelectedItem.Header}"></TextBlock> <TextBlock Text="{ReflectionBinding #menu2.SelectedItem.Header}"></TextBlock>
<u:NavMenu Name="menu2" Width="300" IsHorizontalCollapsed="{Binding #collapse.IsChecked}"> <u:NavMenu Name="menu2" Width="300" IsHorizontalCollapsed="{Binding #collapse.IsChecked}">
<u:NavMenuItem Header="Menu 1"> <u:NavMenuItem Header="Menu 1">
@@ -45,36 +48,36 @@
</u:NavMenuItem> </u:NavMenuItem>
</u:NavMenu> </u:NavMenu>
<u:Divider Content="Divider" /> <u:Divider Content="Divider" />
-->
<TextBlock Text="{ReflectionBinding #menu.SelectedItem.Header}" /> <TextBlock Text="{ReflectionBinding #menu.SelectedItem.Header}" />
<ToggleButton Name="collapse">Collapse</ToggleButton> <ToggleButton Name="collapse">Collapse</ToggleButton>
<u:NavMenu <Border Theme="{DynamicResource CardBorder}" HorizontalAlignment="Left" Padding="0">
Name="menu" <u:NavMenu
Width="300" Name="menu"
HeaderBinding="{Binding Header}" HeaderBinding="{Binding Header}"
IconBinding="{Binding}" IconBinding="{Binding IconIndex}"
IsHorizontalCollapsed="{Binding #collapse.IsChecked, Mode=OneWay}" IsHorizontalCollapsed="{Binding #collapse.IsChecked, Mode=OneWay}"
ItemsSource="{Binding MenuItems}" ItemsSource="{Binding MenuItems}"
SubMenuBinding="{Binding Children}"> SubMenuBinding="{Binding Children}">
<u:NavMenu.Styles> <u:NavMenu.IconTemplate>
<Style Selector="Rectangle"> <DataTemplate DataType="{x:Type x:Int32}">
<Setter Property="Fill" Value="Blue" /> <u:TwoTonePathIcon
</Style> Width="18"
<Style Selector="Rectangle.Active"> Height="18"
<Setter Property="Fill" Value="Red" /> Data="{Binding Converter={StaticResource IconConverter}}"
</Style> StrokeBrush="{DynamicResource SemiBlue2}"
</u:NavMenu.Styles> Foreground="{DynamicResource SemiBlue2}"
<u:NavMenu.IconTemplate> ActiveStrokeBrush="{DynamicResource SemiBlue6}"
<DataTemplate> ActiveForeground="{DynamicResource SemiBlue6}"
<Rectangle IsActive="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=u:NavMenuItem}, Path=IsHighlighted, Mode=TwoWay}">
Width="10" </u:TwoTonePathIcon>
Height="10" </DataTemplate>
Classes.Active="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=u:NavMenuItem}, Path=IsHighlighted, Mode=TwoWay}"> </u:NavMenu.IconTemplate>
</Rectangle> </u:NavMenu>
</DataTemplate> </Border>
</u:NavMenu.IconTemplate>
</u:NavMenu>
</StackPanel> </StackPanel>
</ScrollViewer> </ScrollViewer>
</UserControl> </UserControl>

View File

@@ -1,4 +1,5 @@
using System.Collections.ObjectModel; using System;
using System.Collections.ObjectModel;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Input; using System.Windows.Input;
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
@@ -58,11 +59,13 @@ public class MenuItem
{ {
public string? Header { get; set; } public string? Header { get; set; }
public string? Icon { get; set; } public string? Icon { get; set; }
public int IconIndex { get; set; }
public ICommand NavigationCommand { get; set; } public ICommand NavigationCommand { get; set; }
static Random r = new Random();
public MenuItem() public MenuItem()
{ {
NavigationCommand = new AsyncRelayCommand(OnNavigate); NavigationCommand = new AsyncRelayCommand(OnNavigate);
IconIndex = r.Next(100);
} }
private async Task OnNavigate() private async Task OnNavigate()

View File

@@ -17,6 +17,10 @@
</ScrollViewer> </ScrollViewer>
</ControlTemplate> </ControlTemplate>
</Setter> </Setter>
<Style Selector="^:horizontal-collapsed">
<Setter Property="Width" Value="{x:Static x:Double.NaN}"></Setter>
<Setter Property="HorizontalAlignment" Value="Left"></Setter>
</Style>
</ControlTheme> </ControlTheme>
<ControlTemplate x:Key="DefaultNavMenuItemTemplate" TargetType="u:NavMenuItem"> <ControlTemplate x:Key="DefaultNavMenuItemTemplate" TargetType="u:NavMenuItem">
@@ -24,20 +28,18 @@
<Border <Border
Name="PART_Border" Name="PART_Border"
Grid.Row="0" Grid.Row="0"
MinHeight="32"
Background="{TemplateBinding u:NavMenuItem.Background}" Background="{TemplateBinding u:NavMenuItem.Background}"
CornerRadius="4"> CornerRadius="4">
<Border.ContextFlyout> <Grid
<Flyout> Name="PART_RootGrid"
<Border> HorizontalAlignment="Stretch"
<StackPanel Name="PART_OverflowPanel"></StackPanel> VerticalAlignment="Center">
</Border>
</Flyout>
</Border.ContextFlyout>
<Grid HorizontalAlignment="Stretch">
<Grid.Margin> <Grid.Margin>
<MultiBinding Converter="{StaticResource NavMarginConverter}"> <MultiBinding Converter="{StaticResource NavMarginConverter}">
<Binding Path="SubMenuIndent" RelativeSource="{RelativeSource TemplatedParent}" /> <Binding Path="SubMenuIndent" RelativeSource="{RelativeSource TemplatedParent}" />
<Binding Path="Level" RelativeSource="{RelativeSource TemplatedParent}" /> <Binding Path="Level" RelativeSource="{RelativeSource TemplatedParent}" />
<Binding Path="IsHorizontalCollapsed" RelativeSource="{RelativeSource TemplatedParent}" />
</MultiBinding> </MultiBinding>
</Grid.Margin> </Grid.Margin>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
@@ -46,12 +48,14 @@
<ColumnDefinition Width="Auto" SharedSizeGroup="Expander" /> <ColumnDefinition Width="Auto" SharedSizeGroup="Expander" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<ContentPresenter <ContentPresenter
Name="PART_IconPresenter"
Padding="8" Padding="8"
HorizontalAlignment="Left" HorizontalAlignment="Left"
Background="Transparent" Background="Transparent"
Content="{Binding Icon, RelativeSource={RelativeSource TemplatedParent}}" Content="{Binding Icon, RelativeSource={RelativeSource TemplatedParent}}"
ContentTemplate="{Binding IconTemplate, RelativeSource={RelativeSource TemplatedParent}}" /> ContentTemplate="{Binding IconTemplate, RelativeSource={RelativeSource TemplatedParent}}" />
<ContentPresenter <ContentPresenter
Name="PART_HeaderPresenter"
Grid.Column="1" Grid.Column="1"
Padding="0,8" Padding="0,8"
Background="Transparent" Background="Transparent"
@@ -71,24 +75,44 @@
</Transitions> </Transitions>
</PathIcon.Transitions> </PathIcon.Transitions>
</PathIcon> </PathIcon>
<Popup
Name="PART_Popup"
Grid.Column="0"
IsLightDismissEnabled="True"
Placement="RightEdgeAlignedTop"
PlacementTarget="{Binding #PART_Border}">
<Border
Margin="{DynamicResource NavigationMenuItemFlyoutMargin}"
Padding="{DynamicResource MenuFlyoutPadding}"
HorizontalAlignment="Stretch"
Background="{DynamicResource MenuFlyoutBackground}"
BorderBrush="{DynamicResource MenuFlyoutBorderBrush}"
BorderThickness="{DynamicResource MenuFlyoutBorderThickness}"
BoxShadow="{DynamicResource MenuFlyoutBorderBoxShadow}"
CornerRadius="{DynamicResource MenuFlyoutCornerRadius}">
<StackPanel Name="PART_OverflowPanel" />
</Border>
</Popup>
</Grid> </Grid>
</Border> </Border>
<ItemsPresenter <ItemsPresenter
Name="PART_ItemsPresenter" Name="PART_ItemsPresenter"
Grid.Row="1" Grid.Row="1"
Margin="0,4,0,0"
VerticalAlignment="Top" VerticalAlignment="Top"
Grid.IsSharedSizeScope="True" Grid.IsSharedSizeScope="True"
ItemsPanel="{Binding ItemsPanel, RelativeSource={RelativeSource TemplatedParent}}" ItemsPanel="{Binding ItemsPanel, RelativeSource={RelativeSource TemplatedParent}}"
RenderTransformOrigin="0.5,0" > RenderTransformOrigin="0.5,0">
<ItemsPresenter.Transitions> <ItemsPresenter.Transitions>
<Transitions> <Transitions>
<DoubleTransition Property="Height" Duration="0.1"></DoubleTransition> <DoubleTransition Property="Height" Duration="0.1" />
<TransformOperationsTransition Property="RenderTransform" Duration="0.1" /> <TransformOperationsTransition Property="RenderTransform" Duration="0.1" />
</Transitions> </Transitions>
</ItemsPresenter.Transitions> </ItemsPresenter.Transitions>
</ItemsPresenter> </ItemsPresenter>
</Grid> </Grid>
</ControlTemplate> </ControlTemplate>
<!--
<ControlTemplate x:Key="TopLevelCollapsedNavMenuItemTemplate" TargetType="u:NavMenuItem"> <ControlTemplate x:Key="TopLevelCollapsedNavMenuItemTemplate" TargetType="u:NavMenuItem">
<Border <Border
Name="PART_Border" Name="PART_Border"
@@ -156,33 +180,67 @@
</Grid> </Grid>
</Border> </Border>
</ControlTemplate> </ControlTemplate>
-->
<ControlTheme x:Key="{x:Type u:NavMenuItem}" TargetType="u:NavMenuItem"> <ControlTheme x:Key="{x:Type u:NavMenuItem}" TargetType="u:NavMenuItem">
<Setter Property="Background" Value="Transparent" /> <Setter Property="Background" Value="Transparent" />
<Setter Property="HorizontalAlignment" Value="Stretch"></Setter>
<Setter Property="ItemsPanel"> <Setter Property="ItemsPanel">
<ItemsPanelTemplate> <ItemsPanelTemplate>
<u:OverflowStackPanel></u:OverflowStackPanel> <u:OverflowStackPanel />
</ItemsPanelTemplate> </ItemsPanelTemplate>
</Setter> </Setter>
<Style Selector="^"> <Setter Property="Template" Value="{StaticResource DefaultNavMenuItemTemplate}" />
<Setter Property="Template" Value="{StaticResource DefaultNavMenuItemTemplate}" /> <Style Selector="^:selected">
<Style Selector="^:selected"> <Setter Property="Background" Value="{DynamicResource NavigationMenuItemSelectedBackground}" />
<Setter Property="Background" Value="{DynamicResource NavigationMenuItemSelectedBackground}" /> </Style>
</Style> <Style Selector="^:vertical-collapsed /template/ ItemsPresenter#PART_ItemsPresenter">
<Style Selector="^:vertical-collapsed /template/ ItemsPresenter#PART_ItemsPresenter"> <Setter Property="Height" Value="0" />
<Setter Property="Height" Value="0"></Setter> <Setter Property="RenderTransform" Value="scale(1,0)" />
<Setter Property="RenderTransform" Value="scale(1,0)" /> </Style>
</Style> <Style Selector="^:vertical-collapsed /template/ PathIcon#PART_ExpanderIcon">
<Style Selector="^:vertical-collapsed /template/ PathIcon#PART_ExpanderIcon"> <Setter Property="RenderTransform" Value="rotate(180deg)" />
<Setter Property="RenderTransform" Value="rotate(180deg)" /> </Style>
</Style> <Style Selector="^:empty /template/ PathIcon#PART_ExpanderIcon">
<Style Selector="^:empty /template/ PathIcon#PART_ExpanderIcon"> <Setter Property="IsVisible" Value="False" />
<Setter Property="IsVisible" Value="False" /> </Style>
<Style Selector="^ /template/ Border#PART_Border:pointerover">
<Setter Property="Background" Value="{DynamicResource NavigationMenuItemPointeroverBackground}" />
</Style>
<Style Selector="^:horizontal-collapsed:first-level">
<Setter Property="HorizontalAlignment" Value="Left"></Setter>
<Style Selector="^ /template/ Border#PART_Border">
<Setter Property="ToolTip.Tip" Value="{Binding Header, RelativeSource={RelativeSource TemplatedParent}}" />
<Setter Property="ToolTip.ShowDelay" Value="0" />
<Setter Property="HorizontalAlignment" Value="Left" />
</Style> </Style>
<Style Selector="^ /template/ Border#PART_Border:pointerover"> <Style Selector="^ /template/ Border#PART_Border:pointerover">
<Setter Property="Background" Value="{DynamicResource NavigationMenuItemPointeroverBackground}" /> <Setter Property="Background" Value="{DynamicResource NavigationMenuItemPointeroverBackground}" />
</Style> </Style>
<Style Selector="^ /template/ ContentPresenter#PART_IconPresenter">
<Setter Property="Grid.ColumnSpan" Value="3" />
<Setter Property="Margin" Value="0 8"></Setter>
<Setter Property="HorizontalAlignment" Value="Center" />
</Style>
<Style Selector="^ /template/ ContentPresenter#PART_HeaderPresenter">
<Setter Property="IsVisible" Value="False" />
</Style>
<Style Selector="^ /template/ PathIcon#PART_ExpanderIcon">
<Setter Property="IsVisible" Value="False" />
</Style>
<Style Selector="^ /template/ ItemsPresenter#PART_ItemsPresenter">
<Setter Property="IsVisible" Value="False" />
</Style>
<Style Selector="^ /template/ Grid#PART_RootGrid">
<Setter Property="HorizontalAlignment" Value="Left" />
</Style>
</Style> </Style>
<!-- <Style Selector="^:horizontal-collapsed:not(:first-level)">
<Style Selector="^ /template/ PathIcon#PART_ExpanderIcon">
<Setter Property="RenderTransform" Value="rotate(-90deg)" />
</Style>
</Style>
<!--
<Style Selector="^:horizontal-collapsed:first-level"> <Style Selector="^:horizontal-collapsed:first-level">
<Setter Property="Template" Value="{StaticResource TopLevelCollapsedNavMenuItemTemplate}" /> <Setter Property="Template" Value="{StaticResource TopLevelCollapsedNavMenuItemTemplate}" />
<Style Selector="^ /template/ Border#PART_Border"> <Style Selector="^ /template/ Border#PART_Border">

View File

@@ -8,9 +8,9 @@ public class NavMenuMarginConverter: IMultiValueConverter
{ {
public object? Convert(IList<object?> values, Type targetType, object? parameter, CultureInfo culture) public object? Convert(IList<object?> values, Type targetType, object? parameter, CultureInfo culture)
{ {
if (values[0] is double indent && values[1] is int level) if (values[0] is double indent && values[1] is int level && values[2] is bool b)
{ {
return new Thickness(indent * (level-1), 0, 0, 0); return b ? new Thickness() : new Thickness(indent * (level-1), 0, 0, 0);
} }
return AvaloniaProperty.UnsetValue; return AvaloniaProperty.UnsetValue;
} }

View File

@@ -235,9 +235,16 @@ public class NavMenuItem: HeaderedSelectingItemsControl
} }
else else
{ {
if (_border?.ContextFlyout is not null) if (_popup is not null)
{ {
_border.ContextFlyout.ShowAt(this); if (_popup.IsOpen)
{
_popup.Close();
}
else
{
_popup.Open();
}
} }
} }
} }