Skip to content

Commit

Permalink
Add IWorkspaceManager.SwapActiveWorkspaceWithAdjacentMonitor (#661)
Browse files Browse the repository at this point in the history
Adds a method to swap the active workspace with the workspace in the adjacent monitor.

Co-authored-by: Isaac Daly <[email protected]>
  • Loading branch information
urob and dalyIsaac authored Dec 21, 2023
1 parent a9664f6 commit ca758ae
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 0 deletions.
93 changes: 93 additions & 0 deletions src/Whim.Tests/Workspace/WorkspaceManagerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ internal static void SetupMonitors(IContext ctx, IMonitor[] monitors, int active
if (monitors.Length > 0)
{
ctx.MonitorManager.ActiveMonitor.Returns(monitors[activeMonitorIndex]);

ctx.MonitorManager.GetPreviousMonitor(monitors[activeMonitorIndex])
.Returns(monitors[(activeMonitorIndex - 1).Mod(monitors.Length)]);
ctx.MonitorManager.GetNextMonitor(monitors[activeMonitorIndex]);
}
}

Expand Down Expand Up @@ -877,6 +881,95 @@ IWindow window
activatedWorkspace.Received(1).AddWindow(window);
Assert.Equal(workspaces[activatedWorkspaceIdx], workspaceManager.GetWorkspaceForWindow(window));
}
#endregion

#region SwapActiveWorkspaceWithAdjacentMonitor
[Theory]
[InlineAutoSubstituteData<WorkspaceManagerCustomization>(false)]
[InlineAutoSubstituteData<WorkspaceManagerCustomization>(true)]
internal void SwapActiveWorkspaceWithAdjacentMonitor_NoAdjacentMonitor(
bool reverse,
IContext ctx,
IInternalContext internalCtx
)
{
// Given there is only a single monitor and workspace...
IMonitor[] monitors = new[] { ctx.MonitorManager.ActiveMonitor };
SetupMonitors(ctx, monitors);
IWorkspace[] workspaces = CreateWorkspaces(1);
WorkspaceManagerTestWrapper workspaceManager = CreateSut(ctx, internalCtx, workspaces);

ActivateWorkspacesOnMonitors(workspaceManager, workspaces, monitors);

ClearWorkspaceReceivedCalls(workspaces);

// When SwapActiveWorkspaceWithAdjacentMonitor is called, then no event is raised
CustomAssert.DoesNotRaise<MonitorWorkspaceChangedEventArgs>(
h => workspaceManager.MonitorWorkspaceChanged += h,
h => workspaceManager.MonitorWorkspaceChanged -= h,
() => workspaceManager.SwapActiveWorkspaceWithAdjacentMonitor(reverse)
);
}

[Theory, AutoSubstituteData<WorkspaceManagerCustomization>]
internal void SwapActiveWorkspaceWithAdjacentMonitor_CouldNotFindWorkspace(
IContext ctx,
IInternalContext internalCtx,
IMonitor[] monitors
)
{
// Given a fake monitor is provided
IWorkspace[] workspaces = CreateWorkspaces(2);
WorkspaceManagerTestWrapper workspaceManager = CreateSut(ctx, internalCtx, workspaces);

ActivateWorkspacesOnMonitors(workspaceManager, workspaces, monitors);

ClearWorkspaceReceivedCalls(workspaces);

ctx.MonitorManager.GetNextMonitor(Arg.Any<IMonitor>()).Returns(Substitute.For<IMonitor>());

// When SwapActiveWorkspaceWithAdjacentMonitor is called, then no event is raised
CustomAssert.DoesNotRaise<MonitorWorkspaceChangedEventArgs>(
h => workspaceManager.MonitorWorkspaceChanged += h,
h => workspaceManager.MonitorWorkspaceChanged -= h,
() => workspaceManager.SwapActiveWorkspaceWithAdjacentMonitor(reverse: false)
);
}

[Theory, AutoSubstituteData<WorkspaceManagerCustomization>]
internal void SwapActiveWorkspaceWithAdjacentMonitor_Success(
IContext ctx,
IInternalContext internalCtx,
IMonitor[] monitors
)
{
// Given there are two workspaces and monitors
IWorkspace[] workspaces = CreateWorkspaces(2);
WorkspaceManagerTestWrapper workspaceManager = CreateSut(ctx, internalCtx, workspaces);

ActivateWorkspacesOnMonitors(workspaceManager, workspaces, monitors);

ClearWorkspaceReceivedCalls(workspaces);

ctx.MonitorManager.GetNextMonitor(Arg.Any<IMonitor>()).Returns(monitors[1]);

// When SwapActiveWorkspaceWithAdjacentMonitor is called, then an event is raised
var result = Assert.Raises<MonitorWorkspaceChangedEventArgs>(
h => workspaceManager.MonitorWorkspaceChanged += h,
h => workspaceManager.MonitorWorkspaceChanged -= h,
() => workspaceManager.SwapActiveWorkspaceWithAdjacentMonitor(reverse: false)
);

// Then the raised event will match the expected
Assert.Equal(monitors[0], result.Arguments.Monitor);
Assert.Equal(workspaces[1], result.Arguments.CurrentWorkspace);
Assert.Equal(workspaces[0], result.Arguments.PreviousWorkspace);

// The old workspace is deactivated, the new workspace is laid out, and the first window is
// focused.
workspaces[0].Received(1).DoLayout();
workspaces[1].Received(1).DoLayout();
}

#endregion

Expand Down
8 changes: 8 additions & 0 deletions src/Whim/Workspace/IWorkspaceManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,14 @@ public interface IWorkspaceManager : IEnumerable<IWorkspace>, IDisposable
/// </param>
void MoveWindowToAdjacentWorkspace(IWindow? window = null, bool reverse = false, bool skipActive = false);

/// <summary>
/// Swap the currently active workspace with the one active on the next (or previous) monitor.
/// </summary>
/// <param name="reverse">
/// When <see langword="true"/>, swaps workspace with the previous monitor, otherwise with the next. Defaults to <see langword="false" />.
/// </param>
void SwapActiveWorkspaceWithAdjacentMonitor(bool reverse = false);

/// <summary>
/// Retrieves the monitor for the active workspace.
/// </summary>
Expand Down
25 changes: 25 additions & 0 deletions src/Whim/Workspace/WorkspaceManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,31 @@ public void MoveWindowToAdjacentWorkspace(IWindow? window = null, bool reverse =
return null;
}

public void SwapActiveWorkspaceWithAdjacentMonitor(bool reverse = false)
{
// Get the next monitor.
IMonitor monitor = _context.MonitorManager.ActiveMonitor;
IMonitor nextMonitor = reverse
? _context.MonitorManager.GetPreviousMonitor(monitor)
: _context.MonitorManager.GetNextMonitor(monitor);

if (monitor.Equals(nextMonitor))
{
Logger.Error($"Monitor {monitor} is already the {(!reverse ? "next" : "previous")} monitor");
return;
}

// Get workspace on next monitor.
IWorkspace? nextWorkspace = GetWorkspaceForMonitor(nextMonitor);
if (nextWorkspace == null)
{
Logger.Error($"Monitor {nextMonitor} was not found to correspond to any workspace");
return;
}

Activate(nextWorkspace, monitor);
}

public IMonitor? GetMonitorForWorkspace(IWorkspace workspace)
{
Logger.Debug($"Getting monitor for active workspace {workspace}");
Expand Down

0 comments on commit ca758ae

Please sign in to comment.