Files

241 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Window 窗口
## 概述
Window 是 AtomUI 主题化的窗口控件,提供了自定义标题栏、多层框架(Frame Layer)背景、响应式媒体查询(Media Query)、操作系统感知、窗口拖拽与缩放等能力。它是桌面应用程序的顶层容器,所有 AtomUI 控件都运行在 `Window` 或其子类 `ReactiveWindow<TViewModel>` 之内。
这是 AtomUI 的扩展控件,在 Ant Design 中没有直接对应。其设计灵感部分来源于 [SukiUI](https://github.com/kikipoulet/SukiUI) 项目。
---
## 设计原理
### 窗口控件的设计目标
桌面应用程序与 Web 应用最大的区别之一在于窗口管理。AtomUI 的 Window 控件旨在提供:
1. **统一的主题化外观**:窗口背景、前景色通过 Design Token 系统控制,自动适配亮色/暗色主题切换。
2. **自定义标题栏**:替代操作系统原生标题栏,提供可配置的 Logo、标题、标题按钮(关闭/最大化/最小化/全屏/置顶)。
3. **多层框架背景**:支持 Window 整体框架层、标题栏框架层、内容区框架层的独立背景设置,可用于实现渐变、图片背景等高级视觉效果。
4. **响应式布局**:内置媒体查询系统,基于窗口内容区域宽度自动计算断点,支持响应式 UI 布局。
5. **跨平台适配**:自动识别 Windows/macOS/Linux 平台,针对各平台进行标题栏、边框、装饰等差异化处理。
### Avalonia Window 基础能力
AtomUI 的 `Window` 继承自 Avalonia 框架的 `Avalonia.Controls.Window`。理解 Avalonia Window 的基础能力有助于理解 AtomUI 在其之上做了哪些扩展。
**Avalonia Window 的核心职责:**
Avalonia 的 `Window` 是一个顶层容器控件(`TopLevel`),它负责管理操作系统级别的窗口生命周期、大小、位置、状态(最小化/最大化/全屏/正常)以及系统装饰(标题栏、边框)。其继承链为:
```
Control → TemplatedControl → ContentControl → TopLevel → WindowBase → Window
```
**Avalonia Window 提供的基础属性:**
| 属性 | 说明 |
|---|---|
| `Title` | 窗口标题文本 |
| `Icon` | 窗口图标 |
| `WindowState` | 窗口状态:`Normal`、`Minimized`、`Maximized`、`FullScreen` |
| `WindowStartupLocation` | 启动位置:`Manual`、`CenterScreen`、`CenterOwner` |
| `CanResize` | 是否允许调整窗口大小 |
| `CanMinimize` / `CanMaximize` | 是否允许最小化/最大化 |
| `SystemDecorations` | 系统装饰风格:`None`、`BorderOnly`、`Full` |
| `ExtendClientAreaToDecorationsHint` | 是否将客户区扩展到标题栏装饰区域 |
| `TransparencyLevelHint` | 透明度级别提示 |
| `Content` | 窗口内容 |
| `Topmost` | 是否置顶显示 |
| `ShowInTaskbar` | 是否在任务栏显示 |
### AtomUI 的扩展设计
AtomUI `Window` 在 Avalonia Window 的基础上做了以下扩展:
| 扩展能力 | 实现方式 | 设计动机 |
|---|---|---|
| **自定义标题栏** | `WindowTitleBar` 控件 + `IsTitleBarVisible` 属性 | 替代原生标题栏,提供统一的跨平台外观和可配置性 |
| **多层框架背景** | `WindowFrameLayer` / `ContentFrameLayer` / `TitleBarFrameLayer` 属性 | 支持为窗口不同区域设置独立的背景内容,实现渐变/图片/动画背景 |
| **响应式媒体查询** | `WindowMediaQueryIndicator` + `MediaBreakPoint` + Container Query | 基于内容区域宽度自动计算断点,驱动响应式布局 |
| **操作系统感知** | `IOperationSystemAware` 接口 + `OsType` 属性 | 自动识别平台,针对不同 OS 应用差异化样式 |
| **窗口缩放控制** | `WindowResizer` 控件(Linux 专用) | 在 Linux 无系统装饰环境下提供自定义缩放手柄 |
| **屏幕比例限制** | `MaxWidthScreenRatio` / `MaxHeightScreenRatio` | 按屏幕工作区比例约束窗口最大尺寸 |
| **标题按钮配置** | `IsFullScreenCaptionButtonEnabled` / `IsPinCaptionButtonEnabled` / `IsCloseCaptionButtonEnabled` | 灵活控制标题栏功能按钮的显示/隐藏 |
| **窗口拖拽控制** | `IsMoveEnabled` 属性 | 可禁用标题栏拖拽移动功能 |
| **macOS 原生集成** | `MacOSCaptionGroupOffset` / `MacOSCaptionGroupSpacing` | 精确控制 macOS 红绿灯按钮的位置和间距 |
| **ReactiveUI 集成** | `ReactiveWindow<TViewModel>` 泛型子类 | 原生支持 MVVM 模式,ViewModel 自动激活 |
| **Design Token** | `WindowToken` + Token 资源系统 | 窗口背景/前景/圆角等视觉值从全局 Token 派生 |
| **全局对话框支持** | 模板内嵌 `DialogLayerManager` + `GlobalDialogManager` | 窗口级别的对话框/模态层管理 |
---
## 功能详解
### 自定义标题栏(WindowTitleBar)
Window 默认使用 `WindowTitleBar` 控件替代操作系统原生标题栏。标题栏通过 `ExtendClientAreaToDecorationsHint = True` 将客户区扩展到装饰区域,实现完全自定义的标题栏外观。
标题栏提供以下功能:
- **Logo**:通过 `Window.Logo` 属性设置,通常为应用程序图标
- **标题文本**:通过 `Window.Title` 属性设置,字号和字重由 `TitleFontSize` / `TitleFontWeight` 控制
- **标题按钮组**:关闭、最小化、最大化/还原、全屏、置顶按钮
- **双击最大化/还原**:双击标题栏在 Normal 和 Maximized 之间切换
- **拖拽移动**:鼠标在标题栏按住拖拽以移动窗口,超过 `DragThreshold` 阈值后触发
标题栏可通过 `IsTitleBarVisible = false` 完全隐藏。
### 多层框架背景(Frame Layers)
Window 模板支持三个独立的背景层,每层可设置内容、模板和不透明度:
| 框架层 | 范围 | 属性 |
|---|---|---|
| **WindowFrameLayer** | 覆盖整个窗口区域(标题栏 + 内容区) | `WindowFrameLayer` / `WindowFrameLayerTemplate` / `WindowFrameLayerOpacity` |
| **TitleBarFrameLayer** | 仅覆盖标题栏区域 | `TitleBarFrameLayer` / `TitleBarFrameLayerTemplate` / `TitleBarFrameLayerOpacity` / `TitleBarFrameBackground` |
| **ContentFrameLayer** | 仅覆盖内容区域 | `ContentFrameLayer` / `ContentFrameLayerTemplate` / `ContentFrameLayerOpacity` / `ContentFrameBackground` |
通过这些框架层,可以实现渐变背景、图片背景、动画背景等丰富的视觉效果,而不影响窗口内容的正常渲染。
### 响应式媒体查询(Media Query)
Window 内置了响应式媒体查询系统,基于 Avalonia Container Query 机制实现。内容区域的 `Border#ContentFrame` 被标记为 Container Query 容器,`WindowMediaQueryIndicator` 根据容器宽度自动更新断点值:
| 断点名称 | 容器宽度范围 | 枚举值 |
|---|---|---|
| `ExtraSmall` | ≤ 575px | `MediaBreakPoint.ExtraSmall` |
| `Small` | ≥ 576px | `MediaBreakPoint.Small` |
| `Medium` | ≥ 768px | `MediaBreakPoint.Medium` |
| `Large` | ≥ 992px(默认) | `MediaBreakPoint.Large` |
| `ExtraLarge` | ≥ 1200px | `MediaBreakPoint.ExtraLarge` |
| `ExtraExtraLarge` | ≥ 1600px | `MediaBreakPoint.ExtraExtraLarge` |
断点变化时会更新 `Window.MediaBreakPoint` 属性并触发 `MediaBreakPointChanged` 事件,子控件可通过实现 `IMediaBreakAwareControl` 接口响应布局变化。
### 操作系统感知
Window 实现了 `IOperationSystemAware` 接口,在构造时自动检测当前操作系统并设置 `OsType` 属性。主题系统通过 `[OsType=Windows]`、`[OsType=Linux]`、`[OsType=macOS]` 属性选择器应用平台差异化样式:
| 平台 | 差异化行为 |
|---|---|
| **Windows** | `ExtendClientAreaTitleBarHeightHint = 1`,最大化时自动添加 7px Margin 避免边缘溢出 |
| **Linux** | `SystemDecorations = None`,`ExtendClientAreaChromeHints = NoChrome`,启用自定义 `WindowResizer`,窗口带 1px 边框和 12px 圆角 |
| **macOS** | 使用原生红绿灯按钮(通过 `MacOSCaptionGroupOffset`/`MacOSCaptionGroupSpacing` 定位),支持通过原生 API 控制关闭按钮可用性 |
### 窗口缩放器(WindowResizer)
`WindowResizer` 是一个仅在 Linux 平台可见的内部控件,提供 8 个方向的缩放手柄(N/S/E/W/NW/NE/SW/SE)。由于 Linux 下 `SystemDecorations = None`,没有系统提供的缩放边框,因此由 AtomUI 自行绘制透明的缩放区域。
### ReactiveWindow\<TViewModel\>
`ReactiveWindow<TViewModel>` 是 Window 的 ReactiveUI 集成版本,实现了 `IViewFor<TViewModel>` 接口。它提供:
- `ViewModel` 属性与 `DataContext` 的自动双向同步
- 如果 ViewModel 实现了 `IActivatableViewModel`,窗口显示时自动激活 ViewModel
- 完整的 `WhenActivated` 生命周期管理
---
## 继承关系
```
Avalonia.Controls.Primitives.TemplatedControl
└── Avalonia.Controls.ContentControl
└── Avalonia.Controls.TopLevel
└── Avalonia.Controls.WindowBase
└── Avalonia.Controls.Window
└── AtomUI.Desktop.Controls.Window
├── implements IOperationSystemAware
├── implements IMediaBreakAwareControl
├── implements IDisposable
│
└── AtomUI.Desktop.Controls.ReactiveWindow<TViewModel>
└── implements IViewFor<TViewModel>
```
`Window` 通过 `using AvaloniaWindow = Avalonia.Controls.Window;` 别名引用 Avalonia 原生 Window,避免类名冲突。
**各层级职责划分:**
| 层级 | 提供的能力 |
|---|---|
| `TopLevel` / `WindowBase` | 顶层容器管理、平台实现桥接、渲染启动、DPI 缩放 |
| `Avalonia.Controls.Window` | 窗口状态管理(Normal/Minimized/Maximized/FullScreen)、系统装饰、标题栏高度提示、透明度、启动位置、模态窗口、关闭逻辑 |
| `AtomUI.Desktop.Controls.Window` | 自定义标题栏、多层框架背景、响应式媒体查询、操作系统感知、屏幕比例约束、标题按钮配置、Design Token 集成、全局对话框管理 |
| `ReactiveWindow<TViewModel>` | ReactiveUI MVVM 集成、ViewModel 自动激活、DataContext ↔ ViewModel 同步 |
**实现的共享接口:**
| 接口 | 定义位置 | 作用 |
|---|---|---|
| `IOperationSystemAware` | `AtomUI.Controls.Shared` | 自动检测并暴露当前操作系统类型和版本 |
| `IMediaBreakAwareControl` | `AtomUI.Controls.Shared` | 提供 `MediaBreakPoint` 属性和 `MediaBreakPointChanged` 事件,支持响应式布局 |
| `IDisposable` | `System` | 窗口关闭时清理事件订阅等资源 |
---
## 源码位置
| 层级 | 文件路径 | 说明 |
|---|---|---|
| 控件类 | `src/AtomUI.Desktop.Controls/Window/Window.cs` | Window 主控件 |
| 控件类 | `src/AtomUI.Desktop.Controls/Window/ReactiveWindow.cs` | ReactiveUI 集成的泛型窗口 |
| 扩展方法 | `src/AtomUI.Desktop.Controls/Window/WindowExtensions.cs` | 窗口辅助方法(屏幕居中、尺寸约束等) |
| 内部控件 | `src/AtomUI.Desktop.Controls/Window/WindowMediaQueryIndicator.cs` | 媒体查询断点指示器 |
| 内部控件 | `src/AtomUI.Desktop.Controls/Window/WindowResizer.cs` | Linux 窗口缩放器 |
| 反射扩展 | `src/AtomUI.Desktop.Controls/Window/WindowBaseReflectionExtensions.cs` | WindowBase 反射辅助(ShowWithoutActive 等) |
| 转换器 | `src/AtomUI.Desktop.Controls/Window/Converters/WindowShadowsToPaddingConverter.cs` | BoxShadows → Thickness 转换 |
| Token 定义 | `src/AtomUI.Desktop.Controls/Window/WindowToken.cs` | 组件级 Design Token |
| 主题模板 | `src/AtomUI.Desktop.Controls/Window/Themes/WindowTheme.axaml` | ControlTheme AXAML |
| 主题代码 | `src/AtomUI.Desktop.Controls/Window/Themes/WindowTheme.cs` | 主题 code-behind |
| 模板常量 | `src/AtomUI.Desktop.Controls/Window/Themes/WindowThemeConstants.cs` | 模板部件名称常量 |
| 缩放器主题 | `src/AtomUI.Desktop.Controls/Window/Themes/WindowResizerTheme.axaml` | WindowResizer 主题 |
| 主题注册 | `src/AtomUI.Desktop.Controls/Window/Themes/WindowThemes.axaml` | ResourceDictionary 注册 |
| 标题栏 | `src/AtomUI.Desktop.Controls/Chrome/WindowTitleBar.cs` | 标题栏控件 |
| 标题栏 Token | `src/AtomUI.Desktop.Controls/Chrome/ChromeToken.cs` | 标题栏/标题按钮 Design Token |
| Gallery 示例 | `controlgallery/AtomUIGallery/Workspace/Views/WorkspaceWindow.axaml` | Gallery 主窗口使用范例 |
---
## 模板结构
Window 的 ControlTemplate 采用多层 Panel 布局,整体结构如下:
```
Panel
├── Border#WindowFrame ← 窗口主框架(承载背景、边框、圆角)
│ └── Panel
│ ├── ContentPresenter#WindowFrameLayer ← 窗口级框架背景层(可选,覆盖整个窗口)
│ └── Border (ClipToBounds)
│ └── DialogLayerManager ← 对话框/模态层管理器
│ └── Panel
│ ├── GlobalDialogManager ← 全局对话框管理器
│ └── DockPanel#PART_ContentLayout ← 内容布局容器
│ ├── Panel (Dock=Top, 标题栏区域)
│ │ ├── ContentPresenter#TitleBarFrameLayer ← 标题栏框架背景层
│ │ └── ContentPresenter (TitleBar) ← 标题栏控件
│ └── Panel (内容区域)
│ ├── ContentPresenter#ContentFrameLayer ← 内容区框架背景层
│ └── Border#ContentFrame ← 内容框架(Container Query 容器)
│ └── Panel
│ ├── ContentPresenter#PART_ContentPresenter ← 窗口内容展示
│ └── WindowMediaQueryIndicator#PART_MediaQueryIndicator ← 媒体查询指示器
└── WindowResizer#PART_WindowResizer ← 窗口缩放手柄(仅 Linux 可见)
```
**分层设计理由:**
- **框架背景层独立**:`WindowFrameLayer`、`TitleBarFrameLayer`、`ContentFrameLayer` 作为独立的 `ContentPresenter` 层叠在内容之下,可放置图片、渐变等装饰内容而不影响交互。
- **对话框层嵌入**:`DialogLayerManager` 包裹整个内容区域,确保对话框和模态遮罩可以覆盖标题栏和内容。
- **媒体查询容器**:`Border#ContentFrame` 被标记为 Container Query 容器(`Container.Name` + `Container.Sizing`),`WindowMediaQueryIndicator` 通过 Container Query 机制自动响应宽度变化。
- **缩放器叠加**:`WindowResizer` 位于最顶层,覆盖窗口四边和四角,提供透明的缩放交互区域。
### 模板部件常量
| 常量名 | 值 | 说明 |
|---|---|---|
| `WindowThemeConstants.ContentPresenterPart` | `"PART_ContentPresenter"` | 内容展示器 |
| `WindowThemeConstants.WindowResizerPart` | `"PART_WindowResizer"` | 窗口缩放手柄 |
| `WindowThemeConstants.TitleBarPart` | `"PART_TitleBar"` | 标题栏控件 |
| `WindowThemeConstants.ContentLayoutPart` | `"PART_ContentLayout"` | 内容布局面板 |
| `WindowThemeConstants.MediaQueryIndicatorPart` | `"PART_MediaQueryIndicator"` | 媒体查询指示器 |