fix(dock): fix build breaks and finalize TabTearOffService

This commit is contained in:
2026-03-28 16:29:34 +09:00
parent 10bc76a654
commit 04a3b924ab
4 changed files with 17 additions and 138 deletions

View File

@@ -3,6 +3,7 @@ using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using Ghost.Core; using Ghost.Core;
using Ghost.Editor.Core.Controls.Internal.Docking; using Ghost.Editor.Core.Controls.Internal.Docking;
using Ghost.Editor.View.Windows;
using Microsoft.UI.Xaml; using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Data; using Microsoft.UI.Xaml.Data;
@@ -296,8 +297,6 @@ public sealed partial class DockLayout : Control
return tabView; return tabView;
} }
public event EventHandler<TabTornOffEventArgs>? TabTornOff;
private void TabView_TabDroppedOutside(Microsoft.UI.Xaml.Controls.TabView sender, Microsoft.UI.Xaml.Controls.TabViewTabDroppedOutsideEventArgs args) private void TabView_TabDroppedOutside(Microsoft.UI.Xaml.Controls.TabView sender, Microsoft.UI.Xaml.Controls.TabViewTabDroppedOutsideEventArgs args)
{ {
if (_sourceNode != null && _draggedItem != null) if (_sourceNode != null && _draggedItem != null)
@@ -310,136 +309,13 @@ public sealed partial class DockLayout : Control
} }
else else
{ {
Logger.LogWarning($"Tab tear-off failed: {result.Error}"); Logger.LogWarning($"Tab tear-off failed: {result.Message}");
} }
ClearDragOperationState(); ClearDragOperationState();
} }
} }
private object? _draggedItem;
private DockPanelNode? _sourceNode;
private DockPosition _currentDropPosition = DockPosition.None;
private void TabView_TabDragStarting(Microsoft.UI.Xaml.Controls.TabView sender, Microsoft.UI.Xaml.Controls.TabViewTabDragStartingEventArgs args)
{
_draggedItem = args.Item;
_sourceNode = sender.Tag as DockPanelNode;
args.Data.Properties.Add(DRAG_PROPERTY_DOCK_TAB, _draggedItem); // Identify our drag
}
private void TabView_DragOver(object sender, DragEventArgs e)
{
if (e.DataView.Properties.ContainsKey(DRAG_PROPERTY_DOCK_TAB) && sender is FrameworkElement targetElement)
{
e.AcceptedOperation = global::Windows.ApplicationModel.DataTransfer.DataPackageOperation.Move;
var position = e.GetPosition(targetElement);
var newPosition = DockMath.CalculateDockPosition(targetElement.ActualWidth, targetElement.ActualHeight, position.X, position.Y, DROP_EDGE_THRESHOLD);
if (newPosition != _currentDropPosition)
{
_currentDropPosition = newPosition;
UpdateDropOverlay(targetElement, _currentDropPosition);
}
}
}
private void TabView_DragLeave(object sender, DragEventArgs e)
{
ClearOverlayState();
}
private void ClearOverlayState()
{
if (_dropTargetOverlay != null)
{
_dropTargetOverlay.Visibility = Visibility.Collapsed;
}
_currentDropPosition = DockPosition.None;
}
private void ClearDragOperationState()
{
ClearOverlayState();
_draggedItem = null;
_sourceNode = null;
}
private void UpdateDropOverlay(FrameworkElement targetElement, DockPosition position)
{
if (_dropTargetOverlay == null) return;
if (position == DockPosition.None)
{
_dropTargetOverlay.Visibility = Visibility.Collapsed;
return;
}
var transform = targetElement.TransformToVisual(this);
var bounds = transform.TransformBounds(new global::Windows.Foundation.Rect(0, 0, targetElement.ActualWidth, targetElement.ActualHeight));
_dropTargetOverlay.Visibility = Visibility.Visible;
_dropTargetOverlay.Width = double.NaN;
_dropTargetOverlay.Height = double.NaN;
switch (position)
{
case DockPosition.Center:
_dropTargetOverlay.Margin = new Thickness(bounds.Left, bounds.Top, ActualWidth - bounds.Right, ActualHeight - bounds.Bottom);
break;
case DockPosition.Left:
_dropTargetOverlay.Margin = new Thickness(bounds.Left, bounds.Top, ActualWidth - (bounds.Left + bounds.Width / 2), ActualHeight - bounds.Bottom);
break;
case DockPosition.Right:
_dropTargetOverlay.Margin = new Thickness(bounds.Left + bounds.Width / 2, bounds.Top, ActualWidth - bounds.Right, ActualHeight - bounds.Bottom);
break;
case DockPosition.Top:
_dropTargetOverlay.Margin = new Thickness(bounds.Left, bounds.Top, ActualWidth - bounds.Right, ActualHeight - (bounds.Top + bounds.Height / 2));
break;
case DockPosition.Bottom:
_dropTargetOverlay.Margin = new Thickness(bounds.Left, bounds.Top + bounds.Height / 2, ActualWidth - bounds.Right, ActualHeight - bounds.Bottom);
break;
}
}
private void TabView_Drop(object sender, DragEventArgs e)
{
if (_dropTargetOverlay != null) _dropTargetOverlay.Visibility = Visibility.Collapsed;
if (_draggedItem == null || _sourceNode == null || !(sender is FrameworkElement targetElement) || !(targetElement.Tag is DockPanelNode targetNode))
{
ClearDragOperationState();
return;
}
if (_currentDropPosition == DockPosition.None)
{
ClearDragOperationState();
return;
}
if (_sourceNode == targetNode && _currentDropPosition == DockPosition.Center)
{
ClearDragOperationState();
return; // Reordering within same tab is handled natively by TabView
}
if (Root == null)
{
ClearDragOperationState();
return;
}
// 1. Execute mutation
if (DockMutationEngine.TryApplyDropMutation(Root, targetNode, _sourceNode, _draggedItem, _currentDropPosition))
{
DockMutationEngine.CleanupEmptyNodes(_sourceNode);
}
ClearDragOperationState();
}
protected override void OnApplyTemplate() protected override void OnApplyTemplate()
{ {
base.OnApplyTemplate(); base.OnApplyTemplate();

View File

@@ -17,11 +17,5 @@ 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

@@ -43,11 +43,11 @@ internal sealed partial class EngineEditorWindow : WindowEx
// For static tabs in EngineEditorWindow, we remove the item from TabItems // For static tabs in EngineEditorWindow, we remove the item from TabItems
if (sender.TabItems.Contains(args.Item)) if (sender.TabItems.Contains(args.Item))
{ {
var result = TabTearOffService.TryTearOffTab(sender.TabItems, args.Item, sender); var result = TabTearOffService.TryTearOffTab((System.Collections.IList)sender.TabItems, args.Item, sender);
if (!result.IsSuccess) if (!result.IsSuccess)
{ {
Logger.LogWarning($"Tab tear-off failed: {result.Error}"); Logger.LogWarning($"Tab tear-off failed: {result.Message}");
} }
} }
} }

View File

@@ -38,11 +38,20 @@ internal static class TabTearOffService
} }
catch (Exception ex) catch (Exception ex)
{ {
// Rollback collection Logger.LogError(ex);
// Rollback collection and selection
try
{
sourceItems.Insert(originalIndex, tabItem); sourceItems.Insert(originalIndex, tabItem);
RestoreSelection(selectionContainer, originalSelection); RestoreSelection(selectionContainer, originalSelection);
}
catch (Exception rollbackEx)
{
Logger.LogError(rollbackEx);
return Result.Failure($"Failed to create tear-off window and rollback failed: {ex.Message}. Rollback error: {rollbackEx.Message}");
}
Logger.LogError(ex);
return Result.Failure($"Failed to create tear-off window: {ex.Message}"); return Result.Failure($"Failed to create tear-off window: {ex.Message}");
} }
} }