feat: support popup on closed mode.
This commit is contained in:
@@ -111,10 +111,15 @@
|
||||
Name="PART_IconPresenter"
|
||||
Grid.Column="1"
|
||||
MinWidth="24"
|
||||
Margin="{TemplateBinding Level,
|
||||
Converter={StaticResource MarginConverter}}"
|
||||
Content="{TemplateBinding Icon}"
|
||||
ContentTemplate="{TemplateBinding IconTemplate}" />
|
||||
ContentTemplate="{TemplateBinding IconTemplate}">
|
||||
<ContentPresenter.Margin>
|
||||
<MultiBinding Converter="{StaticResource MarginConverter}">
|
||||
<Binding Path="Level" RelativeSource="{RelativeSource TemplatedParent}" />
|
||||
<Binding Path="IsClosed" RelativeSource="{RelativeSource TemplatedParent}" />
|
||||
</MultiBinding>
|
||||
</ContentPresenter.Margin>
|
||||
</ContentPresenter>
|
||||
<ContentPresenter
|
||||
Name="PART_HeaderPresenter"
|
||||
Grid.Column="2"
|
||||
@@ -170,7 +175,7 @@
|
||||
<Setter Property="u:NavigationMenuItem.Background" Value="{DynamicResource NavigationMenuItemSelectedBackground}" />
|
||||
</Style>
|
||||
|
||||
<Style Selector="^:closed">
|
||||
<Style Selector="^:empty:closed:top-level">
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate TargetType="u:NavigationMenuItem">
|
||||
<Border
|
||||
@@ -179,15 +184,16 @@
|
||||
Padding="8"
|
||||
HorizontalAlignment="Center"
|
||||
Background="{DynamicResource NavigationMenuItemDefaultBackground}"
|
||||
CornerRadius="6">
|
||||
CornerRadius="6"
|
||||
ToolTip.ShowDelay="0"
|
||||
ToolTip.Tip="{TemplateBinding Header}">
|
||||
<ContentPresenter
|
||||
Name="PART_IconPresenter"
|
||||
Grid.Column="1"
|
||||
MinWidth="24"
|
||||
Margin="0"
|
||||
Content="{TemplateBinding Icon}"
|
||||
ContentTemplate="{TemplateBinding IconTemplate}"
|
||||
ToolTip.ShowDelay="0"
|
||||
ToolTip.Tip="{TemplateBinding Header}" />
|
||||
ContentTemplate="{TemplateBinding IconTemplate}" />
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter>
|
||||
@@ -199,7 +205,87 @@
|
||||
</Style>
|
||||
</Style>
|
||||
|
||||
<Style Selector="^:collapsed /template/ ItemsPresenter#PART_ItemsPresenter">
|
||||
<Style Selector="^:not(:empty):closed">
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate TargetType="u:NavigationMenuItem">
|
||||
<Panel>
|
||||
<Border
|
||||
Name="PART_HeaderBorder"
|
||||
Margin="0,2"
|
||||
Padding="8"
|
||||
HorizontalAlignment="Center"
|
||||
Background="{DynamicResource NavigationMenuItemDefaultBackground}"
|
||||
CornerRadius="6">
|
||||
<Grid ColumnDefinitions="Auto, *, Auto">
|
||||
<ContentPresenter
|
||||
Name="PART_IconPresenter"
|
||||
Grid.Column="0"
|
||||
MinWidth="24"
|
||||
Margin="0"
|
||||
Content="{TemplateBinding Icon}"
|
||||
ContentTemplate="{TemplateBinding IconTemplate}" />
|
||||
<ContentPresenter
|
||||
Name="PART_HeaderPresenter"
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
Background="{TemplateBinding Background}"
|
||||
Content="{TemplateBinding Header}"
|
||||
ContentTemplate="{TemplateBinding HeaderTemplate}"
|
||||
IsVisible="{TemplateBinding IsTopLevelMenuItem,
|
||||
Converter={x:Static BoolConverters.Not}}" />
|
||||
<PathIcon
|
||||
Grid.Column="2"
|
||||
Width="8"
|
||||
Height="8"
|
||||
Margin="8,0"
|
||||
Data="M4.08045 7.59809C4.66624 7.01231 5.61599 7.01231 6.20177 7.59809L11.8586 13.2549L17.5155 7.59809C18.1013 7.01231 19.051 7.01231 19.6368 7.59809C20.2226 8.18388 20.2226 9.13363 19.6368 9.71941L12.9193 16.4369C12.3335 17.0227 11.3838 17.0227 10.798 16.4369L4.08045 9.71941C3.49467 9.13363 3.49467 8.18388 4.08045 7.59809Z"
|
||||
Foreground="{DynamicResource NavigationMenuItemExpandIconForeground}"
|
||||
IsVisible="{TemplateBinding IsTopLevelMenuItem,
|
||||
Converter={x:Static BoolConverters.Not}}"
|
||||
RenderTransform="rotate(-90deg)" />
|
||||
</Grid>
|
||||
</Border>
|
||||
<Popup
|
||||
Name="{x:Static u:NavigationMenuItem.PART_Popup}"
|
||||
IsLightDismissEnabled="True"
|
||||
IsOpen="{TemplateBinding IsPopupOpen,
|
||||
Mode=TwoWay}"
|
||||
Placement="RightEdgeAlignedTop"
|
||||
PlacementTarget="PART_HeaderBorder"
|
||||
WindowManagerAddShadowHint="False">
|
||||
<Border
|
||||
MinWidth="{DynamicResource MenuFlyoutMinWidth}"
|
||||
MinHeight="{DynamicResource MenuFlyoutMinHeight}"
|
||||
MaxWidth="{DynamicResource MenuFlyoutMaxWidth}"
|
||||
MaxHeight="{DynamicResource MenuFlyoutMaxHeight}"
|
||||
Margin="8"
|
||||
Padding="{DynamicResource MenuFlyoutPadding}"
|
||||
HorizontalAlignment="Stretch"
|
||||
Background="{DynamicResource MenuFlyoutBackground}"
|
||||
BorderBrush="{DynamicResource MenuFlyoutBorderBrush}"
|
||||
BorderThickness="{DynamicResource MenuFlyoutBorderThickness}"
|
||||
BoxShadow="{DynamicResource MenuFlyoutBorderBoxShadow}"
|
||||
CornerRadius="{DynamicResource MenuFlyoutCornerRadius}">
|
||||
<ScrollViewer Theme="{DynamicResource MenuScrollViewer}">
|
||||
<ItemsPresenter
|
||||
Name="PART_ItemsPresenter"
|
||||
Grid.IsSharedSizeScope="True"
|
||||
ItemsPanel="{TemplateBinding ItemsPanel}" />
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
</Popup>
|
||||
</Panel>
|
||||
</ControlTemplate>
|
||||
</Setter>
|
||||
<Style Selector="^ /template/ Border#PART_HeaderBorder:pointerover">
|
||||
<Setter Property="Border.Background" Value="{DynamicResource NavigationMenuItemPointeroverBackground}" />
|
||||
</Style>
|
||||
<Style Selector="^ /template/ Border#PART_HeaderBorder:pressed">
|
||||
<Setter Property="Border.Background" Value="{DynamicResource NavigationMenuItemPressedBackground}" />
|
||||
</Style>
|
||||
</Style>
|
||||
|
||||
<Style Selector="^:not(:closed):collapsed /template/ ItemsPresenter#PART_ItemsPresenter">
|
||||
<Setter Property="ItemsPresenter.Height" Value="0" />
|
||||
<Setter Property="ItemsPresenter.Opacity" Value="0" />
|
||||
</Style>
|
||||
|
||||
@@ -4,20 +4,20 @@ using Avalonia.Data.Converters;
|
||||
|
||||
namespace Ursa.Themes.Semi.Converters;
|
||||
|
||||
public class NavigationMenuItemLevelToMarginConverter: IValueConverter
|
||||
public class NavigationMenuItemLevelToMarginConverter: IMultiValueConverter
|
||||
{
|
||||
public int Indent { get; set; }
|
||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
|
||||
public object? Convert(IList<object?> values, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is int i)
|
||||
if (values[0] is int i && values[1] is bool b)
|
||||
{
|
||||
if (b)
|
||||
{
|
||||
return new Thickness();
|
||||
}
|
||||
return new Thickness((i-1) * Indent, 0, 0, 0);
|
||||
}
|
||||
return new Thickness();
|
||||
}
|
||||
|
||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
@@ -134,7 +134,7 @@ public class NavigationMenu: HeaderedItemsControl
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateSelectionFromSelectedItem(object? o)
|
||||
internal void UpdateSelectionFromSelectedItem(object? o)
|
||||
{
|
||||
var children = this.ItemsPanelRoot?.Children;
|
||||
if (children is not null)
|
||||
|
||||
@@ -15,18 +15,22 @@ using Avalonia.VisualTree;
|
||||
|
||||
namespace Ursa.Controls;
|
||||
|
||||
[PseudoClasses(PC_Closed, PC_Selected, PC_Highlighted, PC_Collapsed)]
|
||||
[PseudoClasses(PC_Closed, PC_Selected, PC_Highlighted, PC_Collapsed, PC_TopLevel)]
|
||||
[TemplatePart(PART_Popup, typeof(Popup))]
|
||||
public class NavigationMenuItem: HeaderedSelectingItemsControl
|
||||
{
|
||||
public const string PC_Closed = ":closed";
|
||||
public const string PC_Selected = ":selected";
|
||||
public const string PC_Highlighted= ":highlighted";
|
||||
public const string PC_Collapsed = ":collapsed";
|
||||
public const string PC_TopLevel = ":top-level";
|
||||
public const string PART_Popup = "PART_Popup";
|
||||
|
||||
private NavigationMenu? _rootMenu;
|
||||
private IDisposable? _ownerSubscription;
|
||||
private IDisposable? _itemsBinding;
|
||||
private bool _isCollapsed;
|
||||
private Popup? _popup;
|
||||
|
||||
public static readonly StyledProperty<bool> IsClosedProperty = AvaloniaProperty.Register<NavigationMenuItem, bool>(
|
||||
nameof(IsClosed));
|
||||
@@ -54,18 +58,16 @@ public class NavigationMenuItem: HeaderedSelectingItemsControl
|
||||
get => GetValue(IconTemplateProperty);
|
||||
set => SetValue(IconTemplateProperty, value);
|
||||
}
|
||||
|
||||
private int _level;
|
||||
|
||||
|
||||
public static readonly DirectProperty<NavigationMenuItem, int> LevelProperty = AvaloniaProperty.RegisterDirect<NavigationMenuItem, int>(
|
||||
nameof(Level), o => o.Level);
|
||||
|
||||
private int _level;
|
||||
public int Level
|
||||
{
|
||||
get => _level;
|
||||
private set => SetAndRaise(LevelProperty, ref _level, value);
|
||||
}
|
||||
|
||||
|
||||
public static readonly StyledProperty<ICommand> CommandProperty = AvaloniaProperty.Register<NavigationMenuItem, ICommand>(
|
||||
nameof(Command));
|
||||
|
||||
@@ -84,6 +86,24 @@ public class NavigationMenuItem: HeaderedSelectingItemsControl
|
||||
set => SetValue(CommandParameterProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DirectProperty<NavigationMenuItem, bool> IsTopLevelMenuItemProperty = AvaloniaProperty.RegisterDirect<NavigationMenuItem, bool>(
|
||||
nameof(IsTopLevelMenuItem), o => o.IsTopLevelMenuItem, (o, v) => o.IsTopLevelMenuItem = v);
|
||||
private bool _isTopLevelMenuItem;
|
||||
public bool IsTopLevelMenuItem
|
||||
{
|
||||
get => _isTopLevelMenuItem;
|
||||
set => SetAndRaise(IsTopLevelMenuItemProperty, ref _isTopLevelMenuItem, value);
|
||||
}
|
||||
|
||||
public static readonly StyledProperty<bool> IsPopupOpenProperty = AvaloniaProperty.Register<NavigationMenuItem, bool>(
|
||||
nameof(IsPopupOpen));
|
||||
|
||||
public bool IsPopupOpen
|
||||
{
|
||||
get => GetValue(IsPopupOpenProperty);
|
||||
set => SetValue(IsPopupOpenProperty, value);
|
||||
}
|
||||
|
||||
static NavigationMenuItem()
|
||||
{
|
||||
IsClosedProperty.Changed.AddClassHandler<NavigationMenuItem>((o, e) => o.OnIsClosedChanged(e));
|
||||
@@ -96,13 +116,6 @@ public class NavigationMenuItem: HeaderedSelectingItemsControl
|
||||
PseudoClasses.Set(PC_Closed, newValue);
|
||||
}
|
||||
|
||||
|
||||
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
base.OnAttachedToVisualTree(e);
|
||||
|
||||
}
|
||||
|
||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||
{
|
||||
base.OnApplyTemplate(e);
|
||||
@@ -116,10 +129,19 @@ public class NavigationMenuItem: HeaderedSelectingItemsControl
|
||||
SetCurrentValue(ItemContainerThemeProperty, _rootMenu.ItemContainerTheme);
|
||||
}
|
||||
|
||||
if (_rootMenu is not null)
|
||||
{
|
||||
IsClosed = _rootMenu.IsClosed;
|
||||
}
|
||||
|
||||
_rootMenu?.GetObservable(NavigationMenu.IsClosedProperty)
|
||||
.Subscribe(new AnonymousObserver<bool>(a => this.IsClosed = a));
|
||||
|
||||
_rootMenu?.UpdateSelectionFromSelectedItem(_rootMenu.SelectedItem);
|
||||
_popup = e.NameScope.Find<Popup>(PART_Popup);
|
||||
Level = CalculateDistanceFromLogicalParent<NavigationMenu>(this) - 1;
|
||||
bool isTopLevel = Level == 1;
|
||||
IsTopLevelMenuItem = isTopLevel;
|
||||
PseudoClasses.Set(PC_TopLevel, isTopLevel);
|
||||
}
|
||||
|
||||
private void GetRootMenu()
|
||||
@@ -127,11 +149,10 @@ public class NavigationMenuItem: HeaderedSelectingItemsControl
|
||||
_rootMenu = this.FindAncestorOfType<NavigationMenu>();
|
||||
if (_rootMenu is null)
|
||||
{
|
||||
var popupRoot = TopLevel.GetTopLevel(this) as PopupRoot;
|
||||
if (popupRoot?.Parent is Popup popup)
|
||||
var parents = this.FindLogicalAncestorOfType<NavigationMenu>();
|
||||
if (parents is not null)
|
||||
{
|
||||
Control? c = popup.PlacementTarget;
|
||||
_rootMenu = c.FindAncestorOfType<NavigationMenu>();
|
||||
_rootMenu = parents;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -142,8 +163,7 @@ public class NavigationMenuItem: HeaderedSelectingItemsControl
|
||||
// Leaf menu node, can be selected.
|
||||
if (this.ItemCount == 0)
|
||||
{
|
||||
var parents = this.GetSelfAndLogicalAncestors();
|
||||
if (_rootMenu is not null && parents.Contains(_rootMenu))
|
||||
if (_rootMenu is not null )
|
||||
{
|
||||
object? o = this.DataContext ?? this;
|
||||
_rootMenu.SelectedItem = o;
|
||||
@@ -155,6 +175,10 @@ public class NavigationMenuItem: HeaderedSelectingItemsControl
|
||||
{
|
||||
_isCollapsed = !_isCollapsed;
|
||||
this.PseudoClasses.Set(PC_Collapsed, _isCollapsed);
|
||||
if (_popup is not null)
|
||||
{
|
||||
_popup.IsOpen = !_popup.IsOpen;
|
||||
}
|
||||
}
|
||||
e.Handled = true;
|
||||
Command?.Execute(CommandParameter);
|
||||
@@ -188,7 +212,7 @@ public class NavigationMenuItem: HeaderedSelectingItemsControl
|
||||
|
||||
if (propagateToParent)
|
||||
{
|
||||
var parent = this.FindAncestorOfType<NavigationMenuItem>();
|
||||
var parent = this.FindLogicalAncestorOfType<NavigationMenuItem>();
|
||||
if (parent != null)
|
||||
{
|
||||
parent.SetSelection(this, selected, true);
|
||||
|
||||
Reference in New Issue
Block a user