fix(dock): decouple DockLayout from window creation and remove redundant state

This commit is contained in:
2026-03-28 17:08:24 +09:00
parent 65a335fc1a
commit cda3b292b5
5 changed files with 32 additions and 12 deletions

View File

@@ -297,18 +297,17 @@ public sealed partial class DockLayout : Control
return tabView; return tabView;
} }
private object? _draggedItem;
private DockPosition _currentDropPosition = DockPosition.None; private DockPosition _currentDropPosition = DockPosition.None;
private record DockDragPayload(object Item, DockPanelNode SourceNode); private record DockDragPayload(object Item, DockPanelNode SourceNode);
public event EventHandler<TabTornOffEventArgs>? TabTornOff;
private void TabView_TabDragStarting(Microsoft.UI.Xaml.Controls.TabView sender, Microsoft.UI.Xaml.Controls.TabViewTabDragStartingEventArgs args) private void TabView_TabDragStarting(Microsoft.UI.Xaml.Controls.TabView sender, Microsoft.UI.Xaml.Controls.TabViewTabDragStartingEventArgs args)
{ {
_draggedItem = args.Item; if (args.Item != null && sender.Tag is DockPanelNode sourceNode)
if (_draggedItem != null && sender.Tag is DockPanelNode sourceNode)
{ {
var payload = new DockDragPayload(_draggedItem, sourceNode); var payload = new DockDragPayload(args.Item, sourceNode);
args.Data.Properties.Add(DRAG_PROPERTY_DOCK_TAB, payload); // Identify our drag args.Data.Properties.Add(DRAG_PROPERTY_DOCK_TAB, payload); // Identify our drag
} }
} }
@@ -316,7 +315,7 @@ public sealed partial class DockLayout : Control
private void TabView_DragOver(object sender, DragEventArgs e) private void TabView_DragOver(object sender, DragEventArgs e)
{ {
if (e.DataView.Properties.TryGetValue(DRAG_PROPERTY_DOCK_TAB, out var payloadObj) && if (e.DataView.Properties.TryGetValue(DRAG_PROPERTY_DOCK_TAB, out var payloadObj) &&
payloadObj is DockDragPayload payload && payloadObj is DockDragPayload &&
sender is FrameworkElement targetElement) sender is FrameworkElement targetElement)
{ {
e.AcceptedOperation = global::Windows.ApplicationModel.DataTransfer.DataPackageOperation.Move; e.AcceptedOperation = global::Windows.ApplicationModel.DataTransfer.DataPackageOperation.Move;
@@ -349,7 +348,6 @@ public sealed partial class DockLayout : Control
private void ClearDragOperationState() private void ClearDragOperationState()
{ {
ClearOverlayState(); ClearOverlayState();
_draggedItem = null;
} }
private void UpdateDropOverlay(FrameworkElement targetElement, DockPosition position) private void UpdateDropOverlay(FrameworkElement targetElement, DockPosition position)
@@ -441,6 +439,9 @@ public sealed partial class DockLayout : Control
if (result.IsSuccess) if (result.IsSuccess)
{ {
// Raise event to let the host handle window creation
TabTornOff?.Invoke(this, new TabTornOffEventArgs(args.Item, sourceNode));
DockMutationEngine.CleanupEmptyNodes(sourceNode); DockMutationEngine.CleanupEmptyNodes(sourceNode);
} }
else else

View File

@@ -10,8 +10,14 @@ public sealed class TabTornOffEventArgs : EventArgs
/// </summary> /// </summary>
public object TabContent { get; } public object TabContent { get; }
public TabTornOffEventArgs(object tabContent) /// <summary>
/// Gets the source node the tab is being torn off from.
/// </summary>
public object SourceNode { get; }
public TabTornOffEventArgs(object tabContent, object sourceNode)
{ {
TabContent = tabContent; TabContent = tabContent;
SourceNode = sourceNode;
} }
} }

View File

@@ -17,5 +17,13 @@ internal sealed partial class DockWindow : WindowEx
rootGroup.AddChild(panel); rootGroup.AddChild(panel);
PART_DockLayout.Root = rootGroup; PART_DockLayout.Root = rootGroup;
PART_DockLayout.TabTornOff += OnTabTornOff;
}
private void OnTabTornOff(object? sender, TabTornOffEventArgs e)
{
App.CreateAndShowDockWindow(e.TabContent);
} }
} }
}

View File

@@ -45,7 +45,11 @@ internal sealed partial class EngineEditorWindow : WindowEx
{ {
var result = TabTearOffService.TryTearOffTab(list, args.Item, sender); var result = TabTearOffService.TryTearOffTab(list, args.Item, sender);
if (!result.IsSuccess) if (result.IsSuccess)
{
App.CreateAndShowDockWindow(args.Item);
}
else
{ {
Logger.LogWarning($"Tab tear-off failed: {result.Message}"); Logger.LogWarning($"Tab tear-off failed: {result.Message}");
} }

View File

@@ -33,7 +33,8 @@ internal static class TabTearOffService
try try
{ {
App.CreateAndShowDockWindow(tabItem); // We no longer create the window here to decouple the service from the app shell.
// The caller is responsible for window creation (e.g. via an event handler).
return Result.Success(); return Result.Success();
} }
catch (Exception ex) catch (Exception ex)
@@ -49,10 +50,10 @@ internal static class TabTearOffService
catch (Exception rollbackEx) catch (Exception rollbackEx)
{ {
Logger.LogError(rollbackEx); Logger.LogError(rollbackEx);
return Result.Failure($"Failed to create tear-off window and rollback failed: {ex.Message}. Rollback error: {rollbackEx.Message}"); return Result.Failure($"Failed to tear off tab and rollback failed: {ex.Message}. Rollback error: {rollbackEx.Message}");
} }
return Result.Failure($"Failed to create tear-off window: {ex.Message}"); return Result.Failure($"Failed to tear off tab: {ex.Message}");
} }
} }
catch (Exception ex) catch (Exception ex)