diff --git a/demo/Ursa.Demo/Pages/BadgeDemo.axaml b/demo/Ursa.Demo/Pages/BadgeDemo.axaml index 012ea37..fda3614 100644 --- a/demo/Ursa.Demo/Pages/BadgeDemo.axaml +++ b/demo/Ursa.Demo/Pages/BadgeDemo.axaml @@ -8,7 +8,7 @@ d:DesignHeight="850" d:DesignWidth="850" mc:Ignorable="d"> - + + + + + + + + + + + + + + + + + + + diff --git a/src/Ursa.Themes.Semi/Controls/_index.axaml b/src/Ursa.Themes.Semi/Controls/_index.axaml index 6be2414..ddb53fd 100644 --- a/src/Ursa.Themes.Semi/Controls/_index.axaml +++ b/src/Ursa.Themes.Semi/Controls/_index.axaml @@ -25,6 +25,7 @@ + diff --git a/src/Ursa.Themes.Semi/Themes/Shared/ScrollToButton.axaml b/src/Ursa.Themes.Semi/Themes/Shared/ScrollToButton.axaml new file mode 100644 index 0000000..4e45321 --- /dev/null +++ b/src/Ursa.Themes.Semi/Themes/Shared/ScrollToButton.axaml @@ -0,0 +1,5 @@ + + + M19.637 16.4369C19.0513 17.0227 18.1015 17.0227 17.5157 16.4369L11.8589 10.7801L6.20202 16.4369C5.61623 17.0227 4.66648 17.0227 4.0807 16.4369C3.49491 15.8511 3.49491 14.9014 4.0807 14.3156L10.7982 7.59809C11.384 7.01231 12.3337 7.01231 12.9195 7.59809L19.637 14.3156C20.2228 14.9014 20.2228 15.8511 19.637 16.4369Z + diff --git a/src/Ursa.Themes.Semi/Themes/Shared/_index.axaml b/src/Ursa.Themes.Semi/Themes/Shared/_index.axaml index 5e43477..b11d979 100644 --- a/src/Ursa.Themes.Semi/Themes/Shared/_index.axaml +++ b/src/Ursa.Themes.Semi/Themes/Shared/_index.axaml @@ -13,6 +13,7 @@ + diff --git a/src/Ursa/Controls/BackTop/BackTopButton.cs b/src/Ursa/Controls/BackTop/BackTopButton.cs deleted file mode 100644 index 3e035d0..0000000 --- a/src/Ursa/Controls/BackTop/BackTopButton.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Avalonia; -using Avalonia.Controls; - -namespace Ursa.Controls; - -public class BackTopButton: Button -{ - public static readonly StyledProperty TargetProperty = AvaloniaProperty.Register( - nameof(Target)); - - public Control Target - { - get => GetValue(TargetProperty); - set => SetValue(TargetProperty, value); - } - - protected override void OnClick() - { - base.OnClick(); - - } -} \ No newline at end of file diff --git a/src/Ursa/Controls/BackTop/ScrollTo.cs b/src/Ursa/Controls/BackTop/ScrollTo.cs new file mode 100644 index 0000000..09d076c --- /dev/null +++ b/src/Ursa/Controls/BackTop/ScrollTo.cs @@ -0,0 +1,64 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.Notifications; +using Avalonia.Controls.Primitives; +using Avalonia.Layout; +using Avalonia.LogicalTree; +using Avalonia.Styling; +using Ursa.Common; + +namespace Ursa.Controls; + +public class ScrollTo +{ + public static readonly AttachedProperty DirectionProperty = + AvaloniaProperty.RegisterAttached("Direction"); + + public static void SetDirection(Control obj, Position value) => obj.SetValue(DirectionProperty, value); + public static Position GetDirection(Control obj) => obj.GetValue(DirectionProperty); + + public static readonly AttachedProperty ButtonThemeProperty = + AvaloniaProperty.RegisterAttached("ButtonTheme"); + + public static void SetButtonTheme(Control obj, ControlTheme? value) => obj.SetValue(ButtonThemeProperty, value); + public static ControlTheme? GetButtonTheme(Control obj) => obj.GetValue(ButtonThemeProperty); + + static ScrollTo() + { + DirectionProperty.Changed.AddClassHandler(OnDirectionChanged); + ButtonThemeProperty.Changed.AddClassHandler(OnButtonThemeChanged); + } + + private static void OnButtonThemeChanged(Control arg1, AvaloniaPropertyChangedEventArgs arg2) + { + var button = EnsureButtonInAdorner(arg1); + if (button is null) return; + button.SetCurrentValue(StyledElement.ThemeProperty, arg2.NewValue.Value); + } + + private static void OnDirectionChanged(Control control, AvaloniaPropertyChangedEventArgs args) + { + var button = EnsureButtonInAdorner(control); + if (button is null) return; + button.SetCurrentValue(ScrollToButton.DirectionProperty, args.NewValue.Value); + } + + private static ScrollToButton? EnsureButtonInAdorner(Control control) + { + var scroll = control.GetSelfAndLogicalDescendants().OfType().FirstOrDefault(); + if (scroll is null) return null; + var adorner = AdornerLayer.GetAdorner(scroll); + if (adorner is not ScrollToButton button) + { + button = new ScrollToButton(); + AdornerLayer.SetAdorner(control, button); + } + button.SetCurrentValue(ScrollToButton.TargetProperty, scroll); + button.SetCurrentValue(ScrollToButton.DirectionProperty, GetDirection(control)); + if ( GetButtonTheme(control) is { } theme) + { + button.SetCurrentValue(StyledElement.ThemeProperty, theme); + } + return button; + } +} \ No newline at end of file diff --git a/src/Ursa/Controls/BackTop/ScrollToButton.cs b/src/Ursa/Controls/BackTop/ScrollToButton.cs new file mode 100644 index 0000000..18bb28f --- /dev/null +++ b/src/Ursa/Controls/BackTop/ScrollToButton.cs @@ -0,0 +1,116 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.LogicalTree; +using Irihi.Avalonia.Shared.Helpers; +using Ursa.Common; + +namespace Ursa.Controls; + +public class ScrollToButton: Button +{ + private ScrollViewer? _scroll; + private IDisposable? _disposable; + + public static readonly StyledProperty TargetProperty = AvaloniaProperty.Register( + nameof(Target)); + + public Control Target + { + get => GetValue(TargetProperty); + set => SetValue(TargetProperty, value); + } + + public static readonly StyledProperty DirectionProperty = AvaloniaProperty.Register( + nameof(Direction)); + + public Position Direction + { + get => GetValue(DirectionProperty); + set => SetValue(DirectionProperty, value); + } + + static ScrollToButton() + { + TargetProperty.Changed.AddClassHandler((o,e)=>o.OnTargetChanged(e)); + DirectionProperty.Changed.AddClassHandler((o,e)=>o.OnDirectionChanged(e)); + } + + private void OnDirectionChanged(AvaloniaPropertyChangedEventArgs avaloniaPropertyChangedEventArgs) + { + if (_scroll is null) return; + SetVisibility(avaloniaPropertyChangedEventArgs.NewValue.Value, _scroll.Offset); + } + + private void OnTargetChanged(AvaloniaPropertyChangedEventArgs arg2) + { + _disposable?.Dispose(); + if (arg2.NewValue.Value is { } newValue) + { + var scroll = newValue.GetSelfAndLogicalDescendants().OfType().FirstOrDefault(); + if (_scroll is not null) + { + _disposable?.Dispose(); + } + _scroll = scroll; + _disposable = ScrollViewer.OffsetProperty.Changed.AddClassHandler(OnScrollChanged); + SetVisibility(Direction, _scroll?.Offset); + } + } + + protected override void OnClick() + { + var vector = Direction switch + { + Position.Top => new Vector(0, double.NegativeInfinity), + Position.Bottom => new Vector(0, double.PositiveInfinity), + Position.Left => new Vector(double.NegativeInfinity, 0), + Position.Right => new Vector(double.PositiveInfinity, 0), + _ => new Vector(0, 0) + }; + _scroll?.SetCurrentValue(ScrollViewer.OffsetProperty, vector); + } + + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnAttachedToVisualTree(e); + var scroll = Target.GetSelfAndLogicalDescendants().OfType().FirstOrDefault(); + if (_scroll is not null) + { + _disposable?.Dispose(); + } + _scroll = scroll; + _disposable = ScrollViewer.OffsetProperty.Changed.AddClassHandler(OnScrollChanged); + SetVisibility(Direction, _scroll?.Offset); + } + + private void OnScrollChanged(ScrollViewer arg1, AvaloniaPropertyChangedEventArgs arg2) + { + if (arg1 != _scroll) return; + SetVisibility(Direction, arg2.NewValue.Value); + } + + private void SetVisibility(Position direction, Vector? vector) + { + if (vector is null) return; + if (direction == Position.Bottom && vector.Value.Y < 0) + { + IsVisible = true; + } + else if (direction == Position.Top && vector.Value.Y > 0) + { + IsVisible = true; + } + else if (direction == Position.Left && vector.Value.X < 0) + { + IsVisible = true; + } + else if (direction == Position.Right && vector.Value.X > 0) + { + IsVisible = true; + } + else + { + IsVisible = false; + } + } +} \ No newline at end of file