From c939b25884c1475d788a5ee07d09f9e26cc6d919 Mon Sep 17 00:00:00 2001 From: polarboy Date: Sun, 4 Aug 2024 15:28:24 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=20CardTabControl=20=E6=A0=87?= =?UTF-8?q?=E7=AD=BE=E5=B8=83=E5=B1=80=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 完成 CardTabControl 标签布局类 --- .../ShowCase/TabControlShowCase.axaml | 7 ++ .../TabControl/CardTabControlTheme.cs | 15 ++- .../TabControl/TabScrollContentPresenter.cs | 1 - .../TabControl/TabStrip/BaseTabStripTheme.cs | 26 ++-- .../TabControl/TabStrip/CardTabStrip.cs | 75 ------------ .../TabControl/TabStrip/CardTabStripTheme.cs | 25 ++-- .../TabControl/TabStrip/TabStripTheme.cs | 15 ++- .../TabControl/TabsContainerPanel.cs | 115 ++++++++++++++++++ 8 files changed, 171 insertions(+), 108 deletions(-) create mode 100644 src/AtomUI.Controls/TabControl/TabsContainerPanel.cs diff --git a/samples/AtomUI.Demo.Desktop/ShowCase/TabControlShowCase.axaml b/samples/AtomUI.Demo.Desktop/ShowCase/TabControlShowCase.axaml index 0fc880d..3236865 100644 --- a/samples/AtomUI.Demo.Desktop/ShowCase/TabControlShowCase.axaml +++ b/samples/AtomUI.Demo.Desktop/ShowCase/TabControlShowCase.axaml @@ -103,6 +103,13 @@ Tab 2 Tab 3 + + + Tab 1 + Tab 2 + Tab 3 + + diff --git a/src/AtomUI.Controls/TabControl/CardTabControlTheme.cs b/src/AtomUI.Controls/TabControl/CardTabControlTheme.cs index e80e976..872976e 100644 --- a/src/AtomUI.Controls/TabControl/CardTabControlTheme.cs +++ b/src/AtomUI.Controls/TabControl/CardTabControlTheme.cs @@ -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); } diff --git a/src/AtomUI.Controls/TabControl/TabScrollContentPresenter.cs b/src/AtomUI.Controls/TabControl/TabScrollContentPresenter.cs index ad4b8ff..ac930f3 100644 --- a/src/AtomUI.Controls/TabControl/TabScrollContentPresenter.cs +++ b/src/AtomUI.Controls/TabControl/TabScrollContentPresenter.cs @@ -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); } diff --git a/src/AtomUI.Controls/TabControl/TabStrip/BaseTabStripTheme.cs b/src/AtomUI.Controls/TabControl/TabStrip/BaseTabStripTheme.cs index 0e54da5..2a851c4 100644 --- a/src/AtomUI.Controls/TabControl/TabStrip/BaseTabStripTheme.cs +++ b/src/AtomUI.Controls/TabControl/TabStrip/BaseTabStripTheme.cs @@ -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); diff --git a/src/AtomUI.Controls/TabControl/TabStrip/CardTabStrip.cs b/src/AtomUI.Controls/TabControl/TabStrip/CardTabStrip.cs index 0bf0431..afc6ea9 100644 --- a/src/AtomUI.Controls/TabControl/TabStrip/CardTabStrip.cs +++ b/src/AtomUI.Controls/TabControl/TabStrip/CardTabStrip.cs @@ -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() { diff --git a/src/AtomUI.Controls/TabControl/TabStrip/CardTabStripTheme.cs b/src/AtomUI.Controls/TabControl/TabStrip/CardTabStripTheme.cs index 73dd0ba..f3da7b1 100644 --- a/src/AtomUI.Controls/TabControl/TabStrip/CardTabStripTheme.cs +++ b/src/AtomUI.Controls/TabControl/TabStrip/CardTabStripTheme.cs @@ -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) diff --git a/src/AtomUI.Controls/TabControl/TabStrip/TabStripTheme.cs b/src/AtomUI.Controls/TabControl/TabStrip/TabStripTheme.cs index 35cdf06..9696f77 100644 --- a/src/AtomUI.Controls/TabControl/TabStrip/TabStripTheme.cs +++ b/src/AtomUI.Controls/TabControl/TabStrip/TabStripTheme.cs @@ -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) diff --git a/src/AtomUI.Controls/TabControl/TabsContainerPanel.cs b/src/AtomUI.Controls/TabControl/TabsContainerPanel.cs new file mode 100644 index 0000000..1a36201 --- /dev/null +++ b/src/AtomUI.Controls/TabControl/TabsContainerPanel.cs @@ -0,0 +1,115 @@ +using Avalonia; +using Avalonia.Controls; + +namespace AtomUI.Controls; + +internal class TabsContainerPanel : Panel +{ + #region 公共属性定义 + + public static readonly DirectProperty TabScrollViewerProperty = + AvaloniaProperty.RegisterDirect(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 AddTabButtonProperty = + AvaloniaProperty.RegisterDirect(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 TabStripPlacementProperty = + AvaloniaProperty.RegisterDirect(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(TabScrollViewerProperty, AddTabButtonProperty); + AffectsArrange(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(); + 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(); + if (oldAddTabButton is not null) { + Children.Remove(oldAddTabButton); + } + + if (AddTabButton is not null) { + Children.Add(AddTabButton); + } + } + } +} \ No newline at end of file