fix resize on different screens try -1

This commit is contained in:
2026-09-03 09:40:58 +05:00
parent cd63644a83
commit ccd914d6fe
@@ -1,5 +1,6 @@
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media;
using CursorLang.Core.Interop;
using CursorLang.Settings.Interop;
@@ -23,6 +24,11 @@ public sealed class MainWindowPlacement
// what has to be remembered is only what the user chose
private bool _isPlacing;
// Captured once, before anything narrows MaxHeight to a particular monitor —
// otherwise a later, more generous monitor would stay capped at whatever a
// previous, shorter one left behind
private double? _designMaxHeight;
/// <summary>
/// Takes over the placement of the window: puts it in place by the first show
/// and follows where the user moves it.
@@ -31,6 +37,7 @@ public sealed class MainWindowPlacement
{
window.SourceInitialized += OnSourceInitialized;
window.LocationChanged += OnLocationChanged;
window.DpiChanged += OnDpiChanged;
}
/// <summary>
@@ -83,10 +90,88 @@ public sealed class MainWindowPlacement
}
window.SourceInitialized -= OnSourceInitialized;
_designMaxHeight = window.MaxHeight;
LimitHeightToOwnMonitor(window);
window.UpdateLayout();
Apply(window);
}
// A monitor is not necessarily final at creation time — Windows may place the new
// window on one monitor before Apply moves it to another, and the user is free to
// drag it to a third one later. Every one of those is a real DPI change, and this
// runs again for each: recomputing the cap from whichever monitor holds the window
// right now, rather than trusting a value worked out for a previous one.
private void OnDpiChanged(object sender, DpiChangedEventArgs e)
{
if (sender is not Window window)
{
return;
}
LimitHeightToOwnMonitor(window);
// Only the size is revisited here, not the position: the window is commonly
// being dragged across monitors right when its DPI changes, and moving it
// ourselves in the middle of that would fight the drag the user is already
// doing
ReapplySizeToContent(window);
}
// UpdateLayout alone settles the measure/arrange pass of the visual tree, but
// does not reliably make WPF redo its own step of resizing the native window to
// match SizeToContent when the change originates from a DPI event rather than an
// ordinary content change. Left alone, the window can end up either too tall — a
// blank strip below the real content, once MaxHeight has just pulled the content
// shorter — or stuck too short after being dragged back to a monitor with room to
// grow again. Turning SizeToContent off and back on forces that resizing step to
// run again from scratch.
private static void ReapplySizeToContent(Window window)
{
SizeToContent original = window.SizeToContent;
window.SizeToContent = SizeToContent.Manual;
window.SizeToContent = original;
window.UpdateLayout();
}
// MaxHeight in XAML is a constant tuned for an ordinary desktop monitor at 100%
// scaling. At a high scale factor the same number of device-independent pixels
// turns into more physical pixels than a short or heavily scaled monitor has, and
// SizeToContent then grows the window past the bottom of the screen instead of
// asking the ScrollViewer inside it to scroll — with nothing to grab, the excess
// is unreachable. Capping MaxHeight to what the window's own monitor really offers
// keeps the whole window on screen and lets the ScrollViewer take over.
private void LimitHeightToOwnMonitor(Window window)
{
if (_designMaxHeight is not { } designMaxHeight)
{
return;
}
IntPtr handle = new WindowInteropHelper(window).Handle;
if (handle == IntPtr.Zero
|| WindowPlacementNative.TryGetBounds(handle) is not { } bounds
|| WindowPlacementNative.TryGetWorkAreaNear(bounds) is not { } work
|| IsEmpty(work))
{
return;
}
double scale = VisualTreeHelper.GetDpi(window).DpiScaleY;
if (scale <= 0)
{
return;
}
// Leaves room for the title bar and a margin from the edges of the screen,
// in the same units as the work area height once the monitor's scale is
// divided out
const double reservedForChrome = 80;
double workAreaHeight = (work.Bottom - work.Top) / scale;
window.MaxHeight = Math.Min(designMaxHeight, Math.Max(200, workAreaHeight - reservedForChrome));
}
private void OnLocationChanged(object? sender, EventArgs e)
{
if (_isPlacing || sender is not Window { WindowState: WindowState.Normal } window)