feat: implementations.
This commit is contained in:
15
src/Ursa.Themes.Semi/Controls/Anchor.axaml
Normal file
15
src/Ursa.Themes.Semi/Controls/Anchor.axaml
Normal file
@@ -0,0 +1,15 @@
|
||||
<ResourceDictionary
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:u="https://irihi.tech/ursa">
|
||||
<ControlTheme x:Key="{x:Type u:Anchor}" TargetType="{x:Type u:Anchor}">
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate>
|
||||
<ItemsPresenter
|
||||
Name="PART_ItemsPresenter"
|
||||
Margin="{TemplateBinding Padding}"
|
||||
ItemsPanel="{TemplateBinding ItemsPanel}" />
|
||||
</ControlTemplate>
|
||||
</Setter>
|
||||
</ControlTheme>
|
||||
</ResourceDictionary>
|
||||
@@ -1,5 +1,6 @@
|
||||
<ResourceDictionary xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceInclude Source="Anchor.axaml" />
|
||||
<ResourceInclude Source="AutoCompleteBox.axaml" />
|
||||
<ResourceInclude Source="Avatar.axaml" />
|
||||
<ResourceInclude Source="Badge.axaml" />
|
||||
|
||||
99
src/Ursa/Controls/Anchor/Anchor.cs
Normal file
99
src/Ursa/Controls/Anchor/Anchor.cs
Normal file
@@ -0,0 +1,99 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Animation;
|
||||
using Avalonia.Animation.Easings;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Styling;
|
||||
|
||||
namespace Ursa.Controls;
|
||||
|
||||
public class Anchor: SelectingItemsControl
|
||||
{
|
||||
public static readonly StyledProperty<ScrollViewer?> TargetContainerProperty = AvaloniaProperty.Register<Anchor, ScrollViewer?>(
|
||||
nameof(TargetContainer));
|
||||
|
||||
public ScrollViewer? TargetContainer
|
||||
{
|
||||
get => GetValue(TargetContainerProperty);
|
||||
set => SetValue(TargetContainerProperty, value);
|
||||
}
|
||||
|
||||
public static readonly AttachedProperty<string> AnchorIdProperty =
|
||||
AvaloniaProperty.RegisterAttached<Anchor, Control, string>("AnchorId");
|
||||
|
||||
public static void SetAnchorId(Control obj, string value) => obj.SetValue(AnchorIdProperty, value);
|
||||
public static string GetAnchorId(Control obj) => obj.GetValue(AnchorIdProperty);
|
||||
|
||||
|
||||
protected override bool NeedsContainerOverride(object? item, int index, out object? recycleKey)
|
||||
{
|
||||
return NeedsContainer<AnchorItem>(item, out recycleKey);
|
||||
}
|
||||
|
||||
protected override Control CreateContainerForItemOverride(object? item, int index, object? recycleKey)
|
||||
{
|
||||
var i = new AnchorItem();
|
||||
return i;
|
||||
}
|
||||
|
||||
internal void ScrollToAnchor(string anchorId)
|
||||
{
|
||||
if (TargetContainer is null)
|
||||
return;
|
||||
|
||||
var target = TargetContainer.FindControl<Control>(anchorId);
|
||||
if (target is null)
|
||||
return;
|
||||
|
||||
var targetPosition = target.TranslatePoint(new Point(0, 0), TargetContainer);
|
||||
if (targetPosition.HasValue)
|
||||
{
|
||||
TargetContainer.Offset = new Vector(0, targetPosition.Value.Y);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal void ScrollToAnchor(Control target)
|
||||
{
|
||||
if (TargetContainer is null)
|
||||
return;
|
||||
|
||||
var targetPosition = target.TranslatePoint(new Point(0, 0), TargetContainer);
|
||||
if (targetPosition.HasValue)
|
||||
{
|
||||
var from = TargetContainer.Offset.Y;
|
||||
var to = TargetContainer.Offset.Y + targetPosition.Value.Y;
|
||||
if(to > TargetContainer.Extent.Height - TargetContainer.Bounds.Height)
|
||||
{
|
||||
to = TargetContainer.Extent.Height - TargetContainer.Bounds.Height;
|
||||
}
|
||||
Animation animation = new Animation()
|
||||
{
|
||||
Duration = TimeSpan.FromSeconds(0.3),
|
||||
Easing = new QuadraticEaseOut(),
|
||||
Children =
|
||||
{
|
||||
new KeyFrame(){
|
||||
Setters =
|
||||
{
|
||||
new Setter(ScrollViewer.OffsetProperty, new Vector(0, from)),
|
||||
},
|
||||
Cue = new Cue(0.0)
|
||||
},
|
||||
new KeyFrame()
|
||||
{
|
||||
Setters =
|
||||
{
|
||||
new Setter(ScrollViewer.OffsetProperty, new Vector(0, to))
|
||||
},
|
||||
Cue = new Cue(1.0)
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
animation.RunAsync(TargetContainer);
|
||||
// TargetContainer.Offset = TargetContainer.Offset.WithY(TargetContainer.Offset.Y + targetPosition.Value.Y);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
47
src/Ursa/Controls/Anchor/AnchorItem.cs
Normal file
47
src/Ursa/Controls/Anchor/AnchorItem.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using System.Windows.Input;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.VisualTree;
|
||||
|
||||
namespace Ursa.Controls;
|
||||
|
||||
public class AnchorItem: ContentControl
|
||||
{
|
||||
public static readonly StyledProperty<Control?> TargetProperty = AvaloniaProperty.Register<AnchorItem, Control?>(
|
||||
nameof(Target));
|
||||
|
||||
public Control? Target
|
||||
{
|
||||
get => GetValue(TargetProperty);
|
||||
set => SetValue(TargetProperty, value);
|
||||
}
|
||||
|
||||
public static readonly StyledProperty<string?> AnchorNameProperty = AvaloniaProperty.Register<AnchorItem, string?>(
|
||||
nameof(AnchorName));
|
||||
public string? AnchorName
|
||||
{
|
||||
get => GetValue(AnchorNameProperty);
|
||||
set => SetValue(AnchorNameProperty, value);
|
||||
}
|
||||
|
||||
private Anchor? _root;
|
||||
|
||||
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
base.OnAttachedToVisualTree(e);
|
||||
_root = this.FindAncestorOfType<Anchor>() ??
|
||||
throw new InvalidOperationException("AnchorItem must be inside an Anchor control.");
|
||||
}
|
||||
|
||||
protected override void OnPointerPressed(PointerPressedEventArgs e)
|
||||
{
|
||||
base.OnPointerPressed(e);
|
||||
if (_root is null)
|
||||
return;
|
||||
if (Target is not null)
|
||||
{
|
||||
_root.ScrollToAnchor(Target);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user