fix(dock): centralize transactional tear-off logic and fix build break
This commit is contained in:
@@ -53,6 +53,50 @@ public partial class App : Application
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempts to tear off a tab into a new window.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sourceItems">The collection to remove the item from.</param>
|
||||||
|
/// <param name="tabItem">The item to tear off.</param>
|
||||||
|
/// <param name="onSuccess">Optional callback after successful tear-off.</param>
|
||||||
|
/// <returns>A result indicating success or failure.</returns>
|
||||||
|
internal static Result TryTearOffTab(System.Collections.IList sourceItems, object tabItem, Action? onSuccess = null)
|
||||||
|
{
|
||||||
|
int originalIndex = sourceItems.IndexOf(tabItem);
|
||||||
|
if (originalIndex == -1)
|
||||||
|
{
|
||||||
|
return Result.Failure("Item not found in source collection.");
|
||||||
|
}
|
||||||
|
|
||||||
|
object? originalSelection = null;
|
||||||
|
// Try to capture selection if the source is a DockPanelNode or similar
|
||||||
|
// For now, we'll just handle the collection mutation.
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
sourceItems.Remove(tabItem);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
CreateAndShowDockWindow(tabItem);
|
||||||
|
onSuccess?.Invoke();
|
||||||
|
return Result.Success();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// Rollback
|
||||||
|
sourceItems.Insert(originalIndex, tabItem);
|
||||||
|
Logger.LogError(ex);
|
||||||
|
return Result.Failure($"Failed to create tear-off window: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.LogError(ex);
|
||||||
|
return Result.Failure($"Failed to remove item from source: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates, registers, and shows a new DockWindow for a torn-off tab.
|
/// Creates, registers, and shows a new DockWindow for a torn-off tab.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using Ghost.Core;
|
||||||
using Ghost.Editor.Core.Controls.Internal.Docking;
|
using Ghost.Editor.Core.Controls.Internal.Docking;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
@@ -308,41 +309,27 @@ public sealed partial class DockLayout : Control
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int originalIndex = _sourceNode.Items.IndexOf(_draggedItem);
|
|
||||||
object? originalSelection = _sourceNode.SelectedItem;
|
object? originalSelection = _sourceNode.SelectedItem;
|
||||||
|
|
||||||
if (originalIndex == -1)
|
App.TryTearOffTab(_sourceNode.Items, _draggedItem, () =>
|
||||||
{
|
{
|
||||||
ClearDragOperationState();
|
try
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Remove from current tree
|
|
||||||
if (_sourceNode.Items.Remove(_draggedItem))
|
|
||||||
{
|
{
|
||||||
try
|
// Raise event to let the host handle window creation
|
||||||
{
|
TabTornOff.Invoke(this, new TabTornOffEventArgs(_draggedItem));
|
||||||
// Raise event to let the host handle window creation
|
|
||||||
TabTornOff.Invoke(this, new TabTornOffEventArgs(_draggedItem));
|
|
||||||
|
|
||||||
// Only cleanup if the tear-off was successful (didn't throw)
|
// Only cleanup if the tear-off was successful
|
||||||
DockMutationEngine.CleanupEmptyNodes(_sourceNode);
|
DockMutationEngine.CleanupEmptyNodes(_sourceNode);
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
// Rollback: Re-insert the item at original position if the tear-off handler fails
|
|
||||||
_sourceNode.Items.Insert(originalIndex, _draggedItem);
|
|
||||||
_sourceNode.SelectedItem = originalSelection;
|
|
||||||
Logger.LogError(ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
catch (Exception ex)
|
||||||
finally
|
{
|
||||||
{
|
// If the event handler fails, we still need to cleanup or restore selection
|
||||||
ClearDragOperationState();
|
_sourceNode.SelectedItem = originalSelection;
|
||||||
}
|
throw; // Re-throw to trigger rollback in TryTearOffTab
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ClearDragOperationState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,28 +43,18 @@ 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))
|
||||||
{
|
{
|
||||||
int originalIndex = sender.TabItems.IndexOf(args.Item);
|
|
||||||
object? originalSelection = sender.SelectedItem;
|
object? originalSelection = sender.SelectedItem;
|
||||||
|
|
||||||
try
|
App.TryTearOffTab(sender.TabItems, args.Item, () =>
|
||||||
{
|
{
|
||||||
sender.TabItems.Remove(args.Item);
|
// If we need to do anything else on success
|
||||||
|
});
|
||||||
|
|
||||||
try
|
// Note: If TryTearOffTab fails, it will have already re-inserted the item.
|
||||||
{
|
// We might want to restore selection if it was the selected item.
|
||||||
App.CreateAndShowDockWindow(args.Item);
|
if (sender.TabItems.Contains(args.Item) && sender.SelectedItem == null)
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
// Rollback: Re-insert the item at original position if the tear-off fails
|
|
||||||
sender.TabItems.Insert(originalIndex, args.Item);
|
|
||||||
sender.SelectedItem = originalSelection;
|
|
||||||
Logger.LogError(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
{
|
||||||
Logger.LogError(ex);
|
sender.SelectedItem = originalSelection;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user