mirror of
https://gitee.com/chinware/atomui.git
synced 2024-12-02 03:47:52 +08:00
完成tabstrip 阴影
This commit is contained in:
parent
d1867a00a1
commit
1c7c8e458a
@ -55,5 +55,37 @@
|
||||
</atom:TabStrip>
|
||||
</StackPanel>
|
||||
</showcase:ShowCaseItem>
|
||||
|
||||
<showcase:ShowCaseItem
|
||||
Title="Slide"
|
||||
Description="In order to fit in more tabs, they can slide left and right (or up and down).">
|
||||
<StackPanel Orientation="Vertical" Spacing="20">
|
||||
<atom:TabStrip>
|
||||
<atom:TabStripItem>Tab 1</atom:TabStripItem>
|
||||
<atom:TabStripItem>Tab 2</atom:TabStripItem>
|
||||
<atom:TabStripItem>Tab 3</atom:TabStripItem>
|
||||
<atom:TabStripItem>Tab 4</atom:TabStripItem>
|
||||
<atom:TabStripItem>Tab 5</atom:TabStripItem>
|
||||
<atom:TabStripItem>Tab 6</atom:TabStripItem>
|
||||
<atom:TabStripItem>Tab 7</atom:TabStripItem>
|
||||
<atom:TabStripItem>Tab 8</atom:TabStripItem>
|
||||
<atom:TabStripItem>Tab 9</atom:TabStripItem>
|
||||
<atom:TabStripItem>Tab 10</atom:TabStripItem>
|
||||
<atom:TabStripItem>Tab 11</atom:TabStripItem>
|
||||
<atom:TabStripItem>Tab 12</atom:TabStripItem>
|
||||
<atom:TabStripItem>Tab 13</atom:TabStripItem>
|
||||
<atom:TabStripItem>Tab 14</atom:TabStripItem>
|
||||
<atom:TabStripItem>Tab 15</atom:TabStripItem>
|
||||
<atom:TabStripItem>Tab 16</atom:TabStripItem>
|
||||
<atom:TabStripItem>Tab 17</atom:TabStripItem>
|
||||
<atom:TabStripItem>Tab 18</atom:TabStripItem>
|
||||
<atom:TabStripItem>Tab 19</atom:TabStripItem>
|
||||
<atom:TabStripItem>Tab 20</atom:TabStripItem>
|
||||
<atom:TabStripItem>Tab 21</atom:TabStripItem>
|
||||
<atom:TabStripItem>Tab 22</atom:TabStripItem>
|
||||
<atom:TabStripItem>Tab 23</atom:TabStripItem>
|
||||
</atom:TabStrip>
|
||||
</StackPanel>
|
||||
</showcase:ShowCaseItem>
|
||||
</showcase:ShowCasePanel>
|
||||
</UserControl>
|
@ -315,6 +315,9 @@ namespace AtomUI.Theme.Styling
|
||||
public static readonly TokenResourceKey ItemSelectedColor = new TokenResourceKey("TabControl.ItemSelectedColor");
|
||||
public static readonly TokenResourceKey CardGutter = new TokenResourceKey("TabControl.CardGutter");
|
||||
public static readonly TokenResourceKey ItemIconMargin = new TokenResourceKey("TabControl.ItemIconMargin");
|
||||
public static readonly TokenResourceKey MenuIndicatorPaddingHorizontal = new TokenResourceKey("TabControl.MenuIndicatorPaddingHorizontal");
|
||||
public static readonly TokenResourceKey MenuIndicatorPaddingVertical = new TokenResourceKey("TabControl.MenuIndicatorPaddingVertical");
|
||||
public static readonly TokenResourceKey MenuEdgeThickness = new TokenResourceKey("TabControl.MenuEdgeThickness");
|
||||
}
|
||||
|
||||
public static class TagResourceKey
|
||||
|
@ -7,6 +7,7 @@ using Avalonia.Controls.Templates;
|
||||
using Avalonia.Layout;
|
||||
using Avalonia.LogicalTree;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Rendering;
|
||||
|
||||
namespace AtomUI.Controls;
|
||||
|
||||
|
@ -122,6 +122,21 @@ internal class TabControlToken : AbstractControlDesignToken
|
||||
/// </summary>
|
||||
public Thickness ItemIconMargin { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 水平布局的菜单外边距
|
||||
/// </summary>
|
||||
public Thickness MenuIndicatorPaddingHorizontal { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 垂直布局的菜单外边距
|
||||
/// </summary>
|
||||
public Thickness MenuIndicatorPaddingVertical { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 滚动边缘的厚度
|
||||
/// </summary>
|
||||
public double MenuEdgeThickness { get; set; }
|
||||
|
||||
internal override void CalculateFromAlias()
|
||||
{
|
||||
base.CalculateFromAlias();
|
||||
@ -138,6 +153,7 @@ internal class TabControlToken : AbstractControlDesignToken
|
||||
bottom:_globalToken.PaddingXXS * 1.5,
|
||||
left:_globalToken.Padding,
|
||||
right:_globalToken.Padding);
|
||||
|
||||
TitleFontSize = fontToken.FontSize;
|
||||
TitleFontSizeLG = fontToken.FontSizeLG;
|
||||
TitleFontSizeSM = fontToken.FontSize;
|
||||
@ -158,5 +174,10 @@ internal class TabControlToken : AbstractControlDesignToken
|
||||
|
||||
CardGutter = _globalToken.MarginXXS / 2;
|
||||
ItemIconMargin = new Thickness(0, 0, _globalToken.MarginSM, 0);
|
||||
|
||||
MenuIndicatorPaddingHorizontal = new Thickness(_globalToken.PaddingXS, 0, 0, 0);
|
||||
MenuIndicatorPaddingVertical = new Thickness(0, _globalToken.PaddingXS, 0, 0);
|
||||
|
||||
MenuEdgeThickness = 20;
|
||||
}
|
||||
}
|
232
src/AtomUI.Controls/TabControl/TabScrollViewer.cs
Normal file
232
src/AtomUI.Controls/TabControl/TabScrollViewer.cs
Normal file
@ -0,0 +1,232 @@
|
||||
using System.Globalization;
|
||||
using AtomUI.Theme.Styling;
|
||||
using AtomUI.Utils;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Converters;
|
||||
using Avalonia.Controls.Metadata;
|
||||
using Avalonia.Controls.Presenters;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Media;
|
||||
using Colors = Avalonia.Media.Colors;
|
||||
using GradientStop = Avalonia.Media.GradientStop;
|
||||
|
||||
namespace AtomUI.Controls;
|
||||
|
||||
[TemplatePart(TabScrollViewerTheme.ScrollStartEdgeIndicatorPart, typeof(Control))]
|
||||
[TemplatePart(TabScrollViewerTheme.ScrollEndEdgeIndicatorPart, typeof(Control))]
|
||||
[TemplatePart(TabScrollViewerTheme.ScrollMenuIndicatorPart, typeof(IconButton))]
|
||||
[TemplatePart(TabScrollViewerTheme.ScrollViewContentPart, typeof(ScrollContentPresenter))]
|
||||
public class TabScrollViewer : ScrollViewer
|
||||
{
|
||||
private const int EdgeIndicatorZIndex = 1000;
|
||||
|
||||
#region 内部属性定义
|
||||
|
||||
internal static readonly DirectProperty<TabScrollViewer, Dock> TabStripPlacementProperty =
|
||||
AvaloniaProperty.RegisterDirect<TabScrollViewer, Dock>(nameof(TabStripPlacement),
|
||||
o => o.TabStripPlacement,
|
||||
(o, v) => o.TabStripPlacement = v);
|
||||
|
||||
internal static readonly DirectProperty<TabScrollViewer, IBrush?> EdgeShadowStartColorProperty =
|
||||
AvaloniaProperty.RegisterDirect<TabScrollViewer, IBrush?>(nameof(EdgeShadowStartColor),
|
||||
o => o.EdgeShadowStartColor,
|
||||
(o, v) => o.EdgeShadowStartColor = v);
|
||||
|
||||
internal static readonly DirectProperty<TabScrollViewer, double> MenuEdgeThicknessProperty =
|
||||
AvaloniaProperty.RegisterDirect<TabScrollViewer, double>(nameof(MenuEdgeThickness),
|
||||
o => o.MenuEdgeThickness,
|
||||
(o, v) => o.MenuEdgeThickness = v);
|
||||
|
||||
private Dock _tabStripPlacement;
|
||||
|
||||
internal Dock TabStripPlacement
|
||||
{
|
||||
get => _tabStripPlacement;
|
||||
set => SetAndRaise(TabStripPlacementProperty, ref _tabStripPlacement, value);
|
||||
}
|
||||
|
||||
private IBrush? _edgeShadowStartColor;
|
||||
|
||||
internal IBrush? EdgeShadowStartColor
|
||||
{
|
||||
get => _edgeShadowStartColor;
|
||||
set => SetAndRaise(EdgeShadowStartColorProperty, ref _edgeShadowStartColor, value);
|
||||
}
|
||||
|
||||
private double _menuEdgeThickness;
|
||||
|
||||
internal double MenuEdgeThickness
|
||||
{
|
||||
get => _menuEdgeThickness;
|
||||
set => SetAndRaise(MenuEdgeThicknessProperty, ref _menuEdgeThickness, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private IconButton? _menuIndicator;
|
||||
private Border? _startEdgeIndicator;
|
||||
private Border? _endEdgeIndicator;
|
||||
|
||||
static TabScrollViewer()
|
||||
{
|
||||
AffectsMeasure<TabScrollViewer>(TabStripPlacementProperty);
|
||||
}
|
||||
|
||||
public TabScrollViewer()
|
||||
{
|
||||
HorizontalScrollBarVisibility = ScrollBarVisibility.Auto;
|
||||
}
|
||||
|
||||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
|
||||
{
|
||||
base.OnPropertyChanged(change);
|
||||
if (change.Property == TabStripPlacementProperty) {
|
||||
if (Presenter is TabStripScrollContentPresenter tabStripScrollContentPresenter) {
|
||||
tabStripScrollContentPresenter.TabStripPlacement = TabStripPlacement;
|
||||
}
|
||||
} else if (change.Property == VerticalScrollBarVisibilityProperty ||
|
||||
change.Property == OffsetProperty ||
|
||||
change.Property == ExtentProperty ||
|
||||
change.Property == ViewportProperty) {
|
||||
SetupIndicatorsVisibility();
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupEdgeIndicatorSize()
|
||||
{
|
||||
if (Presenter is not null) {
|
||||
if (TabStripPlacement == Dock.Top || TabStripPlacement == Dock.Bottom) {
|
||||
if (_startEdgeIndicator is not null) {
|
||||
_startEdgeIndicator.Height = Presenter.DesiredSize.Height;
|
||||
_startEdgeIndicator.ZIndex = EdgeIndicatorZIndex;
|
||||
_startEdgeIndicator.Background = BuildEdgeIndicatorBrush(TabStripPlacement, true);
|
||||
}
|
||||
|
||||
if (_endEdgeIndicator is not null) {
|
||||
_endEdgeIndicator.Height = Presenter.DesiredSize.Height;
|
||||
_endEdgeIndicator.Margin = new Thickness(0, 0, _menuEdgeThickness, 0);
|
||||
_endEdgeIndicator.ZIndex = EdgeIndicatorZIndex;
|
||||
_endEdgeIndicator.Background = BuildEdgeIndicatorBrush(TabStripPlacement, false);
|
||||
}
|
||||
} else {
|
||||
if (_startEdgeIndicator is not null) {
|
||||
_startEdgeIndicator.Width = Presenter.DesiredSize.Width;
|
||||
_startEdgeIndicator.ZIndex = EdgeIndicatorZIndex;
|
||||
_startEdgeIndicator.Background = BuildEdgeIndicatorBrush(TabStripPlacement, false);
|
||||
}
|
||||
|
||||
if (_endEdgeIndicator is not null) {
|
||||
_endEdgeIndicator.Width = Presenter.DesiredSize.Width;
|
||||
_endEdgeIndicator.Margin = new Thickness(0, _menuEdgeThickness, 0, 0);
|
||||
_endEdgeIndicator.ZIndex = EdgeIndicatorZIndex;
|
||||
_endEdgeIndicator.Background = BuildEdgeIndicatorBrush(TabStripPlacement, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private LinearGradientBrush BuildEdgeIndicatorBrush(Dock placement, bool isStart)
|
||||
{
|
||||
var linearGradientBrush = new LinearGradientBrush()
|
||||
{
|
||||
GradientStops =
|
||||
{
|
||||
new GradientStop((_edgeShadowStartColor as SolidColorBrush)!.Color, 0),
|
||||
new GradientStop(Colors.Transparent, 1),
|
||||
}
|
||||
};
|
||||
if (placement == Dock.Top || placement == Dock.Bottom) {
|
||||
if (isStart) {
|
||||
linearGradientBrush.StartPoint = new RelativePoint(0, 0.5, RelativeUnit.Relative);
|
||||
linearGradientBrush.EndPoint = new RelativePoint(1, 0.5, RelativeUnit.Relative);
|
||||
} else {
|
||||
linearGradientBrush.StartPoint = new RelativePoint(1, 0.5, RelativeUnit.Relative);
|
||||
linearGradientBrush.EndPoint = new RelativePoint(0, 0.5, RelativeUnit.Relative);
|
||||
}
|
||||
} else {
|
||||
if (isStart) {
|
||||
linearGradientBrush.StartPoint = new RelativePoint(0.5, 0, RelativeUnit.Relative);
|
||||
linearGradientBrush.EndPoint = new RelativePoint(0.5, 1, RelativeUnit.Relative);
|
||||
} else {
|
||||
linearGradientBrush.StartPoint = new RelativePoint(0.5, 1, RelativeUnit.Relative);
|
||||
linearGradientBrush.EndPoint = new RelativePoint(0.5, 0, RelativeUnit.Relative);
|
||||
}
|
||||
}
|
||||
return linearGradientBrush;
|
||||
}
|
||||
|
||||
protected override bool RegisterContentPresenter(ContentPresenter presenter)
|
||||
{
|
||||
if (presenter is TabStripScrollContentPresenter tabStripScrollContentPresenter) {
|
||||
tabStripScrollContentPresenter.TabStripPlacement = TabStripPlacement;
|
||||
}
|
||||
|
||||
return base.RegisterContentPresenter(presenter);
|
||||
}
|
||||
|
||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||
{
|
||||
base.OnApplyTemplate(e);
|
||||
_menuIndicator = e.NameScope.Find<IconButton>(TabScrollViewerTheme.ScrollMenuIndicatorPart);
|
||||
_startEdgeIndicator = e.NameScope.Find<Border>(TabScrollViewerTheme.ScrollStartEdgeIndicatorPart);
|
||||
_endEdgeIndicator = e.NameScope.Find<Border>(TabScrollViewerTheme.ScrollEndEdgeIndicatorPart);
|
||||
|
||||
TokenResourceBinder.CreateTokenBinding(this, EdgeShadowStartColorProperty, GlobalResourceKey.ColorFillSecondary);
|
||||
TokenResourceBinder.CreateTokenBinding(this, MenuEdgeThicknessProperty, TabControlResourceKey.MenuEdgeThickness);
|
||||
|
||||
SetupIndicatorsVisibility();
|
||||
}
|
||||
|
||||
protected override Size MeasureOverride(Size availableSize)
|
||||
{
|
||||
var size = base.MeasureOverride(availableSize);
|
||||
SetupEdgeIndicatorSize();
|
||||
return size;
|
||||
}
|
||||
|
||||
private void SetupIndicatorsVisibility()
|
||||
{
|
||||
var args = new List<object?>();
|
||||
|
||||
object? scrollUpVisibility = default;
|
||||
object? scrollDownVisibility = default;
|
||||
if (TabStripPlacement == Dock.Top || TabStripPlacement == Dock.Bottom) {
|
||||
args.Add(ScrollBarVisibility.Auto);
|
||||
args.Add(Offset.X);
|
||||
args.Add(Extent.Width);
|
||||
args.Add(Viewport.Width);
|
||||
scrollUpVisibility =
|
||||
MenuScrollingVisibilityConverter.Instance.Convert(args, typeof(bool), 0d, CultureInfo.CurrentCulture);
|
||||
scrollDownVisibility =
|
||||
MenuScrollingVisibilityConverter.Instance.Convert(args, typeof(bool), 100d, CultureInfo.CurrentCulture);
|
||||
} else {
|
||||
args.Add(ScrollBarVisibility.Auto);
|
||||
args.Add(Offset.Y);
|
||||
args.Add(Extent.Height);
|
||||
args.Add(Viewport.Height);
|
||||
scrollUpVisibility =
|
||||
MenuScrollingVisibilityConverter.Instance.Convert(args, typeof(bool), 0d, CultureInfo.CurrentCulture);
|
||||
scrollDownVisibility =
|
||||
MenuScrollingVisibilityConverter.Instance.Convert(args, typeof(bool), 100d, CultureInfo.CurrentCulture);
|
||||
}
|
||||
|
||||
if (_startEdgeIndicator is not null &&
|
||||
scrollUpVisibility is not null &&
|
||||
scrollUpVisibility != AvaloniaProperty.UnsetValue) {
|
||||
_startEdgeIndicator.IsVisible = (bool)scrollUpVisibility;
|
||||
}
|
||||
|
||||
if (_endEdgeIndicator is not null &&
|
||||
scrollDownVisibility is not null &&
|
||||
scrollDownVisibility != AvaloniaProperty.UnsetValue) {
|
||||
_endEdgeIndicator.IsVisible = (bool)scrollDownVisibility;
|
||||
}
|
||||
|
||||
if (_menuIndicator is not null) {
|
||||
var startEdgeVisible = _startEdgeIndicator?.IsVisible ?? false;
|
||||
var endEdgeVisible = _endEdgeIndicator?.IsVisible ?? false;
|
||||
_menuIndicator.IsVisible = startEdgeVisible || endEdgeVisible;
|
||||
}
|
||||
}
|
||||
}
|
230
src/AtomUI.Controls/TabControl/TabScrollViewerTheme.cs
Normal file
230
src/AtomUI.Controls/TabControl/TabScrollViewerTheme.cs
Normal file
@ -0,0 +1,230 @@
|
||||
using AtomUI.Data;
|
||||
using AtomUI.Theme;
|
||||
using AtomUI.Theme.Styling;
|
||||
using AtomUI.Utils;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Presenters;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Controls.Templates;
|
||||
using Avalonia.Input.GestureRecognizers;
|
||||
using Avalonia.Layout;
|
||||
using Avalonia.Styling;
|
||||
|
||||
namespace AtomUI.Controls;
|
||||
|
||||
[ControlThemeProvider]
|
||||
internal class TabScrollViewerTheme : BaseControlTheme
|
||||
{
|
||||
public const string ScrollStartEdgeIndicatorPart = "Part_ScrollStartEdgeIndicator";
|
||||
public const string ScrollEndEdgeIndicatorPart = "Part_ScrollEndEdgeIndicator";
|
||||
public const string ScrollMenuIndicatorPart = "Part_ScrollMenuIndicator";
|
||||
public const string ScrollViewContentPart = "PART_ContentPresenter";
|
||||
public const string ScrollViewLayoutPart = "PART_ScrollViewLayout";
|
||||
public const string ScrollViewWrapperLayoutPart = "PART_ScrollViewWrapperLayout";
|
||||
|
||||
public TabScrollViewerTheme()
|
||||
: base(typeof(TabScrollViewer)) { }
|
||||
|
||||
protected override IControlTemplate BuildControlTemplate()
|
||||
{
|
||||
return new FuncControlTemplate<TabScrollViewer>((scrollViewer, scope) =>
|
||||
{
|
||||
scrollViewer.HorizontalScrollBarVisibility = ScrollBarVisibility.Auto;
|
||||
var containerLayout = new Panel()
|
||||
{
|
||||
Name = ScrollViewWrapperLayoutPart
|
||||
};
|
||||
|
||||
var scrollViewLayout = new DockPanel()
|
||||
{
|
||||
Name = ScrollViewLayoutPart
|
||||
};
|
||||
|
||||
var menuIndicatorIcon = new PathIcon()
|
||||
{
|
||||
Kind = "EllipsisOutlined",
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
VerticalAlignment = VerticalAlignment.Center
|
||||
};
|
||||
|
||||
TokenResourceBinder.CreateTokenBinding(menuIndicatorIcon, PathIcon.WidthProperty, GlobalResourceKey.IconSize);
|
||||
TokenResourceBinder.CreateTokenBinding(menuIndicatorIcon, PathIcon.HeightProperty, GlobalResourceKey.IconSize);
|
||||
TokenResourceBinder.CreateTokenBinding(menuIndicatorIcon, PathIcon.NormalFilledBrushProperty, GlobalResourceKey.ColorTextSecondary);
|
||||
|
||||
var menuIndicator = new IconButton()
|
||||
{
|
||||
Name = ScrollMenuIndicatorPart,
|
||||
Icon = menuIndicatorIcon
|
||||
};
|
||||
|
||||
menuIndicator.RegisterInNameScope(scope);
|
||||
|
||||
var scrollViewContent = CreateScrollContentPresenter();
|
||||
|
||||
DockPanel.SetDock(scrollViewContent, Dock.Left);
|
||||
DockPanel.SetDock(menuIndicator, Dock.Right);
|
||||
|
||||
scrollViewLayout.Children.Add(menuIndicator);
|
||||
scrollViewLayout.Children.Add(scrollViewContent);
|
||||
|
||||
scrollViewContent.RegisterInNameScope(scope);
|
||||
|
||||
var startEdgeIndicator = new Border()
|
||||
{
|
||||
Name = ScrollStartEdgeIndicatorPart,
|
||||
HorizontalAlignment = HorizontalAlignment.Left,
|
||||
VerticalAlignment = VerticalAlignment.Top,
|
||||
IsHitTestVisible = false
|
||||
};
|
||||
startEdgeIndicator.RegisterInNameScope(scope);
|
||||
|
||||
containerLayout.Children.Add(startEdgeIndicator);
|
||||
|
||||
var endEdgeIndicator = new Border()
|
||||
{
|
||||
HorizontalAlignment = HorizontalAlignment.Right,
|
||||
VerticalAlignment = VerticalAlignment.Top,
|
||||
Name = ScrollEndEdgeIndicatorPart,
|
||||
IsHitTestVisible = false
|
||||
};
|
||||
endEdgeIndicator.RegisterInNameScope(scope);
|
||||
|
||||
containerLayout.Children.Add(endEdgeIndicator);
|
||||
containerLayout.Children.Add(scrollViewLayout);
|
||||
|
||||
return containerLayout;
|
||||
});
|
||||
}
|
||||
|
||||
private ScrollContentPresenter CreateScrollContentPresenter()
|
||||
{
|
||||
var scrollViewContent = new TabStripScrollContentPresenter()
|
||||
{
|
||||
Name = ScrollViewContentPart,
|
||||
};
|
||||
|
||||
CreateTemplateParentBinding(scrollViewContent, ScrollContentPresenter.MarginProperty,
|
||||
TabScrollViewer.PaddingProperty);
|
||||
CreateTemplateParentBinding(scrollViewContent, ScrollContentPresenter.HorizontalSnapPointsAlignmentProperty,
|
||||
TabScrollViewer.HorizontalSnapPointsAlignmentProperty);
|
||||
CreateTemplateParentBinding(scrollViewContent, ScrollContentPresenter.HorizontalSnapPointsTypeProperty,
|
||||
TabScrollViewer.HorizontalSnapPointsTypeProperty);
|
||||
CreateTemplateParentBinding(scrollViewContent, ScrollContentPresenter.VerticalSnapPointsAlignmentProperty,
|
||||
TabScrollViewer.VerticalSnapPointsAlignmentProperty);
|
||||
CreateTemplateParentBinding(scrollViewContent, ScrollContentPresenter.VerticalSnapPointsTypeProperty,
|
||||
TabScrollViewer.VerticalSnapPointsTypeProperty);
|
||||
var scrollGestureRecognizer = new ScrollGestureRecognizer();
|
||||
BindUtils.RelayBind(scrollViewContent, ScrollContentPresenter.CanHorizontallyScrollProperty, scrollGestureRecognizer,
|
||||
ScrollGestureRecognizer.CanHorizontallyScrollProperty);
|
||||
BindUtils.RelayBind(scrollViewContent, ScrollContentPresenter.CanVerticallyScrollProperty, scrollGestureRecognizer,
|
||||
ScrollGestureRecognizer.CanVerticallyScrollProperty);
|
||||
|
||||
CreateTemplateParentBinding(scrollGestureRecognizer, ScrollGestureRecognizer.IsScrollInertiaEnabledProperty,
|
||||
TabScrollViewer.IsScrollInertiaEnabledProperty);
|
||||
scrollViewContent.GestureRecognizers.Add(scrollGestureRecognizer);
|
||||
|
||||
return scrollViewContent;
|
||||
}
|
||||
|
||||
protected override void BuildStyles()
|
||||
{
|
||||
var topPlacementStyle = new Style(selector => selector.Nesting().PropertyEquals(TabScrollViewer.TabStripPlacementProperty, Dock.Top));
|
||||
{
|
||||
var contentPresenterStyle =
|
||||
new Style(selector => selector.Nesting().Template().OfType<TabStripScrollContentPresenter>());
|
||||
contentPresenterStyle.Add(DockPanel.DockProperty, Dock.Left);
|
||||
var menuIndicatorStyle =
|
||||
new Style(selector => selector.Nesting().Template().Name(ScrollMenuIndicatorPart));
|
||||
menuIndicatorStyle.Add(DockPanel.DockProperty, Dock.Right);
|
||||
menuIndicatorStyle.Add(IconButton.PaddingProperty, TabControlResourceKey.MenuIndicatorPaddingHorizontal);
|
||||
|
||||
var startEdgeIndicatorStyle = new Style(selector => selector.Nesting().Template().Name(ScrollStartEdgeIndicatorPart));
|
||||
startEdgeIndicatorStyle.Add(Control.WidthProperty, TabControlResourceKey.MenuEdgeThickness);
|
||||
topPlacementStyle.Add(startEdgeIndicatorStyle);
|
||||
|
||||
var endEdgeIndicatorStyle = new Style(selector => selector.Nesting().Template().Name(ScrollEndEdgeIndicatorPart));
|
||||
endEdgeIndicatorStyle.Add(Control.WidthProperty, TabControlResourceKey.MenuEdgeThickness);
|
||||
topPlacementStyle.Add(endEdgeIndicatorStyle);
|
||||
|
||||
topPlacementStyle.Add(menuIndicatorStyle);
|
||||
topPlacementStyle.Add(contentPresenterStyle);
|
||||
}
|
||||
|
||||
Add(topPlacementStyle);
|
||||
|
||||
var rightPlacementStyle = new Style(selector => selector.Nesting().PropertyEquals(TabScrollViewer.TabStripPlacementProperty, Dock.Right));
|
||||
|
||||
{
|
||||
var contentPresenterStyle =
|
||||
new Style(selector => selector.Nesting().Template().OfType<TabStripScrollContentPresenter>());
|
||||
contentPresenterStyle.Add(DockPanel.DockProperty, Dock.Top);
|
||||
var menuIndicatorStyle =
|
||||
new Style(selector => selector.Nesting().Template().Name(ScrollMenuIndicatorPart));
|
||||
menuIndicatorStyle.Add(DockPanel.DockProperty, Dock.Bottom);
|
||||
menuIndicatorStyle.Add(IconButton.PaddingProperty, TabControlResourceKey.MenuIndicatorPaddingVertical);
|
||||
|
||||
var startEdgeIndicatorStyle = new Style(selector => selector.Nesting().Template().Name(ScrollStartEdgeIndicatorPart));
|
||||
startEdgeIndicatorStyle.Add(Control.HeightProperty, TabControlResourceKey.MenuEdgeThickness);
|
||||
topPlacementStyle.Add(startEdgeIndicatorStyle);
|
||||
|
||||
var endEdgeIndicatorStyle = new Style(selector => selector.Nesting().Template().Name(ScrollStartEdgeIndicatorPart));
|
||||
endEdgeIndicatorStyle.Add(Control.HeightProperty, TabControlResourceKey.MenuEdgeThickness);
|
||||
topPlacementStyle.Add(endEdgeIndicatorStyle);
|
||||
|
||||
rightPlacementStyle.Add(menuIndicatorStyle);
|
||||
rightPlacementStyle.Add(contentPresenterStyle);
|
||||
}
|
||||
|
||||
Add(rightPlacementStyle);
|
||||
|
||||
var bottomPlacementStyle = new Style(selector => selector.Nesting().PropertyEquals(TabScrollViewer.TabStripPlacementProperty, Dock.Bottom));
|
||||
|
||||
{
|
||||
var contentPresenterStyle =
|
||||
new Style(selector => selector.Nesting().Template().OfType<TabStripScrollContentPresenter>());
|
||||
contentPresenterStyle.Add(DockPanel.DockProperty, Dock.Left);
|
||||
var menuIndicatorStyle =
|
||||
new Style(selector => selector.Nesting().Template().Name(ScrollMenuIndicatorPart));
|
||||
menuIndicatorStyle.Add(DockPanel.DockProperty, Dock.Right);
|
||||
menuIndicatorStyle.Add(IconButton.PaddingProperty, TabControlResourceKey.MenuIndicatorPaddingHorizontal);
|
||||
|
||||
var startEdgeIndicatorStyle = new Style(selector => selector.Nesting().Template().Name(ScrollStartEdgeIndicatorPart));
|
||||
startEdgeIndicatorStyle.Add(Control.WidthProperty, TabControlResourceKey.MenuEdgeThickness);
|
||||
topPlacementStyle.Add(startEdgeIndicatorStyle);
|
||||
|
||||
var endEdgeIndicatorStyle = new Style(selector => selector.Nesting().Template().Name(ScrollStartEdgeIndicatorPart));
|
||||
endEdgeIndicatorStyle.Add(Control.WidthProperty, TabControlResourceKey.MenuEdgeThickness);
|
||||
topPlacementStyle.Add(endEdgeIndicatorStyle);
|
||||
|
||||
bottomPlacementStyle.Add(menuIndicatorStyle);
|
||||
bottomPlacementStyle.Add(contentPresenterStyle);
|
||||
}
|
||||
|
||||
Add(bottomPlacementStyle);
|
||||
|
||||
var leftPlacementStyle = new Style(selector => selector.Nesting().PropertyEquals(TabScrollViewer.TabStripPlacementProperty, Dock.Left));
|
||||
|
||||
{
|
||||
var contentPresenterStyle =
|
||||
new Style(selector => selector.Nesting().Template().OfType<TabStripScrollContentPresenter>());
|
||||
contentPresenterStyle.Add(DockPanel.DockProperty, Dock.Top);
|
||||
var menuIndicatorStyle =
|
||||
new Style(selector => selector.Nesting().Template().Name(ScrollMenuIndicatorPart));
|
||||
menuIndicatorStyle.Add(DockPanel.DockProperty, Dock.Bottom);
|
||||
menuIndicatorStyle.Add(IconButton.PaddingProperty, TabControlResourceKey.MenuIndicatorPaddingVertical);
|
||||
|
||||
var startEdgeIndicatorStyle = new Style(selector => selector.Nesting().Template().Name(ScrollStartEdgeIndicatorPart));
|
||||
startEdgeIndicatorStyle.Add(Control.HeightProperty, TabControlResourceKey.MenuEdgeThickness);
|
||||
topPlacementStyle.Add(startEdgeIndicatorStyle);
|
||||
|
||||
var endEdgeIndicatorStyle = new Style(selector => selector.Nesting().Template().Name(ScrollStartEdgeIndicatorPart));
|
||||
endEdgeIndicatorStyle.Add(Control.HeightProperty, TabControlResourceKey.MenuEdgeThickness);
|
||||
topPlacementStyle.Add(endEdgeIndicatorStyle);
|
||||
|
||||
leftPlacementStyle.Add(menuIndicatorStyle);
|
||||
leftPlacementStyle.Add(contentPresenterStyle);
|
||||
}
|
||||
|
||||
Add(leftPlacementStyle);
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
using System.Reflection;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Presenters;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Rendering;
|
||||
|
||||
namespace AtomUI.Controls;
|
||||
|
||||
internal class TabStripScrollContentPresenter : ScrollContentPresenter, ICustomHitTest
|
||||
{
|
||||
internal Dock TabStripPlacement { get; set; } = Dock.Top;
|
||||
|
||||
private static readonly MethodInfo SnapOffsetMethodInfo;
|
||||
|
||||
static TabStripScrollContentPresenter()
|
||||
{
|
||||
SnapOffsetMethodInfo =
|
||||
typeof(ScrollContentPresenter).GetMethod("SnapOffset", BindingFlags.Instance | BindingFlags.NonPublic)!;
|
||||
}
|
||||
|
||||
protected override void OnPointerWheelChanged(PointerWheelEventArgs e)
|
||||
{
|
||||
if (Extent.Height > Viewport.Height || Extent.Width > Viewport.Width) {
|
||||
var scrollable = Child as ILogicalScrollable;
|
||||
var isLogical = scrollable?.IsLogicalScrollEnabled == true;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (Extent.Height > Viewport.Height) {
|
||||
double height = isLogical ? scrollable!.ScrollSize.Height : 50;
|
||||
y += -delta.Y * height;
|
||||
y = Math.Max(y, 0);
|
||||
y = Math.Min(y, Extent.Height - Viewport.Height);
|
||||
}
|
||||
|
||||
if (Extent.Width > Viewport.Width) {
|
||||
double width = isLogical ? scrollable!.ScrollSize.Width : 50;
|
||||
x += -delta.X * width;
|
||||
x = Math.Max(x, 0);
|
||||
x = Math.Min(x, Extent.Width - Viewport.Width);
|
||||
}
|
||||
|
||||
Vector newOffset = (Vector)SnapOffsetMethodInfo.Invoke(this, new object?[] { new Vector(x, y), delta, true })!;
|
||||
|
||||
bool offsetChanged = newOffset != Offset;
|
||||
SetCurrentValue(OffsetProperty, newOffset);
|
||||
|
||||
e.Handled = !IsScrollChainingEnabled || offsetChanged;
|
||||
}
|
||||
}
|
||||
|
||||
public bool HitTest(Point point)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
@ -16,6 +16,15 @@ internal class TabStripTheme : BaseTabStripTheme
|
||||
public TabStripTheme() : base(typeof(TabStrip)) { }
|
||||
|
||||
protected override void NotifyBuildControlTemplate(BaseTabStrip baseTabStrip, INameScope scope, Border container)
|
||||
{
|
||||
var tabScrollViewer = new TabScrollViewer();
|
||||
CreateTemplateParentBinding(tabScrollViewer, TabScrollViewer.TabStripPlacementProperty, TabStrip.TabStripPlacementProperty);
|
||||
var contentPanel = CreateTabStripContentPanel(scope);
|
||||
tabScrollViewer.Content = contentPanel;
|
||||
container.Child = tabScrollViewer;
|
||||
}
|
||||
|
||||
private Panel CreateTabStripContentPanel(INameScope scope)
|
||||
{
|
||||
var layout = new Panel();
|
||||
var itemsPresenter = new ItemsPresenter
|
||||
@ -33,7 +42,7 @@ internal class TabStripTheme : BaseTabStripTheme
|
||||
layout.Children.Add(itemsPresenter);
|
||||
layout.Children.Add(border);
|
||||
CreateTemplateParentBinding(itemsPresenter, ItemsPresenter.ItemsPanelProperty, TabStrip.ItemsPanelProperty);
|
||||
container.Child = layout;
|
||||
return layout;
|
||||
}
|
||||
|
||||
protected override void BuildStyles()
|
||||
|
Loading…
Reference in New Issue
Block a user