feat: update to finish functionality, start to build theme.

This commit is contained in:
rabbitism
2023-06-23 02:46:37 +08:00
parent bacf1a6330
commit c21e571b74
7 changed files with 475 additions and 62 deletions

View File

@@ -1,8 +1,10 @@
using System.Security.Cryptography.X509Certificates;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Data;
using Avalonia.Input;
using Avalonia.LogicalTree;
@@ -12,16 +14,68 @@ using Avalonia.VisualTree;
namespace Ursa.Controls;
[PseudoClasses(PC_Closed, PC_Selected, PC_Empty)]
[PseudoClasses(PC_Closed, PC_Selected, PC_Highlighted, PC_Collapsed)]
public class NavigationMenuItem: HeaderedSelectingItemsControl
{
public const string PC_Closed = ":closed";
public const string PC_Selected = ":selected";
public const string PC_Empty = ":empty";
public const string PC_Highlighted= ":highlighted";
public const string PC_Collapsed = ":collapsed";
private NavigationMenu? _rootMenu;
private IDisposable? _ownerSubscription;
private IDisposable? _itemsBinding;
private bool _isCollapsed;
public static readonly StyledProperty<bool> IsClosedProperty = AvaloniaProperty.Register<NavigationMenuItem, bool>(
nameof(IsClosed));
public bool IsClosed
{
get => GetValue(IsClosedProperty);
set => SetValue(IsClosedProperty, value);
}
public static readonly StyledProperty<object?> IconProperty = AvaloniaProperty.Register<NavigationMenuItem, object?>(
nameof(Icon));
public object? Icon
{
get => GetValue(IconProperty);
set => SetValue(IconProperty, value);
}
public static readonly StyledProperty<IDataTemplate> IconTemplateProperty = AvaloniaProperty.Register<NavigationMenuItem, IDataTemplate>(
nameof(IconTemplate));
public IDataTemplate IconTemplate
{
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);
public int Level
{
get => _level;
private set => SetAndRaise(LevelProperty, ref _level, value);
}
static NavigationMenuItem()
{
IsClosedProperty.Changed.AddClassHandler<NavigationMenuItem>((o, e) => o.OnIsClosedChanged(e));
}
private void OnIsClosedChanged(AvaloniaPropertyChangedEventArgs args)
{
bool newValue = args.GetNewValue<bool>();
PseudoClasses.Set(PC_Closed, newValue);
}
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
@@ -35,6 +89,11 @@ public class NavigationMenuItem: HeaderedSelectingItemsControl
{
SetCurrentValue(ItemContainerThemeProperty, _rootMenu.ItemContainerTheme);
}
_rootMenu?.GetObservable(NavigationMenu.IsClosedProperty)
.Subscribe(new AnonymousObserver<bool>(a => this.IsClosed = a));
Level = CalculateDistanceFromLogicalParent<NavigationMenu>(this) - 1;
}
protected override void OnPointerPressed(PointerPressedEventArgs e)
@@ -47,31 +106,38 @@ public class NavigationMenuItem: HeaderedSelectingItemsControl
if (_rootMenu is not null && parents.Contains(_rootMenu))
{
object? o = this.DataContext ?? this;
_rootMenu.SelectedMenuItem = o;
_rootMenu.SelectedItem = o;
}
SetSelection(this, true, true);
}
// Non-leaf node, act as a toggle button.
else
{
_isCollapsed = !_isCollapsed;
this.PseudoClasses.Set(PC_Collapsed, _isCollapsed);
}
e.Handled = true;
SetSelection(this, true, true);
}
internal void SetSelection(NavigationMenuItem? source, bool selected, bool propagateToParent = false)
{
this.PseudoClasses.Set(PC_Selected, selected);
if (Equals(this, source) && this.ItemCount == 0)
{
this.PseudoClasses.Set(PC_Highlighted, selected);
this.PseudoClasses.Set(PC_Selected, selected);
}
else
{
this.PseudoClasses.Set(PC_Selected, false);
this.PseudoClasses.Set(PC_Highlighted, 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;
}
NavigationMenuItem? item = GetMenuItemFromControl(child);
if (item != null)
{
if(Equals(item, source)) continue;
@@ -96,4 +162,54 @@ public class NavigationMenuItem: HeaderedSelectingItemsControl
}
}
}
internal void UpdateSelectionFromSelectedItem(object? o)
{
if (o is null)
{
this.SetSelection(this, false, false);
return;
}
if (Equals(this, o) || Equals(this.DataContext, o))
{
this.SetSelection(this, true, true);
}
else
{
var children = this.ItemsPanelRoot?.Children;
if (children is not null)
{
foreach (var child in children)
{
NavigationMenuItem? item = GetMenuItemFromControl(child);
if (item != null)
{
item.UpdateSelectionFromSelectedItem(o);
}
}
}
}
}
private static int CalculateDistanceFromLogicalParent<T>(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;
}
public static NavigationMenuItem? GetMenuItemFromControl(Control? control)
{
if (control is null) return null;
if (control is NavigationMenuItem item) return item;
if (control is ContentPresenter { Child: NavigationMenuItem item2 }) return item2;
return null;
}
}