Browse Source

Swap dockables in-place when active tab type changes

Close-then-add in the same dispatcher tick was leaving the previous
view rendered until a layout pass caught up — the user saw the
"Decompiling" spinner from the closed decompiler tab persist after
switching to a metadata node, and going back to an entity node from
metadata didn't bring the text view forward. Replacing the dockable
in the VisibleDockables list as a single mutation fires
INotifyCollectionChanged.Replace, which Dock translates into a clean
visual swap.

Assisted-by: Claude:claude-opus-4-7:Claude Code
pull/3755/head
Siegfried Pammer 2 months ago
parent
commit
da2c856d93
  1. 63
      ILSpy/Docking/DockWorkspace.cs

63
ILSpy/Docking/DockWorkspace.cs

@ -278,26 +278,41 @@ namespace ILSpy.Docking
} }
} }
// Drops <paramref name="prototype"/> into the active document slot. If the slot // Drops <paramref name="prototype"/> into the active document slot. Same concrete
// already holds a tab of the same concrete type, copies state into it; otherwise // type as what's already there → copy state in place. Different type → swap in the
// closes the current active and adds the prototype in its place. // VisibleDockables list (single mutation fires INotifyCollectionChanged.Replace,
// which Dock translates into a clean visual swap; close-then-add in the same tick
// leaves the old view rendered until the next layout pass).
void PutInActiveSlot(TabPageModel prototype) void PutInActiveSlot(TabPageModel prototype)
{ {
if (factory.Documents == null) if (factory.Documents is not { } docs)
return; return;
if (factory.Documents.ActiveDockable is TabPageModel active && active.GetType() == prototype.GetType()) if (docs.ActiveDockable is TabPageModel active && active.GetType() == prototype.GetType())
{ {
CopyTabState(prototype, active); CopyTabState(prototype, active);
return; return;
} }
var oldActive = factory.Documents.ActiveDockable as IDockable; var oldActive = docs.ActiveDockable;
factory.AddDockable(factory.Documents, prototype); if (oldActive != null && docs.VisibleDockables is { } list)
{
int idx = list.IndexOf(oldActive);
if (idx >= 0)
{
prototype.Owner = docs;
prototype.Factory = factory;
list[idx] = prototype;
docs.ActiveDockable = prototype;
factory.SetFocusedDockable(docs, prototype);
return;
}
}
// Fallback (no current active): just add.
factory.AddDockable(docs, prototype);
factory.SetActiveDockable(prototype); factory.SetActiveDockable(prototype);
factory.SetFocusedDockable(factory.Documents, prototype); factory.SetFocusedDockable(docs, prototype);
if (oldActive != null && !ReferenceEquals(oldActive, prototype))
factory.CloseDockable(oldActive);
} }
// Once consumed and later replaced, the factory's InitialDecompilerTab can't be // Once consumed and later replaced, the factory's InitialDecompilerTab can't be
@ -308,9 +323,9 @@ namespace ILSpy.Docking
DecompilerTabPageModel? AcquireActiveDecompilerTab() DecompilerTabPageModel? AcquireActiveDecompilerTab()
{ {
if (factory.Documents == null) if (factory.Documents is not { } docs)
return null; return null;
if (factory.Documents.ActiveDockable is DecompilerTabPageModel active) if (docs.ActiveDockable is DecompilerTabPageModel active)
return active; return active;
DecompilerTabPageModel fresh; DecompilerTabPageModel fresh;
@ -325,12 +340,26 @@ namespace ILSpy.Docking
fresh.NavigateRequested += OnNavigateRequested; fresh.NavigateRequested += OnNavigateRequested;
} }
var oldActive = factory.Documents.ActiveDockable as IDockable; // Replace the active dockable in place (see PutInActiveSlot for why an in-place
factory.AddDockable(factory.Documents, fresh); // list mutation beats close-then-add for the visual swap).
var oldActive = docs.ActiveDockable;
if (oldActive != null && docs.VisibleDockables is { } list)
{
int idx = list.IndexOf(oldActive);
if (idx >= 0)
{
fresh.Owner = docs;
fresh.Factory = factory;
list[idx] = fresh;
docs.ActiveDockable = fresh;
factory.SetFocusedDockable(docs, fresh);
return fresh;
}
}
factory.AddDockable(docs, fresh);
factory.SetActiveDockable(fresh); factory.SetActiveDockable(fresh);
factory.SetFocusedDockable(factory.Documents, fresh); factory.SetFocusedDockable(docs, fresh);
if (oldActive != null && !ReferenceEquals(oldActive, fresh))
factory.CloseDockable(oldActive);
return fresh; return fresh;
} }

Loading…
Cancel
Save