mirror of
https://github.com/wwiinnddyy/LanMountainDesktop.git
synced 2026-06-27 21:04:27 +08:00
Compare commits
4 Commits
v0.8.3.2
...
cf4b8e2132
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf4b8e2132 | ||
|
|
e8ba847328 | ||
|
|
2156922039 | ||
|
|
e795e9964e |
42
.github/workflows/release.yml
vendored
42
.github/workflows/release.yml
vendored
@@ -66,8 +66,19 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
arch: [x64, x86]
|
include:
|
||||||
name: Build_Windows_${{ matrix.arch }}
|
# 完整版(自包含 .NET 运行时)
|
||||||
|
- arch: x64
|
||||||
|
self_contained: true
|
||||||
|
suffix: ''
|
||||||
|
- arch: x86
|
||||||
|
self_contained: true
|
||||||
|
suffix: ''
|
||||||
|
# 轻盈版(框架依赖,仅 x64)
|
||||||
|
- arch: x64
|
||||||
|
self_contained: false
|
||||||
|
suffix: '-lite'
|
||||||
|
name: Build_Windows_${{ matrix.arch }}${{ matrix.suffix }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
@@ -95,13 +106,16 @@ jobs:
|
|||||||
|
|
||||||
- name: Publish
|
- name: Publish
|
||||||
run: |
|
run: |
|
||||||
|
$selfContained = "${{ matrix.self_contained }}" -eq "true"
|
||||||
|
$publishDir = if ($selfContained) { "publish/windows-${{ matrix.arch }}" } else { "publish/windows-${{ matrix.arch }}-lite" }
|
||||||
|
|
||||||
dotnet publish LanMountainDesktop/LanMountainDesktop.csproj `
|
dotnet publish LanMountainDesktop/LanMountainDesktop.csproj `
|
||||||
-c Release `
|
-c Release `
|
||||||
-o ./publish/windows-${{ matrix.arch }} `
|
-o ./$publishDir `
|
||||||
--self-contained `
|
--self-contained:$selfContained `
|
||||||
-r win-${{ matrix.arch }} `
|
-r win-${{ matrix.arch }} `
|
||||||
-p:PublishSingleFile=false `
|
-p:PublishSingleFile=false `
|
||||||
-p:SelfContained=true `
|
-p:SelfContained=$selfContained `
|
||||||
-p:DebugType=none `
|
-p:DebugType=none `
|
||||||
-p:DebugSymbols=false `
|
-p:DebugSymbols=false `
|
||||||
-p:PublishTrimmed=false `
|
-p:PublishTrimmed=false `
|
||||||
@@ -110,6 +124,9 @@ jobs:
|
|||||||
-p:AssemblyVersion=${{ needs.prepare.outputs.assembly_version }} `
|
-p:AssemblyVersion=${{ needs.prepare.outputs.assembly_version }} `
|
||||||
-p:FileVersion=${{ needs.prepare.outputs.assembly_version }} `
|
-p:FileVersion=${{ needs.prepare.outputs.assembly_version }} `
|
||||||
-p:InformationalVersion=${{ needs.prepare.outputs.informational_version }}
|
-p:InformationalVersion=${{ needs.prepare.outputs.informational_version }}
|
||||||
|
|
||||||
|
Write-Host "Published to: $publishDir"
|
||||||
|
Write-Host "Self-contained: $selfContained"
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
|
|
||||||
- name: Install Inno Setup
|
- name: Install Inno Setup
|
||||||
@@ -120,7 +137,9 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
$version = "${{ needs.prepare.outputs.version }}"
|
$version = "${{ needs.prepare.outputs.version }}"
|
||||||
$arch = "${{ matrix.arch }}"
|
$arch = "${{ matrix.arch }}"
|
||||||
$publishDir = "publish\windows-$arch"
|
$selfContained = "${{ matrix.self_contained }}" -eq "true"
|
||||||
|
$suffix = "${{ matrix.suffix }}"
|
||||||
|
$publishDir = if ($selfContained) { "publish\windows-$arch" } else { "publish\windows-$arch-lite" }
|
||||||
$installerScript = "LanMountainDesktop\installer\LanMountainDesktop.iss"
|
$installerScript = "LanMountainDesktop\installer\LanMountainDesktop.iss"
|
||||||
$outputDir = "build-installer"
|
$outputDir = "build-installer"
|
||||||
|
|
||||||
@@ -187,6 +206,8 @@ jobs:
|
|||||||
"/DPublishDir=$publishDir",
|
"/DPublishDir=$publishDir",
|
||||||
"/DMyOutputDir=$outputDir",
|
"/DMyOutputDir=$outputDir",
|
||||||
"/DMyAppArch=$arch",
|
"/DMyAppArch=$arch",
|
||||||
|
"/DMyAppSuffix=$suffix",
|
||||||
|
"/DIsSelfContained=$selfContained",
|
||||||
$installerScript
|
$installerScript
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -213,7 +234,7 @@ jobs:
|
|||||||
- name: Upload Installer
|
- name: Upload Installer
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: release-windows-${{ matrix.arch }}
|
name: release-windows-${{ matrix.arch }}${{ matrix.suffix }}
|
||||||
path: build-installer/*.exe
|
path: build-installer/*.exe
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
retention-days: 30
|
retention-days: 30
|
||||||
@@ -550,8 +571,11 @@ jobs:
|
|||||||
## Release ${{ needs.prepare.outputs.version }}
|
## Release ${{ needs.prepare.outputs.version }}
|
||||||
|
|
||||||
### Windows
|
### Windows
|
||||||
- **LanMountainDesktop-Setup-{version}-x64.exe** - 64-bit installer
|
- **LanMountainDesktop-Setup-{version}-x64.exe** - 64-bit installer (完整版,包含 .NET 运行时)
|
||||||
- **LanMountainDesktop-Setup-{version}-x86.exe** - 32-bit installer
|
- **LanMountainDesktop-Setup-{version}-x64-lite.exe** - 64-bit installer (轻量版,需安装 .NET 10 Runtime)
|
||||||
|
- **LanMountainDesktop-Setup-{version}-x86.exe** - 32-bit installer (完整版,包含 .NET 运行时)
|
||||||
|
|
||||||
|
> **轻量版说明**:轻量版不包含 .NET 运行时,体积更小。首次运行前需安装 [.NET 10 Desktop Runtime](https://dotnet.microsoft.com/download/dotnet/10.0)。
|
||||||
|
|
||||||
Installation: Double-click the .exe file and follow the wizard.
|
Installation: Double-click the .exe file and follow the wizard.
|
||||||
|
|
||||||
|
|||||||
166
.trae/specs/fused-desktop-library-redesign/spec.md
Normal file
166
.trae/specs/fused-desktop-library-redesign/spec.md
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
# 融合桌面组件库窗口重设计规格
|
||||||
|
|
||||||
|
## Why
|
||||||
|
|
||||||
|
当前融合桌面组件库窗口(FusedDesktopComponentLibraryWindow)的UI设计较为基础,与Windows 11小组件编辑面板相比,缺乏现代化的交互体验和视觉层次。用户需要一个更直观、更美观的界面来浏览和添加组件到系统桌面(负一屏)。
|
||||||
|
|
||||||
|
参考Windows 11小组件编辑面板的设计特点:
|
||||||
|
|
||||||
|
* 左侧分类列表,右侧选中组件的详细预览
|
||||||
|
|
||||||
|
* 大型组件预览区域,让用户清楚看到组件效果
|
||||||
|
|
||||||
|
* 底部明显的"添加"操作按钮
|
||||||
|
|
||||||
|
* 简洁的关闭按钮(X)在右上角
|
||||||
|
|
||||||
|
* 深色主题配合毛玻璃效果
|
||||||
|
|
||||||
|
## What Changes
|
||||||
|
|
||||||
|
* **重新设计窗口布局**:从左右分栏(分类列表+组件网格)改为左侧面板+右侧预览区的布局
|
||||||
|
|
||||||
|
* **添加组件详情预览区**:选中组件后右侧显示大尺寸预览和组件信息
|
||||||
|
|
||||||
|
* **优化关闭按钮**:使用标准的X图标按钮,不使用圆形样式
|
||||||
|
|
||||||
|
* **添加底部操作栏**:包含"添加到桌面"主操作按钮和"查找更多组件"链接
|
||||||
|
|
||||||
|
* **复用阑山桌面组件库分类**:使用相同的分类ID、图标和本地化文本
|
||||||
|
|
||||||
|
* **移除搜索功能**:参考Windows 11设计,暂不提供搜索
|
||||||
|
|
||||||
|
## Impact
|
||||||
|
|
||||||
|
* 受影响文件:
|
||||||
|
|
||||||
|
* `LanMountainDesktop/Views/FusedDesktopComponentLibraryWindow.axaml`
|
||||||
|
|
||||||
|
* `LanMountainDesktop/Views/FusedDesktopComponentLibraryWindow.axaml.cs`
|
||||||
|
|
||||||
|
* `LanMountainDesktop/Views/FusedDesktopComponentLibraryControl.axaml`
|
||||||
|
|
||||||
|
* `LanMountainDesktop/Views/FusedDesktopComponentLibraryControl.axaml.cs`
|
||||||
|
|
||||||
|
* `LanMountainDesktop/ViewModels/ComponentLibraryWindowViewModel.cs`(可能需要添加新属性)
|
||||||
|
|
||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: 窗口布局重设计
|
||||||
|
|
||||||
|
系统应提供一个类似于Windows 11小组件编辑面板的组件库窗口。
|
||||||
|
|
||||||
|
#### Scenario: 窗口整体结构
|
||||||
|
|
||||||
|
* **GIVEN** 用户从托盘菜单打开融合桌面组件库
|
||||||
|
|
||||||
|
* **WHEN** 窗口显示时
|
||||||
|
|
||||||
|
* **THEN** 窗口应呈现:
|
||||||
|
|
||||||
|
* 顶部标题栏:左侧显示"添加小组件"标题,右侧有关闭按钮(X)
|
||||||
|
|
||||||
|
* 左侧面板:分类列表(复用阑山桌面组件库的分类和图标)
|
||||||
|
|
||||||
|
* 右侧主区域:选中组件的大尺寸预览 + 组件信息 + 添加按钮
|
||||||
|
|
||||||
|
* 底部:"查找更多组件"链接
|
||||||
|
|
||||||
|
#### Scenario: 分类列表交互
|
||||||
|
|
||||||
|
* **GIVEN** 左侧显示组件分类列表
|
||||||
|
|
||||||
|
* **WHEN** 用户点击某个分类
|
||||||
|
|
||||||
|
* **THEN** 右侧应显示该分类下的第一个组件的预览
|
||||||
|
|
||||||
|
* **AND** 分类项应有选中状态视觉反馈
|
||||||
|
|
||||||
|
* **AND** 分类图标和名称应与阑山桌面组件库保持一致
|
||||||
|
|
||||||
|
#### Scenario: 组件预览区
|
||||||
|
|
||||||
|
* **GIVEN** 用户选中一个组件
|
||||||
|
|
||||||
|
* **WHEN** 预览区显示时
|
||||||
|
|
||||||
|
* **THEN** 应显示:
|
||||||
|
|
||||||
|
* 组件标题(大字号)
|
||||||
|
|
||||||
|
* 大尺寸组件预览图(接近实际尺寸)
|
||||||
|
|
||||||
|
* 组件描述/功能说明
|
||||||
|
|
||||||
|
* 底部"添加到桌面"按钮
|
||||||
|
|
||||||
|
#### Scenario: 添加组件操作
|
||||||
|
|
||||||
|
* **GIVEN** 用户查看组件预览
|
||||||
|
|
||||||
|
* **WHEN** 用户点击"添加到桌面"按钮
|
||||||
|
|
||||||
|
* **THEN** 组件应被添加到系统桌面(负一屏)中央
|
||||||
|
|
||||||
|
* **AND** 窗口应关闭
|
||||||
|
|
||||||
|
#### Scenario: 关闭按钮样式
|
||||||
|
|
||||||
|
* **GIVEN** 窗口标题栏有关闭按钮
|
||||||
|
|
||||||
|
* **THEN** 关闭按钮应使用标准的X图标
|
||||||
|
|
||||||
|
* **AND** 不使用圆形背景或特殊样式
|
||||||
|
|
||||||
|
* **AND** 使用 `DesignCornerRadiusSm` 动态资源
|
||||||
|
|
||||||
|
#### Scenario: 查找更多组件链接
|
||||||
|
|
||||||
|
* **GIVEN** 窗口底部显示"查找更多组件"链接
|
||||||
|
|
||||||
|
* **WHEN** 用户点击该链接
|
||||||
|
|
||||||
|
* **THEN** 应打开设置窗口的插件目录页面(后续将改为插件市场)
|
||||||
|
|
||||||
|
## MODIFIED Requirements
|
||||||
|
|
||||||
|
### Requirement: 组件列表展示
|
||||||
|
|
||||||
|
原实现使用网格展示所有组件,新实现改为:
|
||||||
|
|
||||||
|
* 左侧列表仅显示分类(复用阑山桌面组件库的分类ID和图标映射)
|
||||||
|
|
||||||
|
* 右侧预览区一次只显示一个组件的详细信息
|
||||||
|
|
||||||
|
* ~~移除搜索功能~~(根据Windows 11设计,暂不提供搜索)
|
||||||
|
|
||||||
|
### Requirement: 关闭按钮圆角规范
|
||||||
|
|
||||||
|
原实现关闭按钮使用硬编码 `CornerRadius="18"`,应改为使用动态资源 `DesignCornerRadiusSm`。
|
||||||
|
|
||||||
|
### Requirement: 分类图标复用
|
||||||
|
|
||||||
|
分类图标映射应与阑山桌面组件库保持一致:
|
||||||
|
|
||||||
|
* Clock -> Symbol.Clock
|
||||||
|
|
||||||
|
* Date -> Symbol.CalendarDate
|
||||||
|
|
||||||
|
* Weather -> Symbol.WeatherSunny
|
||||||
|
|
||||||
|
* Board -> Symbol.Edit
|
||||||
|
|
||||||
|
* Media -> Symbol.Play
|
||||||
|
|
||||||
|
* Info -> Symbol.Info
|
||||||
|
|
||||||
|
* Calculator -> Symbol.Calculator
|
||||||
|
|
||||||
|
* Study -> Symbol.Hourglass
|
||||||
|
|
||||||
|
* 其他 -> Symbol.Apps
|
||||||
|
|
||||||
|
## REMOVED Requirements
|
||||||
|
|
||||||
|
* ~~搜索功能~~:根据Windows 11小组件面板设计,暂不提供搜索功能
|
||||||
|
|
||||||
35
.trae/specs/fused-desktop-library-redesign/tasks.md
Normal file
35
.trae/specs/fused-desktop-library-redesign/tasks.md
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# Tasks
|
||||||
|
|
||||||
|
- [x] Task 1: 修改 FusedDesktopComponentLibraryWindow.axaml 窗口布局
|
||||||
|
- [x] SubTask 1.1: 重新设计标题栏,使用标准X关闭按钮,移除圆形样式,使用 DesignCornerRadiusSm
|
||||||
|
- [x] SubTask 1.2: 调整窗口整体布局为左侧面板+右侧预览区
|
||||||
|
- [x] SubTask 1.3: 添加底部"查找更多组件"链接区域
|
||||||
|
|
||||||
|
- [x] Task 2: 修改 FusedDesktopComponentLibraryControl.axaml 控件布局
|
||||||
|
- [x] SubTask 2.1: 重新设计左侧面板:仅保留分类列表(移除搜索框)
|
||||||
|
- [x] SubTask 2.2: 重新设计右侧预览区:组件标题 + 大尺寸预览 + 描述 + 添加按钮
|
||||||
|
- [x] SubTask 2.3: 优化分类列表项样式,添加选中状态视觉反馈
|
||||||
|
- [x] SubTask 2.4: 复用阑山桌面组件库的分类图标映射
|
||||||
|
|
||||||
|
- [x] Task 3: 更新 ViewModel 支持新交互模式
|
||||||
|
- [x] SubTask 3.1: 在 ComponentLibraryWindowViewModel 中添加 SelectedComponent 属性
|
||||||
|
- [x] SubTask 3.2: 添加组件描述属性支持
|
||||||
|
|
||||||
|
- [x] Task 4: 更新 FusedDesktopComponentLibraryControl.axaml.cs 代码逻辑
|
||||||
|
- [x] SubTask 4.1: 修改分类选择逻辑,选中分类时显示该分类第一个组件
|
||||||
|
- [x] SubTask 4.2: 添加组件选中逻辑
|
||||||
|
- [x] SubTask 4.3: 移除搜索相关代码
|
||||||
|
- [x] SubTask 4.4: 复用阑山桌面组件库的分类图标和本地化方法
|
||||||
|
- [x] SubTask 4.5: 添加"查找更多组件"链接点击处理(打开设置窗口插件目录)
|
||||||
|
|
||||||
|
- [x] Task 5: 验证和测试
|
||||||
|
- [x] SubTask 5.1: 验证关闭按钮使用动态圆角资源 DesignCornerRadiusSm
|
||||||
|
- [x] SubTask 5.2: 验证窗口布局符合Windows 11小组件面板风格
|
||||||
|
- [x] SubTask 5.3: 验证分类图标与阑山桌面组件库一致
|
||||||
|
- [x] SubTask 5.4: 验证组件添加功能正常工作
|
||||||
|
- [x] SubTask 5.5: 验证"查找更多组件"链接能打开设置窗口
|
||||||
|
|
||||||
|
# Task Dependencies
|
||||||
|
- Task 3 依赖于 Task 1 和 Task 2 的UI设计确定
|
||||||
|
- Task 4 依赖于 Task 3 的ViewModel更新
|
||||||
|
- Task 5 依赖于所有前置任务完成
|
||||||
44
CHANGELOG.md
44
CHANGELOG.md
@@ -1,27 +1,5 @@
|
|||||||
# 更新日志 / Changelog
|
# 更新日志 / Changelog
|
||||||
|
|
||||||
所有重要的更改都将记录在此文件中。
|
|
||||||
|
|
||||||
格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/),
|
|
||||||
并且本项目遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## [Unreleased]
|
|
||||||
|
|
||||||
### 新增 (Added)
|
|
||||||
- 待发布的新功能
|
|
||||||
|
|
||||||
### 变更 (Changed)
|
|
||||||
- 待发布的变更
|
|
||||||
|
|
||||||
### 修复 (Fixed)
|
|
||||||
- 待发布的修复
|
|
||||||
|
|
||||||
### 移除 (Removed)
|
|
||||||
- 待发布的移除项
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## [0.8.3.2] - 2026-04-09
|
## [0.8.3.2] - 2026-04-09
|
||||||
|
|
||||||
@@ -70,6 +48,28 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
所有重要的更改都将记录在此文件中。
|
||||||
|
|
||||||
|
格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/),
|
||||||
|
并且本项目遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [格式示例]
|
||||||
|
|
||||||
|
### 新增 (Added)
|
||||||
|
- 待发布的新功能
|
||||||
|
|
||||||
|
### 变更 (Changed)
|
||||||
|
- 待发布的变更
|
||||||
|
|
||||||
|
### 修复 (Fixed)
|
||||||
|
- 待发布的修复
|
||||||
|
|
||||||
|
### 移除 (Removed)
|
||||||
|
- 待发布的移除项
|
||||||
|
|
||||||
|
---
|
||||||
## 版本说明
|
## 版本说明
|
||||||
|
|
||||||
### 版本号规则
|
### 版本号规则
|
||||||
|
|||||||
@@ -698,6 +698,7 @@
|
|||||||
"component.editor.placement_label": "Placement ID",
|
"component.editor.placement_label": "Placement ID",
|
||||||
"component.editor.scope_label": "Scope",
|
"component.editor.scope_label": "Scope",
|
||||||
"component.editor.scope_instance": "Instance-scoped editor",
|
"component.editor.scope_instance": "Instance-scoped editor",
|
||||||
|
"component_category.all": "All",
|
||||||
"component_category.clock": "Clock",
|
"component_category.clock": "Clock",
|
||||||
"component_category.date": "Calendar",
|
"component_category.date": "Calendar",
|
||||||
"component_category.weather": "Weather",
|
"component_category.weather": "Weather",
|
||||||
|
|||||||
@@ -692,6 +692,7 @@
|
|||||||
"component.editor.placement_label": "实例 ID",
|
"component.editor.placement_label": "实例 ID",
|
||||||
"component.editor.scope_label": "作用域",
|
"component.editor.scope_label": "作用域",
|
||||||
"component.editor.scope_instance": "实例级编辑器",
|
"component.editor.scope_instance": "实例级编辑器",
|
||||||
|
"component_category.all": "全部",
|
||||||
"component_category.clock": "时钟",
|
"component_category.clock": "时钟",
|
||||||
"component_category.date": "日历",
|
"component_category.date": "日历",
|
||||||
"component_category.weather": "天气",
|
"component_category.weather": "天气",
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ namespace LanMountainDesktop.ViewModels;
|
|||||||
public sealed class ComponentLibraryWindowViewModel : ViewModelBase
|
public sealed class ComponentLibraryWindowViewModel : ViewModelBase
|
||||||
{
|
{
|
||||||
private string _title = "Widgets";
|
private string _title = "Widgets";
|
||||||
|
private ComponentLibraryItemViewModel? _selectedComponent;
|
||||||
|
|
||||||
public string Title
|
public string Title
|
||||||
{
|
{
|
||||||
@@ -20,6 +21,12 @@ public sealed class ComponentLibraryWindowViewModel : ViewModelBase
|
|||||||
public ObservableCollection<ComponentLibraryCategoryViewModel> Categories { get; } = [];
|
public ObservableCollection<ComponentLibraryCategoryViewModel> Categories { get; } = [];
|
||||||
|
|
||||||
public ObservableCollection<ComponentLibraryItemViewModel> Components { get; } = [];
|
public ObservableCollection<ComponentLibraryItemViewModel> Components { get; } = [];
|
||||||
|
|
||||||
|
public ComponentLibraryItemViewModel? SelectedComponent
|
||||||
|
{
|
||||||
|
get => _selectedComponent;
|
||||||
|
set => SetProperty(ref _selectedComponent, value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class ComponentLibraryCategoryViewModel
|
public sealed class ComponentLibraryCategoryViewModel
|
||||||
@@ -51,6 +58,7 @@ public sealed class ComponentLibraryItemViewModel
|
|||||||
private readonly string _loadingPreviewText;
|
private readonly string _loadingPreviewText;
|
||||||
private readonly string _previewUnavailableText;
|
private readonly string _previewUnavailableText;
|
||||||
private string _displayName;
|
private string _displayName;
|
||||||
|
private string? _description;
|
||||||
private ComponentPreviewKey _previewKey;
|
private ComponentPreviewKey _previewKey;
|
||||||
private ComponentPreviewImageEntry? _previewImageEntry;
|
private ComponentPreviewImageEntry? _previewImageEntry;
|
||||||
private ComponentPreviewImageState _previewState;
|
private ComponentPreviewImageState _previewState;
|
||||||
@@ -61,12 +69,14 @@ public sealed class ComponentLibraryItemViewModel
|
|||||||
string componentId,
|
string componentId,
|
||||||
string displayName,
|
string displayName,
|
||||||
ComponentPreviewKey previewKey,
|
ComponentPreviewKey previewKey,
|
||||||
|
string? description = null,
|
||||||
string loadingPreviewText = "Loading preview...",
|
string loadingPreviewText = "Loading preview...",
|
||||||
string previewUnavailableText = "Preview unavailable",
|
string previewUnavailableText = "Preview unavailable",
|
||||||
ComponentPreviewImageEntry? previewImageEntry = null)
|
ComponentPreviewImageEntry? previewImageEntry = null)
|
||||||
{
|
{
|
||||||
ComponentId = componentId;
|
ComponentId = componentId;
|
||||||
_displayName = displayName;
|
_displayName = displayName;
|
||||||
|
_description = description;
|
||||||
_previewKey = previewKey;
|
_previewKey = previewKey;
|
||||||
_loadingPreviewText = loadingPreviewText;
|
_loadingPreviewText = loadingPreviewText;
|
||||||
_previewUnavailableText = previewUnavailableText;
|
_previewUnavailableText = previewUnavailableText;
|
||||||
@@ -82,6 +92,12 @@ public sealed class ComponentLibraryItemViewModel
|
|||||||
set => SetProperty(ref _displayName, value);
|
set => SetProperty(ref _displayName, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string? Description
|
||||||
|
{
|
||||||
|
get => _description;
|
||||||
|
set => SetProperty(ref _description, value);
|
||||||
|
}
|
||||||
|
|
||||||
public ComponentPreviewKey PreviewKey
|
public ComponentPreviewKey PreviewKey
|
||||||
{
|
{
|
||||||
get => _previewKey;
|
get => _previewKey;
|
||||||
|
|||||||
@@ -94,6 +94,7 @@ public partial class ComponentLibraryWindow : Window
|
|||||||
entry.ComponentId,
|
entry.ComponentId,
|
||||||
displayName,
|
displayName,
|
||||||
previewKey,
|
previewKey,
|
||||||
|
description: null,
|
||||||
_localize?.Invoke("component_library.preview.loading", "Loading preview...") ?? "Loading preview...",
|
_localize?.Invoke("component_library.preview.loading", "Loading preview...") ?? "Loading preview...",
|
||||||
_localize?.Invoke("component_library.preview.unavailable", "Preview unavailable") ?? "Preview unavailable",
|
_localize?.Invoke("component_library.preview.unavailable", "Preview unavailable") ?? "Preview unavailable",
|
||||||
previewEntry);
|
previewEntry);
|
||||||
|
|||||||
@@ -34,11 +34,13 @@
|
|||||||
<TextBlock x:Name="WeekdayTextBlock"
|
<TextBlock x:Name="WeekdayTextBlock"
|
||||||
Text="周一"
|
Text="周一"
|
||||||
TextAlignment="Right"
|
TextAlignment="Right"
|
||||||
FontWeight="SemiBold" />
|
FontWeight="SemiBold"
|
||||||
|
TextTrimming="CharacterEllipsis" />
|
||||||
<TextBlock x:Name="ClassCountTextBlock"
|
<TextBlock x:Name="ClassCountTextBlock"
|
||||||
Text="0节课"
|
Text="0节课"
|
||||||
TextAlignment="Right"
|
TextAlignment="Right"
|
||||||
FontWeight="SemiBold" />
|
FontWeight="SemiBold"
|
||||||
|
TextTrimming="CharacterEllipsis" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
|||||||
@@ -928,7 +928,28 @@ public partial class ClassScheduleWidget : UserControl, IDesktopComponentWidget,
|
|||||||
MetaStack.Spacing = Math.Clamp(6 * scale, 3, 10);
|
MetaStack.Spacing = Math.Clamp(6 * scale, 3, 10);
|
||||||
CourseListPanel.Spacing = Math.Clamp(6 * scale, 3, 10);
|
CourseListPanel.Spacing = Math.Clamp(6 * scale, 3, 10);
|
||||||
|
|
||||||
var dateFont = Math.Clamp(66 * scale, 26, 82);
|
var dateFontByScale = Math.Clamp(66 * scale, 26, 82);
|
||||||
|
var weekdayFontByScale = Math.Clamp(34 * scale, 13, 32);
|
||||||
|
var classCountFontByScale = Math.Clamp(40 * scale, 14, 36);
|
||||||
|
|
||||||
|
// 宽度感知:当头部内容总需求超过可用宽度时,按比例缩小日期字体
|
||||||
|
var availableWidth = Math.Max(1, Bounds.Width - rootPadding.Left - rootPadding.Right);
|
||||||
|
var dateGroupEstimatedWidth = dateFontByScale * 0.6 * 3 + DateGroup.Spacing * 2;
|
||||||
|
var metaStackEstimatedWidth = classCountFontByScale * 0.6 * 4 + MetaStack.Spacing;
|
||||||
|
var headerColumnSpacing = Math.Clamp(10 * scale, 4, 16);
|
||||||
|
var totalHeaderNeed = dateGroupEstimatedWidth + headerColumnSpacing + metaStackEstimatedWidth;
|
||||||
|
|
||||||
|
var dateFont = dateFontByScale;
|
||||||
|
if (totalHeaderNeed > availableWidth)
|
||||||
|
{
|
||||||
|
var shrinkRatio = availableWidth / totalHeaderNeed;
|
||||||
|
dateFont = Math.Max(20, dateFontByScale * shrinkRatio);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 为 HeaderGrid 左列设置最小宽度,防止被压缩至零
|
||||||
|
var minDateColumnWidth = dateFont * 0.6 * 3 + DateGroup.Spacing * 2;
|
||||||
|
HeaderGrid.ColumnDefinitions[0].MinWidth = minDateColumnWidth;
|
||||||
|
|
||||||
MonthTextBlock.FontSize = dateFont;
|
MonthTextBlock.FontSize = dateFont;
|
||||||
DayTextBlock.FontSize = dateFont;
|
DayTextBlock.FontSize = dateFont;
|
||||||
SlashTextBlock.FontSize = dateFont;
|
SlashTextBlock.FontSize = dateFont;
|
||||||
@@ -940,8 +961,8 @@ public partial class ClassScheduleWidget : UserControl, IDesktopComponentWidget,
|
|||||||
ClassCountTextBlock.Foreground = CreateBrush(_isNightVisual ? "#8D95A4" : "#738095");
|
ClassCountTextBlock.Foreground = CreateBrush(_isNightVisual ? "#8D95A4" : "#738095");
|
||||||
StatusTextBlock.Foreground = CreateBrush(_isNightVisual ? "#9AA2B1" : "#4B5565");
|
StatusTextBlock.Foreground = CreateBrush(_isNightVisual ? "#9AA2B1" : "#4B5565");
|
||||||
|
|
||||||
WeekdayTextBlock.FontSize = Math.Clamp(34 * scale, 13, 32);
|
WeekdayTextBlock.FontSize = weekdayFontByScale;
|
||||||
ClassCountTextBlock.FontSize = Math.Clamp(40 * scale, 14, 36);
|
ClassCountTextBlock.FontSize = classCountFontByScale;
|
||||||
StatusTextBlock.FontSize = Math.Clamp(30 * scale, 12, 30);
|
StatusTextBlock.FontSize = Math.Clamp(30 * scale, 12, 30);
|
||||||
|
|
||||||
WeekdayTextBlock.FontWeight = ToVariableWeight(Lerp(560, 700, Math.Clamp((scale - 0.60) / 1.2, 0, 1)));
|
WeekdayTextBlock.FontWeight = ToVariableWeight(Lerp(560, 700, Math.Clamp((scale - 0.60) / 1.2, 0, 1)));
|
||||||
|
|||||||
@@ -704,6 +704,24 @@ public partial class CnrDailyNewsWidget : UserControl, IDesktopComponentWidget,
|
|||||||
ExtraNewsItemsPanel.Spacing = Math.Clamp(6 * scale, 3, 10);
|
ExtraNewsItemsPanel.Spacing = Math.Clamp(6 * scale, 3, 10);
|
||||||
|
|
||||||
ApplyNightModeVisual();
|
ApplyNightModeVisual();
|
||||||
|
|
||||||
|
var headerHeight = refreshHeight;
|
||||||
|
var newsItemHeight = Math.Max(imageHeight, mainNewsMinHeight);
|
||||||
|
|
||||||
|
var requiredHeight = verticalPadding * 2
|
||||||
|
+ headerHeight
|
||||||
|
+ rowSpacing
|
||||||
|
+ newsItemHeight
|
||||||
|
+ rowSpacing
|
||||||
|
+ newsItemHeight;
|
||||||
|
|
||||||
|
if (_extraNewsRows.Count > 0)
|
||||||
|
{
|
||||||
|
var extraSpacing = ExtraNewsItemsPanel.Spacing * (_extraNewsRows.Count - 1);
|
||||||
|
requiredHeight += rowSpacing + extraSpacing + _extraNewsRows.Count * newsItemHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.MinHeight = requiredHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateRefreshButtonState()
|
private void UpdateRefreshButtonState()
|
||||||
@@ -842,6 +860,11 @@ public partial class CnrDailyNewsWidget : UserControl, IDesktopComponentWidget,
|
|||||||
oldBitmap?.Dispose();
|
oldBitmap?.Dispose();
|
||||||
_newsBitmaps[index] = bitmap;
|
_newsBitmaps[index] = bitmap;
|
||||||
imageControl.Source = bitmap;
|
imageControl.Source = bitmap;
|
||||||
|
|
||||||
|
if (bitmap != null)
|
||||||
|
{
|
||||||
|
InvalidateMeasure();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DisposeNewsBitmaps()
|
private void DisposeNewsBitmaps()
|
||||||
|
|||||||
@@ -1,160 +1,227 @@
|
|||||||
<UserControl xmlns="https://github.com/avaloniaui"
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:vm="using:LanMountainDesktop.ViewModels"
|
xmlns:vm="using:LanMountainDesktop.ViewModels"
|
||||||
xmlns:fi="using:FluentIcons.Avalonia"
|
xmlns:ui="using:FluentAvalonia.UI.Controls"
|
||||||
|
xmlns:fi="using:FluentIcons.Avalonia.Fluent"
|
||||||
|
xmlns:converters="using:Avalonia.Data.Converters"
|
||||||
x:Class="LanMountainDesktop.Views.FusedDesktopComponentLibraryControl"
|
x:Class="LanMountainDesktop.Views.FusedDesktopComponentLibraryControl"
|
||||||
x:DataType="vm:ComponentLibraryWindowViewModel">
|
x:DataType="vm:ComponentLibraryWindowViewModel">
|
||||||
|
|
||||||
<Grid ColumnDefinitions="240,*"
|
<UserControl.Styles>
|
||||||
ColumnSpacing="12"
|
<!-- 分类列表项样式 - 遵循 Fluent NavigationView 风格 -->
|
||||||
Margin="0">
|
<Style Selector="ListBoxItem.category-item">
|
||||||
<!-- 分类列表 (左侧) -->
|
<Setter Property="Padding" Value="0"/>
|
||||||
<Border Classes="surface-translucent-panel"
|
<Setter Property="Margin" Value="0,2"/>
|
||||||
CornerRadius="{DynamicResource DesignCornerRadiusLg}"
|
<Setter Property="Background" Value="Transparent"/>
|
||||||
Padding="10">
|
<Setter Property="CornerRadius" Value="{DynamicResource DesignCornerRadiusSm}"/>
|
||||||
<Grid RowDefinitions="Auto,*">
|
<Setter Property="Transitions">
|
||||||
<TextBox x:Name="SearchBox"
|
<Transitions>
|
||||||
Watermark="搜索组件..."
|
<BrushTransition Property="Background" Duration="0:0:0.083" Easing="0.05,0.75,0.10,1.00"/>
|
||||||
Margin="0,0,0,12"
|
</Transitions>
|
||||||
Classes="clear"
|
</Setter>
|
||||||
Background="{DynamicResource AdaptiveSurfaceLowBrush}"
|
</Style>
|
||||||
CornerRadius="12"
|
<Style Selector="ListBoxItem.category-item:pointerover /template/ ContentPresenter#PART_ContentPresenter">
|
||||||
Padding="12,8">
|
<Setter Property="Background" Value="{DynamicResource SubtleFillColorSecondaryBrush}"/>
|
||||||
<TextBox.InnerLeftContent>
|
</Style>
|
||||||
<fi:SymbolIcon Symbol="Search" FontSize="16" Margin="10,0,0,0" Opacity="0.5" />
|
<Style Selector="ListBoxItem.category-item:selected /template/ ContentPresenter#PART_ContentPresenter">
|
||||||
</TextBox.InnerLeftContent>
|
<Setter Property="Background" Value="{DynamicResource AdaptiveNavItemSelectedBackgroundBrush}"/>
|
||||||
</TextBox>
|
</Style>
|
||||||
|
<Style Selector="ListBoxItem.category-item:pressed /template/ ContentPresenter#PART_ContentPresenter">
|
||||||
|
<Setter Property="Background" Value="{DynamicResource SubtleFillColorTertiaryBrush}"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<!-- 分类项图标和文字 -->
|
||||||
|
<Style Selector="ListBoxItem.category-item fi|FluentIcon.category-icon">
|
||||||
|
<Setter Property="Foreground" Value="{DynamicResource AdaptiveTextPrimaryBrush}"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="ListBoxItem.category-item:selected fi|FluentIcon.category-icon">
|
||||||
|
<Setter Property="Foreground" Value="{DynamicResource AdaptiveTextPrimaryBrush}"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="ListBoxItem.category-item TextBlock.category-text">
|
||||||
|
<Setter Property="Foreground" Value="{DynamicResource AdaptiveTextPrimaryBrush}"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="ListBoxItem.category-item:selected TextBlock.category-text">
|
||||||
|
<Setter Property="Foreground" Value="{DynamicResource AdaptiveTextPrimaryBrush}"/>
|
||||||
|
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||||
|
</Style>
|
||||||
|
</UserControl.Styles>
|
||||||
|
|
||||||
|
<Grid ColumnDefinitions="Auto,*"
|
||||||
|
ColumnSpacing="0"
|
||||||
|
Margin="0">
|
||||||
|
<!-- 左侧导航列 - 分类列表 + 底部"查找更多组件" -->
|
||||||
|
<Border Width="280"
|
||||||
|
Background="Transparent">
|
||||||
|
<Grid RowDefinitions="*,Auto">
|
||||||
|
<!-- 分类列表 -->
|
||||||
<ListBox x:Name="CategoryListBox"
|
<ListBox x:Name="CategoryListBox"
|
||||||
Grid.Row="1"
|
Grid.Row="0"
|
||||||
Background="Transparent"
|
Background="Transparent"
|
||||||
BorderThickness="0"
|
BorderThickness="0"
|
||||||
|
Margin="8,8,4,0"
|
||||||
SelectionChanged="OnCategorySelectionChanged"
|
SelectionChanged="OnCategorySelectionChanged"
|
||||||
ItemsSource="{Binding Categories}">
|
ItemsSource="{Binding Categories}">
|
||||||
<ListBox.ItemTemplate>
|
<ListBox.ItemTemplate>
|
||||||
<DataTemplate x:DataType="vm:ComponentLibraryCategoryViewModel">
|
<DataTemplate x:DataType="vm:ComponentLibraryCategoryViewModel">
|
||||||
<Border Padding="10"
|
|
||||||
Margin="0,0,0,6"
|
|
||||||
CornerRadius="{DynamicResource DesignCornerRadiusSm}"
|
|
||||||
Background="{DynamicResource AdaptiveNavItemBackgroundBrush}">
|
|
||||||
<Grid ColumnDefinitions="Auto,*"
|
<Grid ColumnDefinitions="Auto,*"
|
||||||
ColumnSpacing="8">
|
ColumnSpacing="12"
|
||||||
<fi:SymbolIcon Symbol="{Binding Icon}"
|
Margin="12,10">
|
||||||
|
<fi:FluentIcon Icon="{Binding Icon}"
|
||||||
IconVariant="Regular"
|
IconVariant="Regular"
|
||||||
FontSize="16" />
|
FontSize="18"
|
||||||
|
Classes="category-icon"/>
|
||||||
<TextBlock Grid.Column="1"
|
<TextBlock Grid.Column="1"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
FontWeight="SemiBold"
|
FontSize="14"
|
||||||
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}"
|
Classes="category-text"
|
||||||
Text="{Binding Title}" />
|
Text="{Binding Title}"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ListBox.ItemTemplate>
|
</ListBox.ItemTemplate>
|
||||||
</ListBox>
|
</ListBox>
|
||||||
|
|
||||||
|
<!-- 底部"查找更多组件" - 在左侧导航列底部 -->
|
||||||
|
<StackPanel Grid.Row="1"
|
||||||
|
Margin="12,8,8,12">
|
||||||
|
<Border Height="1"
|
||||||
|
Background="{DynamicResource AdaptiveGlassPanelBorderBrush}"
|
||||||
|
Opacity="0.4"
|
||||||
|
Margin="0,0,0,8"/>
|
||||||
|
<Button Classes="hyperlink"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
Click="OnFindMoreComponentsClick">
|
||||||
|
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||||
|
<fi:FluentIcon Icon="Globe" IconVariant="Regular" FontSize="14"/>
|
||||||
|
<TextBlock Text="查找更多组件"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<!-- 组件网格 (右侧) -->
|
<!-- 右侧内容区与左侧的分隔线 -->
|
||||||
<Border Grid.Column="1"
|
<Border Grid.Column="1"
|
||||||
Classes="surface-translucent-strong"
|
Width="1"
|
||||||
CornerRadius="{DynamicResource DesignCornerRadiusLg}"
|
HorizontalAlignment="Left"
|
||||||
Padding="10">
|
Background="{DynamicResource AdaptiveGlassPanelBorderBrush}"
|
||||||
<ScrollViewer VerticalScrollBarVisibility="Auto"
|
Opacity="0.5"/>
|
||||||
HorizontalScrollBarVisibility="Disabled">
|
|
||||||
<ItemsControl x:Name="ComponentItemsControl"
|
|
||||||
ItemsSource="{Binding Components}">
|
|
||||||
<ItemsControl.ItemsPanel>
|
|
||||||
<ItemsPanelTemplate>
|
|
||||||
<WrapPanel Orientation="Horizontal" />
|
|
||||||
</ItemsPanelTemplate>
|
|
||||||
</ItemsControl.ItemsPanel>
|
|
||||||
|
|
||||||
<ItemsControl.ItemTemplate>
|
<!-- 组件预览区 (右侧) -->
|
||||||
<DataTemplate x:DataType="vm:ComponentLibraryItemViewModel">
|
<ScrollViewer Grid.Column="1"
|
||||||
<Border Width="240"
|
VerticalScrollBarVisibility="Auto"
|
||||||
Height="220"
|
HorizontalScrollBarVisibility="Disabled">
|
||||||
Margin="6"
|
<StackPanel Margin="16,8,12,8"
|
||||||
CornerRadius="{DynamicResource DesignCornerRadiusComponent}"
|
Spacing="0">
|
||||||
Padding="10"
|
|
||||||
Background="{DynamicResource AdaptiveSurfaceRaisedBrush}"
|
<!-- 有选中组件时的显示 -->
|
||||||
BorderBrush="{DynamicResource AdaptiveButtonBorderBrush}"
|
<Panel IsVisible="{Binding SelectedComponent, Converter={x:Static converters:ObjectConverters.IsNotNull}}">
|
||||||
BorderThickness="1">
|
|
||||||
<Grid RowDefinitions="*,Auto,Auto"
|
<!-- 组件展示面板 - 有独立背景色,与窗口背景形成层级分界 -->
|
||||||
RowSpacing="8">
|
<Border Classes="surface-translucent-panel"
|
||||||
<!-- 预览区域 -->
|
CornerRadius="{DynamicResource DesignCornerRadiusLg}"
|
||||||
|
Padding="20">
|
||||||
|
<StackPanel Spacing="16">
|
||||||
|
<!-- 组件标题 -->
|
||||||
|
<TextBlock FontSize="28"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}"
|
||||||
|
Text="{Binding SelectedComponent.DisplayName}"/>
|
||||||
|
|
||||||
|
<!-- 固定大小的预览卡片 -->
|
||||||
<Border CornerRadius="{DynamicResource DesignCornerRadiusSm}"
|
<Border CornerRadius="{DynamicResource DesignCornerRadiusSm}"
|
||||||
Background="{DynamicResource AdaptiveGlassPanelBackgroundBrush}"
|
Background="{DynamicResource AdaptiveSurfaceBaseBrush}"
|
||||||
BorderThickness="1"
|
|
||||||
BorderBrush="{DynamicResource AdaptiveGlassPanelBorderBrush}"
|
BorderBrush="{DynamicResource AdaptiveGlassPanelBorderBrush}"
|
||||||
Padding="8">
|
BorderThickness="1"
|
||||||
<Grid>
|
Width="420"
|
||||||
<Image Source="{Binding PreviewBitmap}"
|
Height="300"
|
||||||
|
HorizontalAlignment="Center">
|
||||||
|
<Grid Margin="16">
|
||||||
|
<!-- 预览图片 -->
|
||||||
|
<Image Source="{Binding SelectedComponent.PreviewBitmap}"
|
||||||
Stretch="Uniform"
|
Stretch="Uniform"
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Center"
|
||||||
VerticalAlignment="Stretch"
|
VerticalAlignment="Center"
|
||||||
RenderOptions.BitmapInterpolationMode="HighQuality"
|
RenderOptions.BitmapInterpolationMode="HighQuality"
|
||||||
IsVisible="{Binding IsPreviewReady}" />
|
IsVisible="{Binding SelectedComponent.IsPreviewReady}"/>
|
||||||
|
|
||||||
<!-- 加载中状态 -->
|
<!-- 加载中状态 -->
|
||||||
<Border IsVisible="{Binding IsPreviewPending}"
|
<Border IsVisible="{Binding SelectedComponent.IsPreviewPending}"
|
||||||
Background="{DynamicResource AdaptiveSurfaceBaseBrush}">
|
Background="{DynamicResource AdaptiveSurfaceBaseBrush}">
|
||||||
<StackPanel HorizontalAlignment="Center"
|
<StackPanel HorizontalAlignment="Center"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Spacing="8">
|
Spacing="12">
|
||||||
<ProgressBar Width="96"
|
<ProgressBar Width="120"
|
||||||
IsIndeterminate="True" />
|
IsIndeterminate="True"/>
|
||||||
<TextBlock HorizontalAlignment="Center"
|
<TextBlock HorizontalAlignment="Center"
|
||||||
TextAlignment="Center"
|
TextAlignment="Center"
|
||||||
FontSize="12"
|
FontSize="14"
|
||||||
Foreground="{DynamicResource AdaptiveTextSecondaryBrush}"
|
Foreground="{DynamicResource AdaptiveTextSecondaryBrush}"
|
||||||
Text="{Binding PreviewStatusText}" />
|
Text="{Binding SelectedComponent.PreviewStatusText}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<!-- 失败状态 -->
|
<!-- 失败状态 -->
|
||||||
<Border IsVisible="{Binding IsPreviewFailed}"
|
<Border IsVisible="{Binding SelectedComponent.IsPreviewFailed}"
|
||||||
Background="{DynamicResource AdaptiveSurfaceBaseBrush}">
|
Background="{DynamicResource AdaptiveSurfaceBaseBrush}">
|
||||||
<StackPanel HorizontalAlignment="Center"
|
<StackPanel HorizontalAlignment="Center"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Spacing="8">
|
Spacing="8">
|
||||||
|
<fi:FluentIcon Icon="ImageOff"
|
||||||
|
IconVariant="Regular"
|
||||||
|
FontSize="48"
|
||||||
|
Opacity="0.5"
|
||||||
|
Foreground="{DynamicResource AdaptiveTextSecondaryBrush}"/>
|
||||||
<TextBlock HorizontalAlignment="Center"
|
<TextBlock HorizontalAlignment="Center"
|
||||||
TextAlignment="Center"
|
TextAlignment="Center"
|
||||||
FontWeight="SemiBold"
|
FontWeight="SemiBold"
|
||||||
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}"
|
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}"
|
||||||
Text="{Binding PreviewStatusText}" />
|
Text="{Binding SelectedComponent.PreviewStatusText}"/>
|
||||||
<TextBlock HorizontalAlignment="Center"
|
<TextBlock HorizontalAlignment="Center"
|
||||||
TextAlignment="Center"
|
TextAlignment="Center"
|
||||||
FontSize="12"
|
FontSize="12"
|
||||||
TextWrapping="Wrap"
|
TextWrapping="Wrap"
|
||||||
Foreground="{DynamicResource AdaptiveTextSecondaryBrush}"
|
Foreground="{DynamicResource AdaptiveTextSecondaryBrush}"
|
||||||
Text="{Binding PreviewErrorMessage}" />
|
Text="{Binding SelectedComponent.PreviewErrorMessage}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<!-- 组件名称 -->
|
<!-- "添加小组件"按钮 - 在面板内居中,使用主题强调色 -->
|
||||||
<TextBlock Grid.Row="1"
|
<Button HorizontalAlignment="Center"
|
||||||
HorizontalAlignment="Center"
|
Classes="accent"
|
||||||
FontWeight="SemiBold"
|
Padding="24,10"
|
||||||
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}"
|
Tag="{Binding SelectedComponent.ComponentId}"
|
||||||
Text="{Binding DisplayName}" />
|
|
||||||
|
|
||||||
<!-- 添加按钮 -->
|
|
||||||
<Button Grid.Row="2"
|
|
||||||
HorizontalAlignment="Center"
|
|
||||||
Padding="12,6"
|
|
||||||
Tag="{Binding ComponentId}"
|
|
||||||
Click="OnAddComponentClick">
|
Click="OnAddComponentClick">
|
||||||
<TextBlock Text="添加到桌面" />
|
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||||
|
<fi:FluentIcon Icon="Add" IconVariant="Regular" FontSize="16"/>
|
||||||
|
<TextBlock Text="添加小组件" FontWeight="SemiBold"/>
|
||||||
|
</StackPanel>
|
||||||
</Button>
|
</Button>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
</Panel>
|
||||||
|
|
||||||
|
<!-- 空状态 -->
|
||||||
|
<Grid IsVisible="{Binding SelectedComponent, Converter={x:Static converters:ObjectConverters.IsNull}}"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
MinHeight="400">
|
||||||
|
<StackPanel Spacing="16" HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center">
|
||||||
|
<fi:FluentIcon Icon="Apps"
|
||||||
|
IconVariant="Regular"
|
||||||
|
FontSize="64"
|
||||||
|
Opacity="0.3"
|
||||||
|
Foreground="{DynamicResource AdaptiveTextSecondaryBrush}"/>
|
||||||
|
<TextBlock HorizontalAlignment="Center"
|
||||||
|
FontSize="16"
|
||||||
|
Foreground="{DynamicResource AdaptiveTextSecondaryBrush}"
|
||||||
|
Text="请从左侧选择一个组件"/>
|
||||||
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</StackPanel>
|
||||||
</DataTemplate>
|
|
||||||
</ItemsControl.ItemTemplate>
|
|
||||||
</ItemsControl>
|
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</Border>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using System.Linq;
|
|||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
|
using Avalonia.VisualTree;
|
||||||
using FluentIcons.Common;
|
using FluentIcons.Common;
|
||||||
using LanMountainDesktop.ComponentSystem;
|
using LanMountainDesktop.ComponentSystem;
|
||||||
using LanMountainDesktop.Services;
|
using LanMountainDesktop.Services;
|
||||||
@@ -29,6 +30,8 @@ public partial class FusedDesktopComponentLibraryControl : UserControl
|
|||||||
private readonly IRecommendationInfoService _recommendationInfoService = new RecommendationDataService();
|
private readonly IRecommendationInfoService _recommendationInfoService = new RecommendationDataService();
|
||||||
private readonly ICalculatorDataService _calculatorDataService = new CalculatorDataService();
|
private readonly ICalculatorDataService _calculatorDataService = new CalculatorDataService();
|
||||||
|
|
||||||
|
private static readonly LocalizationService _localizationService = new();
|
||||||
|
|
||||||
public FusedDesktopComponentLibraryControl()
|
public FusedDesktopComponentLibraryControl()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
@@ -39,7 +42,9 @@ public partial class FusedDesktopComponentLibraryControl : UserControl
|
|||||||
|
|
||||||
LoadRegistry();
|
LoadRegistry();
|
||||||
LoadCategories();
|
LoadCategories();
|
||||||
SearchBox.KeyUp += (s, e) => FilterComponents();
|
|
||||||
|
// 为 ListBoxItem 添加 category-item 样式类
|
||||||
|
CategoryListBox.ContainerPrepared += OnCategoryListBoxContainerPrepared;
|
||||||
|
|
||||||
// 默认选择第一个分类
|
// 默认选择第一个分类
|
||||||
if (_viewModel.Categories.Count > 0)
|
if (_viewModel.Categories.Count > 0)
|
||||||
@@ -48,6 +53,14 @@ public partial class FusedDesktopComponentLibraryControl : UserControl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnCategoryListBoxContainerPrepared(object? sender, ContainerPreparedEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.Container is ListBoxItem listBoxItem)
|
||||||
|
{
|
||||||
|
listBoxItem.Classes.Add("category-item");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void LoadRegistry()
|
private void LoadRegistry()
|
||||||
{
|
{
|
||||||
var pluginRuntimeService = (Application.Current as App)?.PluginRuntimeService;
|
var pluginRuntimeService = (Application.Current as App)?.PluginRuntimeService;
|
||||||
@@ -65,28 +78,16 @@ public partial class FusedDesktopComponentLibraryControl : UserControl
|
|||||||
private void LoadCategories()
|
private void LoadCategories()
|
||||||
{
|
{
|
||||||
_viewModel.Categories.Clear();
|
_viewModel.Categories.Clear();
|
||||||
_viewModel.Components.Clear();
|
|
||||||
|
var languageCode = _settingsFacade.Region.Get().LanguageCode;
|
||||||
|
|
||||||
// 添加"全部组件"分类
|
// 添加"全部组件"分类
|
||||||
_viewModel.Categories.Add(new ComponentLibraryCategoryViewModel(
|
_viewModel.Categories.Add(new ComponentLibraryCategoryViewModel(
|
||||||
"all",
|
"all",
|
||||||
"全部组件",
|
L(languageCode, "component_category.all", "All"),
|
||||||
Symbol.Apps,
|
Symbol.Apps,
|
||||||
Array.Empty<ComponentLibraryItemViewModel>()));
|
Array.Empty<ComponentLibraryItemViewModel>()));
|
||||||
|
|
||||||
var categoryMap = new Dictionary<string, (string Display, Symbol Icon)>
|
|
||||||
{
|
|
||||||
{ "clock", ("时钟", Symbol.Clock) },
|
|
||||||
{ "date", ("日历", Symbol.CalendarDate) },
|
|
||||||
{ "weather", ("天气", Symbol.WeatherSunny) },
|
|
||||||
{ "board", ("画板", Symbol.Edit) },
|
|
||||||
{ "media", ("媒体", Symbol.Play) },
|
|
||||||
{ "info", ("资讯", Symbol.News) },
|
|
||||||
{ "calculator", ("工具", Symbol.Calculator) },
|
|
||||||
{ "study", ("学习", Symbol.Hourglass) },
|
|
||||||
{ "file", ("文件", Symbol.Folder) }
|
|
||||||
};
|
|
||||||
|
|
||||||
var usedCategories = _allDefinitions
|
var usedCategories = _allDefinitions
|
||||||
.Select(d => d.Category)
|
.Select(d => d.Category)
|
||||||
.Distinct()
|
.Distinct()
|
||||||
@@ -94,8 +95,9 @@ public partial class FusedDesktopComponentLibraryControl : UserControl
|
|||||||
|
|
||||||
foreach (var cat in usedCategories)
|
foreach (var cat in usedCategories)
|
||||||
{
|
{
|
||||||
if (categoryMap.TryGetValue(cat.ToLower(), out var info))
|
var icon = ResolveCategoryIcon(cat);
|
||||||
{
|
var title = GetLocalizedCategoryTitle(languageCode, cat);
|
||||||
|
|
||||||
var categoryComponents = _allDefinitions
|
var categoryComponents = _allDefinitions
|
||||||
.Where(d => string.Equals(d.Category, cat, StringComparison.OrdinalIgnoreCase))
|
.Where(d => string.Equals(d.Category, cat, StringComparison.OrdinalIgnoreCase))
|
||||||
.OrderBy(d => d.DisplayName)
|
.OrderBy(d => d.DisplayName)
|
||||||
@@ -104,11 +106,49 @@ public partial class FusedDesktopComponentLibraryControl : UserControl
|
|||||||
|
|
||||||
_viewModel.Categories.Add(new ComponentLibraryCategoryViewModel(
|
_viewModel.Categories.Add(new ComponentLibraryCategoryViewModel(
|
||||||
cat,
|
cat,
|
||||||
info.Display,
|
title,
|
||||||
info.Icon,
|
icon,
|
||||||
categoryComponents));
|
categoryComponents));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 分类图标映射 - 与阑山桌面 Dock 栏组件库 (MainWindow.ComponentSystem) 保持一致
|
||||||
|
/// </summary>
|
||||||
|
private static Symbol ResolveCategoryIcon(string categoryId)
|
||||||
|
{
|
||||||
|
if (string.Equals(categoryId, "Clock", StringComparison.OrdinalIgnoreCase)) return Symbol.Clock;
|
||||||
|
if (string.Equals(categoryId, "Date", StringComparison.OrdinalIgnoreCase)) return Symbol.CalendarDate;
|
||||||
|
if (string.Equals(categoryId, "Weather", StringComparison.OrdinalIgnoreCase)) return Symbol.WeatherSunny;
|
||||||
|
if (string.Equals(categoryId, "Board", StringComparison.OrdinalIgnoreCase)) return Symbol.Edit;
|
||||||
|
if (string.Equals(categoryId, "Media", StringComparison.OrdinalIgnoreCase)) return Symbol.Play;
|
||||||
|
if (string.Equals(categoryId, "Info", StringComparison.OrdinalIgnoreCase)) return Symbol.Apps;
|
||||||
|
if (string.Equals(categoryId, "Calculator", StringComparison.OrdinalIgnoreCase)) return Symbol.Calculator;
|
||||||
|
if (string.Equals(categoryId, "Study", StringComparison.OrdinalIgnoreCase)) return Symbol.Hourglass;
|
||||||
|
if (string.Equals(categoryId, "File", StringComparison.OrdinalIgnoreCase)) return Symbol.Folder;
|
||||||
|
return Symbol.Apps;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 分类本地化标题 - 与阑山桌面 Dock 栏组件库 (MainWindow.ComponentSystem) 保持一致
|
||||||
|
/// </summary>
|
||||||
|
private string GetLocalizedCategoryTitle(string languageCode, string categoryId)
|
||||||
|
{
|
||||||
|
if (string.Equals(categoryId, "Clock", StringComparison.OrdinalIgnoreCase)) return L(languageCode, "component_category.clock", "Clock");
|
||||||
|
if (string.Equals(categoryId, "Date", StringComparison.OrdinalIgnoreCase)) return L(languageCode, "component_category.date", "Calendar");
|
||||||
|
if (string.Equals(categoryId, "Weather", StringComparison.OrdinalIgnoreCase)) return L(languageCode, "component_category.weather", "Weather");
|
||||||
|
if (string.Equals(categoryId, "Board", StringComparison.OrdinalIgnoreCase)) return L(languageCode, "component_category.board", "Board");
|
||||||
|
if (string.Equals(categoryId, "Media", StringComparison.OrdinalIgnoreCase)) return L(languageCode, "component_category.media", "Media");
|
||||||
|
if (string.Equals(categoryId, "Info", StringComparison.OrdinalIgnoreCase)) return L(languageCode, "component_category.info", "Info");
|
||||||
|
if (string.Equals(categoryId, "Calculator", StringComparison.OrdinalIgnoreCase)) return L(languageCode, "component_category.calculator", "Calculator");
|
||||||
|
if (string.Equals(categoryId, "Study", StringComparison.OrdinalIgnoreCase)) return L(languageCode, "component_category.study", "Study");
|
||||||
|
if (string.Equals(categoryId, "File", StringComparison.OrdinalIgnoreCase)) return L(languageCode, "component_category.file", "File");
|
||||||
|
return categoryId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string L(string languageCode, string key, string fallback)
|
||||||
|
{
|
||||||
|
return _localizationService.GetString(languageCode, key, fallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ComponentLibraryItemViewModel CreateComponentItem(DesktopComponentDefinition definition)
|
private ComponentLibraryItemViewModel CreateComponentItem(DesktopComponentDefinition definition)
|
||||||
@@ -130,6 +170,7 @@ public partial class FusedDesktopComponentLibraryControl : UserControl
|
|||||||
definition.Id,
|
definition.Id,
|
||||||
definition.DisplayName,
|
definition.DisplayName,
|
||||||
previewKey,
|
previewKey,
|
||||||
|
description: null,
|
||||||
"正在加载预览...",
|
"正在加载预览...",
|
||||||
"预览不可用",
|
"预览不可用",
|
||||||
previewEntry);
|
previewEntry);
|
||||||
@@ -158,25 +199,49 @@ public partial class FusedDesktopComponentLibraryControl : UserControl
|
|||||||
|
|
||||||
private void OnCategorySelectionChanged(object? sender, SelectionChangedEventArgs e)
|
private void OnCategorySelectionChanged(object? sender, SelectionChangedEventArgs e)
|
||||||
{
|
{
|
||||||
FilterComponents();
|
UpdateSelectedComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FilterComponents()
|
private void UpdateSelectedComponent()
|
||||||
{
|
{
|
||||||
var selectedCategory = (CategoryListBox.SelectedItem as ComponentLibraryCategoryViewModel)?.Id;
|
var selectedCategory = CategoryListBox.SelectedItem as ComponentLibraryCategoryViewModel;
|
||||||
var searchText = SearchBox.Text?.ToLower() ?? "";
|
if (selectedCategory is null)
|
||||||
|
{
|
||||||
|
_viewModel.SelectedComponent = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var filtered = _allDefinitions.Where(d =>
|
// 获取该分类下的组件列表
|
||||||
|
IEnumerable<DesktopComponentDefinition> filtered;
|
||||||
|
if (selectedCategory.Id == "all")
|
||||||
{
|
{
|
||||||
var matchesCategory = selectedCategory == "all" || string.Equals(d.Category, selectedCategory, StringComparison.OrdinalIgnoreCase);
|
filtered = _allDefinitions.OrderBy(d => d.DisplayName);
|
||||||
var matchesSearch = string.IsNullOrEmpty(searchText) || d.DisplayName.ToLower().Contains(searchText) || d.Id.ToLower().Contains(searchText);
|
}
|
||||||
return matchesCategory && matchesSearch;
|
else
|
||||||
});
|
{
|
||||||
|
filtered = _allDefinitions
|
||||||
|
.Where(d => string.Equals(d.Category, selectedCategory.Id, StringComparison.OrdinalIgnoreCase))
|
||||||
|
.OrderBy(d => d.DisplayName);
|
||||||
|
}
|
||||||
|
|
||||||
_viewModel.Components.Clear();
|
// 选择该分类下的第一个组件作为默认选中
|
||||||
foreach (var def in filtered)
|
var firstComponent = filtered.FirstOrDefault();
|
||||||
|
if (firstComponent is not null)
|
||||||
{
|
{
|
||||||
_viewModel.Components.Add(CreateComponentItem(def));
|
// 查找或创建对应的 ViewModel
|
||||||
|
var existingComponent = selectedCategory.Components.FirstOrDefault(c => c.ComponentId == firstComponent.Id);
|
||||||
|
if (existingComponent is not null)
|
||||||
|
{
|
||||||
|
_viewModel.SelectedComponent = existingComponent;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_viewModel.SelectedComponent = CreateComponentItem(firstComponent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_viewModel.SelectedComponent = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,4 +252,22 @@ public partial class FusedDesktopComponentLibraryControl : UserControl
|
|||||||
AddComponentRequested?.Invoke(this, componentId);
|
AddComponentRequested?.Invoke(this, componentId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnFindMoreComponentsClick(object? sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
// 打开设置窗口并导航到插件目录页面
|
||||||
|
if (Application.Current is App app && app.SettingsWindowService is { } settingsWindowService)
|
||||||
|
{
|
||||||
|
var mainWindow = (Application.Current?.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime)?.MainWindow as MainWindow;
|
||||||
|
var request = new SettingsWindowOpenRequest(
|
||||||
|
Source: "FusedDesktopComponentLibrary",
|
||||||
|
Owner: mainWindow,
|
||||||
|
PageId: "plugin-catalog");
|
||||||
|
settingsWindowService.Open(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭所在窗口
|
||||||
|
var window = this.FindAncestorOfType<Window>();
|
||||||
|
window?.Close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,57 +1,73 @@
|
|||||||
<Window xmlns="https://github.com/avaloniaui"
|
<Window xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:controls="using:LanMountainDesktop.Views"
|
xmlns:controls="using:LanMountainDesktop.Views"
|
||||||
xmlns:fi="using:FluentIcons.Avalonia"
|
xmlns:fi="using:FluentIcons.Avalonia.Fluent"
|
||||||
x:Class="LanMountainDesktop.Views.FusedDesktopComponentLibraryWindow"
|
x:Class="LanMountainDesktop.Views.FusedDesktopComponentLibraryWindow"
|
||||||
Width="860" Height="620"
|
Width="860" Height="620"
|
||||||
MinWidth="600" MinHeight="500"
|
MinWidth="600" MinHeight="500"
|
||||||
|
CanResize="True"
|
||||||
WindowStartupLocation="CenterScreen"
|
WindowStartupLocation="CenterScreen"
|
||||||
SystemDecorations="Full"
|
SystemDecorations="BorderOnly"
|
||||||
ExtendClientAreaToDecorationsHint="True"
|
ExtendClientAreaToDecorationsHint="True"
|
||||||
ExtendClientAreaChromeHints="NoChrome"
|
ExtendClientAreaChromeHints="NoChrome"
|
||||||
ExtendClientAreaTitleBarHeightHint="-1"
|
ExtendClientAreaTitleBarHeightHint="48"
|
||||||
Background="Transparent"
|
Background="Transparent"
|
||||||
TransparencyLevelHint="Mica"
|
Title="添加小组件">
|
||||||
Title="融合桌面组件库">
|
|
||||||
|
|
||||||
<Panel>
|
<Grid x:Name="RootGrid"
|
||||||
<!-- 背景磨砂效果 -->
|
Classes="settings-scope"
|
||||||
<Border Background="{DynamicResource AdaptiveSurfaceLowBrush}"
|
Background="{DynamicResource AdaptiveSettingsWindowBackgroundBrush}"
|
||||||
Opacity="0.85" />
|
RowDefinitions="Auto,*">
|
||||||
|
<!-- 自定义标题栏 - 与 SettingsWindow 风格一致 -->
|
||||||
|
<Border x:Name="WindowTitleBarHost"
|
||||||
|
Height="48"
|
||||||
|
Padding="12,0,12,0"
|
||||||
|
Background="{DynamicResource AdaptiveSettingsWindowBackgroundBrush}"
|
||||||
|
BorderBrush="{DynamicResource AdaptiveSettingsWindowBorderBrush}"
|
||||||
|
BorderThickness="0,0,0,1"
|
||||||
|
PointerPressed="OnWindowTitleBarPointerPressed">
|
||||||
|
<Grid ColumnDefinitions="Auto,Auto,*,Auto"
|
||||||
|
ColumnSpacing="8"
|
||||||
|
VerticalAlignment="Center">
|
||||||
|
<fi:FluentIcon x:Name="WindowBrandIcon"
|
||||||
|
Icon="Apps"
|
||||||
|
IconVariant="Filled"
|
||||||
|
FontSize="16"
|
||||||
|
IsHitTestVisible="False"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
|
||||||
<Grid RowDefinitions="Auto,*">
|
<TextBlock x:Name="WindowTitleTextBlock"
|
||||||
<!-- 自定义标题栏 -->
|
Grid.Column="1"
|
||||||
<Border Background="Transparent"
|
FontSize="12"
|
||||||
IsHitTestVisible="True"
|
|
||||||
Padding="20,16">
|
|
||||||
<Grid ColumnDefinitions="*,Auto">
|
|
||||||
<StackPanel Spacing="6" VerticalAlignment="Center">
|
|
||||||
<TextBlock Text="融合桌面组件库"
|
|
||||||
FontWeight="SemiBold"
|
FontWeight="SemiBold"
|
||||||
FontSize="20"
|
IsHitTestVisible="False"
|
||||||
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}" />
|
Text="添加小组件" />
|
||||||
<TextBlock Text="将精美组件放置在您的系统桌面上(负一屏)"
|
|
||||||
Opacity="0.6"
|
|
||||||
FontSize="13"
|
|
||||||
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}" />
|
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
<Button Grid.Column="1"
|
<TextBlock Grid.Column="2"
|
||||||
Classes="accent"
|
FontSize="12"
|
||||||
Width="36" Height="36"
|
Opacity="0.6"
|
||||||
|
IsHitTestVisible="False"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="将精美组件放置在您的系统桌面上(负一屏)" />
|
||||||
|
|
||||||
|
<Button x:Name="CloseWindowButton"
|
||||||
|
Grid.Column="3"
|
||||||
|
Width="40"
|
||||||
|
Height="32"
|
||||||
Padding="0"
|
Padding="0"
|
||||||
CornerRadius="18"
|
Background="Transparent"
|
||||||
BorderThickness="0"
|
BorderThickness="0"
|
||||||
Background="{DynamicResource AdaptiveButtonHoverBackgroundBrush}"
|
|
||||||
Click="OnCloseClick">
|
Click="OnCloseClick">
|
||||||
<fi:SymbolIcon Symbol="Dismiss" FontSize="18" />
|
<fi:FluentIcon Icon="Dismiss"
|
||||||
|
IconVariant="Regular"
|
||||||
|
FontSize="16" />
|
||||||
</Button>
|
</Button>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<!-- 组件库控件 -->
|
<!-- 组件库控件 -->
|
||||||
<controls:FusedDesktopComponentLibraryControl x:Name="LibraryControl"
|
<controls:FusedDesktopComponentLibraryControl x:Name="LibraryControl"
|
||||||
Grid.Row="1" />
|
Grid.Row="1"
|
||||||
|
Margin="12,8,16,8" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</Panel>
|
|
||||||
</Window>
|
</Window>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Input;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using LanMountainDesktop.ComponentSystem;
|
using LanMountainDesktop.ComponentSystem;
|
||||||
using LanMountainDesktop.Services;
|
using LanMountainDesktop.Services;
|
||||||
@@ -103,6 +104,14 @@ public partial class FusedDesktopComponentLibraryWindow : Window
|
|||||||
Close();
|
Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnWindowTitleBarPointerPressed(object? sender, PointerPressedEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
|
||||||
|
{
|
||||||
|
BeginMoveDrag(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnClosed(EventArgs e)
|
protected override void OnClosed(EventArgs e)
|
||||||
{
|
{
|
||||||
base.OnClosed(e);
|
base.OnClosed(e);
|
||||||
|
|||||||
@@ -20,6 +20,14 @@
|
|||||||
#define MyAppArch "x64"
|
#define MyAppArch "x64"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef MyAppSuffix
|
||||||
|
#define MyAppSuffix ""
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef IsSelfContained
|
||||||
|
#define IsSelfContained "true"
|
||||||
|
#endif
|
||||||
|
|
||||||
[Setup]
|
[Setup]
|
||||||
AppId={#MyAppId}
|
AppId={#MyAppId}
|
||||||
AppName={#MyAppName}
|
AppName={#MyAppName}
|
||||||
@@ -34,7 +42,7 @@ LanguageDetectionMethod=uilanguage
|
|||||||
DefaultGroupName={cm:AppShortcutName}
|
DefaultGroupName={cm:AppShortcutName}
|
||||||
UninstallDisplayIcon={app}\{#MyAppExeName}
|
UninstallDisplayIcon={app}\{#MyAppExeName}
|
||||||
OutputDir={#MyOutputDir}
|
OutputDir={#MyOutputDir}
|
||||||
OutputBaseFilename={#MyAppName}-Setup-{#MyAppVersion}-{#MyAppArch}
|
OutputBaseFilename={#MyAppName}-Setup-{#MyAppVersion}-{#MyAppArch}{#MyAppSuffix}
|
||||||
Compression=lzma2/ultra64
|
Compression=lzma2/ultra64
|
||||||
SolidCompression=yes
|
SolidCompression=yes
|
||||||
WizardStyle=modern
|
WizardStyle=modern
|
||||||
@@ -93,7 +101,17 @@ chinesesimplified.UpgradeCleanupMissingUninstaller=安装程序发现了现有
|
|||||||
english.UpgradeCleanupFailedPrefix=Setup could not remove the existing installation automatically. Error code:
|
english.UpgradeCleanupFailedPrefix=Setup could not remove the existing installation automatically. Error code:
|
||||||
chinesesimplified.UpgradeCleanupFailedPrefix=安装程序无法自动移除现有安装。错误代码:
|
chinesesimplified.UpgradeCleanupFailedPrefix=安装程序无法自动移除现有安装。错误代码:
|
||||||
english.UpgradeCleanupFailedSuffix=Please close LanMountainDesktop, uninstall the current version manually, and then run this installer again.
|
english.UpgradeCleanupFailedSuffix=Please close LanMountainDesktop, uninstall the current version manually, and then run this installer again.
|
||||||
chinesesimplified.UpgradeCleanupFailedSuffix=请关闭 LanMountainDesktop,手动卸载当前版本,然后重新运行此安装程序。
|
chinesesimplified.UpgradeCleanupFailedSuffix=请关闭 LanMountain Desktop,手动卸载当前版本,然后重新运行此安装程序。
|
||||||
|
english.DotNetRuntimeMissingTitle=.NET Desktop Runtime Required
|
||||||
|
chinesesimplified.DotNetRuntimeMissingTitle=需要 .NET Desktop Runtime
|
||||||
|
english.DotNetRuntimeMissingMessage=This application requires .NET 10.0 Desktop Runtime to run.
|
||||||
|
chinesesimplified.DotNetRuntimeMissingMessage=此应用程序需要 .NET 10.0 Desktop Runtime 才能运行。
|
||||||
|
english.DotNetRuntimeMissingAction=Click "Yes" to open the official download page. Install it first, then run this installer again.
|
||||||
|
chinesesimplified.DotNetRuntimeMissingAction=单击"是"打开官方下载页面。请先完成安装,然后重新运行此安装程序。
|
||||||
|
english.DotNetRuntimeOpenFailedMessage=Unable to open the download page automatically.
|
||||||
|
chinesesimplified.DotNetRuntimeOpenFailedMessage=无法自动打开下载页面。
|
||||||
|
english.DotNetRuntimeOpenFailedAction=Please open this URL manually:
|
||||||
|
chinesesimplified.DotNetRuntimeOpenFailedAction=请手动打开以下链接:
|
||||||
|
|
||||||
[Tasks]
|
[Tasks]
|
||||||
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"
|
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"
|
||||||
@@ -127,6 +145,7 @@ const
|
|||||||
UninstallRegSubkey = 'Software\Microsoft\Windows\CurrentVersion\Uninstall\{#MyAppRegistryId}_is1';
|
UninstallRegSubkey = 'Software\Microsoft\Windows\CurrentVersion\Uninstall\{#MyAppRegistryId}_is1';
|
||||||
WebView2RuntimeKeyPath = 'SOFTWARE\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}';
|
WebView2RuntimeKeyPath = 'SOFTWARE\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}';
|
||||||
WebView2RuntimeDownloadUrl = 'https://go.microsoft.com/fwlink/p/?LinkId=2124703';
|
WebView2RuntimeDownloadUrl = 'https://go.microsoft.com/fwlink/p/?LinkId=2124703';
|
||||||
|
DotNetRuntimeDownloadUrl = 'https://dotnet.microsoft.com/download/dotnet/10.0';
|
||||||
UpgradeChoiceInPlace = 0;
|
UpgradeChoiceInPlace = 0;
|
||||||
UpgradeChoiceRelocate = 1;
|
UpgradeChoiceRelocate = 1;
|
||||||
|
|
||||||
@@ -435,10 +454,58 @@ begin
|
|||||||
RegQueryStringValue(HKCU32, WebView2RuntimeKeyPath, 'pv', VersionValue);
|
RegQueryStringValue(HKCU32, WebView2RuntimeKeyPath, 'pv', VersionValue);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function IsDotNetDesktopRuntimeInstalled(): Boolean;
|
||||||
|
var
|
||||||
|
RuntimePath: String;
|
||||||
|
begin
|
||||||
|
Result := False;
|
||||||
|
|
||||||
|
RuntimePath := ExpandConstant('{commonpf64}\dotnet\shared\Microsoft.WindowsDesktop.App');
|
||||||
|
if DirExists(RuntimePath) then
|
||||||
|
begin
|
||||||
|
Result := True;
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
|
|
||||||
|
RuntimePath := ExpandConstant('{commonpf}\dotnet\shared\Microsoft.WindowsDesktop.App');
|
||||||
|
if DirExists(RuntimePath) then
|
||||||
|
begin
|
||||||
|
Result := True;
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
function InitializeSetup(): Boolean;
|
function InitializeSetup(): Boolean;
|
||||||
var
|
var
|
||||||
ErrorCode: Integer;
|
ErrorCode: Integer;
|
||||||
|
IsSelfContainedBuild: Boolean;
|
||||||
begin
|
begin
|
||||||
|
IsSelfContainedBuild := ('{#IsSelfContained}' = 'true');
|
||||||
|
|
||||||
|
if not IsSelfContainedBuild then
|
||||||
|
begin
|
||||||
|
if not IsDotNetDesktopRuntimeInstalled() then
|
||||||
|
begin
|
||||||
|
if MsgBox(
|
||||||
|
CustomMessage('DotNetRuntimeMissingMessage') + #13#10#13#10 +
|
||||||
|
CustomMessage('DotNetRuntimeMissingAction'),
|
||||||
|
mbConfirmation,
|
||||||
|
MB_YESNO) = IDYES then
|
||||||
|
begin
|
||||||
|
if not ShellExec('open', DotNetRuntimeDownloadUrl, '', '', SW_SHOWNORMAL, ewNoWait, ErrorCode) then
|
||||||
|
begin
|
||||||
|
MsgBox(
|
||||||
|
CustomMessage('DotNetRuntimeOpenFailedMessage') + #13#10 +
|
||||||
|
CustomMessage('DotNetRuntimeOpenFailedAction') + #13#10 + DotNetRuntimeDownloadUrl,
|
||||||
|
mbError,
|
||||||
|
MB_OK);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
Result := False;
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
if IsWebView2RuntimeInstalled() then
|
if IsWebView2RuntimeInstalled() then
|
||||||
begin
|
begin
|
||||||
Result := True;
|
Result := True;
|
||||||
|
|||||||
683
design.md
Normal file
683
design.md
Normal file
@@ -0,0 +1,683 @@
|
|||||||
|
# UI Design System Guide (design.md)
|
||||||
|
|
||||||
|
> **目标**: 让 AI 正确使用 Fluent Avalonia / Fluent Icons / Material Avalonia,避免窗口套窗口、容器套容器
|
||||||
|
>
|
||||||
|
> **最后更新**: 2026-04-11
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 一句话总结
|
||||||
|
|
||||||
|
**主界面用 Fluent + FluentIcon,编辑器用 Material + MaterialIcon,永远不要混用,保持扁平结构。**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. 技术栈与职责
|
||||||
|
|
||||||
|
### 1.1 库清单
|
||||||
|
|
||||||
|
| 库 | 包名 | 什么时候用 |
|
||||||
|
|---|------|----------|
|
||||||
|
| **FluentAvaloniaUI** | `FluentAvaloniaUI` | 主界面、设置页、导航 |
|
||||||
|
| **FluentIcons.Avalonia.Fluent** | `FluentIcons.Avalonia.Fluent` | 主界面图标(**首选**)|
|
||||||
|
| **FluentIcons.Avalonia** | `FluentIcons.Avalonia` | 旧图标兼容(SymbolIcon)|
|
||||||
|
| **Material.Icons.Avalonia** | `Material.Icons.Avalonia` | 编辑器图标(**仅限 ComponentEditorWindow**)|
|
||||||
|
| **Material.Avalonia** | `Material.Avalonia` | MD3 主题(**仅限 ComponentEditorWindow**)|
|
||||||
|
|
||||||
|
### 1.2 初始化顺序(App.axaml)
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<Application.Styles>
|
||||||
|
<sty:FluentAvaloniaTheme /> <!-- 第 1 位:基础 Win11 风格 -->
|
||||||
|
<mi:MaterialIconStyles /> <!-- 第 2 位:全局注册 Material Icons 样式 -->
|
||||||
|
|
||||||
|
<!-- 项目自定义样式 -->
|
||||||
|
<StyleInclude Source="avares://LanMountainDesktop/Styles/FlutermotionToken.axaml" />
|
||||||
|
<StyleInclude Source="avares://LanMountainDesktop/Styles/GlassModule.axaml" />
|
||||||
|
<StyleInclude Source="avares://LanMountainDesktop/Styles/SettingsAnimations.axaml" />
|
||||||
|
<StyleInclude Source="avares://LanMountainDesktop/Styles/SettingsCardStyles.axaml" />
|
||||||
|
<StyleInclude Source="avares://LanMountainDesktop/Styles/NavigationStyles.axaml" />
|
||||||
|
</Application.Styles>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 命名空间速查表
|
||||||
|
|
||||||
|
**复制粘贴用:**
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<!-- 必需 -->
|
||||||
|
xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
|
||||||
|
<!-- FluentAvaloniaUI 控件(主界面/设置页必需)-->
|
||||||
|
xmlns:ui="using:FluentAvalonia.UI.Controls"
|
||||||
|
|
||||||
|
<!-- Fluent Icons - 新版推荐(主界面首选)-->
|
||||||
|
xmlns:fi="using:FluentIcons.Avalonia.Fluent"
|
||||||
|
|
||||||
|
<!-- Fluent Icons - 旧版兼容(已有代码维护用)-->
|
||||||
|
<!-- xmlns:fi-legacy="using:FluentIcons.Avalonia" -->
|
||||||
|
|
||||||
|
<!-- Material Icons(仅 ComponentEditorWindow 使用)-->
|
||||||
|
<!-- xmlns:mi="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia" -->
|
||||||
|
|
||||||
|
<!-- Material Theme(仅 ComponentEditorWindow 使用)-->
|
||||||
|
<!-- xmlns:themes="clr-namespace:Material.Styles.Themes;assembly=Material.Styles" -->
|
||||||
|
|
||||||
|
<!-- 项目控件 -->
|
||||||
|
xmlns:local="using:LanMountainDesktop.Controls"
|
||||||
|
xmlns:comp="using:LanMountainDesktop.Views.Components"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 图标系统
|
||||||
|
|
||||||
|
### 3.1 选择决策树
|
||||||
|
|
||||||
|
```
|
||||||
|
你在写什么?
|
||||||
|
├─ 设置页面 / 主界面 / 桌面组件?
|
||||||
|
│ └─ 用 FluentIcon(fi:FluentIcon)
|
||||||
|
│ ├─ 需要 Filled/Regular 切换?→ IconVariant="Filled" 或 "Regular"
|
||||||
|
│ └─ 简单静态图标?→ 也用 FluentIcon,不用 SymbolIcon
|
||||||
|
│
|
||||||
|
├─ ComponentEditorWindow 及其子页面?
|
||||||
|
│ └─ 用 MaterialIcon(mi:MaterialIcon)
|
||||||
|
│
|
||||||
|
└─ 其他情况?
|
||||||
|
└─ 默认 FluentIcon
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.2 FluentIcon 使用方法
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<fi:FluentIcon Icon="Settings" <!-- 图标名称 -->
|
||||||
|
IconVariant="Filled" <!-- Filled | Regular -->
|
||||||
|
Classes="icon-m" /> <!-- icon-s(12px) | icon-m(16px) | icon-l(20px) -->
|
||||||
|
```
|
||||||
|
|
||||||
|
**常用图标名称:**
|
||||||
|
- 导航类:`Home`, `Settings`, `Navigation`, `ArrowLeft`, `ChevronRight`, `Dismiss`
|
||||||
|
- 操作类:`Add`, `Delete`, `Edit`, `Save`, `Refresh`, `Sync`, `ArrowSync`
|
||||||
|
- 状态类:`Info`, `Warning`, `ErrorBadge`, `CheckmarkCircle`
|
||||||
|
- 外观类:`ThemeLightDark`, `ColorBackground`, `Appearance`
|
||||||
|
|
||||||
|
### 3.3 MaterialIcon 使用方法(仅限编辑器)
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<mi:MaterialIcon Kind="Close" <!-- 图标名称 -->
|
||||||
|
Width="24"
|
||||||
|
Height="24"
|
||||||
|
Foreground="{DynamicResource EditorPrimaryBrush}" />
|
||||||
|
```
|
||||||
|
|
||||||
|
**常用 Kind 值:**
|
||||||
|
- 操作:`Close`, `Check`, `Pencil`, `Delete`, `Settings`, `Plus`
|
||||||
|
- 导航:`ArrowLeft`, `ArrowRight`, `Home`, `Menu`
|
||||||
|
- 系统:`Power`, `Lock`, `ExitToApp`, `Refresh`, `WeatherNight`
|
||||||
|
|
||||||
|
### 3.4 ❌ 禁止事项
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<!-- ❌ 错误:同一区域混用两种图标库 -->
|
||||||
|
<StackPanel>
|
||||||
|
<fi:FluentIcon Icon="Home" /> <!-- Fluent -->
|
||||||
|
<mi:MaterialIcon Kind="Settings" /> <!-- Material -->
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<!-- ❌ 错误:硬编码尺寸 -->
|
||||||
|
<fi:FluentIcon Icon="Settings" FontSize="18" />
|
||||||
|
|
||||||
|
<!-- ✅ 正确:使用预定义 class -->
|
||||||
|
<fi:FluentIcon Icon="Settings" Classes="icon-m" />
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. 容器嵌套规范(核心!)
|
||||||
|
|
||||||
|
### 4.1 最大深度限制
|
||||||
|
|
||||||
|
| 场景 | 最大层数 | 从哪里开始数 |
|
||||||
|
|-----|---------|------------|
|
||||||
|
| 普通页面 | **≤ 4 层** | Window/UserControl → ... → 叶子节点 |
|
||||||
|
| Popup/Dialog | **≤ 3 层** | Border → Content |
|
||||||
|
| 列表项/DataTemplate | **≤ 3 层** | Root → ... → 元素 |
|
||||||
|
| MainWindow 桌面布局 | **≤ 6 层** | 特殊允许(多层叠加需求)|
|
||||||
|
|
||||||
|
### 4.2 如何数层级?
|
||||||
|
|
||||||
|
从根元素到目标元素经过的容器标签数:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<UserControl> <!-- 层级 0(根)-->
|
||||||
|
<Border> <!-- 层级 1 -->
|
||||||
|
<Grid> <!-- 层级 2 -->
|
||||||
|
<StackPanel> <!-- 层级 3 -->
|
||||||
|
<Button> <!-- 层级 4(叶子节点)✅ OK -->
|
||||||
|
</Button>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
</UserControl>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.3 推荐的标准结构
|
||||||
|
|
||||||
|
#### 结构 A:标准设置页面(3-4 层)
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<UserControl>
|
||||||
|
<ScrollViewer> <!-- 层 1 -->
|
||||||
|
<StackPanel Spacing="24"> <!-- 层 2 -->
|
||||||
|
<Border Classes="settings-section-card"> <!-- 层 3 -->
|
||||||
|
<StackPanel Spacing="16"> <!-- 层 4 ✅ -->
|
||||||
|
<!-- 内容 -->
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
</StackPanel>
|
||||||
|
</ScrollViewer>
|
||||||
|
</UserControl>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 结构 B:卡片/面板组件(3 层)
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<Border CornerRadius="..." Padding="..."> <!-- 层 1(根)-->
|
||||||
|
<Grid RowDefinitions="Auto,*"> <!-- 层 2 -->
|
||||||
|
<Grid ColumnDefinitions="Auto,*,Auto"> <!-- 层 3a: Header -->
|
||||||
|
<fi:FluentIcon Classes="icon-l" />
|
||||||
|
<TextBlock Grid.Column="1" />
|
||||||
|
<ContentPresenter Grid.Column="2" />
|
||||||
|
</Grid>
|
||||||
|
<ContentControl Grid.Row="1" /> <!-- 层 3b: Body ✅ -->
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 结构 C:列表项(2-3 层)
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<Border Classes="list-item"> <!-- 层 1 -->
|
||||||
|
<Grid ColumnDefinitions="Auto,*,Auto"> <!-- 层 2 -->
|
||||||
|
<Border Classes="icon-host"> <!-- 层 3a -->
|
||||||
|
<fi:FluentIcon Classes="icon-m" />
|
||||||
|
</Border>
|
||||||
|
<StackPanel Grid.Column="1" Spacing="4"> <!-- 层 3b -->
|
||||||
|
<TextBlock Classes="title" />
|
||||||
|
<TextBlock Classes="subtitle" />
|
||||||
|
</StackPanel>
|
||||||
|
<Button Grid.Column="2">...</Button> <!-- 层 3c ✅ -->
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.4 ❌ 反模式:过度嵌套
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<!-- ❌ 错误:7 层嵌套(实际项目中出现的反面教材)-->
|
||||||
|
<Grid> <!-- 1 -->
|
||||||
|
<Grid> <!-- 2 -->
|
||||||
|
<Border> <!-- 3 -->
|
||||||
|
<Grid> <!-- 4 -->
|
||||||
|
<Border> <!-- 5 -->
|
||||||
|
<Grid> <!-- 6 -->
|
||||||
|
<Border> <!-- 7 ❌ 太深了!-->
|
||||||
|
<Button Content="Click" />
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<!-- ✅ 重构后:2 层 -->
|
||||||
|
<Grid HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center">
|
||||||
|
<Button Content="Click"
|
||||||
|
Padding="16,8" />
|
||||||
|
</Grid>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.5 何时可以超过限制?
|
||||||
|
|
||||||
|
只有这 3 种情况:
|
||||||
|
|
||||||
|
1. **MainWindow 桌面布局**(壁纸层 + 组件层 + 拖拽层 + 任务栏)
|
||||||
|
2. **需要独立动画层**(Transform/Opacity 动画需要单独容器)
|
||||||
|
3. **复杂 Popup 内部**
|
||||||
|
|
||||||
|
**必须加注释说明原因:**
|
||||||
|
```xml
|
||||||
|
<!--
|
||||||
|
允许深层嵌套原因:桌面渲染需要支持以下视觉层级
|
||||||
|
- DesktopWallpaperLayer(壁纸背景)
|
||||||
|
- DesktopPagesContainer(桌面分页)
|
||||||
|
- LauncherPagePanel(启动器面板)
|
||||||
|
- Canvas 拖拽层
|
||||||
|
- BottomTaskbarContainer(任务栏)
|
||||||
|
-->
|
||||||
|
<Grid x:Name="DesktopHost">
|
||||||
|
...
|
||||||
|
</Grid>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. 窗口 vs UserControl vs Border
|
||||||
|
|
||||||
|
### 5.1 什么时候用什么?
|
||||||
|
|
||||||
|
| 需求 | 用什么 | 示例 |
|
||||||
|
|-----|-------|------|
|
||||||
|
| 独立窗口(有标题栏、可拖动、任务栏可见)| **Window** | SettingsWindow, ComponentEditorWindow |
|
||||||
|
| 可复用的 UI 组件块 | **UserControl** | SettingsOptionCard, ClockWidget |
|
||||||
|
| 视觉上的卡片/面板/容器 | **Border** | 设置分区卡片、弹出面板 |
|
||||||
|
|
||||||
|
### 5.2 ❌ 禁止:窗口套窗口
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// ❌ 错误:在 XAML 中实例化另一个 Window
|
||||||
|
// <local:SettingsWindow Visibility="Visible" />
|
||||||
|
|
||||||
|
// ✅ 正确:通过代码显示独立窗口
|
||||||
|
var settings = new SettingsWindow { Owner = this };
|
||||||
|
settings.Show();
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.3 ❌ 禁止:把 Window 当 UserControl 用
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<!-- ❌ 错误:MainWindow 内部嵌入了一个本应是独立窗口的东西 -->
|
||||||
|
<Window x:Class="MainWindow">
|
||||||
|
<Grid>
|
||||||
|
<Border x:Name="ComponentLibraryWindow" <!-- 这看起来像窗口,但不应该是 Window -->
|
||||||
|
Width="620"
|
||||||
|
Height="320"
|
||||||
|
CornerRadius="36">
|
||||||
|
<!-- 组件库内容 -->
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
|
```
|
||||||
|
|
||||||
|
**如果它不是真正的操作系统窗口,就用 Border 或 UserControl。**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. 颜色与资源使用规范
|
||||||
|
|
||||||
|
### 6.1 必须使用 DynamicResource
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<!-- ❌ 错误:硬编码颜色 -->
|
||||||
|
<TextBlock Foreground="#FF1D1B20" />
|
||||||
|
<Border Background="#FFF3EDF7" />
|
||||||
|
<Button Background="#FF6750A4" />
|
||||||
|
|
||||||
|
<!-- ✅ 正确:使用动态资源 -->
|
||||||
|
<TextBlock Foreground="{DynamicResource AdaptiveTextPrimaryBrush}" />
|
||||||
|
<Border Background="{DynamicResource AdaptiveSurfaceRaisedBrush}" />
|
||||||
|
<Button Background="{DynamicResource AccentBrush}" />
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.2 常用资源键速查
|
||||||
|
|
||||||
|
| 资源键 | 用途 | 示例场景 |
|
||||||
|
|--------|------|---------|
|
||||||
|
| `AdaptiveTextPrimaryBrush` | 主要文本 | 标题、正文 |
|
||||||
|
| `AdaptiveTextSecondaryBrush` | 次要文本 | 描述文字、提示 |
|
||||||
|
| `AdaptiveSurfaceRaisedBrush` | 抬高表面 | 卡片背景、面板 |
|
||||||
|
| `AdaptiveSurfaceOverlayBrush` | 覆盖层 | 遮罩、弹窗背景 |
|
||||||
|
| `AccentBrush` | 强调色 | 主按钮、选中态 |
|
||||||
|
| `AppFontFamily` | 应用字体 | 全局字体设置 |
|
||||||
|
|
||||||
|
### 6.3 圆角 Token(强制!)
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<!-- ❌ 错误:硬编码圆角 -->
|
||||||
|
<Border CornerRadius="8" />
|
||||||
|
<Button CornerRadius="12" />
|
||||||
|
|
||||||
|
<!-- ❌ 错误:桌面组件根容器用了非组件级 Token -->
|
||||||
|
<Border CornerRadius="{DynamicResource DesignCornerRadiusMd}" />
|
||||||
|
|
||||||
|
<!-- ✅ 正确 -->
|
||||||
|
<!-- 桌面组件(Widget)根容器必须且只能用这个 -->
|
||||||
|
<Border CornerRadius="{DynamicResource DesignCornerRadiusComponent}" />
|
||||||
|
|
||||||
|
<!-- 内部元素按层级选择 -->
|
||||||
|
<Border CornerRadius="{DynamicResource DesignCornerRadiusSm}" /> <!-- 小 -->
|
||||||
|
<Border CornerRadius="{DynamicResource DesignCornerRadiusMd}" /> <!-- 中 -->
|
||||||
|
<Border CornerRadius="{DynamicResource DesignCornerRadiusLg}" /> <!-- 大 -->
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. FluentAvaloniaUI 控件用法
|
||||||
|
|
||||||
|
### 7.1 NavigationView(设置页导航)
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<ui:NavigationView x:Name="RootNav"
|
||||||
|
PaneDisplayMode="Auto" <!-- 自动折叠 -->
|
||||||
|
OpenPaneLength="283" <!-- Win11 标准宽度 -->
|
||||||
|
IsSettingsVisible="False" <!-- 隐藏默认设置按钮 -->
|
||||||
|
IsBackButtonVisible="False"
|
||||||
|
SelectionChanged="OnNavChanged">
|
||||||
|
|
||||||
|
<ui:NavigationView.Resources>
|
||||||
|
<!-- 移除默认背景色 -->
|
||||||
|
<SolidColorBrush x:Key="NavigationViewContentBackground" Color="Transparent" />
|
||||||
|
<SolidColorBrush x:Key="NavigationViewContentGridBorderBrush" Color="Transparent" />
|
||||||
|
</ui:NavigationView.Resources>
|
||||||
|
|
||||||
|
<Grid Margin="12,0,16,16">
|
||||||
|
<ui:Frame x:Name="ContentFrame" />
|
||||||
|
</Grid>
|
||||||
|
</ui:NavigationView>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.2 Frame(页面导航容器)
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<ui:Frame x:Name="ContentFrame" />
|
||||||
|
```
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// C# 代码中导航
|
||||||
|
ContentFrame.Navigate(typeof(SettingsHomePage));
|
||||||
|
// 或绑定
|
||||||
|
ContentFrame.SourcePageType = typeof(SettingsHomePage);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.3 InfoBar(内联通知条)
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<ui:InfoBar Title="发现新版本"
|
||||||
|
Message="v2.0.0 可用"
|
||||||
|
Severity="Informational" <!-- Informational | Warning | Error | Success -->
|
||||||
|
IsOpen="True"
|
||||||
|
ActionButtonText="立即更新"
|
||||||
|
ActionButtonClick="OnUpdateClick"
|
||||||
|
CloseButtonClick="OnDismissClick" />
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.4 ContentDialog(模态对话框)
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var dialog = new ContentDialog
|
||||||
|
{
|
||||||
|
Title = "确认删除",
|
||||||
|
Content = "确定要删除吗?此操作不可撤销。",
|
||||||
|
PrimaryButtonText = "删除",
|
||||||
|
CloseButtonText = "取消",
|
||||||
|
DefaultButton = ContentDialogButton.Primary
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = await dialog.ShowAsync(this);
|
||||||
|
if (result == ContentDialogResult.Primary)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Material.Avalonia 使用规范(严格限制!)
|
||||||
|
|
||||||
|
### 8.1 ⚠️ 只能在这里用
|
||||||
|
|
||||||
|
**✅ 允许:**
|
||||||
|
- `ComponentEditorWindow.axaml`
|
||||||
|
- ComponentEditorWindow 的子编辑页面
|
||||||
|
|
||||||
|
**❌ 禁止:**
|
||||||
|
- MainWindow
|
||||||
|
- SettingsWindow
|
||||||
|
- NotificationWindow
|
||||||
|
- 任何桌面组件(Widget)
|
||||||
|
- 任何其他地方
|
||||||
|
|
||||||
|
### 8.2 如何在 ComponentEditorWindow 中启用
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<Window ...>
|
||||||
|
<Window.Resources>
|
||||||
|
<!-- MD3 色板(仅此窗口有效)-->
|
||||||
|
<SolidColorBrush x:Key="EditorPrimaryBrush" Color="#FF6750A4" />
|
||||||
|
<SolidColorBrush x:Key="EditorSurfaceContainerBrush" Color="#FFF3EDF7" />
|
||||||
|
<SolidColorBrush x:Key="EditorOnPrimaryBrush" Color="#FFFFFFFF" />
|
||||||
|
</Window.Resources>
|
||||||
|
|
||||||
|
<Window.Styles>
|
||||||
|
<!-- 加载 Material 主题(只影响此窗口)-->
|
||||||
|
<themes:CustomMaterialTheme BaseTheme="Light"
|
||||||
|
PrimaryColor="#6750A4"
|
||||||
|
SecondaryColor="#625B71" />
|
||||||
|
|
||||||
|
<!-- 项目自定义覆盖 -->
|
||||||
|
<StyleInclude Source="avares://LanMountainDesktop/Styles/ComponentEditorThemeResources.axaml" />
|
||||||
|
</Window.Styles>
|
||||||
|
</Window>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8.3 MD3 组件示例
|
||||||
|
|
||||||
|
#### FAB 按钮(浮动操作按钮)
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<Button x:Name="SaveFAB"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
VerticalAlignment="Bottom"
|
||||||
|
Margin="28"
|
||||||
|
Width="64" Height="64"
|
||||||
|
Background="{DynamicResource EditorPrimaryBrush}"
|
||||||
|
Foreground="{DynamicResource EditorOnPrimaryBrush}"
|
||||||
|
CornerRadius="{DynamicResource DesignCornerRadiusComponent}"
|
||||||
|
Click="OnSaveClick">
|
||||||
|
<mi:MaterialIcon Kind="Check" Width="32" Height="32" />
|
||||||
|
</Button>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Top App Bar(顶栏)
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<Border Background="{DynamicResource EditorTopAppBarBackgroundBrush}"
|
||||||
|
Padding="24,16">
|
||||||
|
<Grid ColumnDefinitions="Auto,*,Auto">
|
||||||
|
<mi:MaterialIcon Kind="Widgets"
|
||||||
|
Width="28" Height="28"
|
||||||
|
Foreground="{DynamicResource EditorPrimaryBrush}" />
|
||||||
|
<TextBlock Grid.Column="1"
|
||||||
|
FontSize="20" FontWeight="SemiBold"
|
||||||
|
Text="组件编辑器"
|
||||||
|
Margin="16,0,0,0" />
|
||||||
|
<Button Grid.Column="2" Click="OnCloseClick">
|
||||||
|
<mi:MaterialIcon Kind="Close" Width="24" Height="24" />
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. 实战代码模板
|
||||||
|
|
||||||
|
### 模板 1:新建设置页面
|
||||||
|
|
||||||
|
文件位置:`Views/Settings/YourPageName.axaml`
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:fi="using:FluentIcons.Avalonia.Fluent"
|
||||||
|
xmlns:local="using:LanMountainDesktop.Controls"
|
||||||
|
x:Class="LanMountainDesktop.Views.Settings.YourPageName">
|
||||||
|
|
||||||
|
<ScrollViewer Padding="24,0,24,24">
|
||||||
|
<StackPanel Spacing="24">
|
||||||
|
|
||||||
|
<!-- 分区卡片 -->
|
||||||
|
<Border Classes="settings-section-card">
|
||||||
|
<StackPanel Spacing="16">
|
||||||
|
|
||||||
|
<!-- 分区标题 -->
|
||||||
|
<Grid ColumnDefinitions="Auto,*" ColumnSpacing="12">
|
||||||
|
<fi:FluentIcon Icon="YourSectionIcon"
|
||||||
|
IconVariant="Filled"
|
||||||
|
Classes="icon-l" />
|
||||||
|
<TextBlock Grid.Column="1"
|
||||||
|
Classes="settings-section-title"
|
||||||
|
Text="分区标题" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<!-- 选项卡 1 -->
|
||||||
|
<local:SettingsOptionCard Icon="OptionIcon1"
|
||||||
|
Title="选项标题"
|
||||||
|
Title="选项描述">
|
||||||
|
<local:SettingsOptionCard.ActionContent>
|
||||||
|
<ToggleSwitch IsChecked="{Binding YourProperty, Mode=TwoWay}" />
|
||||||
|
</local:SettingsOptionCard.ActionContent>
|
||||||
|
</local:SettingsOptionCard>
|
||||||
|
|
||||||
|
<!-- 选项卡 2 -->
|
||||||
|
<local:SettingsOptionCard Icon="OptionIcon2"
|
||||||
|
Title="另一个选项"
|
||||||
|
Title="描述信息" />
|
||||||
|
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
</StackPanel>
|
||||||
|
</ScrollViewer>
|
||||||
|
</UserControl>
|
||||||
|
```
|
||||||
|
|
||||||
|
**嵌套检查:** ScrollViewer(1) > StackPanel(2) > Border(3) > StackPanel(4) > Items(5) ✅ (因为有 ScrollViewer 容器,5 层可接受)
|
||||||
|
|
||||||
|
### 模板 2:新建桌面小组件
|
||||||
|
|
||||||
|
文件位置:`Views/Components/YourWidget.axaml`
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
x:Class="LanMountainDesktop.Views.Components.YourWidget">
|
||||||
|
|
||||||
|
<Border Classes="desktop-widget-root"
|
||||||
|
CornerRadius="{DynamicResource DesignCornerRadiusComponent}"
|
||||||
|
Padding="16,12">
|
||||||
|
<Grid RowDefinitions="Auto,*" RowSpacing="8">
|
||||||
|
|
||||||
|
<!-- 标题区 -->
|
||||||
|
<TextBlock x:Name="TitleTextBlock"
|
||||||
|
FontSize="14"
|
||||||
|
FontWeight="SemiBold" />
|
||||||
|
|
||||||
|
<!-- 内容区 -->
|
||||||
|
<ContentControl Grid.Row="1" />
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
</UserControl>
|
||||||
|
```
|
||||||
|
|
||||||
|
**嵌套检查:** Border(1) > Grid(2) > Elements(3) ✅ 完美!
|
||||||
|
|
||||||
|
### 模板 3:新建独立窗口
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<Window xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:ui="using:FluentAvalonia.UI.Controls"
|
||||||
|
xmlns:fi="using:FluentIcons.Avalonia.Fluent"
|
||||||
|
x:Class="LanMountainDesktop.Views.YourWindow"
|
||||||
|
Width="800"
|
||||||
|
Height="600"
|
||||||
|
MinWidth="400"
|
||||||
|
MinHeight="300"
|
||||||
|
CanResize="True"
|
||||||
|
SystemDecorations="BorderOnly"
|
||||||
|
Background="Transparent"
|
||||||
|
Title="窗口标题">
|
||||||
|
|
||||||
|
<Grid RowDefinitions="Auto,*">
|
||||||
|
|
||||||
|
<!-- 自定义标题栏 -->
|
||||||
|
<Border Height="48"
|
||||||
|
Padding="12,0"
|
||||||
|
PointerPressed="OnTitleBarPressed">
|
||||||
|
<Grid ColumnDefinitions="Auto,*,Auto">
|
||||||
|
<fi:FluentIcon Icon="WindowIcon"
|
||||||
|
IconVariant="Filled"
|
||||||
|
Classes="icon-m" />
|
||||||
|
<TextBlock Grid.Column="1"
|
||||||
|
Text="{Binding Title}"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
<Button Grid.Column="2"
|
||||||
|
Click="OnCloseClick">
|
||||||
|
<fi:FluentIcon Icon="Dismiss" IconVariant="Regular" />
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- 主内容区 -->
|
||||||
|
<ContentControl Grid.Row="1"
|
||||||
|
Content="{Binding Content}" />
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
|
```
|
||||||
|
|
||||||
|
**嵌套检查:** Grid(1) > Border(2) > Grid(3) > Elements(4) ✅
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. AI 编码检查清单
|
||||||
|
|
||||||
|
### 写代码前问自己
|
||||||
|
|
||||||
|
- [ ] 这个文件是设置页/主界面?→ 用 Fluent + FluentIcon
|
||||||
|
- [ ] 这个文件是 ComponentEditorWindow?→ 用 Material + MaterialIcon
|
||||||
|
- [ ] 我用了正确的命名空间吗?(见第 2 节速查表)
|
||||||
|
- [ ] 图标用了 Classes="icon-s/m/l" 而非硬编码 FontSize 吗?
|
||||||
|
|
||||||
|
### 写完代码后检查
|
||||||
|
|
||||||
|
- [ ] 数一下最大嵌套深度(见第 4.2 节)
|
||||||
|
- [ ] 有没有硬编码颜色值?(应该都用 DynamicResource)
|
||||||
|
- [ ] 有没有硬编码 CornerRadius?(应该用 DesignCornerRadiusXxx)
|
||||||
|
- [ ] 有没有在同一区域混用 FluentIcon 和 MaterialIcon?
|
||||||
|
- [ ] 是不是不小心写了 `<local:SomeWindow>` 在另一个 Window 里?
|
||||||
|
- [ ] 是不是连续写了 Border > Grid > Border > Grid 可以合并?
|
||||||
|
|
||||||
|
### 如果审查别人代码
|
||||||
|
|
||||||
|
- [ ] 发现窗口套窗口了吗?
|
||||||
|
- [ ] 发现超过 4 层的无意义嵌套了吗?(没有注释说明原因的话)
|
||||||
|
- [ ] 发现 Fluent 和 Material 控件混在同一区域了吗?
|
||||||
|
- [ ] 发现应该用 DynamicResource 的地方硬编码了吗?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 附录:常见错误快速修复
|
||||||
|
|
||||||
|
| 错误现象 | 问题原因 | 修复方法 |
|
||||||
|
|---------|---------|---------|
|
||||||
|
| 图标不显示或大小不对 | 用了错误的命名空间或硬编码尺寸 | 改用 `fi:FluentIcon` + `Classes="icon-m"` |
|
||||||
|
| 圆角在设置里改了但没生效 | 硬编码了 CornerRadius | 改用 `{DynamicResource DesignCornerRadiusComponent}` |
|
||||||
|
| 深色模式下颜色刺眼 | 硬编码了颜色值 | 改用 `{DynamicResource AdaptiveTextPrimaryBrush}` 等 |
|
||||||
|
| 设置页风格和其他窗口不一致 | 混用了 Material 控件 | 统一用 FluentAvaloniaUI 控件 |
|
||||||
|
| 性能差/渲染慢 | 嵌套太深(>6 层)| 扁平化结构,合并多余容器 |
|
||||||
|
| 弹窗显示位置/大小异常 | 把 Window 当成 UserControl 嵌套了 | 改为代码中 `.Show()` 显示 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**相关文档:**
|
||||||
|
- [VISUAL_SPEC.md](./VISUAL_SPEC.md) - 视觉规范总纲
|
||||||
|
- [CORNER_RADIUS_SPEC.md](./CORNER_RADIUS_SPEC.md) - 圆角详细规范
|
||||||
|
- AGENTS.md - AI 强制规则
|
||||||
Reference in New Issue
Block a user