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()