Skip to content

Commit

Permalink
Prevent gaps-triggered shifting for FloatingLayoutEngine (#1069)
Browse files Browse the repository at this point in the history
There was special handling for the `ProxyFloatingLayoutEngine`, but `FloatingLayoutEngine` was not included. As a result, windows could shift while using the `FloatingLayoutEngine`. This PR fixes this.
  • Loading branch information
dalyIsaac authored Nov 3, 2024
1 parent 86eace7 commit fe8e309
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 6 deletions.
80 changes: 76 additions & 4 deletions src/Whim.Gaps.Tests/GapsLayoutEngineTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using FluentAssertions;
using NSubstitute;
using Whim.FloatingWindow;
using Whim.SliceLayout;
using Whim.TestUtils;
using Windows.Win32.Foundation;
using Windows.Win32.Graphics.Gdi;
Expand Down Expand Up @@ -99,11 +100,18 @@ public static TheoryData<GapsConfig, IWindow[], int, IWindowState[]> DoLayout_Da
}

[Theory]
[MemberData(nameof(DoLayout_Data))]
public void DoLayout(GapsConfig gapsConfig, IWindow[] windows, int scale, IWindowState[] expectedWindowStates)
[MemberAutoSubstituteData(nameof(DoLayout_Data))]
public void DoLayout(
GapsConfig gapsConfig,
IWindow[] windows,
int scale,
IWindowState[] expectedWindowStates,
ISliceLayoutPlugin sliceLayoutPlugin,
IContext ctx
)
{
// Given
ILayoutEngine innerLayoutEngine = new ColumnLayoutEngine(_identity);
ILayoutEngine innerLayoutEngine = SliceLayouts.CreateRowLayout(ctx, sliceLayoutPlugin, _identity);

foreach (IWindow w in windows)
{
Expand Down Expand Up @@ -405,7 +413,7 @@ IWindowState[] expectedWindowStates

[Theory, AutoSubstituteData<StoreCustomization>]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope")]
internal void DoLayout_WithFloatingLayoutEngine(IContext context, MutableRootSector root)
internal void DoLayout_WithProxyFloatingLayoutEngine(IContext context, MutableRootSector root)
{
// Input
GapsConfig gapsConfig = new();
Expand Down Expand Up @@ -506,6 +514,70 @@ internal void DoLayout_WithFloatingLayoutEngine(IContext context, MutableRootSec
);
}

[Theory, AutoSubstituteData<StoreCustomization>]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope")]
internal void DoLayout_WithFloatingLayoutEngine(IContext context, MutableRootSector root)
{
// Input
GapsConfig gapsConfig = new();

Rectangle<int> rect1 = new(10, 10, 20, 20);
Rectangle<int> rect2 = new(30, 30, 40, 40);

IMonitor monitor = StoreTestUtils.CreateMonitor((HMONITOR)1);
monitor.WorkingArea.Returns(new Rectangle<int>(0, 0, 100, 100));

IWindow window1 = StoreTestUtils.CreateWindow((HWND)1);
IWindow window2 = StoreTestUtils.CreateWindow((HWND)2);

Workspace workspace = StoreTestUtils.CreateWorkspace(context);
StoreTestUtils.PopulateThreeWayMap(context, root, monitor, workspace, window1);
StoreTestUtils.PopulateWindowWorkspaceMap(context, root, window2, workspace);

context.NativeManager.DwmGetWindowRectangle(window1.Handle).Returns(rect1);
context.NativeManager.DwmGetWindowRectangle(window2.Handle).Returns(rect2);

FloatingLayoutEngine floatingLayoutEngine = new(context, _identity);

// Given
GapsLayoutEngine gapsLayoutEngine = new(gapsConfig, floatingLayoutEngine);

// When
GapsLayoutEngine gaps1 = (GapsLayoutEngine)gapsLayoutEngine.AddWindow(window1);
GapsLayoutEngine gaps2 = (GapsLayoutEngine)gaps1.AddWindow(window2);

IWindowState[] outputWindowStates = gaps2.DoLayout(monitor.WorkingArea, monitor).ToArray();

// Then
Assert.Equal(2, outputWindowStates.Length);

Assert.Contains(
outputWindowStates,
ws =>
ws.Equals(
new WindowState()
{
Window = window1,
Rectangle = rect1,
WindowSize = WindowSize.Normal,
}
)
);

Assert.Contains(
outputWindowStates,
ws =>
ws.Equals(
new WindowState()
{
Window = window2,
Rectangle = rect2,
WindowSize = WindowSize.Normal,
}
)
);
}

[Theory, AutoSubstituteData]
public void Count(ILayoutEngine innerLayoutEngine)
{
Expand Down
1 change: 1 addition & 0 deletions src/Whim.Gaps.Tests/Whim.Gaps.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Whim.Gaps\Whim.Gaps.csproj" />
<ProjectReference Include="..\Whim.SliceLayout\Whim.SliceLayout.csproj" />
<ProjectReference Include="..\Whim.TestUtils\Whim.TestUtils.csproj" />
</ItemGroup>
</Project>
15 changes: 13 additions & 2 deletions src/Whim.Gaps/GapsLayoutEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,27 @@ private GapsLayoutEngine UpdateInner(ILayoutEngine newInnerLayoutEngine) =>
/// <inheritdoc />
public override IEnumerable<IWindowState> DoLayout(IRectangle<int> rectangle, IMonitor monitor)
{
// If the inner layout engine is a floating layout engine, then we don't apply gaps.
if (InnerLayoutEngine.GetLayoutEngine<FloatingLayoutEngine>() is not null)
{
foreach (IWindowState windowState in InnerLayoutEngine.DoLayout(rectangle, monitor))
{
yield return windowState;
}
yield break;
}

// If the inner layout engine is a proxy floating layout engine, then we apply gaps for the non-floating windows.
int nonProxiedCount = 0;
if (InnerLayoutEngine is ProxyFloatingLayoutEngine proxy)
if (InnerLayoutEngine.GetLayoutEngine<ProxyFloatingLayoutEngine>() is ProxyFloatingLayoutEngine proxy)
{
nonProxiedCount = proxy.FloatingWindowRects.Count + proxy.MinimizedWindowRects.Count;

// The InnerLayoutEngine will use the default rectangle for nonProxiedCount.
// The InnerLayoutEngine will use the proxied rectangle for the remaining windows.
// This is brittle and relies on the order of the windows in the ProxyFloatingLayoutEngine.

IEnumerable<IWindowState> windows = InnerLayoutEngine.DoLayout(rectangle, monitor);
IEnumerable<IWindowState> windows = proxy.DoLayout(rectangle, monitor);
using IEnumerator<IWindowState> enumerator = windows.GetEnumerator();

for (int i = 0; i < nonProxiedCount; i++)
Expand Down

0 comments on commit fe8e309

Please sign in to comment.