diff --git a/src/OddSnap/App.xaml b/src/OddSnap/App.xaml index 71e62bb4..ce66b318 100644 --- a/src/OddSnap/App.xaml +++ b/src/OddSnap/App.xaml @@ -85,7 +85,7 @@ - + diff --git a/src/OddSnap/UI/OddSnapWindowChrome.cs b/src/OddSnap/UI/OddSnapWindowChrome.cs index 342c4111..71261d04 100644 --- a/src/OddSnap/UI/OddSnapWindowChrome.cs +++ b/src/OddSnap/UI/OddSnapWindowChrome.cs @@ -7,20 +7,20 @@ namespace OddSnap.UI; public static class OddSnapWindowChrome { - private const double DefaultCornerRadius = 12; + private const double DefaultCornerRadius = 7; - public static void Apply(Window window) + public static void Apply(Window window, double cornerRadius = DefaultCornerRadius) { WindowChrome.SetWindowChrome(window, new WindowChrome { CaptionHeight = 0, - CornerRadius = new CornerRadius(DefaultCornerRadius), + CornerRadius = new CornerRadius(cornerRadius), GlassFrameThickness = new Thickness(0), ResizeBorderThickness = new Thickness(8), UseAeroCaptionButtons = false }); - ApplyRoundedCorners(window, DefaultCornerRadius); + ApplyRoundedCorners(window, cornerRadius); } public static void ApplyRoundedCorners(Window window, double radius) @@ -35,6 +35,10 @@ void ApplyCurrentRegion() Dwm.TrySetWindowCornerPreference(hwnd, Dwm.DWMWCP_ROUND); Dwm.TrySetImmersiveDarkMode(hwnd, Theme.IsDark); + + // On Windows 11 DWM rounds layered windows themselves, so clipping + // a region on top of that would leave a crescent wherever the two + // roundings disagree. Only Windows 10 needs the manual region. if (OperatingSystem.IsWindowsVersionAtLeast(10, 0, 22000)) return; @@ -43,8 +47,15 @@ void ApplyCurrentRegion() window.SourceInitialized += (_, _) => ApplyCurrentRegion(); window.SizeChanged += (_, _) => ApplyCurrentRegion(); + + // On Windows 10 the region must be recomputed when the UI scale + // changes, because the content rounding scales with it. + void OnUiScaleChanged(double _) => ApplyCurrentRegion(); + UiScale.Changed += OnUiScaleChanged; + window.Closed += (_, _) => { + UiScale.Changed -= OnUiScaleChanged; var hwnd = new WindowInteropHelper(window).Handle; if (hwnd != IntPtr.Zero) User32.SetWindowRgn(hwnd, IntPtr.Zero, true); @@ -60,10 +71,17 @@ private static void SetRoundedWindowRegion(Window window, IntPtr hwnd, double ra var transform = source?.CompositionTarget?.TransformToDevice ?? System.Windows.Media.Matrix.Identity; int width = Math.Max(1, (int)Math.Ceiling(window.ActualWidth * transform.M11)); int height = Math.Max(1, (int)Math.Ceiling(window.ActualHeight * transform.M22)); - int diameterX = Math.Max(1, (int)Math.Round(radius * 2 * transform.M11)); - int diameterY = Math.Max(1, (int)Math.Round(radius * 2 * transform.M22)); - var region = Gdi32.CreateRoundRectRgn(0, 0, width + 1, height + 1, diameterX, diameterY); + // The root element is scaled by UiScale via a LayoutTransform, so the + // visible corner radius is radius * uiScale in layout units. + var effectiveRadius = radius * UiScale.Current; + + // GDI takes the ellipse *diameter*; WPF's CornerRadius is the radius. + int diameterX = Math.Max(2, (int)Math.Round(effectiveRadius * 2 * transform.M11)); + int diameterY = Math.Max(2, (int)Math.Round(effectiveRadius * 2 * transform.M22)); + + // Use the exact device bounds so the region lines up with the content. + var region = Gdi32.CreateRoundRectRgn(0, 0, width, height, diameterX, diameterY); if (region == IntPtr.Zero) return; diff --git a/src/OddSnap/UI/SettingsWindow.xaml b/src/OddSnap/UI/SettingsWindow.xaml index 4c3e11e5..39820019 100644 --- a/src/OddSnap/UI/SettingsWindow.xaml +++ b/src/OddSnap/UI/SettingsWindow.xaml @@ -643,8 +643,13 @@ - - + @@ -4491,5 +4496,4 @@ - diff --git a/src/OddSnap/UI/UpscaleResultWindow.xaml b/src/OddSnap/UI/UpscaleResultWindow.xaml index e3038267..8ca85e1e 100644 --- a/src/OddSnap/UI/UpscaleResultWindow.xaml +++ b/src/OddSnap/UI/UpscaleResultWindow.xaml @@ -22,7 +22,7 @@ - +