完成 CardTabControl 标签布局类

完成 CardTabControl 标签布局类
This commit is contained in:
polarboy 2024-08-04 15:28:24 +08:00
parent c92a0f557f
commit c939b25884
8 changed files with 171 additions and 108 deletions

View File

@ -103,6 +103,13 @@
<atom:TabStripItem>Tab 2</atom:TabStripItem>
<atom:TabStripItem>Tab 3</atom:TabStripItem>
</atom:TabStrip>
<atom:CardTabStrip TabAlignmentCenter="True">
<atom:TabStripItem>Tab 1</atom:TabStripItem>
<atom:TabStripItem>Tab 2</atom:TabStripItem>
<atom:TabStripItem>Tab 3</atom:TabStripItem>
</atom:CardTabStrip>
</StackPanel>
</showcase:ShowCaseItem>

View File

@ -27,14 +27,12 @@ internal class CardTabControlTheme : BaseTabControlTheme
CreateTemplateParentBinding(alignWrapper, DockPanel.DockProperty, BaseTabControl.TabStripPlacementProperty);
CreateTemplateParentBinding(alignWrapper, Panel.MarginProperty,TabControl.TabStripMarginProperty);
var cardTabStripContainer = new DockPanel()
var cardTabControlContainer = new TabsContainerPanel()
{
Name = TabsContainerPart,
};
cardTabStripContainer.RegisterInNameScope(scope);
TokenResourceBinder.CreateTokenBinding(cardTabStripContainer, StackPanel.SpacingProperty,
TabControlResourceKey.CardGutter);
cardTabControlContainer.RegisterInNameScope(scope);
CreateTemplateParentBinding(cardTabControlContainer, TabsContainerPanel.TabStripPlacementProperty, TabStrip.TabStripPlacementProperty);
var tabScrollViewer = new TabControlScrollViewer()
{
@ -64,6 +62,7 @@ internal class CardTabControlTheme : BaseTabControlTheme
BorderThickness = new Thickness(1),
Icon = addTabIcon
};
DockPanel.SetDock(addTabButton, Dock.Right);
CreateTemplateParentBinding(addTabButton, IconButton.BorderThicknessProperty, CardTabControl.CardBorderThicknessProperty);
CreateTemplateParentBinding(addTabButton, IconButton.CornerRadiusProperty, CardTabControl.CardBorderRadiusProperty);
@ -73,10 +72,10 @@ internal class CardTabControlTheme : BaseTabControlTheme
addTabButton.RegisterInNameScope(scope);
cardTabStripContainer.Children.Add(addTabButton);
cardTabStripContainer.Children.Add(tabScrollViewer);
cardTabControlContainer.TabScrollViewer = tabScrollViewer;
cardTabControlContainer.AddTabButton = addTabButton;
alignWrapper.Children.Add(cardTabStripContainer);
alignWrapper.Children.Add(cardTabControlContainer);
container.Children.Add(alignWrapper);
}

View File

@ -29,7 +29,6 @@ internal class TabScrollContentPresenter : ScrollContentPresenter, ICustomHitTes
var x = Offset.X;
var y = Offset.Y;
var delta = e.Delta;
if (TabStripPlacement == Dock.Top || TabStripPlacement == Dock.Bottom) {
delta = new Vector(delta.Y, delta.X);
}

View File

@ -12,6 +12,8 @@ internal class BaseTabStripTheme : BaseControlTheme
{
public const string FrameDecoratorPart = "PART_FrameDecorator";
public const string ItemsPresenterPart = "PART_ItemsPresenter";
public const string TabsContainerPart = "PART_TabsContainer";
public const string AlignWrapperPart = "PART_AlignWrapper";
public BaseTabStripTheme(Type targetType) : base(targetType) { }
@ -50,9 +52,9 @@ internal class BaseTabStripTheme : BaseControlTheme
// tabs 是否居中
var tabAlignCenterStyle = new Style(selector => selector.Nesting().PropertyEquals(TabStrip.TabAlignmentCenterProperty, true));
{
var itemsPresenterStyle = new Style(selector => selector.Nesting().Template().Name(ItemsPresenterPart));
itemsPresenterStyle.Add(ItemsPresenter.HorizontalAlignmentProperty, HorizontalAlignment.Center);
tabAlignCenterStyle.Add(itemsPresenterStyle);
var tabsContainerStyle = new Style(selector => selector.Nesting().Template().Name(TabsContainerPart));
tabsContainerStyle.Add(ItemsPresenter.HorizontalAlignmentProperty, HorizontalAlignment.Center);
tabAlignCenterStyle.Add(tabsContainerStyle);
}
topStyle.Add(tabAlignCenterStyle);
@ -69,9 +71,9 @@ internal class BaseTabStripTheme : BaseControlTheme
// tabs 是否居中
var tabAlignCenterStyle = new Style(selector => selector.Nesting().PropertyEquals(TabStrip.TabAlignmentCenterProperty, true));
{
var itemsPresenterStyle = new Style(selector => selector.Nesting().Template().Name(ItemsPresenterPart));
itemsPresenterStyle.Add(ItemsPresenter.VerticalAlignmentProperty, VerticalAlignment.Center);
tabAlignCenterStyle.Add(itemsPresenterStyle);
var tabsContainerStyle = new Style(selector => selector.Nesting().Template().Name(TabsContainerPart));
tabsContainerStyle.Add(ItemsPresenter.VerticalAlignmentProperty, VerticalAlignment.Center);
tabAlignCenterStyle.Add(tabsContainerStyle);
}
rightStyle.Add(tabAlignCenterStyle);
@ -86,9 +88,9 @@ internal class BaseTabStripTheme : BaseControlTheme
// tabs 是否居中
var tabAlignCenterStyle = new Style(selector => selector.Nesting().PropertyEquals(TabStrip.TabAlignmentCenterProperty, true));
{
var itemsPresenterStyle = new Style(selector => selector.Nesting().Template().Name(ItemsPresenterPart));
itemsPresenterStyle.Add(ItemsPresenter.HorizontalAlignmentProperty, HorizontalAlignment.Center);
tabAlignCenterStyle.Add(itemsPresenterStyle);
var tabsContainerStyle = new Style(selector => selector.Nesting().Template().Name(TabsContainerPart));
tabsContainerStyle.Add(ItemsPresenter.HorizontalAlignmentProperty, HorizontalAlignment.Center);
tabAlignCenterStyle.Add(tabsContainerStyle);
}
bottomStyle.Add(tabAlignCenterStyle);
@ -105,9 +107,9 @@ internal class BaseTabStripTheme : BaseControlTheme
// tabs 是否居中
var tabAlignCenterStyle = new Style(selector => selector.Nesting().PropertyEquals(TabStrip.TabAlignmentCenterProperty, true));
{
var itemsPresenterStyle = new Style(selector => selector.Nesting().Template().Name(ItemsPresenterPart));
itemsPresenterStyle.Add(ItemsPresenter.VerticalAlignmentProperty, VerticalAlignment.Center);
tabAlignCenterStyle.Add(itemsPresenterStyle);
var tabsContainerStyle = new Style(selector => selector.Nesting().Template().Name(TabsContainerPart));
tabsContainerStyle.Add(ItemsPresenter.VerticalAlignmentProperty, VerticalAlignment.Center);
tabAlignCenterStyle.Add(tabsContainerStyle);
}
leftStyle.Add(tabAlignCenterStyle);

View File

@ -127,37 +127,10 @@ public class CardTabStrip : BaseTabStrip, IControlCustomStyle
if (change.Property == SizeTypeProperty) {
HandleSizeTypeChanged();
} else if (change.Property == TabStripPlacementProperty) {
SetupCardTabStripContainer();
HandleTabStripPlacementChanged();
}
}
private void SetupCardTabStripContainer(Size finalSize)
{
if (_cardTabStripContainer is not null) {
double addButtonOffset = 0;
double markOffset = 0;
if (TabStripPlacement == Dock.Top || TabStripPlacement == Dock.Bottom) {
addButtonOffset = _addTabButton?.Bounds.Right ?? 0;
markOffset = finalSize.Width;
if (addButtonOffset > markOffset) {
_cardTabStripContainer.ColumnDefinitions[0].Width = GridLength.Star;
} else {
_cardTabStripContainer.ColumnDefinitions[0].Width = GridLength.Auto;
}
} else {
addButtonOffset = _addTabButton?.Bounds.Bottom ?? 0;
markOffset = finalSize.Height;
if (addButtonOffset > markOffset) {
_cardTabStripContainer.RowDefinitions[0].Height = GridLength.Star;
} else {
_cardTabStripContainer.RowDefinitions[0].Height = GridLength.Auto;
}
}
}
}
#region IControlCustomStyle
void IControlCustomStyle.HandleTemplateApplied(INameScope scope)
@ -170,7 +143,6 @@ public class CardTabStrip : BaseTabStrip, IControlCustomStyle
_addTabButton.Click += HandleAddButtonClicked;
}
HandleSizeTypeChanged();
SetupCardTabStripContainer();
}
#endregion
@ -193,56 +165,9 @@ public class CardTabStrip : BaseTabStrip, IControlCustomStyle
protected override Size ArrangeOverride(Size finalSize)
{
var size = base.ArrangeOverride(finalSize);
SetupCardTabStripContainer(finalSize);
HandleTabStripPlacementChanged();
return size;
}
private void SetupCardTabStripContainer()
{
if (TabStripPlacement == Dock.Top ||
TabStripPlacement == Dock.Bottom) {
if (_cardTabStripContainer is not null) {
_cardTabStripContainer.Children.Clear();
_cardTabStripContainer.RowDefinitions.Clear();
_cardTabStripContainer.ColumnDefinitions = new ColumnDefinitions()
{
new ColumnDefinition(GridLength.Auto),
new ColumnDefinition(GridLength.Auto),
};
}
if (_tabScrollViewer is not null) {
Grid.SetColumn(_tabScrollViewer, 0);
}
if (_addTabButton is not null) {
Grid.SetColumn(_addTabButton, 1);
}
_cardTabStripContainer!.Children.Add(_tabScrollViewer!);
_cardTabStripContainer.Children.Add(_addTabButton!);
} else {
if (_cardTabStripContainer is not null) {
_cardTabStripContainer.Children.Clear();
_cardTabStripContainer.ColumnDefinitions.Clear();
_cardTabStripContainer.RowDefinitions = new RowDefinitions()
{
new RowDefinition(GridLength.Auto),
new RowDefinition(GridLength.Auto),
};
}
if (_tabScrollViewer is not null) {
Grid.SetRow(_tabScrollViewer, 0);
}
if (_addTabButton is not null) {
Grid.SetRow(_addTabButton, 1);
}
_cardTabStripContainer!.Children.Add(_tabScrollViewer!);
_cardTabStripContainer.Children.Add(_addTabButton!);
}
}
private void HandleTabStripPlacementChanged()
{

View File

@ -12,22 +12,24 @@ namespace AtomUI.Controls;
[ControlThemeProvider]
internal class CardTabStripTheme : BaseTabStripTheme
{
public const string AddTabButtonPart = "PART_AddTabButton";
public const string CardTabStripContainerPart = "PART_CardTabStripContainer";
public const string AddTabButtonPart = "PART_AddTabButton";
public const string CardTabStripContainerPart = "PART_CardTabStripContainer";
public const string CardTabStripScrollViewerPart = "PART_CardTabStripScrollViewer";
public CardTabStripTheme() : base(typeof(CardTabStrip)) { }
protected override void NotifyBuildControlTemplate(BaseTabStrip baseTabStrip, INameScope scope, Border container)
{
var cardTabStripContainer = new Grid()
var alignWrapper = new Panel()
{
Name = CardTabStripContainerPart,
Name = AlignWrapperPart
};
var cardTabStripContainer = new TabsContainerPanel()
{
Name = TabsContainerPart,
};
cardTabStripContainer.RegisterInNameScope(scope);
TokenResourceBinder.CreateTokenBinding(cardTabStripContainer, StackPanel.SpacingProperty,
TabControlResourceKey.CardGutter);
CreateTemplateParentBinding(cardTabStripContainer, TabsContainerPanel.TabStripPlacementProperty, TabStrip.TabStripPlacementProperty);
var tabScrollViewer = new TabStripScrollViewer()
{
@ -65,10 +67,13 @@ internal class CardTabStripTheme : BaseTabStripTheme
TokenResourceBinder.CreateGlobalResourceBinding(addTabButton, IconButton.BorderBrushProperty, GlobalResourceKey.ColorBorderSecondary);
addTabButton.RegisterInNameScope(scope);
cardTabStripContainer.TabScrollViewer = tabScrollViewer;
cardTabStripContainer.AddTabButton = addTabButton;
cardTabStripContainer.Children.Add(tabScrollViewer);
cardTabStripContainer.Children.Add(addTabButton);
container.Child = cardTabStripContainer;
alignWrapper.Children.Add(cardTabStripContainer);
container.Child = alignWrapper;
}
private ItemsPresenter CreateTabStripContentPanel(INameScope scope)

View File

@ -17,12 +17,23 @@ internal class TabStripTheme : BaseTabStripTheme
protected override void NotifyBuildControlTemplate(BaseTabStrip baseTabStrip, INameScope scope, Border container)
{
var tabScrollViewer = new TabStripScrollViewer();
var alignWrapper = new Panel()
{
Name = AlignWrapperPart
};
alignWrapper.RegisterInNameScope(scope);
var tabScrollViewer = new TabStripScrollViewer()
{
Name = TabsContainerPart
};
CreateTemplateParentBinding(tabScrollViewer, BaseTabScrollViewer.TabStripPlacementProperty, TabStrip.TabStripPlacementProperty);
var contentPanel = CreateTabStripContentPanel(scope);
tabScrollViewer.Content = contentPanel;
tabScrollViewer.TabStrip = baseTabStrip;
container.Child = tabScrollViewer;
alignWrapper.Children.Add(tabScrollViewer);
container.Child = alignWrapper;
}
private Panel CreateTabStripContentPanel(INameScope scope)

View File

@ -0,0 +1,115 @@
using Avalonia;
using Avalonia.Controls;
namespace AtomUI.Controls;
internal class TabsContainerPanel : Panel
{
#region
public static readonly DirectProperty<TabsContainerPanel, BaseTabScrollViewer?> TabScrollViewerProperty =
AvaloniaProperty.RegisterDirect<TabsContainerPanel, BaseTabScrollViewer?>(nameof(TabScrollViewer),
o => o.TabScrollViewer,
(o, v) => o.TabScrollViewer = v);
private BaseTabScrollViewer? _tabScrollViewer;
public BaseTabScrollViewer? TabScrollViewer
{
get => _tabScrollViewer;
set => SetAndRaise(TabScrollViewerProperty, ref _tabScrollViewer, value);
}
public static readonly DirectProperty<TabsContainerPanel, IconButton?> AddTabButtonProperty =
AvaloniaProperty.RegisterDirect<TabsContainerPanel, IconButton?>(nameof(AddTabButton),
o => o.AddTabButton,
(o, v) => o.AddTabButton = v);
private IconButton? _addTabButton;
public IconButton? AddTabButton
{
get => _addTabButton;
set => SetAndRaise(AddTabButtonProperty, ref _addTabButton, value);
}
#endregion
#region
internal static readonly DirectProperty<TabsContainerPanel, Dock> TabStripPlacementProperty =
AvaloniaProperty.RegisterDirect<TabsContainerPanel, Dock>(nameof(TabStripPlacement),
o => o.TabStripPlacement,
(o, v) => o.TabStripPlacement = v);
private Dock _tabStripPlacement;
internal Dock TabStripPlacement
{
get => _tabStripPlacement;
set => SetAndRaise(TabStripPlacementProperty, ref _tabStripPlacement, value);
}
#endregion
static TabsContainerPanel()
{
AffectsMeasure<TabsContainerPanel>(TabScrollViewerProperty, AddTabButtonProperty);
AffectsArrange<TabsContainerPanel>(TabStripPlacementProperty);
}
protected override Size ArrangeOverride(Size arrangeSize)
{
// TODO 暂时不做验证,默认认为两个元素都存在
// 理论上这里要报错,但是我们是内部使用
if (_tabScrollViewer is not null && _addTabButton is not null) {
if (TabStripPlacement == Dock.Top || TabStripPlacement == Dock.Bottom) {
var scrollViewerDesiredWidth = _tabScrollViewer.DesiredSize.Width;
var addTabButtonDesiredWidth = _addTabButton.DesiredSize.Width;
var totalDesiredWidth = scrollViewerDesiredWidth + addTabButtonDesiredWidth;
if (totalDesiredWidth > arrangeSize.Width) {
_tabScrollViewer.Arrange(new Rect(new Point(0, 0), new Size(arrangeSize.Width - addTabButtonDesiredWidth, arrangeSize.Height)));
_addTabButton.Arrange(new Rect(new Point(arrangeSize.Width - addTabButtonDesiredWidth, 0), _addTabButton.DesiredSize));
} else {
_tabScrollViewer.Arrange(new Rect(new Point(0, 0), _tabScrollViewer.DesiredSize));
_addTabButton.Arrange(new Rect(new Point(scrollViewerDesiredWidth, 0), _addTabButton.DesiredSize));
}
} else {
var scrollViewerDesiredHeight = _tabScrollViewer.DesiredSize.Height;
var addTabButtonDesiredHeight = _addTabButton.DesiredSize.Height;
var totalDesiredHeight = scrollViewerDesiredHeight + addTabButtonDesiredHeight;
if (totalDesiredHeight > arrangeSize.Height) {
_tabScrollViewer.Arrange(new Rect(new Point(0, 0), new Size(arrangeSize.Width, arrangeSize.Height - addTabButtonDesiredHeight)));
_addTabButton.Arrange(new Rect(new Point(0, arrangeSize.Width - addTabButtonDesiredHeight), _addTabButton.DesiredSize));
} else {
_tabScrollViewer.Arrange(new Rect(new Point(0, 0), _tabScrollViewer.DesiredSize));
_addTabButton.Arrange(new Rect(new Point(scrollViewerDesiredHeight, 0), _addTabButton.DesiredSize));
}
}
}
return (arrangeSize);
}
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
if (change.Property == TabScrollViewerProperty) {
var oldScrollViewer = change.GetOldValue<BaseTabScrollViewer?>();
if (oldScrollViewer is not null) {
Children.Remove(oldScrollViewer);
}
if (TabScrollViewer is not null) {
Children.Add(TabScrollViewer);
}
} else if (change.Property == AddTabButtonProperty) {
var oldAddTabButton = change.GetOldValue<IconButton?>();
if (oldAddTabButton is not null) {
Children.Remove(oldAddTabButton);
}
if (AddTabButton is not null) {
Children.Add(AddTabButton);
}
}
}
}