From 3e2869865d37f936b34d5639524fbc65482b43e0 Mon Sep 17 00:00:00 2001 From: NaBian <836904362@qq.com> Date: Tue, 11 Feb 2025 21:34:21 +0800 Subject: [PATCH] chore: add avalonia TabControlSliding demo. --- .../Styles/NativeTabControlDemo.axaml | 87 ++++++++ .../TabControl/SlidingTabContainer.cs | 113 +++++++++++ .../Themes/Styles/TabControl.axaml | 192 ++++++++++++++++++ .../Tools/Helper/VisualHelper.cs | 17 ++ .../TabItemCapsuleStyleSelector.cs | 2 +- .../TabControl/SlidingTabContainer.cs | 24 ++- 6 files changed, 423 insertions(+), 12 deletions(-) create mode 100644 src/Avalonia/HandyControl_Avalonia/Controls/TabControl/SlidingTabContainer.cs create mode 100644 src/Avalonia/HandyControl_Avalonia/Tools/Helper/VisualHelper.cs diff --git a/src/Avalonia/HandyControlDemo_Avalonia/UserControl/Styles/NativeTabControlDemo.axaml b/src/Avalonia/HandyControlDemo_Avalonia/UserControl/Styles/NativeTabControlDemo.axaml index 13e7a7b6..3cd28289 100644 --- a/src/Avalonia/HandyControlDemo_Avalonia/UserControl/Styles/NativeTabControlDemo.axaml +++ b/src/Avalonia/HandyControlDemo_Avalonia/UserControl/Styles/NativeTabControlDemo.axaml @@ -9,6 +9,93 @@ ItemHeight="200" MaxWidth="832" ChildWrapping="Wrap"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia/HandyControl_Avalonia/Controls/TabControl/SlidingTabContainer.cs b/src/Avalonia/HandyControl_Avalonia/Controls/TabControl/SlidingTabContainer.cs new file mode 100644 index 00000000..8c9174de --- /dev/null +++ b/src/Avalonia/HandyControl_Avalonia/Controls/TabControl/SlidingTabContainer.cs @@ -0,0 +1,113 @@ +using System; +using Avalonia; +using Avalonia.Animation; +using Avalonia.Animation.Easings; +using Avalonia.Controls; +using Avalonia.Controls.Metadata; +using Avalonia.Controls.Primitives; +using Avalonia.Interactivity; +using Avalonia.Layout; +using Avalonia.Media; +using Avalonia.Styling; +using HandyControl.Tools; + +namespace HandyControl.Controls; + +[TemplatePart(Name = ElementSliding, Type = typeof(Layoutable))] +public class SlidingTabContainer : ContentControl +{ + private const string ElementSliding = "PART_Sliding"; + + private Control? _sliding; + private TabControl? _tabControl; + + protected override void OnLoaded(RoutedEventArgs e) + { + base.OnLoaded(e); + OnSelectionChanged(null, null!); + } + + protected override void OnApplyTemplate(TemplateAppliedEventArgs e) + { + if (_tabControl is not null) + { + _tabControl.SelectionChanged -= OnSelectionChanged; + } + + base.OnApplyTemplate(e); + + _tabControl = VisualHelper.GetParent(this); + if (_tabControl is null) + { + return; + } + + _tabControl.SelectionChanged += OnSelectionChanged; + + _sliding = e.NameScope.Find(ElementSliding); + if (_sliding is null) + { + return; + } + + if (IsLoaded) + { + OnSelectionChanged(null, null!); + } + } + + protected override void OnSizeChanged(SizeChangedEventArgs e) + { + base.OnSizeChanged(e); + + if (_tabControl is null) + { + return; + } + + OnSelectionChanged(null, null!); + } + + private void OnSelectionChanged(object? sender, SelectionChangedEventArgs e) + { + const int animationSpeed = 200; + + if (_sliding is null || _tabControl?.SelectedItem is null || !IsLoaded) + { + return; + } + + if (_tabControl.ContainerFromItem(_tabControl.SelectedItem) is not TabItem tabItem) + { + return; + } + + var padding = Padding; + Point? offset = tabItem.TranslatePoint(new Point(-padding.Left, -padding.Top), this); + if (offset is null) + { + return; + } + + new Animation + { + FillMode = FillMode.Forward, + Duration = TimeSpan.FromMilliseconds(animationSpeed), + Easing = new QuadraticEaseOut(), + Children = + { + new KeyFrame + { + Setters = + { + new Setter(WidthProperty, tabItem.Bounds.Width), + new Setter(HeightProperty, tabItem.Bounds.Height), + new Setter(TranslateTransform.XProperty, offset.Value.X), + new Setter(TranslateTransform.YProperty, offset.Value.Y), + }, + Cue = new Cue(1), + } + } + }.RunAsync(_sliding); + } +} diff --git a/src/Avalonia/HandyControl_Avalonia/Themes/Styles/TabControl.axaml b/src/Avalonia/HandyControl_Avalonia/Themes/Styles/TabControl.axaml index cf180748..57cf7619 100644 --- a/src/Avalonia/HandyControl_Avalonia/Themes/Styles/TabControl.axaml +++ b/src/Avalonia/HandyControl_Avalonia/Themes/Styles/TabControl.axaml @@ -402,4 +402,196 @@ Value="{DynamicResource TextIconBrush}" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia/HandyControl_Avalonia/Tools/Helper/VisualHelper.cs b/src/Avalonia/HandyControl_Avalonia/Tools/Helper/VisualHelper.cs new file mode 100644 index 00000000..9e3aab41 --- /dev/null +++ b/src/Avalonia/HandyControl_Avalonia/Tools/Helper/VisualHelper.cs @@ -0,0 +1,17 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.VisualTree; + +namespace HandyControl.Tools; + +public static class VisualHelper +{ + public static T? GetParent(Visual? visual) where T : Visual => + visual switch + { + null => default, + T t => t, + Window _ => null, + _ => GetParent(visual.GetVisualParent()) + }; +} diff --git a/src/Avalonia/HandyControl_Avalonia/Tools/StyleSelector/TabItemCapsuleStyleSelector.cs b/src/Avalonia/HandyControl_Avalonia/Tools/StyleSelector/TabItemCapsuleStyleSelector.cs index 7faa5930..40c9e78b 100644 --- a/src/Avalonia/HandyControl_Avalonia/Tools/StyleSelector/TabItemCapsuleStyleSelector.cs +++ b/src/Avalonia/HandyControl_Avalonia/Tools/StyleSelector/TabItemCapsuleStyleSelector.cs @@ -9,7 +9,7 @@ public class TabItemCapsuleThemeSelector : ThemeSelector { public override ControlTheme? SelectTheme(object? item, AvaloniaObject? container) { - if (container is not TabItem { Parent: TabControl tabControl } tabItem) + if (container is not TabItem tabItem || VisualHelper.GetParent(tabItem) is not { } tabControl) { return null; } diff --git a/src/Shared/HandyControl_Shared/Controls/TabControl/SlidingTabContainer.cs b/src/Shared/HandyControl_Shared/Controls/TabControl/SlidingTabContainer.cs index edb75ebd..afb1526e 100644 --- a/src/Shared/HandyControl_Shared/Controls/TabControl/SlidingTabContainer.cs +++ b/src/Shared/HandyControl_Shared/Controls/TabControl/SlidingTabContainer.cs @@ -37,14 +37,16 @@ public class SlidingTabContainer : ContentControl _tabControl = VisualHelper.GetParent(this); _sliding = GetTemplateChild(ElementSliding) as FrameworkElement; - if (_tabControl is not null) + if (_tabControl is null) { - _tabControl.SelectionChanged += OnSelectionChanged; + return; + } - if (IsLoaded) - { - OnSelectionChanged(null, null); - } + _tabControl.SelectionChanged += OnSelectionChanged; + + if (IsLoaded) + { + OnSelectionChanged(null, null); } } @@ -80,31 +82,31 @@ public class SlidingTabContainer : ContentControl if (useAnimation) { var storyboard = new Storyboard(); - var easingFuction = new PowerEase + var easingFunction = new PowerEase { EasingMode = EasingMode.EaseOut, }; var widthAnimation = AnimationHelper.CreateAnimation(tabWidth); - widthAnimation.EasingFunction = easingFuction; + widthAnimation.EasingFunction = easingFunction; Storyboard.SetTarget(widthAnimation, _sliding); Storyboard.SetTargetProperty(widthAnimation, new PropertyPath(WidthProperty.Name)); storyboard.Children.Add(widthAnimation); var heightAnimation = AnimationHelper.CreateAnimation(tabHeight); - heightAnimation.EasingFunction = easingFuction; + heightAnimation.EasingFunction = easingFunction; Storyboard.SetTarget(heightAnimation, _sliding); Storyboard.SetTargetProperty(heightAnimation, new PropertyPath(HeightProperty.Name)); storyboard.Children.Add(heightAnimation); var xAnimation = AnimationHelper.CreateAnimation(offset.X); - xAnimation.EasingFunction = easingFuction; + xAnimation.EasingFunction = easingFunction; Storyboard.SetTarget(xAnimation, _sliding); Storyboard.SetTargetProperty(xAnimation, new PropertyPath("(UIElement.RenderTransform).(TranslateTransform.X)")); storyboard.Children.Add(xAnimation); var yAnimation = AnimationHelper.CreateAnimation(offset.Y); - yAnimation.EasingFunction = easingFuction; + yAnimation.EasingFunction = easingFunction; Storyboard.SetTarget(yAnimation, _sliding); Storyboard.SetTargetProperty(yAnimation, new PropertyPath("(UIElement.RenderTransform).(TranslateTransform.Y)")); storyboard.Children.Add(yAnimation);