feat: wow, first prototype of navigation menu.
This commit is contained in:
47
demo/Ursa.Demo/Pages/NavigationMenuDemo.axaml
Normal file
47
demo/Ursa.Demo/Pages/NavigationMenuDemo.axaml
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<UserControl
|
||||||
|
x:Class="Ursa.Demo.Pages.NavigationMenuDemo"
|
||||||
|
xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:u="https://irihi.tech/ursa"
|
||||||
|
xmlns:vm="clr-namespace:Ursa.Demo.ViewModels"
|
||||||
|
d:DesignHeight="450"
|
||||||
|
d:DesignWidth="800"
|
||||||
|
x:CompileBindings="True"
|
||||||
|
x:DataType="vm:NavigationMenuDemoViewModel"
|
||||||
|
mc:Ignorable="d">
|
||||||
|
<StackPanel>
|
||||||
|
<u:NavigationMenu Name="menu" ItemsSource="{Binding MenuItems}">
|
||||||
|
<u:NavigationMenu.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<u:NavigationMenuItem Header="{Binding MenuHeader}" ItemsSource="{Binding Children}" />
|
||||||
|
</DataTemplate>
|
||||||
|
</u:NavigationMenu.ItemTemplate>
|
||||||
|
</u:NavigationMenu>
|
||||||
|
|
||||||
|
<TreeView ItemsSource="{Binding MenuItems}">
|
||||||
|
<TreeView.ItemTemplate>
|
||||||
|
<TreeDataTemplate ItemsSource="{Binding Children}">
|
||||||
|
<ContentControl Content="{Binding MenuHeader}" />
|
||||||
|
</TreeDataTemplate>
|
||||||
|
</TreeView.ItemTemplate>
|
||||||
|
</TreeView>
|
||||||
|
|
||||||
|
<TextBlock Text="{ReflectionBinding #menu.SelectedMenuItem.MenuHeader}" />
|
||||||
|
|
||||||
|
<u:NavigationMenu>
|
||||||
|
<u:NavigationMenuItem Header="111" />
|
||||||
|
<u:NavigationMenuItem Header="222" />
|
||||||
|
<u:NavigationMenuItem Header="333" />
|
||||||
|
</u:NavigationMenu>
|
||||||
|
|
||||||
|
<u:NavigationMenu ItemsSource="{Binding MenuItems}">
|
||||||
|
<u:NavigationMenu.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<u:NavigationMenuItem Header="{Binding MenuHeader}" />
|
||||||
|
</DataTemplate>
|
||||||
|
</u:NavigationMenu.ItemTemplate>
|
||||||
|
</u:NavigationMenu>
|
||||||
|
</StackPanel>
|
||||||
|
</UserControl>
|
||||||
15
demo/Ursa.Demo/Pages/NavigationMenuDemo.axaml.cs
Normal file
15
demo/Ursa.Demo/Pages/NavigationMenuDemo.axaml.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
using Ursa.Demo.ViewModels;
|
||||||
|
|
||||||
|
namespace Ursa.Demo.Pages;
|
||||||
|
|
||||||
|
public partial class NavigationMenuDemo : UserControl
|
||||||
|
{
|
||||||
|
public NavigationMenuDemo()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
this.DataContext = new NavigationMenuDemoViewModel();
|
||||||
|
}
|
||||||
|
}
|
||||||
41
demo/Ursa.Demo/ViewModels/NavigationMenuDemoViewModel.cs
Normal file
41
demo/Ursa.Demo/ViewModels/NavigationMenuDemoViewModel.cs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
|
||||||
|
namespace Ursa.Demo.ViewModels;
|
||||||
|
|
||||||
|
public class NavigationMenuDemoViewModel: ObservableObject
|
||||||
|
{
|
||||||
|
public ObservableCollection<NavigationMenuItemViewModel> MenuItems { get; set; } = new()
|
||||||
|
{
|
||||||
|
new NavigationMenuItemViewModel()
|
||||||
|
{
|
||||||
|
MenuHeader = "1",
|
||||||
|
Children = new ObservableCollection<NavigationMenuItemViewModel>()
|
||||||
|
{
|
||||||
|
new NavigationMenuItemViewModel(){
|
||||||
|
MenuHeader = "11" ,
|
||||||
|
Children = new ObservableCollection<NavigationMenuItemViewModel>()
|
||||||
|
{
|
||||||
|
new NavigationMenuItemViewModel(){MenuHeader = "111"},
|
||||||
|
new NavigationMenuItemViewModel(){MenuHeader = "112"}
|
||||||
|
}},
|
||||||
|
new NavigationMenuItemViewModel(){MenuHeader = "12"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new NavigationMenuItemViewModel()
|
||||||
|
{
|
||||||
|
MenuHeader = "2",
|
||||||
|
Children = new ObservableCollection<NavigationMenuItemViewModel>()
|
||||||
|
{
|
||||||
|
new NavigationMenuItemViewModel(){MenuHeader = "21"},
|
||||||
|
new NavigationMenuItemViewModel(){MenuHeader = "22"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public class NavigationMenuItemViewModel: ObservableObject
|
||||||
|
{
|
||||||
|
public string MenuHeader { get; set; }
|
||||||
|
public ObservableCollection<NavigationMenuItemViewModel> Children { get; set; } = new();
|
||||||
|
}
|
||||||
@@ -35,6 +35,9 @@
|
|||||||
<TabItem Header="IPv4Box">
|
<TabItem Header="IPv4Box">
|
||||||
<pages:IPv4BoxDemo />
|
<pages:IPv4BoxDemo />
|
||||||
</TabItem>
|
</TabItem>
|
||||||
|
<TabItem Header="Navigation">
|
||||||
|
<pages:NavigationMenuDemo />
|
||||||
|
</TabItem>
|
||||||
<TabItem Header="Pagination">
|
<TabItem Header="Pagination">
|
||||||
<pages:PaginationDemo />
|
<pages:PaginationDemo />
|
||||||
</TabItem>
|
</TabItem>
|
||||||
|
|||||||
42
src/Ursa.Themes.Semi/Controls/Navigation.axaml
Normal file
42
src/Ursa.Themes.Semi/Controls/Navigation.axaml
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
<ResourceDictionary
|
||||||
|
xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:u="https://irihi.tech/ursa">
|
||||||
|
<!-- Add Resources Here -->
|
||||||
|
<ControlTheme x:Key="{x:Type u:NavigationMenu}" TargetType="u:NavigationMenu">
|
||||||
|
<Setter Property="Template">
|
||||||
|
<ControlTemplate TargetType="u:NavigationMenu">
|
||||||
|
<Border Background="{TemplateBinding Background}">
|
||||||
|
<StackPanel>
|
||||||
|
<ContentPresenter Name="PART_HeaderPresenter" Content="Hello" />
|
||||||
|
<ItemsPresenter ItemsPanel="{TemplateBinding ItemsPanel}" />
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter>
|
||||||
|
</ControlTheme>
|
||||||
|
|
||||||
|
<ControlTheme x:Key="{x:Type u:NavigationMenuItem}" TargetType="u:NavigationMenuItem">
|
||||||
|
<Setter Property="Background" Value="Transparent" />
|
||||||
|
<Setter Property="Template">
|
||||||
|
<ControlTemplate TargetType="u:NavigationMenuItem">
|
||||||
|
<Border
|
||||||
|
Padding="4"
|
||||||
|
BorderBrush="Black"
|
||||||
|
BorderThickness="1">
|
||||||
|
<StackPanel>
|
||||||
|
<ContentPresenter
|
||||||
|
Name="PART_HeaderPresenter"
|
||||||
|
Background="{TemplateBinding Background}"
|
||||||
|
Content="{TemplateBinding Header}"
|
||||||
|
ContentTemplate="{TemplateBinding HeaderTemplate}" />
|
||||||
|
<ItemsPresenter ItemsPanel="{TemplateBinding ItemsPanel}" />
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter>
|
||||||
|
<Style Selector="^:selected">
|
||||||
|
<Setter Property="u:NavigationMenuItem.Background" Value="Red" />
|
||||||
|
</Style>
|
||||||
|
</ControlTheme>
|
||||||
|
</ResourceDictionary>
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
<ResourceInclude Source="Banner.axaml" />
|
<ResourceInclude Source="Banner.axaml" />
|
||||||
<ResourceInclude Source="Divider.axaml" />
|
<ResourceInclude Source="Divider.axaml" />
|
||||||
<ResourceInclude Source="IPv4Box.axaml" />
|
<ResourceInclude Source="IPv4Box.axaml" />
|
||||||
|
<ResourceInclude Source="Navigation.axaml" />
|
||||||
<ResourceInclude Source="Pagination.axaml" />
|
<ResourceInclude Source="Pagination.axaml" />
|
||||||
<ResourceInclude Source="Timeline.axaml" />
|
<ResourceInclude Source="Timeline.axaml" />
|
||||||
</ResourceDictionary.MergedDictionaries>
|
</ResourceDictionary.MergedDictionaries>
|
||||||
|
|||||||
68
src/Ursa/Controls/Navigation/NavigationMenu.cs
Normal file
68
src/Ursa/Controls/Navigation/NavigationMenu.cs
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Specialized;
|
||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Collections;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Controls.Presenters;
|
||||||
|
using Avalonia.Controls.Primitives;
|
||||||
|
using Avalonia.Controls.Templates;
|
||||||
|
using Avalonia.Input;
|
||||||
|
using Avalonia.Markup.Xaml.Templates;
|
||||||
|
using Avalonia.Metadata;
|
||||||
|
|
||||||
|
namespace Ursa.Controls;
|
||||||
|
|
||||||
|
public class NavigationMenu: HeaderedSelectingItemsControl
|
||||||
|
{
|
||||||
|
public static readonly StyledProperty<object?> FooterProperty = AvaloniaProperty.Register<NavigationMenu, object?>(
|
||||||
|
nameof(Footer));
|
||||||
|
|
||||||
|
public object? Footer
|
||||||
|
{
|
||||||
|
get => GetValue(FooterProperty);
|
||||||
|
set => SetValue(FooterProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly StyledProperty<IDataTemplate> FooterTemplateProperty = AvaloniaProperty.Register<NavigationMenu, IDataTemplate>(
|
||||||
|
nameof(FooterTemplate));
|
||||||
|
|
||||||
|
public IDataTemplate FooterTemplate
|
||||||
|
{
|
||||||
|
get => GetValue(FooterTemplateProperty);
|
||||||
|
set => SetValue(FooterTemplateProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly StyledProperty<object?> SelectedMenuItemProperty = AvaloniaProperty.Register<NavigationMenu, object?>(
|
||||||
|
nameof(SelectedMenuItem));
|
||||||
|
|
||||||
|
public object? SelectedMenuItem
|
||||||
|
{
|
||||||
|
get => GetValue(SelectedMenuItemProperty);
|
||||||
|
set => SetValue(SelectedMenuItemProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void UpdateSelection(NavigationMenuItem source)
|
||||||
|
{
|
||||||
|
var children = this.ItemsPanelRoot?.Children;
|
||||||
|
if (children is not null)
|
||||||
|
{
|
||||||
|
foreach (var child in children)
|
||||||
|
{
|
||||||
|
NavigationMenuItem? item = null;
|
||||||
|
if (child is NavigationMenuItem i)
|
||||||
|
{
|
||||||
|
item = i;
|
||||||
|
}
|
||||||
|
else if (child is ContentPresenter { Child: NavigationMenuItem i2 })
|
||||||
|
{
|
||||||
|
item = i2;
|
||||||
|
}
|
||||||
|
if (item != null)
|
||||||
|
{
|
||||||
|
if(Equals(item, source)) continue;
|
||||||
|
item.SetSelection(null, false, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
99
src/Ursa/Controls/Navigation/NavigationMenuItem.cs
Normal file
99
src/Ursa/Controls/Navigation/NavigationMenuItem.cs
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Controls.Metadata;
|
||||||
|
using Avalonia.Controls.Presenters;
|
||||||
|
using Avalonia.Controls.Primitives;
|
||||||
|
using Avalonia.Data;
|
||||||
|
using Avalonia.Input;
|
||||||
|
using Avalonia.LogicalTree;
|
||||||
|
using Avalonia.Markup.Xaml.Templates;
|
||||||
|
using Avalonia.Reactive;
|
||||||
|
using Avalonia.VisualTree;
|
||||||
|
|
||||||
|
namespace Ursa.Controls;
|
||||||
|
|
||||||
|
[PseudoClasses(PC_Closed, PC_Selected, PC_Empty)]
|
||||||
|
public class NavigationMenuItem: HeaderedSelectingItemsControl
|
||||||
|
{
|
||||||
|
public const string PC_Closed = ":closed";
|
||||||
|
public const string PC_Selected = ":selected";
|
||||||
|
public const string PC_Empty = ":empty";
|
||||||
|
|
||||||
|
private NavigationMenu? _rootMenu;
|
||||||
|
private IDisposable? _ownerSubscription;
|
||||||
|
private IDisposable? _itemsBinding;
|
||||||
|
|
||||||
|
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnAttachedToVisualTree(e);
|
||||||
|
_rootMenu = this.FindAncestorOfType<NavigationMenu>();
|
||||||
|
if (ItemTemplate == null && _rootMenu?.ItemTemplate != null)
|
||||||
|
{
|
||||||
|
SetCurrentValue(ItemTemplateProperty, _rootMenu.ItemTemplate);
|
||||||
|
}
|
||||||
|
if (ItemContainerTheme == null && _rootMenu?.ItemContainerTheme != null)
|
||||||
|
{
|
||||||
|
SetCurrentValue(ItemContainerThemeProperty, _rootMenu.ItemContainerTheme);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnPointerPressed(PointerPressedEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnPointerPressed(e);
|
||||||
|
// Leaf menu node, can be selected.
|
||||||
|
if (this.ItemCount == 0)
|
||||||
|
{
|
||||||
|
var parents = this.GetSelfAndLogicalAncestors();
|
||||||
|
if (_rootMenu is not null && parents.Contains(_rootMenu))
|
||||||
|
{
|
||||||
|
object? o = this.DataContext ?? this;
|
||||||
|
_rootMenu.SelectedMenuItem = o;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
e.Handled = true;
|
||||||
|
SetSelection(this, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void SetSelection(NavigationMenuItem? source, bool selected, bool propagateToParent = false)
|
||||||
|
{
|
||||||
|
this.PseudoClasses.Set(PC_Selected, selected);
|
||||||
|
var children = this.ItemsPanelRoot?.Children;
|
||||||
|
if (children is not null)
|
||||||
|
{
|
||||||
|
foreach (var child in children)
|
||||||
|
{
|
||||||
|
NavigationMenuItem? item = null;
|
||||||
|
if (child is NavigationMenuItem i)
|
||||||
|
{
|
||||||
|
item = i;
|
||||||
|
}
|
||||||
|
else if (child is ContentPresenter { Child: NavigationMenuItem i2 })
|
||||||
|
{
|
||||||
|
item = i2;
|
||||||
|
}
|
||||||
|
if (item != null)
|
||||||
|
{
|
||||||
|
if(Equals(item, source)) continue;
|
||||||
|
item.SetSelection(this, false, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (propagateToParent)
|
||||||
|
{
|
||||||
|
var parent = this.FindAncestorOfType<NavigationMenuItem>();
|
||||||
|
if (parent != null)
|
||||||
|
{
|
||||||
|
parent.SetSelection(this, selected, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (selected)
|
||||||
|
{
|
||||||
|
_rootMenu?.UpdateSelection(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
6
src/Ursa/Controls/Navigation/NavigationMenuSeparator.cs
Normal file
6
src/Ursa/Controls/Navigation/NavigationMenuSeparator.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
namespace Ursa.Controls;
|
||||||
|
|
||||||
|
public class NavigationMenuSeparator: NavigationMenuItem
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user