mirror of https://github.com/icsharpcode/ILSpy.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
127 lines
4.9 KiB
127 lines
4.9 KiB
// Copyright (c) 2026 AlphaSierraPapa for the SharpDevelop Team |
|
// |
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this |
|
// software and associated documentation files (the "Software"), to deal in the Software |
|
// without restriction, including without limitation the rights to use, copy, modify, merge, |
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons |
|
// to whom the Software is furnished to do so, subject to the following conditions: |
|
// |
|
// The above copyright notice and this permission notice shall be included in all copies or |
|
// substantial portions of the Software. |
|
// |
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, |
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR |
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE |
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
|
// DEALINGS IN THE SOFTWARE. |
|
|
|
using Avalonia.Controls; |
|
using Avalonia.Headless.NUnit; |
|
using Avalonia.Threading; |
|
|
|
using AwesomeAssertions; |
|
|
|
using ICSharpCode.ILSpyX.TreeView; |
|
using ICSharpCode.ILSpyX.TreeView.PlatformAbstractions; |
|
|
|
using ICSharpCode.ILSpy.Controls.TreeView; |
|
|
|
using NUnit.Framework; |
|
|
|
namespace ICSharpCode.ILSpy.Tests.Controls; |
|
|
|
// External Explorer drops landed only when the cursor was over a row -- ResolveDropTarget bails |
|
// when e.Source has no SharpTreeViewItem ancestor, so a drop into the empty space below the last |
|
// row never reached SharpTreeNode.InternalDrop. In the assembly tree that meant "drop a DLL onto |
|
// an empty list" silently did nothing. The fallback is the same as what the WPF SharpTreeView |
|
// did: target the root node at its end with DropPlace.Inside, then let CanDrop decide. |
|
[TestFixture] |
|
public class SharpTreeViewEmptySpaceDropTests |
|
{ |
|
sealed class RootNode : SharpTreeNode |
|
{ |
|
public bool AcceptDrops { get; init; } |
|
public override object Text => "root"; |
|
public override bool CanDrop(IPlatformDragEventArgs e, int index) => AcceptDrops; |
|
public override void Drop(IPlatformDragEventArgs e, int index) { } |
|
} |
|
|
|
static (Window window, SharpTreeView tree) Host(SharpTreeNode? root) |
|
{ |
|
var tree = new SharpTreeView { ShowRoot = false, Root = root }; |
|
var window = new Window { Content = tree, Width = 300, Height = 400 }; |
|
window.Show(); |
|
Dispatcher.UIThread.RunJobs(); |
|
return (window, tree); |
|
} |
|
|
|
[AvaloniaTest] |
|
public void Empty_space_drop_resolves_to_root_inside_at_end() |
|
{ |
|
var root = new RootNode { AcceptDrops = true }; |
|
root.Children.Add(new RootNode { AcceptDrops = false }); |
|
var (_, tree) = Host(root); |
|
|
|
var target = tree.ResolveEmptySpaceDropTarget(); |
|
|
|
target.Should().NotBeNull(); |
|
ReferenceEquals(target!.Value.Node, root).Should().BeTrue(); |
|
target.Value.Index.Should().Be(root.Children.Count); |
|
target.Value.Place.Should().Be(SharpTreeView.DropPlace.Inside); |
|
target.Value.Item.Should().BeNull(); |
|
} |
|
|
|
[AvaloniaTest] |
|
public void Empty_space_drop_returns_null_when_no_root() |
|
{ |
|
var (_, tree) = Host(root: null); |
|
|
|
tree.ResolveEmptySpaceDropTarget().Should().BeNull(); |
|
} |
|
|
|
// "Drop directly onto a row" lands DropPlace.Inside on the row's own node; most concrete |
|
// SharpTreeNodes inherit the base CanDrop (returns false) so the row would refuse the payload. |
|
// PickAcceptingTarget falls back to the root in that case so the file still gets opened. |
|
[AvaloniaTest] |
|
public void Drop_on_rejecting_row_falls_back_to_root() |
|
{ |
|
var root = new RootNode { AcceptDrops = true }; |
|
var child = new RootNode { AcceptDrops = false }; |
|
root.Children.Add(child); |
|
var (_, tree) = Host(root); |
|
|
|
var initial = new SharpTreeView.DropTarget(child, child.Children.Count, |
|
SharpTreeView.DropPlace.Inside, Item: null); |
|
var picked = tree.PickAcceptingTarget(new AvaloniaPlatformDragEventArgs(new AvaloniaDataObject()), initial); |
|
|
|
picked.Should().NotBeNull(); |
|
ReferenceEquals(picked!.Value.Node, root).Should().BeTrue(); |
|
picked.Value.Index.Should().Be(root.Children.Count); |
|
} |
|
|
|
[AvaloniaTest] |
|
public void Drop_on_accepting_row_keeps_the_row_target() |
|
{ |
|
var root = new RootNode { AcceptDrops = true }; |
|
var child = new RootNode { AcceptDrops = true }; |
|
root.Children.Add(child); |
|
var (_, tree) = Host(root); |
|
|
|
var initial = new SharpTreeView.DropTarget(child, child.Children.Count, |
|
SharpTreeView.DropPlace.Inside, Item: null); |
|
var picked = tree.PickAcceptingTarget(new AvaloniaPlatformDragEventArgs(new AvaloniaDataObject()), initial); |
|
|
|
picked.Should().NotBeNull(); |
|
ReferenceEquals(picked!.Value.Node, child).Should().BeTrue(); |
|
} |
|
|
|
[AvaloniaTest] |
|
public void Drop_with_no_target_and_no_root_returns_null() |
|
{ |
|
var (_, tree) = Host(root: null); |
|
|
|
tree.PickAcceptingTarget(new AvaloniaPlatformDragEventArgs(new AvaloniaDataObject()), initial: null) |
|
.Should().BeNull(); |
|
} |
|
}
|
|
|