diff --git a/.trae/documents/avalonia12-migration-plan.md b/.trae/documents/avalonia12-migration-plan.md new file mode 100644 index 0000000..52c8ade --- /dev/null +++ b/.trae/documents/avalonia12-migration-plan.md @@ -0,0 +1,106 @@ +# Avalonia 12 迁移计划 + +## 当前状态 + +项目已完成以下迁移准备: + +* `Directory.Packages.props` 中 Avalonia 包已升级到 `12.0.1` + +* `FluentAvaloniaUI` 已升级到 `3.0.0-preview1` + +* `Avalonia.Diagnostics` 已替换为 `AvaloniaUI.DiagnosticsSupport` + +* `Avalonia.Controls.WebView` 已升级到 `12.0.0` + +* `ClassIsland.Markdown.Avalonia` 已升级到 `12.0.0` + +## 构建错误清单(26 errors) + +### 1. 窗口装饰 API 移除(8 errors) + +**文件**:`LanMountainDesktop/Views/SettingsWindow.axaml.cs`(4 errors) + +* `ExtendClientAreaChromeHints` 不存在(line 166, 179) + +* `SystemDecorations` 已过时,需改用 `WindowDecorations`(line 168, 177) + +**文件**:`LanMountainDesktop/Views/ComponentEditorWindow.axaml.cs`(4 errors) + +* `ExtendClientAreaChromeHints` 不存在(line 63, 72) + +* `SystemDecorations` 已过时,需改用 `WindowDecorations`(line 65, 70) + +**AXAML 文件**:13 个文件使用 `SystemDecorations` 属性(编译警告) + +### 2. 变量/字段未找到(8 errors) + +**文件**:`LanMountainDesktop/Views/MainWindow.ComponentSystem.cs` + +* `centerLeft` 不存在(line 759, 766, 778) + +* `positions` 不存在(line 1266) + +**文件**:`LanMountainDesktop/Views/MainWindow.DesktopPaging.cs` + +* `child` 不存在(line 312) + +* `_isThreeFingerOrRightDragSwipeActive` 不存在(line 517, 828, 847, 850) + +### 3. API 变更(3 errors) + +**文件**:`LanMountainDesktop/App.axaml.cs` + +* `BindingPlugins` 不可访问(line 532, 537) + +**文件**:`LanMountainDesktop/Views/Components/DesktopComponentFailureView.cs` + +* `IClipboard.SetTextAsync` 不存在(line 187) + +**文件**:`LanMountainDesktop/Services/MonetColorService.cs` + +* `Bitmap.CopyPixels` 参数不匹配(line 91) + +### 4. 第三方库变更(1 error) + +**文件**:`LanMountainDesktop/Views/SettingsWindow.axaml.cs` + +* `FluentIcons.Avalonia.SymbolIconSource` 不存在(line 215) + +### 5. 过时属性警告(需同步修复) + +* `TextBox.Watermark` → `PlaceholderText`(7 处 .cs + 7 处 .axaml) + +## 迁移步骤 + +### Phase 1: 修复窗口装饰 API(高优先级) + +1. 重写 `SettingsWindow.ApplyChromeMode()` 使用 Avalonia 12 新 API +2. 重写 `ComponentEditorWindow.ApplyChromeMode()` 使用 Avalonia 12 新 API +3. 批量替换所有 `.axaml` 中的 `SystemDecorations` → `WindowDecorations` + +### Phase 2: 修复 MainWindow 编译错误(高优先级) + +1. 检查 `MainWindow.ComponentSystem.cs` 中 `centerLeft` 和 `positions` 的作用域问题 +2. 检查 `MainWindow.DesktopPaging.cs` 中 `child` 和 `_isThreeFingerOrRightDragSwipeActive` 的作用域问题 +3. 确认这些变量是否被意外删除或重命名 + +### Phase 3: 修复 Avalonia 12 API 变更(中优先级) + +1. `App.axaml.cs`: 替换 `BindingPlugins.DataValidators` 的访问方式 +2. `DesktopComponentFailureView.cs`: 使用新的剪贴板 API +3. `MonetColorService.cs`: 更新 `Bitmap.CopyPixels` 调用签名 + +### Phase 4: 修复第三方库变更(中优先级) + +1. `SettingsWindow.axaml.cs`: 替换 `FluentIcons.Avalonia.SymbolIconSource` 为 v3 等效 API + +### Phase 5: 清理过时属性(低优先级) + +1. 批量替换 `Watermark` → `PlaceholderText`(所有 .cs 和 .axaml) + +## 验证步骤 + +* 每阶段修复后运行 `dotnet build LanMountainDesktop.slnx -c Debug` + +* 最终运行 `dotnet test LanMountainDesktop.slnx -c Debug` + diff --git a/.trae/specs/avalonia-12-migration/checklist.md b/.trae/specs/avalonia-12-migration/checklist.md new file mode 100644 index 0000000..90ebdfa --- /dev/null +++ b/.trae/specs/avalonia-12-migration/checklist.md @@ -0,0 +1,21 @@ +# Checklist + +- [x] `Directory.Packages.props` contains the Avalonia 12 dependency baseline. +- [x] Main host references `Avalonia.Controls.WebView`. +- [x] Source no longer references `WebView.Avalonia`, `AvaloniaWebView`, or `.UseDesktopWebView()`. +- [x] `BrowserWidget` uses `NativeWebView.Source`, `Navigate`, `Refresh()`, `NavigationStarted`, and `EnvironmentRequested`. +- [x] WebView blanking navigates to `about:blank`. +- [x] Plugin SDK package version is `5.0.0`. +- [x] `PluginSdkInfo.ApiVersion` is `5.0.0`. +- [x] Plugin template package version default is `5.0.0`. +- [x] Plugin template manifest `apiVersion` is `5.0.0`. +- [x] Launcher data location config resolution cannot recurse through `ResolveDataRoot()`. +- [x] `OobeStateServiceTests` pass. +- [x] `dotnet build LanMountainDesktop.slnx -c Debug` has 0 errors. +- [x] `dotnet test LanMountainDesktop.slnx -c Debug` completes without a test host stack overflow. +- [ ] Windows host smoke test completed. +- [ ] Windows Launcher smoke test completed. +- [ ] Settings window FluentAvalonia 3 smoke test completed. +- [ ] Component editor Material smoke test completed. +- [ ] BrowserWidget navigation/refresh/page activation smoke test completed. +- [ ] WebView2 missing-runtime diagnostic smoke test completed. diff --git a/.trae/specs/avalonia-12-migration/spec.md b/.trae/specs/avalonia-12-migration/spec.md new file mode 100644 index 0000000..37206b8 --- /dev/null +++ b/.trae/specs/avalonia-12-migration/spec.md @@ -0,0 +1,49 @@ +# Avalonia 12 Full Stack Migration + +## Summary + +LanMountainDesktop has moved its desktop stack to the Avalonia 12 baseline. The migration covers the main host, Launcher, Plugin SDK, plugin runtime loading policy, official WebView usage, ClassIsland Markdown, FluentAvalonia, FluentIcons, and Material-related dependencies. + +## Requirements + +### Requirement: Centralized Avalonia 12 dependency baseline + +The solution SHALL use central package management for direct Avalonia-facing projects and keep the core UI dependency baseline on Avalonia `12.0.1`. + +Required package baseline: + +- `Avalonia*` `12.0.1` +- `Avalonia.Controls.WebView` `12.0.0` +- `ClassIsland.Markdown.Avalonia` `12.0.0` +- `FluentAvaloniaUI` `3.0.0-preview1` +- `FluentIcons.Avalonia` `2.1.325` +- `Material.Avalonia` `3.16.0` +- `Material.Icons.Avalonia` `3.0.2` + +### Requirement: Official WebView + +The host SHALL use `Avalonia.Controls.NativeWebView` for the browser widget and SHALL NOT reference `WebView.Avalonia`, `AvaloniaWebView`, or `.UseDesktopWebView()`. + +Windows WebView2 user data configuration SHALL be supplied through `EnvironmentRequested` using `WindowsWebView2EnvironmentRequestedEventArgs.UserDataFolder`. + +### Requirement: Plugin SDK v5 + +The Plugin SDK API baseline SHALL be `5.0.0`. SDK v4 plugins are treated as incompatible until rebuilt. + +The SDK SHALL keep the existing public UI extension shape, including `SettingsPageBase` and Avalonia `Control` based desktop components. + +### Requirement: Launcher data location stability + +Launcher data location configuration SHALL be read from a fixed bootstrap Launcher data directory so resolving the selected data root cannot recursively require resolving itself. + +### Requirement: OOBE state compatibility + +The Launcher SHALL read current OOBE state from the resolved `Launcher/state` directory and SHALL continue to migrate the legacy `.launcher/state/first_run_completed` marker. + +## Acceptance + +- `dotnet build LanMountainDesktop.slnx -c Debug` completes with 0 errors. +- `OobeStateServiceTests` pass. +- Full `dotnet test LanMountainDesktop.slnx -c Debug` no longer aborts from `DataLocationResolver` recursion. +- Plugin template defaults to SDK package version `5.0.0` and manifest `apiVersion` `5.0.0`. +- Current developer documentation points to SDK v5 and Avalonia 12. diff --git a/.trae/specs/avalonia-12-migration/tasks.md b/.trae/specs/avalonia-12-migration/tasks.md new file mode 100644 index 0000000..c30c775 --- /dev/null +++ b/.trae/specs/avalonia-12-migration/tasks.md @@ -0,0 +1,18 @@ +# Tasks + +- [x] Centralize Avalonia 12 package versions in `Directory.Packages.props`. +- [x] Move the host, Launcher, Plugin SDK, DesktopHost, Shared.Contracts, and Avalonia-facing projects onto central package versions. +- [x] Replace third-party `WebView.Avalonia` usage with official `NativeWebView`. +- [x] Configure WebView2 user data through `EnvironmentRequested`. +- [x] Move FluentAvalonia usages to the FA3 control names and package baseline. +- [x] Move FluentIcons usage to `FluentIcons.Avalonia` and remove the old `.Fluent` package. +- [x] Update Plugin SDK package version and API baseline to `5.0.0`. +- [x] Update plugin runtime shared assembly policy for Avalonia 12 / FluentAvalonia / FluentIcons / Material. +- [x] Fix Avalonia 12 compile breaks in window chrome, binding plugin access, clipboard, bitmap copy, and icon source usage. +- [x] Fix Launcher data location recursion by using a fixed bootstrap config path. +- [x] Fix OOBE state tests and legacy marker compatibility. +- [x] Update PluginTemplate defaults to SDK v5. +- [x] Add SDK v5 migration documentation. +- [x] Update current docs from SDK v4 / Avalonia 11 examples to SDK v5 / Avalonia 12. +- [x] Run full solution tests and record any remaining non-upgrade failures. +- [ ] Perform Windows manual smoke test for host, Launcher, settings, component editor, BrowserWidget, and WebView2 missing-runtime handling. diff --git a/AGENTS.md b/AGENTS.md index 211433c..792e090 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -74,7 +74,7 @@ dotnet test LanMountainDesktop.slnx -c Debug - SDK 公共 API 以 `LanMountainDesktop.PluginSdk/` 为准 - 共享契约以 `LanMountainDesktop.Shared.Contracts/` 为准 - market 数据来源默认是兄弟仓库 `..\\LanAirApp` -- 迁移或 breaking change 优先同步 `docs/PLUGIN_SDK_V4_MIGRATION.md` +- 迁移或 breaking change 优先同步 `docs/PLUGIN_SDK_V5_MIGRATION.md` ### 设置与主题 @@ -91,6 +91,6 @@ dotnet test LanMountainDesktop.slnx -c Debug - 视觉规范:`docs/VISUAL_SPEC.md` - 圆角规范:`docs/CORNER_RADIUS_SPEC.md` - 生态边界:`docs/ECOSYSTEM_BOUNDARIES.md` -- SDK v4 迁移:`docs/PLUGIN_SDK_V4_MIGRATION.md` +- SDK v5 迁移:`docs/PLUGIN_SDK_V5_MIGRATION.md` 如果多个文档都提到同一件事,以 `docs/ai/DOC_SOURCES.md` 列出的权威来源为准。 diff --git a/Directory.Packages.props b/Directory.Packages.props new file mode 100644 index 0000000..d5a50d7 --- /dev/null +++ b/Directory.Packages.props @@ -0,0 +1,42 @@ + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/LanMountainDesktop.DesktopHost/LanMountainDesktop.DesktopHost.csproj b/LanMountainDesktop.DesktopHost/LanMountainDesktop.DesktopHost.csproj index acb8b49..3de9b89 100644 --- a/LanMountainDesktop.DesktopHost/LanMountainDesktop.DesktopHost.csproj +++ b/LanMountainDesktop.DesktopHost/LanMountainDesktop.DesktopHost.csproj @@ -5,7 +5,7 @@ enable - + diff --git a/LanMountainDesktop.Launcher/LanMountainDesktop.Launcher.csproj b/LanMountainDesktop.Launcher/LanMountainDesktop.Launcher.csproj index e19e124..ce3817c 100644 --- a/LanMountainDesktop.Launcher/LanMountainDesktop.Launcher.csproj +++ b/LanMountainDesktop.Launcher/LanMountainDesktop.Launcher.csproj @@ -22,16 +22,14 @@ - - - - - - - - - - + + + + + + + + diff --git a/LanMountainDesktop.Launcher/Services/DataLocationResolver.cs b/LanMountainDesktop.Launcher/Services/DataLocationResolver.cs index 2fc0cd6..31b3c3c 100644 --- a/LanMountainDesktop.Launcher/Services/DataLocationResolver.cs +++ b/LanMountainDesktop.Launcher/Services/DataLocationResolver.cs @@ -32,6 +32,11 @@ internal sealed class DataLocationResolver /// public string DefaultPortableDataPath => Path.Combine(_appRoot, "AppData"); + private string ResolveBootstrapLauncherDataPath() + { + return Path.Combine(_defaultSystemDataPath, LauncherFolderName); + } + /// /// 检查是否允许便携模式(应用目录是否可写) /// @@ -56,6 +61,11 @@ internal sealed class DataLocationResolver public string ResolveDataRoot() { var config = LoadConfig(); + return ResolveDataRoot(config); + } + + private string ResolveDataRoot(DataLocationConfig? config) + { if (config is null) { return _defaultSystemDataPath; @@ -65,7 +75,7 @@ internal sealed class DataLocationResolver { var portablePath = !string.IsNullOrWhiteSpace(config.PortableDataPath) ? config.PortableDataPath - : _defaultSystemDataPath; + : DefaultPortableDataPath; return Path.GetFullPath(portablePath); } @@ -95,7 +105,7 @@ internal sealed class DataLocationResolver /// public string ResolveConfigPath() { - return Path.Combine(ResolveLauncherDataPath(), ConfigFileName); + return Path.Combine(ResolveBootstrapLauncherDataPath(), ConfigFileName); } /// @@ -153,7 +163,7 @@ internal sealed class DataLocationResolver { try { - var launcherPath = ResolveLauncherDataPath(); + var launcherPath = ResolveBootstrapLauncherDataPath(); Directory.CreateDirectory(launcherPath); var configPath = ResolveConfigPath(); @@ -184,8 +194,9 @@ internal sealed class DataLocationResolver // 先创建目录结构 try { - Directory.CreateDirectory(ResolveLauncherDataPath()); - Directory.CreateDirectory(ResolveDesktopDataPath()); + var resolvedDataRoot = ResolveDataRoot(config); + Directory.CreateDirectory(Path.Combine(resolvedDataRoot, LauncherFolderName)); + Directory.CreateDirectory(Path.Combine(resolvedDataRoot, DesktopFolderName)); } catch (Exception ex) { diff --git a/LanMountainDesktop.Launcher/Services/OobeStateService.cs b/LanMountainDesktop.Launcher/Services/OobeStateService.cs index 9ecca79..a7c4ca3 100644 --- a/LanMountainDesktop.Launcher/Services/OobeStateService.cs +++ b/LanMountainDesktop.Launcher/Services/OobeStateService.cs @@ -9,6 +9,7 @@ internal sealed class OobeStateService private readonly string _stateDirectory; private readonly string _statePath; + private readonly string _legacyStatePath; private readonly string _legacyMarkerPath; private readonly LauncherExecutionSnapshot _executionSnapshot; @@ -25,7 +26,13 @@ internal sealed class OobeStateService : Path.GetFullPath(stateRootOverride); _stateDirectory = Path.Combine(stateRoot, "Launcher", "state"); _statePath = Path.Combine(_stateDirectory, "oobe-state.json"); - _legacyMarkerPath = Path.Combine(_stateDirectory, "first_run_completed"); + + var legacyRoot = string.IsNullOrWhiteSpace(stateRootOverride) + ? Path.GetFullPath(appRoot) + : Path.GetFullPath(stateRootOverride); + var legacyStateDirectory = Path.Combine(legacyRoot, ".launcher", "state"); + _legacyStatePath = Path.Combine(legacyStateDirectory, "oobe-state.json"); + _legacyMarkerPath = Path.Combine(legacyStateDirectory, "first_run_completed"); } public OobeLaunchDecision Evaluate(CommandContext context) @@ -100,14 +107,12 @@ internal sealed class OobeStateService var migratedLegacyMarker = false; if (File.Exists(_statePath)) { - using var stream = File.OpenRead(_statePath); - var state = JsonSerializer.Deserialize(stream, AppJsonContext.Default.OobeStateFile); - if (state is null || state.SchemaVersion <= 0 || string.IsNullOrWhiteSpace(state.CompletedAtUtc)) - { - return BuildUnavailableDecision(context, "OOBE state file is invalid."); - } + return EvaluateStateFile(context, _statePath, migratedLegacyState: false); + } - return BuildDecision(context, OobeStateStatus.Completed, shouldShowOobe: false, migratedLegacyMarker: false); + if (File.Exists(_legacyStatePath)) + { + return EvaluateStateFile(context, _legacyStatePath, migratedLegacyState: false); } if (File.Exists(_legacyMarkerPath)) @@ -140,6 +145,18 @@ internal sealed class OobeStateService return result.Success; } + private OobeLaunchDecision EvaluateStateFile(CommandContext context, string statePath, bool migratedLegacyState) + { + using var stream = File.OpenRead(statePath); + var state = JsonSerializer.Deserialize(stream, AppJsonContext.Default.OobeStateFile); + if (state is null || state.SchemaVersion <= 0 || string.IsNullOrWhiteSpace(state.CompletedAtUtc)) + { + return BuildUnavailableDecision(context, "OOBE state file is invalid."); + } + + return BuildDecision(context, OobeStateStatus.Completed, shouldShowOobe: false, migratedLegacyMarker: migratedLegacyState); + } + private void TryDeleteLegacyMarker() { try diff --git a/LanMountainDesktop.Launcher/Views/DataLocationPromptWindow.axaml b/LanMountainDesktop.Launcher/Views/DataLocationPromptWindow.axaml index 386cba2..ef012b9 100644 --- a/LanMountainDesktop.Launcher/Views/DataLocationPromptWindow.axaml +++ b/LanMountainDesktop.Launcher/Views/DataLocationPromptWindow.axaml @@ -3,13 +3,13 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:views="clr-namespace:LanMountainDesktop.Launcher.Views" - xmlns:ui="using:FluentAvalonia.UI.Controls" + xmlns:fi="using:FluentIcons.Avalonia" mc:Ignorable="d" d:DesignWidth="520" d:DesignHeight="480" x:Class="LanMountainDesktop.Launcher.Views.DataLocationPromptWindow" x:DataType="views:DataLocationPromptWindow" - Title="选择数据保存位置" + Title="Choose Data Location" Width="520" Height="480" CanResize="False" @@ -24,12 +24,13 @@ - - @@ -41,15 +42,15 @@ IsVisible="False"> - - - @@ -70,11 +71,11 @@ GroupName="DataLocation" IsChecked="True" /> - - @@ -101,11 +102,11 @@ GroupName="DataLocation" IsEnabled="False" /> - - @@ -124,7 +125,7 @@ Padding="12,10" IsVisible="False"> - - + - + - + - + - + - + - + - + - + - + - + + Text="Select a component to view its details."/> diff --git a/LanMountainDesktop/Views/FusedDesktopComponentLibraryWindow.axaml b/LanMountainDesktop/Views/FusedDesktopComponentLibraryWindow.axaml index d255473..0630960 100644 --- a/LanMountainDesktop/Views/FusedDesktopComponentLibraryWindow.axaml +++ b/LanMountainDesktop/Views/FusedDesktopComponentLibraryWindow.axaml @@ -1,24 +1,24 @@ + Title="Add Component"> - + Text="Add Component" /> + Text="Browse available widgets and add them to the current fused desktop layout." /> private bool WouldComponentsCollide() { if (TopStatusBarHost is null) return false; - // 获取各区域当前占用的宽度 var leftWidth = GetLeftPanelOccupiedWidth(); var centerWidth = GetCenterPanelOccupiedWidth(); var rightWidth = GetRightPanelOccupiedWidth(); - // 获取状态栏总宽度 var totalWidth = TopStatusBarHost.Bounds.Width; if (totalWidth <= 0) return false; - // 计算中间区域的实际位置 - // 左列是 *, 中列是 Auto, 右列是 * - // 中间区域居中显示 + // 闁荤姳绶ょ槐鏇㈡偩缂佹鈻旀い鎾卞灪閿涚喖鏌涢弽褎鎯堥柣鎾寸懇閹啴宕熼銈嗘緰闂傚倸瀚幊宥囩礊閸涱垳纾? // 閻庡綊娼荤粻鎴﹀垂椤忓牆鍙?*, 婵炴垶鎼╅崢濂稿垂椤忓牆鍙?Auto, 闂佸憡鐟ラ崯鍧楀垂椤忓牆鍙?* + // 婵炴垶鎼╅崣鍐ㄎ涢崸妤€绀岄柛婵嗗閸樼敻鎮橀悙鍙夊櫢闁煎灚鍨垮浼村礈瑜嬫禒? var centerLeft = (totalWidth - centerWidth) / 2; var centerRight = centerLeft + centerWidth; - // 安全间距(像素) + // 闁诲海鎳撻ˇ顖炲矗韫囨稒鈷掔痪鎯ь儑閻涒晠鏌ㄥ☉妯煎闁稿孩姘ㄥΣ鎰版偑閸涱垳顦? const double safetyMargin = 20; - // 检测左侧组件是否会与中间区域碰撞 - // 左侧组件右边界 = leftWidth - // 中间区域左边界 = centerLeft + // 濠碘槅鍋€閸嬫挻绻涢弶鎴剰濞戞柨绻戠粭鐔活槾缂侇喖绉电粋鎺楁嚋閸倣锕傛煕濮樺墽绱扮紒杈╁缁嬪鎯斿┑濠傚箑闂傚倸鍊瑰娆戜焊椤栫偛鏄ラ柣鏂捐濞奸箖鏌? // 閻庡綊娼荤紓姘跺疾閸撲胶纾奸柛鏇ㄤ簼椤愪粙鏌涘▎蹇曟瀮缂佹梻鍠栭幃?= leftWidth + // 婵炴垶鎼╅崣鍐ㄎ涢崸妤€绀岄柛婵嗗閸樼數鈧綊娼荤粻鎺旂博閻斿吋鍋?= centerLeft if (leftWidth + safetyMargin > centerLeft) { return true; } - // 检测右侧组件是否会与中间区域碰撞 - // 右侧组件左边界 = totalWidth - rightWidth - // 中间区域右边界 = centerRight + // 濠碘槅鍋€閸嬫挻绻涢弶鎴剰鐟滄澘鎲$粭鐔活槾缂侇喖绉电粋鎺楁嚋閸倣锕傛煕濮樺墽绱扮紒杈╁缁嬪鎯斿┑濠傚箑闂傚倸鍊瑰娆戜焊椤栫偛鏄ラ柣鏂捐濞奸箖鏌? // 闂佸憡鐟ラ崢鏍疾閸撲胶纾奸柛鏇ㄤ簼椤愮晫鈧綊娼荤粻鎺旂博閻斿吋鍋?= totalWidth - rightWidth + // 婵炴垶鎼╅崣鍐ㄎ涢崸妤€绀岄柛婵嗗閸樼敻鏌涘▎蹇曟瀮缂佹梻鍠栭幃?= centerRight if (totalWidth - rightWidth - safetyMargin < centerRight) { return true; } - // 检测中间区域是否会与左右两侧碰撞(中间区域过宽) if (centerLeft < leftWidth + safetyMargin || centerRight > totalWidth - rightWidth - safetyMargin) { @@ -797,8 +786,7 @@ public partial class MainWindow } /// - /// 获取左侧面板占用的宽度(包括间距) - /// + /// 闂佸吋鍎抽崲鑼躲亹閸パ屽晠闁挎梹瀵у▍鐘绘⒒閸稑鐏繝銏★耿瀹曪繝鎮╅崹顐f闂佹眹鍔岀€氼剟顢欓弮鈧幆鏃堟晜閼测晝顦╅梺鍛婄墪閹冲繒鈧凹鍙冨鑽ゅ鐎n剛宕洪梺? /// private double GetLeftPanelOccupiedWidth() { if (TopStatusLeftPanel is null) @@ -817,8 +805,7 @@ public partial class MainWindow } } - // 添加间距 - if (visibleCount > 1) + // 濠电儑缍€椤曆勬叏閻愮儤鈷掔痪鎯ь儑閻? if (visibleCount > 1) { width += spacing * (visibleCount - 1); } @@ -827,8 +814,7 @@ public partial class MainWindow } /// - /// 获取中间面板占用的宽度(包括间距) - /// + /// 闂佸吋鍎抽崲鑼躲亹閸ャ劎鈻旀い鎾卞灪閿涚喖姊婚崼娑樼仾婵犮垺锕㈠畷锟犳偐閸偅娈㈤梺姹囧妼鐎氼剟顢欓弮鈧幆鏃堟晜閼测晝顦╅梺鍛婄墪閹冲繒鈧凹鍙冨鑽ゅ鐎n剛宕洪梺? /// private double GetCenterPanelOccupiedWidth() { if (TopStatusCenterPanel is null) @@ -847,8 +833,7 @@ public partial class MainWindow } } - // 添加间距 - if (visibleCount > 1) + // 濠电儑缍€椤曆勬叏閻愮儤鈷掔痪鎯ь儑閻? if (visibleCount > 1) { width += spacing * (visibleCount - 1); } @@ -857,8 +842,7 @@ public partial class MainWindow } /// - /// 获取右侧面板占用的宽度(包括间距) - /// + /// 闂佸吋鍎抽崲鑼躲亹閸ヮ剙鐭楅柛蹇撴噺濞呯娀姊婚崼娑樼仾婵犮垺锕㈠畷锟犳偐閸偅娈㈤梺姹囧妼鐎氼剟顢欓弮鈧幆鏃堟晜閼测晝顦╅梺鍛婄墪閹冲繒鈧凹鍙冨鑽ゅ鐎n剛宕洪梺? /// private double GetRightPanelOccupiedWidth() { if (TopStatusRightPanel is null) @@ -877,8 +861,7 @@ public partial class MainWindow } } - // 添加间距 - if (visibleCount > 1) + // 濠电儑缍€椤曆勬叏閻愮儤鈷掔痪鎯ь儑閻? if (visibleCount > 1) { width += spacing * (visibleCount - 1); } @@ -887,25 +870,19 @@ public partial class MainWindow } /// - /// 检查是否可以在指定位置添加组件 - /// + /// 濠碘槅鍋€閸嬫捇鏌$仦璇插姕婵″弶鎮傚畷銉╂晝閳ь剝銇愰崣澶岊浄闁靛鍎查煬顒勬煙缁嬫寧鎼愰柣锝囧亾閹峰懎顓奸崶鈺傜€┑鐑囩秬椤曆勬叏閻愮數纾奸柛鏇ㄤ簼椤? /// private bool CanAddComponentAtPosition(string position) { - // 先临时显示组件以计算宽度 var wouldCollide = WouldComponentsCollide(); if (!wouldCollide) return true; - // 如果会发生碰撞,检查是否是因为目标位置导致的 - // 获取当前各区域宽度 var leftWidth = GetLeftPanelOccupiedWidth(); var centerWidth = GetCenterPanelOccupiedWidth(); var rightWidth = GetRightPanelOccupiedWidth(); - // 估算新组件的宽度(基于当前单元格大小) var estimatedNewComponentWidth = _currentDesktopCellSize > 0 ? _currentDesktopCellSize * 2 : 120; - // 根据目标位置检查添加后是否会碰撞 return position switch { "Left" => CanAddToLeft(leftWidth, centerWidth, rightWidth, estimatedNewComponentWidth), @@ -924,7 +901,7 @@ public partial class MainWindow if (totalWidth <= 0) return true; - var newLeftWidth = leftWidth + newWidth + TopStatusLeftPanel?.Spacing ?? 6; + var newLeftWidth = leftWidth + newWidth + (TopStatusLeftPanel?.Spacing ?? 6); var centerLeft = (totalWidth - centerWidth) / 2; const double safetyMargin = 20; @@ -940,7 +917,7 @@ public partial class MainWindow if (totalWidth <= 0) return true; - var newCenterWidth = centerWidth + newWidth + TopStatusCenterPanel?.Spacing ?? 6; + var newCenterWidth = centerWidth + newWidth + (TopStatusCenterPanel?.Spacing ?? 6); var centerLeft = (totalWidth - newCenterWidth) / 2; var centerRight = centerLeft + newCenterWidth; @@ -958,7 +935,7 @@ public partial class MainWindow if (totalWidth <= 0) return true; - var newRightWidth = rightWidth + newWidth + TopStatusRightPanel?.Spacing ?? 6; + var newRightWidth = rightWidth + newWidth + (TopStatusRightPanel?.Spacing ?? 6); var centerRight = (totalWidth + centerWidth) / 2; const double safetyMargin = 20; @@ -970,7 +947,6 @@ public partial class MainWindow var showClock = _topStatusComponentIds.Contains(BuiltInComponentIds.Clock); var hasVisibleTopStatusComponent = false; - // 先隐藏所有时钟控件 if (ClockWidgetLeft is not null) ClockWidgetLeft.IsVisible = false; if (ClockWidgetCenter is not null) @@ -978,7 +954,6 @@ public partial class MainWindow if (ClockWidgetRight is not null) ClockWidgetRight.IsVisible = false; - // 先隐藏所有文字胶囊控件 if (TextCapsuleWidgetLeft is not null) TextCapsuleWidgetLeft.IsVisible = false; if (TextCapsuleWidgetCenter is not null) @@ -986,7 +961,6 @@ public partial class MainWindow if (TextCapsuleWidgetRight is not null) TextCapsuleWidgetRight.IsVisible = false; - // 先隐藏所有网速控件 if (NetworkSpeedWidgetLeft is not null) NetworkSpeedWidgetLeft.IsVisible = false; if (NetworkSpeedWidgetCenter is not null) @@ -994,7 +968,6 @@ public partial class MainWindow if (NetworkSpeedWidgetRight is not null) NetworkSpeedWidgetRight.IsVisible = false; - // 根据位置设置显示对应的时钟控件(带碰撞检测) if (showClock) { var targetPosition = _clockPosition; @@ -1019,7 +992,6 @@ public partial class MainWindow } else { - // 如果目标位置无法添加,尝试其他位置 var alternativePosition = FindAlternativePosition(targetPosition); if (alternativePosition is not null) { @@ -1041,8 +1013,7 @@ public partial class MainWindow } } - // 根据位置设置显示对应的文字胶囊控件(带碰撞检测) - if (_showTextCapsule) + // 闂佸搫绉烽~澶婄暤娴h濯寸€广儱娲ㄩ弸鍌炴偣娴g鈷旈柣銈呮瀵即宕滆娴犳盯鎮楅悽鍨殌缂併劍鐓¢幆鍐礋椤掍胶鈧噣鎮楀☉娆樻畽闁稿繐鐭傚畷鑸电節閸愩劋绮繛瀵稿Ь椤旀劗妲愬▎鎴炴殰闁挎梻铏庡楣冩煙閸撗冧沪妞ゃ儱鎳庨湁閻庯絽澧庣粈? if (_showTextCapsule) { var targetPosition = _textCapsulePosition; var canAdd = CanAddComponentAtPosition(targetPosition); @@ -1066,7 +1037,6 @@ public partial class MainWindow } else { - // 如果目标位置无法添加,尝试其他位置 var alternativePosition = FindAlternativePosition(targetPosition); if (alternativePosition is not null) { @@ -1088,7 +1058,6 @@ public partial class MainWindow } } - // 根据位置设置显示对应的网速控件(带碰撞检测) if (_showNetworkSpeed) { var targetPosition = _networkSpeedPosition; @@ -1113,7 +1082,6 @@ public partial class MainWindow } else { - // 如果目标位置无法添加,尝试其他位置 var alternativePosition = FindAlternativePosition(targetPosition); if (alternativePosition is not null) { @@ -1140,7 +1108,6 @@ public partial class MainWindow TopStatusBarHost.IsVisible = hasVisibleTopStatusComponent; } - // 延迟检查碰撞并调整 Dispatcher.UIThread.Post(async () => { await System.Threading.Tasks.Task.Delay(50); @@ -1149,22 +1116,18 @@ public partial class MainWindow } /// - /// 当组件发生碰撞时,自动调整位置 - /// + /// 閻熸粎澧楅幐鍓у垝瀹ュ棛顩烽悹鍝勬惈缁叉椽鏌i姀銏犳灁妞ゎ偒鍋婇獮姗€鎮欑€涙﹩妲梺鎸庣☉閻線宕靛鍫濈闁靛鍔庡▓鍫曟煛娴h櫣绡€缂傚秴鎳愮槐? /// private void AdjustComponentsIfColliding() { if (!WouldComponentsCollide()) return; - // 获取当前可见的组件 var leftComponents = GetVisibleLeftComponents(); var centerComponents = GetVisibleCenterComponents(); var rightComponents = GetVisibleRightComponents(); - // 优先保留时钟,调整文字胶囊位置 if (TextCapsuleWidgetLeft?.IsVisible == true && WouldComponentsCollide()) { - // 尝试将左侧文字胶囊移到中间 if (CanAddComponentAtPosition("Center")) { TextCapsuleWidgetLeft.IsVisible = false; @@ -1172,7 +1135,6 @@ public partial class MainWindow TextCapsuleWidgetCenter.SetTransparentBackground(_textCapsuleTransparentBackground); TextCapsuleWidgetCenter.SetText(_textCapsuleContent); } - // 或者移到右侧 else if (CanAddComponentAtPosition("Right")) { TextCapsuleWidgetLeft.IsVisible = false; @@ -1180,7 +1142,6 @@ public partial class MainWindow TextCapsuleWidgetRight.SetTransparentBackground(_textCapsuleTransparentBackground); TextCapsuleWidgetRight.SetText(_textCapsuleContent); } - // 如果都无法添加,则隐藏文字胶囊 else { TextCapsuleWidgetLeft.IsVisible = false; @@ -1189,7 +1150,6 @@ public partial class MainWindow if (TextCapsuleWidgetRight?.IsVisible == true && WouldComponentsCollide()) { - // 尝试将右侧文字胶囊移到中间 if (CanAddComponentAtPosition("Center")) { TextCapsuleWidgetRight.IsVisible = false; @@ -1197,7 +1157,6 @@ public partial class MainWindow TextCapsuleWidgetCenter.SetTransparentBackground(_textCapsuleTransparentBackground); TextCapsuleWidgetCenter.SetText(_textCapsuleContent); } - // 或者移到左侧 else if (CanAddComponentAtPosition("Left")) { TextCapsuleWidgetRight.IsVisible = false; @@ -1205,7 +1164,6 @@ public partial class MainWindow TextCapsuleWidgetLeft.SetTransparentBackground(_textCapsuleTransparentBackground); TextCapsuleWidgetLeft.SetText(_textCapsuleContent); } - // 如果都无法添加,则隐藏文字胶囊 else { TextCapsuleWidgetRight.IsVisible = false; @@ -1214,7 +1172,6 @@ public partial class MainWindow if (TextCapsuleWidgetCenter?.IsVisible == true && WouldComponentsCollide()) { - // 尝试将中间文字胶囊移到左侧 if (CanAddComponentAtPosition("Left")) { TextCapsuleWidgetCenter.IsVisible = false; @@ -1222,7 +1179,6 @@ public partial class MainWindow TextCapsuleWidgetLeft.SetTransparentBackground(_textCapsuleTransparentBackground); TextCapsuleWidgetLeft.SetText(_textCapsuleContent); } - // 或者移到右侧 else if (CanAddComponentAtPosition("Right")) { TextCapsuleWidgetCenter.IsVisible = false; @@ -1230,17 +1186,14 @@ public partial class MainWindow TextCapsuleWidgetRight.SetTransparentBackground(_textCapsuleTransparentBackground); TextCapsuleWidgetRight.SetText(_textCapsuleContent); } - // 如果都无法添加,则隐藏文字胶囊 else { TextCapsuleWidgetCenter.IsVisible = false; } } - // 调整网速组件位置(优先级:时钟 > 文字胶囊 > 网速) if (NetworkSpeedWidgetLeft?.IsVisible == true && WouldComponentsCollide()) { - // 尝试将左侧网速移到中间 if (CanAddComponentAtPosition("Center")) { NetworkSpeedWidgetLeft.IsVisible = false; @@ -1248,7 +1201,6 @@ public partial class MainWindow NetworkSpeedWidgetCenter.SetTransparentBackground(_networkSpeedTransparentBackground); NetworkSpeedWidgetCenter.SetDisplayMode(_networkSpeedDisplayMode); } - // 或者移到右侧 else if (CanAddComponentAtPosition("Right")) { NetworkSpeedWidgetLeft.IsVisible = false; @@ -1256,7 +1208,6 @@ public partial class MainWindow NetworkSpeedWidgetRight.SetTransparentBackground(_networkSpeedTransparentBackground); NetworkSpeedWidgetRight.SetDisplayMode(_networkSpeedDisplayMode); } - // 如果都无法添加,则隐藏网速 else { NetworkSpeedWidgetLeft.IsVisible = false; @@ -1265,7 +1216,6 @@ public partial class MainWindow if (NetworkSpeedWidgetRight?.IsVisible == true && WouldComponentsCollide()) { - // 尝试将右侧网速移到中间 if (CanAddComponentAtPosition("Center")) { NetworkSpeedWidgetRight.IsVisible = false; @@ -1273,7 +1223,6 @@ public partial class MainWindow NetworkSpeedWidgetCenter.SetTransparentBackground(_networkSpeedTransparentBackground); NetworkSpeedWidgetCenter.SetDisplayMode(_networkSpeedDisplayMode); } - // 或者移到左侧 else if (CanAddComponentAtPosition("Left")) { NetworkSpeedWidgetRight.IsVisible = false; @@ -1281,7 +1230,6 @@ public partial class MainWindow NetworkSpeedWidgetLeft.SetTransparentBackground(_networkSpeedTransparentBackground); NetworkSpeedWidgetLeft.SetDisplayMode(_networkSpeedDisplayMode); } - // 如果都无法添加,则隐藏网速 else { NetworkSpeedWidgetRight.IsVisible = false; @@ -1290,7 +1238,6 @@ public partial class MainWindow if (NetworkSpeedWidgetCenter?.IsVisible == true && WouldComponentsCollide()) { - // 尝试将中间网速移到左侧 if (CanAddComponentAtPosition("Left")) { NetworkSpeedWidgetCenter.IsVisible = false; @@ -1298,7 +1245,6 @@ public partial class MainWindow NetworkSpeedWidgetLeft.SetTransparentBackground(_networkSpeedTransparentBackground); NetworkSpeedWidgetLeft.SetDisplayMode(_networkSpeedDisplayMode); } - // 或者移到右侧 else if (CanAddComponentAtPosition("Right")) { NetworkSpeedWidgetCenter.IsVisible = false; @@ -1306,7 +1252,6 @@ public partial class MainWindow NetworkSpeedWidgetRight.SetTransparentBackground(_networkSpeedTransparentBackground); NetworkSpeedWidgetRight.SetDisplayMode(_networkSpeedDisplayMode); } - // 如果都无法添加,则隐藏网速 else { NetworkSpeedWidgetCenter.IsVisible = false; @@ -1315,11 +1260,10 @@ public partial class MainWindow } /// - /// 查找可用的替代位置 - /// + /// 闂佸搫琚崕鍙夌珶濮椻偓瀹曪綁顢涘鍕闂佹眹鍔岀€氼厼霉濞戞瑧顩烽柨婵嗗缁夊绱? /// private string? FindAlternativePosition(string originalPosition) { - // 尝试所有可能的位置 + // 闁诲繐绻戠换鍡涙儊椤栫偛绠ラ柍褜鍓熷鍨緞婵犲倽顔夐梺鐓庣-閺咁偄鈻撻幋鐐村鐎广儱娲ㄩ弸? var positions = new[] { "Left", "Center", "Right" }; foreach (var position in positions) { @@ -1332,8 +1276,7 @@ public partial class MainWindow } /// - /// 获取左侧可见组件列表 - /// + /// 闂佸吋鍎抽崲鑼躲亹閸パ屽晠闁挎梹瀵у▍鐘绘煕濞嗘ê鐏ユい顐㈡缁辨帡宕熼鍜佸仺闂佸憡甯楅〃澶愬Υ? /// private List GetVisibleLeftComponents() { var result = new List(); @@ -1348,8 +1291,7 @@ public partial class MainWindow } /// - /// 获取中间可见组件列表 - /// + /// 闂佸吋鍎抽崲鑼躲亹閸ャ劎鈻旀い鎾卞灪閿涚喖鏌涘▎妯虹仴妞ゎ偄妫涚槐鎺楀礋椤忓拋鍋ㄩ梺鍛婂笚椤ㄥ濡? /// private List GetVisibleCenterComponents() { var result = new List(); @@ -1364,8 +1306,7 @@ public partial class MainWindow } /// - /// 获取右侧可见组件列表 - /// + /// 闂佸吋鍎抽崲鑼躲亹閸ヮ剙鐭楅柛蹇撴噺濞呯娀鏌涘▎妯虹仴妞ゎ偄妫涚槐鎺楀礋椤忓拋鍋ㄩ梺鍛婂笚椤ㄥ濡? /// private List GetVisibleRightComponents() { var result = new List(); @@ -1833,17 +1774,17 @@ public partial class MainWindow return; } - var dialog = new ContentDialog + var dialog = new FAContentDialog { - Title = L("desktop.delete_page_confirm.title", "确认删除页面"), - Content = L("desktop.delete_page_confirm.message", "确定要删除当前页面吗?\n\n此操作将删除当前页面上的所有组件,且无法撤销。"), - PrimaryButtonText = L("desktop.delete_page_confirm.close", "取消"), - SecondaryButtonText = L("desktop.delete_page_confirm.primary", "删除"), - DefaultButton = ContentDialogButton.Primary + Title = L("desktop.delete_page_confirm.title", "Delete desktop page"), + Content = L("desktop.delete_page_confirm.message", "This will permanently remove the current desktop page and all widgets placed on it.\n\nThis action cannot be undone."), + PrimaryButtonText = L("desktop.delete_page_confirm.close", "Cancel"), + SecondaryButtonText = L("desktop.delete_page_confirm.primary", "Delete"), + DefaultButton = FAContentDialogButton.Primary }; var result = await dialog.ShowAsync(this); - if (result == ContentDialogResult.Secondary) + if (result == FAContentDialogResult.Secondary) { DeleteCurrentDesktopPage(); } @@ -3938,7 +3879,7 @@ public partial class MainWindow } var point = e.GetPosition(ComponentLibraryWindow); - if (point.Y > 40) // 閺嶅洭顣介弽蹇涚彯鎼达妇瀹虫稉?0px + if (point.Y > 40) // 闂傚倷绀侀幖顐ょ矓閺夋嚚娲敇椤兘鍋撻崒娑氼浄閻庯綆浜滈崬銊╂椤愩垺澶勭紒瀣崄閵囨劙顢涢悙鑼啇闁哄鐗婇崕鎶姐€呴鍕€电痪顓炴噺閻濐亞绱?0px { return; } diff --git a/LanMountainDesktop/Views/MainWindow.DesktopEditing.cs b/LanMountainDesktop/Views/MainWindow.DesktopEditing.cs index 2d0f65f..68f4447 100644 --- a/LanMountainDesktop/Views/MainWindow.DesktopEditing.cs +++ b/LanMountainDesktop/Views/MainWindow.DesktopEditing.cs @@ -12,7 +12,7 @@ using LanMountainDesktop.Theme; namespace LanMountainDesktop.Views; -public partial class MainWindow +public partial class MainWindow : Window { private static readonly TimeSpan DesktopEditCommitAnimationDuration = FluttermotionToken.Standard; private static readonly TimeSpan DesktopEditCancelAnimationDuration = FluttermotionToken.Fast; diff --git a/LanMountainDesktop/Views/MainWindow.DesktopPaging.cs b/LanMountainDesktop/Views/MainWindow.DesktopPaging.cs index 33efce2..83a0204 100644 --- a/LanMountainDesktop/Views/MainWindow.DesktopPaging.cs +++ b/LanMountainDesktop/Views/MainWindow.DesktopPaging.cs @@ -22,7 +22,7 @@ using LanMountainDesktop.Theme; namespace LanMountainDesktop.Views; -public partial class MainWindow +public partial class MainWindow : Window { private const int MinDesktopPageCount = 1; private const int MaxDesktopPageCount = 12; @@ -75,7 +75,7 @@ public partial class MainWindow private int? _desktopPageContextSettlingTargetIndex; private int _desktopPageContextSettleRevision; - // 三指滑动/右键拖动相关 + // 婵犵數鍋為崹鍫曞箰閹间絸鍥箥椤旂懓浜鹃柛顭戝亯婢规ɑ銇勯婊冨妤犵偛顑呴埞鎴﹀窗?闂傚倷绀侀幉锟犳偡閿旂晫绠惧┑鐘叉搐閺嬩焦銇勯幘鍗炵仼缂佺媭鍨堕弻鈥崇暤椤旂厧鏁俊銈呮噺閻撶喖鏌嶉崫鍕灓闁绘帡绠栭弻? private bool _isThreeFingerOrRightDragSwipeActive; private readonly HashSet _activePointerIds = []; @@ -264,7 +264,6 @@ public partial class MainWindow Grid.SetColumn(LauncherPagePanel, 1); Grid.SetRow(LauncherPagePanel, 0); - // 为启动台添加安全边距以确保圆角不被裁剪 var launcherMargin = Math.Clamp(gridMetrics.CellSize * 0.15, 6, 16); LauncherPagePanel.Margin = new Thickness(launcherMargin); LauncherPagePanel.Width = Math.Max(1, pageWidth - launcherMargin * 2); @@ -272,7 +271,7 @@ public partial class MainWindow LauncherPagePanel.MaxWidth = pageWidth - launcherMargin * 2; LauncherPagePanel.MaxHeight = pageHeight - launcherMargin * 2; - // 更新启动台图标布局 + // 闂傚倷绀侀幖顐⒚洪妶澶嬪仱闁靛ň鏅涢拑鐔封攽閻樺弶鎼愰悷娆欓檮閵囧嫰寮介妸銊ヮ棟閻炴氨鍠栧娲川婵犲嫭鍣┑鐘灪閿氶棁澶嬫叏濡炶浜鹃悗娈垮枙缁瑥鐣烽幆閭︽Ь濡炪倕绻戦幐鎶藉箖濮椻偓閹瑩鍩℃担宄邦棜 UpdateLauncherTileLayout(); _desktopSurfacePageWidth = pageWidth; @@ -287,38 +286,29 @@ public partial class MainWindow return; } - // 获取启动台面板的实际可用宽度(减去Padding) var availableWidth = Math.Max(1, LauncherPagePanel.Bounds.Width - 36); // 18px padding on each side - var availableHeight = Math.Max(1, LauncherPagePanel.Bounds.Height - 100); // 预留标题空间 + var availableHeight = Math.Max(1, LauncherPagePanel.Bounds.Height - 100); // 婵犵妲呴崑鍛熆濡皷鍋撳鐓庢珝鐎殿喗濞婇崺鈧い鎺戝閻撴稓鈧箍鍎遍幊蹇涘窗濡眹浜滈柨婵嗘处濞呮洜绱掗鍊熷閻撱倖銇勮箛鎾村珔缂? if (availableWidth <= 1 || availableHeight <= 1) { - // 如果尺寸还未计算,使用默认值 - availableWidth = 600; + // 婵犵數濮烽。浠嬪焵椤掆偓閸熷潡鍩€椤掆偓缂嶅﹪骞冨Ο璇茬窞閻忕偠鍋愰崜銊╂⒑閸涘﹦绠撻悗姘卞厴瀹曠敻鎮㈤悡搴i獓闂佸啿鎼导鎺楀箣濠垫捁鈧寧銇勯幘璺盒e┑顖氥偢閺屻劌鈽夊Ο渚紑闂佸搫妫崜鐔煎蓟閵娿儮妲堟俊顖欒濞堫厽绻濋悽闈涗粶婵炲樊鍙冮獮鍐╃鐎n€晠鏌嶉崫鍕殭缂佹绻濋弻锝夋偐闁秵顎栭梺绋匡攻濞茬喖宕洪埀? availableWidth = 600; availableHeight = 400; } - // 计算最佳图标尺寸 - // 目标:每行显示4-8个图标,根据屏幕宽度调整 + // 闂備浇宕垫慨宕囨閵堝洦顫曢柡鍥ュ灪閸嬧晛鈹戦悩瀹犲閻庢艾顦甸弻宥堫檨闁告挻宀搁獮蹇涘川閺夋垹顦ㄩ梺鍛婄懃椤﹂亶銆呴銏♀拺闁告繂瀚瓭濠电偛鐪伴崐婵嗩嚕娴兼潙纾兼繝褎鍎虫禍? // 闂傚倷鑳堕崕鐢稿疾閳哄懎绐楁俊銈呮噺閸嬪鏌ㄥ┑鍡╂Ч闁哄拋鍓氶幈銊ヮ潨閸℃绠诲┑鈥崇湴閸旀垿骞冪捄琛℃婵☆垳绮幏鍗炩攽閳藉棗鐏犳い锕佷含閸?-8婵犵數鍋為崹鍫曞箹閳哄倻顩叉繝濠傚幘閻熼偊娼ㄩ柍褜鍓欓锝嗙鐎n亞鍊炴俊鐐差儏濞寸兘藝椤曗偓濮婃椽宕崟顓夈儲銇勯銏╂Ц闁伙絽鐏氶幏鍛姜閻楀牆濯伴梻濠庡亜濞诧箓骞愭ィ鍐炬晩閹兼番鍔嶉崐鐢电棯椤撶偞鍣烘い銉ヮ樀閹鎮烽幍顕嗙礊闂佺懓顨庨崑濠傜暦濮椻偓閸╋繝宕掑☉鍗炴櫔 const int minColumns = 4; const int maxColumns = 8; - const double targetAspectRatio = 1.2; // 图标宽高比 - - // 计算每列可以显示的图标数量 + const double targetAspectRatio = 1.2; // 闂傚倷鐒﹂幃鍫曞磿閹惰棄纾婚柟鍓х帛閸嬪鏌ㄥ┑鍡樼闁稿鎹囬弻鍛槈濮樿京鍘梻浣虹帛缁诲秹宕伴弽顒夋毎? var optimalColumnCount = Math.Clamp((int)Math.Floor(availableWidth / 120), minColumns, maxColumns); - // 根据列数计算图标尺寸 var tileWidth = Math.Floor(availableWidth / optimalColumnCount) - 12; // 12px spacing - var tileHeight = Math.Min(tileWidth / targetAspectRatio, availableHeight / 4); // 至少显示4行 - - // 确保最小尺寸 - tileWidth = Math.Max(tileWidth, 100); + var tileHeight = Math.Min(tileWidth / targetAspectRatio, availableHeight / 4); // 闂傚倷鑳堕崢褔宕查弻銉ョ柈闁秆勵殕閸庡秵銇勯弽顐粶闁告瑥锕弻娑㈠箻濡炵偓顦风紒?闂? + // 缂傚倷鑳堕搹搴ㄥ矗鎼淬劌绐楅柡鍥╁У瀹曞弶鎱ㄥΟ鎸庣【閻庢艾顦甸弻宥堫檨闁告挻绋掔粋宥咁潰瀹€鈧悿鈧梺瑙勫劤閻°劑锝為崨瀛樼厽? tileWidth = Math.Max(tileWidth, 100); tileHeight = Math.Max(tileHeight, 80); - // 更新WrapPanel的Item尺寸 - LauncherRootTilePanel.Width = availableWidth; + // 闂傚倷绀侀幖顐⒚洪妶澶嬪仱闁靛ň鏅涢拑鐔封攽閸屻倖杈渁pPanel闂傚倷鐒﹂惇褰掑礉瀹€鍕惞婵帞妫渕闂備浇顕х换鎰崲閹版澘绠规い鎰跺瘜閺? LauncherRootTilePanel.Width = availableWidth; - // 更新所有子元素的尺寸 + // 闂傚倷绀侀幖顐⒚洪妶澶嬪仱闁靛ň鏅涢拑鐔封攽閻樺弶鎼愮紒鐘劦閺屽秷顧侀柛鎾跺枎椤曪綁宕归銏㈢獮婵犵數濮寸€氼參骞夐妶澶嬧拺缂佸娉曠粻浼存煕閻旂顥嬬紒顔肩墕閻f繈宕熼鈧崜顓㈡⒑閸涘﹥澶勯柛瀣噹鍗遍柍褜鍓熼弻? foreach (var child in LauncherRootTilePanel.Children) { if (child is Button button) @@ -487,7 +477,7 @@ public partial class MainWindow return; } - // 如果在组件编辑模式下点击空白区域,取消选中(组件或启动台图标) + // 婵犵數濮烽。浠嬪焵椤掆偓閸熷潡鍩€椤掆偓缂嶅﹪骞冨Ο璇茬窞闁归偊鍓氬畵宥夋⒑闂堟丹娑㈠川椤栨粌甯掓繝鐢靛仜椤曨厽鎱ㄧ€涙ɑ娅犻幖杈剧稻椤洘銇勮箛鎾村櫤缂傚秴娲弻鐔衡偓鐢告櫜鏉╃懓霉閿濆懎顥忛柛銈嗘礋閻擃偊宕惰閹癸綁鏌i悢鍛婂磳闁哄矉缍侀獮鍥敊閽樺鐣梻浣规偠閸娿倝宕板鍗炲灊婵鍩栭幆鐐烘偡濞嗗繐顏村ù鐘讳憾濮婃椽宕ㄦ繝鍕吂闂佸湱鈷堥崑濠囧箖閳ユ枼鏋庨柟鎯х摠濞呮牠鏌h箛鏇炰哗婵☆偄瀚濠囧箰鎼达絿顔曢梺鐟扮摠缁诲嫭鏅堕敃鍌涚厓鐟滄粓宕滃▎鎾嶅洭顢氶埀顒勫箠濞嗘挸绠i柨鏃囧Г濞呮牠姊洪崜鎻掍簴闁搞劌顭烽幆宀€鈧綆鈧垹缍婇幃鈺呭传閸曨厼甯块梻浣规偠閸斿﹪宕濋幋婵堟殾闁靛鏅╅弫宥嗘叏濮楀棗鍔俊銈呮噺閻撴洘绻涢崱妯哄缂佽泛寮剁换娑氣偓娑欙公閼拌法鈧鍠曠划娆忕暦閼告妲归幖杈剧秵濡? if (_isComponentLibraryOpen && (_selectedDesktopComponentHost is not null || _selectedLauncherTileButton is not null)) { @@ -504,7 +494,6 @@ public partial class MainWindow return; } - // 检查三指滑动功能是否启用 var appSnapshot = _settingsFacade.Settings.LoadSnapshot(SettingsScope.App); var isThreeFingerSwipeEnabled = appSnapshot.EnableThreeFingerSwipe; @@ -513,23 +502,20 @@ public partial class MainWindow var isRightButtonPressed = currentPoint.Properties.IsRightButtonPressed; var isLeftButtonPressed = currentPoint.Properties.IsLeftButtonPressed; - // 处理三指滑动/右键拖动模式 + // 婵犵數濮伴崹鐓庘枖濞戞埃鍋撳鐓庢珝妤犵偛鍟换婵嬪礃椤忎焦鐏冨┑鐘灱濞夋盯顢栭崨瀛樺剨閻熸瑥瀚弧鈧繝鐢靛Т閸燁偊鎮橀妷銉㈡斀?闂傚倷绀侀幉锟犳偡閿旂晫绠惧┑鐘叉搐閺嬩焦銇勯幘鍗炵仼缂佺媭鍨堕弻鈥崇暤椤旂厧鏁俊銈勬缁诲棙銇勯弽銊d粶闁稿鎸搁悾鐑藉炊閳哄﹥鏁? if (isThreeFingerSwipeEnabled) { - // 跟踪活跃指针 if (isLeftButtonPressed || isRightButtonPressed) { _activePointerIds.Add(pointerId); } - // 判断是否是三指滑动或右键拖动 var isThreeFinger = _activePointerIds.Count >= 3; var isRightDrag = isRightButtonPressed; if (isThreeFinger || isRightDrag) { - // 三指/右键拖动模式:跳过所有组件交互检查,直接开始滑动 - ClearDesktopPageContextSettle(refreshContext: false); + // 婵犵數鍋為崹鍫曞箰閹间絸鍥箥椤旂懓浜?闂傚倷绀侀幉锟犳偡閿旂晫绠惧┑鐘叉搐閺嬩焦銇勯幘鍗炵仼缂佺媭鍨堕弻鈥崇暤椤旂厧鏁俊銈勬缁诲棙銇勯弽銊d粶闁稿鎸搁悾鐑藉炊閳哄﹥鏁ら梻鍌欑劍鐎笛呯矙閹烘挾鈹嶆繛宸簼閸婂鏌ㄩ弮鍥撳ù婧垮€濋弻娑㈠Ψ閿濆懎顬堝銈忕稻閻擄繝寮婚敓鐘查唶婵犲灚鍔栨缂傚倷绶¢崰鏍矓閻㈢數鐭夐柟鐑橆殔鐎氬鏌涢…鎴濅簻闁衡偓椤撶喓绠鹃悗娑欘焽閻鎮介娑辨疁閽樼喖鏌涘☉娆愮稇闁藉啰鍠栭弻鏇熷緞濡櫣浠紓浣插亾濠㈣埖鍔栭悡鐔兼煃鏉炴媽鍏岄柟鐣屽█閹粙顢涘☉娆戠▏濡炪倖娲╃紞渚€宕洪埀顒併亜閹哄秶鍔嶉柛娆忕箻閹鏁愭惔鈥茬敖闂佽鐏氶崝鎴﹀蓟? ClearDesktopPageContextSettle(refreshContext: false); _isThreeFingerOrRightDragSwipeActive = true; _isDesktopSwipeActive = true; _isDesktopSwipeDirectionLocked = false; @@ -540,14 +526,12 @@ public partial class MainWindow _desktopSwipeLastTimestamp = Stopwatch.GetTimestamp(); _desktopSwipeBaseOffset = -_currentDesktopSurfaceIndex * _desktopSurfacePageWidth; - // 标记事件已处理,防止组件响应 - e.Handled = true; + // 闂傚倷绀侀幖顐ょ矓閺夋嚚娲煛閸滀焦鏅╅梺鎼炲劘閸斿酣銆呴弻銉﹀€甸柨婵嗗€瑰▍鍡樸亜閹邦喗娅曢柍褜鍓涢幊鎾诲箟闄囬妵鎰板礃椤斻垹娲崺锟犲川椤旈棿鍝楅梻浣虹《濡插懘宕㈤崜褏鐭嗗鑸靛姈閳锋帡鏌涢幇鈺佸缂佺嫏鍕╀簻闁圭儤鎸鹃妴鎺旂磼鏉堛劌娴€规洜鍠栭、鏃堝椽娴i晲缂撻梻鍌欑閹诧紕鎹㈤崒婊呯煋閻庡灚鐡曟慨? e.Handled = true; return; } } - // 原有单指滑动逻辑 - if (IsInteractivePointerSource(e.Source)) + // 闂傚倷绀侀幉锟犫€﹂崶顒€绐楅柟閭﹀墾閼板灝銆掑锝呬壕閻庤娲╃换婵嗩嚕閹绢喗鍋勫瀣閳诲本绻濋悽闈浶㈤柨鏇樺劦瀹曞綊宕归锝呭伎闂佸啿鎼幊蹇涙倿婵犳碍鐓涢柛鏇ㄥ亞缁犳娊鎮? if (IsInteractivePointerSource(e.Source)) { return; } @@ -811,7 +795,6 @@ public partial class MainWindow private void OnDesktopPagesPointerReleased(object? sender, PointerReleasedEventArgs e) { - // 清理活跃指针 var pointerId = e.Pointer?.Id ?? 0; _activePointerIds.Remove(pointerId); @@ -823,7 +806,6 @@ public partial class MainWindow private void OnDesktopPagesPointerCaptureLost(object? sender, PointerCaptureLostEventArgs e) { - // 清理活跃指针 var pointerId = e.Pointer?.Id ?? 0; _activePointerIds.Remove(pointerId); @@ -898,13 +880,12 @@ public partial class MainWindow var hasDistanceIntent = absDeltaX >= distanceThreshold && absDeltaX > absDeltaY * 1.05; var hasVelocityIntent = Math.Abs(_desktopSwipeVelocityX) >= velocityThreshold; - // 检查:三指/右键拖动 && 在第一页 && 向右滑动 + // 濠电姷顣藉Σ鍛村磻閳ь剟鏌涚€n偅宕岄柡宀嬬磿娴狅妇鎷犻幓鎺懶ョ紓鍌欐祰娴滎剚鏅跺Δ鍐煓濠㈣泛顑呯欢鐐烘倵閿濆簼绨芥俊?闂傚倷绀侀幉锟犳偡閿旂晫绠惧┑鐘叉搐閺嬩焦銇勯幘鍗炵仼缂佺媭鍨堕弻鈥崇暤椤旂厧鏁?&& 闂傚倷绶氬鑽ゆ嫻閻旂厧绀夐幖鎼厛閺佸嫰鏌涢妷锝呭闁崇粯妫冮弻宥堫檨闁告挻宀告俊?&& 闂傚倷绀侀幉锛勫枈瀹ュ鍨傚ù锝呭暔娴滃湱绱掔€n偒鍎ラ柣鎾卞劦閺岀喓鈧稒顭囩粻鎾舵偖? if (wasThreeFingerOrRightDrag && _currentDesktopSurfaceIndex == 0 && - deltaX > 0 && // 向右滑动 + deltaX > 0 && // 闂傚倷绀侀幉锛勫枈瀹ュ鍨傚ù锝呭暔娴滃湱绱掔€n偒鍎ラ柣鎾卞劦閺岀喓鈧稒顭囩粻鎾舵偖? (hasDistanceIntent || hasVelocityIntent)) { - // 最小化到 Windows 桌面 if (Application.Current is App app) { app.HideMainWindowToTray(this, "ThreeFingerOrRightDragSwipe"); @@ -998,8 +979,7 @@ public partial class MainWindow string.Empty)); } - // 在图标渲染完成后,应用布局计算 - Dispatcher.UIThread.Post(() => UpdateLauncherTileLayout(), DispatcherPriority.Background); + // 闂傚倷绶氬鑽ゆ嫻閻旂厧绀夐悘鐐电叓閻熼偊娼ㄩ柍褜鍓欓锝嗙鐎n亞鍊為梺闈涱煬閻撳牆煤椤掑嫭鈷戦柛婵嗗濠€浼存煙閸涘﹥鍊愰柟顕€绠栭、妤呭礋椤愩値鍚呴梻浣哥秺閸嬪﹪宕滃璺虹9闁汇垹鎲¢悡銉︾箾閹寸儐鐒鹃悗姘缁辨帡濡搁敂鎯у绩闂佽鍠曠划娆愪繆閹间礁唯鐟滄粍瀵煎畝鍕厽闊洦娲栨禍褰掓煕鐎n偅宕岄柟顔款潐缁楃喐绻濋崟顓ㄧ吹闂? Dispatcher.UIThread.Post(() => UpdateLauncherTileLayout(), DispatcherPriority.Background); } private Button CreateLauncherFolderTile(StartMenuFolderNode folder) @@ -1063,8 +1043,7 @@ public partial class MainWindow BorderThickness = new Thickness(0), Margin = new Thickness(0, 0, 12, 12), CornerRadius = new CornerRadius(20), - Child = panel - // 不设置固定 Width 和 Height,由 UpdateLauncherTileLayout 动态设置 + Child = panel, }; } @@ -1145,11 +1124,8 @@ public partial class MainWindow BorderBrush = Brushes.Transparent, CornerRadius = new CornerRadius(20), Padding = new Thickness(10), - Content = content - // 不设置固定 Width 和 Height,由 UpdateLauncherTileLayout 动态设置 + Content = content, }; - - // 根据设置决定是否显示背景 if (_showLauncherTileBackground) { button.Classes.Add("glass-panel"); @@ -1415,7 +1391,7 @@ public partial class MainWindow : fileName; } - private SettingsExpanderItem CreateLauncherHiddenItemRow(LauncherHiddenItemView hiddenItem) + private FASettingsExpanderItem CreateLauncherHiddenItemRow(LauncherHiddenItemView hiddenItem) { var typeText = hiddenItem.Kind == LauncherEntryKind.Folder ? L("settings.launcher.hidden_type_folder", "Folder") @@ -1430,7 +1406,7 @@ public partial class MainWindow BorderThickness = new Thickness(0), Tag = new LauncherHiddenItemToken(hiddenItem.Kind, hiddenItem.Key) }; - restoreButton.Content = new FluentIcons.Avalonia.Fluent.SymbolIcon + restoreButton.Content = new FluentIcons.Avalonia.SymbolIcon { Symbol = FluentIcons.Common.Symbol.Eye, IconVariant = FluentIcons.Common.IconVariant.Regular, @@ -1441,7 +1417,7 @@ public partial class MainWindow ToolTip.SetTip(restoreButton, L("settings.launcher.restore_button", "Unhide")); restoreButton.Click += OnRestoreLauncherHiddenItemClick; - return new SettingsExpanderItem + return new FASettingsExpanderItem { Content = hiddenItem.DisplayName, Description = typeText, @@ -1451,23 +1427,17 @@ public partial class MainWindow }; } - private IconSource CreateLauncherHiddenItemIconSource(LauncherHiddenItemView hiddenItem) + private FAIconSource? CreateLauncherHiddenItemIconSource(LauncherHiddenItemView hiddenItem) { if (hiddenItem.IconBitmap is not null) { - return new ImageIconSource + return new FAImageIconSource { Source = hiddenItem.IconBitmap }; } - return new FluentIcons.Avalonia.Fluent.SymbolIconSource - { - Symbol = hiddenItem.Kind == LauncherEntryKind.Folder - ? FluentIcons.Common.Symbol.Folder - : FluentIcons.Common.Symbol.Apps, - IconVariant = FluentIcons.Common.IconVariant.Regular - }; + return null; } private void OnRestoreLauncherHiddenItemClick(object? sender, RoutedEventArgs e) @@ -1696,7 +1666,7 @@ public partial class MainWindow Content = content }; - // 根据设置决定是否显示背景 + // 闂傚倷绀侀幖顐ょ矓閻戞枻缍栧璺猴功閺嗐倕霉閿濆洤鍔嬪┑顖氥偢閺屾盯骞樺Δ鈧幊蹇涙倵椤撱垺鈷戦柛娑橈工婵洭鏌涢悢閿嬪仴闁诡喚鍋撻妶锝夊礃閵娿儱鎸ゆ俊鐐€栭悧妤冨枈瀹ュ纾垮┑鐘叉处閻撴盯鏌涢弴銊ヤ簻闁抽攱妫冮弻鏇㈠炊閵娿儱鎽甸梺纭呮珪椤ㄥ牊绂掗敃鍌涘€锋い鎺戝€哥拋? if (_showLauncherTileBackground) { button.Classes.Add("glass-panel"); @@ -1775,7 +1745,7 @@ public partial class MainWindow Content = content }; - // 根据设置决定是否显示背景 + // 闂傚倷绀侀幖顐ょ矓閻戞枻缍栧璺猴功閺嗐倕霉閿濆洤鍔嬪┑顖氥偢閺屾盯骞樺Δ鈧幊蹇涙倵椤撱垺鈷戦柛娑橈工婵洭鏌涢悢閿嬪仴闁诡喚鍋撻妶锝夊礃閵娿儱鎸ゆ俊鐐€栭悧妤冨枈瀹ュ纾垮┑鐘叉处閻撴盯鏌涢弴銊ヤ簻闁抽攱妫冮弻鏇㈠炊閵娿儱鎽甸梺纭呮珪椤ㄥ牊绂掗敃鍌涘€锋い鎺戝€哥拋? if (_showLauncherTileBackground) { button.Classes.Add("glass-panel"); diff --git a/LanMountainDesktop/Views/MainWindow.RenderBackend.cs b/LanMountainDesktop/Views/MainWindow.RenderBackend.cs index aef13b9..504968e 100644 --- a/LanMountainDesktop/Views/MainWindow.RenderBackend.cs +++ b/LanMountainDesktop/Views/MainWindow.RenderBackend.cs @@ -1,9 +1,10 @@ using System; +using Avalonia.Controls; using LanMountainDesktop.Services; namespace LanMountainDesktop.Views; -public partial class MainWindow +public partial class MainWindow : Window { private void UpdateCurrentRenderBackendStatus() { diff --git a/LanMountainDesktop/Views/MainWindow.SettingsHardCut.Stubs.cs b/LanMountainDesktop/Views/MainWindow.SettingsHardCut.Stubs.cs index a0a3772..e44b96b 100644 --- a/LanMountainDesktop/Views/MainWindow.SettingsHardCut.Stubs.cs +++ b/LanMountainDesktop/Views/MainWindow.SettingsHardCut.Stubs.cs @@ -20,13 +20,13 @@ using LanMountainDesktop.Views.Components; namespace LanMountainDesktop.Views; -public partial class MainWindow +public partial class MainWindow : Window { private TextBlock? CurrentRenderBackendLabelTextBlock => this.FindControl("CurrentRenderBackendLabelTextBlock"); private TextBlock? CurrentRenderBackendValueTextBlock => this.FindControl("CurrentRenderBackendValueTextBlock"); private TextBlock? CurrentRenderBackendImplementationTextBlock => this.FindControl("CurrentRenderBackendImplementationTextBlock"); private ComboBox? TimeZoneComboBox => this.FindControl("TimeZoneComboBox"); - private SettingsExpander? LauncherHiddenItemsSettingsExpander => this.FindControl("LauncherHiddenItemsSettingsExpander"); + private FASettingsExpander? LauncherHiddenItemsSettingsExpander => this.FindControl("LauncherHiddenItemsSettingsExpander"); private TextBlock? LauncherHiddenItemsEmptyTextBlock => this.FindControl("LauncherHiddenItemsEmptyTextBlock"); private void OnSettingsChanged(object? sender, SettingsChangedEvent e) @@ -38,13 +38,12 @@ public partial class MainWindow return; } - // 组件实例范围的设置变更不应触发整个桌面重新加载(比如翻页保存图片索引) - if (e.Scope == SettingsScope.ComponentInstance) + // 缁勪欢瀹炰緥鑼冨洿鐨勮缃彉鏇翠笉搴旇Е鍙戞暣涓闈㈤噸鏂板姞杞斤紙姣斿缈婚〉淇濆瓨鍥剧墖绱㈠紩锛? if (e.Scope == SettingsScope.ComponentInstance) { return; } - // 启动台设置变化时,重新渲染启动台图标 + // 鍚姩鍙拌缃彉鍖栨椂锛岄噸鏂版覆鏌撳惎鍔ㄥ彴鍥炬爣 if (e.Scope == SettingsScope.Launcher && e.ChangedKeys is { Count: > 0 }) { var changedKeys = e.ChangedKeys.ToArray(); diff --git a/LanMountainDesktop/Views/MainWindow.SingleInstanceNotice.cs b/LanMountainDesktop/Views/MainWindow.SingleInstanceNotice.cs index 02c5ba4..4c5eb1b 100644 --- a/LanMountainDesktop/Views/MainWindow.SingleInstanceNotice.cs +++ b/LanMountainDesktop/Views/MainWindow.SingleInstanceNotice.cs @@ -1,11 +1,12 @@ using System.Threading.Tasks; +using Avalonia.Controls; using Avalonia.Threading; using FluentAvalonia.UI.Controls; using LanMountainDesktop.Services; namespace LanMountainDesktop.Views; -public partial class MainWindow +public partial class MainWindow : Window { private bool _isSingleInstancePromptVisible; @@ -38,14 +39,14 @@ public partial class MainWindow try { - var dialog = new ContentDialog + var dialog = new FAContentDialog { - Title = L("single_instance.notice.title", "应用已经运行"), + Title = L("single_instance.notice.title", "Already running"), Content = L( "single_instance.notice.description", - "应用已经运行,无需多次点击打开。"), - PrimaryButtonText = L("single_instance.notice.button", "确定"), - DefaultButton = ContentDialogButton.Primary + "LanMountainDesktop is already running. The existing window will stay active, so no new instance was started."), + PrimaryButtonText = L("single_instance.notice.button", "OK"), + DefaultButton = FAContentDialogButton.Primary }; await dialog.ShowAsync(this); diff --git a/LanMountainDesktop/Views/MainWindow.axaml b/LanMountainDesktop/Views/MainWindow.axaml index e8dd12e..d193249 100644 --- a/LanMountainDesktop/Views/MainWindow.axaml +++ b/LanMountainDesktop/Views/MainWindow.axaml @@ -3,7 +3,7 @@ xmlns:vm="using:LanMountainDesktop.ViewModels" xmlns:ui="using:FluentAvalonia.UI.Controls" xmlns:fi="using:FluentIcons.Avalonia" - xmlns:ic="using:FluentIcons.Avalonia.Fluent" + xmlns:ic="using:FluentIcons.Avalonia" xmlns:mi="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia" xmlns:comp="using:LanMountainDesktop.Views.Components" @@ -15,7 +15,7 @@ x:Class="LanMountainDesktop.Views.MainWindow" x:DataType="vm:MainWindowViewModel" WindowState="FullScreen" - SystemDecorations="None" + WindowDecorations="None" CanResize="False" UseLayoutRounding="True" Foreground="{DynamicResource AdaptiveTextPrimaryBrush}" @@ -236,7 +236,7 @@ - + - + - + - + diff --git a/LanMountainDesktop/Views/SettingsPages/AboutSettingsPage.axaml b/LanMountainDesktop/Views/SettingsPages/AboutSettingsPage.axaml index c26f2e7..beee6de 100644 --- a/LanMountainDesktop/Views/SettingsPages/AboutSettingsPage.axaml +++ b/LanMountainDesktop/Views/SettingsPages/AboutSettingsPage.axaml @@ -1,19 +1,27 @@ - - + - + @@ -36,7 +44,7 @@ - - + - + - + Lincube - - + + diff --git a/LanMountainDesktop/Views/SettingsPages/AboutSettingsPage.axaml.cs b/LanMountainDesktop/Views/SettingsPages/AboutSettingsPage.axaml.cs index 141317b..40fd3cd 100644 --- a/LanMountainDesktop/Views/SettingsPages/AboutSettingsPage.axaml.cs +++ b/LanMountainDesktop/Views/SettingsPages/AboutSettingsPage.axaml.cs @@ -74,6 +74,9 @@ public partial class AboutSettingsPage : SettingsPageBase private void OnAboutHeroCardPointerPressed(object? sender, PointerPressedEventArgs e) { + _ = sender; + _ = e; + var now = DateTime.UtcNow; var elapsed = now - _lastHeroCardClickTime; @@ -111,25 +114,23 @@ public partial class AboutSettingsPage : SettingsPageBase } else if (remaining <= 2) { - Debug.WriteLine($"[AboutSettingsPage] 再点击 {remaining} 次即可启用开发者模式。"); + Debug.WriteLine($"[AboutSettingsPage] {remaining} tap(s) remaining before developer mode unlocks."); } } private async void PromptEnableDevMode(ISettingsFacadeService settingsFacade) { - var dialog = new ContentDialog + var dialog = new FAContentDialog { - Title = "启用开发者模式", - Content = "开发者模式提供了插件调试、热重载等高级功能,仅供开发和调试用途。\n\n" + - "请注意:开发者不对以非开发用途使用此功能造成的任何后果负责,也不接受以非开发用途使用时产生的 Bug 反馈。\n\n" + - "确定要启用开发者模式吗?", - PrimaryButtonText = "启用", - CloseButtonText = "取消", - DefaultButton = ContentDialogButton.Close + Title = "Enable developer mode", + Content = "Developer mode exposes experimental settings, diagnostics, and local plugin debugging options.\n\nUse it only when you are actively testing or troubleshooting the desktop host.", + PrimaryButtonText = "Enable", + CloseButtonText = "Not now", + DefaultButton = FAContentDialogButton.Close }; var result = await dialog.ShowAsync(); - if (result != ContentDialogResult.Primary) + if (result != FAContentDialogResult.Primary) { return; } diff --git a/LanMountainDesktop/Views/SettingsPages/AppearanceSettingsPage.axaml b/LanMountainDesktop/Views/SettingsPages/AppearanceSettingsPage.axaml index 61d8d73..6ae5d35 100644 --- a/LanMountainDesktop/Views/SettingsPages/AppearanceSettingsPage.axaml +++ b/LanMountainDesktop/Views/SettingsPages/AppearanceSettingsPage.axaml @@ -1,9 +1,9 @@ - @@ -13,12 +13,12 @@ Text="{Binding ThemeHeader}" Margin="0,0,0,4" /> - - - - - + + + + @@ -28,24 +28,24 @@ - - + + - - - - - + + + + + - - + + - - - - - + + + + @@ -55,15 +55,15 @@ - - + + - - - - - + + + + @@ -73,15 +73,15 @@ - - + + - - - - - + + + + - - + + diff --git a/LanMountainDesktop/Views/SettingsPages/ComponentsSettingsPage.axaml b/LanMountainDesktop/Views/SettingsPages/ComponentsSettingsPage.axaml index 47099c4..ec7628a 100644 --- a/LanMountainDesktop/Views/SettingsPages/ComponentsSettingsPage.axaml +++ b/LanMountainDesktop/Views/SettingsPages/ComponentsSettingsPage.axaml @@ -1,9 +1,9 @@ - @@ -12,12 +12,12 @@ Text="{Binding ComponentsHeader}" Margin="0,0,0,4" /> - - - - - + + + + @@ -33,8 +33,8 @@ VerticalAlignment="Center" HorizontalAlignment="Right" /> - - + + @@ -50,8 +50,8 @@ VerticalAlignment="Center" HorizontalAlignment="Right" /> - - + + @@ -66,19 +66,19 @@ - - + + - - - - - + + + + - - + + diff --git a/LanMountainDesktop/Views/SettingsPages/DevSettingsPage.axaml b/LanMountainDesktop/Views/SettingsPages/DevSettingsPage.axaml index 343a26e..8c19024 100644 --- a/LanMountainDesktop/Views/SettingsPages/DevSettingsPage.axaml +++ b/LanMountainDesktop/Views/SettingsPages/DevSettingsPage.axaml @@ -1,84 +1,92 @@ - - - + Title="Preview and developer features" + Message="These options are intended for debugging, diagnostics, and local plugin development." + Margin="0,0,0,16"> + + + + - - - - - + + + + + - - + + - - - - - + + + + + - - + + - - - - - + + + + + - - + + - - - - - + + + + + - - + + - - - - - - - + + + + + + + - + + @@ -87,23 +95,25 @@ Text="LMD_DEV_PLUGIN=<path>" TextWrapping="Wrap" /> - + + + Text="--dev-mode / -dev Enable developer mode startup helpers." /> + Text="--hot-reload / -hr Enable hot reload for development builds." /> - - - + + diff --git a/LanMountainDesktop/Views/SettingsPages/DevSettingsPage.axaml.cs b/LanMountainDesktop/Views/SettingsPages/DevSettingsPage.axaml.cs index fa16ae0..65fc06c 100644 --- a/LanMountainDesktop/Views/SettingsPages/DevSettingsPage.axaml.cs +++ b/LanMountainDesktop/Views/SettingsPages/DevSettingsPage.axaml.cs @@ -6,7 +6,7 @@ namespace LanMountainDesktop.Views.SettingsPages; [SettingsPageInfo( "dev", - "开发者", + "Developer", SettingsPageCategory.Dev, IconKey = "DeveloperBoard", SortOrder = 0, diff --git a/LanMountainDesktop/Views/SettingsPages/GeneralSettingsPage.axaml b/LanMountainDesktop/Views/SettingsPages/GeneralSettingsPage.axaml index 94bba06..e1021b4 100644 --- a/LanMountainDesktop/Views/SettingsPages/GeneralSettingsPage.axaml +++ b/LanMountainDesktop/Views/SettingsPages/GeneralSettingsPage.axaml @@ -1,23 +1,22 @@ - - - - - - - + + + + + @@ -27,15 +26,15 @@ - - + + - - - - - + + + + @@ -45,14 +44,14 @@ - - + + - - - - - + + + + + - - + + @@ -79,13 +78,13 @@ Text="{Binding RuntimeHeader}" Margin="0,0,0,4" /> - - - - - + + + + @@ -95,48 +94,47 @@ - - + + - - + + - - - - - + + + + - - + + - - - - - + + + + - - + + - - - - - + + + + + - - - + + diff --git a/LanMountainDesktop/Views/SettingsPages/LauncherSettingsPage.axaml b/LanMountainDesktop/Views/SettingsPages/LauncherSettingsPage.axaml index e6ce7c0..8394886 100644 --- a/LanMountainDesktop/Views/SettingsPages/LauncherSettingsPage.axaml +++ b/LanMountainDesktop/Views/SettingsPages/LauncherSettingsPage.axaml @@ -1,9 +1,9 @@ - @@ -56,14 +56,14 @@ Text="{Binding AppearanceHeader}" Margin="0,0,0,4" /> - - - - - + + + + @@ -73,21 +73,21 @@ - - + + - - - - - + + + + - - - - - + + + + - - + + diff --git a/LanMountainDesktop/Views/SettingsPages/PluginCatalogDetailDrawer.axaml b/LanMountainDesktop/Views/SettingsPages/PluginCatalogDetailDrawer.axaml index aad25d8..a7a6404 100644 --- a/LanMountainDesktop/Views/SettingsPages/PluginCatalogDetailDrawer.axaml +++ b/LanMountainDesktop/Views/SettingsPages/PluginCatalogDetailDrawer.axaml @@ -3,7 +3,7 @@ xmlns:vm="using:LanMountainDesktop.ViewModels" xmlns:mdxaml="https://github.com/whistyun/Markdown.Avalonia" xmlns:helpers="using:LanMountainDesktop.Helpers" - xmlns:fi="using:FluentIcons.Avalonia.Fluent" + xmlns:fi="using:FluentIcons.Avalonia" x:Class="LanMountainDesktop.Views.SettingsPages.PluginCatalogDetailDrawer" x:DataType="vm:PluginCatalogDetailViewModel"> diff --git a/LanMountainDesktop/Views/SettingsPages/PluginCatalogSettingsPage.axaml b/LanMountainDesktop/Views/SettingsPages/PluginCatalogSettingsPage.axaml index d58e494..7087971 100644 --- a/LanMountainDesktop/Views/SettingsPages/PluginCatalogSettingsPage.axaml +++ b/LanMountainDesktop/Views/SettingsPages/PluginCatalogSettingsPage.axaml @@ -1,32 +1,32 @@ - - - - - - + + + + - - + + - - + + diff --git a/LanMountainDesktop/Views/SettingsPages/StudySettingsPage.axaml b/LanMountainDesktop/Views/SettingsPages/StudySettingsPage.axaml index fa367a9..60ce53e 100644 --- a/LanMountainDesktop/Views/SettingsPages/StudySettingsPage.axaml +++ b/LanMountainDesktop/Views/SettingsPages/StudySettingsPage.axaml @@ -1,36 +1,36 @@ - - - + - - - - + + + + - - + + - - + - - - + + + - - + + @@ -52,10 +52,10 @@ TickFrequency="20" IsSnapToTickEnabled="True" /> - + - - + + @@ -77,27 +77,27 @@ TickFrequency="5" IsSnapToTickEnabled="True" /> - + - - + + - - + + - - + - - - + + + - - + + @@ -119,10 +119,10 @@ TickFrequency="5" IsSnapToTickEnabled="True" /> - + - - + + @@ -144,10 +144,10 @@ TickFrequency="5" IsSnapToTickEnabled="True" /> - + - - + + @@ -169,10 +169,10 @@ TickFrequency="5" IsSnapToTickEnabled="True" /> - + - - + + @@ -194,10 +194,10 @@ TickFrequency="1" IsSnapToTickEnabled="True" /> - + - - + + - + - - + + - - + + - - + - - - + + + - - + + - + - - + + - - + + - - + - - - + + + - - + + - + - - + + @@ -313,10 +313,10 @@ TickFrequency="5" IsSnapToTickEnabled="True" /> - + - - + + @@ -338,8 +338,8 @@ TickFrequency="1" IsSnapToTickEnabled="True" /> - - + + - \ No newline at end of file + diff --git a/LanMountainDesktop/Views/SettingsPages/UpdateSettingsPage.axaml b/LanMountainDesktop/Views/SettingsPages/UpdateSettingsPage.axaml index d855851..2ebf7eb 100644 --- a/LanMountainDesktop/Views/SettingsPages/UpdateSettingsPage.axaml +++ b/LanMountainDesktop/Views/SettingsPages/UpdateSettingsPage.axaml @@ -1,9 +1,9 @@ - @@ -163,13 +163,13 @@ Margin="0,0,0,18" Text="{Binding PreferencesDescription}" /> - - - - - + + + + @@ -179,24 +179,24 @@ - - + - - - - - + + + + + - - - - - + + + + @@ -206,16 +206,16 @@ - - + + - - - - - + + + + @@ -225,23 +225,23 @@ - - + + - - - - - - + + + + - - + + diff --git a/LanMountainDesktop/Views/SettingsPages/WallpaperSettingsPage.axaml b/LanMountainDesktop/Views/SettingsPages/WallpaperSettingsPage.axaml index 9e91059..f76fe93 100644 --- a/LanMountainDesktop/Views/SettingsPages/WallpaperSettingsPage.axaml +++ b/LanMountainDesktop/Views/SettingsPages/WallpaperSettingsPage.axaml @@ -1,35 +1,36 @@ - - - - - - + + - - - + - - - + @@ -39,100 +40,139 @@ - - + - - - - - + + - - - - - - + + + + @@ -248,18 +289,17 @@ - - + + - - - - - - + + + + @@ -269,9 +309,8 @@ - - - + + diff --git a/LanMountainDesktop/Views/SettingsPages/WeatherSettingsPage.axaml b/LanMountainDesktop/Views/SettingsPages/WeatherSettingsPage.axaml index e77f47b..5bf7b2a 100644 --- a/LanMountainDesktop/Views/SettingsPages/WeatherSettingsPage.axaml +++ b/LanMountainDesktop/Views/SettingsPages/WeatherSettingsPage.axaml @@ -1,10 +1,10 @@ - @@ -47,7 +47,7 @@