Creator ruling: open in the upper-left of the screen with ~75px padding. The restore path was placing the window below the fold. RestoreWindowGeometry now ignores the saved position and calls the pure TopLeftPlacement(workArea, w, h, padding): work-area origin + LaunchPadding (75, in DIPs so it scales with DPI), clamped so the window is never pushed off-screen. A window that fits stays wholly visible; one too big to fit falls back to the work-area origin. Size and maximized state are still persisted -- only position was dropped. The size clamp is unchanged (saved values clamp to [MinWidth, work.Width], which is why a saved 800x500 returns as 1366x768 per the XAML minimums). The old clamp tolerated Left as low as work.Left - width + 120 and Top up to work.Bottom - 80, ranges wide enough to leave the window almost entirely off-screen while still looking like a successful clamp. A clamp that permits the window to be unreachable is worse than none: it reads as a guarantee it does not keep. BroadcastPullOutTests is renamed ..._Window_Launches_TopLeft_... and now asserts the constructor applies the derived placement for both an on-screen and a -99999 saved position. Placement math is covered by WindowPlacementTests as a pure function needing no HWND. Suite 395/395, 0 warnings.
54 lines
2.5 KiB
C#
54 lines
2.5 KiB
C#
using System.Windows;
|
|
using Xunit;
|
|
|
|
namespace ytLive.Tests;
|
|
|
|
/// <summary>
|
|
/// Launch placement (2026-10-04). The window used to restore a saved position clamped to
|
|
/// <c>[work.Left - width + 120, work.Right - 120]</c> horizontally and
|
|
/// <c>[work.Top, work.Bottom - 80]</c> vertically — ranges wide enough to leave most of the
|
|
/// window off-screen (the creator's "below the fold"). Launch position is now derived:
|
|
/// upper-left of the work area plus padding.
|
|
/// </summary>
|
|
public class WindowPlacementTests
|
|
{
|
|
/// <summary>Three properties in one guard, because the third is the regression:
|
|
/// (1) the requested inset, (2) a window that nearly fills the screen is pulled back
|
|
/// so it stays <i>wholly</i> visible — padding must never win over visibility, or a
|
|
/// large window re-creates "below the fold" — and (3) a window too big to fit falls
|
|
/// back to the work-area origin rather than hanging off an edge. Work area is
|
|
/// 1920x1040 at the origin (a taskbar-less secondary-style rect) to prove the maths
|
|
/// does not quietly assume (0,0).</summary>
|
|
[Theory]
|
|
[InlineData(1280, 800, 75, 75)] // padding wins when it fits
|
|
[InlineData(1900, 1000, 20, 40)] // does not fit -> pulled back on-screen
|
|
[InlineData(2400, 1200, 0, 0)] // larger than the work area -> origin
|
|
public void TopLeftPlacement_PadsButNeverHangsOffScreen(int width, int height, double expX, double expY )
|
|
{
|
|
var work = new Rect(0, 0, 1920, 1040);
|
|
|
|
var place = MainWindow.TopLeftPlacement(work, width, height, 75);
|
|
|
|
Assert.Equal(expX, place.X);
|
|
Assert.Equal(expY, place.Y);
|
|
|
|
// Whatever the size, the window must not start beyond the work area when it fits.
|
|
if (width <= work.Width) Assert.True(place.X >= work.Left, $"left {place.X} hangs off");
|
|
if (height <= work.Height) Assert.True(place.Y >= work.Top, $"top {place.Y} hangs off");
|
|
}
|
|
|
|
/// <summary>Padding is applied to a work area that does not start at the origin — the
|
|
/// secondary-monitor case. A hardcoded <c>Left = 75</c> would drop the window off the
|
|
/// left edge of a monitor whose work area begins at a negative X.</summary>
|
|
[Fact]
|
|
public void TopLeftPlacement_OffsetsFromTheWorkAreaOrigin_NotFromZero()
|
|
{
|
|
var secondary = new Rect(-1920, -200, 1920, 1040);
|
|
|
|
var place = MainWindow.TopLeftPlacement(secondary, 1280, 800, 75);
|
|
|
|
Assert.Equal(-1845, place.X);
|
|
Assert.Equal(-125, place.Y);
|
|
}
|
|
}
|