diff --git a/src/Dock.Model/FactoryBase.Dockable.cs b/src/Dock.Model/FactoryBase.Dockable.cs index 5dbb1f2f9..6be83bb3f 100644 --- a/src/Dock.Model/FactoryBase.Dockable.cs +++ b/src/Dock.Model/FactoryBase.Dockable.cs @@ -12,6 +12,10 @@ namespace Dock.Model; /// public abstract partial class FactoryBase { + private const double DefaultFloatingWidth = 300; + private const double DefaultFloatingHeight = 400; + private const double MinimumTrackedFloatingSize = 16; + /// public virtual void AddDockable(IDock dock, IDockable dockable) { @@ -624,6 +628,21 @@ private static bool IsValidSize(double value) return !double.IsNaN(value) && !double.IsInfinity(value) && value > 0; } + private static double ResolveFloatingSize(double dockableSize, double ownerSize, double fallbackSize) + { + if (IsValidSize(dockableSize) && dockableSize > MinimumTrackedFloatingSize) + { + return dockableSize; + } + + if (IsValidSize(ownerSize) && ownerSize > MinimumTrackedFloatingSize) + { + return ownerSize; + } + + return fallbackSize; + } + /// public virtual void PinDockable(IDockable dockable) { @@ -1016,14 +1035,8 @@ public virtual void FloatDockable(IDockable dockable, DockWindowOptions? options { dockablePointerScreenY = !double.IsNaN(dockableY) ? dockableY : !double.IsNaN(ownerY) ? ownerY : 0; } - if (double.IsNaN(dockableWidth)) - { - dockableWidth = double.IsNaN(ownerWidth) ? 300 : ownerWidth; - } - if (double.IsNaN(dockableHeight)) - { - dockableHeight = double.IsNaN(ownerHeight) ? 400 : ownerHeight; - } + dockableWidth = ResolveFloatingSize(dockableWidth, ownerWidth, DefaultFloatingWidth); + dockableHeight = ResolveFloatingSize(dockableHeight, ownerHeight, DefaultFloatingHeight); PrepareWindowOptionsForDockable(dockable, options); SplitToWindow(dock, dockable, dockablePointerScreenX, dockablePointerScreenY, dockableWidth, dockableHeight, options); @@ -1070,8 +1083,8 @@ public virtual void FloatAllDockables(IDockable dockable, DockWindowOptions? opt pointerY = !double.IsNaN(ownerY) ? ownerY : 0; } - var width = double.IsNaN(ownerWidth) ? 300 : ownerWidth; - var height = double.IsNaN(ownerHeight) ? 400 : ownerHeight; + var width = ResolveFloatingSize(double.NaN, ownerWidth, DefaultFloatingWidth); + var height = ResolveFloatingSize(double.NaN, ownerHeight, DefaultFloatingHeight); IDock targetDock = dock switch { diff --git a/tests/Dock.Avalonia.HeadlessTests/FactoryTests.cs b/tests/Dock.Avalonia.HeadlessTests/FactoryTests.cs index ed28aca13..e57d169bf 100644 --- a/tests/Dock.Avalonia.HeadlessTests/FactoryTests.cs +++ b/tests/Dock.Avalonia.HeadlessTests/FactoryTests.cs @@ -11,6 +11,18 @@ namespace Dock.Avalonia.HeadlessTests; public class FactoryTests { + private sealed class RecordingFloatFactory : TestFactory + { + public double? SplitWidth { get; private set; } + public double? SplitHeight { get; private set; } + + public override void SplitToWindow(IDock dock, IDockable dockable, double x, double y, double width, double height, DockWindowOptions? options) + { + SplitWidth = width; + SplitHeight = height; + } + } + [AvaloniaFact] public void TestFactory_Ctor() { @@ -199,6 +211,54 @@ public void DockingState_Transitions_Cover_Docked_Pinned_Document_Floating_And_H Assert.Equal(DockingWindowState.Document | DockingWindowState.Floating, document.DockingState); } + [AvaloniaFact] + public void FloatDockable_UsesOwnerSize_WhenTrackedDocumentBoundsAreTiny() + { + var factory = new RecordingFloatFactory(); + var root = factory.CreateRootDock(); + root.VisibleDockables = factory.CreateList(); + root.HiddenDockables = factory.CreateList(); + root.Windows = factory.CreateList(); + + var documentDock = factory.CreateDocumentDock(); + documentDock.VisibleDockables = factory.CreateList(); + documentDock.SetVisibleBounds(0, 0, 900, 700); + factory.AddDockable(root, documentDock); + + var document = factory.CreateDocument(); + document.SetVisibleBounds(0, 0, 10, 10); + factory.AddDockable(documentDock, document); + + factory.FloatDockable(document); + + Assert.Equal(900, factory.SplitWidth); + Assert.Equal(700, factory.SplitHeight); + } + + [AvaloniaFact] + public void FloatDockable_PreservesSmallTrackedDocumentBoundsAboveTinyThreshold() + { + var factory = new RecordingFloatFactory(); + var root = factory.CreateRootDock(); + root.VisibleDockables = factory.CreateList(); + root.HiddenDockables = factory.CreateList(); + root.Windows = factory.CreateList(); + + var documentDock = factory.CreateDocumentDock(); + documentDock.VisibleDockables = factory.CreateList(); + documentDock.SetVisibleBounds(0, 0, 900, 700); + factory.AddDockable(root, documentDock); + + var document = factory.CreateDocument(); + document.SetVisibleBounds(0, 0, 24, 24); + factory.AddDockable(documentDock, document); + + factory.FloatDockable(document); + + Assert.Equal(24, factory.SplitWidth); + Assert.Equal(24, factory.SplitHeight); + } + [AvaloniaFact] public void DockingState_HiddenContainer_Propagates_To_Descendants()