mirror of
https://gitee.com/chinware/atomui.git
synced 2024-12-02 03:47:52 +08:00
完成树菜单的拖动
This commit is contained in:
parent
583c4aba59
commit
e1b1d8e71d
@ -1,9 +1,14 @@
|
||||
using AtomUI.Controls.Utils;
|
||||
using AtomUI.Data;
|
||||
using AtomUI.Theme.Styling;
|
||||
using AtomUI.Utils;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Metadata;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Layout;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Threading;
|
||||
using Avalonia.VisualTree;
|
||||
|
||||
@ -83,7 +88,74 @@ public class TreeView : AvaloniaTreeView
|
||||
|
||||
#endregion
|
||||
|
||||
#region 内部属性定义
|
||||
|
||||
internal static readonly DirectProperty<TreeView, bool> IsDraggingProperty =
|
||||
AvaloniaProperty.RegisterDirect<TreeView, bool>(nameof(IsDragging),
|
||||
o => o.IsDragging,
|
||||
(o, v) => o.IsDragging = v);
|
||||
|
||||
internal static readonly DirectProperty<TreeView, DragIndicatorRenderInfo?> DragIndicatorRenderInfoProperty =
|
||||
AvaloniaProperty.RegisterDirect<TreeView, DragIndicatorRenderInfo?>(nameof(DragIndicatorRenderInfo),
|
||||
o => o.DragIndicatorRenderInfo,
|
||||
(o, v) => o.DragIndicatorRenderInfo = v);
|
||||
|
||||
internal static readonly DirectProperty<TreeView, double> DragIndicatorLineWidthProperty =
|
||||
AvaloniaProperty.RegisterDirect<TreeView, double>(nameof(DragIndicatorLineWidth),
|
||||
o => o.DragIndicatorLineWidth,
|
||||
(o, v) => o.DragIndicatorLineWidth = v);
|
||||
|
||||
internal static readonly DirectProperty<TreeView, IBrush?> DragIndicatorBrushProperty =
|
||||
AvaloniaProperty.RegisterDirect<TreeView, IBrush?>(nameof(DragIndicatorBrush),
|
||||
o => o.DragIndicatorBrush,
|
||||
(o, v) => o.DragIndicatorBrush = v);
|
||||
|
||||
private DragIndicatorRenderInfo? _dragIndicatorRenderInfo;
|
||||
|
||||
internal DragIndicatorRenderInfo? DragIndicatorRenderInfo
|
||||
{
|
||||
get => _dragIndicatorRenderInfo;
|
||||
set => SetAndRaise(DragIndicatorRenderInfoProperty, ref _dragIndicatorRenderInfo, value);
|
||||
}
|
||||
|
||||
private bool _isDragging;
|
||||
|
||||
internal bool IsDragging
|
||||
{
|
||||
get => _isDragging;
|
||||
set => SetAndRaise(IsDraggingProperty, ref _isDragging, value);
|
||||
}
|
||||
|
||||
private double _dragIndicatorLineWidth;
|
||||
|
||||
internal double DragIndicatorLineWidth
|
||||
{
|
||||
get => _dragIndicatorLineWidth;
|
||||
set => SetAndRaise(DragIndicatorLineWidthProperty, ref _dragIndicatorLineWidth, value);
|
||||
}
|
||||
|
||||
private IBrush? _dragIndicatorBrush;
|
||||
|
||||
internal IBrush? DragIndicatorBrush
|
||||
{
|
||||
get => _dragIndicatorBrush;
|
||||
set => SetAndRaise(DragIndicatorBrushProperty, ref _dragIndicatorBrush, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
internal List<TreeViewItem> DefaultCheckedItems { get; set; }
|
||||
private Point? _lastPoint;
|
||||
private TreeViewItem? _beingDraggedTreeItem;
|
||||
private DragPreviewAdorner? _dragPreview;
|
||||
private TreeViewItem? _currentDragOver; // 这个不是目标节点,有可能是在父节点上拖动
|
||||
private TreeViewItem? _dropTargetNode; // 目标释放节点
|
||||
private DropTargetInfo? _dropTargetInfo;
|
||||
|
||||
static TreeView()
|
||||
{
|
||||
AffectsRender<TreeView>(DragIndicatorRenderInfoProperty);
|
||||
}
|
||||
|
||||
public TreeView()
|
||||
{
|
||||
@ -91,6 +163,64 @@ public class TreeView : AvaloniaTreeView
|
||||
DefaultCheckedItems = new List<TreeViewItem>();
|
||||
}
|
||||
|
||||
public void ExpandAll()
|
||||
{
|
||||
foreach (var item in Items) {
|
||||
if (item is TreeViewItem treeItem) {
|
||||
this.ExpandSubTree(treeItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void CheckedSubTree(TreeViewItem item)
|
||||
{
|
||||
if (!IsCheckable) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!item.IsEnabled || !item.IsCheckable) {
|
||||
return;
|
||||
}
|
||||
|
||||
item.IsChecked = true;
|
||||
if (item.Presenter?.Panel == null && this.GetVisualRoot() is ILayoutRoot visualRoot) {
|
||||
var layoutManager = LayoutUtils.GetLayoutManager(visualRoot);
|
||||
layoutManager.ExecuteLayoutPass();
|
||||
}
|
||||
foreach (var childItem in item.Items) {
|
||||
if (childItem is TreeViewItem treeViewItem) {
|
||||
CheckedSubTree(treeViewItem);
|
||||
}
|
||||
}
|
||||
|
||||
if (item.Parent is TreeViewItem itemParent) {
|
||||
SetupParentNodeCheckedStatus(itemParent);
|
||||
}
|
||||
}
|
||||
|
||||
public void UnCheckedSubTree(TreeViewItem item)
|
||||
{
|
||||
if (!IsCheckable) {
|
||||
return;
|
||||
}
|
||||
if (!item.IsEnabled || !item.IsCheckable) {
|
||||
return;
|
||||
}
|
||||
item.IsChecked = false;
|
||||
if (item.Presenter?.Panel == null && this.GetVisualRoot() is ILayoutRoot visualRoot) {
|
||||
var layoutManager = LayoutUtils.GetLayoutManager(visualRoot);
|
||||
layoutManager.ExecuteLayoutPass();
|
||||
}
|
||||
foreach (var childItem in item.Items) {
|
||||
if (childItem is TreeViewItem treeViewItem) {
|
||||
UnCheckedSubTree(treeViewItem);
|
||||
}
|
||||
}
|
||||
if (item.Parent is TreeViewItem itemParent) {
|
||||
SetupParentNodeCheckedStatus(itemParent);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdatePseudoClasses()
|
||||
{
|
||||
PseudoClasses.Set(DraggablePC, IsDraggable);
|
||||
@ -125,7 +255,6 @@ public class TreeView : AvaloniaTreeView
|
||||
BindUtils.RelayBind(this, TreeView.IsShowIconProperty, treeViewItem, TreeViewItem.IsShowIconProperty);
|
||||
BindUtils.RelayBind(this, TreeView.IsShowLeafSwitcherProperty, treeViewItem, TreeViewItem.IsShowLeafSwitcherProperty);
|
||||
BindUtils.RelayBind(this, TreeView.IsCheckableProperty, treeViewItem, TreeViewItem.IsCheckboxVisibleProperty);
|
||||
BindUtils.RelayBind(this, TreeView.IsDraggableProperty, treeViewItem, TreeViewItem.IsDraggableProperty);
|
||||
}
|
||||
}
|
||||
|
||||
@ -141,13 +270,11 @@ public class TreeView : AvaloniaTreeView
|
||||
ApplyDefaultChecked();
|
||||
}
|
||||
|
||||
public void ExpandAll()
|
||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||
{
|
||||
foreach (var item in Items) {
|
||||
if (item is TreeViewItem treeItem) {
|
||||
this.ExpandSubTree(treeItem);
|
||||
}
|
||||
}
|
||||
base.OnApplyTemplate(e);
|
||||
TokenResourceBinder.CreateGlobalResourceBinding(this, DragIndicatorLineWidthProperty, TreeViewResourceKey.DragIndicatorLineWidth);
|
||||
TokenResourceBinder.CreateGlobalResourceBinding(this, DragIndicatorBrushProperty, GlobalResourceKey.ColorPrimary);
|
||||
}
|
||||
|
||||
private void ApplyDefaultChecked()
|
||||
@ -157,56 +284,6 @@ public class TreeView : AvaloniaTreeView
|
||||
}
|
||||
DefaultCheckedItems.Clear();
|
||||
}
|
||||
|
||||
public void CheckedSubTree(TreeViewItem item)
|
||||
{
|
||||
if (!IsCheckable) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!item.IsEnabled || !item.IsCheckable) {
|
||||
return;
|
||||
}
|
||||
|
||||
item.IsChecked = true;
|
||||
if (item.Presenter?.Panel == null && this.GetVisualRoot() is ILayoutRoot visualRoot) {
|
||||
var layoutManager = LayoutUtils.GetLayoutManager(visualRoot);
|
||||
layoutManager.ExecuteLayoutPass();
|
||||
}
|
||||
foreach (var childItem in item.Items) {
|
||||
if (childItem is TreeViewItem treeViewItem) {
|
||||
CheckedSubTree(treeViewItem);
|
||||
}
|
||||
}
|
||||
|
||||
if (item.Parent is TreeViewItem itemParent) {
|
||||
SetupParentNodeCheckedStatus(itemParent);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void UnCheckedSubTree(TreeViewItem item)
|
||||
{
|
||||
if (!IsCheckable) {
|
||||
return;
|
||||
}
|
||||
if (!item.IsEnabled || !item.IsCheckable) {
|
||||
return;
|
||||
}
|
||||
item.IsChecked = false;
|
||||
if (item.Presenter?.Panel == null && this.GetVisualRoot() is ILayoutRoot visualRoot) {
|
||||
var layoutManager = LayoutUtils.GetLayoutManager(visualRoot);
|
||||
layoutManager.ExecuteLayoutPass();
|
||||
}
|
||||
foreach (var childItem in item.Items) {
|
||||
if (childItem is TreeViewItem treeViewItem) {
|
||||
UnCheckedSubTree(treeViewItem);
|
||||
}
|
||||
}
|
||||
if (item.Parent is TreeViewItem itemParent) {
|
||||
SetupParentNodeCheckedStatus(itemParent);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupParentNodeCheckedStatus(TreeViewItem parent)
|
||||
{
|
||||
@ -236,14 +313,55 @@ public class TreeView : AvaloniaTreeView
|
||||
parent.IsChecked = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 自己优先的查找,用于确认拖动发生的节点
|
||||
internal TreeViewItem? GetNodeByPositionSelfFirst(Point position)
|
||||
{
|
||||
TreeViewItem? result = null;
|
||||
for (var i = 0; i < ItemCount; i++) {
|
||||
var current = ContainerFromIndex(i);
|
||||
if (current is TreeViewItem currentTreeItem) {
|
||||
result = GetNodeByPositionSelfFirst(position, currentTreeItem);
|
||||
}
|
||||
if (result is not null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private TreeViewItem? GetNodeByPositionSelfFirst(Point position, TreeViewItem current)
|
||||
{
|
||||
var localPosition = this.TranslatePoint(position, current) ?? default;
|
||||
TreeViewItem? result = current.IsInDragHeaderBounds(localPosition) ? current : null;
|
||||
|
||||
if (result is not null) {
|
||||
return result;
|
||||
}
|
||||
|
||||
for (var i = 0; i < current.ItemCount; i++) {
|
||||
var child = current.ContainerFromIndex(i);
|
||||
if (child is TreeViewItem childItem) {
|
||||
result = GetNodeByPositionSelfFirst(position, childItem);
|
||||
}
|
||||
|
||||
if (result is not null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// 孩子优先的查找
|
||||
internal TreeViewItem? GetNodeByPosition(Point position)
|
||||
{
|
||||
TreeViewItem? result = null;
|
||||
for (var i = 0; i < ItemCount; i++) {
|
||||
var current = ContainerFromIndex(i) as TreeViewItem;
|
||||
if (current is not null) {
|
||||
result = GetNodeByPosition(position, current);
|
||||
var child = ContainerFromIndex(i);
|
||||
if (child is TreeViewItem childItem) {
|
||||
result = GetNodeByPosition(position, childItem);
|
||||
}
|
||||
if (result is not null) {
|
||||
break;
|
||||
@ -257,17 +375,17 @@ public class TreeView : AvaloniaTreeView
|
||||
{
|
||||
TreeViewItem? result = null;
|
||||
for (var i = 0; i < current.ItemCount; i++) {
|
||||
var child = current.ContainerFromIndex(i) as TreeViewItem;
|
||||
if (child is not null) {
|
||||
result = GetNodeByPosition(position, child);
|
||||
var child = current.ContainerFromIndex(i);
|
||||
if (child is TreeViewItem childItem) {
|
||||
result = GetNodeByPosition(position, childItem);
|
||||
}
|
||||
|
||||
if (result is not null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
result ??= current.IsDragOverForPoint(position) ? current : null;
|
||||
var localPosition = this.TranslatePoint(position, current) ?? default;
|
||||
result ??= current.IsInDragBounds(localPosition) ? current : null;
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -275,9 +393,9 @@ public class TreeView : AvaloniaTreeView
|
||||
{
|
||||
TreeViewItem? result = null;
|
||||
for (var i = 0; i < ItemCount; i++) {
|
||||
var current = ContainerFromIndex(i) as TreeViewItem;
|
||||
if (current is not null) {
|
||||
result = GetNodeByOffsetY(position, current);
|
||||
var child = ContainerFromIndex(i);
|
||||
if (child is TreeViewItem childItem) {
|
||||
result = GetNodeByOffsetY(position, childItem);
|
||||
}
|
||||
if (result is not null) {
|
||||
break;
|
||||
@ -301,7 +419,302 @@ public class TreeView : AvaloniaTreeView
|
||||
}
|
||||
}
|
||||
|
||||
result ??= current.IsDragOverForOffsetY(position) ? current : null;
|
||||
var localPosition = this.TranslatePoint(position, current) ?? default;
|
||||
result ??= current.IsDragOverForOffsetY(localPosition) ? current : null;
|
||||
return result;
|
||||
}
|
||||
|
||||
#region 拖动相关处理
|
||||
|
||||
protected override void OnPointerPressed(PointerPressedEventArgs e)
|
||||
{
|
||||
base.OnPointerPressed(e);
|
||||
if (IsDraggable) {
|
||||
e.Handled = true;
|
||||
_lastPoint = e.GetPosition(this);
|
||||
e.PreventGestureRecognition();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnPointerMoved(PointerEventArgs e)
|
||||
{
|
||||
if (_lastPoint.HasValue) {
|
||||
var delta = e.GetPosition(this) - _lastPoint.Value;
|
||||
var manhattanDistance = Math.Abs(delta.X) + Math.Abs(delta.Y);
|
||||
// 先写死
|
||||
if (manhattanDistance > 5) {
|
||||
if (!IsDragging) {
|
||||
HandlePrepareDrag();
|
||||
IsDragging = true;
|
||||
}
|
||||
HandleDragging(e.GetPosition(this), delta);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnPointerCaptureLost(PointerCaptureLostEventArgs e)
|
||||
{
|
||||
if (_lastPoint.HasValue) {
|
||||
HandleDragCompleted(_lastPoint.Value);
|
||||
_lastPoint = null;
|
||||
IsDragging = false;
|
||||
}
|
||||
|
||||
base.OnPointerCaptureLost(e);
|
||||
}
|
||||
|
||||
protected override void OnPointerReleased(PointerReleasedEventArgs e)
|
||||
{
|
||||
base.OnPointerReleased(e);
|
||||
if (_lastPoint.HasValue) {
|
||||
e.Handled = true;
|
||||
HandleDragCompleted(e.GetPosition(this));
|
||||
_lastPoint = null;
|
||||
IsDragging = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandlePrepareDrag()
|
||||
{
|
||||
_beingDraggedTreeItem = GetNodeByPositionSelfFirst(_lastPoint!.Value);
|
||||
if (_beingDraggedTreeItem is not null) {
|
||||
Dispatcher.UIThread.Post(() => { _beingDraggedTreeItem.IsDragging = true; });
|
||||
}
|
||||
var adornerLayer = AdornerLayer.GetAdornerLayer(this);
|
||||
if (adornerLayer == null || _beingDraggedTreeItem is null) {
|
||||
return;
|
||||
}
|
||||
|
||||
_dragPreview = _beingDraggedTreeItem.BuildPreviewAdorner();
|
||||
AdornerLayer.SetAdornedElement(_dragPreview, TopLevel.GetTopLevel(this));
|
||||
AdornerLayer.SetIsClipEnabled(_dragPreview, false);
|
||||
adornerLayer.Children.Add(_dragPreview);
|
||||
}
|
||||
|
||||
private void HandleDragging(Point position, Point delta)
|
||||
{
|
||||
if (_dragPreview is not null && _beingDraggedTreeItem is not null) {
|
||||
var basePosition = _beingDraggedTreeItem.TranslatePoint(new Point(0, 0), TopLevel.GetTopLevel(this)!) ?? default;
|
||||
_dragPreview.OffsetX = basePosition.X + delta.X;
|
||||
_dragPreview.OffsetY = basePosition.Y + delta.Y;
|
||||
SetupDragOver(position);
|
||||
if (_currentDragOver is not null) {
|
||||
_dropTargetNode = GetNodeByOffsetY(position);
|
||||
}
|
||||
|
||||
SetupDragIndicatorRenderInfo(in position);
|
||||
}
|
||||
}
|
||||
|
||||
// 正常一个有效的拖动,应该 _currentDragOver 不为空
|
||||
private void SetupDragIndicatorRenderInfo(in Point position)
|
||||
{
|
||||
if (_currentDragOver is null ||
|
||||
_currentDragOver == _beingDraggedTreeItem ||
|
||||
_dropTargetNode == _beingDraggedTreeItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
var effectiveDropTarget = _currentDragOver;
|
||||
if (_dropTargetNode is not null) {
|
||||
effectiveDropTarget = _dropTargetNode;
|
||||
}
|
||||
|
||||
_dropTargetInfo = new DropTargetInfo();
|
||||
_dropTargetInfo.IsRoot = false;
|
||||
|
||||
var effectiveDragHeaderLocalBounds = effectiveDropTarget.GetDragBounds();
|
||||
var effectiveDragHeaderLocalOffset = effectiveDragHeaderLocalBounds.Position;
|
||||
var effectiveDragHeaderBounds = new Rect(effectiveDropTarget.TranslatePoint(effectiveDragHeaderLocalOffset, this) ?? effectiveDragHeaderLocalOffset,
|
||||
effectiveDragHeaderLocalBounds.Size);
|
||||
|
||||
var effectiveDragHeaderOffset = effectiveDragHeaderBounds.Position;
|
||||
var dropTargetHalfOffsetY = effectiveDragHeaderOffset.Y + effectiveDragHeaderBounds.Height / 2;
|
||||
var offsetY = position.Y;
|
||||
Point startPoint = default;
|
||||
Point endPoint = default;
|
||||
IndicatorPosition indicatorPosition = IndicatorPosition.In;
|
||||
var offsetYDelta = effectiveDropTarget.FrameDecoratorMargin().Bottom + DragIndicatorLineWidth / 3;
|
||||
|
||||
var minOffsetY = DragIndicatorLineWidth / 2;
|
||||
var maxOffsetY = Bounds.Height - DragIndicatorLineWidth / 2;
|
||||
|
||||
if (effectiveDropTarget != _currentDragOver) {
|
||||
// 这种情况父节点不可能为空
|
||||
var effectiveIndex = 0;
|
||||
if (effectiveDropTarget.Parent is TreeViewItem parentTreeItem) {
|
||||
effectiveIndex = parentTreeItem.IndexFromContainer(effectiveDropTarget);
|
||||
_dropTargetInfo.TargetTreeItem = parentTreeItem;
|
||||
}
|
||||
|
||||
if (offsetY > dropTargetHalfOffsetY) {
|
||||
startPoint = effectiveDragHeaderBounds.BottomLeft;
|
||||
endPoint = effectiveDragHeaderBounds.BottomRight;
|
||||
indicatorPosition = IndicatorPosition.After;
|
||||
startPoint = startPoint.WithY(Math.Min(startPoint.Y + offsetYDelta, maxOffsetY));
|
||||
endPoint = endPoint.WithY(Math.Min(endPoint.Y + offsetYDelta, maxOffsetY));
|
||||
_dropTargetInfo.Index = effectiveIndex + 1;
|
||||
} else {
|
||||
startPoint = effectiveDragHeaderBounds.TopLeft;
|
||||
endPoint = effectiveDragHeaderBounds.TopRight;
|
||||
indicatorPosition = IndicatorPosition.Before;
|
||||
startPoint = startPoint.WithY(Math.Max(startPoint.Y - offsetYDelta, minOffsetY));
|
||||
endPoint = endPoint.WithY(Math.Max(endPoint.Y - offsetYDelta, minOffsetY));
|
||||
_dropTargetInfo.Index = effectiveIndex;
|
||||
}
|
||||
|
||||
} else {
|
||||
var isFirstChild = false;
|
||||
var effectiveIndex = 0;
|
||||
if (effectiveDropTarget.Parent is TreeViewItem parentItem) {
|
||||
effectiveIndex = parentItem.IndexFromContainer(effectiveDropTarget);
|
||||
isFirstChild = parentItem.ContainerFromIndex(0) == this;
|
||||
}
|
||||
|
||||
var fixedOffset = 25;
|
||||
if (isFirstChild || effectiveDropTarget.Level == 0) {
|
||||
if (offsetY > dropTargetHalfOffsetY) {
|
||||
startPoint = effectiveDragHeaderBounds.BottomLeft.WithX(effectiveDragHeaderBounds.Left + fixedOffset);
|
||||
endPoint = effectiveDragHeaderBounds.BottomRight;
|
||||
indicatorPosition = IndicatorPosition.In;
|
||||
startPoint = startPoint.WithY(Math.Min(startPoint.Y + offsetYDelta, maxOffsetY));
|
||||
endPoint = endPoint.WithY(Math.Min(endPoint.Y + offsetYDelta, maxOffsetY));
|
||||
_dropTargetInfo.Index = effectiveDropTarget.ItemCount;
|
||||
_dropTargetInfo.IsRoot = false;
|
||||
_dropTargetInfo.TargetTreeItem = effectiveDropTarget;
|
||||
} else {
|
||||
_dropTargetInfo.IsRoot = effectiveDropTarget.Level == 0;
|
||||
_dropTargetInfo.Index = effectiveIndex;
|
||||
startPoint = effectiveDragHeaderBounds.TopLeft;
|
||||
endPoint = effectiveDragHeaderBounds.TopRight;
|
||||
indicatorPosition = IndicatorPosition.Before;
|
||||
startPoint = startPoint.WithY(Math.Max(startPoint.Y - offsetYDelta, minOffsetY));
|
||||
endPoint = endPoint.WithY(Math.Max(endPoint.Y - offsetYDelta, minOffsetY));
|
||||
}
|
||||
} else {
|
||||
startPoint = effectiveDragHeaderBounds.BottomLeft.WithX(effectiveDragHeaderBounds.Left + fixedOffset);
|
||||
endPoint = effectiveDragHeaderBounds.BottomRight;
|
||||
indicatorPosition = IndicatorPosition.In;
|
||||
startPoint = startPoint.WithY(Math.Min(startPoint.Y + offsetYDelta, maxOffsetY));
|
||||
endPoint = endPoint.WithY(Math.Min(endPoint.Y + offsetYDelta,maxOffsetY));
|
||||
_dropTargetInfo.IsRoot = false;
|
||||
_dropTargetInfo.TargetTreeItem = effectiveDropTarget;
|
||||
_dropTargetInfo.Index = effectiveDropTarget.ItemCount;
|
||||
}
|
||||
}
|
||||
|
||||
DragIndicatorRenderInfo = new DragIndicatorRenderInfo()
|
||||
{
|
||||
TargetTreeItem = effectiveDropTarget,
|
||||
StartPoint = startPoint,
|
||||
EndPoint = endPoint,
|
||||
IndicatorPosition = indicatorPosition
|
||||
};
|
||||
}
|
||||
|
||||
private void SetupDragOver(Point position)
|
||||
{
|
||||
var treeViewItem = GetNodeByPosition(position);
|
||||
if (_currentDragOver is not null) {
|
||||
if (_currentDragOver != treeViewItem) {
|
||||
_currentDragOver.IsDragOver = false;
|
||||
} else {
|
||||
_currentDragOver.IsDragOver = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (treeViewItem is not null) {
|
||||
_currentDragOver = treeViewItem;
|
||||
_currentDragOver.IsDragOver = true;
|
||||
} else {
|
||||
_currentDragOver = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleDragCompleted(Point point)
|
||||
{
|
||||
PerformDropOperation();
|
||||
if (_dragPreview is not null) {
|
||||
AdornerLayer layer = AdornerLayer.GetAdornerLayer(this)!;
|
||||
layer.Children.Remove(_dragPreview);
|
||||
if (_currentDragOver is not null) {
|
||||
_currentDragOver.IsDragOver = false;
|
||||
_currentDragOver = null;
|
||||
}
|
||||
|
||||
_dropTargetNode = null;
|
||||
}
|
||||
if (_beingDraggedTreeItem is not null) {
|
||||
_beingDraggedTreeItem.IsDragging = false;
|
||||
_beingDraggedTreeItem = null;
|
||||
}
|
||||
|
||||
DragIndicatorRenderInfo = null;
|
||||
_dropTargetInfo = null;
|
||||
}
|
||||
|
||||
private void PerformDropOperation()
|
||||
{
|
||||
if (_dropTargetInfo is null || _beingDraggedTreeItem is null) {
|
||||
return;
|
||||
}
|
||||
|
||||
object? sourceItem = default;
|
||||
if (_beingDraggedTreeItem.Parent is TreeViewItem parentItem) {
|
||||
sourceItem = parentItem.ItemFromContainer(_beingDraggedTreeItem);
|
||||
if (sourceItem is not null) {
|
||||
parentItem.Items.Remove(sourceItem);
|
||||
}
|
||||
} else if (_beingDraggedTreeItem.Level == 0) {
|
||||
sourceItem = ItemFromContainer(_beingDraggedTreeItem);
|
||||
if (sourceItem is not null) {
|
||||
Items.Remove(sourceItem);
|
||||
}
|
||||
}
|
||||
|
||||
if (_dropTargetInfo.IsRoot) {
|
||||
Items.Insert(_dropTargetInfo.Index, sourceItem);
|
||||
} else if (_dropTargetInfo.TargetTreeItem is not null) {
|
||||
_dropTargetInfo.TargetTreeItem.Items.Insert(_dropTargetInfo.Index, sourceItem);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public override void Render(DrawingContext context)
|
||||
{
|
||||
if (IsDragging && _dragIndicatorRenderInfo is not null) {
|
||||
var pen = new Pen(DragIndicatorBrush, DragIndicatorLineWidth);
|
||||
{
|
||||
using var state = context.PushRenderOptions(new RenderOptions
|
||||
{
|
||||
EdgeMode = EdgeMode.Aliased
|
||||
});
|
||||
context.DrawLine(pen, _dragIndicatorRenderInfo.StartPoint, _dragIndicatorRenderInfo.EndPoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class DropTargetInfo
|
||||
{
|
||||
public TreeViewItem? TargetTreeItem { get; set; }
|
||||
public int Index { get; set; }
|
||||
public bool IsRoot { get; set; }
|
||||
}
|
||||
|
||||
internal enum IndicatorPosition
|
||||
{
|
||||
Before,
|
||||
In,
|
||||
After
|
||||
}
|
||||
|
||||
internal record DragIndicatorRenderInfo
|
||||
{
|
||||
public TreeViewItem? TargetTreeItem { get; set; }
|
||||
public IndicatorPosition IndicatorPosition { get; set; }
|
||||
public Point StartPoint { get; set; }
|
||||
public Point EndPoint { get; set; }
|
||||
}
|
@ -11,7 +11,6 @@ using Avalonia.Data;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.LogicalTree;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Threading;
|
||||
|
||||
namespace AtomUI.Controls;
|
||||
|
||||
@ -127,11 +126,6 @@ public class TreeViewItem : AvaloniaTreeItem
|
||||
o => o.IsCheckboxEnable,
|
||||
(o, v) => o.IsCheckboxEnable = v);
|
||||
|
||||
internal static readonly DirectProperty<TreeViewItem, bool> IsDraggableProperty =
|
||||
AvaloniaProperty.RegisterDirect<TreeViewItem, bool>(nameof(IsDraggable),
|
||||
o => o.IsDraggable,
|
||||
(o, v) => o.IsDraggable = v);
|
||||
|
||||
internal static readonly DirectProperty<TreeViewItem, bool> IsDraggingProperty =
|
||||
AvaloniaProperty.RegisterDirect<TreeViewItem, bool>(nameof(IsDragging),
|
||||
o => o.IsDragging,
|
||||
@ -141,26 +135,11 @@ public class TreeViewItem : AvaloniaTreeItem
|
||||
AvaloniaProperty.RegisterDirect<TreeViewItem, bool>(nameof(IsDragOver),
|
||||
o => o.IsDragOver,
|
||||
(o, v) => o.IsDragOver = v);
|
||||
|
||||
internal static readonly DirectProperty<TreeViewItem, Point> DragOverPositionProperty =
|
||||
AvaloniaProperty.RegisterDirect<TreeViewItem, Point>(nameof(DragOverPosition),
|
||||
o => o.DragOverPosition,
|
||||
(o, v) => o.DragOverPosition = v);
|
||||
|
||||
internal static readonly DirectProperty<TreeViewItem, Thickness> DragFrameBorderThicknessProperty =
|
||||
AvaloniaProperty.RegisterDirect<TreeViewItem, Thickness>(nameof(DragFrameBorderThickness),
|
||||
o => o.DragFrameBorderThickness,
|
||||
(o, v) => o.DragFrameBorderThickness = v);
|
||||
|
||||
internal static readonly DirectProperty<TreeViewItem, double> DragIndicatorLineWidthProperty =
|
||||
AvaloniaProperty.RegisterDirect<TreeViewItem, double>(nameof(DragIndicatorLineWidth),
|
||||
o => o.DragIndicatorLineWidth,
|
||||
(o, v) => o.DragIndicatorLineWidth = v);
|
||||
|
||||
internal static readonly DirectProperty<TreeViewItem, IBrush?> DragIndicatorBrushProperty =
|
||||
AvaloniaProperty.RegisterDirect<TreeViewItem, IBrush?>(nameof(DragIndicatorBrush),
|
||||
o => o.DragIndicatorBrush,
|
||||
(o, v) => o.DragIndicatorBrush = v);
|
||||
|
||||
internal static readonly StyledProperty<IBrush?> EffectiveNodeBgProperty
|
||||
= AvaloniaProperty.Register<TreeViewItem, IBrush?>(nameof(EffectiveNodeBg));
|
||||
@ -232,14 +211,6 @@ public class TreeViewItem : AvaloniaTreeItem
|
||||
set => SetAndRaise(IsCheckboxEnableProperty, ref _isCheckboxEnable, value);
|
||||
}
|
||||
|
||||
private bool _isDraggable;
|
||||
|
||||
internal bool IsDraggable
|
||||
{
|
||||
get => _isDraggable;
|
||||
set => SetAndRaise(IsDraggableProperty, ref _isDraggable, value);
|
||||
}
|
||||
|
||||
private bool _isDragging;
|
||||
|
||||
internal bool IsDragging
|
||||
@ -256,14 +227,6 @@ public class TreeViewItem : AvaloniaTreeItem
|
||||
set => SetAndRaise(IsDragOverProperty, ref _isDragOver, value);
|
||||
}
|
||||
|
||||
private Point _dragOverPosition;
|
||||
|
||||
internal Point DragOverPosition
|
||||
{
|
||||
get => _dragOverPosition;
|
||||
set => SetAndRaise(DragOverPositionProperty, ref _dragOverPosition, value);
|
||||
}
|
||||
|
||||
private Thickness _dragFrameBorderThickness;
|
||||
|
||||
internal Thickness DragFrameBorderThickness
|
||||
@ -272,22 +235,6 @@ public class TreeViewItem : AvaloniaTreeItem
|
||||
set => SetAndRaise(DragFrameBorderThicknessProperty, ref _dragFrameBorderThickness, value);
|
||||
}
|
||||
|
||||
private double _dragIndicatorLineWidth;
|
||||
|
||||
internal double DragIndicatorLineWidth
|
||||
{
|
||||
get => _dragIndicatorLineWidth;
|
||||
set => SetAndRaise(DragIndicatorLineWidthProperty, ref _dragIndicatorLineWidth, value);
|
||||
}
|
||||
|
||||
private IBrush? _dragIndicatorBrush;
|
||||
|
||||
internal IBrush? DragIndicatorBrush
|
||||
{
|
||||
get => _dragIndicatorBrush;
|
||||
set => SetAndRaise(DragIndicatorBrushProperty, ref _dragIndicatorBrush, value);
|
||||
}
|
||||
|
||||
internal IBrush? EffectiveNodeBg
|
||||
{
|
||||
get => GetValue(EffectiveNodeBgProperty);
|
||||
@ -311,10 +258,6 @@ public class TreeViewItem : AvaloniaTreeItem
|
||||
private NodeSwitcherButton? _switcherButton;
|
||||
private Rect _effectiveBgRect;
|
||||
private readonly BorderRenderHelper _borderRenderHelper;
|
||||
private Point? _lastPoint;
|
||||
private DragPreviewAdorner? _dragPreview;
|
||||
private TreeViewItem? _currentDragOver; // 这个不是目标节点,有可能是在父节点上拖动
|
||||
private TreeViewItem? _dropTargetNode; // 目标释放节点
|
||||
|
||||
static TreeViewItem()
|
||||
{
|
||||
@ -323,8 +266,7 @@ public class TreeViewItem : AvaloniaTreeItem
|
||||
IsShowLineProperty,
|
||||
IsShowLeafSwitcherProperty,
|
||||
IsDraggingProperty,
|
||||
IsDragOverProperty,
|
||||
DragOverPositionProperty);
|
||||
IsDragOverProperty);
|
||||
}
|
||||
|
||||
public TreeViewItem()
|
||||
@ -402,10 +344,6 @@ public class TreeViewItem : AvaloniaTreeItem
|
||||
GlobalResourceKey.BorderThickness,
|
||||
BindingPriority.Template,
|
||||
new RenderScaleAwareThicknessConfigure(this));
|
||||
TokenResourceBinder.CreateGlobalResourceBinding(this, DragIndicatorLineWidthProperty,
|
||||
TreeViewResourceKey.DragIndicatorLineWidth);
|
||||
TokenResourceBinder.CreateGlobalResourceBinding(this, DragIndicatorBrushProperty,
|
||||
GlobalResourceKey.ColorPrimary);
|
||||
|
||||
base.OnApplyTemplate(e);
|
||||
_headerPresenter = e.NameScope.Find<ContentPresenter>(TreeViewItemTheme.HeaderPresenterPart);
|
||||
@ -473,112 +411,112 @@ public class TreeViewItem : AvaloniaTreeItem
|
||||
if (IsShowLine && (IsExpanded || IsLeaf)) {
|
||||
RenderTreeNodeLine(context);
|
||||
}
|
||||
if (IsDragOver) {
|
||||
RenderDraggingIndicator(context);
|
||||
}
|
||||
// if (IsDragOver) {
|
||||
// RenderDraggingIndicator(context);
|
||||
// }
|
||||
}
|
||||
|
||||
private void RenderDraggingIndicator(DrawingContext context)
|
||||
{
|
||||
if (OwnerTreeView is null || _dropTargetNode is null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var localPosition = OwnerTreeView.TranslatePoint(DragOverPosition, this) ?? default;
|
||||
var dropTargetOffset = _dropTargetNode.TranslatePoint(new Point(0, 0), this) ?? default;
|
||||
var dropTargetHalfOffsetY = dropTargetOffset.Y + _dropTargetNode._frameDecorator?.DesiredSize.Height / 2 ?? default;
|
||||
var offsetY = localPosition.Y;
|
||||
Point startPoint = default;
|
||||
Point endPoint = default;
|
||||
|
||||
var indicatorOffsetX = 0d;
|
||||
|
||||
if (this != _dropTargetNode) {
|
||||
if (_dropTargetNode._iconPresenter is not null && _dropTargetNode._iconPresenter.IsVisible) {
|
||||
var offset = _dropTargetNode._iconPresenter.TranslatePoint(new Point(0, 0), OwnerTreeView) ?? default;
|
||||
indicatorOffsetX = offset.X;
|
||||
} else if (_dropTargetNode._headerPresenter is not null) {
|
||||
var offset = _dropTargetNode._headerPresenter.TranslatePoint(new Point(0, 0), OwnerTreeView) ?? default;
|
||||
indicatorOffsetX = offset.X;
|
||||
}
|
||||
|
||||
var frameHeight = _dropTargetNode._frameDecorator?.Bounds.Height ?? default;
|
||||
var frameMargin = _dropTargetNode._frameDecorator?.Margin.Bottom ?? default;
|
||||
|
||||
var indicatorOffsetY = Math.Min(dropTargetOffset.Y + frameHeight + frameMargin + DragIndicatorLineWidth / 2, Bounds.Height - DragIndicatorLineWidth / 2);
|
||||
|
||||
if (offsetY > dropTargetHalfOffsetY) {
|
||||
startPoint = new Point(indicatorOffsetX, indicatorOffsetY);
|
||||
endPoint = new Point(indicatorOffsetX + _dropTargetNode.Bounds.Width, indicatorOffsetY);
|
||||
} else {
|
||||
startPoint = new Point(indicatorOffsetX, dropTargetOffset.Y + DragIndicatorLineWidth / 2);
|
||||
endPoint = new Point(indicatorOffsetX + _dropTargetNode.Bounds.Width, dropTargetOffset.Y + DragIndicatorLineWidth / 2);
|
||||
}
|
||||
} else {
|
||||
if (_iconPresenter is not null && _iconPresenter.IsVisible) {
|
||||
var offset = _iconPresenter.TranslatePoint(new Point(0, 0), OwnerTreeView) ?? default;
|
||||
indicatorOffsetX = offset.X;
|
||||
} else if (_headerPresenter is not null) {
|
||||
var offset = _headerPresenter.TranslatePoint(new Point(0, 0), OwnerTreeView) ?? default;
|
||||
indicatorOffsetX = offset.X;
|
||||
}
|
||||
|
||||
var isFirstChild = false;
|
||||
|
||||
var frameHeight = _frameDecorator?.Bounds.Height ?? default;
|
||||
|
||||
if (Parent is TreeViewItem parentItem) {
|
||||
isFirstChild = parentItem.ContainerFromIndex(0) == this;
|
||||
}
|
||||
|
||||
if (isFirstChild || Level == 0) {
|
||||
if (offsetY > dropTargetHalfOffsetY) {
|
||||
indicatorOffsetX += 20;
|
||||
startPoint = new Point(indicatorOffsetX, dropTargetOffset.Y + frameHeight);
|
||||
endPoint = new Point(indicatorOffsetX + Bounds.Width, dropTargetOffset.Y + frameHeight);
|
||||
} else {
|
||||
startPoint = new Point(indicatorOffsetX, dropTargetOffset.Y + DragIndicatorLineWidth / 2);
|
||||
endPoint = new Point(indicatorOffsetX + _dropTargetNode.Bounds.Width, dropTargetOffset.Y + DragIndicatorLineWidth / 2);
|
||||
}
|
||||
} else {
|
||||
indicatorOffsetX += 20;
|
||||
startPoint = new Point(indicatorOffsetX, dropTargetOffset.Y + frameHeight);
|
||||
endPoint = new Point(indicatorOffsetX + Bounds.Width, dropTargetOffset.Y + frameHeight);
|
||||
}
|
||||
}
|
||||
|
||||
var pen = new Pen(DragIndicatorBrush, DragIndicatorLineWidth);
|
||||
{
|
||||
using var state = context.PushRenderOptions(new RenderOptions
|
||||
{
|
||||
EdgeMode = EdgeMode.Aliased
|
||||
});
|
||||
context.DrawLine(pen, startPoint, endPoint);
|
||||
}
|
||||
}
|
||||
// private void RenderDraggingIndicator(DrawingContext context)
|
||||
// {
|
||||
// if (OwnerTreeView is null || _dropTargetNode is null) {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// var localPosition = OwnerTreeView.TranslatePoint(DragOverPosition, this) ?? default;
|
||||
// var dropTargetOffset = _dropTargetNode.TranslatePoint(new Point(0, 0), this) ?? default;
|
||||
// var dropTargetHalfOffsetY = dropTargetOffset.Y + _dropTargetNode._frameDecorator?.DesiredSize.Height / 2 ?? default;
|
||||
// var offsetY = localPosition.Y;
|
||||
// Point startPoint = default;
|
||||
// Point endPoint = default;
|
||||
//
|
||||
// var indicatorOffsetX = 0d;
|
||||
//
|
||||
// if (this != _dropTargetNode) {
|
||||
// if (_dropTargetNode._iconPresenter is not null && _dropTargetNode._iconPresenter.IsVisible) {
|
||||
// var offset = _dropTargetNode._iconPresenter.TranslatePoint(new Point(0, 0), OwnerTreeView) ?? default;
|
||||
// indicatorOffsetX = offset.X;
|
||||
// } else if (_dropTargetNode._headerPresenter is not null) {
|
||||
// var offset = _dropTargetNode._headerPresenter.TranslatePoint(new Point(0, 0), OwnerTreeView) ?? default;
|
||||
// indicatorOffsetX = offset.X;
|
||||
// }
|
||||
//
|
||||
// var frameHeight = _dropTargetNode._frameDecorator?.Bounds.Height ?? default;
|
||||
// var frameMargin = _dropTargetNode._frameDecorator?.Margin.Bottom ?? default;
|
||||
//
|
||||
// var indicatorOffsetY = Math.Min(dropTargetOffset.Y + frameHeight + frameMargin + DragIndicatorLineWidth / 2, Bounds.Height - DragIndicatorLineWidth / 2);
|
||||
//
|
||||
// if (offsetY > dropTargetHalfOffsetY) {
|
||||
// startPoint = new Point(indicatorOffsetX, indicatorOffsetY);
|
||||
// endPoint = new Point(indicatorOffsetX + _dropTargetNode.Bounds.Width, indicatorOffsetY);
|
||||
// } else {
|
||||
// startPoint = new Point(indicatorOffsetX, dropTargetOffset.Y + DragIndicatorLineWidth / 2);
|
||||
// endPoint = new Point(indicatorOffsetX + _dropTargetNode.Bounds.Width, dropTargetOffset.Y + DragIndicatorLineWidth / 2);
|
||||
// }
|
||||
// } else {
|
||||
// if (_iconPresenter is not null && _iconPresenter.IsVisible) {
|
||||
// var offset = _iconPresenter.TranslatePoint(new Point(0, 0), OwnerTreeView) ?? default;
|
||||
// indicatorOffsetX = offset.X;
|
||||
// } else if (_headerPresenter is not null) {
|
||||
// var offset = _headerPresenter.TranslatePoint(new Point(0, 0), OwnerTreeView) ?? default;
|
||||
// indicatorOffsetX = offset.X;
|
||||
// }
|
||||
//
|
||||
// var isFirstChild = false;
|
||||
//
|
||||
// var frameHeight = _frameDecorator?.Bounds.Height ?? default;
|
||||
//
|
||||
// if (Parent is TreeViewItem parentItem) {
|
||||
// isFirstChild = parentItem.ContainerFromIndex(0) == this;
|
||||
// }
|
||||
//
|
||||
// if (isFirstChild || Level == 0) {
|
||||
// if (offsetY > dropTargetHalfOffsetY) {
|
||||
// indicatorOffsetX += 20;
|
||||
// startPoint = new Point(indicatorOffsetX, dropTargetOffset.Y + frameHeight);
|
||||
// endPoint = new Point(indicatorOffsetX + Bounds.Width, dropTargetOffset.Y + frameHeight);
|
||||
// } else {
|
||||
// startPoint = new Point(indicatorOffsetX, dropTargetOffset.Y + DragIndicatorLineWidth / 2);
|
||||
// endPoint = new Point(indicatorOffsetX + _dropTargetNode.Bounds.Width, dropTargetOffset.Y + DragIndicatorLineWidth / 2);
|
||||
// }
|
||||
// } else {
|
||||
// indicatorOffsetX += 20;
|
||||
// startPoint = new Point(indicatorOffsetX, dropTargetOffset.Y + frameHeight);
|
||||
// endPoint = new Point(indicatorOffsetX + Bounds.Width, dropTargetOffset.Y + frameHeight);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// var pen = new Pen(DragIndicatorBrush, DragIndicatorLineWidth);
|
||||
// {
|
||||
// using var state = context.PushRenderOptions(new RenderOptions
|
||||
// {
|
||||
// EdgeMode = EdgeMode.Aliased
|
||||
// });
|
||||
// context.DrawLine(pen, startPoint, endPoint);
|
||||
// }
|
||||
// }
|
||||
|
||||
private void RenderTreeNodeLine(DrawingContext context)
|
||||
{
|
||||
if (_switcherButton is null) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var penWidth = BorderUtils.BuildRenderScaleAwareThickness(BorderThickness, VisualRoot?.RenderScaling ?? 1.0).Top;
|
||||
using var state = context.PushRenderOptions(new RenderOptions
|
||||
{
|
||||
EdgeMode = EdgeMode.Aliased
|
||||
});
|
||||
|
||||
|
||||
if (!IsLeaf) {
|
||||
var switcherMiddleBottom =
|
||||
_switcherButton.TranslatePoint(
|
||||
new Point(_switcherButton.DesiredSize.Width / 2, _switcherButton.DesiredSize.Height), this) ?? default;
|
||||
var blockStartPoint = new Point(switcherMiddleBottom.X, switcherMiddleBottom.Y);
|
||||
var blockEndPoint = new Point(blockStartPoint.X, DesiredSize.Height);
|
||||
|
||||
|
||||
context.DrawLine(new Pen(BorderBrush, penWidth), blockStartPoint, blockEndPoint);
|
||||
}
|
||||
|
||||
|
||||
// 画孩子线条
|
||||
if (!IsShowLeafSwitcher && IsLeaf) {
|
||||
var isLastChild = false;
|
||||
@ -587,7 +525,7 @@ public class TreeViewItem : AvaloniaTreeItem
|
||||
isLastChild = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
// 纵向
|
||||
var childStartPoint =
|
||||
@ -596,14 +534,14 @@ public class TreeViewItem : AvaloniaTreeItem
|
||||
_switcherButton.TranslatePoint(
|
||||
new Point(_switcherButton.DesiredSize.Width / 2,
|
||||
isLastChild ? _switcherButton.DesiredSize.Height : DesiredSize.Height), this) ?? default;
|
||||
|
||||
|
||||
if (isLastChild) {
|
||||
childEndPoint = childEndPoint.WithY(childEndPoint.Y / 2);
|
||||
}
|
||||
|
||||
|
||||
context.DrawLine(new Pen(BorderBrush, penWidth), childStartPoint, childEndPoint);
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
// 横向
|
||||
var childStartPoint =
|
||||
@ -614,7 +552,7 @@ public class TreeViewItem : AvaloniaTreeItem
|
||||
_switcherButton.TranslatePoint(
|
||||
new Point(_switcherButton.DesiredSize.Width, _switcherButton.DesiredSize.Height / 2),
|
||||
this) ?? default;
|
||||
|
||||
|
||||
context.DrawLine(new Pen(BorderBrush, penWidth), childStartPoint, childEndPoint);
|
||||
}
|
||||
}
|
||||
@ -707,227 +645,56 @@ public class TreeViewItem : AvaloniaTreeItem
|
||||
PseudoClasses.Set(TreeNodeHoverPC, false);
|
||||
}
|
||||
|
||||
#region 拖动相关处理
|
||||
|
||||
protected override void OnPointerPressed(PointerPressedEventArgs e)
|
||||
internal Rect GetDragBounds(bool includeChildren = false)
|
||||
{
|
||||
base.OnPointerPressed(e);
|
||||
if (IsDraggable) {
|
||||
e.Handled = true;
|
||||
_lastPoint = e.GetPosition(this);
|
||||
e.PreventGestureRecognition();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnPointerMoved(PointerEventArgs e)
|
||||
{
|
||||
if (_lastPoint.HasValue) {
|
||||
var delta = e.GetPosition(this) - _lastPoint.Value;
|
||||
var manhattanDistance = Math.Abs(delta.X) + Math.Abs(delta.Y);
|
||||
// 先写死
|
||||
if (manhattanDistance > 5) {
|
||||
if (!IsDragging) {
|
||||
HandlePrepareDrag();
|
||||
Dispatcher.UIThread.Post(() => { IsDragging = true; });
|
||||
}
|
||||
|
||||
HandleDragging(e.GetPosition(OwnerTreeView), delta);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnPointerCaptureLost(PointerCaptureLostEventArgs e)
|
||||
{
|
||||
if (_lastPoint.HasValue) {
|
||||
HandleDragCompleted(_lastPoint.Value);
|
||||
_lastPoint = null;
|
||||
IsDragging = false;
|
||||
}
|
||||
|
||||
base.OnPointerCaptureLost(e);
|
||||
}
|
||||
|
||||
protected override void OnPointerReleased(PointerReleasedEventArgs e)
|
||||
{
|
||||
base.OnPointerReleased(e);
|
||||
if (_lastPoint.HasValue) {
|
||||
e.Handled = true;
|
||||
HandleDragCompleted(e.GetPosition(this));
|
||||
_lastPoint = null;
|
||||
IsDragging = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandlePrepareDrag()
|
||||
{
|
||||
var adornerLayer = AdornerLayer.GetAdornerLayer(this);
|
||||
if (adornerLayer == null || _frameDecorator is null) {
|
||||
return;
|
||||
}
|
||||
|
||||
_dragPreview = new DragPreviewAdorner(_frameDecorator);
|
||||
AdornerLayer.SetAdornedElement(_dragPreview, TopLevel.GetTopLevel(this));
|
||||
AdornerLayer.SetIsClipEnabled(_dragPreview, false);
|
||||
adornerLayer.Children.Add(_dragPreview);
|
||||
}
|
||||
|
||||
private void HandleDragCompleted(Point point)
|
||||
{
|
||||
var dropInfo = FindDropTargetPosition();
|
||||
Console.WriteLine($"{dropInfo.Item1?.Header}|{dropInfo.Item2}|{dropInfo.Item3}");
|
||||
if (dropInfo.Item1 is not null) {
|
||||
PerformDropOperation(this, dropInfo.Item1, dropInfo.Item2);
|
||||
}
|
||||
if (_dragPreview is not null) {
|
||||
AdornerLayer layer = AdornerLayer.GetAdornerLayer(this)!;
|
||||
layer.Children.Remove(_dragPreview);
|
||||
if (_currentDragOver is not null) {
|
||||
_currentDragOver.IsDragOver = false;
|
||||
_currentDragOver.DragOverPosition = default;
|
||||
_currentDragOver = null;
|
||||
}
|
||||
|
||||
DragOverPosition = default;
|
||||
_dropTargetNode = null;
|
||||
}
|
||||
}
|
||||
|
||||
private (TreeViewItem?, int, bool) FindDropTargetPosition()
|
||||
{
|
||||
Console.WriteLine($"{_currentDragOver?.Header}-{_dropTargetNode?.Header}");
|
||||
if (_currentDragOver is null || OwnerTreeView is null || _dropTargetNode is null) {
|
||||
return (null, -1, false);
|
||||
}
|
||||
|
||||
bool isRoot = false;
|
||||
var index = 0;
|
||||
TreeViewItem? targetTreeItem = null;
|
||||
|
||||
var localPosition = OwnerTreeView.TranslatePoint(DragOverPosition, _currentDragOver) ?? default;
|
||||
var dropTargetOffset = _dropTargetNode.TranslatePoint(new Point(0, 0), _currentDragOver) ?? default;
|
||||
var dropTargetHalfOffsetY = dropTargetOffset.Y + _dropTargetNode._frameDecorator?.DesiredSize.Height / 2 ?? default;
|
||||
var offsetY = localPosition.Y;
|
||||
|
||||
if (_currentDragOver == _dropTargetNode) {
|
||||
|
||||
// 有可能是自己,也可能是自己前面,因为第一个元素我们区别对待
|
||||
var isFirstChild = false;
|
||||
|
||||
if (isFirstChild || Level == 0) {
|
||||
if (offsetY > dropTargetHalfOffsetY) {
|
||||
index = _dropTargetNode.ItemCount;
|
||||
targetTreeItem = _dropTargetNode;
|
||||
} else {
|
||||
if (_dropTargetNode.Parent is TreeViewItem parentItem) {
|
||||
index = parentItem.IndexFromContainer(_dropTargetNode);
|
||||
targetTreeItem = parentItem;
|
||||
} else {
|
||||
isRoot = true;
|
||||
index = OwnerTreeView.IndexFromContainer(_dropTargetNode);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
index = _dropTargetNode.ItemCount;
|
||||
}
|
||||
} else {
|
||||
var dropTargetNodeIndex = _currentDragOver.IndexFromContainer(_dropTargetNode);
|
||||
targetTreeItem = _dropTargetNode;
|
||||
if (offsetY > dropTargetHalfOffsetY) {
|
||||
index = dropTargetNodeIndex;
|
||||
} else {
|
||||
index = dropTargetNodeIndex - 1;
|
||||
}
|
||||
}
|
||||
return (targetTreeItem, index, isRoot);
|
||||
}
|
||||
|
||||
private void PerformDropOperation(TreeViewItem sourceItem, TreeViewItem targetItem, int index)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private void HandleDragging(Point treePosition, Point delta)
|
||||
{
|
||||
if (_dragPreview is not null && OwnerTreeView is not null) {
|
||||
var basePosition = this.TranslatePoint(new Point(0, 0), TopLevel.GetTopLevel(this)!) ?? default;
|
||||
_dragPreview.OffsetX = basePosition.X + delta.X;
|
||||
_dragPreview.OffsetY = basePosition.Y + delta.Y;
|
||||
SetupDragOver(treePosition);
|
||||
if (_currentDragOver is not null) {
|
||||
_currentDragOver._dropTargetNode = OwnerTreeView?.GetNodeByOffsetY(treePosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void SetupDragOver(Point treePosition)
|
||||
{
|
||||
if (OwnerTreeView is null) {
|
||||
return;
|
||||
}
|
||||
var treeViewItem = OwnerTreeView.GetNodeByPosition(treePosition);
|
||||
if (_currentDragOver is not null) {
|
||||
if (_currentDragOver != treeViewItem) {
|
||||
_currentDragOver.IsDragOver = false;
|
||||
_currentDragOver.DragOverPosition = default;
|
||||
} else {
|
||||
_currentDragOver.IsDragOver = true;
|
||||
_currentDragOver.DragOverPosition = treePosition;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (treeViewItem is not null) {
|
||||
_currentDragOver = treeViewItem;
|
||||
_currentDragOver.IsDragOver = true;
|
||||
_currentDragOver.DragOverPosition = treePosition;
|
||||
} else {
|
||||
_currentDragOver = null;
|
||||
}
|
||||
}
|
||||
|
||||
internal bool IsDragOverForPoint(Point treePosition)
|
||||
{
|
||||
if (OwnerTreeView is null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var offsetX = 0d;
|
||||
var offsetY = 0d;
|
||||
|
||||
|
||||
if (_iconPresenter is not null && _iconPresenter.IsVisible) {
|
||||
var offset = _iconPresenter.TranslatePoint(new Point(0, 0), OwnerTreeView) ?? default;
|
||||
var offset = _iconPresenter.TranslatePoint(new Point(0, 0), this) ?? default;
|
||||
offsetX = offset.X;
|
||||
offsetY = offset.Y;
|
||||
} else if (_headerPresenter is not null) {
|
||||
var offset = _headerPresenter.TranslatePoint(new Point(0, 0), OwnerTreeView) ?? default;
|
||||
var offset = _headerPresenter.TranslatePoint(new Point(0, 0), this) ?? default;
|
||||
offsetX = offset.X;
|
||||
offsetY = offset.Y;
|
||||
}
|
||||
|
||||
|
||||
if (_switcherButton is not null && _switcherButton.IsIconVisible) {
|
||||
offsetX -= _switcherButton.Bounds.Width;
|
||||
}
|
||||
|
||||
var treeViewBound = new Rect(new Point(offsetX, offsetY), Bounds.Size);
|
||||
return treeViewBound.Contains(treePosition);
|
||||
return new Rect(new Point(offsetX, offsetY), new Size(Bounds.Width, includeChildren ? Bounds.Height : _headerPresenter?.Bounds.Height ?? default));
|
||||
}
|
||||
|
||||
internal bool IsDragOverForOffsetY(Point treePosition)
|
||||
internal Thickness FrameDecoratorMargin()
|
||||
{
|
||||
return _frameDecorator?.Margin ?? default;
|
||||
}
|
||||
|
||||
internal bool IsInDragBounds(Point point)
|
||||
{
|
||||
return GetDragBounds(true).Contains(point);
|
||||
}
|
||||
|
||||
internal bool IsInDragHeaderBounds(Point point)
|
||||
{
|
||||
return GetDragBounds(false).Contains(point);
|
||||
}
|
||||
|
||||
internal bool IsDragOverForOffsetY(Point point)
|
||||
{
|
||||
if (OwnerTreeView is null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var offsetX = 0d;
|
||||
var offsetY = 0d;
|
||||
|
||||
var offset = this.TranslatePoint(new Point(0, 0), OwnerTreeView) ?? default;
|
||||
offsetX = offset.X;
|
||||
offsetY = offset.Y;
|
||||
|
||||
var treeViewBound = new Rect(new Point(offsetX, offsetY), Bounds.Size);
|
||||
return treeViewBound.Contains(treePosition);
|
||||
|
||||
return new Rect(Bounds.Size).Contains(point);
|
||||
}
|
||||
|
||||
#endregion
|
||||
internal DragPreviewAdorner BuildPreviewAdorner()
|
||||
{
|
||||
return new DragPreviewAdorner(_frameDecorator!);
|
||||
}
|
||||
|
||||
// #endregion
|
||||
}
|
Loading…
Reference in New Issue
Block a user