Skip to content

Commit

Permalink
Restore ProxyFloatingLayoutEngine functionality (#1058)
Browse files Browse the repository at this point in the history
The following `FloatingLayoutPlugin` functionality has been restored:

- Mark a window as floating
- Mark a window as docked
- Move non-floating window across monitors while the `FloatingLayoutPlugin` is enabled
- Move floating window across monitors while the `FloatingLayoutPlugin` is enabled
- Move formerly floating windows across monitors while the `FloatingLayoutPlugin` is enabled

During the course of the rewrite to facilitate the above features, windows now float across workspaces. This actually significantly simplified the implementation as it meant that side-effects within the `ProxyFloatingLayoutEngine` could be removed.

Additionally, the `GapsLayoutEngine` lost the ability to prevent shifting for floating windows. This has been fixed, and new edge cases have been handled by using multiple iterators.
  • Loading branch information
dalyIsaac authored Oct 27, 2024
1 parent bbe9e80 commit 6ccaa47
Show file tree
Hide file tree
Showing 28 changed files with 1,374 additions and 1,534 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public class BaseProxyFloatingLayoutEngineTests : ProxyLayoutEngineBaseTests
{
IContext context = Substitute.For<IContext>();
IMonitor monitor = Substitute.For<IMonitor>();
IInternalFloatingWindowPlugin plugin = Substitute.For<IInternalFloatingWindowPlugin>();
IFloatingWindowPlugin plugin = Substitute.For<IFloatingWindowPlugin>();
ILayoutEngine innerLayoutEngine = Substitute.For<ILayoutEngine>();

context
Expand Down
2 changes: 1 addition & 1 deletion src/Whim.FloatingWindow.Tests/FloatingLayoutEngineTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ public void MoveWindowEdgesInDirection(IContext context, IWindow window)
IRectangle<double> rect = new Rectangle<double>();

// When
ILayoutEngine newEngine = engine.MoveWindowToPoint(window, rect);
ILayoutEngine newEngine = engine.MoveWindowEdgesInDirection(Direction.Up, rect, window);

// Then
Assert.Equal(engine, newEngine);
Expand Down
122 changes: 122 additions & 0 deletions src/Whim.FloatingWindow.Tests/FloatingUtilsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
using System.Collections.Immutable;
using NSubstitute;
using Whim.TestUtils;
using Windows.Win32.Foundation;
using Xunit;

namespace Whim.FloatingWindow.Tests;

[System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope")]
public class FloatingUtilsTests
{
[Theory, AutoSubstituteData]
public void UpdateWindowRectangle_NoRectangle(IContext ctx)
{
// Given a window with no rectangle
IWindow window = StoreTestUtils.CreateWindow((HWND)1);
ctx.NativeManager.DwmGetWindowRectangle(window.Handle).Returns((IRectangle<int>?)null);

// When we update the rectangle
var result = FloatingUtils.UpdateWindowRectangle(
ctx,
ImmutableDictionary<IWindow, IRectangle<double>>.Empty,
window
);

// Then the result should be null
Assert.Null(result);
}

[Theory, AutoSubstituteData<StoreCustomization>]
public void UpdateWindowRectangle_NoMonitorForWindow(IContext ctx)
{
// Given a window with a rectangle, but no monitor
IWindow window = StoreTestUtils.CreateWindow((HWND)1);
ctx.NativeManager.DwmGetWindowRectangle(window.Handle).Returns(new Rectangle<int>());

// When we update the rectangle
var result = FloatingUtils.UpdateWindowRectangle(
ctx,
ImmutableDictionary<IWindow, IRectangle<double>>.Empty,
window
);

// Then the result should be null
Assert.Null(result);
}

[Theory, AutoSubstituteData<StoreCustomization>]
internal void UpdateWindowRectangle_NoOldRectangle(IContext ctx, MutableRootSector root)
{
// Given a window with a rectangle and a monitor, but no old rectangle
IWindow window = StoreTestUtils.CreateWindow((HWND)1);
IMonitor monitor = StoreTestUtils.CreateMonitor();
Workspace workspace = StoreTestUtils.CreateWorkspace(ctx);

StoreTestUtils.PopulateThreeWayMap(ctx, root, monitor, workspace, window);

ImmutableDictionary<IWindow, IRectangle<double>> dict = new Dictionary<IWindow, IRectangle<double>>()
{
{ StoreTestUtils.CreateWindow((HWND)123), new Rectangle<double>() },
}.ToImmutableDictionary();

// When we update the rectangle
var result = FloatingUtils.UpdateWindowRectangle(ctx, dict, window);

// Then the result should be the new dictionary
Assert.NotEqual(dict, result);
Assert.Equal(2, result!.Count);
Assert.Contains(window, result.Keys);
}

[Theory, AutoSubstituteData<StoreCustomization>]
internal void UpdateWindowRectangle_NoChange(IContext ctx, MutableRootSector root)
{
// Given a window with a rectangle and a monitor, but no change
IWindow window = StoreTestUtils.CreateWindow((HWND)1);
IMonitor monitor = StoreTestUtils.CreateMonitor();
Workspace workspace = StoreTestUtils.CreateWorkspace(ctx);

StoreTestUtils.PopulateThreeWayMap(ctx, root, monitor, workspace, window);

ImmutableDictionary<IWindow, IRectangle<double>> dict = new Dictionary<IWindow, IRectangle<double>>()
{
{ window, monitor.WorkingArea.NormalizeRectangle(new Rectangle<int>()) },
}.ToImmutableDictionary();

// When we update the rectangle
var result = FloatingUtils.UpdateWindowRectangle(ctx, dict, window);

// Then the result should be the same dictionary
Assert.Same(dict, result);
}

[Theory, AutoSubstituteData<StoreCustomization>]
internal void UpdateWindowRectangle_Change(IContext ctx, MutableRootSector root)
{
// Given a window with a rectangle and a monitor, and a change
IWindow window = StoreTestUtils.CreateWindow((HWND)1);
IMonitor monitor = StoreTestUtils.CreateMonitor();
monitor.WorkingArea.Returns(new Rectangle<int>(0, 0, 1920, 1080));

Workspace workspace = StoreTestUtils.CreateWorkspace(ctx);

StoreTestUtils.PopulateThreeWayMap(ctx, root, monitor, workspace, window);

ImmutableDictionary<IWindow, IRectangle<double>> dict = new Dictionary<IWindow, IRectangle<double>>()
{
{ window, monitor.WorkingArea.NormalizeRectangle(new Rectangle<int>(10, 10, 1920, 1080)) },
}.ToImmutableDictionary();

ctx.NativeManager.DwmGetWindowRectangle(window.Handle).Returns(new Rectangle<int>(0, 0, 192, 108));

// When we update the rectangle
var result = FloatingUtils.UpdateWindowRectangle(ctx, dict, window);

// Then the result should be the new dictionary
Assert.NotEqual(dict, result);
Assert.Single(result!);
Assert.Contains(window, result!.Keys);
Assert.Equal(new Rectangle<double>(0, 0, 0.1, 0.1), result[window]);
}
}
Loading

0 comments on commit 6ccaa47

Please sign in to comment.