using System.ComponentModel; using System.Windows; namespace CursorLang.Services; /// /// Keeps the settings window between the screen and the tray. /// /// /// The application works in the background, and the settings window is a guest on /// the screen rather than the application itself. Both buttons in its title bar /// therefore mean the same thing — put the window away — and neither ends the /// application: that is what the tray menu is for. /// /// The window is hidden rather than closed. Closing it would take its handle with /// it, and everything hung on the window — the theme, the place it was left in, the /// computed layout — would have to be built anew on every show. /// public sealed class MainWindowPresenter { private readonly MainWindowPlacement _placement; private Window? _window; // The application is quitting: the window may close for real now private bool _isExiting; public MainWindowPresenter(MainWindowPlacement placement) => _placement = placement; /// Whether the window is on the screen right now. public bool IsShown => _window is { IsVisible: true }; /// Takes over the fate of the window: the close button and the minimise one. public void Attach(Window window) { _window = window; window.Closing += OnClosing; window.StateChanged += OnStateChanged; } /// /// Shows the window and brings it forward — from the tray as well as from a /// second launch of the application. /// public void Show() { if (_window is null) { return; } _window.Show(); // A window put away while minimised is restored first: only then does it // have bounds to be placed by if (_window.WindowState == WindowState.Minimized) { _window.WindowState = WindowState.Normal; } _placement.Apply(_window); _window.Activate(); } /// Puts the window away into the tray. public void Hide() { if (_window is null) { return; } _window.Hide(); // The state is straightened out after the window is out of sight: the next // show is to bring back a window, not an icon on the taskbar _window.WindowState = WindowState.Normal; } /// /// Lets the window close. Called on the way out through the tray menu — up to /// that moment closing means hiding. /// public void AllowClose() => _isExiting = true; /// /// Gives the window back its usual behaviour: the close button closes, and the /// minimise button minimises. /// /// /// The last resort for a session with no notification area in it. Without the /// icon there is no way back to a hidden window and no way out of the /// application — the window has to take both duties back. /// public void Detach() { if (_window is null) { return; } _window.Closing -= OnClosing; _window.StateChanged -= OnStateChanged; _window = null; } private void OnClosing(object? sender, CancelEventArgs e) { if (_isExiting) { return; } e.Cancel = true; Hide(); } private void OnStateChanged(object? sender, EventArgs e) { if (_window is { WindowState: WindowState.Minimized }) { Hide(); } } }