using System; using System.Linq; using Avalonia; using Avalonia.Controls; using OpenNest.Data; using OpenNest.Desktop; namespace OpenNest.Avalonia { /// /// Restores a window's size, position and maximized state from the shared user-settings /// file and saves them when it closes, under the same names the WinForms app uses. /// /// /// Sizes are stored in screen pixels. Avalonia sets the client area, while WinForms stores /// the outer window, so a size saved by one app opens a frame's width different in the /// other; each app's own round trip is exact. /// internal sealed class WindowPlacementBinding { private readonly Window window; private readonly string name; private readonly string settingsPath; private PixelPoint? normalPosition; private (int Width, int Height)? normalSize; private WindowPlacementBinding(Window window, string name, string settingsPath) { this.window = window; this.name = name; this.settingsPath = settingsPath; } /// Call before the window is shown. public static void Remember(Window window, string name, string settingsPath) { var binding = new WindowPlacementBinding(window, name, settingsPath); binding.Restore(); window.Opened += (_, _) => binding.Track(); window.PositionChanged += (_, _) => binding.Track(); window.PropertyChanged += (_, e) => { if (e.Property == TopLevel.ClientSizeProperty || e.Property == Window.WindowStateProperty) binding.Track(); }; window.Closing += (_, _) => binding.Save(); } private void Restore() { var saved = WindowPlacement.Read(UserSettingsFile.Read(settingsPath), name); var screens = window.Screens?.All; if (saved == null || screens == null) return; var areas = screens .Select(s => s.WorkingArea) .Select(a => new OpenNest.Desktop.PixelRect(a.X, a.Y, a.Width, a.Height)) .ToList(); var placement = saved.FitTo(areas); var screen = placement.Location is { } at ? screens.First(s => s.WorkingArea.Contains(new PixelPoint(at.X, at.Y))) : screens.FirstOrDefault(s => s.IsPrimary) ?? screens.FirstOrDefault(); if (placement.Location is { } location) { window.WindowStartupLocation = WindowStartupLocation.Manual; window.Position = new PixelPoint(location.X, location.Y); } if (placement.Size is { } size) { var scaling = screen?.Scaling ?? 1; window.Width = size.Width / scaling; window.Height = size.Height / scaling; } if (placement.Maximized) window.WindowState = WindowState.Maximized; } /// Remembers the normal bounds, which maximizing keeps. private void Track() { if (window.WindowState != WindowState.Normal || !window.IsVisible) return; normalPosition = window.Position; var scaling = window.DesktopScaling; normalSize = ((int)System.Math.Round(window.ClientSize.Width * scaling), (int)System.Math.Round(window.ClientSize.Height * scaling)); } private void Save() { Track(); var maximized = window.WindowState == WindowState.Maximized; UserSettingsFile.Update(settingsPath, values => { // A window never shown normal this session keeps the bounds saved before. var previous = WindowPlacement.Read(values, name); var placement = new WindowPlacement( normalPosition is { } p ? (p.X, p.Y) : previous?.Location, normalSize ?? previous?.Size, maximized); placement.WriteTo(values, name); }); } } }