diff --git a/.trae/specs/independent-settings-window/checklist.md b/.trae/specs/independent-settings-window/checklist.md new file mode 100644 index 0000000..5a202cd --- /dev/null +++ b/.trae/specs/independent-settings-window/checklist.md @@ -0,0 +1,6 @@ +- [x] 从桌面、托盘、IPC、组件库进入设置时,都会落到同一个设置窗口 +- [x] 设置已打开时再次触发设置入口,只会聚焦已有窗口,不会切换成关闭 +- [x] 设置窗口始终拥有独立任务栏图标,不受“桌面主窗口在任务栏显示图标”开关影响 +- [x] 点击“回到 Windows”后,只隐藏或最小化桌面主窗口,设置窗口保持可见 +- [x] 启用滑入滑出动画后,只有主窗口参与动画,设置窗口不参与 +- [x] 点击设置窗口关闭按钮后会真实关闭;再次打开时创建新的居中窗口 diff --git a/.trae/specs/independent-settings-window/spec.md b/.trae/specs/independent-settings-window/spec.md new file mode 100644 index 0000000..1c0c8d0 --- /dev/null +++ b/.trae/specs/independent-settings-window/spec.md @@ -0,0 +1,78 @@ +# 独立设置窗口 Spec + +## Why + +- 当前设置窗口仍然带有桌面壳的 owner / anchor 语义,点击“回到 Windows”或触发桌面动画时,容易被一起隐藏或重新定位。 +- 产品新增了“在任务栏显示图标”和“启用滑入滑出动画”设置,需要明确边界:它们只影响桌面主窗口,不影响设置窗口。 +- 桌面底栏、托盘菜单、IPC、组件库等入口应当始终打开同一个独立设置窗口,而不是切换成附属浮窗或开关行为。 + +## What Changes + +- 将设置窗口改为独立顶层窗口,始终使用自己的任务栏按钮和图标。 +- `SettingsWindowService.Open` 改为幂等的 open-or-focus;重复打开只聚焦已有窗口,并在提供目标页时切换到对应页面。 +- 移除 `Owner`、锚点定位和 `Toggle` 语义;首次打开按参考屏幕居中,关闭为真实关闭。 +- 桌面壳的“回到 Windows”、最小化到托盘/任务栏、滑入滑出动画,只影响 `MainWindow`,不会影响设置窗口。 +- 统一桌面、托盘、IPC、组件库等设置入口,全部走 `OpenIndependentSettingsModule`。 +- 设置页文案明确“在任务栏显示图标”只控制桌面主窗口;设置窗口始终保留独立任务栏图标。 + +## Impact + +- Affected code: + - `LanMountainDesktop/Services/Settings/SettingsWindowService.cs` + - `LanMountainDesktop/App.axaml.cs` + - `LanMountainDesktop/Views/MainWindow.axaml.cs` + - `LanMountainDesktop/Views/MainWindow.ComponentSystem.cs` + - `LanMountainDesktop/Views/FusedDesktopComponentLibraryControl.axaml.cs` + - `LanMountainDesktop/Views/SettingsPages/GeneralSettingsPage.axaml` +- Affected behavior: + - 设置窗口生命周期 + - 设置入口一致性 + - 任务栏图标与桌面壳显示边界 + +--- + +## ADDED Requirements + +### Requirement: 设置窗口为独立顶层窗口 + +系统 SHALL 将设置窗口作为独立顶层窗口显示,而不是作为桌面主窗口的附属子窗。 + +#### Scenario: 设置窗口拥有独立任务栏图标 +- **WHEN** 用户打开设置窗口 +- **THEN** 设置窗口使用独立顶层窗口方式显示 +- **AND THEN** 设置窗口在任务栏中保留自己的独立按钮和图标 +- **AND THEN** “在任务栏显示图标”开关不会影响设置窗口的任务栏按钮 + +### Requirement: 设置入口统一为 open-or-focus + +系统 SHALL 让所有设置入口打开或聚焦同一个设置窗口实例。 + +#### Scenario: 已打开时重复触发设置入口 +- **WHEN** 设置窗口已经打开,用户再次从桌面、托盘或 IPC 触发打开设置 +- **THEN** 系统只聚焦现有设置窗口 +- **AND THEN** 如果请求包含目标页,则导航到目标页 +- **AND THEN** 不会把已打开的设置窗口当作开关关闭 + +### Requirement: 设置窗口不参与桌面壳可见性切换 + +系统 SHALL 让桌面壳的隐藏、最小化和进出场动画只作用于主窗口。 + +#### Scenario: 回到 Windows 时设置窗口保持可见 +- **WHEN** 主窗口执行“回到 Windows”并隐藏到托盘或最小化到任务栏 +- **THEN** 设置窗口保持当前可见状态 +- **AND THEN** 设置窗口不会跟随主窗口一起隐藏、最小化或重定位 + +#### Scenario: 桌面滑入滑出动画不作用于设置窗口 +- **WHEN** 启用了滑入滑出动画并触发主窗口退场或入场 +- **THEN** 只有主窗口参与动画 +- **AND THEN** 设置窗口不会消失,也不会跟随主窗口做进出场动画 + +### Requirement: 关闭设置窗口时真实销毁实例 + +系统 SHALL 在用户关闭设置窗口时真实关闭该窗口实例。 + +#### Scenario: 关闭后再次打开 +- **WHEN** 用户点击设置窗口右上角关闭按钮 +- **THEN** 当前设置窗口实例被关闭并销毁 +- **AND THEN** 下次再次打开设置时创建新的设置窗口实例 +- **AND THEN** 新窗口按参考屏幕居中显示 diff --git a/.trae/specs/independent-settings-window/tasks.md b/.trae/specs/independent-settings-window/tasks.md new file mode 100644 index 0000000..70034ed --- /dev/null +++ b/.trae/specs/independent-settings-window/tasks.md @@ -0,0 +1,25 @@ +# Tasks + +- [x] Task 1: 简化设置窗口打开契约 + - [x] 将 `SettingsWindowOpenRequest` 从 owner / anchor 语义改为目标页 + 参考屏幕语义 + - [x] 移除 `ISettingsWindowService.Toggle` + +- [x] Task 2: 重做设置窗口服务行为 + - [x] 设置窗口始终使用 `Show()` 打开 + - [x] 设置窗口始终 `ShowInTaskbar = true` + - [x] 已打开时只聚焦并在需要时切页 + - [x] 关闭后销毁实例,下次打开重新创建并居中 + +- [x] Task 3: 统一设置入口并解耦桌面壳 + - [x] 桌面底栏设置按钮改为 open-or-focus + - [x] 组件库入口改为复用 `OpenIndependentSettingsModule` + - [x] 移除 `MainWindow` 上的设置窗口锚点逻辑 + +- [x] Task 4: 明确产品边界 + - [x] 调整“在任务栏显示图标”文案,限定为桌面主窗口 + - [x] 新增独立设置窗口 feature spec + - [x] 在窗口过渡动画 spec 中补充“设置窗口不参与动画” + +- [x] Task 5: 验证 + - [x] 运行 `dotnet build LanMountainDesktop.slnx -c Debug` + - [x] 运行与新 helper 相关的测试 diff --git a/.trae/specs/window-slide-transition/spec.md b/.trae/specs/window-slide-transition/spec.md index f7b8ea3..0150a28 100644 --- a/.trae/specs/window-slide-transition/spec.md +++ b/.trae/specs/window-slide-transition/spec.md @@ -113,6 +113,15 @@ - **AND THEN** 过渡时长使用 `FluttermotionToken.Duration.Page`(320ms)和 `FluttermotionToken.Duration.Intro`(400ms) - **AND THEN** 缓动函数使用 `0.05,0.75,0.10,1.00`(DecelerateBezier) +### Requirement: 设置窗口不参与桌面壳过渡动画 + +系统 SHALL 将桌面壳进出场动画限制在主窗口范围内,不影响独立设置窗口。 + +#### Scenario: 设置窗口在桌面动画期间保持独立 +- **WHEN** 主窗口执行滑入、滑出、最小化或恢复动画 +- **THEN** 设置窗口不参与该动画 +- **AND THEN** 设置窗口不会跟随主窗口一起隐藏、最小化或重定位 + ## MODIFIED Requirements ### Requirement: OnMinimizeClick 行为 diff --git a/LanMountainDesktop.Tests/SettingsWindowPlacementHelperTests.cs b/LanMountainDesktop.Tests/SettingsWindowPlacementHelperTests.cs new file mode 100644 index 0000000..85a78c0 --- /dev/null +++ b/LanMountainDesktop.Tests/SettingsWindowPlacementHelperTests.cs @@ -0,0 +1,50 @@ +using Avalonia; +using LanMountainDesktop.Services.Settings; +using Xunit; + +namespace LanMountainDesktop.Tests; + +public sealed class SettingsWindowPlacementHelperTests +{ + [Fact] + public void ResolveWorkingArea_PrefersReferenceScreen() + { + var referenceArea = new PixelRect(1920, 0, 2560, 1440); + var primaryArea = new PixelRect(0, 0, 1920, 1080); + + var result = SettingsWindowPlacementHelper.ResolveWorkingArea( + referenceArea, + primaryArea, + fallbackWindowWidth: 1120, + fallbackWindowHeight: 760); + + Assert.Equal(referenceArea, result); + } + + [Fact] + public void ResolveWorkingArea_FallsBackToPrimaryScreenWhenReferenceIsMissing() + { + var primaryArea = new PixelRect(0, 0, 1920, 1080); + + var result = SettingsWindowPlacementHelper.ResolveWorkingArea( + referenceWorkingArea: null, + primaryWorkingArea: primaryArea, + fallbackWindowWidth: 1120, + fallbackWindowHeight: 760); + + Assert.Equal(primaryArea, result); + } + + [Fact] + public void CalculateCenteredPosition_ReturnsCenteredPointInsideWorkingArea() + { + var workingArea = new PixelRect(1920, 40, 2560, 1400); + + var result = SettingsWindowPlacementHelper.CalculateCenteredPosition( + workingArea, + windowWidth: 1120, + windowHeight: 760); + + Assert.Equal(new PixelPoint(2640, 360), result); + } +} diff --git a/LanMountainDesktop/App.axaml.cs b/LanMountainDesktop/App.axaml.cs index f57d214..9918373 100644 --- a/LanMountainDesktop/App.axaml.cs +++ b/LanMountainDesktop/App.axaml.cs @@ -117,8 +117,8 @@ public partial class App : Application $"Opening settings window. Source='{source}'; PageTag='{pageTag ?? ""}'."); _settingsWindowService?.Open(new SettingsWindowOpenRequest( Source: source, - Owner: _mainWindow is { IsVisible: true } ? _mainWindow : null, - PageId: pageTag)); + PageId: pageTag, + ScreenReferenceWindow: _mainWindow is { IsVisible: true } ? _mainWindow : null)); } public App() @@ -738,7 +738,7 @@ public partial class App : Application var mainWindow = GetOrCreateMainWindow(desktop, source); mainWindow.PrepareEnterAnimation(); - mainWindow.ShowInTaskbar = true; + mainWindow.ShowInTaskbar = ShouldShowMainWindowInTaskbar(); if (!mainWindow.IsVisible) { @@ -1106,7 +1106,7 @@ public partial class App : Application var mainWindow = new MainWindow { DataContext = new MainWindowViewModel(), - ShowInTaskbar = true + ShowInTaskbar = ShouldShowMainWindowInTaskbar() }; _mainWindowOpened = false; @@ -1296,6 +1296,11 @@ public partial class App : Application } } + private bool ShouldShowMainWindowInTaskbar() + { + return _settingsFacade.Settings.LoadSnapshot(SettingsScope.App).ShowInTaskbar; + } + private void SetDesktopShellState(DesktopShellState state, string source) { if (_desktopShellState == state) diff --git a/LanMountainDesktop/Models/AppSettingsSnapshot.cs b/LanMountainDesktop/Models/AppSettingsSnapshot.cs index f83d756..4d369ea 100644 --- a/LanMountainDesktop/Models/AppSettingsSnapshot.cs +++ b/LanMountainDesktop/Models/AppSettingsSnapshot.cs @@ -154,6 +154,8 @@ public sealed class AppSettingsSnapshot public bool EnableSlideTransition { get; set; } = false; + public bool ShowInTaskbar { get; set; } = false; + public bool EnableFusedDesktop { get; set; } = false; public List DisabledPluginIds { get; set; } = []; diff --git a/LanMountainDesktop/Services/Settings/SettingsWindowService.cs b/LanMountainDesktop/Services/Settings/SettingsWindowService.cs index bb12cc5..edc0ad1 100644 --- a/LanMountainDesktop/Services/Settings/SettingsWindowService.cs +++ b/LanMountainDesktop/Services/Settings/SettingsWindowService.cs @@ -14,28 +14,10 @@ using LanMountainDesktop.Views; namespace LanMountainDesktop.Services.Settings; -public enum SettingsWindowAnchorTarget -{ - DesktopDockTrailingEdge = 0 -} - -public enum SettingsWindowFallbackMode -{ - None = 0, - ScreenBottomRight = 1 -} - public readonly record struct SettingsWindowOpenRequest( string Source, - Window? Owner = null, string? PageId = null, - SettingsWindowAnchorTarget AnchorTarget = SettingsWindowAnchorTarget.DesktopDockTrailingEdge, - SettingsWindowFallbackMode FallbackMode = SettingsWindowFallbackMode.ScreenBottomRight); - -public interface ISettingsWindowAnchorProvider -{ - bool TryGetSettingsWindowAnchorBounds(out PixelRect anchorBounds); -} + Window? ScreenReferenceWindow = null); public interface ISettingsWindowService { @@ -46,8 +28,6 @@ public interface ISettingsWindowService void Open(SettingsWindowOpenRequest request); void Close(); - - void Toggle(SettingsWindowOpenRequest request); } internal sealed class SettingsWindowService : ISettingsWindowService @@ -92,27 +72,25 @@ internal sealed class SettingsWindowService : ISettingsWindowService var appearanceSnapshot = _appearanceThemeService.GetCurrent(); _window.ApplyChromeMode(appearanceSnapshot.UseSystemChrome); ApplyTheme(_window); - _window.ReloadPages(request.PageId); - PositionWindow(_window, request); + + var targetPageId = request.PageId ?? _window.ViewModel.CurrentPageId; + _window.ReloadPages(targetPageId); if (!_window.IsVisible) { - if (request.Owner is not null && request.Owner.IsVisible) - { - _window.Show(request.Owner); - } - else - { - _window.Show(); - } - + CenterWindow(_window, request); + _window.Show(); NotifyStateChanged(); - PositionWindowLater(_window, request); + CenterWindowLater(_window, request); return; } + if (_window.WindowState == WindowState.Minimized) + { + _window.WindowState = WindowState.Normal; + } + _window.Activate(); - PositionWindowLater(_window, request); } public void Close() @@ -120,17 +98,6 @@ internal sealed class SettingsWindowService : ISettingsWindowService _window?.Close(); } - public void Toggle(SettingsWindowOpenRequest request) - { - if (IsOpen) - { - Close(); - return; - } - - Open(request); - } - private SettingsWindow CreateWindow() { var regionState = _settingsFacade.Region.Get(); @@ -147,7 +114,7 @@ internal sealed class SettingsWindowService : ISettingsWindowService _hostApplicationLifecycle, useSystemChrome); ApplyTheme(window); - window.ShowInTaskbar = false; + window.ShowInTaskbar = true; window.Closed += (_, _) => { _window = null; @@ -156,106 +123,87 @@ internal sealed class SettingsWindowService : ISettingsWindowService return window; } - private void PositionWindowLater(SettingsWindow window, SettingsWindowOpenRequest request) + private void CenterWindowLater(SettingsWindow window, SettingsWindowOpenRequest request) { Dispatcher.UIThread.Post( () => { - if (!window.IsVisible) + if (!ReferenceEquals(_window, window) || !window.IsVisible) { return; } - PositionWindow(window, request); + CenterWindow(window, request); }, DispatcherPriority.Background); } - private static void PositionWindow(SettingsWindow window, SettingsWindowOpenRequest request) + private static void CenterWindow(SettingsWindow window, SettingsWindowOpenRequest request) { - if (request.AnchorTarget == SettingsWindowAnchorTarget.DesktopDockTrailingEdge && - request.Owner is ISettingsWindowAnchorProvider anchorProvider && - anchorProvider.TryGetSettingsWindowAnchorBounds(out var anchorBounds)) - { - PositionWindowAboveAnchor(window, anchorBounds, request); - return; - } - - if (request.FallbackMode == SettingsWindowFallbackMode.ScreenBottomRight) - { - PositionWindowNearScreenBottomRight(window, request); - } + var referenceWorkingArea = + request.ScreenReferenceWindow is { IsVisible: true } screenReferenceWindow && + screenReferenceWindow.Screens?.ScreenFromWindow(screenReferenceWindow) is { } referenceScreen + ? referenceScreen.WorkingArea + : (PixelRect?)null; + var width = ResolveWindowWidth(window, request.ScreenReferenceWindow); + var height = ResolveWindowHeight(window, request.ScreenReferenceWindow); + var workingArea = SettingsWindowPlacementHelper.ResolveWorkingArea( + referenceWorkingArea, + window.Screens?.Primary?.WorkingArea, + width, + height); + window.Position = SettingsWindowPlacementHelper.CalculateCenteredPosition(workingArea, width, height); } - private static void PositionWindowAboveAnchor(Window window, PixelRect anchorBounds, SettingsWindowOpenRequest request) + private static int ResolveWindowWidth(Window window, Window? referenceWindow) { - var workingArea = GetWorkingArea(window, request); - - if (anchorBounds.Width <= 0 || anchorBounds.Height <= 0 || - anchorBounds.Right < workingArea.X || anchorBounds.Y > workingArea.Bottom) - { - PositionWindowNearScreenBottomRight(window, request); - return; - } - - var scale = window.RenderScaling > 0 ? window.RenderScaling : 1d; - var width = ResolveWindowWidth(window, scale); - var height = ResolveWindowHeight(window, scale); - var inset = (int)Math.Round(24 * scale); - var gap = (int)Math.Round(16 * scale); - - var x = anchorBounds.Right - width - inset; - var y = anchorBounds.Y - height - gap; - x = Math.Clamp(x, workingArea.X + inset, Math.Max(workingArea.X + inset, workingArea.Right - width - inset)); - y = Math.Clamp(y, workingArea.Y + inset, Math.Max(workingArea.Y + inset, workingArea.Bottom - height - inset)); - window.Position = new PixelPoint(x, y); - } - - private static void PositionWindowNearScreenBottomRight(Window window, SettingsWindowOpenRequest request) - { - var workingArea = GetWorkingArea(window, request); - var scale = window.RenderScaling > 0 ? window.RenderScaling : 1d; - var width = ResolveWindowWidth(window, scale); - var height = ResolveWindowHeight(window, scale); - var inset = (int)Math.Round(24 * scale); - - var x = Math.Max(workingArea.X + inset, workingArea.Right - width - inset); - var y = Math.Max(workingArea.Y + inset, workingArea.Bottom - height - inset); - window.Position = new PixelPoint(x, y); - } - - private static PixelRect GetWorkingArea(Window window, SettingsWindowOpenRequest request) - { - if (request.Owner is not null && request.Owner.Screens?.ScreenFromWindow(request.Owner) is { } ownerScreen) - { - return ownerScreen.WorkingArea; - } - - if (window.Screens?.ScreenFromWindow(window) is { } windowScreen) - { - return windowScreen.WorkingArea; - } - - return window.Screens?.Primary?.WorkingArea - ?? new PixelRect( - 0, - 0, - Math.Max(1280, ResolveWindowWidth(window, 1d) + 96), - Math.Max(720, ResolveWindowHeight(window, 1d) + 96)); - } - - private static int ResolveWindowWidth(Window window, double scale) - { - var widthDip = window.Bounds.Width > 1 ? window.Bounds.Width : Math.Max(window.Width, window.MinWidth); + var widthDip = ResolveWindowDimensionDip(window.Bounds.Width, window.Width, window.MinWidth, 1120d); + var scale = ResolveWindowScale(window, referenceWindow); return Math.Max(320, (int)Math.Round(widthDip * scale)); } - private static int ResolveWindowHeight(Window window, double scale) + private static int ResolveWindowHeight(Window window, Window? referenceWindow) { - var heightDip = window.Bounds.Height > 1 ? window.Bounds.Height : Math.Max(window.Height, window.MinHeight); + var heightDip = ResolveWindowDimensionDip(window.Bounds.Height, window.Height, window.MinHeight, 760d); + var scale = ResolveWindowScale(window, referenceWindow); return Math.Max(240, (int)Math.Round(heightDip * scale)); } + private static double ResolveWindowScale(Window window, Window? referenceWindow) + { + if (referenceWindow is not null && referenceWindow.RenderScaling > 0) + { + return referenceWindow.RenderScaling; + } + + if (window.RenderScaling > 0) + { + return window.RenderScaling; + } + + return 1d; + } + + private static double ResolveWindowDimensionDip(double boundsDip, double configuredDip, double minimumDip, double fallbackDip) + { + if (boundsDip > 1) + { + return boundsDip; + } + + if (!double.IsNaN(configuredDip) && configuredDip > 1) + { + return configuredDip; + } + + if (!double.IsNaN(minimumDip) && minimumDip > 1) + { + return minimumDip; + } + + return fallbackDip; + } + private void NotifyStateChanged() { StateChanged?.Invoke(this, EventArgs.Empty); @@ -363,3 +311,38 @@ internal sealed class SettingsWindowService : ISettingsWindowService }, DispatcherPriority.Background); } } + +internal static class SettingsWindowPlacementHelper +{ + internal static PixelRect ResolveWorkingArea( + PixelRect? referenceWorkingArea, + PixelRect? primaryWorkingArea, + int fallbackWindowWidth, + int fallbackWindowHeight) + { + if (referenceWorkingArea is { } referenceArea) + { + return referenceArea; + } + + if (primaryWorkingArea is { } primaryArea) + { + return primaryArea; + } + + return new PixelRect( + 0, + 0, + Math.Max(1280, fallbackWindowWidth + 96), + Math.Max(720, fallbackWindowHeight + 96)); + } + + internal static PixelPoint CalculateCenteredPosition(PixelRect workingArea, int windowWidth, int windowHeight) + { + var horizontalOffset = Math.Max(0, (workingArea.Width - windowWidth) / 2); + var verticalOffset = Math.Max(0, (workingArea.Height - windowHeight) / 2); + return new PixelPoint( + workingArea.X + horizontalOffset, + workingArea.Y + verticalOffset); + } +} diff --git a/LanMountainDesktop/ViewModels/SettingsViewModels.cs b/LanMountainDesktop/ViewModels/SettingsViewModels.cs index 08f05b7..945eeea 100644 --- a/LanMountainDesktop/ViewModels/SettingsViewModels.cs +++ b/LanMountainDesktop/ViewModels/SettingsViewModels.cs @@ -202,6 +202,7 @@ public sealed partial class GeneralSettingsPageViewModel : ViewModelBase, IDispo string.Equals(option.Value, normalizedRenderMode, StringComparison.OrdinalIgnoreCase)) ?? RenderModes[0]; EnableSlideTransition = appSnapshot.EnableSlideTransition; + ShowInTaskbar = appSnapshot.ShowInTaskbar; _isInitializing = false; RefreshPreview(); @@ -238,6 +239,11 @@ public sealed partial class GeneralSettingsPageViewModel : ViewModelBase, IDispo { EnableSlideTransition = _settingsFacade.Settings.LoadSnapshot(SettingsScope.App).EnableSlideTransition; } + + if (changedKeys.Contains(nameof(AppSettingsSnapshot.ShowInTaskbar))) + { + ShowInTaskbar = _settingsFacade.Settings.LoadSnapshot(SettingsScope.App).ShowInTaskbar; + } } public event Action? RestartRequested; @@ -260,6 +266,9 @@ public sealed partial class GeneralSettingsPageViewModel : ViewModelBase, IDispo [ObservableProperty] private bool _enableSlideTransition; + [ObservableProperty] + private bool _showInTaskbar; + public bool IsSlideTransitionAvailable => System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows); [ObservableProperty] @@ -367,6 +376,12 @@ public sealed partial class GeneralSettingsPageViewModel : ViewModelBase, IDispo SaveField(nameof(AppSettingsSnapshot.EnableSlideTransition), value); } + partial void OnShowInTaskbarChanged(bool value) + { + if (_isInitializing) return; + SaveField(nameof(AppSettingsSnapshot.ShowInTaskbar), value); + } + private void SaveField(string key, T value) { var snapshot = _settingsFacade.Settings.LoadSnapshot(SettingsScope.App); diff --git a/LanMountainDesktop/Views/FusedDesktopComponentLibraryControl.axaml.cs b/LanMountainDesktop/Views/FusedDesktopComponentLibraryControl.axaml.cs index a463aea..491f3dc 100644 --- a/LanMountainDesktop/Views/FusedDesktopComponentLibraryControl.axaml.cs +++ b/LanMountainDesktop/Views/FusedDesktopComponentLibraryControl.axaml.cs @@ -256,18 +256,14 @@ public partial class FusedDesktopComponentLibraryControl : UserControl private void OnFindMoreComponentsClick(object? sender, RoutedEventArgs e) { // 打开设置窗口并导航到插件目录页面 - if (Application.Current is App app && app.SettingsWindowService is { } settingsWindowService) + if (Application.Current is App app) { - var mainWindow = (Application.Current?.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime)?.MainWindow as MainWindow; - var request = new SettingsWindowOpenRequest( - Source: "FusedDesktopComponentLibrary", - Owner: mainWindow, - PageId: "plugin-catalog"); - settingsWindowService.Open(request); + app.OpenIndependentSettingsModule("FusedDesktopComponentLibrary", "plugin-catalog"); } // 关闭所在窗口 var window = this.FindAncestorOfType(); - window?.Close(); + var componentLibraryWindow = this.FindAncestorOfType(); + componentLibraryWindow?.Close(); } } diff --git a/LanMountainDesktop/Views/MainWindow.ComponentSystem.cs b/LanMountainDesktop/Views/MainWindow.ComponentSystem.cs index eaf949f..4a72409 100644 --- a/LanMountainDesktop/Views/MainWindow.ComponentSystem.cs +++ b/LanMountainDesktop/Views/MainWindow.ComponentSystem.cs @@ -19,7 +19,6 @@ using LanMountainDesktop.DesktopEditing; using LanMountainDesktop.Host.Abstractions; using LanMountainDesktop.Models; using LanMountainDesktop.Services; -using LanMountainDesktop.Services.Settings; using LanMountainDesktop.Settings.Core; using LanMountainDesktop.Theme; using LanMountainDesktop.Views.Components; @@ -282,16 +281,7 @@ public partial class MainWindow CloseComponentLibraryWindow(reopenSettings: false); } - var app = Application.Current as App; - if (app?.SettingsWindowService is { } settingsWindowService) - { - settingsWindowService.Toggle(new SettingsWindowOpenRequest( - Source: "MainWindowTaskbar", - Owner: this)); - return; - } - - app?.OpenIndependentSettingsModule("MainWindowTaskbar"); + (Application.Current as App)?.OpenIndependentSettingsModule("MainWindowTaskbar"); } private void OnPowerMenuEnterClick(object? sender, RoutedEventArgs e) @@ -2861,34 +2851,6 @@ public partial class MainWindow CloseDetachedComponentLibraryWindow(); } - public bool TryGetSettingsWindowAnchorBounds(out PixelRect anchorBounds) - { - anchorBounds = default; - if (!IsVisible || BottomTaskbarContainer is null) - { - return false; - } - - var origin = BottomTaskbarContainer.TranslatePoint(new Point(0, 0), this); - if (origin is null) - { - return false; - } - - var scale = RenderScaling > 0 ? RenderScaling : 1d; - var width = (int)Math.Round(BottomTaskbarContainer.Bounds.Width * scale); - var height = (int)Math.Round(BottomTaskbarContainer.Bounds.Height * scale); - if (width <= 0 || height <= 0) - { - return false; - } - - var x = Position.X + (int)Math.Round(origin.Value.X * scale); - var y = Position.Y + (int)Math.Round(origin.Value.Y * scale); - anchorBounds = new PixelRect(x, y, width, height); - return true; - } - private void CollapseComponentLibraryPanel() { // Animate component library panel collapsing downward diff --git a/LanMountainDesktop/Views/MainWindow.SettingsHardCut.Stubs.cs b/LanMountainDesktop/Views/MainWindow.SettingsHardCut.Stubs.cs index 03ffa8f..81b260f 100644 --- a/LanMountainDesktop/Views/MainWindow.SettingsHardCut.Stubs.cs +++ b/LanMountainDesktop/Views/MainWindow.SettingsHardCut.Stubs.cs @@ -79,6 +79,7 @@ public partial class MainWindow string.Equals(key, nameof(AppSettingsSnapshot.UpdateDownloadSource), StringComparison.OrdinalIgnoreCase) || string.Equals(key, nameof(AppSettingsSnapshot.UpdateDownloadThreads), StringComparison.OrdinalIgnoreCase) || string.Equals(key, nameof(AppSettingsSnapshot.EnableThreeFingerSwipe), StringComparison.OrdinalIgnoreCase) || + string.Equals(key, nameof(AppSettingsSnapshot.ShowInTaskbar), StringComparison.OrdinalIgnoreCase) || string.Equals(key, nameof(AppSettingsSnapshot.EnableSlideTransition), StringComparison.OrdinalIgnoreCase))) { return; @@ -688,6 +689,10 @@ public partial class MainWindow StatusBarShadowEnabled = _statusBarShadowEnabled, StatusBarShadowColor = _statusBarShadowColor, StatusBarShadowOpacity = _statusBarShadowOpacity, + EnableThreeFingerSwipe = existingSnapshot.EnableThreeFingerSwipe, + EnableSlideTransition = existingSnapshot.EnableSlideTransition, + ShowInTaskbar = existingSnapshot.ShowInTaskbar, + EnableFusedDesktop = existingSnapshot.EnableFusedDesktop, DisabledPluginIds = existingSnapshot.DisabledPluginIds, StudyFrameMs = existingSnapshot.StudyFrameMs, StudyScoreThresholdDbfs = existingSnapshot.StudyScoreThresholdDbfs, diff --git a/LanMountainDesktop/Views/MainWindow.axaml b/LanMountainDesktop/Views/MainWindow.axaml index c9b00df..e8dd12e 100644 --- a/LanMountainDesktop/Views/MainWindow.axaml +++ b/LanMountainDesktop/Views/MainWindow.axaml @@ -20,6 +20,7 @@ UseLayoutRounding="True" Foreground="{DynamicResource AdaptiveTextPrimaryBrush}" Background="Transparent" + TransparencyLevelHint="Transparent" Title="LanMountainDesktop"> @@ -99,12 +100,17 @@ HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> - + + + + + + + - diff --git a/LanMountainDesktop/Views/MainWindow.axaml.cs b/LanMountainDesktop/Views/MainWindow.axaml.cs index b56a297..06ae999 100644 --- a/LanMountainDesktop/Views/MainWindow.axaml.cs +++ b/LanMountainDesktop/Views/MainWindow.axaml.cs @@ -29,7 +29,7 @@ using LanMountainDesktop.Views.Components; namespace LanMountainDesktop.Views; -public partial class MainWindow : Window, ISettingsWindowAnchorProvider +public partial class MainWindow : Window { private enum WallpaperMediaType { @@ -450,6 +450,8 @@ public partial class MainWindow : Window, ISettingsWindowAnchorProvider MinShortSideCells, MaxShortSideCells); + ShowInTaskbar = snapshot.ShowInTaskbar; + _gridSpacingPreset = _gridSettingsService.NormalizeSpacingPreset(snapshot.GridSpacingPreset); _desktopEdgeInsetPercent = Math.Clamp(snapshot.DesktopEdgeInsetPercent, MinEdgeInsetPercent, MaxEdgeInsetPercent); @@ -884,7 +886,19 @@ public partial class MainWindow : Window, ISettingsWindowAnchorProvider return; } - WindowState = WindowState.Minimized; + var snapshot = _settingsService.LoadSnapshot(SettingsScope.App); + if (snapshot.ShowInTaskbar) + { + WindowState = WindowState.Minimized; + } + else if (Application.Current is App app) + { + app.HideMainWindowToTray(this, "MinimizeAction"); + } + else + { + WindowState = WindowState.Minimized; + } slideTransform.X = 0; DesktopPage.Opacity = 1; @@ -906,7 +920,8 @@ public partial class MainWindow : Window, ISettingsWindowAnchorProvider if (useSlide) { - slideTransform.X = Bounds.Width > 0 ? Bounds.Width : 1920; + var screenWidth = Screens.ScreenFromVisual(this)?.Bounds.Width ?? 3840; + slideTransform.X = Bounds.Width > 0 ? Bounds.Width : screenWidth; } DesktopPage.Transitions = savedTransitions; @@ -941,7 +956,27 @@ public partial class MainWindow : Window, ISettingsWindowAnchorProvider return; } - if (WindowState is WindowState.Minimized or WindowState.FullScreen) + var newState = (WindowState)e.NewValue!; + var oldState = (WindowState)e.OldValue!; + + if (oldState == WindowState.Minimized && newState != WindowState.Minimized) + { + PrepareEnterAnimation(); + + if (newState != WindowState.FullScreen) + { + WindowState = WindowState.FullScreen; + } + + Dispatcher.UIThread.Post(() => + { + PlayEnterAnimation(); + }, DispatcherPriority.Background); + + return; + } + + if (newState is WindowState.Minimized or WindowState.FullScreen) { return; } diff --git a/LanMountainDesktop/Views/SettingsPages/GeneralSettingsPage.axaml b/LanMountainDesktop/Views/SettingsPages/GeneralSettingsPage.axaml index 67108f9..f0cf797 100644 --- a/LanMountainDesktop/Views/SettingsPages/GeneralSettingsPage.axaml +++ b/LanMountainDesktop/Views/SettingsPages/GeneralSettingsPage.axaml @@ -117,6 +117,16 @@ + + + + + + + + +