/* * Copyright (c) 2026 James Maes (KofTwentyTwo) * SPDX-License-Identifier: MIT */ using KofTwentyTwo.AppKit.Settings; using KofTwentyTwo.AppKit.Updates; using KofTwentyTwo.AppKit.WinUI; using Microsoft.UI.Xaml; namespace AppKit.Sample.WinUI; /// The sample's main window: each control exercises one AppKit service. public sealed partial class MainWindow : Window { private readonly SampleSettings _settings = SampleApp.SettingsStore.Load(); private readonly UpdateCoordinator _updates; /// Sizes and brands the window, wires the update coordinator, restores the saved settings, shows the splash, and schedules the quiet startup update check. public MainWindow() { InitializeComponent(); Title = SampleApp.Info.DisplayName; this.ResizeForDpi(900, 600); this.SetMinimumSize(640, 420); this.SetAppIcon(SampleApp.Info); _updates = new UpdateCoordinator( new VelopackUpdateService(SampleApp.Info.RepositoryUrl!), new ContentDialogPrompter(() => Content.XamlRoot), SampleApp.Info.DisplayName, App.Log); GreetingText.Text = _settings.Greeting; SplashToggle.IsChecked = _settings.ShowSplashScreen; (_settings.ThemeKind switch { AppTheme.Light => ThemeLight, AppTheme.Dark => ThemeDark, _ => ThemeSystem }).IsChecked = true; SplashOverlay.ShowOver(RootGrid, SampleApp.Info, _settings); Closed += (_, _) => LogWindow.CloseCurrent(); // a log-only process would linger otherwise if(_settings.CheckForUpdatesOnStartup) { // The constructor runs on the UI thread, so the check resumes there to show dialogs. _ = CheckForUpdatesAfterStartupAsync(); } } /// /// Waits for the splash to clear (dialogs need the window's XamlRoot), then runs the /// quiet startup update check, which only speaks up when an update exists. /// private async Task CheckForUpdatesAfterStartupAsync() { await Task.Delay(_settings.ShowSplashScreen ? _settings.SplashMilliseconds + 500 : 1000); await _updates.CheckQuietlyAsync(); } /// File, Exit. private void Exit_Click(object sender, RoutedEventArgs e) => Close(); /// View, theme: applies the chosen theme to the window and saves it. private void Theme_Click(object sender, RoutedEventArgs e) { _settings.Theme = (string)((FrameworkElement)sender).Tag; WinUIShell.ApplyTheme(RootGrid, _settings.ThemeKind); Save(); } /// View, Show splash at startup: saves the preference. private void Splash_Click(object sender, RoutedEventArgs e) { _settings.ShowSplashScreen = SplashToggle.IsChecked; Save(); } /// Help, Check for updates: runs the interactive update flow. private async void CheckForUpdates_Click(object sender, RoutedEventArgs e) => await _updates.CheckInteractivelyAsync(); /// Help, Activity log: opens the live log window, or brings the open one forward. private void ActivityLog_Click(object sender, RoutedEventArgs e) => LogWindow.ShowSingle(App.Log, SampleApp.Info); /// Help, About: shows the About dialog. private async void About_Click(object sender, RoutedEventArgs e) => await AboutDialog.ShowAsync(SampleApp.Info, Content.XamlRoot); /// Writes an INFO entry, to show logging. private void LogInfo_Click(object sender, RoutedEventArgs e) { App.Log.Info("The user pressed 'Write a log entry'."); StatusText.Text = "Wrote an INFO entry."; } /// Logs a handled exception with its stack trace, to show error logging. private void LogError_Click(object sender, RoutedEventArgs e) { try { throw new InvalidOperationException("A deliberately handled sample failure."); } catch(InvalidOperationException ex) { App.Log.Error("Sample operation failed.", ex); StatusText.Text = "Wrote an ERROR entry with its stack trace."; } } /// Throws on purpose, to show that the crash net logs the exception and the app keeps running. private void Throw_Click(object sender, RoutedEventArgs e) { StatusText.Text = "Threw; the crash net logged it and the app kept running."; throw new InvalidOperationException("Deliberately unhandled sample exception."); } /// Saves the settings and reports a failed save in the status line. private void Save() { if(!SampleApp.SettingsStore.Save(_settings)) { StatusText.Text = "Could not save settings."; } } }