完成 TabStrip 下拉菜单

当Tab菜单过多的时候显示一个下拉菜单
This commit is contained in:
polarboy 2024-08-01 19:44:03 +08:00
parent 5dbd7e45b5
commit 3ef2587f39
4 changed files with 195 additions and 13 deletions

View File

@ -210,9 +210,11 @@ public class TabScrollViewer : ScrollViewer
var right = Math.Floor(itemBounds.Right - Offset.X);
if (TabStripPlacement == Dock.Top || TabStripPlacement == Dock.Bottom) {
if (left < 0 || right > Viewport.Width) {
var menuItem = new MenuItem()
var menuItem = new TabStripMenuItem()
{
Header = tabStripItem.Content,
TabStripItem = tabStripItem,
IsClosable = tabStripItem.IsClosable
};
menuItem.Click += HandleMenuItemClicked;
_menuFlyout.Items.Add(menuItem);
@ -230,14 +232,16 @@ public class TabScrollViewer : ScrollViewer
private void HandleMenuItemClicked(object? sender, RoutedEventArgs args)
{
if (TabStrip is not null) {
TabStrip.BringIntoView();
Dispatcher.UIThread.Post(sender =>
{
var item = TabStrip.Items[5];
if (item is TabStripItem tabStripItem) {
tabStripItem.BringIntoView();
if (sender is TabStripMenuItem tabStripMenuItem) {
var tabStripItem = tabStripMenuItem.TabStripItem;
if (tabStripItem is not null) {
tabStripItem.BringIntoView();
TabStrip.SelectedItem = tabStripItem;
}
}
}, sender);
}, sender);
}
}

View File

@ -33,8 +33,8 @@ public class TabStripItem : AvaloniaTabStripItem, IControlCustomStyle, ICustomHi
public static readonly StyledProperty<PathIcon?> CloseIconProperty =
AvaloniaProperty.Register<TabStripItem, PathIcon?>(nameof(CloseIcon));
public static readonly StyledProperty<bool> ClosableProperty =
AvaloniaProperty.Register<TabStripItem, bool>(nameof(Closable));
public static readonly StyledProperty<bool> IsClosableProperty =
AvaloniaProperty.Register<TabStripItem, bool>(nameof(IsClosable));
public static readonly DirectProperty<TabStripItem, Dock?> TabStripPlacementProperty =
AvaloniaProperty.RegisterDirect<TabStripItem, Dock?>(nameof(TabStripPlacement), o => o.TabStripPlacement);
@ -57,10 +57,10 @@ public class TabStripItem : AvaloniaTabStripItem, IControlCustomStyle, ICustomHi
set => SetValue(CloseIconProperty, value);
}
public bool Closable
public bool IsClosable
{
get => GetValue(ClosableProperty);
set => SetValue(ClosableProperty, value);
get => GetValue(IsClosableProperty);
set => SetValue(IsClosableProperty, value);
}
private Dock? _tabStripPlacement;

View File

@ -1,6 +1,56 @@
namespace AtomUI.Controls;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Interactivity;
using Avalonia.LogicalTree;
namespace AtomUI.Controls;
internal class TabStripMenuItem : MenuItem
{
#region
public static readonly DirectProperty<TabStripMenuItem, bool> IsClosableProperty =
AvaloniaProperty.RegisterDirect<TabStripMenuItem, bool>(nameof(IsClosable),
o => o.IsClosable,
(o, v) => o.IsClosable = v);
public static readonly RoutedEvent<RoutedEventArgs> CloseTabEvent =
RoutedEvent.Register<Button, RoutedEventArgs>(nameof(CloseTab), RoutingStrategies.Bubble);
private bool _isClosable = false;
public bool IsClosable
{
get => _isClosable;
set => SetAndRaise(IsClosableProperty, ref _isClosable, value);
}
public TabStripItem? TabStripItem { get; set; }
public event EventHandler<RoutedEventArgs>? CloseTab
{
add => AddHandler(CloseTabEvent, value);
remove => RemoveHandler(CloseTabEvent, value);
}
#endregion
private IconButton? _iconButton;
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
_iconButton = e.NameScope.Find<IconButton>(TabStripMenuItemTheme.ItemCloseButtonPart);
if (_iconButton is not null) {
_iconButton.Click += (sender, args) =>
{
var menu = this.FindLogicalAncestorOfType<MenuBase>();
if (menu is not null) {
menu.Close();
var eventArgs = new RoutedEventArgs(CloseTabEvent);
RaiseEvent(eventArgs);
}
};
}
}
}

View File

@ -1,13 +1,141 @@
using AtomUI.Theme;
using AtomUI.Media;
using AtomUI.Theme;
using AtomUI.Theme.Styling;
using AtomUI.Theme.Utils;
using AtomUI.Utils;
using Avalonia.Animation;
using Avalonia.Controls;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Templates;
using Avalonia.Layout;
using Avalonia.Styling;
namespace AtomUI.Controls;
[ControlThemeProvider]
internal class TabStripMenuItemTheme : BaseControlTheme
{
public const string ItemDecoratorPart = "Part_ItemDecorator";
public const string MainContainerPart = "Part_MainContainer";
public const string ItemTextPresenterPart = "Part_ItemTextPresenter";
public const string ItemCloseButtonPart = "Part_ItemCloseIcon";
public TabStripMenuItemTheme()
: base(typeof(TabStripMenuItem))
{
}
protected override IControlTemplate BuildControlTemplate()
{
return new FuncControlTemplate<TabStripMenuItem>((item, scope) =>
{
var container = new Border()
{
Name = ItemDecoratorPart
};
var transitions = new Transitions();
transitions.Add(AnimationUtils.CreateTransition<SolidColorBrushTransition>(Border.BackgroundProperty));
container.Transitions = transitions;
var layout = new Grid()
{
Name = MainContainerPart,
ColumnDefinitions =
{
new ColumnDefinition(GridLength.Star),
new ColumnDefinition(GridLength.Auto)
{
SharedSizeGroup = "MenuCloseIcon"
}
}
};
var itemTextPresenter = new ContentPresenter
{
Name = ItemTextPresenterPart,
HorizontalAlignment = HorizontalAlignment.Stretch,
VerticalAlignment = VerticalAlignment.Center,
RecognizesAccessKey = true,
IsHitTestVisible = false
};
Grid.SetColumn(itemTextPresenter, 0);
TokenResourceBinder.CreateTokenBinding(itemTextPresenter, ContentPresenter.MarginProperty, MenuResourceKey.ItemMargin);
CreateTemplateParentBinding(itemTextPresenter, ContentPresenter.ContentProperty, TabStripMenuItem.HeaderProperty);
CreateTemplateParentBinding(itemTextPresenter, ContentPresenter.ContentTemplateProperty, TabStripMenuItem.HeaderTemplateProperty);
itemTextPresenter.RegisterInNameScope(scope);
var menuCloseIcon = new PathIcon
{
HorizontalAlignment = HorizontalAlignment.Right,
VerticalAlignment = VerticalAlignment.Center,
Kind = "CloseOutlined"
};
var closeButton = new IconButton()
{
Name = ItemCloseButtonPart,
HorizontalAlignment = HorizontalAlignment.Right,
VerticalAlignment = VerticalAlignment.Center,
Icon = menuCloseIcon
};
CreateTemplateParentBinding(closeButton, IconButton.IsVisibleProperty, TabStripMenuItem.IsClosableProperty);
TokenResourceBinder.CreateGlobalTokenBinding(menuCloseIcon, PathIcon.NormalFilledBrushProperty, GlobalResourceKey.ColorIcon);
TokenResourceBinder.CreateGlobalTokenBinding(menuCloseIcon, PathIcon.ActiveFilledBrushProperty, GlobalResourceKey.ColorIconHover);
TokenResourceBinder.CreateGlobalTokenBinding(menuCloseIcon, PathIcon.WidthProperty, GlobalResourceKey.IconSizeSM);
TokenResourceBinder.CreateGlobalTokenBinding(menuCloseIcon, PathIcon.HeightProperty, GlobalResourceKey.IconSizeSM);
Grid.SetColumn(menuCloseIcon, 4);
closeButton.RegisterInNameScope(scope);
layout.Children.Add(itemTextPresenter);
layout.Children.Add(closeButton);
container.Child = layout;
return container;
});
}
protected override void BuildStyles()
{
var commonStyle = new Style(selector => selector.Nesting());
BuildCommonStyle(commonStyle);
BuildDisabledStyle();
Add(commonStyle);
}
private void BuildCommonStyle(Style commonStyle)
{
commonStyle.Add(TabStripMenuItem.ForegroundProperty, MenuResourceKey.ItemColor);
{
var borderStyle = new Style(selector => selector.Nesting().Template().Name(ItemDecoratorPart));
borderStyle.Add(Border.MinHeightProperty, MenuResourceKey.ItemHeight);
borderStyle.Add(Border.PaddingProperty, MenuResourceKey.ItemPaddingInline);
borderStyle.Add(Border.BackgroundProperty, MenuResourceKey.ItemBg);
borderStyle.Add(Border.CornerRadiusProperty, MenuResourceKey.ItemBorderRadius);
commonStyle.Add(borderStyle);
}
// Hover 状态
var hoverStyle = new Style(selector => selector.Nesting().Class(StdPseudoClass.PointerOver));
hoverStyle.Add(TabStripMenuItem.ForegroundProperty, MenuResourceKey.ItemHoverColor);
{
var borderStyle = new Style(selector => selector.Nesting().Template().Name(ItemDecoratorPart));
borderStyle.Add(Border.BackgroundProperty, MenuResourceKey.ItemHoverBg);
hoverStyle.Add(borderStyle);
}
commonStyle.Add(hoverStyle);
}
private void BuildDisabledStyle()
{
var disabledStyle = new Style(selector => selector.Nesting().Class(StdPseudoClass.Disabled));
disabledStyle.Add(TabStripMenuItem.ForegroundProperty, MenuResourceKey.ItemDisabledColor);
Add(disabledStyle);
}
}