feat: implementations.

This commit is contained in:
Dong Bin
2025-07-02 12:12:09 +08:00
parent 405d283c10
commit 881b9ca7d3
9 changed files with 261 additions and 6 deletions

View 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>

View File

@@ -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" />

View 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);
}
}
}

View 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);
}
}
}