Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/Dock.Model/FactoryBase.Events.cs
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,12 @@ public virtual void OnWindowActivated(IDockWindow? window)
var rootDock = window?.Layout;
var dockable = rootDock is null ? null : ResolveTrackedDockable(rootDock);
UpdateGlobalDockTracking(dockable, rootDock, window, DockTrackingChangeReason.WindowActivated);

if (rootDock?.FocusedDockable is { Owner: IDock focusedOwner } focusedDockable)
{
SetFocusedDockable(focusedOwner, focusedDockable);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid replaying deactivation for every other root

When a window is activated while other focusable roots retain their normal FocusedDockable references, this new call enters SetFocusedDockable's cross-root loop and invokes OnDockableDeactivated for every other root without checking whether its owner is still active. In the standard HostWindow A→B transition, A's active dockable was already deactivated by HostWindow_Deactivated, so it receives a duplicate event; with three windows, unrelated already-inactive roots receive spurious events as well. Restore B's focus without replaying deactivation for roots whose owners are already inactive.

Useful? React with 👍 / 👎.

}

WindowActivated?.Invoke(this, new WindowActivatedEventArgs(window));
}

Expand Down Expand Up @@ -412,6 +418,11 @@ private bool IsCurrentGlobalTrackingRootStale()
/// <inheritdoc />
public virtual void OnWindowDeactivated(IDockWindow? window)
{
if (window?.Layout?.FocusedDockable?.Owner is IDock focusedOwner)
{
SetIsActive(focusedOwner, false);
Comment thread
wieslawsoltes marked this conversation as resolved.
}

if (window is not null && ReferenceEquals(CurrentDockWindow, window))
{
UpdateGlobalDockTracking(null, null, null, DockTrackingChangeReason.WindowDeactivated);
Expand Down
11 changes: 10 additions & 1 deletion src/Dock.Model/FactoryBase.Init.cs
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@ public virtual void SetFocusedDockable(IDock dock, IDockable? dockable)
}

var previousFocused = root.FocusedDockable;
var focusedOwnerWasActive = previousFocused?.Owner is IDock focusedOwner && focusedOwner.IsActive;

if (dockable is not null)
{
Expand All @@ -317,7 +318,8 @@ public virtual void SetFocusedDockable(IDock dock, IDockable? dockable)
}
}

if (root.FocusedDockable?.Owner is not null)
if (!ReferenceEquals(root.FocusedDockable, dockable)
&& root.FocusedDockable?.Owner is not null)
{
SetIsActive(root.FocusedDockable.Owner, false);
// Trigger deactivation event for the dockable that lost focus
Expand All @@ -341,6 +343,13 @@ public virtual void SetFocusedDockable(IDock dock, IDockable? dockable)
SetIsActive(root.FocusedDockable.Owner, true);
}

if (dockable is not null
&& ReferenceEquals(previousFocused, root.FocusedDockable)
&& !focusedOwnerWasActive)
Comment thread
wieslawsoltes marked this conversation as resolved.
{
OnFocusedDockableChanged(dockable);
}

if (previousFocused is not null && !ReferenceEquals(previousFocused, root.FocusedDockable))
{
SynchronizeDockingWindowState(previousFocused);
Expand Down
44 changes: 44 additions & 0 deletions tests/Dock.Model.Mvvm.UnitTests/GlobalDockTrackingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,50 @@ public void Initial_State_Is_Empty()
Assert.Null(factory.GlobalDockTrackingState.HostWindow);
}

[Fact]
public void WindowActivated_Raises_Focus_Events_When_Focus_Returns_To_Same_Dockables_Across_Roots()
{
var factory = new TrackingTestFactory();
var main = CreateContext(factory, "main");
var floating = CreateContext(factory, "floating");
factory.DockControls.Add(new TestDockControl { Factory = factory, Layout = main.Root });
factory.DockControls.Add(new TestDockControl { Factory = factory, Layout = floating.Root });
factory.InitLayout(main.Root);
factory.InitLayout(floating.Root);
factory.OnWindowActivated(main.Window);
factory.OnWindowActivated(floating.Window);

FocusedDockableChangedEventArgs? raised = null;
var raisedCount = 0;
factory.FocusedDockableChanged += (_, args) =>
{
raised = args;
raisedCount++;
};

factory.OnWindowActivated(main.Window);
factory.OnWindowActivated(main.Window);

Assert.Equal(1, raisedCount);
Assert.NotNull(raised);
Assert.Same(main.Dockable1, raised!.Dockable);
Assert.Same(main.Root, raised.RootDock);
Assert.Same(main.Window, raised.Window);
Assert.True(main.Dock.IsActive);
Assert.False(floating.Dock.IsActive);

factory.OnWindowDeactivated(main.Window);
factory.OnWindowActivated(floating.Window);
factory.OnWindowActivated(floating.Window);

Assert.Equal(2, raisedCount);
Assert.Same(floating.Dockable1, raised.Dockable);
Assert.Same(floating.Root, raised.RootDock);
Assert.Same(floating.Window, raised.Window);
Assert.False(main.Dock.IsActive);
Assert.True(floating.Dock.IsActive);
}

[Fact]
public void InitLayout_Initializes_Global_Tracking_From_Restored_Focused_Dockable()
{
Expand Down
Loading