feat: WIP.
This commit is contained in:
@@ -12,4 +12,16 @@
|
||||
</ControlTemplate>
|
||||
</Setter>
|
||||
</ControlTheme>
|
||||
|
||||
<ControlTheme x:Key="{x:Type u:AnchorItem}" TargetType="u:AnchorItem" >
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate TargetType="u:AnchorItem">
|
||||
<StackPanel>
|
||||
<ContentPresenter Content="{TemplateBinding Header}" />
|
||||
<ItemsPresenter Margin="8 0 0 0" ItemsPanel="{TemplateBinding ItemsPanel}" />
|
||||
</StackPanel>
|
||||
</ControlTemplate>
|
||||
</Setter>
|
||||
</ControlTheme>
|
||||
</ResourceDictionary>
|
||||
|
||||
@@ -46,12 +46,13 @@ public class Anchor: SelectingItemsControl
|
||||
if (target is null) return;
|
||||
ScrollToAnchor(target);
|
||||
}
|
||||
|
||||
private CancellationTokenSource _cts = new();
|
||||
|
||||
internal void ScrollToAnchor(Visual target)
|
||||
private void ScrollToAnchor(Visual target)
|
||||
{
|
||||
if (TargetContainer is null)
|
||||
return;
|
||||
TargetContainer.Loaded += OnTargetLoaded;
|
||||
var targetPosition = target.TranslatePoint(new Point(0, 0), TargetContainer);
|
||||
if (targetPosition.HasValue)
|
||||
{
|
||||
@@ -61,6 +62,7 @@ public class Anchor: SelectingItemsControl
|
||||
{
|
||||
to = TargetContainer.Extent.Height - TargetContainer.Bounds.Height;
|
||||
}
|
||||
if (from == to) return;
|
||||
Animation animation = new Animation()
|
||||
{
|
||||
Duration = TimeSpan.FromSeconds(0.3),
|
||||
@@ -85,29 +87,51 @@ public class Anchor: SelectingItemsControl
|
||||
|
||||
}
|
||||
};
|
||||
animation.RunAsync(TargetContainer);
|
||||
// TargetContainer.Offset = TargetContainer.Offset.WithY(TargetContainer.Offset.Y + targetPosition.Value.Y);
|
||||
_cts.Cancel();
|
||||
_cts = new CancellationTokenSource();
|
||||
animation.RunAsync(TargetContainer, _cts.Token);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTargetLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is ScrollViewer scrollViewer)
|
||||
{
|
||||
scrollViewer.Loaded -= OnTargetLoaded;
|
||||
if (scrollViewer.Content is Visual target)
|
||||
{
|
||||
var anchorId = GetAnchorId(target);
|
||||
if (!string.IsNullOrEmpty(anchorId))
|
||||
{
|
||||
ScrollToAnchor(anchorId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void InvalidatePositions()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected override void OnLoaded(RoutedEventArgs e)
|
||||
{
|
||||
base.OnLoaded(e);
|
||||
var items = this.GetVisualDescendants().OfType<AnchorItem>().ToList();
|
||||
var target = this.TargetContainer;
|
||||
if (target is null) return;
|
||||
var targetItems = target.GetVisualDescendants().Where(a => Anchor.GetAnchorId(a) is not null).ToList();
|
||||
var tops = targetItems.Select(a => (a.TransformToVisual(target)?.M32, GetAnchorId(a)));
|
||||
var isloaded = TargetContainer?.IsLoaded;
|
||||
TargetContainer?.AddHandler(ScrollViewer.ScrollChangedEvent, OnScrollChanged);
|
||||
}
|
||||
|
||||
private void OnScrollChanged(object? sender, ScrollChangedEventArgs e)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
internal Control CreateContainerForItemOverride_INTERNAL(object? item, int index, object? recycleKey)
|
||||
{
|
||||
return CreateContainerForItemOverride(item, index, recycleKey);
|
||||
}
|
||||
|
||||
internal bool NeedsContainerOverride_INTERNAL(object? item, int index, out object? recycleKey)
|
||||
{
|
||||
return NeedsContainerOverride(item, index, out recycleKey);
|
||||
}
|
||||
|
||||
internal void PrepareContainerForItemOverride_INTERNAL(Control container, object? item, int index)
|
||||
{
|
||||
PrepareContainerForItemOverride(container, item, index);
|
||||
}
|
||||
|
||||
internal void ContainerForItemPreparedOverride_INTERNAL(Control container, object? item, int index)
|
||||
{
|
||||
ContainerForItemPreparedOverride(container, item, index);
|
||||
}
|
||||
}
|
||||
@@ -1,51 +1,87 @@
|
||||
using System.Windows.Input;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Mixins;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Controls.Templates;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.VisualTree;
|
||||
using Avalonia.LogicalTree;
|
||||
|
||||
namespace Ursa.Controls;
|
||||
|
||||
public class AnchorItem: ContentControl
|
||||
public class AnchorItem : HeaderedItemsControl, ISelectable
|
||||
{
|
||||
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?> AnchorIdProperty = AvaloniaProperty.Register<AnchorItem, string?>(
|
||||
nameof(AnchorId));
|
||||
|
||||
public static readonly StyledProperty<bool> IsSelectedProperty =
|
||||
SelectingItemsControl.IsSelectedProperty.AddOwner<AnchorItem>();
|
||||
|
||||
private static readonly FuncTemplate<Panel?> DefaultPanel =
|
||||
new(() => new StackPanel());
|
||||
|
||||
private Anchor? _root;
|
||||
|
||||
static AnchorItem()
|
||||
{
|
||||
SelectableMixin.Attach<AnchorItem>(IsSelectedProperty);
|
||||
PressedMixin.Attach<AnchorItem>();
|
||||
ItemsPanelProperty.OverrideDefaultValue<TreeViewItem>(DefaultPanel);
|
||||
}
|
||||
|
||||
public string? AnchorId
|
||||
{
|
||||
get => GetValue(AnchorIdProperty);
|
||||
set => SetValue(AnchorIdProperty, value);
|
||||
}
|
||||
|
||||
private Anchor? _root;
|
||||
public bool IsSelected
|
||||
{
|
||||
get => GetValue(IsSelectedProperty);
|
||||
set => SetValue(IsSelectedProperty, value);
|
||||
}
|
||||
|
||||
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
base.OnAttachedToVisualTree(e);
|
||||
_root = this.FindAncestorOfType<Anchor>() ??
|
||||
throw new InvalidOperationException("AnchorItem must be inside an Anchor control.");
|
||||
_root = this.GetLogicalAncestors().OfType<Anchor>().FirstOrDefault();
|
||||
}
|
||||
|
||||
protected override void OnPointerPressed(PointerPressedEventArgs e)
|
||||
{
|
||||
var item = new TreeViewItem();
|
||||
base.OnPointerPressed(e);
|
||||
if (e.Handled) return;
|
||||
if (_root is null)
|
||||
return;
|
||||
if (Target is not null)
|
||||
{
|
||||
_root.ScrollToAnchor(Target);
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(AnchorId))
|
||||
if (!string.IsNullOrEmpty(AnchorId))
|
||||
{
|
||||
_root.ScrollToAnchor(AnchorId);
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected override Control CreateContainerForItemOverride(object? item, int index, object? recycleKey)
|
||||
{
|
||||
return EnsureRoot().CreateContainerForItemOverride_INTERNAL(item, index, recycleKey);
|
||||
}
|
||||
|
||||
protected override bool NeedsContainerOverride(object? item, int index, out object? recycleKey)
|
||||
{
|
||||
return EnsureRoot().NeedsContainerOverride_INTERNAL(item, index, out recycleKey);
|
||||
}
|
||||
|
||||
protected override void PrepareContainerForItemOverride(Control container, object? item, int index)
|
||||
{
|
||||
EnsureRoot().PrepareContainerForItemOverride_INTERNAL(container, item, index);
|
||||
}
|
||||
|
||||
protected override void ContainerForItemPreparedOverride(Control container, object? item, int index)
|
||||
{
|
||||
EnsureRoot().ContainerForItemPreparedOverride_INTERNAL(container, item, index);
|
||||
}
|
||||
|
||||
private Anchor EnsureRoot()
|
||||
{
|
||||
return _root ?? throw new InvalidOperationException("AnchorItem must be inside an Anchor control.");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user