/*
* 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.";
}
}
}