diff --git a/LanAirApp/samples/LanMountainDesktop.SamplePlugin/LanMountainDesktop.SamplePlugin.csproj b/LanAirApp/samples/LanMountainDesktop.SamplePlugin/LanMountainDesktop.SamplePlugin.csproj index 3856220..f7cc095 100644 --- a/LanAirApp/samples/LanMountainDesktop.SamplePlugin/LanMountainDesktop.SamplePlugin.csproj +++ b/LanAirApp/samples/LanMountainDesktop.SamplePlugin/LanMountainDesktop.SamplePlugin.csproj @@ -9,9 +9,9 @@ bin\$(Configuration)\$(TargetFramework)\content\ false false - ..\..\..\LanMountainDesktop\bin\$(Configuration)\$(TargetFramework)\Extensions\Plugins\ - $(PluginPackageOutputDirectory)$(AssemblyName).laapp - ..\..\..\LanMountainDesktop\bin\$(Configuration)\$(TargetFramework)\Extensions\Plugins\SamplePlugin\ + $(MSBuildThisFileDirectory)artifacts\Packages\ + $(PluginPackageOutputDirectory)$(AssemblyName).$(Version).laapp + $(MSBuildThisFileDirectory)artifacts\Loose\ diff --git a/LanMountainDesktop/App.axaml.cs b/LanMountainDesktop/App.axaml.cs index 22e5397..e6d9094 100644 --- a/LanMountainDesktop/App.axaml.cs +++ b/LanMountainDesktop/App.axaml.cs @@ -1,32 +1,50 @@ +using System; +using System.Linq; using Avalonia; using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Data.Core; using Avalonia.Data.Core.Plugins; -using System; -using System.Linq; using Avalonia.Markup.Xaml; using Avalonia.Platform; using Avalonia.Threading; +using AvaloniaWebView; using LanMountainDesktop.ComponentSystem; +using LanMountainDesktop.PluginSdk; using LanMountainDesktop.Services; using LanMountainDesktop.ViewModels; using LanMountainDesktop.Views; -using AvaloniaWebView; -using LanMountainDesktop.PluginSdk; namespace LanMountainDesktop; public partial class App : Application { + private enum DesktopShellState + { + ForegroundDesktop = 0, + MinimizedToTaskbar = 1, + TrayOnly = 2 + } + + private enum ShutdownIntent + { + None = 0, + ExitRequested = 1, + RestartRequested = 2 + } + private readonly AppSettingsService _appSettingsService = new(); private readonly LocalizationService _localizationService = new(); private readonly IHostApplicationLifecycle _hostApplicationLifecycle = new HostApplicationLifecycleService(); private bool _exitCleanupCompleted; + private DesktopShellState _desktopShellState = DesktopShellState.ForegroundDesktop; + private ShutdownIntent _shutdownIntent; - private SettingsWindow? _traySettingsWindow; + private readonly IndependentSettingsModuleService _independentSettingsModuleService = new(); private TrayIcons? _trayIcons; private PluginRuntimeService? _pluginRuntimeService; + private MainWindow? _mainWindow; + private bool _mainWindowClosed; internal static SingleInstanceService? CurrentSingleInstanceService { get; set; } internal static IHostApplicationLifecycle? CurrentHostApplicationLifecycle => @@ -35,6 +53,11 @@ public partial class App : Application public PluginRuntimeService? PluginRuntimeService => _pluginRuntimeService; public IHostApplicationLifecycle HostApplicationLifecycle => _hostApplicationLifecycle; + internal void OpenIndependentSettingsModule(string source, string? pageTag = null) + { + _independentSettingsModuleService.ShowOrActivate(source, pageTag); + } + public override void Initialize() { AppLogger.Info("App", "Initializing application resources."); @@ -62,12 +85,8 @@ public partial class App : Application AppLogger.Info("App", "Desktop lifetime exit triggered."); PerformExitCleanup(); }; - desktop.MainWindow = new MainWindow - { - DataContext = new MainWindowViewModel(), - }; - AppLogger.Info("App", $"Main window created. LogFile={AppLogger.LogFilePath}"); - LogBrowserStartupDiagnostics(); + + CreateAndAssignMainWindow(desktop, "FrameworkInitialization"); CurrentSingleInstanceService?.StartActivationListener(ActivateMainWindow); } @@ -81,42 +100,14 @@ public partial class App : Application Reason: "User selected Exit App from the tray menu.")); } + private void OnTrayShowDesktopClick(object? sender, EventArgs e) + { + RestoreOrCreateMainWindow(showSingleInstanceNotice: false, source: "TrayMenu"); + } + private void OnTraySettingsClick(object? sender, EventArgs e) { - if (ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime) - { - return; - } - - Dispatcher.UIThread.Post(() => - { - try - { - if (_traySettingsWindow is { } existingWindow && existingWindow.IsVisible) - { - existingWindow.WindowState = Avalonia.Controls.WindowState.Normal; - existingWindow.Activate(); - return; - } - - var settingsWindow = new SettingsWindow(); - settingsWindow.Closed += (_, _) => - { - if (ReferenceEquals(_traySettingsWindow, settingsWindow)) - { - _traySettingsWindow = null; - } - }; - - _traySettingsWindow = settingsWindow; - settingsWindow.Show(); - settingsWindow.Activate(); - } - catch (Exception ex) - { - AppLogger.Warn("TraySettings", "Failed to open settings window.", ex); - } - }, DispatcherPriority.Normal); + OpenIndependentSettingsModule("TrayMenu"); } private void OnTrayRestartClick(object? sender, EventArgs e) @@ -209,19 +200,25 @@ public partial class App : Application { var menu = new NativeMenu(); - var settingsItem = new NativeMenuItem(L("tray.menu.settings", "设置")); + var showDesktopItem = new NativeMenuItem(L("tray.menu.show_desktop", "Open Desktop")); + showDesktopItem.Click += OnTrayShowDesktopClick; + menu.Items.Add(showDesktopItem); + + menu.Items.Add(new NativeMenuItemSeparator()); + + var settingsItem = new NativeMenuItem(L("tray.menu.settings", "Settings")); settingsItem.Click += OnTraySettingsClick; menu.Items.Add(settingsItem); menu.Items.Add(new NativeMenuItemSeparator()); - var restartItem = new NativeMenuItem(L("tray.menu.restart", "重启应用")); + var restartItem = new NativeMenuItem(L("tray.menu.restart", "Restart App")); restartItem.Click += OnTrayRestartClick; menu.Items.Add(restartItem); menu.Items.Add(new NativeMenuItemSeparator()); - var exitItem = new NativeMenuItem(L("tray.menu.exit", "退出应用")); + var exitItem = new NativeMenuItem(L("tray.menu.exit", "Exit App")); exitItem.Click += OnTrayExitClick; menu.Items.Add(exitItem); @@ -245,6 +242,11 @@ public partial class App : Application } private void ActivateMainWindow() + { + RestoreOrCreateMainWindow(showSingleInstanceNotice: true, source: "SingleInstance"); + } + + private void RestoreOrCreateMainWindow(bool showSingleInstanceNotice, string source) { Dispatcher.UIThread.Post(() => { @@ -253,13 +255,11 @@ public partial class App : Application return; } - if (desktop.MainWindow is not Window mainWindow) - { - return; - } - try { + var mainWindow = GetOrCreateMainWindow(desktop, source); + mainWindow.ShowInTaskbar = true; + if (!mainWindow.IsVisible) { mainWindow.Show(); @@ -278,18 +278,68 @@ public partial class App : Application mainWindow.Activate(); mainWindow.Topmost = true; mainWindow.Topmost = false; - if (mainWindow is MainWindow lanMountainMainWindow) + SetDesktopShellState(DesktopShellState.ForegroundDesktop, $"Restore:{source}"); + AppLogger.Info( + "DesktopShell", + $"Desktop restored. Source='{source}'; MainWindowClosed={_mainWindowClosed}; ShowSingleInstanceNotice={showSingleInstanceNotice}; WindowState='{mainWindow.WindowState}'."); + + if (showSingleInstanceNotice) { - lanMountainMainWindow.ShowSingleInstanceNotice(); + mainWindow.ShowSingleInstanceNotice(); } } catch (Exception ex) { - AppLogger.Warn("SingleInstance", "Failed to activate the existing main window.", ex); + AppLogger.Warn("DesktopShell", $"Failed to restore desktop shell. Source='{source}'.", ex); } }, DispatcherPriority.Send); } + internal void PrepareForShutdown(bool isRestart, string source) + { + void Mark() + { + _shutdownIntent = isRestart + ? ShutdownIntent.RestartRequested + : ShutdownIntent.ExitRequested; + AppLogger.Info( + "DesktopShell", + $"Shutdown intent marked. Intent='{_shutdownIntent}'; Source='{source}'; CurrentShellState='{_desktopShellState}'."); + } + + if (Dispatcher.UIThread.CheckAccess()) + { + Mark(); + return; + } + + Dispatcher.UIThread.InvokeAsync(Mark, DispatcherPriority.Send).GetAwaiter().GetResult(); + } + + internal void ResetShutdownIntent(string source) + { + void Reset() + { + if (_shutdownIntent == ShutdownIntent.None) + { + return; + } + + AppLogger.Warn( + "DesktopShell", + $"Shutdown intent cleared without process exit. PreviousIntent='{_shutdownIntent}'; Source='{source}'."); + _shutdownIntent = ShutdownIntent.None; + } + + if (Dispatcher.UIThread.CheckAccess()) + { + Reset(); + return; + } + + Dispatcher.UIThread.InvokeAsync(Reset, DispatcherPriority.Send).GetAwaiter().GetResult(); + } + private void OnAppSettingsSaved(string _) { Dispatcher.UIThread.Post(() => @@ -311,18 +361,7 @@ public partial class App : Application _exitCleanupCompleted = true; AppSettingsService.SettingsSaved -= OnAppSettingsSaved; - try - { - _traySettingsWindow?.Close(); - } - catch (Exception ex) - { - AppLogger.Warn("App", "Failed to close tray-opened settings window during shutdown.", ex); - } - finally - { - _traySettingsWindow = null; - } + _independentSettingsModuleService.CloseIfOpen(); try { @@ -342,6 +381,171 @@ public partial class App : Application DisposeTrayIcon(); } + private MainWindow CreateAndAssignMainWindow( + IClassicDesktopStyleApplicationLifetime desktop, + string reason) + { + var mainWindow = new MainWindow + { + DataContext = new MainWindowViewModel(), + ShowInTaskbar = true + }; + + AttachMainWindow(mainWindow); + desktop.MainWindow = mainWindow; + AppLogger.Info("App", $"Main window created. Reason='{reason}'. LogFile={AppLogger.LogFilePath}"); + LogBrowserStartupDiagnostics(); + SetDesktopShellState(DesktopShellState.ForegroundDesktop, $"MainWindowCreated:{reason}"); + return mainWindow; + } + + private MainWindow GetOrCreateMainWindow( + IClassicDesktopStyleApplicationLifetime desktop, + string reason) + { + if (_mainWindow is not null && !_mainWindowClosed) + { + return _mainWindow; + } + + if (desktop.MainWindow is MainWindow desktopMainWindow && !_mainWindowClosed) + { + AttachMainWindow(desktopMainWindow); + return desktopMainWindow; + } + + return CreateAndAssignMainWindow(desktop, reason); + } + + private void AttachMainWindow(MainWindow mainWindow) + { + if (ReferenceEquals(_mainWindow, mainWindow)) + { + _mainWindowClosed = false; + return; + } + + if (_mainWindow is not null) + { + _mainWindow.Closing -= OnMainWindowClosing; + _mainWindow.Closed -= OnMainWindowClosed; + _mainWindow.PropertyChanged -= OnMainWindowPropertyChanged; + } + + _mainWindow = mainWindow; + _mainWindowClosed = false; + mainWindow.Closing += OnMainWindowClosing; + mainWindow.Closed += OnMainWindowClosed; + mainWindow.PropertyChanged += OnMainWindowPropertyChanged; + } + + private void OnMainWindowClosing(object? sender, WindowClosingEventArgs e) + { + if (sender is not MainWindow mainWindow) + { + return; + } + + AppLogger.Info( + "DesktopShell", + $"Main window closing requested. Intent='{_shutdownIntent}'; ShellState='{_desktopShellState}'; WindowState='{mainWindow.WindowState}'; IsVisible={mainWindow.IsVisible}."); + + if (_shutdownIntent is ShutdownIntent.ExitRequested or ShutdownIntent.RestartRequested) + { + AppLogger.Info( + "DesktopShell", + $"Main window close allowed. Intent='{_shutdownIntent}'; ShellState='{_desktopShellState}'."); + return; + } + + e.Cancel = true; + HideMainWindowToTray(mainWindow, "MainWindowClosing"); + } + + private void OnMainWindowClosed(object? sender, EventArgs e) + { + if (sender is not MainWindow mainWindow) + { + return; + } + + mainWindow.Closing -= OnMainWindowClosing; + mainWindow.Closed -= OnMainWindowClosed; + mainWindow.PropertyChanged -= OnMainWindowPropertyChanged; + + if (ReferenceEquals(_mainWindow, mainWindow)) + { + _mainWindow = null; + } + + _mainWindowClosed = true; + AppLogger.Info( + "DesktopShell", + $"Main window closed. Intent='{_shutdownIntent}'; ShellState='{_desktopShellState}'."); + + if (_shutdownIntent == ShutdownIntent.None) + { + SetDesktopShellState(DesktopShellState.TrayOnly, "MainWindowClosedUnexpected"); + } + } + + private void OnMainWindowPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e) + { + if (sender is not MainWindow mainWindow) + { + return; + } + + if (e.Property != Window.WindowStateProperty) + { + return; + } + + if (_shutdownIntent != ShutdownIntent.None || !mainWindow.IsVisible) + { + return; + } + + if (mainWindow.WindowState == WindowState.Minimized) + { + SetDesktopShellState(DesktopShellState.MinimizedToTaskbar, "MainWindowMinimized"); + return; + } + + SetDesktopShellState(DesktopShellState.ForegroundDesktop, "MainWindowRestored"); + } + + private void HideMainWindowToTray(MainWindow mainWindow, string source) + { + try + { + mainWindow.ShowInTaskbar = false; + mainWindow.Hide(); + SetDesktopShellState(DesktopShellState.TrayOnly, source); + AppLogger.Info( + "DesktopShell", + $"Main window hidden to tray. Source='{source}'; WindowState='{mainWindow.WindowState}'."); + } + catch (Exception ex) + { + AppLogger.Warn("DesktopShell", $"Failed to hide main window to tray. Source='{source}'.", ex); + } + } + + private void SetDesktopShellState(DesktopShellState state, string source) + { + if (_desktopShellState == state) + { + return; + } + + var previous = _desktopShellState; + _desktopShellState = state; + AppLogger.Info( + "DesktopShell", + $"Shell state changed. Previous='{previous}'; Current='{state}'; Source='{source}'."); + } + private void LogBrowserStartupDiagnostics() { try diff --git a/LanMountainDesktop/Localization/en-US.json b/LanMountainDesktop/Localization/en-US.json index 7a60ca1..dfee34e 100644 --- a/LanMountainDesktop/Localization/en-US.json +++ b/LanMountainDesktop/Localization/en-US.json @@ -1,6 +1,7 @@ { "app.title": "LanMountainDesktop", "tray.tooltip": "LanMountainDesktop", + "tray.menu.show_desktop": "Open Desktop", "tray.menu.settings": "Settings", "tray.menu.restart": "Restart App", "tray.menu.exit": "Exit App", @@ -8,10 +9,10 @@ "tooltip.back_to_windows": "Back to Windows", "tooltip.open_settings": "Settings", "settings.title": "Settings", - "settings.shell.title": "Application Settings", - "settings.shell.subtitle": "LanMountainDesktop standalone preferences", + "settings.shell.title": "Settings", + "settings.shell.subtitle": "LanMountainDesktop independent settings module", "settings.shell.sidebar_hint": "Choose a category to adjust application behavior, desktop layout, and appearance.", - "settings.shell.footer_hint": "Tray-opened settings are managed in this standalone window.", + "settings.shell.footer_hint": "Tray-opened settings are managed in this independent settings module.", "settings.back_to_desktop": "Back to Desktop", "settings.nav_header": "Settings", "settings.nav.group_desktop": "Desktop", diff --git a/LanMountainDesktop/Localization/zh-CN.json b/LanMountainDesktop/Localization/zh-CN.json index 9ba6b7e..f33fbc7 100644 --- a/LanMountainDesktop/Localization/zh-CN.json +++ b/LanMountainDesktop/Localization/zh-CN.json @@ -1,6 +1,7 @@ { "app.title": "LanMountainDesktop", "tray.tooltip": "LanMountainDesktop", + "tray.menu.show_desktop": "打开桌面", "tray.menu.settings": "设置", "tray.menu.restart": "重启应用", "tray.menu.exit": "退出应用", @@ -8,10 +9,10 @@ "tooltip.back_to_windows": "回到Windows", "tooltip.open_settings": "设置", "settings.title": "设置", - "settings.shell.title": "应用设置", - "settings.shell.subtitle": "LanMountainDesktop 独立设置窗口", + "settings.shell.title": "设置", + "settings.shell.subtitle": "LanMountainDesktop 独立设置模块", "settings.shell.sidebar_hint": "选择一个分类以调整应用行为、桌面布局与外观。", - "settings.shell.footer_hint": "托盘菜单打开的设置会统一在这个独立窗口中管理。", + "settings.shell.footer_hint": "托盘菜单打开的设置会统一在这个独立设置模块中管理。", "settings.back_to_desktop": "返回桌面", "settings.nav_header": "设置选项", "settings.nav.group_desktop": "桌面", diff --git a/LanMountainDesktop/Services/HostApplicationLifecycleService.cs b/LanMountainDesktop/Services/HostApplicationLifecycleService.cs index ec6717e..5af6817 100644 --- a/LanMountainDesktop/Services/HostApplicationLifecycleService.cs +++ b/LanMountainDesktop/Services/HostApplicationLifecycleService.cs @@ -11,18 +11,21 @@ public sealed class HostApplicationLifecycleService : IHostApplicationLifecycle { public bool TryExit(HostApplicationLifecycleRequest? request = null) { + App? app = null; try { AppLogger.Info( "HostLifecycle", $"Exit requested. Source='{request?.Source ?? "Unknown"}'; Reason='{request?.Reason ?? string.Empty}'."); - if (Application.Current?.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop) + app = Application.Current as App; + if (app?.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop) { AppLogger.Warn("HostLifecycle", "Exit request ignored because desktop lifetime is unavailable."); return false; } + app.PrepareForShutdown(isRestart: false, request?.Source ?? "Unknown"); if (Dispatcher.UIThread.CheckAccess()) { desktop.Shutdown(); @@ -36,6 +39,7 @@ public sealed class HostApplicationLifecycleService : IHostApplicationLifecycle } catch (Exception ex) { + app?.ResetShutdownIntent(request?.Source ?? "Unknown"); AppLogger.Warn("HostLifecycle", "Failed to exit the application.", ex); return false; } @@ -43,6 +47,7 @@ public sealed class HostApplicationLifecycleService : IHostApplicationLifecycle public bool TryRestart(HostApplicationLifecycleRequest? request = null) { + App? app = null; try { var startInfo = AppRestartService.CreateRestartStartInfo(); @@ -55,6 +60,8 @@ public sealed class HostApplicationLifecycleService : IHostApplicationLifecycle } Process.Start(startInfo); + app = Application.Current as App; + app?.PrepareForShutdown(isRestart: true, request?.Source ?? "Unknown"); var exitRequest = request is null ? new HostApplicationLifecycleRequest(Reason: "Restart accepted.") : request with @@ -68,6 +75,7 @@ public sealed class HostApplicationLifecycleService : IHostApplicationLifecycle } catch (Exception ex) { + app?.ResetShutdownIntent(request?.Source ?? "Unknown"); AppLogger.Warn("HostLifecycle", "Failed to restart the application.", ex); return false; } diff --git a/LanMountainDesktop/Services/IndependentSettingsModuleService.cs b/LanMountainDesktop/Services/IndependentSettingsModuleService.cs new file mode 100644 index 0000000..99927a9 --- /dev/null +++ b/LanMountainDesktop/Services/IndependentSettingsModuleService.cs @@ -0,0 +1,88 @@ +using System; +using Avalonia.Threading; +using LanMountainDesktop.Views; + +namespace LanMountainDesktop.Services; + +internal sealed class IndependentSettingsModuleService +{ + private SettingsWindow? _window; + + public void ShowOrActivate(string source, string? pageTag = null) + { + AppLogger.Info("IndependentSettingsModule", $"OpenRequested; Source='{source}'; PageTag='{pageTag ?? ""}'."); + + void ShowCore() + { + try + { + if (_window is not { } window) + { + AppLogger.Info("IndependentSettingsModule", $"WindowConstructionStarted; Source='{source}'."); + window = new SettingsWindow(); + AppLogger.Info("IndependentSettingsModule", $"WindowConstructionCompleted; Source='{source}'."); + window.Closed += (_, _) => + { + if (ReferenceEquals(_window, window)) + { + _window = null; + } + + AppLogger.Info("IndependentSettingsModule", "WindowClosed."); + }; + _window = window; + } + + window.Open(pageTag); + AppLogger.Info( + "IndependentSettingsModule", + $"WindowActivated; Source='{source}'; ReusedExisting={ReferenceEquals(_window, window)}; WasVisible={window.IsVisible}; PageTag='{pageTag ?? ""}'."); + } + catch (Exception ex) + { + AppLogger.Warn("IndependentSettingsModule", $"Failed to open independent settings module window. Source='{source}'.", ex); + } + } + + if (Dispatcher.UIThread.CheckAccess()) + { + ShowCore(); + return; + } + + Dispatcher.UIThread.Post(ShowCore, DispatcherPriority.Normal); + } + + public void CloseIfOpen() + { + void CloseCore() + { + if (_window is null) + { + return; + } + + try + { + _window.PrepareForForceClose(); + _window.Close(); + } + catch (Exception ex) + { + AppLogger.Warn("IndependentSettingsModule", "Failed to close independent settings module window during shutdown.", ex); + } + finally + { + _window = null; + } + } + + if (Dispatcher.UIThread.CheckAccess()) + { + CloseCore(); + return; + } + + Dispatcher.UIThread.Post(CloseCore, DispatcherPriority.Send); + } +} diff --git a/LanMountainDesktop/Views/IndependentSettingsModuleWindowBase.cs b/LanMountainDesktop/Views/IndependentSettingsModuleWindowBase.cs new file mode 100644 index 0000000..af3955b --- /dev/null +++ b/LanMountainDesktop/Views/IndependentSettingsModuleWindowBase.cs @@ -0,0 +1,23 @@ +using System; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Media; +using FluentAvalonia.UI.Windowing; + +namespace LanMountainDesktop.Views; + +public class IndependentSettingsModuleWindowBase : AppWindow +{ + public IndependentSettingsModuleWindowBase() + { + TitleBar.ExtendsContentIntoTitleBar = true; + TitleBar.TitleBarHitTestType = TitleBarHitTestType.Complex; + TitleBar.Height = 48; + + if (OperatingSystem.IsWindows()) + { + TransparencyLevelHint = [WindowTransparencyLevel.Mica]; + Background = Brushes.Transparent; + } + } +} diff --git a/LanMountainDesktop/Views/IndependentSettingsPageCategory.cs b/LanMountainDesktop/Views/IndependentSettingsPageCategory.cs new file mode 100644 index 0000000..bfb126b --- /dev/null +++ b/LanMountainDesktop/Views/IndependentSettingsPageCategory.cs @@ -0,0 +1,9 @@ +namespace LanMountainDesktop.Views; + +internal enum IndependentSettingsPageCategory +{ + Internal = 0, + External = 1, + About = 2, + Debug = 3 +} diff --git a/LanMountainDesktop/Views/IndependentSettingsPageDefinition.cs b/LanMountainDesktop/Views/IndependentSettingsPageDefinition.cs new file mode 100644 index 0000000..c8401cd --- /dev/null +++ b/LanMountainDesktop/Views/IndependentSettingsPageDefinition.cs @@ -0,0 +1,12 @@ +using FluentIcons.Common; + +namespace LanMountainDesktop.Views; + +internal sealed record IndependentSettingsPageDefinition( + string Tag, + string Title, + string Description, + Symbol Icon, + IndependentSettingsPageCategory Category, + int SortOrder, + string? ToolTip = null); diff --git a/LanMountainDesktop/Views/MainWindow.ComponentSystem.cs b/LanMountainDesktop/Views/MainWindow.ComponentSystem.cs index 1253060..e7134d8 100644 --- a/LanMountainDesktop/Views/MainWindow.ComponentSystem.cs +++ b/LanMountainDesktop/Views/MainWindow.ComponentSystem.cs @@ -410,7 +410,10 @@ public partial class MainWindow _reopenSettingsAfterComponentLibraryClose = false; if (shouldReopenSettings) { - OpenSettingsPage(); + if (Application.Current is App app) + { + app.OpenIndependentSettingsModule("ComponentLibrary"); + } } }, FluttermotionToken.Slow); } diff --git a/LanMountainDesktop/Views/MainWindow.Settings.cs b/LanMountainDesktop/Views/MainWindow.Settings.cs index 3193565..103f7ad 100644 --- a/LanMountainDesktop/Views/MainWindow.Settings.cs +++ b/LanMountainDesktop/Views/MainWindow.Settings.cs @@ -80,11 +80,13 @@ public partial class MainWindow if (_isSettingsOpen) { - CloseSettingsPage(); - return; + CloseSettingsPage(immediate: true); } - OpenSettingsPage(); + if (Application.Current is App app) + { + app.OpenIndependentSettingsModule("MainWindow"); + } } private void OnCloseSettingsClick(object? sender, RoutedEventArgs e) diff --git a/LanMountainDesktop/Views/SettingsComponentCategorySummary.cs b/LanMountainDesktop/Views/SettingsComponentCategorySummary.cs new file mode 100644 index 0000000..aa1681f --- /dev/null +++ b/LanMountainDesktop/Views/SettingsComponentCategorySummary.cs @@ -0,0 +1,3 @@ +namespace LanMountainDesktop.Views; + +internal sealed record SettingsComponentCategorySummary(string Name, string CountText); diff --git a/LanMountainDesktop/Views/SettingsPages/AboutSettingsPage.axaml b/LanMountainDesktop/Views/SettingsPages/AboutSettingsPage.axaml index 2075e2d..fe9534b 100644 --- a/LanMountainDesktop/Views/SettingsPages/AboutSettingsPage.axaml +++ b/LanMountainDesktop/Views/SettingsPages/AboutSettingsPage.axaml @@ -1,4 +1,4 @@ - - + - - + + - + - + @@ -49,17 +49,17 @@ Text="Current actual backend" FontSize="12" FontWeight="SemiBold" - Foreground="{DynamicResource AdaptiveTextPrimaryBrush}" /> + Foreground="{DynamicResource TextFillColorPrimaryBrush}" /> + Foreground="{DynamicResource TextFillColorPrimaryBrush}" /> + Foreground="{DynamicResource TextFillColorSecondaryBrush}" /> + diff --git a/LanMountainDesktop/Views/SettingsPages/AppearanceSettingsPage.axaml b/LanMountainDesktop/Views/SettingsPages/AppearanceSettingsPage.axaml new file mode 100644 index 0000000..1501242 --- /dev/null +++ b/LanMountainDesktop/Views/SettingsPages/AppearanceSettingsPage.axaml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/LanMountainDesktop/Views/SettingsPages/AppearanceSettingsPage.axaml.cs b/LanMountainDesktop/Views/SettingsPages/AppearanceSettingsPage.axaml.cs new file mode 100644 index 0000000..cefb969 --- /dev/null +++ b/LanMountainDesktop/Views/SettingsPages/AppearanceSettingsPage.axaml.cs @@ -0,0 +1,11 @@ +using Avalonia.Controls; + +namespace LanMountainDesktop.Views.SettingsPages; + +public partial class AppearanceSettingsPage : UserControl +{ + public AppearanceSettingsPage() + { + InitializeComponent(); + } +} diff --git a/LanMountainDesktop/Views/SettingsPages/ColorSettingsPage.axaml b/LanMountainDesktop/Views/SettingsPages/ColorSettingsPage.axaml index 7cde1a8..c0835aa 100644 --- a/LanMountainDesktop/Views/SettingsPages/ColorSettingsPage.axaml +++ b/LanMountainDesktop/Views/SettingsPages/ColorSettingsPage.axaml @@ -1,4 +1,4 @@ - + + - + - + - - - - - - + + + + + + + + + + + - + + + + + + + + - - - - + + + + + Text="Use stable left navigation and a single right-side page host, following the ClassIsland settings rhythm." /> + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - + - - - - - - - - - - - - - - - - - - + + - - - - - - - + + - + + Text="Configure this part of LanMountainDesktop in the independent settings module." /> - + + + + + + + + + + + + + + + + + + + + + - - + + - +