diff --git a/WinUIGallery/Helpers/SettingsHelper/SettingsHelper.cs b/WinUIGallery/Helpers/SettingsHelper/SettingsHelper.cs index abb8b8a40..c0f015bf6 100644 --- a/WinUIGallery/Helpers/SettingsHelper/SettingsHelper.cs +++ b/WinUIGallery/Helpers/SettingsHelper/SettingsHelper.cs @@ -65,4 +65,46 @@ public void UpdateRecentlyVisited(Action> updater) RecentlyVisited = list; _ = JumpListHelper.UpdateJumpListAsync(); } + + public int MainWindowPositionX + { + get => GetOrCreateDefault(0); + set => Set(value); + } + + public int MainWindowPositionY + { + get => GetOrCreateDefault(0); + set => Set(value); + } + + public int MainWindowWidth + { + get => GetOrCreateDefault(0); + set => Set(value); + } + + public int MainWindowHeight + { + get => GetOrCreateDefault(0); + set => Set(value); + } + + public bool IsMainWindowMaximized + { + get => GetOrCreateDefault(false); + set => Set(value); + } + + public double MainWindowScale + { + get => GetOrCreateDefault(1.0); + set => Set(value); + } + + public bool SaveWindowState + { + get => GetOrCreateDefault(false); + set => Set(value); + } } diff --git a/WinUIGallery/Helpers/WindowStateHelper.cs b/WinUIGallery/Helpers/WindowStateHelper.cs new file mode 100644 index 000000000..cf6cec978 --- /dev/null +++ b/WinUIGallery/Helpers/WindowStateHelper.cs @@ -0,0 +1,109 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Microsoft.UI.Windowing; +using Microsoft.UI.Xaml; +using System; +using Windows.Graphics; +using WinUIGallery.Models; + +namespace WinUIGallery.Helpers; + +public static class WindowStateHelper +{ + private static void SaveWindowState(WindowState state) + { + var s = SettingsHelper.Current; + + s.MainWindowPositionX = state.PositionX; + s.MainWindowPositionY = state.PositionY; + s.MainWindowWidth = state.Width; + s.MainWindowHeight = state.Height; + s.MainWindowScale = state.Scale; + s.IsMainWindowMaximized = state.IsMaximized; + } + + private static WindowState? LoadWindowState() + { + var s = SettingsHelper.Current; + + bool isInvalid = + s.MainWindowWidth <= 0 || + s.MainWindowHeight <= 0 || + ( + s.MainWindowPositionX == 0 && + s.MainWindowPositionY == 0 && + s.MainWindowWidth == 0 && + s.MainWindowHeight == 0 && + s.MainWindowScale == 1.0 && + !s.IsMainWindowMaximized + ); + + if (isInvalid) + return null; + + return new WindowState + { + PositionX = s.MainWindowPositionX, + PositionY = s.MainWindowPositionY, + Width = s.MainWindowWidth, + Height = s.MainWindowHeight, + Scale = s.MainWindowScale, + IsMaximized = s.IsMainWindowMaximized + }; + } + + public static void Save(Window window) + { + var appWindow = window.AppWindow; + + double scale = window.Content.XamlRoot.RasterizationScale; + bool isMaximized = appWindow.Presenter is OverlappedPresenter p && + p.State == OverlappedPresenterState.Maximized; + + // Restore before saving accurate dimensions + (appWindow.Presenter as OverlappedPresenter)?.Restore(); + + var state = new WindowState + { + PositionX = appWindow.Position.X, + PositionY = appWindow.Position.Y, + Width = appWindow.Size.Width, + Height = appWindow.Size.Height, + Scale = scale, + IsMaximized = isMaximized + }; + + SaveWindowState(state); + } + + public static void ApplySavedState(Window window) + { + if (window.Content is FrameworkElement content) + { + double currentScale = content.XamlRoot.RasterizationScale; + var state = LoadWindowState(); + if (state is null) return; + + // adjust for DPI/scaling change + double scaleFactor = currentScale / state.Scale; + int width = (int)(state.Width * scaleFactor); + int height = (int)(state.Height * scaleFactor); + int x = state.PositionX; + int y = state.PositionY; + + // ensure window fits display area + var displayArea = DisplayArea.GetFromPoint(new PointInt32(x,y), DisplayAreaFallback.Primary).WorkArea; + width = Math.Min(width, displayArea.Width); + height = Math.Min(height, displayArea.Height); + x = Math.Clamp(x, displayArea.X, displayArea.X + displayArea.Width - width); + y = Math.Clamp(y, displayArea.Y, displayArea.Y + displayArea.Height - height); + + window.AppWindow.Move(new PointInt32(x, y)); + window.AppWindow.Resize(new SizeInt32(width, height)); + + if (state.IsMaximized) + (window.AppWindow.Presenter as OverlappedPresenter)?.Maximize(); + } + } +} diff --git a/WinUIGallery/MainWindow.xaml.cs b/WinUIGallery/MainWindow.xaml.cs index 261b5323e..6a3e31839 100644 --- a/WinUIGallery/MainWindow.xaml.cs +++ b/WinUIGallery/MainWindow.xaml.cs @@ -53,6 +53,24 @@ public MainWindow() AdjustNavigationViewMargin(force: true); AppWindow.Changed += (_, _) => AdjustNavigationViewMargin(); } + + // Restore window state on load + if (SettingsHelper.Current.SaveWindowState) + { + if (Content is FrameworkElement content) + { + content.Loaded += (_, _) => WindowStateHelper.ApplySavedState(this); + } + } + + // Save window state on close + Closed += (_, _) => + { + if (SettingsHelper.Current.SaveWindowState) + { + WindowStateHelper.Save(this); + } + }; } // Adjusts the NavigationView margin based on the window state diff --git a/WinUIGallery/Models/WindowState.cs b/WinUIGallery/Models/WindowState.cs new file mode 100644 index 000000000..0b82f0679 --- /dev/null +++ b/WinUIGallery/Models/WindowState.cs @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace WinUIGallery.Models; + +public class WindowState +{ + public int PositionX { get; set; } + public int PositionY { get; set; } + public int Width { get; set; } + public int Height { get; set; } + public double Scale { get; set; } + public bool IsMaximized { get; set; } +} diff --git a/WinUIGallery/Pages/SettingsPage.xaml b/WinUIGallery/Pages/SettingsPage.xaml index b45d6a3e4..6c99d4f95 100644 --- a/WinUIGallery/Pages/SettingsPage.xaml +++ b/WinUIGallery/Pages/SettingsPage.xaml @@ -103,6 +103,15 @@ + + + + + + diff --git a/WinUIGallery/Pages/SettingsPage.xaml.cs b/WinUIGallery/Pages/SettingsPage.xaml.cs index 3b2011cfc..8829b0abb 100644 --- a/WinUIGallery/Pages/SettingsPage.xaml.cs +++ b/WinUIGallery/Pages/SettingsPage.xaml.cs @@ -28,7 +28,9 @@ public string Version } public string WinAppSdkRuntimeDetails => VersionHelper.WinAppSdkRuntimeDetails; - private int lastNavigationSelectionMode = 0; + private int lastNavigationSelectionMode = 0; + + private SettingsHelper Settings => SettingsHelper.Current; public SettingsPage() {