using System.Reflection; using Avalonia.Controls; using Avalonia.Threading; using Shouldly; using Xunit; using AtomUIWindow = AtomUI.Desktop.Controls.Window; namespace AtomUI.Desktop.Controls.Tests.TreeView; public class TreeViewPinnedOpenTests { static TreeViewPinnedOpenTests() { AvaloniaTestApp.EnsureInitialized(); } [Fact] public void Pinned_FloatableTreeView_Opens_And_Rejects_Normal_Close() { var flyout = CreateFlyout(); var tree = CreateTree(flyout); var window = CreateWindow(tree); try { window.Show(); Dispatcher.UIThread.RunJobs(); SetPopupPinnedOpen(tree, true); Dispatcher.UIThread.RunJobs(); flyout.IsPopupPinnedOpen.ShouldBeTrue(); flyout.IsOpen.ShouldBeTrue(); flyout.Popup.ShouldBeOfType().IsPopupPinnedOpen.ShouldBeTrue(); tree.Close(); Dispatcher.UIThread.RunJobs(); flyout.IsOpen.ShouldBeTrue(); flyout.Popup.IsOpen.ShouldBeTrue(); } finally { SetPopupPinnedOpenIfAvailable(tree, false); flyout.Hide(); window.Close(); Dispatcher.UIThread.RunJobs(); } } [Fact] public void Pinned_FloatableTreeView_Replacement_Closes_Old_And_Opens_New_Flyout() { var oldFlyout = CreateFlyout(); var newFlyout = CreateFlyout(); var tree = CreateTree(oldFlyout); var window = CreateWindow(tree); try { window.Show(); Dispatcher.UIThread.RunJobs(); SetPopupPinnedOpen(tree, true); Dispatcher.UIThread.RunJobs(); oldFlyout.IsOpen.ShouldBeTrue(); tree.TreeViewFlyout = newFlyout; Dispatcher.UIThread.RunJobs(); oldFlyout.IsPopupPinnedOpen.ShouldBeFalse(); oldFlyout.IsOpen.ShouldBeFalse(); oldFlyout.Popup.IsOpen.ShouldBeFalse(); newFlyout.IsPopupPinnedOpen.ShouldBeTrue(); newFlyout.IsOpen.ShouldBeTrue(); } finally { SetPopupPinnedOpenIfAvailable(tree, false); oldFlyout.Hide(); newFlyout.Hide(); window.Close(); Dispatcher.UIThread.RunJobs(); } } [Fact] public void Pinned_FloatableTreeView_Detach_ForceCloses_And_Reattach_Reopens_Flyout() { var flyout = CreateFlyout(); var tree = CreateTree(flyout); var window = CreateWindow(tree); try { window.Show(); Dispatcher.UIThread.RunJobs(); SetPopupPinnedOpen(tree, true); Dispatcher.UIThread.RunJobs(); flyout.IsOpen.ShouldBeTrue(); window.Content = new Border(); Dispatcher.UIThread.RunJobs(); flyout.IsPopupPinnedOpen.ShouldBeFalse(); flyout.IsOpen.ShouldBeFalse(); flyout.Popup.IsOpen.ShouldBeFalse(); window.Content = tree; Dispatcher.UIThread.RunJobs(); flyout.IsPopupPinnedOpen.ShouldBeTrue(); flyout.IsOpen.ShouldBeTrue(); } finally { SetPopupPinnedOpenIfAvailable(tree, false); flyout.Hide(); window.Close(); Dispatcher.UIThread.RunJobs(); } } [Fact] public void Unpinned_FloatableTreeView_Does_Not_Repin_The_Flyout_When_It_Reopens() { var flyout = CreateFlyout(); var tree = CreateTree(flyout); var window = CreateWindow(tree); try { window.Show(); Dispatcher.UIThread.RunJobs(); SetPopupPinnedOpen(tree, true); Dispatcher.UIThread.RunJobs(); flyout.IsOpen.ShouldBeTrue(); SetPopupPinnedOpen(tree, false); Dispatcher.UIThread.RunJobs(); flyout.IsPopupPinnedOpen.ShouldBeFalse(); flyout.IsOpen.ShouldBeTrue(); flyout.Hide(); Dispatcher.UIThread.RunJobs(); flyout.IsOpen.ShouldBeFalse(); flyout.ShowAt(tree); Dispatcher.UIThread.RunJobs(); flyout.IsPopupPinnedOpen.ShouldBeFalse(); flyout.Hide(); Dispatcher.UIThread.RunJobs(); flyout.IsOpen.ShouldBeFalse(); } finally { SetPopupPinnedOpenIfAvailable(tree, false); flyout.CloseForLifecycle(); window.Close(); Dispatcher.UIThread.RunJobs(); } } private static FloatableTreeView CreateTree(TreeViewFlyout flyout) { var tree = new FloatableTreeView { Width = 180, Height = 120, TreeViewFlyout = flyout, IsMotionEnabled = false }; tree.Items.Add(new TreeItemNode { Header = "Anchor" }); return tree; } private static TreeViewFlyout CreateFlyout() { var flyout = new TreeViewFlyout { IsMotionEnabled = false, ShouldUseOverlayPopup = true }; flyout.Items.Add(new TreeItemNode { Header = "Child" }); return flyout; } private static AtomUIWindow CreateWindow(Control content) { return new AtomUIWindow { Width = 360, Height = 260, Content = content }; } private static void SetPopupPinnedOpen(object target, bool value) { var property = target.GetType().GetProperty( "IsPopupPinnedOpen", BindingFlags.Instance | BindingFlags.NonPublic); property.ShouldNotBeNull($"{target.GetType().Name} must own the internal popup pin contract.") .SetValue(target, value); } private static void SetPopupPinnedOpenIfAvailable(object target, bool value) { target.GetType() .GetProperty("IsPopupPinnedOpen", BindingFlags.Instance | BindingFlags.NonPublic)? .SetValue(target, value); } }