Skip to content

Commit

Permalink
Add MoveWindowToMonitorIndexTransform (#975)
Browse files Browse the repository at this point in the history
Adds a transform to make it easier to move a window to a monitor with a specific index (#974).
  • Loading branch information
dalyIsaac authored Aug 14, 2024
1 parent 0487484 commit f497291
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using System.Diagnostics.CodeAnalysis;

namespace Whim.Tests;

[SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope")]
public class MoveWindowToMonitorIndexTransformTests
{
[Theory, AutoSubstituteData<StoreCustomization>]
internal void NoMonitorAtIndex(IContext ctx, MutableRootSector rootSector)
{
// Given there is no monitor at the index
IWindow window = CreateWindow((HWND)10);
IMonitor originalMonitor = CreateMonitor((HMONITOR)10);
IMonitor newMonitor = CreateMonitor((HMONITOR)11);

PopulateThreeWayMap(ctx, rootSector, originalMonitor, CreateWorkspace(ctx), window);
PopulateMonitorWorkspaceMap(ctx, rootSector, newMonitor, CreateWorkspace(ctx));

MoveWindowToMonitorIndexTransform sut = new(2, window.Handle);

// When
var result = ctx.Store.Dispatch(sut);

// Then
Assert.False(result.IsSuccessful);
}

[Theory, AutoSubstituteData<StoreCustomization>]
internal void Success(IContext ctx, MutableRootSector rootSector)
{
// Given there is a three way map
IWindow window = CreateWindow((HWND)10);

IMonitor originalMonitor = CreateMonitor((HMONITOR)10);
IMonitor newMonitor = CreateMonitor((HMONITOR)11);

Workspace originalWorkspace = CreateWorkspace(ctx);
Workspace newWorkspace = CreateWorkspace(ctx);

PopulateThreeWayMap(ctx, rootSector, originalMonitor, originalWorkspace, window);
PopulateMonitorWorkspaceMap(ctx, rootSector, newMonitor, newWorkspace);

MoveWindowToMonitorIndexTransform sut = new(1, window.Handle);

// When
var result = ctx.Store.Dispatch(sut);

// Then
Assert.True(result.IsSuccessful);
Assert.Equal(newWorkspace.Id, rootSector.MapSector.WindowWorkspaceMap[window.Handle]);
Assert.Equal(newWorkspace.Id, rootSector.MapSector.MonitorWorkspaceMap[newMonitor.Handle]);
}
}
35 changes: 35 additions & 0 deletions src/Whim.Tests/Store/MonitorSector/MonitorPickersTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ internal void PickLastWhimActiveMonitor(IContext ctx, MutableRootSector root)
Assert.Equal((HMONITOR)2, result.Handle);
}

#region PickAdjacentMonitor
[InlineAutoSubstituteData<StoreCustomization>(true)]
[InlineAutoSubstituteData<StoreCustomization>(false)]
[Theory]
Expand Down Expand Up @@ -132,6 +133,40 @@ MutableRootSector root
// Then
Assert.Equal(root.MonitorSector.Monitors[endIdx].Handle, result.Value.Handle);
}
#endregion

#region PickMonitorByIndex
[Theory]
[InlineAutoSubstituteData<StoreCustomization>(-1)]
[InlineAutoSubstituteData<StoreCustomization>(4)]
internal void PickMonitorByIndex_MonitorIndexOutOfRange(int index, IContext ctx, MutableRootSector root)
{
// Given
PopulateMonitors(root);

// When
Result<IMonitor> result = ctx.Store.Pick(Pickers.PickMonitorByIndex(index));

// Then
Assert.False(result.IsSuccessful);
}

[Theory]
[InlineAutoSubstituteData<StoreCustomization>(0)]
[InlineAutoSubstituteData<StoreCustomization>(3)]
internal void PickMonitorByIndex_Success(int index, IContext ctx, MutableRootSector root)
{
// Given
PopulateMonitors(root);

// When
Result<IMonitor> result = ctx.Store.Pick(Pickers.PickMonitorByIndex(index));

// Then
Assert.True(result.IsSuccessful);
Assert.Equal(root.MonitorSector.Monitors[index].Handle, result.Value.Handle);
}
#endregion
}

public class GetMonitorAtPointPickerTests
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
namespace Whim;

/// <summary>
/// Move the window with <paramref name="WindowHandle"/> to the monitor at <paramref name="MonitorIndex"/>.
/// </summary>
/// <param name="MonitorIndex">
/// The 0-based index of the monitor to move the window to.
/// </param>
/// <param name="WindowHandle">
/// The handle of the window to move. If not provided, this will default to the focused/active window.
/// </param>
public record MoveWindowToMonitorIndexTransform(int MonitorIndex, HWND WindowHandle = default) : Transform
{
internal override Result<Unit> Execute(IContext ctx, IInternalContext internalCtx, MutableRootSector rootSector)
{
// Get the monitor at index.
Result<IMonitor> monitorResult = ctx.Store.Pick(PickMonitorByIndex(MonitorIndex));
if (!monitorResult.TryGet(out IMonitor monitor))
{
return Result.FromException<Unit>(monitorResult.Error!);
}

return ctx.Store.Dispatch(new MoveWindowToMonitorTransform(monitor.Handle, WindowHandle));
}
}
7 changes: 6 additions & 1 deletion src/Whim/Store/MonitorSector/IMonitorSector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ namespace Whim;
public interface IMonitorSector
{
/// <summary>
/// All the monitors currently tracked by Whim.
/// All the monitors currently tracked by Whim. The monitors are ordered by their x-coordinate
/// and then by their y-coordinate.
///
/// Your primary monitor will have the top-left coordinate be (0, 0).
/// Accordingly, monitors to the left of the primary monitor will have negative x-coordinates,
/// and monitors above the primary monitor will have negative y-coordinates.
/// </summary>
ImmutableArray<IMonitor> Monitors { get; }

Expand Down
19 changes: 19 additions & 0 deletions src/Whim/Store/MonitorSector/MonitorPickers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,25 @@ public static PurePicker<Result<IMonitor>> PickAdjacentMonitor(
return Result.FromValue(monitors[(monitorIdx + delta).Mod(monitors.Length)]);
};

/// <summary>
/// Get the monitor at the given index.
/// </summary>
/// <param name="index">
/// The 0-based index of the monitor to get.
/// </param>
/// <returns></returns>
public static PurePicker<Result<IMonitor>> PickMonitorByIndex(int index) =>
(rootSector) =>
{
ImmutableArray<IMonitor> monitors = rootSector.MonitorSector.Monitors;
if (index < 0 || index >= monitors.Length)
{
return Result.FromException<IMonitor>(StoreExceptions.InvalidMonitorIndex(index));
}

return Result.FromValue(monitors[index]);
};

/// <summary>
/// Get the monitor at the point <paramref name="point"/>
/// </summary>
Expand Down
3 changes: 3 additions & 0 deletions src/Whim/Store/StoreExceptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,7 @@ public static Exception NoWorkspaceFoundForWindow(HWND windowHandle) =>
new WhimException($"No workspace found for window {windowHandle}.");

public static Exception NoValidWindow() => new WhimException("No valid window found.");

public static Exception InvalidMonitorIndex(int monitorIndex) =>
new WhimException($"No monitor found at index {monitorIndex}.");
}

0 comments on commit f497291

Please sign in to comment.