92
src/Ursa.Themes.Semi/Controls/ScrollToButton.axaml
Normal file
92
src/Ursa.Themes.Semi/Controls/ScrollToButton.axaml
Normal file
@@ -0,0 +1,92 @@
|
||||
<ResourceDictionary
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:u="https://irihi.tech/ursa">
|
||||
<!-- Add Resources Here -->
|
||||
<ControlTheme x:Key="{x:Type u:ScrollToButton}" TargetType="u:ScrollToButton">
|
||||
<Setter Property="HorizontalAlignment" Value="Right" />
|
||||
<Setter Property="VerticalAlignment" Value="Bottom" />
|
||||
<Setter Property="Cursor" Value="Hand"></Setter>
|
||||
<Setter Property="Margin" Value="0, 0, 16, 16" />
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate TargetType="u:ScrollToButton">
|
||||
<Border
|
||||
Name="PART_Background"
|
||||
Background="{DynamicResource ButtonDefaultBackground}"
|
||||
CornerRadius="{DynamicResource ButtonCornerRadius}">
|
||||
<PathIcon
|
||||
Name="PART_Icon"
|
||||
Margin="8"
|
||||
Width="12"
|
||||
Height="12"
|
||||
Data="{DynamicResource ScrollToButtonIconGlyph}"
|
||||
Foreground="{DynamicResource ButtonDefaultPrimaryForeground}" />
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter>
|
||||
<Style Selector="^:pressed">
|
||||
<Setter Property="RenderTransform" Value="scale(0.98)" />
|
||||
</Style>
|
||||
<Style Selector="^:pointerover /template/ Border#PART_Background">
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource ButtonDefaultPointeroverBorderBrush}" />
|
||||
<Setter Property="Background" Value="{DynamicResource ButtonDefaultPointeroverBackground}" />
|
||||
</Style>
|
||||
<Style Selector="^:pressed /template/ Border#PART_Background">
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource ButtonDefaultPressedBorderBrush}" />
|
||||
<Setter Property="Background" Value="{DynamicResource ButtonDefaultPressedBackground}" />
|
||||
</Style>
|
||||
<Style Selector="^[Direction=Right] /template/ PathIcon#PART_Icon">
|
||||
<Setter Property="RenderTransform" Value="rotate(90deg)"></Setter>
|
||||
</Style>
|
||||
<Style Selector="^[Direction=Bottom] /template/ PathIcon#PART_Icon">
|
||||
<Setter Property="RenderTransform" Value="rotate(180deg)"></Setter>
|
||||
</Style>
|
||||
<Style Selector="^[Direction=Left] /template/ PathIcon#PART_Icon">
|
||||
<Setter Property="RenderTransform" Value="rotate(270deg)"></Setter>
|
||||
</Style>
|
||||
</ControlTheme>
|
||||
|
||||
|
||||
<ControlTheme x:Key="PrimaryScrollToButton" TargetType="u:ScrollToButton">
|
||||
<Setter Property="HorizontalAlignment" Value="Right" />
|
||||
<Setter Property="VerticalAlignment" Value="Bottom" />
|
||||
<Setter Property="Cursor" Value="Hand"></Setter>
|
||||
<Setter Property="Margin" Value="0, 0, 16, 16" />
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate TargetType="u:ScrollToButton">
|
||||
<Border
|
||||
Name="PART_Background"
|
||||
Background="{DynamicResource ButtonSolidPrimaryBackground}"
|
||||
CornerRadius="{DynamicResource ButtonCornerRadius}">
|
||||
<PathIcon
|
||||
Name="PART_Icon"
|
||||
Margin="8"
|
||||
Width="12"
|
||||
Height="12"
|
||||
Data="{DynamicResource ScrollToButtonIconGlyph}"
|
||||
Foreground="{DynamicResource ButtonSolidForeground}" />
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter>
|
||||
<Style Selector="^:pressed">
|
||||
<Setter Property="RenderTransform" Value="scale(0.98)" />
|
||||
</Style>
|
||||
<Style Selector="^:pointerover /template/ Border#PART_Background">
|
||||
<Setter Property="Background" Value="{DynamicResource ButtonSolidPrimaryPointeroverBackground}" />
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource ButtonSolidPrimaryPointeroverBorderBrush}" />
|
||||
</Style>
|
||||
<Style Selector="^:pressed /template/ Border#PART_Background">
|
||||
<Setter Property="Background" Value="{DynamicResource ButtonSolidPrimaryPressedBackground}" />
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource ButtonSolidPrimaryPressedBorderBrush}" />
|
||||
</Style>
|
||||
<Style Selector="^[Direction=Right] /template/ PathIcon#PART_Icon">
|
||||
<Setter Property="RenderTransform" Value="rotate(90deg)"></Setter>
|
||||
</Style>
|
||||
<Style Selector="^[Direction=Bottom] /template/ PathIcon#PART_Icon">
|
||||
<Setter Property="RenderTransform" Value="rotate(180deg)"></Setter>
|
||||
</Style>
|
||||
<Style Selector="^[Direction=Left] /template/ PathIcon#PART_Icon">
|
||||
<Setter Property="RenderTransform" Value="rotate(270deg)"></Setter>
|
||||
</Style>
|
||||
</ControlTheme>
|
||||
</ResourceDictionary>
|
||||
@@ -25,6 +25,7 @@
|
||||
<ResourceInclude Source="NumberDisplayer.axaml" />
|
||||
<ResourceInclude Source="Pagination.axaml" />
|
||||
<ResourceInclude Source="RangeSlider.axaml" />
|
||||
<ResourceInclude Source="ScrollToButton.axaml" />
|
||||
<ResourceInclude Source="SelectionList.axaml" />
|
||||
<ResourceInclude Source="TagInput.axaml" />
|
||||
<ResourceInclude Source="ThemeSelector.axaml" />
|
||||
|
||||
5
src/Ursa.Themes.Semi/Themes/Shared/ScrollToButton.axaml
Normal file
5
src/Ursa.Themes.Semi/Themes/Shared/ScrollToButton.axaml
Normal file
@@ -0,0 +1,5 @@
|
||||
<ResourceDictionary xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<!-- Add Resources Here -->
|
||||
<StreamGeometry x:Key="ScrollToButtonIconGlyph">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</StreamGeometry>
|
||||
</ResourceDictionary>
|
||||
@@ -13,6 +13,7 @@
|
||||
<MergeResourceInclude Source="MessageBox.axaml" />
|
||||
<MergeResourceInclude Source="NavigationMenu.axaml" />
|
||||
<MergeResourceInclude Source="Pagination.axaml" />
|
||||
<MergeResourceInclude Source="ScrollToButton.axaml" />
|
||||
<MergeResourceInclude Source="TagInput.axaml" />
|
||||
<MergeResourceInclude Source="Skeleton.axaml" />
|
||||
<MergeResourceInclude Source="ThemeSelector.axaml" />
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Media;
|
||||
|
||||
namespace Ursa.Controls.BackTop;
|
||||
|
||||
public class BackTop: Control
|
||||
{
|
||||
public static readonly AttachedProperty<bool> AttachProperty =
|
||||
AvaloniaProperty.RegisterAttached<BackTop, Control, bool>("Attach");
|
||||
|
||||
public static void SetAttach(Control obj, bool value) => obj.SetValue(AttachProperty, value);
|
||||
public static bool GetAttach(Control obj) => obj.GetValue(AttachProperty);
|
||||
}
|
||||
60
src/Ursa/Controls/ScrollTo/ScrollTo.cs
Normal file
60
src/Ursa/Controls/ScrollTo/ScrollTo.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Styling;
|
||||
using Ursa.Common;
|
||||
|
||||
namespace Ursa.Controls;
|
||||
|
||||
public class ScrollTo
|
||||
{
|
||||
public static readonly AttachedProperty<Position?> DirectionProperty =
|
||||
AvaloniaProperty.RegisterAttached<ScrollTo, Control, Position?>("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<ControlTheme?> ButtonThemeProperty =
|
||||
AvaloniaProperty.RegisterAttached<ScrollTo, Control, ControlTheme?>("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<Control, Position?>(OnDirectionChanged);
|
||||
ButtonThemeProperty.Changed.AddClassHandler<Control, ControlTheme?>(OnButtonThemeChanged);
|
||||
}
|
||||
|
||||
private static void OnButtonThemeChanged(Control arg1, AvaloniaPropertyChangedEventArgs<ControlTheme?> arg2)
|
||||
{
|
||||
var button = EnsureButtonInAdorner(arg1);
|
||||
if (button is null) return;
|
||||
button.SetCurrentValue(StyledElement.ThemeProperty, arg2.NewValue.Value);
|
||||
}
|
||||
|
||||
private static void OnDirectionChanged(Control control, AvaloniaPropertyChangedEventArgs<Position?> args)
|
||||
{
|
||||
if (args.NewValue.Value is null) return;
|
||||
var button = EnsureButtonInAdorner(control);
|
||||
if (button is null) return;
|
||||
button.SetCurrentValue(ScrollToButton.DirectionProperty, args.NewValue.Value);
|
||||
}
|
||||
|
||||
private static ScrollToButton? EnsureButtonInAdorner(Control control)
|
||||
{
|
||||
var adorner = AdornerLayer.GetAdorner(control);
|
||||
if (adorner is not ScrollToButton button)
|
||||
{
|
||||
button = new ScrollToButton();
|
||||
AdornerLayer.SetAdorner(control, button);
|
||||
}
|
||||
button.SetCurrentValue(ScrollToButton.TargetProperty, control);
|
||||
button.SetCurrentValue(ScrollToButton.DirectionProperty, GetDirection(control));
|
||||
if ( GetButtonTheme(control) is { } theme)
|
||||
{
|
||||
button.SetCurrentValue(StyledElement.ThemeProperty, theme);
|
||||
}
|
||||
return button;
|
||||
}
|
||||
}
|
||||
125
src/Ursa/Controls/ScrollTo/ScrollToButton.cs
Normal file
125
src/Ursa/Controls/ScrollTo/ScrollToButton.cs
Normal file
@@ -0,0 +1,125 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Animation;
|
||||
using Avalonia.Animation.Easings;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.LogicalTree;
|
||||
using Avalonia.Styling;
|
||||
using Avalonia.VisualTree;
|
||||
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<Control> TargetProperty = AvaloniaProperty.Register<ScrollToButton, Control>(
|
||||
nameof(Target));
|
||||
|
||||
public Control Target
|
||||
{
|
||||
get => GetValue(TargetProperty);
|
||||
set => SetValue(TargetProperty, value);
|
||||
}
|
||||
|
||||
public static readonly StyledProperty<Position> DirectionProperty = AvaloniaProperty.Register<ScrollToButton, Position>(
|
||||
nameof(Direction));
|
||||
|
||||
public Position Direction
|
||||
{
|
||||
get => GetValue(DirectionProperty);
|
||||
set => SetValue(DirectionProperty, value);
|
||||
}
|
||||
|
||||
static ScrollToButton()
|
||||
{
|
||||
TargetProperty.Changed.AddClassHandler<ScrollToButton, Control>((o,e)=>o.OnTargetChanged(e));
|
||||
DirectionProperty.Changed.AddClassHandler<ScrollToButton, Position>((o,e)=>o.OnDirectionChanged(e));
|
||||
}
|
||||
|
||||
private void OnDirectionChanged(AvaloniaPropertyChangedEventArgs<Position> avaloniaPropertyChangedEventArgs)
|
||||
{
|
||||
if (_scroll is null) return;
|
||||
SetVisibility(avaloniaPropertyChangedEventArgs.NewValue.Value, _scroll.Offset);
|
||||
}
|
||||
|
||||
private void OnTargetChanged(AvaloniaPropertyChangedEventArgs<Control> arg2)
|
||||
{
|
||||
_disposable?.Dispose();
|
||||
if (arg2.NewValue.Value is { } newValue)
|
||||
{
|
||||
var scroll = newValue.GetSelfAndVisualDescendants().OfType<ScrollViewer>().FirstOrDefault();
|
||||
if (_scroll is not null)
|
||||
{
|
||||
_disposable?.Dispose();
|
||||
_scroll = null;
|
||||
}
|
||||
_scroll = scroll;
|
||||
|
||||
_disposable = ScrollViewer.OffsetProperty.Changed.AddClassHandler<ScrollViewer, Vector>(OnScrollChanged);
|
||||
SetVisibility(Direction, _scroll?.Offset);
|
||||
}
|
||||
}
|
||||
|
||||
protected override async void OnClick()
|
||||
{
|
||||
if (_scroll is null) return;
|
||||
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 OnLoaded(RoutedEventArgs e)
|
||||
{
|
||||
base.OnLoaded(e);
|
||||
var scroll = Target.GetSelfAndVisualDescendants().OfType<ScrollViewer>().FirstOrDefault();
|
||||
if (_scroll is not null)
|
||||
{
|
||||
_disposable?.Dispose();
|
||||
_scroll = null;
|
||||
}
|
||||
_scroll = scroll;
|
||||
_disposable = ScrollViewer.OffsetProperty.Changed.AddClassHandler<ScrollViewer, Vector>(OnScrollChanged);
|
||||
SetVisibility(Direction, _scroll?.Offset);
|
||||
}
|
||||
|
||||
private void OnScrollChanged(ScrollViewer arg1, AvaloniaPropertyChangedEventArgs<Vector> arg2)
|
||||
{
|
||||
if (arg1 != _scroll) return;
|
||||
SetVisibility(Direction, arg2.NewValue.Value);
|
||||
}
|
||||
|
||||
private void SetVisibility(Position direction, Vector? vector)
|
||||
{
|
||||
if (vector is null || _scroll is null) return;
|
||||
if (direction == Position.Bottom && vector.Value.Y < _scroll.Extent.Height - _scroll.Bounds.Height)
|
||||
{
|
||||
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 < _scroll.Extent.Width - _scroll.Bounds.Width)
|
||||
{
|
||||
IsVisible = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
IsVisible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user