using Microsoft.UI; using Microsoft.UI.Windowing; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Media.Imaging; using System; using System.IO; using System.Reflection; using System.Runtime.InteropServices; using System.Runtime.InteropServices.WindowsRuntime; using Windows.Graphics; using Windows.UI; using WinRT.Interop; // To learn more about WinUI, the WinUI project structure, // and more about our project templates, see: http://aka.ms/winui-project-info. namespace AmiReel; /// /// The application window. This hosts a Frame that displays pages. Add your /// UI and logic to MainPage.xaml / MainPage.xaml.cs instead of here so you /// can use Page features such as navigation events and the Loaded lifecycle. /// public sealed partial class MainWindow : Window { private bool _exitConfirmed; public MainWindow() { InitializeComponent(); App.MainWindowInstance = this; ExtendsContentIntoTitleBar = true; SetTitleBar(AppTitleBar); SetWindowIcon(); SetDefaultSizeAndPosition(); LoadTitleBarIcon(); // Navigate the root frame to the main page on startup. RootFrame.Navigate(typeof(MainPage)); AppWindow.Closing += AppWindow_Closing; } /// /// Sets the window's taskbar/Alt+Tab icon. requires an /// actual .ico file path (an .exe path is silently ignored), so the embedded .ico is extracted /// once to a stable file under %LOCALAPPDATA% and that path is used instead. /// private void SetWindowIcon() { try { string iconPath = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "AmiReel", "app-icon.ico"); if (!File.Exists(iconPath)) { Directory.CreateDirectory(Path.GetDirectoryName(iconPath)!); Assembly assembly = Assembly.GetExecutingAssembly(); using Stream? resource = assembly.GetManifestResourceStream("AmiReel.Assets.AppIcon.ico"); if (resource is null) return; using FileStream file = File.Create(iconPath); resource.CopyTo(file); } AppWindow.SetIcon(iconPath); } catch { // Non-critical: the window simply keeps whatever default icon Windows assigned. } } /// /// Loads the title bar icon from an embedded resource instead of an "ms-appx:///Assets/..." /// URI. A single-file self-contained publish bundles Content/Assets items into the exe in a /// way the ms-appx resource pipeline can no longer resolve at runtime, leaving the title bar /// blank; a plain embedded resource stream works the same in every publish mode. /// private async void LoadTitleBarIcon() { try { Assembly assembly = Assembly.GetExecutingAssembly(); using Stream? stream = assembly.GetManifestResourceStream("AmiReel.Assets.TitleBarIcon.png"); if (stream is null) return; BitmapImage bitmap = new(); await bitmap.SetSourceAsync(stream.AsRandomAccessStream()); TitleBarIcon.Source = bitmap; } catch { // Non-critical: the title bar simply shows no icon if this fails. } } private void SetDefaultSizeAndPosition() { const int DefaultWidth = 1280; const int DefaultHeight = 860; const int MinWidth = 1100; const int MinHeight = 780; IntPtr hwnd = WindowNative.GetWindowHandle(this); double scale = GetDpiForWindow(hwnd) / 96.0; int width = (int)(DefaultWidth * scale); int height = (int)(DefaultHeight * scale); AppWindow.Resize(new SizeInt32(width, height)); if (AppWindow.Presenter is OverlappedPresenter presenter) { presenter.PreferredMinimumWidth = (int)(MinWidth * scale); presenter.PreferredMinimumHeight = (int)(MinHeight * scale); } DisplayArea? displayArea = DisplayArea.GetFromWindowId(Win32Interop.GetWindowIdFromWindow(hwnd), DisplayAreaFallback.Primary); if (displayArea is not null) { int centerX = displayArea.WorkArea.X + (displayArea.WorkArea.Width - width) / 2; int centerY = displayArea.WorkArea.Y + (displayArea.WorkArea.Height - height) / 2; AppWindow.Move(new PointInt32(centerX, centerY)); } } [DllImport("user32.dll")] private static extern int GetDpiForWindow(IntPtr hwnd); public void ApplyTheme(bool isDark) { if (Content is FrameworkElement root) root.RequestedTheme = isDark ? ElementTheme.Dark : ElementTheme.Light; AppWindowTitleBar titleBar = AppWindow.TitleBar; Color foreground = isDark ? Color.FromArgb(255, 0xF5, 0xF7, 0xFB) : Color.FromArgb(255, 0x12, 0x20, 0x33); Color hoverBackground = isDark ? Color.FromArgb(255, 0x2E, 0x44, 0x6C) : Color.FromArgb(255, 0xD7, 0xE4, 0xF4); titleBar.ButtonBackgroundColor = Colors.Transparent; titleBar.ButtonInactiveBackgroundColor = Colors.Transparent; titleBar.ButtonForegroundColor = foreground; titleBar.ButtonInactiveForegroundColor = foreground; titleBar.ButtonHoverBackgroundColor = hoverBackground; titleBar.ButtonHoverForegroundColor = foreground; titleBar.ButtonPressedBackgroundColor = hoverBackground; titleBar.ButtonPressedForegroundColor = foreground; } private async void AppWindow_Closing(AppWindow sender, AppWindowClosingEventArgs args) { if (_exitConfirmed) return; args.Cancel = true; FrameworkElement root = (FrameworkElement)Content; ContentDialog dialog = DialogHelper.CreateStyled(RootFrame.XamlRoot, root.RequestedTheme); dialog.Title = "Exit AmiReel?"; dialog.Content = "Are you sure you want to close the application?"; dialog.PrimaryButtonText = "Exit"; dialog.CloseButtonText = "Cancel"; dialog.DefaultButton = ContentDialogButton.Primary; ContentDialogResult result = await dialog.ShowAsync(); if (result != ContentDialogResult.Primary) return; _exitConfirmed = true; Close(); } }