# AtomUI Desktop Controls Full CN

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

Source: ./controls/button/index-cn.md

# Button

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

Button 是 AtomUI 桌面控件体系中的基础动作触发控件，用于承载用户可执行的明确操作。Button 负责把动作语义稳定映射为公共 API、交互状态、主题 Token、模板结构和反馈效果。

Button 不承担复杂内容布局、导航结构管理或业务状态表达职责。复杂按钮形态应通过 Button 家族控件扩展，而不是扩大 Button 本体的职责边界。

Button 家族包括 `DropdownButton`、`SplitButton`、`IconButton` 和 `HyperLinkButton`。这些控件可以拥有不同模板结构，但必须共享 Button 的动作语义、状态解释、尺寸体系和主题资源。

典型使用场景：

- 提交、保存、删除、确认、取消等明确动作。
- 表达主动作、普通动作、弱强调动作、链接式动作和危险动作。
- 在工具栏、表单、对话框、卡片和列表项中提供可点击操作入口。
- 使用图标、加载态、禁用态、尺寸和形状增强动作可识别性。

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/General/Button` |
| 状态 | Stable |

## 何时使用

典型使用场景：

- 提交、保存、删除、确认、取消等明确动作。
- 表达主动作、普通动作、弱强调动作、链接式动作和危险动作。
- 在工具栏、表单、对话框、卡片和列表项中提供可点击操作入口。
- 使用图标、加载态、禁用态、尺寸和形状增强动作可识别性。

## 公共 API

Button 的公共 API 是控件最重要的稳定契约。公共属性、事件和方法集中在 `Button.cs`，内部主题变量和实现细节不得替代公共 API。

基础与兼容 API：

- `ButtonType`: `Default`、`Dashed`、`Primary`、`Link`、`Text`。
- `ButtonShape`: `Default`、`Circle`、`Round`。
- `IsDanger`、`IsGhost`、`IsLoading`。
- `SizeType`、`Icon`、`IconPlacement`、`IconWidth`、`IconHeight`。
- `IsMotionEnabled`、`IsWaveSpiritEnabled`。
- `CustomBackground`。

正交 API：

```csharp
public ButtonColor? Color { get; set; }
public ButtonVariant? Variant { get; set; }
```

`Color` 与 `Variant` 使用 nullable 类型，用于区分“未参与新模型”和“显式设置默认值”。该区分是保持 `ButtonType`、`IsDanger` 与正交 API 兼容优先级的必要条件。

解析优先级：

```text
显式 Color + Variant
> ButtonType / IsDanger 兼容映射
> Variant=Solid 自动补 Color=Primary
> Color=Default, Variant=Outlined
```

旧 API 映射：

| 旧 API | 正交模型 |
| --- | --- |
| `ButtonType=Default` | `Color=Default, Variant=Outlined` |
| `ButtonType=Primary` | `Color=Primary, Variant=Solid` |
| `ButtonType=Dashed` | `Color=Default, Variant=Dashed` |
| `ButtonType=Text` | `Color=Default, Variant=Text` |
| `ButtonType=Link` | internal link color, `Variant=Link` |

`ButtonColor` 不暴露 `Link`。`ButtonType.Link` 是兼容入口，内部映射到链接视觉。

`CustomBackground` 表示 Button normal 状态的受控自定义背景覆层，主要用于渐变、图片或其他非纯色表面。它不是颜色语义，不参与 `Color + Variant` 的状态归一、文字色、边框色、阴影或 wave 颜色计算。`CustomBackground == null` 表示不启用自定义背景覆层。

`SizeType` 使用可自定义尺寸模型，支持 `Large`、`Middle`、`Small` 和 `Custom`。`Large`、`Middle`、`Small` 是 Button 预设尺寸档，完全由 Token 和主题决定。`Custom` 表示用户希望基于 Button 现有属性进行实例级尺寸定制，而不是引入 Button 专属的 `CustomHeight`、`CustomPadding` 或尺寸对象。

`IconWidth` 与 `IconHeight` 是 Button 用户图标和 loading 图标共享的公共 Avalonia 尺寸入口。两个属性相互独立，支持非正方形图标；对应的公共属性字段为 `IconWidthProperty` 与 `IconHeightProperty`。Theme 根据 `SizeType` 提供默认值，用户设置在 Button 上的本地值具有更高优先级，并同时投影到 `PART_ButtonIcon` 与 `PART_LoadingIcon`。

`IconPlacement` 控制 `Icon` 相对内容的位置，支持 `Start` 和 `End`。默认值必须是 `Start`，以保持既有 `Icon` 使用方式不变。`End` 只改变用户图标与内容的排列方向，不改变 loading、icon-only、尺寸、颜色、变体或交互状态语义。

```csharp
public enum ButtonIconPlacement
{
    Start,
    End
}

public ButtonIconPlacement IconPlacement { get; set; }
```

Template part 与主题入口：

| 节点 | 职责 |
| --- | --- |
| `PART_WaveSpirit` | 承载点击 wave 反馈。 |
| `ShadowsFrame` | 承载按钮阴影。 |
| `Frame` | 承载主体背景、边框、圆角和尺寸基底。 |
| `CustomBackgroundLayer` | 主题内部自定义背景覆层，不作为用户 template part。 |
| `PART_RootLayout` | 排列 loading icon、icon 和 content，并根据 `IconPlacement` 调整用户 icon 位置。 |
| `PART_LoadingIcon` | 展示 loading 状态图标，宽高通过 `TemplateBinding` 跟随 `IconWidth`、`IconHeight`。 |
| `PART_ButtonIcon` | 展示用户设置的 icon，位置由 `IconPlacement` 控制，宽高通过 `TemplateBinding` 跟随 `IconWidth`、`IconHeight`。 |
| `PART_ContentPresenter` | 展示用户内容。 |

LLMS 语义区域：

| Part | AtomUI 节点 | 职责 | 相关 API | 相关 Token | 稳定性 |
| --- | --- | --- | --- | --- | --- |
| `root` | `Button` | 控件根语义区域，承载 public API、命令、点击、状态归一和伪类。 | `ButtonType`、`Color`、`Variant`、`IsDanger`、`IsGhost`、`IsLoading`、`SizeType`、`Shape`、`Icon`、`IconPlacement`、`IconWidth`、`IconHeight` | ButtonToken、SharedToken | stable |
| `wave` | `PART_WaveSpirit` | 点击 wave 反馈区域，跟随有效圆角和 wave 类型。 | `IsWaveSpiritEnabled`、`IsMotionEnabled` | SharedToken motion / wave 资源 | stable |
| `shadow` | `ShadowsFrame` | 阴影绘制层，独立于主体背景和边框。 | effective state | `DefaultShadow`、`PrimaryShadow`、`DangerShadow` | stable |
| `surface` | `Frame` | 主体背景、边框、圆角、尺寸和虚线边框绘制层。 | `ButtonType`、`Color`、`Variant`、`Shape`、`SizeType`、`CornerRadius`、`Padding` | default、primary、danger、text、link、padding、corner radius 相关 Token | stable |
| `customBackground` | `CustomBackgroundLayer` | normal 状态自定义背景覆层，只服务 `CustomBackground` 视觉模型。 | `CustomBackground` | 不新增专属 Token | internal-stable |
| `contentLayout` | `PART_RootLayout` | loading icon、用户 icon 和内容的排列区域。 | `IconPlacement`、`HorizontalContentAlignment`、`VerticalContentAlignment` | `IconMargin`、尺寸 Token | stable |
| `loadingIcon` | `PART_LoadingIcon` | loading 状态图标区域。 | `IsLoading`、`IconWidth`、`IconHeight` | `IconSize`、`OnlyIconSize` 相关 Token | stable |
| `icon` | `PART_ButtonIcon` | 用户 icon 区域，支持内容前后位置和 icon-only 场景。 | `Icon`、`IconPlacement`、`IconWidth`、`IconHeight` | `IconSize`、`IconMargin` | stable |
| `content` | `PART_ContentPresenter` | 用户内容展示区域。 | `Content`、`ContentTemplate` | `ContentFontSize`、`ContentLineHeight`、`FontWeight` | stable |

`CustomBackgroundLayer` 是主题内部实现细节，不作为用户可直接依赖的 template part。LLMS semantic 文档可以记录它的存在和边界，但应明确它只服务 `CustomBackground` 受控视觉模型。

## 事件与命令

Button 的公共 API 是控件最重要的稳定契约。公共属性、事件和方法集中在 `Button.cs`，内部主题变量和实现细节不得替代公共 API。
| `root` | `Button` | 控件根语义区域，承载 public API、命令、点击、状态归一和伪类。 | `ButtonType`、`Color`、`Variant`、`IsDanger`、`IsGhost`、`IsLoading`、`SizeType`、`Shape`、`Icon`、`IconPlacement`、`IconWidth`、`IconHeight` | ButtonToken、SharedToken | stable |

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 按钮类型

来源：`controlgallery/AtomUIGallery/ShowCases/General/Button/Views/ButtonShowCase.axaml:43`

SourceKey：`button-type`

```axaml
<WrapPanel HorizontalAlignment="Left" Orientation="Horizontal">
    <atom:Button ButtonType="Primary" Content="主要按钮" />
    <atom:Button Content="默认按钮" />
    <atom:Button ButtonType="Dashed" Content="虚线" />
    <atom:Button ButtonType="Text" Content="文本按钮" />
    <atom:Button ButtonType="Link" Content="链接按钮" />
</WrapPanel>
```

### 按钮形状

来源：`controlgallery/AtomUIGallery/ShowCases/General/Button/Views/ButtonShowCase.axaml:61`

SourceKey：`button-shape`

```axaml
<StackPanel Orientation="Vertical">
    <WrapPanel HorizontalAlignment="Left" Orientation="Horizontal" Margin="0, 0, 0, 20">
        <atom:Button ButtonType="Primary" Content="主要" />
        <atom:Button Content="默认" />

        <atom:Button ButtonType="Text" Content="文本" />
        <atom:Button ButtonType="Link" Content="链接" />
    </WrapPanel>
    <WrapPanel HorizontalAlignment="Left" Orientation="Horizontal" Margin="0, 0, 0, 20">
        <atom:Button ButtonType="Primary" Shape="Round" Content="主要" />
        <atom:Button Shape="Round" Content="默认" />
        <atom:Button ButtonType="Text" Shape="Round" Content="文本" />
        <atom:Button ButtonType="Link" Shape="Round" Content="链接" />
    </WrapPanel>
    <StackPanel HorizontalAlignment="Left" Spacing="10" Orientation="Horizontal" Margin="0, 0, 0, 20">
        <atom:Button ButtonType="Primary" Shape="Circle" Content="AA" />
        <atom:Button Shape="Circle" Content="AA" />
        <atom:Button ButtonType="Text" Shape="Circle" Content="AA" />
        <atom:Button ButtonType="Link" Shape="Circle" Content="AA" />
    </StackPanel>
</StackPanel>
```

### 通栏按钮

来源：`controlgallery/AtomUIGallery/ShowCases/General/Button/Views/ButtonShowCase.axaml:316`

SourceKey：`button-block`

```axaml
<StackPanel HorizontalAlignment="Stretch" Orientation="Vertical" Margin="10">
    <atom:Button ButtonType="Primary" HorizontalAlignment="Stretch" Content="主要" />
    <atom:Button ButtonType="Default" HorizontalAlignment="Stretch" Content="默认" />
    <atom:Button ButtonType="Dashed" HorizontalAlignment="Stretch" Content="虚线" />
    <atom:Button ButtonType="Text" HorizontalAlignment="Stretch" Content="文本" />
    <atom:Button ButtonType="Link" HorizontalAlignment="Stretch" Content="链接" />
</StackPanel>
```

### 危险按钮

来源：`controlgallery/AtomUIGallery/ShowCases/General/Button/Views/ButtonShowCase.axaml:334`

SourceKey：`button-danger`

```axaml
<WrapPanel HorizontalAlignment="Stretch" Orientation="Horizontal">
    <atom:Button ButtonType="Primary" IsDanger="True" Content="主要" />
    <atom:Button ButtonType="Default" IsDanger="True" Content="默认" />
    <atom:Button ButtonType="Dashed" IsDanger="True" Content="虚线" />
    <atom:Button ButtonType="Text" IsDanger="True" Content="文本" />
    <atom:Button ButtonType="Link" IsDanger="True" Content="链接" />
</WrapPanel>
```

## 状态模型

Button 的交互优先级：

```text
Disabled
> Loading
> Pressed
> PointerOver
> Normal
```

`Disabled` 表示控件不可交互，应屏蔽 hover、pressed、wave 等交互反馈。`Loading` 表示操作正在进行中，应影响 loading icon、透明度、原 icon 展示和 wave 播放条件，但不等价于 API 层面的禁用状态。

Button 继承 Avalonia Button 的基础点击、命令和键盘行为。AtomUI 扩展逻辑不得绕过或破坏基础控件语义。

Button 的 effective state 由 C# 层归一，AXAML 主题只消费已经归一的状态或主题变量。核心 effective state 包括：

- `EffectiveColor`、`EffectiveVariant`。
- `EffectiveIsDanger`、`EffectiveIsGhost`、`EffectiveIsBordered`。
- `EffectiveBorderThickness`、`EffectiveCornerRadius`。
- `WaveSpiritType`。
- icon-only、loading、custom background 可见性相关伪类。

## 主题与 Design Token

Button 模板应保持阴影层、主体绘制层、内容层、wave 层和自定义背景覆层的职责分离。可以移除无明确职责的包装层，但不得合并承担不同视觉职责的节点。

Button 主题采用分层变量模型，避免直接展开 `Color × Variant × State` 的组合样式。

```text
Color Selector
  设置颜色语义变量

Variant Selector
  将颜色语义变量映射到文字、背景、边框、阴影变量

State Selector
  将 Normal / PointerOver / Pressed / Disabled / Loading 状态应用到最终视觉属性

Custom Background Selector
  在受支持状态显示自定义背景覆层，在 hover / pressed / disabled / danger 状态隐藏覆层
```

用于 AXAML `Setter`、selector、动态资源和主题切换的变量应定义为 internal `StyledProperty`。普通 CLR 属性不适合作为主题变量，`DirectProperty` 仅适用于不参与 Style 系统的内部运行时状态。

ButtonToken 负责提供组件级语义值，Theme 负责把状态映射为视觉属性。Token 的分类、用途、边界和扩展策略见 [Button Token 设计](token.md)。

Token 来源：

ButtonToken 是 Button 的组件级设计变量层。它把全局设计体系中的颜色、尺寸、间距、字体和阴影转换为 Button 可消费的语义值。

ButtonToken 服务以下主题和控件：

- `ButtonTheme.axaml`
- `DropdownButtonTheme.axaml`
- `SplitButtonTheme.axaml`
- `HyperLinkButtonTheme.axaml`
- Button 家族控件中的 icon、额外内容、下拉间距和状态视觉

ButtonToken 不承载 `IsPressed`、`IsPointerOver`、`IsLoading`、`EffectiveColor`、`EffectiveVariant` 等实例状态。这些状态由 Button 状态模型和主题变量处理。

## AOT 与裁剪注意事项

Button 主题变量使用 Avalonia 属性和动态资源，不使用反射读取模板状态。Token 资源由 ButtonToken scope 提供，并跟随主题切换。

`IconWidth`、`IconHeight` 使用 Avalonia 属性优先级完成 Theme 默认值与 LocalValue 的覆盖，不增加订阅、运行时 part 遍历或状态变化时的视觉对象创建。两个模板 part 共享同一对属性，因此 loading 切换只改变可见性和默认状态映射，不引入尺寸同步副本。

Custom background 覆层是现有模板内的一层视觉节点，启用时不应增加额外控件实例或重建模板。未设置 `CustomBackground` 时，覆层保持不可见，不应影响默认路径的命中测试、wave 或内容布局。

Button 实现不得引入运行时反射、动态代码生成或非 AOT 友好的资源查找路径。

## 源码索引

主要源码：

- `src/AtomUI.Desktop.Controls/Buttons/Button.cs`：Button public API、Avalonia 属性注册、effective state、伪类同步、CompactSpace / Form / Wave 接口实现。
- `src/AtomUI.Desktop.Controls/Buttons/ButtonToken.cs`：Button 控件 Token 定义与派生。
- `src/AtomUI.Desktop.Controls/Buttons/Themes/ButtonTheme.axaml`：Button 跨平台模板、状态 selector 和主题变量映射。
- `src/AtomUI.Desktop.Controls/Buttons/Themes/DropdownButtonBaseTheme.axaml`、`DropdownButtonTheme.axaml`：DropdownButton 对 Button 图标尺寸和状态语义的跨平台投影。
- `src/AtomUI.Desktop.Controls/Buttons/Themes/IconButtonTheme.axaml`：IconButton 默认主题叶子。
- `src/AtomUI.Desktop.Controls/Buttons/Themes/ButtonTheme.cs`：主题资源注册辅助。
- `src/AtomUI.Controls/Buttons/ButtonPseudoClass.cs`：共享 Button 伪类定义。
- `src/AtomUI.Controls/Buttons/Converters/ButtonIconVisibleConverter.cs`：icon 可见性转换辅助。
- `src/AtomUI.Desktop.Controls/DropdownButton/DropdownButton.cs`、`src/AtomUI.Desktop.Controls/SplitButton/SplitButton.cs`、`src/AtomUI.Desktop.Controls/Buttons/IconButton.cs`、`src/AtomUI.Desktop.Controls/Buttons/HyperLinkButton.cs`：Button 家族控件。

## 相关文档

- 源设计文档：`docs/controls/desktop/general/button/overview.md`
- 实现文档：`docs/controls/desktop/general/button/implementation.md`
- Token 文档：`docs/controls/desktop/general/button/token.md`
- 变更记录：`docs/controls/desktop/general/button/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/float-button/index-cn.md

# FloatButton

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

FloatButton 是 AtomUI 桌面控件体系中的悬浮动作按钮，用于页面或窗口边缘提供全局快捷操作、回到顶部、帮助和工具入口。

FloatButton 不负责普通表单提交按钮、菜单导航或复杂工具栏。这些职责应由业务层、组合控件或更专用的 AtomUI 控件承担。

主要源码入口：

- `src/AtomUI.Desktop.Controls/FloatButton`
- `src/AtomUI.Controls/FloatButton`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/General/FloatButton` |
| 状态 | Stable |

## 何时使用

FloatButton 的设计语言围绕控件职责、可观察状态和主题契约组织，而不是围绕模板节点组织。

| 维度 | 含义 | FloatButton 中的表达 |
| --- | --- | --- |
| 产品语义 | 控件在界面中承担的稳定职责。 | FloatButton 是 AtomUI 桌面控件体系中的悬浮动作按钮，用于页面或窗口边缘提供全局快捷操作、回到顶部、帮助和工具入口。 |
| 内容承载 | 用户数据、展示内容、集合项或操作入口如何进入控件。 | `CloseIcon`、`Description`、`DescriptionTemplate`、`Icon`。 |
| 状态反馈 | public API、内部状态和伪类如何形成用户可感知反馈。 | open/close、motion、visual option。 |
| 主题语义 | ControlTheme、SharedToken、控件 Token 和模板绑定如何表达视觉。 | FloatButton Token + ControlTheme。 |

## 公共 API

FloatButton 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。

核心 public surface 按语义分组维护：

| 契约组 | 代表成员 | 维护含义 |
| --- | --- | --- |
| 内容与数据 | `CloseIcon`、`Description`、`DescriptionTemplate`、`Icon` | 定义控件展示内容、输入数据、模板或业务对象入口。 |
| 选择与集合 | `BadgeCount`、`BadgeOverflowCount`、`IsTriggerMode` | 维护选择、展开、过滤、分页、分组或集合状态。 |
| 命令与动作 | `Command`、`CommandParameter`、`Href` | 通过 Avalonia `Button` 命令语义触发业务动作；host 只做投影转发，不自行执行命令。 |
| 交互与状态 | `IsBadgeEnabled`、`IsDotBadge`、`IsMotionEnabled`、`IsOpen` | 表达用户可观察状态、可用性、清除、加载或反馈语义；`FloatButtonGroup.IsOpen` 与 `FloatButtonGroupHost.IsOpen` 默认双向绑定。 |
| 视觉与布局 | `BadgeColor`、`BadgeOffset`、`BoxShadow`、`FloatOffsetX`、`FloatOffsetY`、`MenuPlacement`、`Orientation`、`Placement`、`SeparatorBrush`、`Shape` 等 12 项 | 影响尺寸、位置、颜色、形状、密度和模板视觉变量。 |
| 动效与异步 | `MenuMotionDuration`、`MotionDuration`、`ToTopDuration` | 约束动效开关、异步加载、播放速度、超时和任务边界。 |
| 其他稳定入口 | `ButtonType`、`Target`、`Tooltip`、`Trigger` | 保留为 public surface，变更前需确认 Gallery 和用户 XAML 依赖。 |

稳定事件包括 `Clicked`、`Closed`、`Opened`。事件触发顺序属于兼容契约，不能因内部状态重排而改变。

`FloatButton` 本体继承 Avalonia `Button` 命令契约，`Command`、`CommandParameter`、`CanExecute` 和禁用状态必须遵循 Avalonia 原生按钮语义。`FloatButtonHost` 与 `BackTopFloatButtonHost` 是 overlay host/adaptor，公开的 `Command` 和 `CommandParameter` 必须投影到运行时创建的真实按钮；host 不直接调用 `ICommand.Execute`，也不复制一套命令状态机。

`FloatButtonGroupHost` 的 `Trigger` 是展开/收起语义，不默认承载业务 `Command`。业务动作应绑定在 group 内部的 `FloatButton` 子项上；这些子项必须能继承 host 的 `DataContext`，同时不得覆盖子项显式设置的本地 `DataContext`。

`IsOpen` 是用户可拥有的受控打开状态，Avalonia Binding 默认使用 `TwoWay`；hover、click 和 host 转接触发的打开/关闭都必须回写同一 public 属性。该状态不是 Form value，不写入 `DataValidationErrors`。

主要公开类型与枚举：

- 类型：`AbstractBackTopFloatButton`、`AbstractBackTopFloatButtonHost`、`AbstractFloatButton`、`AbstractFloatButtonHost`、`BackTopFloatButton`、`BackTopFloatButtonHost`、`FloatButton`、`FloatButtonGroup`、`FloatButtonGroupHost`、`FloatButtonHost`、`FloatButtonItemsControl`、`FloatButtonSeparatorLayer`。
- 枚举：`FloatButtonGroupMenuPlacement`、`FloatButtonGroupTrigger`、`FloatButtonPlacement`、`FloatButtonShape`、`FloatButtonType`。

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_BadgeLayout` | `Canvas` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_ItemsLayout` | `?` | 承载集合项、布局面板或虚拟化内容。 |

控件专属或内部伪类包括 `ButtonPseudoClass.IconOnly`。这些伪类属于主题 selector 可观察契约，不能在未同步主题和 Gallery 的情况下重命名或删除。

## 事件与命令

FloatButton 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。
| 命令与动作 | `Command`、`CommandParameter`、`Href` | 通过 Avalonia `Button` 命令语义触发业务动作；host 只做投影转发，不自行执行命令。 |
稳定事件包括 `Clicked`、`Closed`、`Opened`。事件触发顺序属于兼容契约，不能因内部状态重排而改变。
`FloatButton` 本体继承 Avalonia `Button` 命令契约，`Command`、`CommandParameter`、`CanExecute` 和禁用状态必须遵循 Avalonia 原生按钮语义。`FloatButtonHost` 与 `BackTopFloatButtonHost` 是 overlay host/adaptor，公开的 `Command` 和 `CommandParameter` 必须投影到运行时创建的真实按钮；host 不直接调用 `ICommand.Execute`，也不复制一套命令状态机。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/General/FloatButton/Views/FloatButtonShowCase.axaml:36`

Gallery key：`ExamplesContent` / item `0`

```axaml
<atom:ScrollViewer Height="300">
    <Border Background="White">
        <Panel Height="500">
            <atom:FloatButtonHost />
        </Panel>
    </Border>
</atom:ScrollViewer>
```

### 带提示的 FloatButton

来源：`controlgallery/AtomUIGallery/ShowCases/General/FloatButton/Views/FloatButtonShowCase.axaml:164`

Gallery key：`ExamplesContent` / item `4`

```axaml
<atom:ScrollViewer Height="300">
    <Border Background="White">
        <Panel Height="500">
            <atom:FloatButtonHost FloatOffsetX="80"
                                  Tooltip="自 5.25.0 起" TooltipColor="blue" />
            <atom:FloatButtonHost Tooltip="文档" />
        </Panel>
    </Border>
</atom:ScrollViewer>
```

### 回到顶部

来源：`controlgallery/AtomUIGallery/ShowCases/General/FloatButton/Views/FloatButtonShowCase.axaml:408`

Gallery key：`ExamplesContent` / item `11`

```axaml
<atom:ScrollViewer Height="300">
    <Border Background="White">
        <Panel Height="1000">
            <StackPanel>
                <TextBlock Text="滚动到底部" />
                <TextBlock Text="滚动到底部" />
                <TextBlock Text="滚动到底部" />
                <TextBlock Text="滚动到底部" />
                <TextBlock Text="滚动到底部" />
                <TextBlock Text="滚动到底部" />
                <TextBlock Text="滚动到底部" />
                <TextBlock Text="滚动到底部" />
            </StackPanel>
            <atom:BackTopFloatButtonHost ButtonType="Default" />
        </Panel>
    </Border>
</atom:ScrollViewer>
```

## 状态模型

FloatButton 的状态流按以下路径收敛：

```text
Public API / inherited command / item source / user input
  -> 控件实例状态
  -> effective state / pseudo-class / template property
  -> ControlTheme selector / presenter / renderer
  -> Gallery 可观察行为
```

状态维护规则：

- Disabled 或不可交互状态优先屏蔽 pointer、keyboard、motion 和提交类反馈。
- `Command.CanExecute=false` 必须通过 Avalonia Button 语义反映为不可执行状态；BackTop 场景下不可执行命令也应阻止回到顶部动作。
- open/close、motion、visual option 状态由控件实例或明确的数据 owner 推导，不能在 template part 之间双向竞争。
- `IsOpen` 是默认 `TwoWay` 的受控状态；交互路径应使用 current value 语义更新，不得用样式优先级写入破坏用户绑定。
- Host 创建的 overlay 按钮只能接收 host 公共属性投影；命令执行、`CanExecute`、事件顺序和禁用状态仍由真实 `FloatButton` 作为交互 owner。
- Group host 搬移子按钮到 overlay group 时必须保留原有绑定语义，使未设置本地 `DataContext` 的子项继承 host 数据上下文。
- 模板重套用时必须把 public API 对应状态回放到新的 part、伪类和主题变量。
- 集合、弹层、异步、动效或窗口相关状态必须能处理 reset、close、cancel、detach 和 owner 释放。

## 主题与 Design Token

FloatButton 的视觉模型由控件模板、ControlTheme、SharedToken 和必要的控件 Token 共同构成。

| 主题文件 | 职责 |
| --- | --- |
| `AbstractFloatButtonTheme.axaml` | 定义局部操作入口、按钮或 handle 的状态视觉。 |
| `BackTopFloatButtonHostTheme.axaml` | 定义局部操作入口、按钮或 handle 的状态视觉。 |
| `BackTopFloatButtonTheme.axaml` | 定义局部操作入口、按钮或 handle 的状态视觉。 |
| `FloatButtonGroupHostTheme.axaml` | 定义局部操作入口、按钮或 handle 的状态视觉。 |
| `FloatButtonGroupTheme.axaml` | 定义局部操作入口、按钮或 handle 的状态视觉。 |
| `FloatButtonHostTheme.axaml` | 定义局部操作入口、按钮或 handle 的状态视觉。 |
| `FloatButtonItemsControlTheme.axaml` | 定义集合项、容器项或局部单元的状态视觉。 |
| `FloatButtonTheme.axaml` | 定义局部操作入口、按钮或 handle 的状态视觉。 |

FloatButton 使用 `FloatButtonToken` 作为控件 Token scope。Token 只表达组件视觉语义，不承载 open/close、motion、visual option 运行时状态。

主题维护规则：

- 不删除或重命名已经稳定的 ControlTheme key、template part、伪类和资源 key。
- 不把可由 AXAML 表达的模板状态迁移为 C# 动态创建视觉。
- 不把 hover、pressed、selected、expanded、loading、filter、popup open 等运行时状态写入 Token。
- Browser 或平台特化主题必须保持同一 API 的语义一致。

Token 来源：

FloatButton Token 只表达组件级视觉变量，例如尺寸、间距、颜色、圆角、阴影、图标尺寸和弹层边界。Token 不承载运行时选择、展开、加载、错误、上传任务、过滤条件或业务状态。

当前 Token scope：

- `FloatButtonToken`，scope id 为 `FloatButton`，源码位于 `src/AtomUI.Desktop.Controls/FloatButton/FloatButtonToken.cs`。

## AOT 与裁剪注意事项

资源和 AOT 约束：

- 不通过运行时反射扫描 public API、Token 或 Gallery 示例数据。
- 不把可静态声明的模板结构迁移到 C# 动态创建。
- 异步加载、上传、弹层和窗口生命周期必须能取消或释放。
- 缓存对象必须与控件、窗口、弹层或数据 owner 生命周期一致。
- Source generator 生成文件不手工编辑；需要修改时改输入源或 generator。

性能边界：

- 控件应优先复用 Avalonia 原生虚拟化、模板绑定和资源系统。
- 命令执行、`CanExecute` 和禁用状态优先复用 Avalonia Button 原生实现，不维护重复命令缓存。
- Host 动态投影绑定数量应保持为公共属性映射所需的最小集合，生命周期绑定到 overlay target，避免 detach 后保留 host 或 child。
- 避免为每次状态变化创建不必要的视觉对象、订阅或动画对象。
- 大集合控件必须保证 container recycle 后不会泄漏旧 item 状态。

## 源码索引

主要源码文件：

- `src/AtomUI.Desktop.Controls/FloatButton/BackTopFloatButton.cs`
- `src/AtomUI.Desktop.Controls/FloatButton/BackTopFloatButtonHost.cs`
- `src/AtomUI.Desktop.Controls/FloatButton/FloatButton.cs`
- `src/AtomUI.Desktop.Controls/FloatButton/FloatButtonGroup.cs`
- `src/AtomUI.Desktop.Controls/FloatButton/FloatButtonGroupHost.cs`
- `src/AtomUI.Desktop.Controls/FloatButton/FloatButtonHost.cs`
- `src/AtomUI.Desktop.Controls/FloatButton/FloatButtonItemsControl.cs`
- `src/AtomUI.Desktop.Controls/FloatButton/FloatButtonToken.cs`
- `src/AtomUI.Desktop.Controls/FloatButton/Themes/AbstractFloatButtonTheme.axaml`
- `src/AtomUI.Desktop.Controls/FloatButton/Themes/AbstractFloatButtonTheme.cs`
- `src/AtomUI.Desktop.Controls/FloatButton/Themes/BackTopFloatButtonHostTheme.axaml`
- `src/AtomUI.Desktop.Controls/FloatButton/Themes/BackTopFloatButtonTheme.axaml`
- `src/AtomUI.Desktop.Controls/FloatButton/Themes/FloatButtonGroupHostTheme.axaml`
- `src/AtomUI.Desktop.Controls/FloatButton/Themes/FloatButtonGroupTheme.axaml`
- `src/AtomUI.Desktop.Controls/FloatButton/Themes/FloatButtonHostTheme.axaml`
- `src/AtomUI.Desktop.Controls/FloatButton/Themes/FloatButtonItemsControlTheme.axaml`
- `src/AtomUI.Desktop.Controls/FloatButton/Themes/FloatButtonTheme.axaml`
- `src/AtomUI.Controls/FloatButton/AbstractBackTopFloatButton.cs`
- `src/AtomUI.Controls/FloatButton/AbstractBackTopFloatButtonHost.cs`
- `src/AtomUI.Controls/FloatButton/AbstractFloatButton.cs`
- `src/AtomUI.Controls/FloatButton/AbstractFloatButtonHost.cs`
- `src/AtomUI.Controls/FloatButton/FloatButtonEnums.cs`
- `src/AtomUI.Controls/FloatButton/FloatButtonSeparatorLayer.cs`

职责边界：

- 控件主文件保留 public/protected API、Avalonia 属性注册、事件和主要生命周期入口。
- Theme 文件负责静态视觉结构、template part、selector 和资源绑定。
- Token 文件只提供组件视觉变量，不保存实例状态。
- Gallery 文件只展示用法和示例，不作为运行时逻辑 owner。

## 相关文档

- 源设计文档：`docs/controls/desktop/general/float-button/overview.md`
- 实现文档：`docs/controls/desktop/general/float-button/implementation.md`
- Token 文档：`docs/controls/desktop/general/float-button/token.md`
- 变更记录：`docs/controls/desktop/general/float-button/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/icon/index-cn.md

# Icon

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

Icon 是 AtomUI 桌面控件体系中的图标渲染控件，用于在按钮、菜单、表格和独立图标场景中稳定呈现矢量图标。

Icon 不负责业务图标语义、图标资源注册和图标字体打包。这些职责应由业务层、组合控件或更专用的 AtomUI 控件承担。

主要源码入口：

- `src/AtomUI.Controls/Icon`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/General/Icon` |
| 状态 | Stable |

## 何时使用

Icon 的设计语言围绕控件职责、可观察状态和主题契约组织，而不是围绕模板节点组织。

| 维度 | 含义 | Icon 中的表达 |
| --- | --- | --- |
| 产品语义 | 控件在界面中承担的稳定职责。 | Icon 是 AtomUI 桌面控件体系中的图标渲染控件，用于在按钮、菜单、表格和独立图标场景中稳定呈现矢量图标。 |
| 内容承载 | 用户数据、展示内容、集合项或操作入口如何进入控件。 | `Icon`、`IconBrush`、`IconTemplate`；Presenter 的 `IconBrush` 表达宿主单色前景投影。 |
| 状态反馈 | public API、内部状态和伪类如何形成用户可感知反馈。 | 基础交互和主题状态。 |
| 主题语义 | ControlTheme、SharedToken、控件 Token 和模板绑定如何表达视觉。 | Icon Token + ControlTheme。 |

## 公共 API

Icon 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。

核心 public surface 按语义分组维护：

| 契约组 | 代表成员 | 维护含义 |
| --- | --- | --- |
| 内容与数据 | `Icon`、`IconBrush`、`IconTemplate` | 定义控件展示内容、输入数据、模板或业务对象入口；Presenter 场景下 `IconBrush` 会投影到 AtomUI `Icon` 的主画刷、辅助画刷和 fallback 画刷槽位。 |

当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。

主要公开类型与枚举：

- 类型：`IconFuncTemplate`、`IconPresenter`、`IconTemplate`、`IconTemplatePresenter`。
- 枚举：无。

稳定 template part：

当前控件没有显式 `[TemplatePart]` 契约；主题节点仍通过 ControlTheme key、资源 key 和 Gallery 可观察行为形成稳定边界。

当前未抽取到控件专属伪类；主题主要依赖 Avalonia 标准伪类、模板绑定和内部 StyledProperty。

## 事件与命令

Icon 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。
当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

- `controlgallery/AtomUIGallery/ShowCases/General/Icon/Views/IconShowCase.axaml`

## 状态模型

Icon 的状态流按以下路径收敛：

```text
Public API / inherited command / item source / user input
  -> 控件实例状态
  -> effective state / pseudo-class / template property
  -> ControlTheme selector / presenter / renderer
  -> Gallery 可观察行为
```

状态维护规则：

- Disabled 或不可交互状态优先屏蔽 pointer、keyboard、motion 和提交类反馈。
- 基础交互和主题状态 状态由控件实例或明确的数据 owner 推导，不能在 template part 之间双向竞争。
- `IconPresenter` 和 `IconTemplatePresenter` 的 `IconBrush` 是宿主控件单色前景状态入口；当承载对象是 AtomUI `Icon` 时，应同步到 `StrokeBrush`、`FillBrush`、`SecondaryStrokeBrush`、`SecondaryFillBrush` 和 `FallbackBrush`，保证 Button、Menu、List 等宿主的 hover、pressed、disabled 前景状态能够完整接管多画刷图标。
- 模板重套用时必须把 public API 对应状态回放到新的 part、伪类和主题变量。
- 集合、弹层、异步、动效或窗口相关状态必须能处理 reset、close、cancel、detach 和 owner 释放。

## 主题与 Design Token

Icon 的视觉模型由控件模板、ControlTheme、SharedToken 和必要的控件 Token 共同构成。

| 主题文件 | 职责 |
| --- | --- |
| `IconPresenterTheme.axaml` | 定义布局、内容承载或框架节点视觉。 |
| `IconTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `PathIconTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |

Icon 使用 `IconToken` 作为控件 Token scope。Token 只表达组件视觉语义，不承载 基础交互和主题状态 运行时状态。

主题维护规则：

- 不删除或重命名已经稳定的 ControlTheme key、template part、伪类和资源 key。
- 不把可由 AXAML 表达的模板状态迁移为 C# 动态创建视觉。
- 不把 hover、pressed、selected、expanded、loading、filter、popup open 等运行时状态写入 Token。
- Browser 或平台特化主题必须保持同一 API 的语义一致。

Token 来源：

Icon Token 只表达组件级视觉变量，例如尺寸、间距、颜色、圆角、阴影、图标尺寸和弹层边界。Token 不承载运行时选择、展开、加载、错误、上传任务、过滤条件或业务状态。

当前 Token scope：

- `IconToken`，scope id 为 `Icon`，源码位于 `src/AtomUI.Controls/Icon/IconToken.cs`。

## AOT 与裁剪注意事项

资源和 AOT 约束：

- 不通过运行时反射扫描 public API、Token 或 Gallery 示例数据。
- 不把可静态声明的模板结构迁移到 C# 动态创建。
- 异步加载、上传、弹层和窗口生命周期必须能取消或释放。
- 缓存对象必须与控件、窗口、弹层或数据 owner 生命周期一致。
- Source generator 生成文件不手工编辑；需要修改时改输入源或 generator。

性能边界：

- 控件应优先复用 Avalonia 原生虚拟化、模板绑定和资源系统。
- 避免为每次状态变化创建不必要的视觉对象、订阅或动画对象。
- 未配置 loading animation 的普通 Icon 不建立有效可见性跟踪；隐藏祖先下的 loading Icon 不保留 Compositor 无限动画。
- 大集合控件必须保证 container recycle 后不会泄漏旧 item 状态。

## 源码索引

主要源码文件：

- `src/AtomUI.Controls/Icon/IconFuncTemplate.cs`
- `src/AtomUI.Controls/Icon/IconPresenter.cs`
- `src/AtomUI.Controls/Icon/IconTemplate.cs`
- `src/AtomUI.Controls/Icon/IconTemplatePresenter.cs`
- `src/AtomUI.Controls/Icon/IconToken.cs`
- `src/AtomUI.Controls/Icon/Themes/IconPresenterTheme.axaml`
- `src/AtomUI.Controls/Icon/Themes/IconTheme.axaml`
- `src/AtomUI.Controls/Icon/Themes/PathIconTheme.axaml`

职责边界：

- 控件主文件保留 public/protected API、Avalonia 属性注册、事件和主要生命周期入口。
- Theme 文件负责静态视觉结构、template part、selector 和资源绑定。
- Token 文件只提供组件视觉变量，不保存实例状态。
- Gallery 文件只展示用法和示例，不作为运行时逻辑 owner。

## 相关文档

- 源设计文档：`docs/controls/desktop/general/icon/overview.md`
- 实现文档：`docs/controls/desktop/general/icon/implementation.md`
- Token 文档：`docs/controls/desktop/general/icon/token.md`
- 变更记录：`docs/controls/desktop/general/icon/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/separator/index-cn.md

# Separator

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

Separator 是 AtomUI 桌面控件体系中的分割线控件，用于在列表、菜单、工具栏和内容区之间表达轻量分隔。

Separator 不负责布局容器、业务分组模型或可交互控件。这些职责应由业务层、组合控件或更专用的 AtomUI 控件承担。

主要源码入口：

- `src/AtomUI.Controls/Separator`
- `src/AtomUI.Desktop.Controls/Separator`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/General/Separator` |
| 状态 | Stable |

## 何时使用

Separator 的设计语言围绕控件职责、可观察状态和主题契约组织，而不是围绕模板节点组织。

| 维度 | 含义 | Separator 中的表达 |
| --- | --- | --- |
| 产品语义 | 控件在界面中承担的稳定职责。 | Separator 是 AtomUI 桌面控件体系中的分割线控件，用于在列表、菜单、工具栏和内容区之间表达轻量分隔。 |
| 内容承载 | 用户数据、展示内容、集合项或操作入口如何进入控件。 | `Title`、`TitleColor`、`TitlePosition`。 |
| 状态反馈 | public API、内部状态和伪类如何形成用户可感知反馈。 | visual option。 |
| 主题语义 | ControlTheme、SharedToken、控件 Token 和模板绑定如何表达视觉。 | Separator Token + ControlTheme。 |

## 公共 API

Separator 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。

核心 public surface 按语义分组维护：

| 契约组 | 代表成员 | 维护含义 |
| --- | --- | --- |
| 内容与数据 | `Title`、`TitleColor`、`TitlePosition` | 定义控件展示内容、输入数据、模板或业务对象入口。 |
| 交互与状态 | `IsPlain` | 表达用户可观察状态、可用性、清除、加载或反馈语义。 |
| 视觉与布局 | `LineColor`、`LineWidth`、`Orientation`、`OrientationMargin`、`SizeType`、`Variant` | 影响尺寸、位置、颜色、形状、密度和模板视觉变量。 |

当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。

主要公开类型与枚举：

- 类型：`AbstractSeparator`、`Separator`、`VerticalSeparator`。
- 枚举：`SeparatorTitlePosition`、`SeparatorVariant`。
- 尺寸接口：`ICustomizableSizeTypeAware`。

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_Title` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |

控件专属或内部伪类包括 `HasTitleText=:has-title`、`SeparatorPseudoClass.HasTitleText`。这些伪类属于主题 selector 可观察契约，不能在未同步主题和 Gallery 的情况下重命名或删除。

## 事件与命令

Separator 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。
当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 水平分割线

来源：`controlgallery/AtomUIGallery/ShowCases/General/Separator/Views/SeparatorShowCase.axaml:35`

Gallery key：`ExamplesContent` / item `0`

```axaml
<StackPanel Orientation="Vertical">
    <atom:TextBlock TextWrapping="Wrap" Text="这是一段用于演示分割线效果的示例文本。分割线可以组织内容层次，让页面结构更加清晰。" />
    <atom:Separator/>
    <atom:TextBlock TextWrapping="Wrap" Text="这是一段用于演示分割线效果的示例文本。分割线可以组织内容层次，让页面结构更加清晰。" />
    <atom:Separator/>
    <atom:TextBlock TextWrapping="Wrap" Text="这是一段用于演示分割线效果的示例文本。分割线可以组织内容层次，让页面结构更加清晰。" />
</StackPanel>
```

### 带标题的分割线

来源：`controlgallery/AtomUIGallery/ShowCases/General/Separator/Views/SeparatorShowCase.axaml:52`

Gallery key：`ExamplesContent` / item `1`

```axaml
<StackPanel Orientation="Vertical">
    <atom:TextBlock TextWrapping="Wrap" Text="这是一段用于演示分割线效果的示例文本。分割线可以组织内容层次，让页面结构更加清晰。" />
    <atom:Separator Title="文本" FontStyle="Italic" IsVisible="False"/>
    <atom:TextBlock TextWrapping="Wrap" Text="这是一段用于演示分割线效果的示例文本。分割线可以组织内容层次，让页面结构更加清晰。" />
    <atom:Separator Title="左侧文本" TitlePosition="Left" FontWeight="Bold" />
    <atom:TextBlock TextWrapping="Wrap" Text="这是一段用于演示分割线效果的示例文本。分割线可以组织内容层次，让页面结构更加清晰。" />
    <atom:Separator Title="右侧文本" TitlePosition="Right" FontStyle="Oblique" />
    <atom:TextBlock TextWrapping="Wrap" Text="这是一段用于演示分割线效果的示例文本。分割线可以组织内容层次，让页面结构更加清晰。" />
    <atom:Separator Title="orientationMargin 为 0 的左侧文本" TitlePosition="Left" FontStyle="Oblique"
                    FontWeight="Medium" OrientationMargin="0" />
    <atom:TextBlock TextWrapping="Wrap" Text="这是一段用于演示分割线效果的示例文本。分割线可以组织内容层次，让页面结构更加清晰。" />

    <atom:Separator Title="orientationMargin 为 50px 的右侧文本" TitlePosition="Right" TitleColor="Coral"
                    FontWeight="Medium"
                    OrientationMargin="50" />
    <atom:TextBlock TextWrapping="Wrap" Text="这是一段用于演示分割线效果的示例文本。分割线可以组织内容层次，让页面结构更加清晰。" />
</StackPanel>
```

### 无标题样式文本

来源：`controlgallery/AtomUIGallery/ShowCases/General/Separator/Views/SeparatorShowCase.axaml:79`

Gallery key：`ExamplesContent` / item `2`

```axaml
<StackPanel Orientation="Vertical">
    <atom:TextBlock TextWrapping="Wrap" Text="这是一段用于演示分割线效果的示例文本。分割线可以组织内容层次，让页面结构更加清晰。" />
    <atom:Separator Title="文本" IsPlain="True"/>
    <atom:TextBlock TextWrapping="Wrap" Text="这是一段用于演示分割线效果的示例文本。分割线可以组织内容层次，让页面结构更加清晰。" />
    <atom:Separator Title="左侧文本" TitlePosition="Left" IsPlain="True"/>
    <atom:TextBlock TextWrapping="Wrap" Text="这是一段用于演示分割线效果的示例文本。分割线可以组织内容层次，让页面结构更加清晰。" />
    <atom:Separator Title="右侧文本" TitlePosition="Right" IsPlain="True"/>
    <atom:TextBlock TextWrapping="Wrap" Text="这是一段用于演示分割线效果的示例文本。分割线可以组织内容层次，让页面结构更加清晰。" />
</StackPanel>
```

### 设置分割线间距

来源：`controlgallery/AtomUIGallery/ShowCases/General/Separator/Views/SeparatorShowCase.axaml:98`

Gallery key：`ExamplesContent` / item `3`

```axaml
<StackPanel Orientation="Vertical">
    <atom:TextBlock TextWrapping="Wrap" Text="这是一段用于演示分割线效果的示例文本。分割线可以组织内容层次，让页面结构更加清晰。" />
    <atom:Separator SizeType="Small"/>
    <atom:TextBlock TextWrapping="Wrap" Text="这是一段用于演示分割线效果的示例文本。分割线可以组织内容层次，让页面结构更加清晰。" />
    <atom:Separator SizeType="Middle"/>
    <atom:TextBlock TextWrapping="Wrap" Text="这是一段用于演示分割线效果的示例文本。分割线可以组织内容层次，让页面结构更加清晰。" />
    <atom:Separator SizeType="Large"/>
    <atom:TextBlock TextWrapping="Wrap" Text="这是一段用于演示分割线效果的示例文本。分割线可以组织内容层次，让页面结构更加清晰。" />
</StackPanel>
```

## 状态模型

Separator 的状态流按以下路径收敛：

```text
Public API / inherited command / item source / user input
  -> 控件实例状态
  -> effective state / pseudo-class / template property
  -> ControlTheme selector / presenter / renderer
  -> Gallery 可观察行为
```

状态维护规则：

- Disabled 或不可交互状态优先屏蔽 pointer、keyboard、motion 和提交类反馈。
- visual option 状态由控件实例或明确的数据 owner 推导，不能在 template part 之间双向竞争。
- 模板重套用时必须把 public API 对应状态回放到新的 part、伪类和主题变量。
- 集合、弹层、异步、动效或窗口相关状态必须能处理 reset、close、cancel、detach 和 owner 释放。

### 4.1 可自定义间距尺寸

`AbstractSeparator` 实现 `ICustomizableSizeTypeAware`，`SizeType` 使用 `CustomizableSizeType`，默认值为
`Middle`。水平 Separator 的预设尺寸控制上下外间距：`Small`、`Middle`、`Large` 分别映射到控件 Token 的
小、中、大 block margin；该规则对带标题和无标题的水平 Separator 一致，垂直 Separator 不应用这组间距。

`SizeType=Custom` 表示调用方接管间距。Theme 保留 Middle block margin 作为未指定 `Margin` 时的基础值，但不为
`Custom` 声明专属 selector；调用方可以通过实例 `Margin` 或 owner-scoped Style 覆盖。组合控件模板中的结构性
Separator 应使用 `Custom`，并由组合控件自身的 Theme 明确设置间距。

## 主题与 Design Token

Separator 的视觉模型由控件模板、ControlTheme、SharedToken 和必要的控件 Token 共同构成。

| 主题文件 | 职责 |
| --- | --- |
| `SeparatorTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |

Separator 使用 `SeparatorToken` 作为控件 Token scope。Token 只表达组件视觉语义，不承载 visual option 运行时状态。

主题维护规则：

- 不删除或重命名已经稳定的 ControlTheme key、template part、伪类和资源 key。
- 不把可由 AXAML 表达的模板状态迁移为 C# 动态创建视觉。
- 不把 hover、pressed、selected、expanded、loading、filter、popup open 等运行时状态写入 Token。
- Browser 或平台特化主题必须保持同一 API 的语义一致。

Token 来源：

Separator Token 只表达组件级视觉变量，例如尺寸、间距、颜色、圆角、阴影、图标尺寸和弹层边界。Token 不承载运行时选择、展开、加载、错误、上传任务、过滤条件或业务状态。

当前 Token scope：

- `SeparatorToken`，scope id 为 `Separator`，源码位于 `src/AtomUI.Desktop.Controls/Separator/SeparatorToken.cs`。

## AOT 与裁剪注意事项

资源和 AOT 约束：

- 不通过运行时反射扫描 public API、Token 或 Gallery 示例数据。
- 不把可静态声明的模板结构迁移到 C# 动态创建。
- 异步加载、上传、弹层和窗口生命周期必须能取消或释放。
- 缓存对象必须与控件、窗口、弹层或数据 owner 生命周期一致。
- Source generator 生成文件不手工编辑；需要修改时改输入源或 generator。

性能边界：

- 控件应优先复用 Avalonia 原生虚拟化、模板绑定和资源系统。
- 避免为每次状态变化创建不必要的视觉对象、订阅或动画对象。
- 大集合控件必须保证 container recycle 后不会泄漏旧 item 状态。

## 源码索引

主要源码文件：

- `src/AtomUI.Controls/Separator/AbstractSeparator.cs`
- `src/AtomUI.Controls/Separator/SeparatorEnums.cs`
- `src/AtomUI.Controls/Separator/SeparatorPseudoClass.cs`
- `src/AtomUI.Desktop.Controls/Separator/Separator.cs`
- `src/AtomUI.Desktop.Controls/Separator/SeparatorToken.cs`
- `src/AtomUI.Desktop.Controls/Separator/Themes/SeparatorTheme.axaml`

职责边界：

- 控件主文件保留 public/protected API、Avalonia 属性注册、事件和主要生命周期入口。
- Theme 文件负责静态视觉结构、template part、selector 和资源绑定。
- Token 文件只提供组件视觉变量，不保存实例状态。
- Gallery 文件只展示用法和示例，不作为运行时逻辑 owner。

## 相关文档

- 源设计文档：`docs/controls/desktop/general/separator/overview.md`
- 实现文档：`docs/controls/desktop/general/separator/implementation.md`
- Token 文档：`docs/controls/desktop/general/separator/token.md`
- 变更记录：`docs/controls/desktop/general/separator/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/split-button/index-cn.md

# SplitButton

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

SplitButton 是 AtomUI 桌面控件体系中的拆分按钮，用于把主命令和次级下拉命令组合成一个紧凑动作入口。

SplitButton 不负责完整菜单系统、导航菜单或多步骤向导。这些职责应由业务层、组合控件或更专用的 AtomUI 控件承担。

主要源码入口：

- `src/AtomUI.Desktop.Controls/Buttons`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/General/SplitButton` |
| 状态 | Stable |

## 何时使用

SplitButton 的设计语言围绕控件职责、可观察状态和主题契约组织，而不是围绕模板节点组织。

| 维度 | 含义 | SplitButton 中的表达 |
| --- | --- | --- |
| 产品语义 | 控件在界面中承担的稳定职责。 | SplitButton 是 AtomUI 桌面控件体系中的拆分按钮，用于把主命令和次级下拉命令组合成一个紧凑动作入口。 |
| 内容承载 | 用户数据、展示内容、集合项或操作入口如何进入控件。 | `Content`、`Icon`。 |
| 状态反馈 | public API、内部状态和伪类如何形成用户可感知反馈。 | open/close、motion、visual option。 |
| 主题语义 | ControlTheme、SharedToken、控件 Token 和模板绑定如何表达视觉。 | SharedToken / 关联控件 Token + ControlTheme。 |

## 公共 API

SplitButton 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。

核心 public surface 按语义分组维护：

| 契约组 | 代表成员 | 维护含义 |
| --- | --- | --- |
| 内容与数据 | `Content`、`Icon` | 定义控件展示内容、输入数据、模板或业务对象入口。 |
| 交互与状态 | `IsArrowVisible`、`IsDanger`、`IsMotionEnabled`、`IsPointAtCenter`、`IsPrimaryButtonType`、`IsWaveSpiritEnabled`、`ShouldUseOverlayPopup` | 表达用户可观察状态、可用性、清除、加载或反馈语义。 |
| 视觉与布局 | `Placement`、`PlacementAnchor`、`PlacementGravity`、`SizeType` | 影响尺寸、位置、颜色、形状、密度和模板视觉变量。 |
| 弹层与窗口 | `Flyout`、`GutterToFlyout` | 控制 popup、flyout、dialog、window 或 overlay 宿主协作。 |
| 动效与异步 | `MouseEnterDelay`、`MouseLeaveDelay` | 约束动效开关、异步加载、播放速度、超时和任务边界。 |
| 其他稳定入口 | `Command`、`CommandParameter`、`HotKey`、`OpenIndicator`、`TriggerType` | 保留为 public surface，变更前需确认 Gallery 和用户 XAML 依赖。 |

稳定事件包括 `Click`。事件触发顺序属于兼容契约，不能因内部状态重排而改变。

主要公开类型与枚举：

- 类型：`SplitButton`、`Button`、`FlyoutStateHelper`。
- 枚举：无。

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_MainLayout` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_PrimaryButton` | `Button` | 承载用户触发入口、导航或关闭动作。 |
| `PART_SecondaryButton` | `Button` | 承载用户触发入口、导航或关闭动作。 |

当前未抽取到控件专属伪类；主题主要依赖 Avalonia 标准伪类、模板绑定和内部 StyledProperty。

## 事件与命令

SplitButton 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。
稳定事件包括 `Click`。事件触发顺序属于兼容契约，不能因内部状态重排而改变。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

- `controlgallery/AtomUIGallery/ShowCases/General/SplitButton/Views/SplitButtonShowCase.axaml`

## 状态模型

SplitButton 的状态流按以下路径收敛：

```text
Public API / inherited command / item source / user input
  -> 控件实例状态
  -> effective state / pseudo-class / template property
  -> ControlTheme selector / presenter / renderer
  -> Gallery 可观察行为
```

状态维护规则：

- Disabled 或不可交互状态优先屏蔽 pointer、keyboard、motion 和提交类反馈。
- open/close、motion、visual option 状态由控件实例或明确的数据 owner 推导，不能在 template part 之间双向竞争。
- 模板重套用时必须把 public API 对应状态回放到新的 part、伪类和主题变量。
- 集合、弹层、异步、动效或窗口相关状态必须能处理 reset、close、cancel、detach 和 owner 释放。

## 主题与 Design Token

SplitButton 的视觉模型由控件模板、ControlTheme、SharedToken 和必要的控件 Token 共同构成。

| 主题文件 | 职责 |
| --- | --- |
| `SplitButtonTheme.axaml` | 定义局部操作入口、按钮或 handle 的状态视觉。 |

SplitButton 当前没有专属 Token 文档；主题通过 SharedToken、关联控件 Token 或继承主题资源表达视觉语义。运行时状态不得写入 Token 模型。

主题维护规则：

- 不删除或重命名已经稳定的 ControlTheme key、template part、伪类和资源 key。
- 不把可由 AXAML 表达的模板状态迁移为 C# 动态创建视觉。
- 不把 hover、pressed、selected、expanded、loading、filter、popup open 等运行时状态写入 Token。
- Browser 或平台特化主题必须保持同一 API 的语义一致。

Token 来源：

- SplitButton 当前没有专属 `token.md`；LLMS 生成按第 5 节视觉与主题模型、SharedToken、控件家族 Token 或主题资源说明 Token 边界。

## AOT 与裁剪注意事项

资源和 AOT 约束：

- 不通过运行时反射扫描 public API、Token 或 Gallery 示例数据。
- 不把可静态声明的模板结构迁移到 C# 动态创建。
- 异步加载、上传、弹层和窗口生命周期必须能取消或释放。
- 缓存对象必须与控件、窗口、弹层或数据 owner 生命周期一致。
- Source generator 生成文件不手工编辑；需要修改时改输入源或 generator。

性能边界：

- 控件应优先复用 Avalonia 原生虚拟化、模板绑定和资源系统。
- 避免为每次状态变化创建不必要的视觉对象、订阅或动画对象。
- 大集合控件必须保证 container recycle 后不会泄漏旧 item 状态。

## 源码索引

主要源码文件：

- `src/AtomUI.Desktop.Controls/SplitButton/SplitButton.cs`
- `src/AtomUI.Desktop.Controls/Buttons/Themes/SplitButtonTheme.axaml`

职责边界：

- 控件主文件保留 public/protected API、Avalonia 属性注册、事件和主要生命周期入口。
- Theme 文件负责静态视觉结构、template part、selector 和资源绑定。
- Token 文件只提供组件视觉变量，不保存实例状态。
- Gallery 文件只展示用法和示例，不作为运行时逻辑 owner。

## 相关文档

- 源设计文档：`docs/controls/desktop/general/split-button/overview.md`
- 实现文档：`docs/controls/desktop/general/split-button/implementation.md`
- 变更记录：`docs/controls/desktop/general/split-button/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/flex-panel/index-cn.md

# FlexPanel

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

FlexPanel 是 AtomUI 桌面控件体系中的弹性布局面板，用于按主轴、换行、对齐和 flex 参数排列子元素。

FlexPanel 不负责响应式断点系统、数据列表虚拟化或 Grid 栅格模型。这些职责应由业务层、组合控件或更专用的 AtomUI 控件承担。

主要源码入口：

- `src/AtomUI.Controls/FlexPanel`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/Layout/FlexPanel` |
| 状态 | Stable |

## 何时使用

FlexPanel 的设计语言围绕控件职责、可观察状态和主题契约组织，而不是围绕模板节点组织。

| 维度 | 含义 | FlexPanel 中的表达 |
| --- | --- | --- |
| 产品语义 | 控件在界面中承担的稳定职责。 | FlexPanel 是 AtomUI 桌面控件体系中的弹性布局面板，用于按主轴、换行、对齐和 flex 参数排列子元素。 |
| 内容承载 | 用户数据、展示内容、集合项或操作入口如何进入控件。 | `AlignContent`、`AlignItems`、`JustifyContent`。 |
| 状态反馈 | public API、内部状态和伪类如何形成用户可感知反馈。 | collection/filter。 |
| 主题语义 | ControlTheme、SharedToken、控件 Token 和模板绑定如何表达视觉。 | SharedToken / 关联控件 Token + ControlTheme。 |

## 公共 API

FlexPanel 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。

核心 public surface 按语义分组维护：

| 契约组 | 代表成员 | 维护含义 |
| --- | --- | --- |
| 内容与数据 | `AlignContent`、`AlignItems`、`JustifyContent` | 定义控件展示内容、输入数据、模板或业务对象入口。 |
| 视觉与布局 | `ColumnSpacing`、`RowSpacing` | 影响尺寸、位置、颜色、形状、密度和模板视觉变量。 |
| 其他稳定入口 | `Direction`、`Wrap` | 保留为 public surface，变更前需确认 Gallery 和用户 XAML 依赖。 |

当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。

主要公开类型与枚举：

- 类型：无。
- 枚举：`AlignContent`、`AlignItems`、`FlexBasisKind`、`FlexDirection`、`FlexWrap`、`JustifyContent`。

稳定 template part：

当前控件没有显式 `[TemplatePart]` 契约；主题节点仍通过 ControlTheme key、资源 key 和 Gallery 可观察行为形成稳定边界。

当前未抽取到控件专属伪类；主题主要依赖 Avalonia 标准伪类、模板绑定和内部 StyledProperty。

## 事件与命令

FlexPanel 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。
当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础布局

来源：`controlgallery/AtomUIGallery/ShowCases/Layout/FlexPanel/Views/FlexPanelShowCase.axaml:33`

Gallery key：`ExamplesContent` / item `0`

```axaml
<StackPanel Margin="20" Spacing="16" AttachedToVisualTree="InitializeBasicExample">
    <StackPanel Orientation="Horizontal" Spacing="20">
        <atom:RadioButton x:Name="DirectionHorizontal" IsChecked="True" Content="horizontal" />
        <atom:RadioButton x:Name="DirectionVertical" Content="vertical" />
    </StackPanel>

    <atom:PixelAlignedBorder BorderBrush="#E5E5E5" BorderThickness="1" CornerRadius="6" Padding="12"
                             HorizontalAlignment="Stretch">
        <atom:FlexPanel x:Name="BasicFlexPanel"
                        Direction="Row"
                        AlignItems="Stretch"
                        HorizontalAlignment="Stretch">
            <Border x:Name="BasicItem1" Background="#4F7CF5" Height="60" MinWidth="80" />
            <Border x:Name="BasicItem2" Background="#1F5BFF" Height="60" MinWidth="80" />
            <Border x:Name="BasicItem3" Background="#4F7CF5" Height="60" MinWidth="80" />
            <Border x:Name="BasicItem4" Background="#1F5BFF" Height="60" MinWidth="80" />
        </atom:FlexPanel>
    </atom:PixelAlignedBorder>
</StackPanel>
```

### 对齐

来源：`controlgallery/AtomUIGallery/ShowCases/Layout/FlexPanel/Views/FlexPanelShowCase.axaml:59`

Gallery key：`ExamplesContent` / item `1`

```axaml
<StackPanel Margin="20" Spacing="12" AttachedToVisualTree="InitializeAlignmentExample">
    <atom:TextBlock FontWeight="Bold" Text="选择 justify：" />
    <atom:Segmented x:Name="JustifySegmented">
        <atom:SegmentedItem IsSelected="True" Content="flex-start" />
        <atom:SegmentedItem Content="center" />
        <atom:SegmentedItem Content="flex-end" />
        <atom:SegmentedItem Content="space-between" />
        <atom:SegmentedItem Content="space-around" />
        <atom:SegmentedItem Content="space-evenly" />
    </atom:Segmented>

    <atom:TextBlock FontWeight="Bold" Text="选择 align：" />
    <atom:Segmented x:Name="AlignSegmented">
        <atom:SegmentedItem Content="flex-start" />
        <atom:SegmentedItem Content="center" />
        <atom:SegmentedItem IsSelected="True" Content="flex-end" />
        <atom:SegmentedItem Content="stretch" />
    </atom:Segmented>

    <atom:PixelAlignedBorder BorderBrush="#E5E5E5" BorderThickness="1" CornerRadius="6"  Height="120">
        <atom:FlexPanel x:Name="AlignFlexPanel"
                        Direction="Row"
                        JustifyContent="FlexStart"
                        AlignItems="FlexEnd">
            <atom:Button ButtonType="Primary" Content="主要" />
            <atom:Button ButtonType="Primary" Content="主要" />
            <atom:Button ButtonType="Primary" Content="主要" />
            <atom:Button ButtonType="Primary" Content="主要" />
        </atom:FlexPanel>
    </atom:PixelAlignedBorder>
</StackPanel>
```

### 间距

来源：`controlgallery/AtomUIGallery/ShowCases/Layout/FlexPanel/Views/FlexPanelShowCase.axaml:97`

Gallery key：`ExamplesContent` / item `2`

```axaml
<StackPanel Margin="20" Spacing="16" AttachedToVisualTree="InitializeGapExample">
    <StackPanel Orientation="Horizontal" Spacing="20">
        <atom:RadioButton x:Name="GapSmall" IsChecked="True" GroupName="GapMode" Content="small" />
        <atom:RadioButton x:Name="GapMiddle" GroupName="GapMode" Content="middle" />
        <atom:RadioButton x:Name="GapLarge" GroupName="GapMode" Content="large" />
        <atom:RadioButton x:Name="GapCustomize" GroupName="GapMode" Content="customize" />
    </StackPanel>

    <StackPanel x:Name="GapCustomPanel" Orientation="Horizontal" Spacing="8" IsVisible="False">
        <atom:TextBlock VerticalAlignment="Center" Text="自定义间距：" />
        <Slider x:Name="GapValueSlider"
                Width="200"
                Minimum="0"
                Maximum="100"
                TickFrequency="4"
                Value="12" />
    </StackPanel>

    <atom:FlexPanel x:Name="GapFlexPanel" Direction="Row" ColumnSpacing="8" RowSpacing="8">
        <atom:Button ButtonType="Primary" Content="主要" />
        <atom:Button ButtonType="Default" Content="默认" />
        <atom:Button ButtonType="Dashed" Content="虚线" />
        <atom:Button ButtonType="Link" Content="链接" />
    </atom:FlexPanel>
</StackPanel>
```

### 自动换行

来源：`controlgallery/AtomUIGallery/ShowCases/Layout/FlexPanel/Views/FlexPanelShowCase.axaml:129`

Gallery key：`ExamplesContent` / item `3`

```axaml
<StackPanel Margin="20" Spacing="16" AttachedToVisualTree="InitializeAutoWrapExample">
    <StackPanel Orientation="Horizontal" Spacing="20">
        <atom:RadioButton x:Name="WrapEnabled" IsChecked="True" GroupName="WrapMode" Content="wrap" />
        <atom:RadioButton x:Name="WrapDisabled" GroupName="WrapMode" Content="nowrap" />
    </StackPanel>
    <atom:PixelAlignedBorder BorderBrush="#E5E5E5" BorderThickness="1" CornerRadius="6" Padding="12">
        <atom:ScrollViewer x:Name="WrapScrollViewer"
                           HorizontalScrollBarVisibility="Disabled"
                           VerticalScrollBarVisibility="Disabled">
            <atom:FlexPanel x:Name="WrapFlexPanel"
                            Direction="Row"
                            Wrap="Wrap"
                            ColumnSpacing="8"
                            RowSpacing="8"
                            JustifyContent="FlexStart">
                <atom:Button ButtonType="Primary" Content="按钮" />
                <atom:Button ButtonType="Primary" Content="按钮" />
                <atom:Button ButtonType="Primary" Content="按钮" />
                <atom:Button ButtonType="Primary" Content="按钮" />
                <atom:Button ButtonType="Primary" Content="按钮" />
                <atom:Button ButtonType="Primary" Content="按钮" />
                <atom:Button ButtonType="Primary" Content="按钮" />
                <atom:Button ButtonType="Primary" Content="按钮" />
                <atom:Button ButtonType="Primary" Content="按钮" />
                <atom:Button ButtonType="Primary" Content="按钮" />
                <atom:Button ButtonType="Primary" Content="按钮" />
                <atom:Button ButtonType="Primary" Content="按钮" />
                <atom:Button ButtonType="Primary" Content="按钮" />
                <atom:Button ButtonType="Primary" Content="按钮" />
                <atom:Button ButtonType="Primary" Content="按钮" />
            </atom:FlexPanel>
        </atom:ScrollViewer>
    </atom:PixelAlignedBorder>
</StackPanel>
```

## 状态模型

FlexPanel 的状态流按以下路径收敛：

```text
Public API / inherited command / item source / user input
  -> 控件实例状态
  -> effective state / pseudo-class / template property
  -> ControlTheme selector / presenter / renderer
  -> Gallery 可观察行为
```

状态维护规则：

- Disabled 或不可交互状态优先屏蔽 pointer、keyboard、motion 和提交类反馈。
- collection/filter 状态由控件实例或明确的数据 owner 推导，不能在 template part 之间双向竞争。
- 模板重套用时必须把 public API 对应状态回放到新的 part、伪类和主题变量。
- 集合、弹层、异步、动效或窗口相关状态必须能处理 reset、close、cancel、detach 和 owner 释放。

## 主题与 Design Token

FlexPanel 的视觉模型由控件模板、ControlTheme、SharedToken 和必要的控件 Token 共同构成。

当前控件未抽取到专属 AXAML 主题文件；视觉契约主要来自继承控件、共享主题和资源 key。

FlexPanel 当前没有专属 Token 文档；主题通过 SharedToken、关联控件 Token 或继承主题资源表达视觉语义。运行时状态不得写入 Token 模型。

主题维护规则：

- 不删除或重命名已经稳定的 ControlTheme key、template part、伪类和资源 key。
- 不把可由 AXAML 表达的模板状态迁移为 C# 动态创建视觉。
- 不把 hover、pressed、selected、expanded、loading、filter、popup open 等运行时状态写入 Token。
- Browser 或平台特化主题必须保持同一 API 的语义一致。

Token 来源：

- FlexPanel 当前没有专属 `token.md`；LLMS 生成按第 5 节视觉与主题模型、SharedToken、控件家族 Token 或主题资源说明 Token 边界。

## AOT 与裁剪注意事项

资源和 AOT 约束：

- 不通过运行时反射扫描 public API、Token 或 Gallery 示例数据。
- 不把可静态声明的模板结构迁移到 C# 动态创建。
- 异步加载、上传、弹层和窗口生命周期必须能取消或释放。
- 缓存对象必须与控件、窗口、弹层或数据 owner 生命周期一致。
- Source generator 生成文件不手工编辑；需要修改时改输入源或 generator。

性能边界：

- 控件应优先复用 Avalonia 原生虚拟化、模板绑定和资源系统。
- 避免为每次状态变化创建不必要的视觉对象、订阅或动画对象。
- 大集合控件必须保证 container recycle 后不会泄漏旧 item 状态。

## 源码索引

主要源码文件：

- `src/AtomUI.Controls/FlexPanel/AlignContent.cs`
- `src/AtomUI.Controls/FlexPanel/AlignItems.cs`
- `src/AtomUI.Controls/FlexPanel/Flex.cs`
- `src/AtomUI.Controls/FlexPanel/FlexBasis.cs`
- `src/AtomUI.Controls/FlexPanel/FlexBasisKind.cs`
- `src/AtomUI.Controls/FlexPanel/FlexDirection.cs`
- `src/AtomUI.Controls/FlexPanel/FlexPanel.cs`
- `src/AtomUI.Controls/FlexPanel/FlexWrap.cs`
- `src/AtomUI.Controls/FlexPanel/JustifyContent.cs`
- `src/AtomUI.Controls/FlexPanel/Uv.cs`

职责边界：

- 控件主文件保留 public/protected API、Avalonia 属性注册、事件和主要生命周期入口。
- Theme 文件负责静态视觉结构、template part、selector 和资源绑定。
- Token 文件只提供组件视觉变量，不保存实例状态。
- Gallery 文件只展示用法和示例，不作为运行时逻辑 owner。

## 相关文档

- 源设计文档：`docs/controls/desktop/layout/flex-panel/overview.md`
- 实现文档：`docs/controls/desktop/layout/flex-panel/implementation.md`
- 变更记录：`docs/controls/desktop/layout/flex-panel/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/grid/index-cn.md

# Grid

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

Grid 是 AtomUI 桌面控件体系中的响应式栅格控件，用 Row、Col 和断点信息组织十二栅格布局。

Grid 不负责替代 Avalonia 原生 Grid 的全部布局能力，也不承担复杂数据表格职责。这些职责应由业务层、组合控件或更专用的 AtomUI 控件承担。

主要源码入口：

- `src/AtomUI.Controls/Grid`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/Layout/Grid` |
| 状态 | Stable |

## 何时使用

Grid 的设计语言围绕控件职责、可观察状态和主题契约组织，而不是围绕模板节点组织。

| 维度 | 含义 | Grid 中的表达 |
| --- | --- | --- |
| 产品语义 | 控件在界面中承担的稳定职责。 | Grid 是 AtomUI 桌面控件体系中的响应式栅格控件，用 Row、Col 和断点信息组织十二栅格布局。 |
| 内容承载 | 用户数据、展示内容、集合项或操作入口如何进入控件。 | `Row` / `Col` 子元素、栅格断点信息和布局属性。 |
| 状态反馈 | public API、内部状态和伪类如何形成用户可感知反馈。 | 基础交互和主题状态。 |
| 主题语义 | ControlTheme、SharedToken、控件 Token 和模板绑定如何表达视觉。 | SharedToken / 关联控件 Token + ControlTheme。 |

## 公共 API

Grid 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。

核心 public surface 按语义分组维护：

| 契约组 | 代表成员 | 维护含义 |
| --- | --- | --- |
| 内容与数据 | `Row`、`Col`、`Children` | 继承布局容器的子元素入口和栅格单元。 |
| 交互与状态 | `IsWrapped` | 表达用户可观察状态、可用性、清除、加载或反馈语义。 |
| 视觉与布局 | `Align`、`AlignInfo`、`Offset` | 影响尺寸、位置、颜色、形状、密度和模板视觉变量。 |
| 其他稳定入口 | `Flex`、`Gutter`、`Justify`、`JustifyInfo`、`Lg`、`Md`、`Order`、`Pull`、`Push`、`Sm` 等 15 项 | 保留为 public surface，变更前需确认 Gallery 和用户 XAML 依赖。 |

当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。

主要公开类型与枚举：

- 类型：`Col`、`ColInfoExtension`、`GridColFlexConverter`、`GridColSizeConverter`、`GridColSpanInfoConverter`、`GridGutterConverter`、`GridRowAlignInfoConverter`、`GridRowJustifyInfoConverter`、`Row`、`RowAlignConverter`、`RowJustifyConverter`。
- 枚举：`RowAlign`、`RowJustify`。

稳定 template part：

当前控件没有显式 `[TemplatePart]` 契约；主题节点仍通过 ControlTheme key、资源 key 和 Gallery 可观察行为形成稳定边界。

当前未抽取到控件专属伪类；主题主要依赖 Avalonia 标准伪类、模板绑定和内部 StyledProperty。

## 事件与命令

Grid 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。
当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础栅格

来源：`controlgallery/AtomUIGallery/ShowCases/Layout/Grid/Views/GridShowCase.axaml:57`

Gallery key：`ExamplesContent` / item `0`

```axaml
<StackPanel Margin="20" Spacing="12">
    <atom:Row Gutter="16,16">
        <atom:Col Span="24">
            <Border Classes="grid-cell">
                <TextBlock Classes="grid-cell-text" Text="col" />
            </Border>
        </atom:Col>
    </atom:Row>
    <atom:Row Gutter="16,16">
        <atom:Col Span="12">
            <Border Classes="grid-cell">
                <TextBlock Classes="grid-cell-text" Text="列-12" />
            </Border>
        </atom:Col>
        <atom:Col Span="12">
            <Border Classes="grid-cell alt">
                <TextBlock Classes="grid-cell-text" Text="列-12" />
            </Border>
        </atom:Col>
    </atom:Row>
    <atom:Row Gutter="16,16">
        <atom:Col Span="8">
            <Border Classes="grid-cell">
                <TextBlock Classes="grid-cell-text" Text="列-8" />
            </Border>
        </atom:Col>
        <atom:Col Span="8">
            <Border Classes="grid-cell alt">
                <TextBlock Classes="grid-cell-text" Text="列-8" />
            </Border>
        </atom:Col>
        <atom:Col Span="8">
            <Border Classes="grid-cell">
                <TextBlock Classes="grid-cell-text" Text="列-8" />
            </Border>
        </atom:Col>
    </atom:Row>
    <atom:Row Gutter="16,16">
        <atom:Col Span="6">
            <Border Classes="grid-cell">
                <TextBlock Classes="grid-cell-text" Text="列-6" />
            </Border>
        </atom:Col>
        <atom:Col Span="6">
            <Border Classes="grid-cell alt">
                <TextBlock Classes="grid-cell-text" Text="列-6" />
            </Border>
        </atom:Col>
        <atom:Col Span="6">
            <Border Classes="grid-cell">
                <TextBlock Classes="grid-cell-text" Text="列-6" />
            </Border>
        </atom:Col>
        <atom:Col Span="6">
            <Border Classes="grid-cell alt">
                <TextBlock Classes="grid-cell-text" Text="列-6" />
            </Border>
        </atom:Col>
    </atom:Row>
</StackPanel>
```

### 偏移

来源：`controlgallery/AtomUIGallery/ShowCases/Layout/Grid/Views/GridShowCase.axaml:248`

Gallery key：`ExamplesContent` / item `2`

```axaml
<StackPanel Margin="20" Spacing="12">
    <atom:Row Gutter="16,16">
        <atom:Col Span="8">
            <Border Classes="grid-cell">
                <TextBlock Classes="grid-cell-text" Text="列-8" />
            </Border>
        </atom:Col>
        <atom:Col Span="8" Offset="8">
            <Border Classes="grid-cell alt">
                <TextBlock Classes="grid-cell-text" Text="列-8 列-offset-8" />
            </Border>
        </atom:Col>
    </atom:Row>
    <atom:Row Gutter="16,16">
        <atom:Col Span="6" Offset="6">
            <Border Classes="grid-cell">
                <TextBlock Classes="grid-cell-text" Text="列-6 列-offset-6" />
            </Border>
        </atom:Col>
    </atom:Row>
    <atom:Row Gutter="16,16">
        <atom:Col Span="12" Offset="6">
            <Border Classes="grid-cell alt">
                <TextBlock Classes="grid-cell-text" Text="列-12 列-offset-6" />
            </Border>
        </atom:Col>
    </atom:Row>
    <atom:Row Gutter="16,16">
        <atom:Col Span="6" Offset="18">
            <Border Classes="grid-cell">
                <TextBlock Classes="grid-cell-text" Text="列-6 列-offset-18" />
            </Border>
        </atom:Col>
    </atom:Row>
</StackPanel>
```

### 推拉排序

来源：`controlgallery/AtomUIGallery/ShowCases/Layout/Grid/Views/GridShowCase.axaml:290`

Gallery key：`ExamplesContent` / item `3`

```axaml
<StackPanel Margin="20" Spacing="12">
    <atom:Row Gutter="16,16">
        <atom:Col Span="18" Push="6">
            <Border Classes="grid-cell alt">
                <TextBlock Classes="grid-cell-text" Text="列-18 列-push-6" />
            </Border>
        </atom:Col>
        <atom:Col Span="6" Pull="18">
            <Border Classes="grid-cell">
                <TextBlock Classes="grid-cell-text" Text="列-6 列-pull-18" />
            </Border>
        </atom:Col>
    </atom:Row>
</StackPanel>
```

### 交叉轴对齐

来源：`controlgallery/AtomUIGallery/ShowCases/Layout/Grid/Views/GridShowCase.axaml:475`

Gallery key：`ExamplesContent` / item `5`

```axaml
<StackPanel Margin="20" Spacing="12">
    <TextBlock Classes="grid-section-title" Text="Align Top" />
    <Border Classes="grid-row-surface">
        <atom:Row Gutter="16,16" Justify="center" Align="top">
            <atom:Col Span="4">
                <Border Classes="grid-cell" Height="72">
                    <TextBlock Classes="grid-cell-text" Text="列-4" />
                </Border>
            </atom:Col>
            <atom:Col Span="4">
                <Border Classes="grid-cell alt" Height="48">
                    <TextBlock Classes="grid-cell-text" Text="列-4" />
                </Border>
            </atom:Col>
            <atom:Col Span="4">
                <Border Classes="grid-cell" Height="64">
                    <TextBlock Classes="grid-cell-text" Text="列-4" />
                </Border>
            </atom:Col>
            <atom:Col Span="4">
                <Border Classes="grid-cell alt" Height="56">
                    <TextBlock Classes="grid-cell-text" Text="列-4" />
                </Border>
            </atom:Col>
        </atom:Row>
    </Border>

    <TextBlock Classes="grid-section-title" Text="Align Middle" />
    <Border Classes="grid-row-surface">
        <atom:Row Gutter="16,16" Justify="center" Align="middle">
            <atom:Col Span="4">
                <Border Classes="grid-cell" Height="72">
                    <TextBlock Classes="grid-cell-text" Text="列-4" />
                </Border>
            </atom:Col>
            <atom:Col Span="4">
                <Border Classes="grid-cell alt" Height="48">
                    <TextBlock Classes="grid-cell-text" Text="列-4" />
                </Border>
            </atom:Col>
            <atom:Col Span="4">
                <Border Classes="grid-cell" Height="64">
                    <TextBlock Classes="grid-cell-text" Text="列-4" />
                </Border>
            </atom:Col>
            <atom:Col Span="4">
                <Border Classes="grid-cell alt" Height="56">
                    <TextBlock Classes="grid-cell-text" Text="列-4" />
                </Border>
            </atom:Col>
        </atom:Row>
    </Border>

    <TextBlock Classes="grid-section-title" Text="Align Bottom" />
    <Border Classes="grid-row-surface">
        <atom:Row Gutter="16,16" Justify="center" Align="bottom">
            <atom:Col Span="4">
                <Border Classes="grid-cell" Height="72">
                    <TextBlock Classes="grid-cell-text" Text="列-4" />
                </Border>
            </atom:Col>
            <atom:Col Span="4">
                <Border Classes="grid-cell alt" Height="48">
                    <TextBlock Classes="grid-cell-text" Text="列-4" />
                </Border>
            </atom:Col>
            <atom:Col Span="4">
                <Border Classes="grid-cell" Height="64">
                    <TextBlock Classes="grid-cell-text" Text="列-4" />
                </Border>
            </atom:Col>
            <atom:Col Span="4">
                <Border Classes="grid-cell alt" Height="56">
                    <TextBlock Classes="grid-cell-text" Text="列-4" />
                </Border>
            </atom:Col>
        </atom:Row>
    </Border>
</StackPanel>
```

## 状态模型

Grid 的状态流按以下路径收敛：

```text
Public API / inherited command / item source / user input
  -> 控件实例状态
  -> effective state / pseudo-class / template property
  -> ControlTheme selector / presenter / renderer
  -> Gallery 可观察行为
```

状态维护规则：

- Disabled 或不可交互状态优先屏蔽 pointer、keyboard、motion 和提交类反馈。
- 基础交互和主题状态 状态由控件实例或明确的数据 owner 推导，不能在 template part 之间双向竞争。
- 模板重套用时必须把 public API 对应状态回放到新的 part、伪类和主题变量。
- 集合、弹层、异步、动效或窗口相关状态必须能处理 reset、close、cancel、detach 和 owner 释放。

## 主题与 Design Token

Grid 的视觉模型由控件模板、ControlTheme、SharedToken 和必要的控件 Token 共同构成。

当前控件未抽取到专属 AXAML 主题文件；视觉契约主要来自继承控件、共享主题和资源 key。

Grid 当前没有专属 Token 文档；主题通过 SharedToken、关联控件 Token 或继承主题资源表达视觉语义。运行时状态不得写入 Token 模型。

主题维护规则：

- 不删除或重命名已经稳定的 ControlTheme key、template part、伪类和资源 key。
- 不把可由 AXAML 表达的模板状态迁移为 C# 动态创建视觉。
- 不把 hover、pressed、selected、expanded、loading、filter、popup open 等运行时状态写入 Token。
- Browser 或平台特化主题必须保持同一 API 的语义一致。

Token 来源：

- Grid 当前没有专属 `token.md`；LLMS 生成按第 5 节视觉与主题模型、SharedToken、控件家族 Token 或主题资源说明 Token 边界。

## AOT 与裁剪注意事项

资源和 AOT 约束：

- 不通过运行时反射扫描 public API、Token 或 Gallery 示例数据。
- 不把可静态声明的模板结构迁移到 C# 动态创建。
- 异步加载、上传、弹层和窗口生命周期必须能取消或释放。
- 缓存对象必须与控件、窗口、弹层或数据 owner 生命周期一致。
- Source generator 生成文件不手工编辑；需要修改时改输入源或 generator。

性能边界：

- 控件应优先复用 Avalonia 原生虚拟化、模板绑定和资源系统。
- 避免为每次状态变化创建不必要的视觉对象、订阅或动画对象。
- 大集合控件必须保证 container recycle 后不会泄漏旧 item 状态。

## 源码索引

主要源码文件：

- `src/AtomUI.Controls/Grid/Col.cs`
- `src/AtomUI.Controls/Grid/ColInfo.cs`
- `src/AtomUI.Controls/Grid/ColInfoExtension.cs`
- `src/AtomUI.Controls/Grid/GridAlignment.cs`
- `src/AtomUI.Controls/Grid/GridColFlex.cs`
- `src/AtomUI.Controls/Grid/GridColSize.cs`
- `src/AtomUI.Controls/Grid/GridColSpanInfo.cs`
- `src/AtomUI.Controls/Grid/GridGutter.cs`
- `src/AtomUI.Controls/Grid/GridGutterInfo.cs`
- `src/AtomUI.Controls/Grid/GridRowResponsiveInfo.cs`
- `src/AtomUI.Controls/Grid/Row.cs`

职责边界：

- 控件主文件保留 public/protected API、Avalonia 属性注册、事件和主要生命周期入口。
- Theme 文件负责静态视觉结构、template part、selector 和资源绑定。
- Token 文件只提供组件视觉变量，不保存实例状态。
- Gallery 文件只展示用法和示例，不作为运行时逻辑 owner。

## 相关文档

- 源设计文档：`docs/controls/desktop/layout/grid/overview.md`
- 实现文档：`docs/controls/desktop/layout/grid/implementation.md`
- 变更记录：`docs/controls/desktop/layout/grid/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/masonry/index-cn.md

# Masonry

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

Masonry 是桌面端瀑布流布局控件，用于将高度不一致的内容块按列组织。它支持稳定列归属和每轮 shortest-column 重排两种策略，适合图片墙、卡片流、示例集合、资源列表等内容高度不可预先统一的场景。

`Masonry` 派生自 Avalonia `ItemsControl`，同时承担数据绑定入口和子项布局元数据 attached property 容器（`atom:Masonry.Column`、`atom:Masonry.Span`）两种职责。

Masonry 是布局控件，不是数据源管理器、卡片控件、图片加载器、虚拟化列表、滚动容器或内容渲染器。它只负责对子元素进行测量、列分配和排列，不拥有子元素内容，也不改变子元素的业务语义。

Masonry 支持两种内容提供方式：

- 直接放置子元素：在 `atom:Masonry` 的内容中放置任意 `Control`，子元素本身即作为容器，不产生 Masonry 主动插入的视觉包装层。
- 数据绑定：通过 `ItemsSource` 绑定数据集合，由 `ItemsControl` 固有机制生成容器。集合监听、容器生成和增删同步由基类承担。

滚动由外层 `ScrollViewer` 承担，图片加载由图片控件或外部加载库承担。

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/Layout/Masonry` |
| 状态 | Stable |

## 何时使用

Masonry 表达的是“密集但有秩序”的内容浏览体验。它利用可变高度卡片形成自然节奏，避免为了对齐网格而牺牲内容本身的信息密度。

设计语言要求：

- 首次自动分配以当前最短列降低初始列高差；后续由 `LayoutStrategy` 在视觉连续性和实时列高平衡之间取舍。
- 逻辑上保留子元素顺序，保证读屏、键盘导航、数据绑定容器生成顺序和源集合顺序一致。
- 不为子项强加卡片外观。子项可以是 Card、Image、ShowCaseItem 或任意 `Control`。
- 不插入额外视觉包装层，使 VisualTree 保持最小。
- 间距只表达布局节奏，不表达组件语义颜色、阴影或卡片样式。

## 公共 API

Masonry 的公共 API 采用 Avalonia 原生属性模型。需要 XAML 设置、Style 设置、绑定、动画或主题参与的状态必须定义为 `StyledProperty` 或 attached property。

核心布局属性：

| API | 类型 | 默认值 | 语义 |
| --- | --- | --- | --- |
| `ColumnCount` | `int` | `0` | 固定列数；`<= 0` 时进入容器自适应列数。 |
| `ColumnInfo` | `ResponsiveInt?` | `null` | 按媒体断点变化的列数。 |
| `MinColumnWidth` | `double` | `320d` | 自适应列数下的最小列宽。 |
| `MaxColumnCount` | `int` | `4` | 自适应列数下的最大列数。 |
| `ColumnGap` | `double` | `16d` | 水平列间距。 |
| `RowGap` | `double` | `16d` | 垂直行间距。 |
| `Gutter` | `ResponsiveGutter?` | `null` | 按媒体断点变化的水平/垂直间距。 |
| `LayoutStrategy` | `MasonryLayoutStrategy` | `StableColumns` | 控制自动子项在后续布局中保持原列，或每次重新按最短列分配。 |

响应式属性遵循 [AtomUI 响应式机制设计](../../../../architecture/systems/control-infrastructure/responsive.md)。Masonry 不复制断点定义、解析顺序或 partial map 继承规则；它只在 effective state 层提供列数和间距 fallback。

列数优先级：

1. `ColumnInfo` 在当前断点命中显式配置时，使用响应式列数。
2. `ColumnInfo` 未命中且 `ColumnCount > 0` 时，使用固定列数。
3. 前两者均未命中时，使用 `MinColumnWidth + MaxColumnCount + AvailableWidth` 的容器自适应列数。

间距优先级：

1. `Gutter` 在当前断点命中显式配置时，使用响应式水平/垂直间距。
2. `Gutter` 未命中时，使用 `ColumnGap` 与 `RowGap`。

子项布局元数据：

| API | 类型 | 语义 |
| --- | --- | --- |
| `Masonry.Column` | `int?` attached property | 指定子项所在列，列索引按 `0` 起始。 |
| `Masonry.Span` | `MasonryItemSpan` attached property | `Auto` 普通瀑布流项；`Full` 整行项。 |

attached property 的消费对象是 `MasonryPanel.Children` 中被测量和排列的 item container。直接放置子元素时写在子元素本身；`ItemsSource` 场景应写在 generated container 上，例如通过 `ItemContainerTheme` 绑定。

布局策略：

| 场景 | `StableColumns`（默认） | `Reflow` |
| --- | --- | --- |
| 首次成功 Arrange | 自动子项按当前最短列分配；Arrange 完成后，该结果成为后续布局保持的列归属。 | 自动子项按当前最短列分配，不保留跨布局列归属。 |
| 有效列数不变的 resize | 已提交的 item container 保持原列；重新测量宽高并按列内顺序更新纵向位置。 | 按本轮测得高度从头执行 shortest-column 分配，部分或全部自动子项可能换列。 |
| 子项内容或 DesiredSize 变化 | 已提交 container 保持原列；只更新尺寸、列高和后续子项纵向位置。 | 从头重新计算自动列分配。 |
| 新增 item container | 已存在 container 保持原列；新 container 进入当前最短列。 | 包含新增项在内从头重新计算。 |
| 删除 item container | 其余现存 container 保持原列；删除项不再参与后续布局。 | 对剩余自动子项从头重新计算。 |
| 有效列数变化 | 既有列归属不再适用，全部自动子项按新列数重新计算。 | 按新列数从头重新计算。 |
| `Masonry.Column` / `Masonry.Span` 变化 | attached property 继续优先于自动策略，并重新建立全部自动列归属。 | attached property 继续优先于自动策略，本轮按新规则重新计算。 |
| 策略切换 | 放弃既有稳定列归属；切换后的下一次布局按目标策略执行。 | 放弃既有稳定列归属；后续不再复用列归属。 |

`StableColumns` 的“首次”指 item container 第一次完成 Arrange，而不是图片、网络内容或其他异步内容全部加载完成。若占位内容与最终内容高度不同，首次 Arrange 时的高度决定初始列归属；后续加载只改变该列中的尺寸和纵向位置。需要始终追求当前列高平衡的场景应显式选择 `Reflow`，图片墙希望初始分配也接近最终高度时应提供稳定的宽高比或等价尺寸约束。

默认策略无需声明：

```xml
<atom:Masonry />
```

经典 shortest-column 重排需要显式选择：

```xml
<atom:Masonry LayoutStrategy="Reflow" />
```

事件模型：

```csharp
public event EventHandler<MasonryLayoutChangedEventArgs>? LayoutChanged;
```

`LayoutChanged` 是有效布局分配通知，不是像素级滚动或动画事件。它只表达 item container 顺序、有效列和整行状态的变化。`StableColumns` 下仅有尺寸或纵向位置变化时不派发；`Reflow` 也只有最终有效列分配实际变化时才派发。

## 事件与命令

事件模型：
public event EventHandler<MasonryLayoutChangedEventArgs>? LayoutChanged;
`LayoutChanged` 是有效布局分配通知，不是像素级滚动或动画事件。它只表达 item container 顺序、有效列和整行状态的变化。`StableColumns` 下仅有尺寸或纵向位置变化时不派发；`Reflow` 也只有最终有效列分配实际变化时才派发。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/Layout/Masonry/Views/MasonryShowCase.axaml:40`

Gallery key：`ExamplesContent` / item `0`

```axaml
<atom:Masonry ItemsSource="{Binding BasicItems}"
              ColumnCount="4"
              ColumnGap="16"
              RowGap="16"
              HorizontalAlignment="Stretch">
    <atom:Masonry.ItemTemplate>
        <DataTemplate x:DataType="vm:MasonryBasicItem">
            <Panel>
                <atom:Card SizeType="Small"
                           Height="{Binding Height}"
                           HorizontalAlignment="Stretch"
                           IsVisible="{Binding !IsSpecial}">
                    <atom:TextBlock Text="{Binding Index}" />
                </atom:Card>
                <atom:Card SizeType="Small"
                           ClipToBounds="True"
                           HorizontalAlignment="Stretch"
                           IsVisible="{Binding IsSpecial}">
                    <atom:Card.Cover>
                        <atom:AsyncImage Name="SpecialCoverImage"
                                         Source="{Binding CoverSource}"
                                         MinHeight="210"
                                         ClipToBounds="True"
                                         Stretch="UniformToFill">
                            <atom:AsyncImage.LoadingContent>
                                <Border Name="SpecialCoverSkeleton"
                                        Padding="16,16">
                                    <atom:Skeleton IsLoading="True"
                                                   IsActive="True"
                                                   IsShowAvatar="False"
                                                   IsShowTitle="True"
                                                   ParagraphRows="3"
                                                   IsRound="True" />
                                </Border>
                            </atom:AsyncImage.LoadingContent>
                        </atom:AsyncImage>
                    </atom:Card.Cover>
                    <atom:CardMetaContent Header="{Binding Title}"
                                          Content="{Binding Description}" />
                </atom:Card>
            </Panel>
        </DataTemplate>
    </atom:Masonry.ItemTemplate>
</atom:Masonry>
```

### 响应式

来源：`controlgallery/AtomUIGallery/ShowCases/Layout/Masonry/Views/MasonryShowCase.axaml:96`

Gallery key：`ExamplesContent` / item `1`

```axaml
<atom:Masonry ItemsSource="{Binding ResponsiveItems}"
              ColumnInfo="xs: 1, sm: 2, md: 3, lg: 4"
              Gutter="xs: 8, sm: 12, md: 16; xs: 8, sm: 12, md: 16"
              HorizontalAlignment="Stretch">
    <atom:Masonry.ItemTemplate>
        <DataTemplate x:DataType="vm:MasonryBasicItem">
            <atom:Card SizeType="Small"
                       Height="{Binding Height}"
                       HorizontalAlignment="Stretch">
                <atom:TextBlock Text="{Binding Index}" />
            </atom:Card>
        </DataTemplate>
    </atom:Masonry.ItemTemplate>
</atom:Masonry>
```

### 图片

来源：`controlgallery/AtomUIGallery/ShowCases/Layout/Masonry/Views/MasonryShowCase.axaml:122`

Gallery key：`ExamplesContent` / item `2`

```axaml
<atom:Masonry ItemsSource="{Binding ImageItems}"
              ColumnCount="4"
              ColumnGap="16"
              RowGap="16"
              HorizontalAlignment="Stretch">
    <atom:Masonry.ItemTemplate>
        <DataTemplate x:DataType="vm:MasonryImageItem">
            <atom:AsyncImage Name="MasonryImage"
                             Source="{Binding ImageSource}"
                             ClipToBounds="True"
                             Stretch="Uniform"
                             HorizontalAlignment="Stretch">
                <atom:AsyncImage.LoadingContent>
                    <Border Name="MasonryImageSkeleton"
                            MinHeight="210"
                            Padding="16,16">
                        <atom:Skeleton IsLoading="True"
                                       IsActive="True"
                                       IsShowAvatar="False"
                                       IsShowTitle="True"
                                       ParagraphRows="3"
                                       IsRound="True" />
                    </Border>
                </atom:AsyncImage.LoadingContent>
            </atom:AsyncImage>
        </DataTemplate>
    </atom:Masonry.ItemTemplate>
</atom:Masonry>
```

## 状态模型

Masonry 本身没有 hover、pressed、disabled、loading 或 checked 等交互状态。它必须完整保留子元素自身的命中测试、焦点、键盘导航、拖拽、上下文菜单和动画行为。

有效状态由响应式属性、兼容属性、可用宽度和子项 attached property 共同决定。状态归一发生在 C# 布局层，AXAML 不承担列数、间距或子项位置计算。

布局状态包括：

- 有效列数。
- 有效列宽。
- 有效水平和垂直间距。
- 子项显式列。
- 子项是否整行。
- 最终有效列分配。
- 自动列分配策略，以及 `StableColumns` 下按 item container 实例维护的已提交列归属。

`Tab`、读屏顺序、logical children 顺序和 item container 顺序必须保持 `ItemsControl` 源集合顺序。两种列分配策略都只改变视觉位置，不重排 logical order，也不改变数据绑定容器生成顺序。

## 主题与 Design Token

Masonry 的默认主题只装配 `ItemsPresenter` 与 internal `MasonryPanel`，并把 Masonry 布局属性传递给布局面板。Theme 不绘制子项外观，不通过 selector 计算列数和位置。

默认视觉树：

```text
Masonry (ItemsControl, default ItemsPanel = MasonryPanel)
└─ ItemsPresenter
   └─ MasonryPanel (internal)
      ├─ <子元素>
      ├─ ContentPresenter → <子元素>
      └─ ...
```

视觉树要求：

- `Masonry` 和 `MasonryPanel` 节点只承担布局与容器装配职责。
- 子项视觉结构完全由子项控件或 `ItemTemplate` 负责。
- 不通过模板节点实现列、行、占位或测量辅助对象。
- 不通过透明 Border 扩展命中区域。
- 不通过不可见控件缓存测量结果。
- 不为子项主动插入额外视觉包装层。

Masonry 当前不定义专属 Token，不需要创建 `token.md`。Masonry 的间距和列宽属于实例布局状态，不应迁移为控件 Token。

Token 来源：

- Masonry 当前没有专属 `token.md`；LLMS 生成按第 5 节视觉与主题模型、SharedToken、控件家族 Token 或主题资源说明 Token 边界。

## AOT 与裁剪注意事项

Masonry 不使用反射读取 item template 内部元素，不创建不可见测量控件，不通过透明元素扩展命中区域。

稳定列快照只存储直接 item container 引用和列索引，由 `MasonryPanel` 单独持有。每次 Stable Arrange 后重建快照，detached 时清空；不复制业务数据，不要求稳定 key，也不改变 ItemsControl 容器生命周期。

响应式 resolver 必须保持纯计算属性，不依赖 VisualTree、主题资源或控件实例默认值。控件级 fallback 始终保留在 Masonry effective state 层。

布局变化通知比较维度只包含 item container 数量、顺序、有效列和 `IsFullSpan`。不把像素矩形作为事件比较维度，可以避免宽度变化、字体渲染或子项高度细微变化造成高频事件。

## 源码索引

主要源码：

- `src/AtomUI.Desktop.Controls/Masonry/Masonry.cs`：公开控件类型、布局属性、attached property、`LayoutChanged` 事件入口。
- `src/AtomUI.Desktop.Controls/Masonry/MasonryLayoutStrategy.cs`：公开布局策略枚举，定义稳定列与经典重排语义。
- `src/AtomUI.Desktop.Controls/Masonry/MasonryPanel.cs`：internal 布局引擎，执行测量、排列、响应式断点监听和布局结果比较。
- `src/AtomUI.Desktop.Controls/Masonry/MasonryItemSpan.cs`：子项 span 枚举。
- `src/AtomUI.Desktop.Controls/Masonry/MasonryLayoutChangedEventArgs.cs`：布局结果事件参数。
- `src/AtomUI.Desktop.Controls/Masonry/Themes/MasonryTheme.axaml`：默认 ControlTheme，装配 `ItemsPresenter` 和 `MasonryPanel`。

## 相关文档

- 源设计文档：`docs/controls/desktop/layout/masonry/overview.md`
- 实现文档：`docs/controls/desktop/layout/masonry/implementation.md`
- 变更记录：`docs/controls/desktop/layout/masonry/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/space/index-cn.md

# Space

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

Space 是 AtomUI 桌面控件体系中的间距布局控件，用于在一组子元素之间提供一致的水平、垂直或紧凑间距。

Space 不负责复杂响应式排版、滚动容器或数据虚拟化列表。这些职责应由业务层、组合控件或更专用的 AtomUI 控件承担。

主要源码入口：

- `src/AtomUI.Desktop.Controls/Space`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/Layout/Space` |
| 状态 | Stable |

## 何时使用

Space 的设计语言围绕控件职责、可观察状态和主题契约组织，而不是围绕模板节点组织。

| 维度 | 含义 | Space 中的表达 |
| --- | --- | --- |
| 产品语义 | 控件在界面中承担的稳定职责。 | Space 是 AtomUI 桌面控件体系中的间距布局控件，用于在一组子元素之间提供一致的水平、垂直或紧凑间距。 |
| 内容承载 | 用户数据、展示内容、集合项或操作入口如何进入控件。 | `CompactSpaceItemPosition`、`Content`、`ContentTemplate`、`ItemHeight`、`ItemSpacing`、`ItemWidth`、`ItemsAlignment`。 |
| 状态反馈 | public API、内部状态和伪类如何形成用户可感知反馈。 | collection/filter、visual option。 |
| 主题语义 | ControlTheme、SharedToken、控件 Token 和模板绑定如何表达视觉。 | Space Token + ControlTheme。 |

## 公共 API

Space 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。

核心 public surface 按语义分组维护：

| 契约组 | 代表成员 | 维护含义 |
| --- | --- | --- |
| 内容与数据 | `CompactSpaceItemPosition`、`Content`、`ContentTemplate`、`ItemHeight`、`ItemSpacing`、`ItemWidth`、`ItemsAlignment` | 定义控件展示内容、输入数据、模板或业务对象入口。 |
| 选择与集合 | `PositionIndex` | 维护选择、展开、过滤、分页、分组或集合状态。 |
| 交互与状态 | `IsUsedInCompactSpace`、`Status` | 表达用户可观察状态、可用性、清除、加载或反馈语义。 |
| 视觉与布局 | `CompactSpaceOrientation`、`LineSpacing`、`Orientation`、`SizeType`、`StyleVariant` | 影响尺寸、位置、颜色、形状、密度和模板视觉变量。 |

当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。

主要公开类型与枚举：

- 类型：`CompactSpace`、`CompactSpaceAddOn`、`CompactSpaceAwareControlProperty`、`CompactSpaceFiller`、`CompactSpaceItem`、`InvalidSpaceFillerUsageException`、`Space`。
- 枚举：`CompactSpaceUnitType`、`SpaceItemsAlignment`。

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_ContentLayout` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |

当前未抽取到控件专属伪类；主题主要依赖 Avalonia 标准伪类、模板绑定和内部 StyledProperty。

## 事件与命令

Space 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。
当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 垂直间距

来源：`controlgallery/AtomUIGallery/ShowCases/Layout/Space/Views/SpaceShowCase.axaml:74`

Gallery key：`ExamplesContent` / item `1`

```axaml
<atom:Space Orientation="Vertical" HorizontalAlignment="Stretch" Height="300">
    <atom:Card Header="卡片" SizeType="Middle" HorizontalAlignment="Stretch">
        <StackPanel Orientation="Vertical" Spacing="10">
            <atom:TextBlock Text="卡片内容" />
            <atom:TextBlock Text="卡片内容" />
        </StackPanel>
    </atom:Card>

    <atom:Card Header="卡片" SizeType="Middle" HorizontalAlignment="Stretch">
        <StackPanel Orientation="Vertical" Spacing="10">
            <atom:TextBlock Text="卡片内容" />
            <atom:TextBlock Text="卡片内容" />
        </StackPanel>
    </atom:Card>

    <atom:Card Header="卡片" SizeType="Middle" HorizontalAlignment="Stretch">
        <StackPanel Orientation="Vertical" Spacing="10">
            <atom:TextBlock Text="卡片内容" />
            <atom:TextBlock Text="卡片内容" />
        </StackPanel>
    </atom:Card>
</atom:Space>
```

### 间距尺寸

来源：`controlgallery/AtomUIGallery/ShowCases/Layout/Space/Views/SpaceShowCase.axaml:105`

Gallery key：`ExamplesContent` / item `2`

```axaml
<StackPanel Orientation="Vertical" Spacing="15">
    <StackPanel Orientation="Horizontal" Spacing="10">
        <atom:RadioButton Tag="{x:Static atom:CustomizableSizeType.Small}"
                          IsCheckedChanged="HandleSizeTypeChanged"
                          IsChecked="True" Content="小号" />
        <atom:RadioButton Tag="{x:Static atom:CustomizableSizeType.Middle}"
                          IsCheckedChanged="HandleSizeTypeChanged" Content="中号" />
        <atom:RadioButton Tag="{x:Static atom:CustomizableSizeType.Large}"
                          IsCheckedChanged="HandleSizeTypeChanged" Content="大号" />
        <atom:RadioButton Tag="{x:Static atom:CustomizableSizeType.Custom}"
                          IsCheckedChanged="HandleSizeTypeChanged" Content="自定义" />
    </StackPanel>
    <atom:Slider Name="CustomSizeSlider"
                 Minimum="0" Maximum="100"
                 Value="{Binding CustomSpacingValue, Mode=TwoWay}"
                 ValueChanged="HandleCustomSpacingValueChanged"
                 IsVisible="False"/>
    <atom:Space Name="SizeDemoSpace"
                Orientation="Horizontal"
                SizeType="{Binding SizeType}">
        <atom:Button ButtonType="Primary" Content="主要" />
        <atom:Button Content="默认" />
        <atom:Button ButtonType="Dashed" Content="虚线" />
        <atom:Button ButtonType="Link" Content="链接" />
    </atom:Space>
</StackPanel>
```

### 对齐

来源：`controlgallery/AtomUIGallery/ShowCases/Layout/Space/Views/SpaceShowCase.axaml:140`

Gallery key：`ExamplesContent` / item `3`

```axaml
<StackPanel Orientation="Horizontal" Spacing="10">
    <Border Classes="align-box">
        <atom:Space ItemsAlignment="Start" Orientation="Horizontal">
            <atom:TextBlock Text="居中" />
            <atom:Button ButtonType="Primary" Content="主要" />
            <Border Classes="align-block-item">
                <atom:TextBlock VerticalAlignment="Center" Text="块" />
            </Border>
        </atom:Space>
    </Border>

    <Border Classes="align-box">
        <atom:Space ItemsAlignment="Center" Orientation="Horizontal">
            <atom:TextBlock Text="居中" />
            <atom:Button ButtonType="Primary" Content="主要" />
            <Border Classes="align-block-item">
                <atom:TextBlock VerticalAlignment="Center" Text="块" />
            </Border>
        </atom:Space>
    </Border>

    <Border Classes="align-box">
        <atom:Space ItemsAlignment="End" Orientation="Horizontal">
            <atom:TextBlock Text="居中" />
            <atom:Button ButtonType="Primary" Content="主要" />
            <Border Classes="align-block-item">
                <atom:TextBlock VerticalAlignment="Center" Text="块" />
            </Border>
        </atom:Space>
    </Border>
</StackPanel>
```

### 自动换行

来源：`controlgallery/AtomUIGallery/ShowCases/Layout/Space/Views/SpaceShowCase.axaml:180`

Gallery key：`ExamplesContent` / item `4`

```axaml
<atom:Space Orientation="Horizontal" ItemSpacing="8" LineSpacing="16">
    <atom:Button Content="按钮" />
    <atom:Button Content="按钮" />
    <atom:Button Content="按钮" />
    <atom:Button Content="按钮" />
    <atom:Button Content="按钮" />
    <atom:Button Content="按钮" />
    <atom:Button Content="按钮" />
    <atom:Button Content="按钮" />
    <atom:Button Content="按钮" />
    <atom:Button Content="按钮" />
    <atom:Button Content="按钮" />
    <atom:Button Content="按钮" />
    <atom:Button Content="按钮" />
    <atom:Button Content="按钮" />
    <atom:Button Content="按钮" />
</atom:Space>
```

## 状态模型

Space 的状态流按以下路径收敛：

```text
Public API / inherited command / item source / user input
  -> 控件实例状态
  -> effective state / pseudo-class / template property
  -> ControlTheme selector / presenter / renderer
  -> Gallery 可观察行为
```

状态维护规则：

- Disabled 或不可交互状态优先屏蔽 pointer、keyboard、motion 和提交类反馈。
- collection/filter、visual option 状态由控件实例或明确的数据 owner 推导，不能在 template part 之间双向竞争。
- 模板重套用时必须把 public API 对应状态回放到新的 part、伪类和主题变量。
- 集合、弹层、异步、动效或窗口相关状态必须能处理 reset、close、cancel、detach 和 owner 释放。

## 主题与 Design Token

Space 的视觉模型由控件模板、ControlTheme、SharedToken 和必要的控件 Token 共同构成。

| 主题文件 | 职责 |
| --- | --- |
| `CompactSpaceAddOnTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `CompactSpaceTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |

Space 使用 `SpaceToken` 作为控件 Token scope。Token 只表达组件视觉语义，不承载 collection/filter、visual option 运行时状态。

主题维护规则：

- 不删除或重命名已经稳定的 ControlTheme key、template part、伪类和资源 key。
- 不把可由 AXAML 表达的模板状态迁移为 C# 动态创建视觉。
- 不把 hover、pressed、selected、expanded、loading、filter、popup open 等运行时状态写入 Token。
- Browser 或平台特化主题必须保持同一 API 的语义一致。

Token 来源：

Space Token 只表达组件级视觉变量，例如尺寸、间距、颜色、圆角、阴影、图标尺寸和弹层边界。Token 不承载运行时选择、展开、加载、错误、上传任务、过滤条件或业务状态。

当前 Token scope：

- `SpaceToken`，scope id 为 `Space`，源码位于 `src/AtomUI.Desktop.Controls/Space/SpaceToken.cs`。

## AOT 与裁剪注意事项

资源和 AOT 约束：

- 不通过运行时反射扫描 public API、Token 或 Gallery 示例数据。
- 不把可静态声明的模板结构迁移到 C# 动态创建。
- 异步加载、上传、弹层和窗口生命周期必须能取消或释放。
- 缓存对象必须与控件、窗口、弹层或数据 owner 生命周期一致。
- Source generator 生成文件不手工编辑；需要修改时改输入源或 generator。

性能边界：

- 控件应优先复用 Avalonia 原生虚拟化、模板绑定和资源系统。
- 避免为每次状态变化创建不必要的视觉对象、订阅或动画对象。
- 大集合控件必须保证 container recycle 后不会泄漏旧 item 状态。

## 源码索引

主要源码文件：

- `src/AtomUI.Desktop.Controls/Space/CompactSpace.cs`
- `src/AtomUI.Desktop.Controls/Space/CompactSpaceAddOn.cs`
- `src/AtomUI.Desktop.Controls/Space/CompactSpaceFiller.cs`
- `src/AtomUI.Desktop.Controls/Space/CompactSpaceItem.cs`
- `src/AtomUI.Desktop.Controls/Space/CompactSpaceSize.cs`
- `src/AtomUI.Desktop.Controls/Space/ICompactSpaceAware.cs`
- `src/AtomUI.Desktop.Controls/Space/InvalidSpaceFillerUsageException.cs`
- `src/AtomUI.Desktop.Controls/Space/Space.cs`
- `src/AtomUI.Desktop.Controls/Space/SpaceToken.cs`
- `src/AtomUI.Desktop.Controls/Space/Themes/CompactSpaceAddOnTheme.axaml`
- `src/AtomUI.Desktop.Controls/Space/Themes/CompactSpaceTheme.axaml`

职责边界：

- 控件主文件保留 public/protected API、Avalonia 属性注册、事件和主要生命周期入口。
- Theme 文件负责静态视觉结构、template part、selector 和资源绑定。
- Token 文件只提供组件视觉变量，不保存实例状态。
- Gallery 文件只展示用法和示例，不作为运行时逻辑 owner。

## 相关文档

- 源设计文档：`docs/controls/desktop/layout/space/overview.md`
- 实现文档：`docs/controls/desktop/layout/space/implementation.md`
- Token 文档：`docs/controls/desktop/layout/space/token.md`
- 变更记录：`docs/controls/desktop/layout/space/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/splitter/index-cn.md

# Splitter

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

Splitter 是 AtomUI 桌面控件体系中的分割面板控件，用于把一个区域拆成两个或多个可调整尺寸的面板。它负责面板尺寸计算、拖拽调整、延迟拖拽反馈、面板折叠状态和分割把手视觉。

Splitter 不负责窗口停靠系统、路由容器、业务布局状态持久化、面板内容装饰或复杂 IDE docking。子面板的内容、背景、内部圆角和业务状态应由用户提供的子控件负责。

主要源码入口：

- `src/AtomUI.Desktop.Controls/Splitter/Splitter.cs`
- `src/AtomUI.Desktop.Controls/Splitter/SplitterPanel.cs`
- `src/AtomUI.Desktop.Controls/Splitter/SplitterHandle.cs`
- `src/AtomUI.Desktop.Controls/Splitter/SplitterDragBar.cs`
- `src/AtomUI.Desktop.Controls/Splitter/Themes/`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/Layout/Splitter` |
| 状态 | Stable |

## 何时使用

Splitter 的设计语言是“低干扰的可调整边界”。分割线应明确表示可拖拽边界，但不能抢夺面板内容的视觉层级。折叠图标只在可折叠边界上表达动作，不应让普通分割线看起来像常驻工具栏。

| 维度 | 含义 | Splitter 中的表达 |
| --- | --- | --- |
| 产品语义 | 将一个区域拆成若干可调整子区域。 | `Children` 承载面板，`Orientation` 决定分割方向。 |
| 内容承载 | 面板内容由用户直接提供。 | 子控件通过 `Splitter.Size`、`Splitter.MinSize` 等附加属性参与布局。 |
| 状态反馈 | 拖拽、可折叠、禁用拖拽和延迟反馈需要可见状态。 | `ResizeStarted` / `ResizeDelta` / `ResizeCompleted` 事件、handle hover/dragging 视觉和折叠按钮。 |
| 主题语义 | 外层容器与分割把手分开定制。 | `SplitterTheme` 承载根框架，`SplitterHandleTheme` 和 `SplitterDragBarTheme` 承载分割线和拖拽命中区。 |

## 公共 API

Splitter 的公共契约由根控件 API、面板附加属性、折叠模型、事件和主题入口共同组成。`SplitterPanel`、`SplitterHandle` 和 `SplitterDragBar` 是 internal 协作对象，不属于用户可直接依赖的 public surface。

| 契约组 | 代表成员 | 维护含义 |
| --- | --- | --- |
| 根布局 | `Orientation`、`Children` | 定义面板排列方向和面板集合。 |
| 拖拽反馈 | `IsLazy`、`HandleSize` | 控制拖拽反馈时机和把手命中区域尺寸。 |
| 折叠入口 | `CollapsePreviousIcon`、`CollapseNextIcon`、`Splitter.Collapsible`、`Splitter.IsCollapsed` | 定义面板是否可折叠、折叠状态以及折叠图标。 |
| 面板尺寸 | `Splitter.Size`、`Splitter.DefaultSize`、`Splitter.MinSize`、`Splitter.MaxSize` | 定义子面板初始尺寸、默认尺寸和尺寸约束。 |
| 面板交互 | `Splitter.IsResizable` | 控制相邻边界是否允许拖拽调整。 |
| 事件 | `ResizeStarted`、`ResizeDelta`、`ResizeCompleted` | 把 internal drag 流转换为控件级 resize 事件。 |
| 主题入口 | `HandleSize`、控件 Token、根模板外观入口 | 区分命中区域、可见分割线和根框架外观。 |

样式能力边界：

- `HandleSize` 只表示分割把手的命中区域，不等同于可见分割线厚度。
- 可见分割线的厚度、圆角和 hover/dragging 颜色属于 handle 视觉模型，应通过根控件实例属性和 Token 映射到 internal handle，而不是要求用户样式化 internal 类型。
- Splitter 根框架可承接 `Background`、`BorderBrush`、`BorderThickness`、`CornerRadius` 等基础外观属性；子面板圆角仍由用户提供的面板控件自行控制。
- 不新增 `PanelCornerRadius`、`PanelBackground` 这类统一改写子面板的属性，避免 Splitter 篡改用户内容树。

稳定 template part：

| Template Part | 类型 | 稳定性 | 职责 |
| --- | --- | --- | --- |
| `PART_SplitterPanel` | `SplitterPanel` | public control template part | 承载子面板并生成 internal handle。 |
| `PART_HandleLine` | `Border` | internal handle template part | 展示可见分割线。 |
| `PART_DragBar` | `SplitterDragBar` | internal handle template part | 提供拖拽命中区和拖拽事件源。 |
| `PART_Grip` | `Border` | internal drag-bar template part | 展示拖拽区域内的 grip。 |
| `PART_CollapsePrevButton` | `IconButton` | internal handle template part | 触发前侧面板折叠或展开。 |
| `PART_CollapseNextButton` | `IconButton` | internal handle template part | 触发后侧面板折叠或展开。 |

`PART_HandleLine`、`PART_DragBar`、`PART_Grip` 和折叠按钮虽然是稳定 internal theme part，但不应作为用户自定义入口暴露。用户级定制应通过 `Splitter` public API、控件 Token、ControlTheme 和 Gallery 展示的稳定用法进入。

伪类模型：

- `Splitter` 根控件当前不定义专属伪类。
- `SplitterHandle` 使用 `:pointerover` 和 `:dragging` 驱动分割线颜色。
- `SplitterDragBar` 使用 `:dragging` 表达拖拽状态。
- `IconButton` 折叠按钮继续使用标准 pointer/pressed/disabled 视觉。

## 事件与命令

Splitter 的公共契约由根控件 API、面板附加属性、折叠模型、事件和主题入口共同组成。`SplitterPanel`、`SplitterHandle` 和 `SplitterDragBar` 是 internal 协作对象，不属于用户可直接依赖的 public surface。
| 事件 | `ResizeStarted`、`ResizeDelta`、`ResizeCompleted` | 把 internal drag 流转换为控件级 resize 事件。 |
| `PART_DragBar` | `SplitterDragBar` | internal handle template part | 提供拖拽命中区和拖拽事件源。 |

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/Layout/Splitter/Views/SplitterShowCase.axaml:59`

Gallery key：`ExamplesContent` / item `0`

```axaml
<Border Classes="splitter-surface">
    <atom:Splitter Orientation="Vertical" Height="220">
        <Border Classes="splitter-panel" atom:Splitter.Size="30%">
            <TextBlock Classes="splitter-label" Text="第一项" />
        </Border>
        <Border Classes="splitter-panel" atom:Splitter.DefaultSize="100" atom:Splitter.MinSize="60">
            <TextBlock Classes="splitter-label" Text="第二项" />
        </Border>
    </atom:Splitter>
</Border>
```

### 水平分割

来源：`controlgallery/AtomUIGallery/ShowCases/Layout/Splitter/Views/SplitterShowCase.axaml:77`

Gallery key：`ExamplesContent` / item `1`

```axaml
<Border Classes="splitter-surface">
    <atom:Splitter Orientation="Horizontal" Height="220">
        <Border Classes="splitter-panel" atom:Splitter.Size="40%">
            <TextBlock Classes="splitter-label" Text="顶部" />
        </Border>
        <Border Classes="splitter-panel alt">
            <TextBlock Classes="splitter-label" Text="底部" />
        </Border>
    </atom:Splitter>
</Border>
```

### 组合布局

来源：`controlgallery/AtomUIGallery/ShowCases/Layout/Splitter/Views/SplitterShowCase.axaml:95`

Gallery key：`ExamplesContent` / item `2`

```axaml
<Border Classes="splitter-surface">
    <atom:Splitter Orientation="Vertical" Height="260">
        <Border Classes="splitter-panel" atom:Splitter.Size="40%">
            <TextBlock Classes="splitter-label" Text="左侧" />
        </Border>
        <atom:Splitter Orientation="Horizontal">
            <Border Classes="splitter-panel">
                <TextBlock Classes="splitter-label" Text="顶部" />
            </Border>
            <Border Classes="splitter-panel alt">
                <TextBlock Classes="splitter-label" Text="底部" />
            </Border>
        </atom:Splitter>
    </atom:Splitter>
</Border>
```

### 禁用拖拽调整

来源：`controlgallery/AtomUIGallery/ShowCases/Layout/Splitter/Views/SplitterShowCase.axaml:118`

Gallery key：`ExamplesContent` / item `3`

```axaml
<Border Classes="splitter-surface">
    <atom:Splitter Orientation="Vertical" Height="220">
        <Border Classes="splitter-panel" atom:Splitter.Size="35%">
            <TextBlock Classes="splitter-label" Text="Resizable" />
        </Border>
        <Border Classes="splitter-panel alt" atom:Splitter.DefaultSize="120" atom:Splitter.IsResizable="False">
            <TextBlock Classes="splitter-label" Text="Not Resizable" />
        </Border>
        <Border Classes="splitter-panel" atom:Splitter.DefaultSize="120">
            <TextBlock Classes="splitter-label" Text="Resizable" />
        </Border>
    </atom:Splitter>
</Border>
```

## 状态模型

Splitter 的状态流按以下路径收敛：

```text
Public API / attached panel properties / pointer drag / collapse button
  -> SplitterPanel layout state
  -> SplitterHandle drag and collapse state
  -> SplitterDragBar pointer state
  -> ControlTheme selector / template binding / resize events
  -> Gallery 可观察行为
```

行为规则：

- `Orientation=Vertical` 表示左右分割，handle 横向拖动；`Orientation=Horizontal` 表示上下分割，handle 纵向拖动。
- `HandleSize` 控制 handle 在布局中占用的命中区域，不能被可见线条厚度替代。
- `IsLazy=False` 时拖拽过程中实时调整面板尺寸；`IsLazy=True` 时拖拽过程中移动 drag bar，完成后提交尺寸。
- `Splitter.Size` 是用户可双向绑定的实际尺寸入口；`DefaultSize` 是未提供实际尺寸时的初始化入口。
- `MinSize`、`MaxSize`、`IsResizable` 和折叠状态共同决定某个 handle 是否可拖拽。
- 折叠按钮只在相邻面板支持折叠或存在可恢复折叠面板时显示。
- resize 事件以 handle index 和当前尺寸快照暴露，不让用户直接依赖 internal handle 实例。

## 主题与 Design Token

Splitter 的视觉模型由根控件模板、internal 面板和 handle 模板、SharedToken、控件 Token 共同构成。

| 主题文件 | 职责 |
| --- | --- |
| `SplitterTheme.axaml` | 定义 `Splitter` 根模板、外层 Frame 和 `PART_SplitterPanel`。 |
| `SplitterHandleTheme.axaml` | 定义可见分割线、拖拽命中区和折叠按钮的组合结构。 |
| `SplitterDragBarTheme.axaml` | 定义拖拽命中区、grip 尺寸、grip 圆角和方向 cursor。 |

视觉语义拆分：

- 根框架：承载 Splitter 整体背景、边框、圆角和裁剪。
- 命中区域：由 `HandleSize` 和 `SplitBarHandleSize` 定义，保证拖拽易用性。
- 可见分割线：由 line thickness、line corner radius 和 line color 定义，不能反向改变命中区域。
- grip：由 drag bar 模板展示，使用较短尺寸提示可拖拽。
- 折叠按钮：依赖 `SplitterPanelCollapsible` 和 hover/press 状态显示，不参与面板尺寸计算。

主题维护规则：

- 不删除或重命名已经稳定的 ControlTheme key、template part、伪类和资源 key。
- 不把可由 AXAML 表达的模板状态迁移为 C# 动态创建视觉。
- 不把 hover、dragging、collapsed 等运行时状态写入 Token。
- 调整线条厚度时必须同时检查 `PART_HandleLine` 和 `PART_Grip`，避免可见线条与拖拽提示尺寸不一致。

Token 来源：

Splitter Token 只表达组件级视觉变量，包括分割线尺寸、拖拽命中区、折叠按钮定位、handle 颜色和图标尺寸。Token 不承载运行时拖拽状态、折叠状态、面板尺寸、业务布局数据或用户内容背景。

当前 Token scope：

- `SplitterToken`，scope id 为 `Splitter`，源码位于 `src/AtomUI.Desktop.Controls/Splitter/SplitterToken.cs`。

## AOT 与裁剪注意事项

资源边界：

- 不通过运行时反射扫描 public API、Token 或 Gallery 示例数据。
- 不把可静态声明的模板结构迁移到 C# 动态创建。
- handle、drag bar、button 的事件订阅必须在模板重套用或 handle 移除时解绑。
- `_trackedPanels` 中的面板属性订阅必须在面板离开时解绑。

性能边界：

- 面板集合变化时只重建必要的 handle 结构，避免把业务子控件重新包装成不可追踪结构。
- 拖拽过程中避免创建临时视觉对象。
- lazy 模式下拖拽反馈应复用现有 drag bar 状态，不引入额外弹层或全局输入监听。
- 分割线样式变化应通过 Avalonia 属性和模板绑定流动，不使用手动遍历视觉树刷新。

AOT 边界：

- API 与 Token 契约不通过运行时反射生成。
- Source generator 生成文件不手工编辑。
- 文档、Gallery 和源码发生冲突时，应修复源文档或结构化数据，不直接改 `docs/AI/generated/llms` 生成产物。

## 源码索引

主要源码文件：

- `src/AtomUI.Desktop.Controls/Splitter/Splitter.cs`
- `src/AtomUI.Desktop.Controls/Splitter/SplitterPanel.cs`
- `src/AtomUI.Desktop.Controls/Splitter/SplitterHandle.cs`
- `src/AtomUI.Desktop.Controls/Splitter/SplitterDragBar.cs`
- `src/AtomUI.Desktop.Controls/Splitter/SplitterPanelCollapsible.cs`
- `src/AtomUI.Desktop.Controls/Splitter/SplitterResizeEventArgs.cs`
- `src/AtomUI.Desktop.Controls/Splitter/SplitterToken.cs`
- `src/AtomUI.Desktop.Controls/Splitter/Themes/SplitterTheme.axaml`
- `src/AtomUI.Desktop.Controls/Splitter/Themes/SplitterHandleTheme.axaml`
- `src/AtomUI.Desktop.Controls/Splitter/Themes/SplitterDragBarTheme.axaml`

Gallery 与文档结构：

- `controlgallery/AtomUIGallery/ShowCases/Layout/Splitter`
- `tests/AtomUIGallery.Tests/ShowCases/SplitterShowCasePageTests.cs`
- `tests/AtomUIGallery.Tests/ShowCases/SplitterShowCaseExamples.snapshot`
- `docs/controls/desktop/layout/splitter/`

职责边界：

- `Splitter.cs` 保留 public API、附加属性、Children 同步、template part 获取和 resize 事件抛出。
- `SplitterPanel.cs` 保留布局、拖拽、折叠、尺寸约束和 handle 状态刷新。
- `SplitterHandle.cs` 保留单个 handle 的按钮、hover、dragging、collapse request 和 drag event 转发。
- `SplitterDragBar.cs` 保留 Thumb 输入事件和禁用拖拽拦截。
- Theme 文件负责静态视觉结构、template binding、selector 和 TokenResource 映射。
- Token 文件只提供组件视觉变量，不保存实例状态。

## 相关文档

- 源设计文档：`docs/controls/desktop/layout/splitter/overview.md`
- 实现文档：`docs/controls/desktop/layout/splitter/implementation.md`
- Token 文档：`docs/controls/desktop/layout/splitter/token.md`
- 变更记录：`docs/controls/desktop/layout/splitter/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/breadcrumb/index-cn.md

# Breadcrumb

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

Breadcrumb 是 AtomUI 桌面控件体系中的面包屑导航控件，用于展示当前位置路径并提供上级跳转入口。

Breadcrumb 不负责树控件、主导航菜单或完整路由系统。这些职责应由业务层、组合控件或更专用的 AtomUI 控件承担。

主要源码入口：

- `src/AtomUI.Desktop.Controls/Breadcrumb`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/Navigation/Breadcrumb` |
| 状态 | Stable |

## 何时使用

Breadcrumb 的设计语言围绕控件职责、可观察状态和主题契约组织，而不是围绕模板节点组织。

| 维度 | 含义 | Breadcrumb 中的表达 |
| --- | --- | --- |
| 产品语义 | 控件在界面中承担的稳定职责。 | Breadcrumb 是 AtomUI 桌面控件体系中的面包屑导航控件，用于展示当前位置路径并提供上级跳转入口。 |
| 内容承载 | 用户数据、展示内容、集合项或操作入口如何进入控件。 | `Icon`、`SeparatorTemplate`。 |
| 状态反馈 | public API、内部状态和伪类如何形成用户可感知反馈。 | motion。 |
| 主题语义 | ControlTheme、SharedToken、控件 Token 和模板绑定如何表达视觉。 | Breadcrumb Token + ControlTheme。 |

## 公共 API

Breadcrumb 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。

核心 public surface 按语义分组维护：

| 契约组 | 代表成员 | 维护含义 |
| --- | --- | --- |
| 内容与数据 | `Icon`、`SeparatorTemplate` | 定义控件展示内容、输入数据、模板或业务对象入口。 |
| 交互与状态 | `IsMotionEnabled` | 表达用户可观察状态、可用性、清除、加载或反馈语义。 |
| 其他稳定入口 | `NavigateContext`、`NavigateUri`、`Separator` | 保留为 public surface，变更前需确认 Gallery 和用户 XAML 依赖。 |

当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。

主要公开类型与枚举：

- 类型：`Breadcrumb`、`BreadcrumbItem`、`BreadcrumbItemData`、`BreadcrumbNavigateEventArgs`。
- 枚举：无。

稳定 template part：

当前控件没有显式 `[TemplatePart]` 契约；主题节点仍通过 ControlTheme key、资源 key 和 Gallery 可观察行为形成稳定边界。

控件专属或内部伪类包括 `BreadcrumbPseudoClass.HasIcon`、`BreadcrumbPseudoClass.IsLast`、`HasIcon=:has-icon`、`IsLast=:is-last`。这些伪类属于主题 selector 可观察契约，不能在未同步主题和 Gallery 的情况下重命名或删除。

## 事件与命令

Breadcrumb 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。
当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。
- 类型：`Breadcrumb`、`BreadcrumbItem`、`BreadcrumbItemData`、`BreadcrumbNavigateEventArgs`。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/Navigation/Breadcrumb/Views/BreadcrumbShowCase.axaml:37`

Gallery key：`ExamplesContent` / item `0`

```axaml
<StackPanel>
    <atom:Breadcrumb>
        <atom:BreadcrumbItem Content="Home" />
        <atom:BreadcrumbItem NavigateContext="#" Content="Application Center" />
        <atom:BreadcrumbItem NavigateContext="#" Content="Application List" />
        <atom:BreadcrumbItem Content="An Application" />
    </atom:Breadcrumb>
</StackPanel>
```

### 带参数

来源：`controlgallery/AtomUIGallery/ShowCases/Navigation/Breadcrumb/Views/BreadcrumbShowCase.axaml:74`

Gallery key：`ExamplesContent` / item `2`

```axaml
<StackPanel>
    <atom:Breadcrumb NavigateRequest="HandleNavigateRequest">
        <atom:BreadcrumbItem Content="Users" />
        <atom:BreadcrumbItem NavigateContext="Param(1)" Content="Param" />
    </atom:Breadcrumb>
</StackPanel>
```

### 配置分隔符

来源：`controlgallery/AtomUIGallery/ShowCases/Navigation/Breadcrumb/Views/BreadcrumbShowCase.axaml:91`

Gallery key：`ExamplesContent` / item `3`

```axaml
<StackPanel>
    <atom:Breadcrumb Separator=">">
        <atom:BreadcrumbItem Content="Home" />
        <atom:BreadcrumbItem NavigateContext="#" Content="Application Center" />
        <atom:BreadcrumbItem NavigateContext="#" Content="Application List" />
        <atom:BreadcrumbItem Content="An Application" />
    </atom:Breadcrumb>
</StackPanel>
```

### 单独配置分隔符

来源：`controlgallery/AtomUIGallery/ShowCases/Navigation/Breadcrumb/Views/BreadcrumbShowCase.axaml:110`

Gallery key：`ExamplesContent` / item `4`

```axaml
<StackPanel>
    <atom:Breadcrumb>
        <atom:BreadcrumbItem Separator=":" Content="Location" />
        <atom:BreadcrumbItem NavigateContext="#" Content="Application Center" />
        <atom:BreadcrumbItem NavigateContext="#" Content="Application List" />
        <atom:BreadcrumbItem Content="An Application" />
    </atom:Breadcrumb>
</StackPanel>
```

## 状态模型

Breadcrumb 的状态流按以下路径收敛：

```text
Public API / inherited command / item source / user input
  -> 控件实例状态
  -> effective state / pseudo-class / template property
  -> ControlTheme selector / presenter / renderer
  -> Gallery 可观察行为
```

状态维护规则：

- Disabled 或不可交互状态优先屏蔽 pointer、keyboard、motion 和提交类反馈。
- motion 状态由控件实例或明确的数据 owner 推导，不能在 template part 之间双向竞争。
- 模板重套用时必须把 public API 对应状态回放到新的 part、伪类和主题变量。
- 集合、弹层、异步、动效或窗口相关状态必须能处理 reset、close、cancel、detach 和 owner 释放。

## 主题与 Design Token

Breadcrumb 的视觉模型由控件模板、ControlTheme、SharedToken 和必要的控件 Token 共同构成。

| 主题文件 | 职责 |
| --- | --- |
| `BreadcrumbItemTheme.axaml` | 定义集合项、容器项或局部单元的状态视觉。 |
| `BreadcrumbTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |

Breadcrumb 使用 `BreadcrumbToken` 作为控件 Token scope。Token 只表达组件视觉语义，不承载 motion 运行时状态。

主题维护规则：

- 不删除或重命名已经稳定的 ControlTheme key、template part、伪类和资源 key。
- 不把可由 AXAML 表达的模板状态迁移为 C# 动态创建视觉。
- 不把 hover、pressed、selected、expanded、loading、filter、popup open 等运行时状态写入 Token。
- Browser 或平台特化主题必须保持同一 API 的语义一致。

Token 来源：

Breadcrumb Token 只表达组件级视觉变量，例如尺寸、间距、颜色、圆角、阴影、图标尺寸和弹层边界。Token 不承载运行时选择、展开、加载、错误、上传任务、过滤条件或业务状态。

当前 Token scope：

- `BreadcrumbToken`，scope id 为 `Breadcrumb`，源码位于 `src/AtomUI.Desktop.Controls/Breadcrumb/BreadcrumbToken.cs`。

## AOT 与裁剪注意事项

资源和 AOT 约束：

- 不通过运行时反射扫描 public API、Token 或 Gallery 示例数据。
- 不把可静态声明的模板结构迁移到 C# 动态创建。
- 异步加载、上传、弹层和窗口生命周期必须能取消或释放。
- 缓存对象必须与控件、窗口、弹层或数据 owner 生命周期一致。
- Source generator 生成文件不手工编辑；需要修改时改输入源或 generator。

性能边界：

- 控件应优先复用 Avalonia 原生虚拟化、模板绑定和资源系统。
- 避免为每次状态变化创建不必要的视觉对象、订阅或动画对象。
- 大集合控件必须保证 container recycle 后不会泄漏旧 item 状态。

## 源码索引

主要源码文件：

- `src/AtomUI.Desktop.Controls/Breadcrumb/Breadcrumb.cs`
- `src/AtomUI.Desktop.Controls/Breadcrumb/BreadcrumbItem.cs`
- `src/AtomUI.Desktop.Controls/Breadcrumb/BreadcrumbItemData.cs`
- `src/AtomUI.Desktop.Controls/Breadcrumb/BreadcrumbNavigateEventArgs.cs`
- `src/AtomUI.Desktop.Controls/Breadcrumb/BreadcrumbPseudoClass.cs`
- `src/AtomUI.Desktop.Controls/Breadcrumb/BreadcrumbToken.cs`
- `src/AtomUI.Desktop.Controls/Breadcrumb/IBreadcrumbItemData.cs`
- `src/AtomUI.Desktop.Controls/Breadcrumb/Themes/BreadcrumbItemTheme.axaml`
- `src/AtomUI.Desktop.Controls/Breadcrumb/Themes/BreadcrumbTheme.axaml`

职责边界：

- 控件主文件保留 public/protected API、Avalonia 属性注册、事件和主要生命周期入口。
- Theme 文件负责静态视觉结构、template part、selector 和资源绑定。
- Token 文件只提供组件视觉变量，不保存实例状态。
- Gallery 文件只展示用法和示例，不作为运行时逻辑 owner。

## 相关文档

- 源设计文档：`docs/controls/desktop/navigation/breadcrumb/overview.md`
- 实现文档：`docs/controls/desktop/navigation/breadcrumb/implementation.md`
- Token 文档：`docs/controls/desktop/navigation/breadcrumb/token.md`
- 变更记录：`docs/controls/desktop/navigation/breadcrumb/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/button-spinner/index-cn.md

# ButtonSpinner

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

ButtonSpinner 是 AtomUI 桌面控件体系中的带步进按钮的输入基座，用于承载增减、选择或候选切换类输入。

ButtonSpinner 不负责普通按钮、完整数字输入或 ComboBox 选择器。这些职责应由业务层、组合控件或更专用的 AtomUI 控件承担。

主要源码入口：

- `src/AtomUI.Desktop.Controls/ButtonSpinner`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/Navigation/ButtonSpinner` |
| 状态 | Stable |

## 何时使用

ButtonSpinner 的设计语言围绕控件职责、可观察状态和主题契约组织，而不是围绕模板节点组织。

| 维度 | 含义 | ButtonSpinner 中的表达 |
| --- | --- | --- |
| 产品语义 | 控件在界面中承担的稳定职责。 | ButtonSpinner 是 AtomUI 桌面控件体系中的带步进按钮的输入基座，用于承载增减、选择或候选切换类输入。 |
| 内容承载 | 用户数据、展示内容、集合项或操作入口如何进入控件。 | `ContentLeftShift`、`ContentPadding`、`ContentRightShift`、`InnerLeftContent`、`InnerLeftContentTemplate`、`InnerRightContent`、`InnerRightContentTemplate`、`LeftAddOnTemplate` 等 10 项。 |
| 状态反馈 | public API、内部状态和伪类如何形成用户可感知反馈。 | motion、visual option。 |
| 主题语义 | ControlTheme、SharedToken、控件 Token 和模板绑定如何表达视觉。 | ButtonSpinner Token + ControlTheme。 |

## 公共 API

ButtonSpinner 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。

核心 public surface 按语义分组维护：

| 契约组 | 代表成员 | 维护含义 |
| --- | --- | --- |
| 内容与数据 | `ContentLeftShift`、`ContentPadding`、`ContentRightShift`、`InnerLeftContent`、`InnerLeftContentTemplate`、`InnerRightContent`、`InnerRightContentTemplate`、`LeftAddOnTemplate`、`RightAddOnTemplate`、`SpinnerContent` | 定义控件展示内容、输入数据、模板或业务对象入口。 |
| 交互与状态 | `IsButtonSpinnerFloatable`、`IsButtonSpinnerVisible`、`IsHandleFloatable`、`IsMotionEnabled`、`IsShowHandle`、`IsSpinEnabled`、`Status` | 表达用户可观察状态、可用性、清除、加载或反馈语义。 |
| 视觉与布局 | `HandleOffset`、`SizeType`、`SpinnerBorderThickness`、`SpinnerHandleWidth`、`StyleVariant` | 影响尺寸、位置、颜色、形状、密度和模板视觉变量。 |
| 其他稳定入口 | `ButtonSpinnerLocation`、`HandleOpacity`、`LeftAddOn`、`RightAddOn` | 保留为 public surface，变更前需确认 Gallery 和用户 XAML 依赖。 |

当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。

主要公开类型与枚举：

- 类型：`ButtonSpinner`、`ButtonSpinnerContentPanel`、`ButtonSpinnerDecoratedBox`、`ButtonSpinnerHandle`。
- 枚举：`ButtonSpinnerLocation`。

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_ContentFrame` | `?` | 承载根视觉、边框、背景或尺寸基线。 |
| `PART_DecoratedBox` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_DecreaseButton` | `?` | 承载用户触发入口、导航或关闭动作。 |
| `PART_IncreaseButton` | `?` | 承载用户触发入口、导航或关闭动作。 |
| `PART_SpinnerHandle` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |

控件专属或内部伪类包括 `ButtonSpinnerPseudoClass.Left`、`ButtonSpinnerPseudoClass.Right`、`Left=:left`、`Right=:right`。这些伪类属于主题 selector 可观察契约，不能在未同步主题和 Gallery 的情况下重命名或删除。

## 事件与命令

ButtonSpinner 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。
当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/Navigation/ButtonSpinner/Views/ButtonSpinnerShowCase.axaml:36`

Gallery key：`ExamplesContent` / item `0`

```axaml
<atom:ButtonSpinner>
    <atom:TextBlock
        HorizontalAlignment="Left"
        VerticalAlignment="Center"
        Text="床前明月光" />
</atom:ButtonSpinner>
```

### 按钮微调器尺寸

来源：`controlgallery/AtomUIGallery/ShowCases/Navigation/ButtonSpinner/Views/ButtonSpinnerShowCase.axaml:52`

Gallery key：`ExamplesContent` / item `1`

```axaml
<Grid ColumnDefinitions="Auto,Auto"
      RowDefinitions="Auto,Auto,Auto,Auto"
      ColumnSpacing="12"
      RowSpacing="10"
      Margin="0,0,20,0">
    <atom:TextBlock VerticalAlignment="Center"
                    Foreground="{atom:SharedTokenResource ColorTextSecondary}"
                    Text="大号" />
    <atom:ButtonSpinner Grid.Column="1" SizeType="Large">
        <atom:TextBlock
            HorizontalAlignment="Left"
            VerticalAlignment="Center"
            Text="床前明月光" />
    </atom:ButtonSpinner>

    <atom:TextBlock Grid.Row="1"
                    VerticalAlignment="Center"
                    Foreground="{atom:SharedTokenResource ColorTextSecondary}"
                    Text="中号" />
    <atom:ButtonSpinner Grid.Row="1" Grid.Column="1" SizeType="Middle">
        <atom:TextBlock
            HorizontalAlignment="Left"
            VerticalAlignment="Center"
            Text="床前明月光" />
    </atom:ButtonSpinner>

    <atom:TextBlock Grid.Row="2"
                    VerticalAlignment="Center"
                    Foreground="{atom:SharedTokenResource ColorTextSecondary}"
                    Text="小号" />
    <atom:ButtonSpinner Grid.Row="2" Grid.Column="1" SizeType="Small">
        <atom:TextBlock
            HorizontalAlignment="Left"
            VerticalAlignment="Center"
            Text="床前明月光" />
    </atom:ButtonSpinner>

    <atom:TextBlock Grid.Row="3"
                    VerticalAlignment="Center"
                    Foreground="{atom:SharedTokenResource ColorTextSecondary}"
                    Text="Custom" />
    <atom:ButtonSpinner Grid.Row="3"
                        Grid.Column="1"
                        SizeType="Custom"
                        Height="36"
                        SpinnerHandleWidth="28">
        <atom:TextBlock
            HorizontalAlignment="Left"
            VerticalAlignment="Center"
            FontSize="15"
            Text="床前明月光" />
    </atom:ButtonSpinner>
</Grid>
```

### 不同形态

来源：`controlgallery/AtomUIGallery/ShowCases/Navigation/ButtonSpinner/Views/ButtonSpinnerShowCase.axaml:115`

Gallery key：`ExamplesContent` / item `2`

```axaml
<StackPanel Orientation="Vertical" Spacing="10">
    <atom:ButtonSpinner StyleVariant="Outlined">
        <atom:TextBlock
            HorizontalAlignment="Left"
            VerticalAlignment="Center"
            Text="床前明月光" />
    </atom:ButtonSpinner>
    <atom:ButtonSpinner StyleVariant="Filled">
        <atom:TextBlock
            HorizontalAlignment="Left"
            VerticalAlignment="Center"
            Text="床前明月光" />
    </atom:ButtonSpinner>
    <atom:ButtonSpinner StyleVariant="Borderless">
        <atom:TextBlock
            HorizontalAlignment="Left"
            VerticalAlignment="Center"
            Text="床前明月光" />
    </atom:ButtonSpinner>
</StackPanel>
```

### 禁用状态

来源：`controlgallery/AtomUIGallery/ShowCases/Navigation/ButtonSpinner/Views/ButtonSpinnerShowCase.axaml:145`

Gallery key：`ExamplesContent` / item `3`

```axaml
<StackPanel Orientation="Vertical" Spacing="10">
    <atom:ButtonSpinner StyleVariant="Outlined" IsEnabled="False">
        <atom:TextBlock
            HorizontalAlignment="Left"
            VerticalAlignment="Center"
            Text="床前明月光" />
    </atom:ButtonSpinner>
    <atom:ButtonSpinner StyleVariant="Filled" IsEnabled="False">
        <atom:TextBlock
            HorizontalAlignment="Left"
            VerticalAlignment="Center"
            Text="床前明月光" />
    </atom:ButtonSpinner>
    <atom:ButtonSpinner StyleVariant="Borderless" IsEnabled="False">
        <atom:TextBlock
            HorizontalAlignment="Left"
            VerticalAlignment="Center"
            Text="床前明月光" />
    </atom:ButtonSpinner>
</StackPanel>
```

## 状态模型

ButtonSpinner 的状态流按以下路径收敛：

```text
Public API / inherited command / item source / user input
  -> 控件实例状态
  -> effective state / pseudo-class / template property
  -> ControlTheme selector / presenter / renderer
  -> Gallery 可观察行为
```

状态维护规则：

- Disabled 或不可交互状态优先屏蔽 pointer、keyboard、motion 和提交类反馈。
- motion、visual option 状态由控件实例或明确的数据 owner 推导，不能在 template part 之间双向竞争。
- 模板重套用时必须把 public API 对应状态回放到新的 part、伪类和主题变量。
- 集合、弹层、异步、动效或窗口相关状态必须能处理 reset、close、cancel、detach 和 owner 释放。

## 主题与 Design Token

ButtonSpinner 的视觉模型由控件模板、ControlTheme、SharedToken 和必要的控件 Token 共同构成。

| 主题文件 | 职责 |
| --- | --- |
| `ButtonSpinnerDecoratedBoxTheme.axaml` | 定义局部操作入口、按钮或 handle 的状态视觉。 |
| `ButtonSpinnerHandleTheme.axaml` | 定义局部操作入口、按钮或 handle 的状态视觉。 |
| `ButtonSpinnerTheme.axaml` | 定义局部操作入口、按钮或 handle 的状态视觉。 |

ButtonSpinner 使用 `ButtonSpinnerToken` 作为控件 Token scope。Token 只表达组件视觉语义，不承载 motion、visual option 运行时状态。

主题维护规则：

- 不删除或重命名已经稳定的 ControlTheme key、template part、伪类和资源 key。
- 不把可由 AXAML 表达的模板状态迁移为 C# 动态创建视觉。
- 不把 hover、pressed、selected、expanded、loading、filter、popup open 等运行时状态写入 Token。
- Browser 或平台特化主题必须保持同一 API 的语义一致。

Token 来源：

ButtonSpinner Token 只表达组件级视觉变量，例如尺寸、间距、颜色、圆角、阴影、图标尺寸和弹层边界。Token 不承载运行时选择、展开、加载、错误、上传任务、过滤条件或业务状态。

当前 Token scope：

- `ButtonSpinnerToken`，scope id 为 `ButtonSpinner`，源码位于 `src/AtomUI.Desktop.Controls/ButtonSpinner/ButtonSpinnerToken.cs`。

## AOT 与裁剪注意事项

资源和 AOT 约束：

- 不通过运行时反射扫描 public API、Token 或 Gallery 示例数据。
- 不把可静态声明的模板结构迁移到 C# 动态创建。
- 异步加载、上传、弹层和窗口生命周期必须能取消或释放。
- 缓存对象必须与控件、窗口、弹层或数据 owner 生命周期一致。
- Source generator 生成文件不手工编辑；需要修改时改输入源或 generator。

性能边界：

- 控件应优先复用 Avalonia 原生虚拟化、模板绑定和资源系统。
- 避免为每次状态变化创建不必要的视觉对象、订阅或动画对象。
- 大集合控件必须保证 container recycle 后不会泄漏旧 item 状态。

## 源码索引

主要源码文件：

- `src/AtomUI.Desktop.Controls/ButtonSpinner/ButtonSpinner.cs`
- `src/AtomUI.Desktop.Controls/ButtonSpinner/ButtonSpinnerContentPanel.cs`
- `src/AtomUI.Desktop.Controls/ButtonSpinner/ButtonSpinnerDecoratedBox.cs`
- `src/AtomUI.Desktop.Controls/ButtonSpinner/ButtonSpinnerHandle.cs`
- `src/AtomUI.Desktop.Controls/ButtonSpinner/ButtonSpinnerPseudoClass.cs`
- `src/AtomUI.Desktop.Controls/ButtonSpinner/ButtonSpinnerToken.cs`
- `src/AtomUI.Desktop.Controls/ButtonSpinner/Themes/ButtonSpinnerDecoratedBoxTheme.axaml`
- `src/AtomUI.Desktop.Controls/ButtonSpinner/Themes/ButtonSpinnerHandleTheme.axaml`
- `src/AtomUI.Desktop.Controls/ButtonSpinner/Themes/ButtonSpinnerTheme.axaml`

职责边界：

- 控件主文件保留 public/protected API、Avalonia 属性注册、事件和主要生命周期入口。
- Theme 文件负责静态视觉结构、template part、selector 和资源绑定。
- Token 文件只提供组件视觉变量，不保存实例状态。
- Gallery 文件只展示用法和示例，不作为运行时逻辑 owner。

## 相关文档

- 源设计文档：`docs/controls/desktop/navigation/button-spinner/overview.md`
- 实现文档：`docs/controls/desktop/navigation/button-spinner/implementation.md`
- Token 文档：`docs/controls/desktop/navigation/button-spinner/token.md`
- 变更记录：`docs/controls/desktop/navigation/button-spinner/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/combo-box/index-cn.md

# ComboBox

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

ComboBox 是 AtomUI 桌面控件体系中的组合框控件，用于在可输入文本框和候选列表之间完成选择。

ComboBox 不负责远程自动完成、树形选择或多列数据选择。这些职责应由业务层、组合控件或更专用的 AtomUI 控件承担。

主要源码入口：

- `src/AtomUI.Desktop.Controls/ComboBox`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/Navigation/ComboBox` |
| 状态 | Stable |

## 何时使用

ComboBox 的设计语言围绕控件职责、可观察状态和主题契约组织，而不是围绕模板节点组织。

| 维度 | 含义 | ComboBox 中的表达 |
| --- | --- | --- |
| 产品语义 | 控件在界面中承担的稳定职责。 | ComboBox 是 AtomUI 桌面控件体系中的组合框控件，用于在可输入文本框和候选列表之间完成选择。 |
| 内容承载 | 用户数据、展示内容、集合项或操作入口如何进入控件。 | `ContentLeftAddOn`、`ContentLeftAddOnTemplate`、`ContentRightAddOn`、`ContentRightAddOnTemplate`、`FilterValue`、`FilterValueSelector`、`LeftAddOnTemplate`、`OptionFontSize` 等 9 项。 |
| 状态反馈 | public API、内部状态和伪类如何形成用户可感知反馈。 | open/close、collection/filter、input/value、motion、visual option。 |
| 主题语义 | ControlTheme、SharedToken、控件 Token 和模板绑定如何表达视觉。 | ComboBox Token + ControlTheme。 |

## 公共 API

ComboBox 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。

核心 public surface 按语义分组维护：

| 契约组 | 代表成员 | 维护含义 |
| --- | --- | --- |
| 内容与数据 | `ContentLeftAddOn`、`ContentLeftAddOnTemplate`、`ContentRightAddOn`、`ContentRightAddOnTemplate`、`FilterValue`、`FilterValueSelector`、`LeftAddOnTemplate`、`OptionFontSize`、`RightAddOnTemplate` | 定义控件展示内容、输入数据、模板或业务对象入口。 |
| 选择与集合 | `SelectedItem`、`SelectedIndex`、`DropDownDisplayPageSize`、`Filter`、`IsFilterEnabled` | 维护选择、展开、过滤、分页、分组或集合状态。 |
| 交互与状态 | `IsAllowClear`、`IsMotionEnabled`、`ShouldUseOverlayPopup`、`Status`、`IsShowOverflowTip`、`OverflowTipDelay`、`OverflowTipPlacement` | 表达用户可观察状态、可用性、清除、加载、反馈和非编辑态选中内容溢出提示语义。Form 校验扩展状态进入内部 `FormStatus`，不覆盖显式 `Status`。 |
| 视觉与布局 | `SizeType`、`StyleVariant` | 影响尺寸、位置、颜色、形状、密度和模板视觉变量。 |
| 其他稳定入口 | `LeftAddOn`、`RightAddOn` | 保留为 public surface，变更前需确认 Gallery 和用户 XAML 依赖。 |

当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。

主要公开类型与枚举：

- 类型：`ComboBox`、`ComboBoxHandle`、`ComboBoxItem`。
- 枚举：无。

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_ComboBoxHandle` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_ContentRightAddOnPresenter` | `?` | 展示用户内容、文本、图标或模板化数据。 |
| `PART_EditableTextBox` | `?` | 承载文本输入、过滤、显示或编辑入口。 |
| `PART_EmptyIndicator` | `?` | 展示指示器、进度、分页或状态反馈。 |
| `PART_FormFeedBack` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_ItemsPresenter` | `?` | 展示用户内容、文本、图标或模板化数据。 |
| `PART_OpenIndicatorButton` | `?` | 承载用户触发入口、导航或关闭动作。 |
| `PART_Popup` | `?` | 承载弹层宿主、打开关闭或候选内容。 |
| `PART_TextPresenter` | `?` | 展示用户内容、文本、图标或模板化数据。 |

当前未抽取到控件专属伪类；主题主要依赖 Avalonia 标准伪类、模板绑定和内部 StyledProperty。

## 事件与命令

ComboBox 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。
当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/Navigation/ComboBox/Views/ComboBoxShowCase.axaml:37`

Gallery key：`ExamplesContent` / item `0`

```axaml
<atom:ComboBox PlaceholderText="请选择" Width="300">
    <atom:ComboBoxItem Content="床前明月光" />
    <atom:ComboBoxItem Content="疑是地上霜" />
    <atom:ComboBoxItem Content="举头望明月" />
    <atom:ComboBoxItem Content="低头思故乡" />
    <atom:ComboBoxItem Content="床前明月光" />
    <atom:ComboBoxItem Content="疑是地上霜" />
    <atom:ComboBoxItem Content="举头望明月" />
    <atom:ComboBoxItem Content="低头思故乡" />
    <atom:ComboBoxItem Content="床前明月光" />
    <atom:ComboBoxItem Content="疑是地上霜" />
    <atom:ComboBoxItem Content="举头望明月" />
    <atom:ComboBoxItem Content="低头思故乡" />
    <atom:ComboBoxItem Content="床前明月光" />
    <atom:ComboBoxItem Content="疑是地上霜" />
    <atom:ComboBoxItem Content="举头望明月" />
    <atom:ComboBoxItem Content="低头思故乡" />
    <atom:ComboBoxItem Content="床前明月光" />
    <atom:ComboBoxItem Content="疑是地上霜" />
    <atom:ComboBoxItem Content="举头望明月" />
    <atom:ComboBoxItem Content="低头思故乡" />
    <atom:ComboBoxItem Content="床前明月光" />
    <atom:ComboBoxItem Content="疑是地上霜" />
    <atom:ComboBoxItem Content="举头望明月" />
    <atom:ComboBoxItem Content="低头思故乡" />
    <atom:ComboBoxItem Content="床前明月光" />
    <atom:ComboBoxItem Content="疑是地上霜" />
    <atom:ComboBoxItem Content="举头望明月" />
    <atom:ComboBoxItem Content="低头思故乡" />
    <atom:ComboBoxItem Content="床前明月光" />
    <atom:ComboBoxItem Content="疑是地上霜" />
    <atom:ComboBoxItem Content="举头望明月" />
    <atom:ComboBoxItem Content="低头思故乡" />
    <atom:ComboBoxItem Content="床前明月光" />
    <atom:ComboBoxItem Content="疑是地上霜" />
    <atom:ComboBoxItem Content="举头望明月" />
    <atom:ComboBoxItem Content="低头思故乡" />
    <atom:ComboBoxItem Content="床前明月光" />
    <atom:ComboBoxItem Content="疑是地上霜" />
    <atom:ComboBoxItem Content="举头望明月" />
    <atom:ComboBoxItem Content="低头思故乡" />
</atom:ComboBox>
```

### 通过 ItemsSource 生成 ComboBoxItem

来源：`controlgallery/AtomUIGallery/ShowCases/Navigation/ComboBox/Views/ComboBoxShowCase.axaml:89`

Gallery key：`ExamplesContent` / item `1`

```axaml
<atom:ComboBox Name="TplComboBox"
               PlaceholderText="请选择" Width="300"
               ItemsSource="{Binding ComboBoxItems}">
    <atom:ComboBox.ItemTemplate>
        <DataTemplate>
            <atom:TextBlock Text="{Binding Text}" VerticalAlignment="Center"/>
        </DataTemplate>
    </atom:ComboBox.ItemTemplate>
</atom:ComboBox>
```

### SelectedItem 绑定

来源：`controlgallery/AtomUIGallery/ShowCases/Navigation/ComboBox/Views/ComboBoxShowCase.axaml:109`

Gallery key：`ExamplesContent` / item `2`

```axaml
<StackPanel Spacing="8" MinWidth="320">
    <TextBlock Text="SelectedItem"
               FontWeight="SemiBold" />
    <atom:ComboBox PlaceholderText="请选择"
                   Width="300"
                   IsAllowClear="True"
                   ItemsSource="{Binding ComboBoxItems}"
                   SelectedItem="{Binding BoundSelectedItem}">
        <atom:ComboBox.ItemTemplate>
            <DataTemplate>
                <atom:TextBlock Text="{Binding Text}" VerticalAlignment="Center" />
            </DataTemplate>
        </atom:ComboBox.ItemTemplate>
    </atom:ComboBox>
    <WrapPanel ItemSpacing="8">
        <atom:Button SizeType="Small"
                     Command="{Binding SetBoundSelectedItemCommand}"
                     Content="设为第三句" />
        <atom:Button SizeType="Small"
                     Command="{Binding ClearBoundSelectedItemCommand}"
                     Content="清空" />
    </WrapPanel>
    <TextBlock Text="ViewModel 值" />
    <TextBlock Text="{Binding BoundSelectedItemText}" />
</StackPanel>
```

### 可编辑过滤

来源：`controlgallery/AtomUIGallery/ShowCases/Navigation/ComboBox/Views/ComboBoxShowCase.axaml:144`

Gallery key：`ExamplesContent` / item `3`

```axaml
<atom:ComboBox PlaceholderText="输入内容过滤"
               Width="300"
               IsEditable="True"
               IsFilterEnabled="True">
    <atom:ComboBoxItem Content="Alpha" />
    <atom:ComboBoxItem Content="Alpine" />
    <atom:ComboBoxItem Content="Beta" />
    <atom:ComboBoxItem Content="Gamma" />
    <atom:ComboBoxItem Content="Delta" />
</atom:ComboBox>
```

## 状态模型

ComboBox 的状态流按以下路径收敛：

```text
Public API / inherited command / item source / user input
  -> 控件实例状态
  -> effective state / pseudo-class / template property
  -> ControlTheme selector / presenter / renderer
  -> Gallery 可观察行为
```

状态维护规则：

- Disabled 或不可交互状态优先屏蔽 pointer、keyboard、motion 和提交类反馈。
- `DataValidationErrors` 是 native error 唯一真源；`FormStatus` 承载 Form 的 warning、success、validating 和 error 投影，`Status` 只表示用户显式请求。输入表面通过 `InputControlState.ResolveEffectiveStatus` 计算有效状态，native/Form error 优先于 Form warning，再优先于显式 warning/error。
- open/close、collection/filter、input/value、motion、visual option 状态由控件实例或明确的数据 owner 推导，不能在 template part 之间双向竞争。
- 单选结果以继承的 `SelectedItem` / `SelectedIndex` 为准；AtomUI Form 集成使用 `SelectedItem` 作为 ComboBox 的默认表单值，`SetFormValue`、`GetFormValue` 和 `ClearFormValue` 不应转换为字符串或读取展示文本。
- 下拉项的 active candidate 与 `SelectedItem` / `SelectedIndex` 分离。鼠标移动和键盘 `Up` / `Down` 共享同一个 active candidate；迁移只改变候选视觉，`Enter` 才提交 `SelectedItem`。`:pointerover` 不得形成第二个候选高亮，selected 视觉优先于 active 视觉。
- 模板重套用时必须把 public API 对应状态回放到新的 part、伪类和主题变量。
- 集合、弹层、异步、动效或窗口相关状态必须能处理 reset、close、cancel、detach 和 owner 释放。

## 主题与 Design Token

ComboBox 的视觉模型由控件模板、ControlTheme、SharedToken 和必要的控件 Token 共同构成。

| 主题文件 | 职责 |
| --- | --- |
| `ComboBoxHandleTheme.axaml` | 定义局部操作入口、按钮或 handle 的状态视觉。 |
| `ComboBoxItemTheme.axaml` | 定义集合项、容器项或局部单元的状态视觉。 |
| `ComboBoxTheme.axaml` | 定义布局、内容承载或框架节点视觉。 |

非编辑态选中内容的完整文本提示复用共享 `OverflowTip` attached behavior。模板只在 `SelectedContentPresenter` 上接入 `IsShowOverflowTip`、`OverflowTipDelay`、`OverflowTipPlacement` 和 `SelectionBoxItem`；`IsEditable=true` 时编辑输入框不默认启用该提示。

ComboBox 使用 `ComboBoxToken` 作为控件 Token scope。Token 只表达组件视觉语义，不承载 open/close、collection/filter、input/value、motion、visual option 运行时状态。

主题维护规则：

- 不删除或重命名已经稳定的 ControlTheme key、template part、伪类和资源 key。
- 不把可由 AXAML 表达的模板状态迁移为 C# 动态创建视觉。
- 不把 hover、pressed、selected、expanded、loading、filter、popup open 等运行时状态写入 Token。
- Browser 或平台特化主题必须保持同一 API 的语义一致。

Token 来源：

ComboBox Token 只表达组件级视觉变量，例如尺寸、间距、颜色、圆角、阴影、图标尺寸和弹层边界。Token 不承载运行时选择、展开、加载、错误、上传任务、过滤条件或业务状态。

当前 Token scope：

- `ComboBoxToken`，scope id 为 `ComboBox`，源码位于 `src/AtomUI.Desktop.Controls/ComboBox/ComboBoxToken.cs`。

## AOT 与裁剪注意事项

资源和 AOT 约束：

- 不通过运行时反射扫描 public API、Token 或 Gallery 示例数据。
- 不把可静态声明的模板结构迁移到 C# 动态创建。
- 异步加载、上传、弹层和窗口生命周期必须能取消或释放。
- 缓存对象必须与控件、窗口、弹层或数据 owner 生命周期一致。
- Source generator 生成文件不手工编辑；需要修改时改输入源或 generator。

性能边界：

- 控件应优先复用 Avalonia 原生虚拟化、模板绑定和资源系统。
- 避免为每次状态变化创建不必要的视觉对象、订阅或动画对象。
- 大集合控件必须保证 container recycle 后不会泄漏旧 item 状态。

## 源码索引

主要源码文件：

- `src/AtomUI.Desktop.Controls/ComboBox/ComboBox.cs`
- `src/AtomUI.Desktop.Controls/ComboBox/ComboBoxHandle.cs`
- `src/AtomUI.Desktop.Controls/ComboBox/ComboBoxItem.cs`
- `src/AtomUI.Desktop.Controls/ComboBox/ComboBoxReflectionExtensions.cs`
- `src/AtomUI.Desktop.Controls/ComboBox/ComboBoxToken.cs`
- `src/AtomUI.Desktop.Controls/Tooltip/OverflowTip.cs`
- `src/AtomUI.Desktop.Controls/ComboBox/Themes/ComboBoxHandleTheme.axaml`
- `src/AtomUI.Desktop.Controls/ComboBox/Themes/ComboBoxItemTheme.axaml`
- `src/AtomUI.Desktop.Controls/ComboBox/Themes/ComboBoxTheme.axaml`

职责边界：

- 控件主文件保留 public/protected API、Avalonia 属性注册、事件和主要生命周期入口。
- Theme 文件负责静态视觉结构、template part、selector 和资源绑定。
- Token 文件只提供组件视觉变量，不保存实例状态。
- Gallery 文件只展示用法和示例，不作为运行时逻辑 owner。

## 相关文档

- 源设计文档：`docs/controls/desktop/navigation/combo-box/overview.md`
- 实现文档：`docs/controls/desktop/navigation/combo-box/implementation.md`
- Token 文档：`docs/controls/desktop/navigation/combo-box/token.md`
- 变更记录：`docs/controls/desktop/navigation/combo-box/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/dropdown-button/index-cn.md

# DropdownButton

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

DropdownButton 是 AtomUI 桌面控件体系中的下拉按钮，用于从一个动作按钮展开次级命令或菜单内容。

DropdownButton 不负责拆分主次命令、完整 Menu 导航或上下文菜单。这些职责应由业务层、组合控件或更专用的 AtomUI 控件承担。

主要源码入口：

- `src/AtomUI.Desktop.Controls/Buttons`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/Navigation/DropdownButton` |
| 状态 | Stable |

## 何时使用

DropdownButton 的设计语言围绕控件职责、可观察状态和主题契约组织，而不是围绕模板节点组织。

| 维度 | 含义 | DropdownButton 中的表达 |
| --- | --- | --- |
| 产品语义 | 控件在界面中承担的稳定职责。 | DropdownButton 是 AtomUI 桌面控件体系中的下拉按钮，用于从一个动作按钮展开次级命令或菜单内容。 |
| 内容承载 | 用户数据、展示内容、集合项或操作入口如何进入控件。 | 继承 Button 的 `Content`、`Icon`、`Command` 等动作入口。 |
| 状态反馈 | public API、内部状态和伪类如何形成用户可感知反馈。 | open/close、visual option。 |
| 主题语义 | ControlTheme、SharedToken、控件 Token 和模板绑定如何表达视觉。 | SharedToken / 关联控件 Token + ControlTheme。 |

## 公共 API

DropdownButton 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。

核心 public surface 按语义分组维护：

| 契约组 | 代表成员 | 维护含义 |
| --- | --- | --- |
| 内容与数据 | `Content`、`Icon`、`Command` | 继承 Button 的展示内容、图标和动作命令入口。 |
| 交互与状态 | `IsArrowVisible`、`IsPointAtCenter`、`IsShowOpenIndicator`、`ShouldUseOverlayPopup` | 表达用户可观察状态、可用性、清除、加载或反馈语义。 |
| 视觉与布局 | `IconWidth`、`IconHeight`、`MarginToAnchor`、`Placement`、`PlacementAnchor`、`PlacementGravity` | 继承 Button 的用户图标和 loading 图标尺寸入口，并管理下拉定位、密度和模板视觉变量。 |
| 弹层与窗口 | `DropdownFlyout` | 控制 popup、flyout、dialog、window 或 overlay 宿主协作。 |
| 动效与异步 | `MouseEnterDelay`、`MouseLeaveDelay` | 约束动效开关、异步加载、播放速度、超时和任务边界。 |
| 其他稳定入口 | `OpenIndicator`、`TriggerType` | 保留为 public surface，变更前需确认 Gallery 和用户 XAML 依赖。 |

稳定事件包括 `MenuItemClicked`。事件触发顺序属于兼容契约，不能因内部状态重排而改变。

主要公开类型与枚举：

- 类型：`DropdownButton`、`Button`、`MenuFlyout`、`FlyoutStateHelper`。
- 枚举：无。

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_ButtonIcon` | `?` | 承载用户触发入口、导航或关闭动作。 |
| `PART_LoadingIcon` | `Icon` | 展示继承自 Button loading 状态的图标。 |
| `PART_ContentPresenter` | `?` | 展示用户内容、文本、图标或模板化数据。 |
| `PART_DropdownIndicator` | `?` | 展示指示器、进度、分页或状态反馈。 |
| `PART_RootLayout` | `?` | 承载根视觉、边框、背景或尺寸基线。 |
| `PART_WaveSpirit` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |

当前未抽取到控件专属伪类；主题主要依赖 Avalonia 标准伪类、模板绑定和内部 StyledProperty。

DropdownButton 继承 Button 的 `IconWidthProperty`、`IconHeightProperty` 及 CLR wrapper。`PART_ButtonIcon` 与 `PART_LoadingIcon` 通过 `TemplateBinding` 消费这两个属性，保持与 Button 相同的 SizeType 默认值、本地值优先级和 icon-only loading 状态语义。`PART_DropdownIndicator` 展示的 `OpenIndicator` 是独立图标角色，不受 `IconWidth`、`IconHeight` 控制。

## 事件与命令

DropdownButton 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。
| 内容与数据 | `Content`、`Icon`、`Command` | 继承 Button 的展示内容、图标和动作命令入口。 |
稳定事件包括 `MenuItemClicked`。事件触发顺序属于兼容契约，不能因内部状态重排而改变。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

- `controlgallery/AtomUIGallery/ShowCases/Navigation/DropdownButton/Views/DropdownButtonShowCase.axaml`

## 状态模型

DropdownButton 的状态流按以下路径收敛：

```text
Public API / inherited command / item source / user input
  -> 控件实例状态
  -> effective state / pseudo-class / template property
  -> ControlTheme selector / presenter / renderer
  -> Gallery 可观察行为
```

状态维护规则：

- Disabled 或不可交互状态优先屏蔽 pointer、keyboard、motion 和提交类反馈。
- open/close、visual option 状态由控件实例或明确的数据 owner 推导，不能在 template part 之间双向竞争。
- 模板重套用时必须把 public API 对应状态回放到新的 part、伪类和主题变量。
- 集合、弹层、异步、动效或窗口相关状态必须能处理 reset、close、cancel、detach 和 owner 释放。

## 主题与 Design Token

DropdownButton 的视觉模型由控件模板、ControlTheme、SharedToken 和必要的控件 Token 共同构成。

| 主题文件 | 职责 |
| --- | --- |
| `DropdownButtonBaseTheme.axaml` | 定义 DropdownButton 与 Button 共享的模板、图标尺寸和基础状态视觉。 |
| `DropdownButtonTheme.axaml` | 定义 DropdownButton 的下拉指示器和具体视觉入口。 |

DropdownButton 当前没有专属 Token 文档；主题通过 SharedToken、关联控件 Token 或继承主题资源表达视觉语义。运行时状态不得写入 Token 模型。

主题维护规则：

- 不删除或重命名已经稳定的 ControlTheme key、template part、伪类和资源 key。
- 不把可由 AXAML 表达的模板状态迁移为 C# 动态创建视觉。
- 不把 hover、pressed、selected、expanded、loading、filter、popup open 等运行时状态写入 Token。
- Native 和 Browser 支持宿主必须使用同一套 DropdownButton 主题资产；平台差异不能通过 Browser 专用主题分叉复制视觉。
- 用户 icon 与 loading icon 的尺寸只能从 DropdownButton 自身的 `IconWidth`、`IconHeight` 投影；应用和 Gallery 不得通过 `/template/` 或 `PART_ButtonIcon`、`PART_LoadingIcon` selector 修改内部尺寸。
- `OpenIndicator` 尺寸由 DropdownButton 主题单独管理，不复用用户 icon 的宽高属性。

Token 来源：

- DropdownButton 当前没有专属 `token.md`；LLMS 生成按第 5 节视觉与主题模型、SharedToken、控件家族 Token 或主题资源说明 Token 边界。

## AOT 与裁剪注意事项

资源和 AOT 约束：

- 不通过运行时反射扫描 public API、Token 或 Gallery 示例数据。
- 不把可静态声明的模板结构迁移到 C# 动态创建。
- 异步加载、上传、弹层和窗口生命周期必须能取消或释放。
- 缓存对象必须与控件、窗口、弹层或数据 owner 生命周期一致。
- Source generator 生成文件不手工编辑；需要修改时改输入源或 generator。

性能边界：

- 控件应优先复用 Avalonia 原生虚拟化、模板绑定和资源系统。
- 图标尺寸使用 owner StyledProperty、Style 默认值与 `TemplateBinding` 单向投影，不为模板 part 建立运行时订阅或查找同步。
- 避免为每次状态变化创建不必要的视觉对象、订阅或动画对象。
- 大集合控件必须保证 container recycle 后不会泄漏旧 item 状态。

## 源码索引

主要源码文件：

- `src/AtomUI.Desktop.Controls/DropdownButton/DropdownButton.cs`
- `src/AtomUI.Desktop.Controls/Buttons/Themes/DropdownButtonBaseTheme.axaml`
- `src/AtomUI.Desktop.Controls/Buttons/Themes/DropdownButtonTheme.axaml`

职责边界：

- 控件主文件保留 public/protected API、Avalonia 属性注册、事件和主要生命周期入口。
- Theme 文件负责静态视觉结构、template part、selector 和资源绑定。
- Token 文件只提供组件视觉变量，不保存实例状态。
- Gallery 文件只展示用法和示例，不作为运行时逻辑 owner。

## 相关文档

- 源设计文档：`docs/controls/desktop/navigation/dropdown-button/overview.md`
- 实现文档：`docs/controls/desktop/navigation/dropdown-button/implementation.md`
- 变更记录：`docs/controls/desktop/navigation/dropdown-button/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/menu/index-cn.md

# Menu

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

Menu 是 AtomUI 桌面控件体系中的菜单控件家族，用于组织命令列表、上下文操作、MenuFlyout 和数据驱动菜单项。

Menu 不负责页面级导航树、树形选择或任意弹层宿主。这些职责应由业务层、组合控件或更专用的 AtomUI 控件承担。

主要源码入口：

- `src/AtomUI.Desktop.Controls/Menu`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/Navigation/Menu` |
| 状态 | Stable |

## 何时使用

Menu 的设计语言围绕控件职责、可观察状态和主题契约组织，而不是围绕模板节点组织。

| 维度 | 含义 | Menu 中的表达 |
| --- | --- | --- |
| 产品语义 | 控件在界面中承担的稳定职责。 | Menu 是 AtomUI 桌面控件体系中的菜单控件家族，用于组织命令列表、上下文操作、MenuFlyout 和数据驱动菜单项。 |
| 内容承载 | 用户数据、展示内容、集合项或操作入口如何进入控件。 | `Items`、`MenuItem`、`MenuItemData` 和 `MenuSeparatorData`。 |
| 状态反馈 | public API、内部状态和伪类如何形成用户可感知反馈。 | open/close、collection/filter、motion、visual option。 |
| 主题语义 | ControlTheme、SharedToken、控件 Token 和模板绑定如何表达视觉。 | Menu Token + ControlTheme。 |

## 公共 API

Menu 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。

核心 public surface 按语义分组维护：

| 契约组 | 代表成员 | 维护含义 |
| --- | --- | --- |
| 内容与数据 | `Items`、`MenuItem`、`MenuItemData`、`MenuSeparatorData` | 定义菜单项集合、数据驱动菜单项和分割项入口。 |
| 选择与集合 | `DisplayPageSize`、`IsScrollEnabled` | 维护弹层显示页数上限、滚动开关、选择、展开和集合状态。 |
| 交互与状态 | `IsMotionEnabled`、`ShouldUseOverlayPopup` | 表达用户可观察状态、可用性、清除、加载或反馈语义。 |
| 视觉与布局 | `LineWidth`、`Orientation`、`OverlayHostShadow`、`PopupRootShadow`、`SizeType` | 影响尺寸、位置、颜色、形状、密度和模板视觉变量。 |
| 动效与异步 | `CloseMotion`、`MotionDuration`、`OpenMotion` | 约束动效开关、异步加载、播放速度、超时和任务边界。 |

稳定事件包括 `IsCheckStateChanged`。事件触发顺序属于兼容契约，不能因内部状态重排而改变。

主要公开类型与枚举：

- 类型：`ContextMenu`、`DefaultMenuInteractionHandler`、`FlyoutMenuItemClickedEventArgs`、`Menu`、`MenuFlyout`、`MenuFlyoutPresenter`、`MenuItem`、`MenuItemData`、`MenuSeparator`、`MenuSeparatorData`、`ToggleItemsLayoutVisibleConverter`。
- 枚举：无。

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_ItemsPresenter` | `?` | 展示用户内容、文本、图标或模板化数据。 |
| `PART_Popup` | `?` | 承载弹层宿主、打开关闭或候选内容。 |
| `PART_ToggleCheckbox` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_ToggleRadio` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |

控件专属或内部伪类包括 `MenuItemPseudoClass.TopLevel`、`TopLevel=:toplevel`。这些伪类属于主题 selector 可观察契约，不能在未同步主题和 Gallery 的情况下重命名或删除。

## 事件与命令

Menu 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。
稳定事件包括 `IsCheckStateChanged`。事件触发顺序属于兼容契约，不能因内部状态重排而改变。
- 类型：`ContextMenu`、`DefaultMenuInteractionHandler`、`FlyoutMenuItemClickedEventArgs`、`Menu`、`MenuFlyout`、`MenuFlyoutPresenter`、`MenuItem`、`MenuItemData`、`MenuSeparator`、`MenuSeparatorData`、`ToggleItemsLayoutVisibleConverter`。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/Navigation/Menu/Views/MenuShowCase.axaml:37`

Gallery key：`ExamplesContent` / item `0`

```axaml
<atom:Menu>
    <atom:MenuItem Header="_文件">
        <atom:MenuItem Header="新建文本文件" InputGesture="Ctrl+N" />
        <atom:MenuItem Header="新建文件" InputGesture="Ctrl+Alt+N" />
        <atom:MenuItem Header="新建窗口" InputGesture="Ctrl+Shift+N" />
    </atom:MenuItem>
    <atom:MenuItem Header="_编辑">
        <atom:MenuItem Header="撤销" InputGesture="Ctrl+Shift+Z" />
        <atom:MenuSeparator />
        <atom:MenuItem Header="剪切" InputGesture="Ctrl+X" />
    </atom:MenuItem>
    <atom:MenuItem Header="禁用项" IsEnabled="False" />
</atom:Menu>
```

### 可滚动菜单

来源：`controlgallery/AtomUIGallery/ShowCases/Navigation/Menu/Views/MenuShowCase.axaml:122`

Gallery key：`ExamplesContent` / item `3`

```axaml
<StackPanel Spacing="12">
    <StackPanel Orientation="Horizontal" Spacing="8">
        <atom:ToggleSwitch IsChecked="{Binding IsPopupScrollEnabled}" />
        <atom:TextBlock VerticalAlignment="Center"
                        Text="开启弹层滚动" />
    </StackPanel>
    <atom:Menu IsScrollEnabled="{Binding IsPopupScrollEnabled}">
        <atom:MenuItem Header="_菜单">
            <atom:MenuItem Header="菜单项" />
            <atom:MenuItem Header="菜单项" />
            <atom:MenuItem Header="菜单项" />
            <atom:MenuItem Header="菜单项" />
            <atom:MenuItem Header="菜单项" />
            <atom:MenuItem Header="菜单项" />
            <atom:MenuItem Header="菜单项" />
            <atom:MenuItem Header="菜单项" />
            <atom:MenuItem Header="菜单项" />
            <atom:MenuItem Header="菜单项" />
            <atom:MenuItem Header="菜单项" />
            <atom:MenuItem Header="菜单项" />
        </atom:MenuItem>
    </atom:Menu>
</StackPanel>
```

### 通过 ItemsSource 生成 MenuItem

来源：`controlgallery/AtomUIGallery/ShowCases/Navigation/Menu/Views/MenuShowCase.axaml:155`

Gallery key：`ExamplesContent` / item `4`

```axaml
<atom:Menu Name="BasicItemsSourceMenu"
           ItemsSource="{Binding MenuItems}">
    <atom:Menu.ItemTemplate>
        <TreeDataTemplate ItemsSource="{Binding Children}"
                          x:DataType="atom:IMenuItemData">
            <atom:TextBlock Text="{Binding Header}" />
        </TreeDataTemplate>
    </atom:Menu.ItemTemplate>
</atom:Menu>
```

### 通过 ItemsSource 生成内联 NavMenu

来源：`controlgallery/AtomUIGallery/ShowCases/Navigation/Menu/Views/MenuShowCase.axaml:174`

Gallery key：`ExamplesContent` / item `5`

```axaml
<atom:NavMenu Mode="Inline" Name="InlineModeMenu"
              ItemsSource="{Binding InlineNavMenuNodes}">
    <atom:NavMenu.ItemTemplate>
        <TreeDataTemplate ItemsSource="{Binding Children}"
                          x:DataType="atom:INavMenuNode">
            <atom:TextBlock Text="{Binding Header}" />
        </TreeDataTemplate>
    </atom:NavMenu.ItemTemplate>
</atom:NavMenu>
```

## 状态模型

Menu 的状态流按以下路径收敛：

```text
Public API / inherited command / item source / user input
  -> 控件实例状态
  -> effective state / pseudo-class / template property
  -> ControlTheme selector / presenter / renderer
  -> Gallery 可观察行为
```

状态维护规则：

- Disabled 或不可交互状态优先屏蔽 pointer、keyboard、motion 和提交类反馈。
- open/close、collection/filter、motion、visual option 状态由控件实例或明确的数据 owner 推导，不能在 template part 之间双向竞争。
- `IsScrollEnabled` 控制弹层内容是否创建 `ScrollViewer`。滚动开启时 `DisplayPageSize` 参与最大高度计算；滚动禁用时弹层直接显示全部菜单项，不使用 `DisplayPageSize` 限高。
- 模板重套用时必须把 public API 对应状态回放到新的 part、伪类和主题变量。
- 集合、弹层、异步、动效或窗口相关状态必须能处理 reset、close、cancel、detach 和 owner 释放。

子菜单的 pointer 交互采用独立的 hover intent 模型：

- `SelectedItem` 表达菜单导航和当前项状态，`IsSubMenuOpen` 表达已提交的弹层状态；二者都不能作为延迟任务是否仍然有效的唯一依据。
- pointer 进入带子菜单的非顶层项时，只为当前目标建立延迟打开意图。pointer 在延迟完成前离开该项时，打开意图立即失效，子菜单不得在离开后继续弹出。
- 已打开子菜单的关闭延迟只用于允许 pointer 从父项移动到其弹层。pointer 重新进入父项、子菜单弹层或其后代项时，待执行的关闭意图必须失效。
- 同一目标不能同时持有互相矛盾的打开和关闭意图。不同兄弟项切换时可以同时存在“关闭旧项”和“打开新项”，但每类意图最多只有一个当前目标。
- keyboard、access key 和 pointer press 触发的显式打开不经过 hover 延迟，不得被旧 hover callback 覆盖或回滚。
- 菜单关闭、窗口失活、宿主解除连接或交互处理器 detach 时，所有未完成 hover intent 必须统一失效。

## 主题与 Design Token

Menu 的视觉模型由控件模板、ControlTheme、SharedToken 和必要的控件 Token 共同构成。

| 主题文件 | 职责 |
| --- | --- |
| `ContextMenuTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `MenuItemTheme.axaml` | 定义集合项、容器项或局部单元的状态视觉。 |
| `MenuSeparatorTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `MenuTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `TopLevelMenuItemTheme.axaml` | 定义集合项、容器项或局部单元的状态视觉。 |
| `src/AtomUI.Desktop.Controls/Flyouts/Themes/MenuFlyoutPresenterTheme.axaml` | 定义 `MenuFlyout` 菜单项 presenter 的弹层内容模板和滚动承载结构。 |

Menu 使用 `MenuToken` 作为控件 Token scope。Token 只表达组件视觉语义，不承载 open/close、collection/filter、motion、visual option 运行时状态。

主题维护规则：

- 不删除或重命名已经稳定的 ControlTheme key、template part、伪类和资源 key。
- 不把可由 AXAML 表达的模板状态迁移为 C# 动态创建视觉。
- 弹层滚动开关通过内部 `MenuPopupScrollHost` 复用模板分支；禁用滚动时不能保留隐藏或禁用状态的 `ScrollViewer`。
- 不把 hover、pressed、selected、expanded、loading、filter、popup open 等运行时状态写入 Token。
- Browser 或平台特化主题必须保持同一 API 的语义一致。

Token 来源：

Menu Token 只表达组件级视觉变量，例如尺寸、间距、颜色、圆角、阴影、图标尺寸和弹层边界。Token 不承载运行时选择、展开、加载、错误、上传任务、过滤条件或业务状态。

当前 Token scope：

- `MenuToken`，scope id 为 `Menu`，源码位于 `src/AtomUI.Desktop.Controls/Menu/MenuToken.cs`。

## AOT 与裁剪注意事项

资源和 AOT 约束：

- 不通过运行时反射扫描 public API、Token 或 Gallery 示例数据。
- 不把可静态声明的模板结构迁移到 C# 动态创建。
- 异步加载、上传、弹层和窗口生命周期必须能取消或释放。
- 缓存对象必须与控件、窗口、弹层或数据 owner 生命周期一致。
- Source generator 生成文件不手工编辑；需要修改时改输入源或 generator。
- hover intent 调度只使用显式类型、委托和 `IDisposable` 生命周期，不依赖反射或运行时类型扫描。
- 滚动模式使用共享 styled property、AXAML 模板分支和 `TemplateBinding`，不引入运行时反射、字符串 binding、动态注册或生成器变更。

性能边界：

- 控件应优先复用 Avalonia 原生虚拟化、模板绑定和资源系统。
- 避免为每次状态变化创建不必要的视觉对象、订阅或动画对象。
- 默认滚动路径允许保留一个轻量 `MenuPopupScrollHost` 节点，以换取四套弹层模板的统一分支；禁用滚动路径必须移除 `ScrollViewer` 子树，但会让全部菜单项直接参与测量和显示。
- 大集合控件必须保证 container recycle 后不会泄漏旧 item 状态。
- 默认调度路径在 intent 失效时停止实际 timer；兼容的外部 delay runner 至少必须通过原子 callback clearing/release 使 callback 失效，避免过期任务修改状态并保留 owner graph。
- 交互处理器只保留当前 pending open/close 目标，不维护随 pointer 移动增长的历史队列。

## 源码索引

主要源码文件：

- `src/AtomUI.Controls.Shared/IScrollAware.cs`
- `src/AtomUI.Desktop.Controls/Flyouts/MenuFlyout.cs`
- `src/AtomUI.Desktop.Controls/Flyouts/MenuFlyoutPresenter.cs`
- `src/AtomUI.Desktop.Controls/Flyouts/Themes/MenuFlyoutPresenterTheme.axaml`
- `src/AtomUI.Desktop.Controls/Menu/ContextMenu.cs`
- `src/AtomUI.Desktop.Controls/Menu/ContextMenuReflectionExtensions.cs`
- `src/AtomUI.Desktop.Controls/Menu/Converters/ToggleItemsLayoutVisibleConverter.cs`
- `src/AtomUI.Desktop.Controls/Menu/DefaultMenuInteractionHandler.cs`
- `src/AtomUI.Desktop.Controls/Menu/Menu.cs`
- `src/AtomUI.Desktop.Controls/Menu/MenuItem.cs`
- `src/AtomUI.Desktop.Controls/Menu/MenuItemData.cs`
- `src/AtomUI.Desktop.Controls/Menu/MenuItemPseudoClass.cs`
- `src/AtomUI.Desktop.Controls/Menu/MenuPopupScrollHost.cs`
- `src/AtomUI.Desktop.Controls/Menu/MenuSeparator.cs`
- `src/AtomUI.Desktop.Controls/Menu/MenuSeparatorData.cs`
- `src/AtomUI.Desktop.Controls/Menu/MenuToken.cs`
- `src/AtomUI.Desktop.Controls/Menu/Themes/ContextMenuTheme.axaml`
- `src/AtomUI.Desktop.Controls/Menu/Themes/MenuItemTheme.axaml`
- `src/AtomUI.Desktop.Controls/Menu/Themes/MenuItemTheme.cs`
- `src/AtomUI.Desktop.Controls/Menu/Themes/MenuSeparatorTheme.axaml`
- `src/AtomUI.Desktop.Controls/Menu/Themes/MenuTheme.axaml`
- `src/AtomUI.Desktop.Controls/Menu/Themes/TopLevelMenuItemTheme.axaml`

职责边界：

- 控件主文件保留 public/protected API、Avalonia 属性注册、事件和主要生命周期入口。
- Theme 文件负责静态视觉结构、template part、selector 和资源绑定。
- Token 文件只提供组件视觉变量，不保存实例状态。
- Gallery 文件只展示用法和示例，不作为运行时逻辑 owner。

## 相关文档

- 源设计文档：`docs/controls/desktop/navigation/menu/overview.md`
- 实现文档：`docs/controls/desktop/navigation/menu/implementation.md`
- Token 文档：`docs/controls/desktop/navigation/menu/token.md`
- 变更记录：`docs/controls/desktop/navigation/menu/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/nav-menu/index-cn.md

# NavMenu

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

NavMenu 是 AtomUI 桌面导航体系中的层级菜单导航控件，用于表达应用页面、模块、功能入口或命令集合之间的层级关系。它以树形节点为数据模型，以 `Inline`、`Vertical`、`Horizontal` 三种模式映射到不同导航场景。

NavMenu 的职责是管理导航节点容器生成、层级展开、选中路径、弹出式子菜单、主题视觉和菜单交互。它不负责路由切换、页面生命周期、权限过滤、数据懒加载、业务命令编排或页面内容渲染。业务导航行为可以通过节点 `Command`、`NavMenuNodeSelected`、`NavMenuItemClick` 或外部 ViewModel 处理。

NavMenu 支持两种 entry 提供方式：

- 直接在 `NavMenu.Items` 中放置 `INavMenuEntry`。
- 通过 `ItemsSource` 绑定 `INavMenuEntry` 集合。

`INavMenuEntry` 是节点、分组和分隔线的共同结构契约。`INavMenuNode` 表达可交互导航节点，`NavMenuGroup` 表达带标题的透明结构分组，`NavMenuDivider` 表达不可交互分隔线。首版支持的 entry 种类封闭为这三类；仅自行实现 `INavMenuEntry` 不能注册第四种容器类型，扩展交互节点应实现 `INavMenuNode`。NavMenu 不支持把任意 `Control`、`Panel` 或 `TextBlock` 作为 entry 直接加入；Header、Footer 和分组标题的任意内容通过对应 content/template API 承载。

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/Navigation/Menu`；结构化 entry 示例位于 Menu ShowCase 最后一项` |
| 状态 | Stable |

## 何时使用

NavMenu 表达的是“层级入口 + 当前路径”的导航语义。用户应能通过文字、图标、缩进、选中态、悬浮态和展开状态理解当前位置、可进入的下级路径以及不同层级之间的关系。

三种模式对应不同产品语义：

| 模式 | 语义 | 典型场景 |
| --- | --- | --- |
| `Inline` | 子菜单在当前导航面板内展开，强调完整层级结构。 | 侧边栏主导航、管理后台模块导航。 |
| `Vertical` | 顶层项目垂直排列，子菜单通过 Popup 展开。 | 紧凑菜单、上下文导航、浮层式侧栏。 |
| `Horizontal` | 顶层项目水平排列，子菜单通过 Popup 展开。 | 顶部导航栏、一级模块切换。 |

Light 与 Dark 样式不是简单反色。Dark 样式有独立的根背景、Popup 背景、子菜单背景、选中背景和文字透明度规则，应保持与 参考 Menu 的层级背景语义一致。

`IsItemBackgroundEnabled` 控制菜单项背景块模型。开启时，inline 子菜单背景块、选中背景和 hover 背景形成连续层级背景；关闭时，菜单项背景块保持透明，但 header 文本颜色、选中路径颜色和交互状态仍然保留。

## 公共 API

NavMenu 的公共 API 分为控件 API、节点 API 和事件 API。

控件 API：

| API | 类型 | 语义 |
| --- | --- | --- |
| `Mode` | `NavMenuMode` | 菜单呈现模式，默认 `Inline`。 |
| `IsInlineCollapsed` | `bool` | `Mode=Inline` 时是否进入内联折叠状态，默认 `false`。折叠不改变 public `Mode`，只改变内部有效呈现和交互模式。 |
| `InlineCollapsedWidth` | `double` | 内联折叠状态下的菜单宽度。默认值来自 `NavMenuToken.InlineCollapsedWidth`，初始设计值为 `48`；开发者可通过本地值覆盖 token 默认宽度。 |
| `IsCollapsedTooltipEnabled` | `bool` | 是否为有效 inline collapsed 状态下的顶层叶子节点启用 Tooltip，默认 `true`。 |
| `CollapsedTooltipPlacement` | `PlacementMode` | 折叠 Tooltip 的放置方向，默认 `Right`。 |
| `CollapsedTooltipShowDelay` | `int` | 首个折叠 Tooltip 的显示延迟，单位毫秒，默认 `400`。 |
| `CollapsedTooltipBetweenShowDelay` | `int` | 连续折叠 Tooltip 之间允许立即切换的时间窗口，单位毫秒，默认 `100`。 |
| `SelectedItem` | `INavMenuNode?` | 当前选中节点，双向绑定入口。 |
| `DefaultSelectedPath` | `TreeNodePath?` | 初始选中路径；`SelectedItem` 非空时优先级更高。 |
| `DefaultOpenPaths` | `IList<TreeNodePath>?` | 初始展开路径集合。 |
| `IsAccordionMode` | `bool` | 顶层 inline/vertical 子菜单互斥展开策略。 |
| `IsDarkStyle` | `bool` | 使用 NavMenu dark style 视觉体系。 |
| `IsItemBackgroundEnabled` | `bool` | 控制 item / inline submenu 背景块模型，默认 `true`。 |
| `IsMotionEnabled` | `bool` | 控制 inline 展开收起和主题颜色过渡。 |
| `ShouldUseOverlayPopup` | `bool` | 控制弹出式子菜单是否使用 overlay popup。 |
| `Header` | `object?` | 根导航固定头部内容；Inline/Vertical 位于滚动菜单区上方，Horizontal 位于菜单项左侧。 |
| `HeaderTemplate` | `IDataTemplate?` | 根导航头部内容模板。 |
| `Footer` | `object?` | 根导航固定尾部内容；Inline/Vertical 位于滚动菜单区下方，Horizontal 位于菜单项右侧。 |
| `FooterTemplate` | `IDataTemplate?` | 根导航尾部内容模板。 |
| `ItemSpacing` | `double` | 默认 ItemsPanel 中相邻 entry 容器的额外间距；默认值来自当前 mode 的主题映射。 |
| `Close()` | method | 关闭所有子菜单并清空 `SelectedItem`。 |

节点 API：

| API | 类型 | 语义 |
| --- | --- | --- |
| `Header` | `object?` | 菜单项显示内容。 |
| `HeaderTemplate` | `IDataTemplate?` | 菜单项 header 模板。 |
| `Tooltip` | `object?` | 折叠叶子节点的独立 Tooltip 内容；为 `null` 时回退到节点 `Header`。传入 `ToolTip` 实例可获得完整定制能力：实例上显式设置的呈现类附加属性（位置、颜色、箭头、文本换行等）优先于菜单级与宿主配置，未设置的回落，详见 [Tooltip 桌面版架构设计](../../data-display/tooltip/overview.md) 的 Tip 实例定制模型。 |
| `IsTooltipEnabled` | `bool` | 是否允许当前节点显示折叠 Tooltip，默认 `true`。 |
| `ItemKey` | `EntityKey?` | 路径和业务标识。 |
| `Icon` | `PathIcon?` | 菜单项图标。 |
| `IsEnabled` | `bool` | 节点可用状态，默认 `true`。 |
| `Command` | `ICommand?` | 节点被有效触发时由当前 `NavMenuItem` 容器执行的业务命令。节点只承载命令配置，不负责订阅或执行。 |
| `CommandParameter` | `object?` | 传递给 `Command` 的参数。默认 `null`，不隐式回退到 `ItemKey` 或节点自身。 |
| `INavMenuNode.Entries` | `IEnumerable<INavMenuEntry>` | 容器读取子 entry 的协变契约。接口默认返回既有 `Children`，自定义节点无需新增实现。 |
| `NavMenuNode.Entries` | `IList<INavMenuEntry>` | 内置节点的唯一有序、可写子 entry 集合，也是 XAML content 属性；允许节点、任意层级分组和分隔线。 |
| `Children` | `IList<INavMenuNode>` | `Entries` 的实时语义节点兼容视图，不保存第二份集合；分组对该视图透明。 |

结构 entry API：

| API | 类型 | 语义 |
| --- | --- | --- |
| `INavMenuEntry` | interface | NavMenu 有序 entry 的共同标记契约，不带选择、命令或路径语义；它不是自定义容器注册点。 |
| `NavMenuGroup.Header` | `object?` | 分组标题内容。 |
| `NavMenuGroup.HeaderTemplate` | `IDataTemplate?` | 分组标题模板。 |
| `NavMenuGroup.Entries` | `IList<INavMenuEntry>` | 分组内有序 entry 集合，允许继续嵌套分组。 |
| `NavMenuDivider` | class | 不可聚焦、不可选中、不进入路径或键盘漫游的结构分隔线。 |

事件 API：

| 事件 | 参数 | 语义 |
| --- | --- | --- |
| `NavMenuItemClick` | `NavMenuItemClickEventArgs` | 菜单项点击事件，事件参数暴露 `INavMenuItem` 作为只读交互上下文。 |
| `NavMenuNodeSelected` | `NavMenuNodeSelectedEventArgs` | 叶子节点选中事件，事件参数暴露 `INavMenuNode`。 |

`DefaultSelectedPath` 和 `DefaultOpenPaths` 是默认值入口，不是持续受控展开状态。运行期受控选择应使用 `SelectedItem`。

`NavMenuNode.Entries` 是内置节点子结构的唯一真源。`Children` 保持既有类型和节点语义：枚举时递归穿过同一语义层级内的 `NavMenuGroup`，但不进入子节点自身的后代；`Add` 把节点追加为直接 entry，`Insert`、替换和删除写回节点当前所在的实际 entry owner，`Clear` 清空当前节点的整个 `Entries`。`INavMenuNode.Entries` 使用 `IEnumerable<INavMenuEntry>`，利用 `IEnumerable<T>` 协变在接口默认实现中直接返回既有 `IEnumerable<INavMenuNode> Children`，不创建适配集合；`NavMenuNode` 通过显式接口实现把其可写 `Entries` 投影为该读取契约。因此既有纯节点自定义实现保持源码兼容，并继续由原 `Children` 集合通知驱动容器更新。

根 `NavMenu.Items`、根 `ItemsSource`、`NavMenuNode.Entries` 和 `NavMenuGroup.Entries` 使用同一 entry 类型边界：集合中的每一项必须是 `INavMenuNode`、`NavMenuGroup` 或 `NavMenuDivider`；`null`、普通业务对象、内部生成容器和仅实现 marker 的其他类型都不是合法数据项。直接 `Items` 变更与 `ItemsSource` 的初始装载、替换以及 Add、Replace、Reset 通知必须进入同一验证入口；发现非法项时立即抛出包含来源和索引信息的 `InvalidOperationException`，不能静默跳过、按普通内容呈现或等到容器绑定阶段再产生类型错误。

内置 `NavMenuNode` 和 `NavMenuGroup` 是有状态结构 entry，同一实例在整个 entry 树中只能拥有一个直接结构 owner，不能在同一集合重复，也不能同时挂到两个节点、分组或根 `NavMenu`。从原 owner Remove、Replace、Clear 或移除根 source 后，该实例可以重新挂载。结构 owner 使用弱引用，外部长期持有 node/group 不会反向保留已经不可达的根菜单。`NavMenuDivider` 不保存选择、展开、父级、资源或容器状态，因此同一 divider 实例允许在多个位置复用。

自定义 `INavMenuNode` 实例自身保持既有兼容契约，不强制登记内置 structural owner；但其 `Entries` / `Children` 中出现的内置 `NavMenuNode` 或 `NavMenuGroup` 仍必须参加完整引用唯一性检查。每个内置 `NavMenuNode` / `NavMenuGroup` entry owner 和根 `NavMenu` 都协调自己的结构 scope：遍历直接 entry，并递归穿过 custom node；遇到内置 node/group 后由该内置 entry 自己的协调器接管后代。custom node 下的内置后代继承最近的内置 entry owner，只有根级 custom node 的内置后代才由根菜单作为结构 owner。所有 scope 组合后覆盖完整 entry 图，因此离线构造的树也不能通过 custom wrapper 绕过唯一性，同时纯 built-in 深树不会形成祖先对后代集合的重复订阅。协调器只弱订阅当前 scope 内可通知的 custom entry source；custom node 增删内置后代时立即重新协调，在加入时拒绝指回当前 built-in owner 或任意 built-in 祖先的动态环，并在移除后释放 owner。不可通知的自定义 enumerable 按每次所属集合或可通知 custom 祖先变化时取得的当前快照校验。

`NavMenuNode.Entries` 和 `NavMenuGroup.Entries` 是控件拥有的可写集合，单项 Add、Insert、Replace 以及批量初始化先完成类型、循环、直接 owner 和批次重复校验，失败时不产生部分写入。成功写入先同步完整 structural ownership，再调用自定义节点的 parent callback 或分组语义父级投影，最后发送集合通知；因此可重入回调和观察者都不能抢占已经属于本次写入的 entry 或内置后代。批量初始化一次提交完整批次，并只发送单次 Add 通知。根 direct `Items` 与任意外部 `ItemsSource` 都通过 post-mutation 的 `ItemsView.CollectionChanged` 到达控件；根 `NavMenu` 对完整 entry 图做校验并确定性抛错，但不通过重入 Remove/Replace 回滚或篡改数据源。Move 和 Remove 仍执行正常的 owner、父级、容器和生命周期协调。

`IsInlineCollapsed` 是唯一折叠状态源。NavMenu 不增加语义相反的 `Expanded` / `IsExpanded` 属性；调用方需要正向展开状态时，通过双向 binding converter 映射 `IsInlineCollapsed`，避免两个公共状态互相写回。

节点命令遵守 Avalonia 命令语义：`NavMenuNode` / `INavMenuNode` 只保存 `Command` 和 `CommandParameter`，实际执行、`CanExecute` 评估和 `CanExecuteChanged` 生命周期由生成出的 `NavMenuItem` 容器负责。`INavMenuNode` 为两个成员提供 `null` 默认实现，使既有自定义节点实现无需声明命令也能继续工作。`CommandParameter=null` 表示显式空参数，控件不能自动替换为 `ItemKey`；需要使用业务 key 时，应显式把 `ItemKey` 绑定或赋值给 `CommandParameter`。容器按 UI 周期合并连续的 `CanExecuteChanged` 通知：持续的 `false` 仍进入 disabled，同一同步执行周期内的 `false -> true` 瞬时变化只投影最终状态，避免多个共享命令节点触发无业务意义的禁用颜色闪动。

`IsInlineCollapsed` 只对 `Mode=Inline` 生效。`Mode=Vertical` 或 `Mode=Horizontal` 时设置该属性不应改变当前模式的 popup、布局或键盘语义。`InlineCollapsedWidth` 参与布局测量，默认通过 theme setter 取得 `NavMenuToken.InlineCollapsedWidth`；collapsed 状态下由控件内部对 `Width` / `MinWidth` 做有效值 coercion，本地设置的属性值应按 Avalonia 属性优先级覆盖 token 默认值，展开后原始 `Width` 或绑定必须恢复。

键盘漫游状态属于内部交互状态，不进入公共 API。NavMenu 保持与 参考 Menu 一致的分层：`SelectedItem` 表示已提交的导航选择，`DefaultOpenPaths` / `IsSubMenuOpen` 表示展开状态，键盘当前项只表示临时 active/focus 目标。业务代码不应通过公开属性控制键盘 active 项，也不应把 active 项误认为已选择节点。

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_ItemsPresenter` | `ItemsPresenter` | 承载顶层 entry 容器。 |
| `PART_HeaderPresenter` | `ContentPresenter` | 承载根导航固定 Header；内容为空时折叠。 |
| `PART_FooterPresenter` | `ContentPresenter` | 承载根导航固定 Footer；内容为空或处于有效 inline collapsed 状态时折叠。 |
| `PART_HorizontalLine` | `PixelAlignedBorder` | Horizontal light style 下的底部分割线。 |
| `PART_Header` | `BaseNavMenuItemHeader` 的 mode 专用实现 | 菜单项 header，承载文字、图标、箭头和交互视觉。 |
| `PART_Popup` | `Popup` | `Vertical` / `Horizontal` 模式下的子菜单浮层。 |
| `PART_PopupFrame` | `NavMenuPopupFrame` | Popup 背景、圆角、尺寸约束和内容边距。 |
| `PART_ChildItemsLayoutTransform` | `LayoutAwareMotionActor` | `Inline` 模式下的子菜单展开收起 motion 容器。 |
| `PART_ChildItemsFrame` | `Border` | `Inline` 模式下的子菜单背景块。 |
| `ChildItemsPresenter` | `ItemsPresenter` | `Inline` 模式下的子菜单内容承载。 |
| `PART_ActiveIndicator` | `Rectangle` | `Horizontal` 顶层 light style 下的活动指示条。 |

## 事件与命令

NavMenu 的公共 API 分为控件 API、节点 API 和事件 API。
| `Command` | `ICommand?` | 节点被有效触发时由当前 `NavMenuItem` 容器执行的业务命令。节点只承载命令配置，不负责订阅或执行。 |
| `INavMenuEntry` | interface | NavMenu 有序 entry 的共同标记契约，不带选择、命令或路径语义；它不是自定义容器注册点。 |
事件 API：
| 事件 | 参数 | 语义 |
| `NavMenuItemClick` | `NavMenuItemClickEventArgs` | 菜单项点击事件，事件参数暴露 `INavMenuItem` 作为只读交互上下文。 |
| `NavMenuNodeSelected` | `NavMenuNodeSelectedEventArgs` | 叶子节点选中事件，事件参数暴露 `INavMenuNode`。 |
节点命令遵守 Avalonia 命令语义：`NavMenuNode` / `INavMenuNode` 只保存 `Command` 和 `CommandParameter`，实际执行、`CanExecute` 评估和 `CanExecuteChanged` 生命周期由生成出的 `NavMenuItem` 容器负责。`INavMenuNode` 为两个成员提供 `null` 默认实现，使既有自定义节点实现无需声明命令也能继续工作。`CommandParameter=null` 表示显式空参数，控件不能自动替换为 `ItemKey`；需要使用业务 key 时，应显式把 `ItemKey` 绑定或赋值给 `CommandParameter`。容器按 UI 周期合并连续的 `CanExecuteChanged` 通知：持续的 `false` 仍进入 disabled，同一同步执行周期内的 `false -> true` 瞬时变化只投影最终状态，避免多个共享命令节点触发无业务意义的禁用颜色闪动。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

Gallery 目录 `controlgallery/AtomUIGallery/ShowCases/Navigation/Menu`；结构化 entry 示例位于 Menu ShowCase 最后一项` 当前不存在；请检查控件文档中的 Gallery 页面元数据。

## 状态模型

NavMenu 的交互行为由 mode 决定。所有 mode 共用项激活事务契约：指针按下只建立待提交事务并尝试移动真实焦点，合法释放（按下与释放命中同一项）才按固定顺序提交选择并派发命令与事件，其余中断路径一律取消。键盘 Enter/Space 与指针合法释放复用激活入口；程序化 `SelectedItem` 是独立的选择入口，只同步选择状态，不执行节点命令或触发 `NavMenuItemClick`。完整状态机、提交顺序、同步重入和取消路径见 [NavMenu 项激活事务设计](item-activation-design.md)。

`Inline` 模式：

- 合法释放带子菜单的项目时切换 `IsSubMenuOpen`。
- 子菜单在当前视觉树中展开，使用 `LayoutAwareMotionActor` 承载展开收起 motion。
- 合法释放叶子节点时提交选择，并更新所有祖先 `IsInSelectedPath`。
- `IsAccordionMode=true` 时，顶层子菜单互斥展开；Inline 模式下旧分支收起与新分支展开同时开始，高度连续变化，动画期间再次点击可从当前进度反向切换。
- `IsInlineCollapsed=true` 时，public `Mode` 仍保持 `Inline`，但内部有效模式切换为 vertical popup 语义：顶层只显示图标或无图标首字符，inline 子树不在主视觉树中展开，带子菜单的顶层项目通过 popup 打开。
- 有效 inline collapsed 状态下，顶层叶子节点通过实际 header control 承载 Tooltip。节点显式 `Tooltip` 优先；未设置时回退到 `Header`；菜单级或节点级 Tooltip 被禁用、节点拥有子菜单或退出有效折叠状态时，不创建有效提示内容。
- 进入折叠时缓存当前 inline 打开路径并关闭主视觉树中的 inline 子菜单；退出折叠时恢复缓存路径。折叠和展开不得清空 `SelectedItem` 或 selected path。
- 键盘 Up / Down 在当前可见层级内移动 active/focus 项。
- Enter 在带子菜单项上切换展开状态，在叶子节点上提交选择。
- Left / Right 可作为桌面增强支持折叠或展开当前 active 子菜单，并保持 keyboard active 在当前项；叶子项上为 no-op，不能改变 `SelectedItem`。

`Vertical` 与 `Horizontal` 模式：

- 带子菜单的项目通过 Popup 展开。
- hover 可以延迟打开子菜单；pointer 离开后延迟关闭。hover 打开流程独立于激活事务。
- 合法释放叶子节点时提交选择；弹出层关闭由 pointer、窗口失焦、非客户端点击和同级打开状态共同控制。这些关闭入口只结束临时 popup open state，不清空持续的 `SelectedItem` 或 selected path。
- `Horizontal` 顶层菜单 popup 位于下方；非顶层 popup 按右侧边缘对齐。
- 键盘导航以当前打开的可见菜单层级为边界移动 active/focus 项，跳过禁用项、分割线和不可聚焦内容。
- 键盘 active 初次移动时优先以当前可见且已生成的 `SelectedItem` 容器作为方向键锚点，并立即移动到前一个或后一个可导航节点；如果没有选中项，或选中项隐藏在未打开的子菜单中，则从第一个可导航节点开始。
- `Horizontal` 顶层菜单使用 Left / Right 在顶层兄弟项之间移动，Down 或 Enter 打开当前 active 子菜单并进入子菜单第一项。
- `Vertical` 根层和所有 popup 子菜单使用 Up / Down 在同层兄弟项之间移动，Right 或 Enter 打开当前 active 子菜单并进入子菜单第一项，Left 或 Esc 返回父级并关闭当前 popup 分支。
- Enter 在叶子节点上提交选择并触发 `NavMenuNodeSelected` / `NavMenuItemClick`；方向键只改变 active/focus，不触发选择。
- Esc 只关闭当前键盘导航所在的 popup 分支并返回父级 active 项，不调用 `Close()`，因此不能清空 `SelectedItem`。

公共交互状态：

- `Disabled` 由节点 `IsEnabled` 和 command can-execute 共同决定，禁用项不应触发有效点击。
- 节点命令必须复用 `NavMenuItem` 的有效激活入口；pointer 与 keyboard 提交不能形成两条独立命令执行路径，也不能因选择事件再次执行命令。
- `PointerOver` 改变 header 前景和背景，但不能改变选中路径。
- `PressCandidate` 表示激活事务的待提交项，建立后一直保留到提交或取消；只有指针仍命中该项时才贡献与 selected 相同的背景，文字颜色保持按下前状态，不影响选中状态。
- `KeyboardActive` 表示键盘漫游中的当前项，只影响键盘导航锚点和 active 视觉，不改变选中路径；pointer-hold 与 keyboard-active 由独立 owner 维护，可以同时存在。
- `Selected` 表示当前叶子节点已提交选中。
- `IsInSelectedPath` 表示某个祖先位于当前选中路径中。
- `Open` 表示当前项目子菜单打开。

结构 entry 不进入公共交互状态：分组和分隔线不产生 `Selected`、`KeyboardActive`、`Open`、`ItemKey` 或 `Command`。分组中的节点仍使用最近的节点祖先作为 `ParentNode`；分组本身不增加 `Level`，也不进入 `TreeNodePath`。

## 主题与 Design Token

Inline 内容动效的共享设计要求稳定内容排版、完整高度裁剪、从当前帧反转及当前请求独占完成权；菜单选择、路径和
顶层互斥继续由 NavMenu 拥有。具体执行路径由本控件实现文档描述，根菜单宽度切换和 Popup 动效分别维护。
共享设计来源：`docs/architecture/systems/control-infrastructure/content-expansion.md`。

NavMenu Theme 按 mode、dark style、header state 和 item background model 分层。

```text
NavMenuTheme
  root template by Mode
  fixed Header / scrollable entry region / fixed Footer
  root background / padding / scroll behavior
  top-level ItemsPanel orientation

NavMenuItemTheme
  item template by Mode
  popup frame
  inline child frame
  motion duration

NavMenuGroupItemTheme / NavMenuDividerItemTheme
  non-interactive structure containers
  group title / divider orientation

Header Themes
  shared text/icon/background/selection
  horizontal active bar
  inline indentation and arrow rotation
```

Theme 映射规则：

- Root 背景使用 `ItemBg`，Dark root 背景使用 `DarkMenuBg`。
- Popup 背景使用 `MenuPopupBg`，Dark popup 使用 `DarkMenuPopupBg`。
- Header 默认背景为 `Transparent`，hover / selected 背景由 header state 直接控制。
- Keyboard active 通过 `IsKeyboardActive` / `ItemActiveBg` 表达；指针按住通过独立的 `IsPointerHold` 只使用与 selected 相同的 `ItemSelectedBg`（dark 使用 `DarkItemSelectedBg`），不覆盖既有文字颜色。两者优先级都低于真实 `Selected`，清除其中一个 owner 不得覆盖另一个 owner 的状态。keyboard active 可以叠加在 `IsInSelectedPath` 父节点上，使父节点保留 selected-path 文字色的同时显示临时 active 背景。header 背景使用 `MotionDurationSlow`（默认 300ms）与 `ItemBackgroundMotionEasing`（默认 CSS `ease` 等价曲线），使 hover 灰在按下后过渡到 selected 色，而 selection 只在合法释放时提交。
- Inline collapsed 根宽度使用 `InlineCollapsedWidth`，默认来自 `NavMenuToken.InlineCollapsedWidth=48`。折叠视觉只作用于 `Mode=Inline && IsInlineCollapsed=true`：一级 icon 使用 `CollapsedIconSize` 居中，标题和箭头收起，未配置 icon 的一级项从节点 `Header` 显示首字符；顶层叶子项使用独立 `Tooltip`，未设置时回退到 `Header`。
- Inline/Vertical 的 Header 和 Footer 位于菜单滚动区之外；无 Header/Footer 时对应 presenter 折叠，不占用布局空间。Horizontal 中 Header 左停靠、Footer 右停靠，菜单项占用中间区域。进入 inline collapsed 后 Header 保持可见以承载展开入口，Footer 自动隐藏；Header 内容需要根据 `IsInlineCollapsed` 自适应折叠宽度。
- 根层 inline collapsed 分组标题隐藏，分组及其透明嵌套分组内的节点继续继承根折叠状态，按顶层节点使用 `CollapsedIconSize` 居中；popup 或非根语义层级中的分组标题和节点保持普通 vertical 视觉。Horizontal 根层把分组渲染为透明水平集合并隐藏标题，popup 中恢复垂直分组标题。
- Horizontal 根层分隔线为竖线；Inline、Vertical、popup 和 inline collapsed 根层分隔线为横线。
- 根默认 ItemsPanel 通过 `TemplateBinding` 消费公开 `ItemSpacing`；submenu、popup 和 group 默认 ItemsPanel 消费由根控件投影的内部 effective spacing。该路径不使用进入子控件模板的 selector，也不建立逐容器 binding。自定义 ItemsPanel 是否消费 spacing 由自定义面板负责。
- `IsItemBackgroundEnabled=true` 时，inline child frame 使用 `SubMenuItemBg` / `DarkSubMenuItemBg`，并应用背景块专用外距。
- `IsItemBackgroundEnabled=false` 时，inline child frame 背景为 `Transparent`，不应用背景块专用外距；header 的文字色、hover、selected 和 selected path 仍然生效。
- Horizontal 顶层 light style 通过 `PART_ActiveIndicator` 表达选中；dark style 可以使用 selected background。

Header 背景与 NavMenuItem / inline submenu 背景块是不同职责，不应混为一个 selector 控制。

Token 来源：

NavMenuToken 是 NavMenu 的组件级设计变量层。它把全局颜色、尺寸、间距、圆角、字体、动效和 popup 体系转换为 NavMenu 可消费的语义值。

NavMenuToken 服务以下主题：

- `NavMenuTheme.axaml`
- `NavMenuItemTheme.axaml`
- `NavMenuGroupItemTheme.axaml`
- `NavMenuDividerItemTheme.axaml`
- `BaseNavMenuItemHeaderTheme.axaml`
- `HorizontalNavMenuItemHeaderTheme.axaml`
- `VerticalNavMenuItemHeaderTheme.axaml`
- `InlineNavMenuItemHeaderTheme.axaml`

NavMenuToken 不承载 `SelectedItem`、`IsSubMenuOpen`、`IsInSelectedPath`、`IsPointerOverSubMenu`、`Level`、`IsTopLevel` 等实例状态。这些状态由控件状态模型、容器层和主题 selector 处理。

## AOT 与裁剪注意事项

NavMenu 不应通过反射访问 template part 或内部状态。Header、popup、inline child frame 和 active indicator 均通过稳定 template part 和 Avalonia 属性接入。

`NavMenuNode` 必须通过 `[GenerateScopedResourceHost]` 生成 `IResourceHost` / `IThemeVariantHost`、attachment count、host generation 和 `IDisposable` attach token。generation 用于使跨 host 切换后遗留的 stale token 失效，尤其不能让 `A -> B -> A` 中第一轮 A token 释放当前 A attachment。`Header`、`Tooltip` 等节点属性的 `DynamicResource` 都使用该 scoped owner；scoped resource host 只解决动态资源宿主及其事件订阅，不替代节点到容器 binding 的释放，也不替代 `ICommand.CanExecuteChanged` 的解绑。

`NavMenuGroup` 遵守相同的 generated scoped resource-host 生命周期；结构容器不能永久持有 group 或 owner menu。`NavMenuDivider` 不需要资源宿主和订阅。

命令能力的完整释放边界由三层共同组成：

1. generated scoped resource host 释放 `ResourcesChanged` / `ActualThemeVariantChanged` owner 订阅；
2. container `CompositeDisposable` 释放节点到 `NavMenuItem` 的 `Command`、`CommandParameter` 和其他属性 binding；
3. `NavMenuItem` 在 command replacement 与 logical-tree detach 时解除 `CanExecuteChanged`。

缺少任意一层都不能宣称节点命令生命周期完整。不得通过弱化动态资源、改为静态值、永久 owner 引用、全局 command cache 或延迟清理规避释放问题。

`CanExecuteChanged` 可能在一次同步命令执行中快速发出 `false -> true`，共享同一命令的多个叶子容器如果逐次立即更新 effective enabled，会同时启动 disabled 前景色过渡并产生闪动。`NavMenuItem` 将通知统一 marshal 到 UI Dispatcher，并以 `Input` 优先级合并同一 UI 周期内的重复通知；回调只重新读取一次当前 `Command.CanExecute(CommandParameter)`，因此持续 `false` 仍会在下一轮交互前生效，而瞬时变化不会暴露中间视觉状态。待处理 operation 由当前 container 持有，并在 command replacement、parameter replacement 和 logical-tree detach 时 abort，不能让已回收容器被 dispatcher callback 延迟持有或被旧 command 状态回写。

handler 持有事件订阅时必须在 mode 切换、detached 或模板替换时释放。延迟打开 / 关闭任务必须支持取消，避免旧 pointer 状态影响新 mode 或新 popup。任务必须记录对应目标容器，container clear 只取消引用该容器的 pending open / close，不能误取消其他项的当前 hover。

inline collapsed cache 不得持有 `NavMenuItem`、header、popup 或 template part 引用。状态失效边界包括 ItemsSource reset、container clear、detach、mode change 和 default path replay revision 变化。

生成容器进入 clear/recycle 时必须通过 `NavMenu.ForgetGeneratedContainer` 同时通知 selection coordinator 和当前 interaction handler。该入口清除 selection coordinator 的 realized container 引用、keyboard-active 引用、激活事务的待提交项与 pointer-hold 视觉，以及 Default handler 指向该容器的 pending open / close；之后才释放 binding、resource host 和 entry context，避免旧交互回调读取已清空的数据对象。

键盘导航状态持有的 active item 引用必须随 detach、mode 切换、container clear、popup close 和 item disabled 变化失效。popup 内容可能在关闭后继续保留生成容器，因此有效性不能只看局部 `IsVisible`；必须同时验证 effective visible/effective enabled 和完整语义父链的打开状态。失效后第一次 Enter 只从当前可见层级重新建立 active 项，不提交旧项。

默认路径 replay 必须有界，避免容器生成失败时形成无休止 dispatcher 队列。

纯节点菜单必须保持一个数据节点对应一个 `NavMenuItem`，不得为统一模型增加额外 wrapper。只有实际存在 `NavMenuGroup` 或 `NavMenuDivider` 时才生成结构容器。语义 `Level` 在 prepare 上下文中 O(1) 得到；直接容器和路径查找优先走无分组快路径；spacing 只在根控件计算公开值与后代默认值的关系，再通过内部继承属性和 ItemsPanel `TemplateBinding` 传播，不为每个节点增加 relay binding。

默认 TreeDataTemplate 对 `INavMenuNode.Entries` 使用编译绑定；自定义节点通过协变接口默认实现回退到 `Children`。内置集合直接满足 Avalonia ItemsSource 的 `IList` / `INotifyCollectionChanged` 契约，该默认路径不分配适配集合。实现不使用反射、运行期类型扫描、动态 Style、dispatcher 延时或持久化扁平导航缓存，保持 NativeAOT 可分析。

## 源码索引

主要源码：

- `src/AtomUI.Desktop.Controls/NavMenu/NavMenu.cs`：公开控件、属性、ItemsControl 容器入口、默认路径 replay 和 mode 状态同步。
- `src/AtomUI.Desktop.Controls/NavMenu/NavMenuItem.cs`：内部容器、header 转发、子菜单、popup、选中和打开状态。
- `src/AtomUI.Desktop.Controls/NavMenu/NavMenuEntry.cs`：`INavMenuEntry` 最小标记契约和无状态 `NavMenuDivider` 数据模型。
- `src/AtomUI.Desktop.Controls/NavMenu/NavMenuNode.cs`：`INavMenuNode` 契约、接口级协变 entry 读取入口、`NavMenuNode.Entries` 唯一可写集合和 `Children` 实时兼容视图；`NavMenuNode` 使用 `[GenerateScopedResourceHost]` 获得 scoped `IResourceHost` / `IThemeVariantHost` 生命周期。
- `src/AtomUI.Desktop.Controls/NavMenu/NavMenuGroup.cs`：公开分组 entry 数据模型，管理透明 entry 子树和 scoped resource-host 生命周期。
- `src/AtomUI.Desktop.Controls/NavMenu/NavMenuEntryCollection.cs`、`NavMenuEntryGraph.cs`、`NavMenuEntryOwnershipCoordinator.cs`：内置 entry owner 的事务前校验、循环检测、完整图唯一性协调、弱 structural owner 和嵌套 source 生命周期。
- `src/AtomUI.Desktop.Controls/NavMenu/NavMenuGroupItem.cs`、`NavMenuDividerItem.cs`：内部不可交互结构容器。
- `src/AtomUI.Desktop.Controls/NavMenu/NavMenuEntryContainerCoordinator.cs`：统一节点、分组和分隔线的容器类型分派、准备、清理和回收键。
- `src/AtomUI.Desktop.Controls/NavMenu/NavMenuSemanticNavigator.cs`：按语义 owner 遍历已生成节点容器，跳过结构 entry。
- `src/AtomUI.Desktop.Controls/NavMenu/INavMenu.cs`、`INavMenuItem.cs`、`INavMenuElement.cs`：菜单和容器的内部/公共契约。
- `src/AtomUI.Desktop.Controls/NavMenu/NavMenuSelectionCoordinator.cs`：选择状态和祖先路径状态同步。
- `src/AtomUI.Desktop.Controls/NavMenu/NavMenuItemContainerBinder.cs`：节点数据与容器状态绑定。
- `src/AtomUI.Desktop.Controls/NavMenu/NavMenuInteractionHandlerBase.cs`：交互 handler 基类，拥有激活事务状态、统一提交管线、释放合法性判定和取消路径。
- `src/AtomUI.Desktop.Controls/NavMenu/DefaultNavMenuInteractionHandler.cs`：Vertical / Horizontal popup 模式交互策略，父节点激活确保 popup 打开，独立拥有 hover 延迟打开 / 关闭流程。
- `src/AtomUI.Desktop.Controls/NavMenu/InlineNavMenuInteractionHandler.cs`：Inline 展开收起交互策略，父节点激活切换展开状态。
- `src/AtomUI.Desktop.Controls/NavMenu/Header/`：三种 header 控件。
- `src/AtomUI.Desktop.Controls/NavMenu/Themes/`：root、item、header 和 popup 主题。
- `src/AtomUI.Desktop.Controls/NavMenu/NavMenuToken.cs`：控件 Token。

## 相关文档

- 源设计文档：`docs/controls/desktop/navigation/nav-menu/overview.md`
- 实现文档：`docs/controls/desktop/navigation/nav-menu/implementation.md`
- Token 文档：`docs/controls/desktop/navigation/nav-menu/token.md`
- 变更记录：`docs/controls/desktop/navigation/nav-menu/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/pagination/index-cn.md

# Pagination

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

Pagination 是 AtomUI 桌面控件体系中的分页控件，用于在有限页集合中切换页码、页大小和上一页下一页。

Pagination 不负责数据源过滤、排序或虚拟化列表本身。这些职责应由业务层、组合控件或更专用的 AtomUI 控件承担。

主要源码入口：

- `src/AtomUI.Desktop.Controls/Pagination`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/Navigation/Pagination` |
| 状态 | Stable |

## 何时使用

Pagination 的设计语言围绕控件职责、可观察状态和主题契约组织，而不是围绕模板节点组织。

| 维度 | 含义 | Pagination 中的表达 |
| --- | --- | --- |
| 产品语义 | 控件在界面中承担的稳定职责。 | Pagination 是 AtomUI 桌面控件体系中的分页控件，用于在有限页集合中切换页码、页大小和上一页下一页。 |
| 内容承载 | 用户数据、展示内容、集合项或操作入口如何进入控件。 | `Icon`、`JumpToText`、`PageText`、`PaginationItemType`、`TotalInfoTemplate`。 |
| 状态反馈 | public API、内部状态和伪类如何形成用户可感知反馈。 | selection/checked/active、collection/filter、input/value、motion、visual option。 |
| 主题语义 | ControlTheme、SharedToken、控件 Token 和模板绑定如何表达视觉。 | Pagination Token + ControlTheme。 |

## 公共 API

Pagination 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。

核心 public surface 按语义分组维护：

| 契约组 | 代表成员 | 维护含义 |
| --- | --- | --- |
| 内容与数据 | `Icon`、`JumpToText`、`PageText`、`PaginationItemType`、`TotalInfoTemplate` | 定义控件展示内容、输入数据、模板或业务对象入口。 |
| 选择与集合 | `CurrentPage`、`IsHideOnSinglePage`、`IsSelected`、`PageCount`、`PageSize` | 维护选择、展开、过滤、分页、分组或集合状态；`CurrentPage` 和 `PageSize` 默认 `TwoWay`。 |
| 交互与状态 | `IsMotionEnabled`、`IsPressed`、`IsReadOnly`、`IsShowQuickJumper`、`IsShowSizeChanger`、`IsShowTotalInfo` | 表达用户可观察状态、可用性、清除、加载或反馈语义。 |
| 视觉与布局 | `Align`、`SizeType` | 影响尺寸、位置、颜色、形状、密度和模板视觉变量。 |
| 其他稳定入口 | `Maximum`、`Minimum`、`Total` | 保留为 public surface，变更前需确认 Gallery 和用户 XAML 依赖。 |

稳定事件包括 `Click`。事件触发顺序属于兼容契约，不能因内部状态重排而改变。

主要公开类型与枚举：

- 类型：`AbstractPagination`、`PageNavRequestArgs`、`PageSizeComboBoxItem`、`Pagination`、`PaginationNav`、`PaginationNavItem`、`QuickJumpArgs`、`QuickJumpEdit`、`QuickJumperBar`、`SimplePagination`。
- 枚举：`PaginationAlign`。
- 本地化 Catalog：`PaginationLangResourceKind`。

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_Frame` | `?` | 承载根视觉、边框、背景或尺寸基线。 |
| `PART_InfoIndicator` | `?` | 展示指示器、进度、分页或状态反馈。 |
| `PART_JumpToContentPresenter` | `?` | 展示用户内容、文本、图标或模板化数据。 |
| `PART_Nav` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_NextNavItem` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_PageContentPresenter` | `?` | 展示用户内容、文本、图标或模板化数据。 |
| `PART_PageLineEdit` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_PreviousNavItem` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_QuickJumper` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_QuickJumperBarPresenter` | `?` | 展示用户内容、文本、图标或模板化数据。 |
| `PART_RootLayout` | `?` | 承载根视觉、边框、背景或尺寸基线。 |
| `PART_RootLayoutPart` | `?` | 承载根视觉、边框、背景或尺寸基线。 |
| `PART_SizeChangerPresenter` | `?` | 展示用户内容、文本、图标或模板化数据。 |
| `PART_TotalInfoPresenter` | `?` | 展示用户内容、文本、图标或模板化数据。 |

当前未抽取到控件专属伪类；主题主要依赖 Avalonia 标准伪类、模板绑定和内部 StyledProperty。

## 事件与命令

Pagination 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。
稳定事件包括 `Click`。事件触发顺序属于兼容契约，不能因内部状态重排而改变。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础分页

来源：`controlgallery/AtomUIGallery/ShowCases/Navigation/Pagination/Views/PaginationShowCase.axaml:37`

Gallery key：`ExamplesContent` / item `0`

```axaml
<atom:Pagination Total="50" CurrentPage="1" />
```

### 受控绑定

来源：`controlgallery/AtomUIGallery/ShowCases/Navigation/Pagination/Views/PaginationShowCase.axaml:51`

Gallery key：`ExamplesContent` / item `1`

```axaml
<StackPanel Orientation="Vertical" Spacing="12">
    <atom:Pagination Total="120"
                     CurrentPage="{Binding BoundCurrentPage}"
                     PageSize="{Binding BoundPageSize}"
                     IsShowSizeChanger="True"
                     IsShowQuickJumper="True"
                     IsShowTotalInfo="True" />
    <StackPanel Orientation="Horizontal" Spacing="16">
        <StackPanel Orientation="Horizontal" Spacing="8">
            <atom:TextBlock VerticalAlignment="Center"
                            Text="CurrentPage：" />
            <atom:TextBlock VerticalAlignment="Center"
                            Text="{Binding BoundCurrentPageText}" />
        </StackPanel>
        <StackPanel Orientation="Horizontal" Spacing="8">
            <atom:TextBlock VerticalAlignment="Center"
                            Text="PageSize：" />
            <atom:TextBlock VerticalAlignment="Center"
                            Text="{Binding BoundPageSizeText}" />
        </StackPanel>
    </StackPanel>
    <StackPanel Orientation="Horizontal" Spacing="10">
        <atom:Button SizeType="Small"
                     Command="{Binding SetBoundPaginationCommand}"
                     Content="设置第 5 页 / 20" />
        <atom:Button SizeType="Small"
                     Command="{Binding ResetBoundPaginationCommand}"
                     Content="重置" />
    </StackPanel>
</StackPanel>
```

### 对齐方式

来源：`controlgallery/AtomUIGallery/ShowCases/Navigation/Pagination/Views/PaginationShowCase.axaml:93`

Gallery key：`ExamplesContent` / item `2`

```axaml
<StackPanel Orientation="Vertical" Spacing="10">
    <atom:Pagination Total="50" CurrentPage="1" Align="Start" />
    <atom:Pagination Total="50" CurrentPage="1" Align="Center" />
    <atom:Pagination Total="50" CurrentPage="1" Align="End" />
</StackPanel>
```

### 更多页码

来源：`controlgallery/AtomUIGallery/ShowCases/Navigation/Pagination/Views/PaginationShowCase.axaml:110`

Gallery key：`ExamplesContent` / item `3`

```axaml
<atom:Pagination Total="500" CurrentPage="6" IsShowSizeChanger="True" />
```

## 状态模型

Pagination 的状态流按以下路径收敛：

```text
Public API / inherited command / item source / user input
  -> 控件实例状态
  -> effective state / pseudo-class / template property
  -> ControlTheme selector / presenter / renderer
  -> Gallery 可观察行为
```

状态维护规则：

- Disabled 或不可交互状态优先屏蔽 pointer、keyboard、motion 和提交类反馈。
- 用户点击页码、快速跳转或切换页大小时，通过 `CurrentPage` / `PageSize` 写回同一个受控状态；绑定方不需要显式设置 `Mode=TwoWay`。
- selection/checked/active、collection/filter、input/value、motion、visual option 状态由控件实例或明确的数据 owner 推导，不能在 template part 之间双向竞争。
- 模板重套用时必须把 public API 对应状态回放到新的 part、伪类和主题变量。
- 集合、弹层、异步、动效或窗口相关状态必须能处理 reset、close、cancel、detach 和 owner 释放。

## 主题与 Design Token

Pagination 的视觉模型由控件模板、ControlTheme、SharedToken 和必要的控件 Token 共同构成。

| 主题文件 | 职责 |
| --- | --- |
| `PaginationNavItemTheme.axaml` | 定义集合项、容器项或局部单元的状态视觉。 |
| `PaginationNavTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `PaginationTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `QuickJumperBarTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `SimplePaginationTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |

Pagination 使用 `PaginationToken` 作为控件 Token scope。Token 只表达组件视觉语义，不承载 selection/checked/active、collection/filter、input/value、motion、visual option 运行时状态。

主题维护规则：

- 不删除或重命名已经稳定的 ControlTheme key、template part、伪类和资源 key。
- 不把可由 AXAML 表达的模板状态迁移为 C# 动态创建视觉。
- 不把 hover、pressed、selected、expanded、loading、filter、popup open 等运行时状态写入 Token。
- Browser 或平台特化主题必须保持同一 API 的语义一致。

Token 来源：

Pagination Token 只表达组件级视觉变量，例如尺寸、间距、颜色、圆角、阴影、图标尺寸和弹层边界。Token 不承载运行时选择、展开、加载、错误、上传任务、过滤条件或业务状态。

当前 Token scope：

- `PaginationToken`，scope id 为 `Pagination`，源码位于 `src/AtomUI.Desktop.Controls/Pagination/PaginationToken.cs`。

## AOT 与裁剪注意事项

资源和 AOT 约束：

- 不通过运行时反射扫描 public API、Token 或 Gallery 示例数据。
- 不把可静态声明的模板结构迁移到 C# 动态创建。
- 异步加载、上传、弹层和窗口生命周期必须能取消或释放。
- 缓存对象必须与控件、窗口、弹层或数据 owner 生命周期一致。
- Source generator 生成文件不手工编辑；需要修改时改输入源或 generator。

性能边界：

- 控件应优先复用 Avalonia 原生虚拟化、模板绑定和资源系统。
- 避免为每次状态变化创建不必要的视觉对象、订阅或动画对象。
- 大集合控件必须保证 container recycle 后不会泄漏旧 item 状态。

## 源码索引

主要源码文件：

- `src/AtomUI.Desktop.Controls/Pagination/AbstractPagination.cs`
- `src/AtomUI.Desktop.Controls/Pagination/Localization/PaginationLangResourceKind.cs`
- `src/AtomUI.Desktop.Controls/Pagination/Localization/en-US.xlf`
- `src/AtomUI.Desktop.Controls/Pagination/Localization/zh-CN.xlf`
- `src/AtomUI.Desktop.Controls/Pagination/Localization/zh-TW.xlf`
- `src/AtomUI.Desktop.Controls/Pagination/PageNavRequestArgs.cs`
- `src/AtomUI.Desktop.Controls/Pagination/PageSizeComboBoxItem.cs`
- `src/AtomUI.Desktop.Controls/Pagination/Pagination.cs`
- `src/AtomUI.Desktop.Controls/Pagination/PaginationNav.cs`
- `src/AtomUI.Desktop.Controls/Pagination/PaginationNavItem.cs`
- `src/AtomUI.Desktop.Controls/Pagination/PaginationToken.cs`
- `src/AtomUI.Desktop.Controls/Pagination/QuickJumpEdit.cs`
- `src/AtomUI.Desktop.Controls/Pagination/QuickJumperBar.cs`
- `src/AtomUI.Desktop.Controls/Pagination/SimplePagination.cs`
- `src/AtomUI.Desktop.Controls/Pagination/Themes/PaginationNavItemTheme.axaml`
- `src/AtomUI.Desktop.Controls/Pagination/Themes/PaginationNavTheme.axaml`
- `src/AtomUI.Desktop.Controls/Pagination/Themes/PaginationTheme.axaml`
- `src/AtomUI.Desktop.Controls/Pagination/Themes/QuickJumperBarTheme.axaml`
- `src/AtomUI.Desktop.Controls/Pagination/Themes/SimplePaginationTheme.axaml`

职责边界：

- 控件主文件保留 public/protected API、Avalonia 属性注册、事件和主要生命周期入口。
- Theme 文件负责静态视觉结构、template part、selector 和资源绑定。
- Token 文件只提供组件视觉变量，不保存实例状态。
- Gallery 文件只展示用法和示例，不作为运行时逻辑 owner。

## 相关文档

- 源设计文档：`docs/controls/desktop/navigation/pagination/overview.md`
- 实现文档：`docs/controls/desktop/navigation/pagination/implementation.md`
- Token 文档：`docs/controls/desktop/navigation/pagination/token.md`
- 变更记录：`docs/controls/desktop/navigation/pagination/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/steps/index-cn.md

# Steps

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

Steps 是线性流程的状态导航条，用于表达当前步骤、已完成步骤、等待步骤、错误步骤和当前步骤的局部进度。它适合安装向导、分步表单、审批流、支付流程和任务流程。

Steps 负责：

- 展示有序步骤及其标题、副标题和详情内容。
- 根据唯一的 `Current` 输入计算每个 item 的有效状态。
- 展示 Indicator、Connector、Dot、Navigation、Inline、Panel 和 Progress 视觉。
- 在启用交互时发出当前步骤变更请求。

Steps 不负责：

- 保存或切换步骤对应的页面内容。
- 业务流程校验、路由、异步任务编排、表单提交或权限控制。
- 维护 Avalonia Selection 状态。
- 在用户激活 item 后自行修改 `Current`。

步骤页面由调用方根据 `Current` 在 Steps 外部切换。`StepsItem.Content` 表示步骤详情，不表示步骤页面。

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/Navigation/Steps` |
| 状态 | Stable |

## 何时使用

Steps 表达“有序流程 + 当前进度 + 可选导航请求”。

| 维度 | 含义 | 典型表达 |
| --- | --- | --- |
| 顺序 | item 按 `Initial + index` 获得稳定步骤编号。 | Indicator number、Connector。 |
| 当前 | `Current` 是唯一当前步骤输入。 | `IsCurrent`、当前 Indicator。 |
| 历史 | 当前步骤之前且未显式覆盖的 item 为完成。 | `StepsStatus.Finish`。 |
| 等待 | 当前步骤之后且未显式覆盖的 item 为等待。 | `StepsStatus.Wait`。 |
| 异常 | 根当前状态或 item 显式状态可以为错误。 | `StepsStatus.Error`。 |
| 导航 | 激活 item 只发出变更请求。 | `CurrentChangeRequested`。 |
| 反馈 | Wave 表达真实 pointer click，不表达状态变化。 | Indicator Wave。 |
| 密度 | 尺寸控制 Indicator、文字和间距。 | `SizeType`。 |

`StepsType.Default` 表达标准流程，`Dot` 表达实心点状流程，`OutlineDot` 表达空心点状流程，`Navigation` 强调导航入口，`Inline` 表达紧凑内联流程，`Panel` 表达面板式分段步骤。

## 公共 API

### 3.1 根控件契约

`Steps` 继承 `ItemsControl`。它不公开或继承 Selection API。

| API | 类型 | 语义 |
| --- | --- | --- |
| `Current` | `int` | 唯一当前步骤编号，默认 `0`；控件不在激活路径中自行写入。 |
| `Initial` | `int` | 第一个 item 的编号偏移，默认 `0`；不用于初始化或重置 `Current`。 |
| `Status` | `StepsStatus` | 当前步骤的默认状态，默认 `Process`。 |
| `Percent` | `double?` | 当前 Process item 的局部进度；`null` 表示不显示。 |
| `Type` | `StepsType` | `Default`、`Dot`、`OutlineDot`、`Navigation`、`Inline` 或 `Panel`。Panel 强制采用水平等宽布局。 |
| `PanelVariant` | `StepsPanelVariant` | Panel 的视觉变体：`Filled`（默认）或 `Outlined`。其他类型忽略。 |
| `Orientation` | `Orientation` | 步骤排列方向，默认 `Horizontal`。 |
| `TitlePlacement` | `Orientation` | 标题相对 Indicator 的请求布局，默认 `Horizontal`。 |
| `SizeType` | `SizeType` | Indicator、文字和间距尺寸，默认 `Middle`。 |
| `IsItemClickable` | `bool` | 是否允许 item 发出导航请求，默认 `false`。 |
| `Offset` | `int` | Inline 类型前方保留的空 item 单元数量，默认 `0`；其他类型忽略。 |
| `HorizontalContentAlignment` | `HorizontalAlignment` | 垂直 Navigation item 列的水平对齐，默认 `Center`；普通 Steps 和水平 Navigation 不使用。 |
| `IsMotionEnabled` | `bool` | 是否启用 transition 和 Wave，默认来自 SharedToken。 |
| `ItemHeaderForeground` | `IBrush?` | 所有 item 主标题的实例级语义前景色覆盖；默认 `null`，保留当前类型和状态的 Token 视觉。 |
| `ItemSubHeaderForeground` | `IBrush?` | 所有 item 辅助标题的实例级语义前景色覆盖；默认 `null`，保留当前类型和状态的 Token 视觉。 |
| `ItemRailBackground` | `IBrush?` | 所有 item Connector 的实例级语义背景覆盖；默认 `null`，保留当前类型和状态的 Token 视觉。 |
| `Items` / `ItemsSource` / `ItemTemplate` | inherited | 步骤集合和数据模板入口。 |

`Current` 允许小于 `Initial` 或大于所有 item 的编号。Steps 不裁剪、不归一也不回写越界值。

三个 `Item*` 属性只覆盖实例的语义视觉，不参与 `EffectiveStatus`、`ConnectorStatus`、布局或交互计算。它们作用于 `Steps` 拥有的全部直接或生成容器；属性为 `null` 时，主题继续使用 Wait、Process、Finish、Error 以及 Inline 对应的 Token。

### 3.2 事件契约

```csharp
public event EventHandler<StepsCurrentChangeRequestedEventArgs>?
    CurrentChangeRequested;
```

事件仅由可交互 item 的 pointer 或 keyboard 激活路径触发，参数提供目标步骤编号。调用方决定是否更新 `Current`。当前 item 被重复激活时不发出请求。

### 3.3 Item 契约

`StepsItem` 继承 `HeaderedContentControl`。

| API | 类型 | 语义 |
| --- | --- | --- |
| `Header` / `HeaderTemplate` | inherited | 步骤标题。 |
| `SubHeader` / `SubHeaderTemplate` | `object?` / `IDataTemplate?` | 标题旁的辅助信息。 |
| `Content` / `ContentTemplate` | inherited | 步骤详情内容。 |
| `Icon` | `PathIcon?` | 自定义 Indicator 图标。 |
| `Status` | `StepsStatus?` | item 显式状态；`null` 使用根控件自动状态。 |
| `IsEnabled` | inherited | 是否允许该 item 参与交互。 |

`StepsItem` 不实现选择协议，不公开选择态、额外完成态或页面内容 API。

### 3.4 枚举契约

| 枚举 | 成员 | 语义 |
| --- | --- | --- |
| `StepsStatus` | `Wait`、`Process`、`Finish`、`Error` | 根当前状态、item 显式状态和有效状态。 |
| `StepsType` | `Default`、`Dot`、`OutlineDot`、`Navigation`、`Inline`、`Panel` | Steps 的完整视觉类型。Panel 隐藏 Indicator/Connector，使用面板背景、边框和外溢箭头。 |
| `StepsPanelVariant` | `Filled`、`Outlined` | Panel 的填充或描边变体。 |

`StepsType` 同时表达原 Style 和 Indicator 类型，禁止形成 `Navigation + Dot` 等没有明确 Steps 语义的组合。
`OutlineDot` 与 `Dot` 共享布局语义和 Dot 尺寸 Token，但 Indicator 使用透明背景和状态色边框，并且不播放 Indicator Wave。

### 3.5 主题契约

LLMS 语义区域：

| Part | AtomUI 节点 | 职责 | 稳定性 |
| --- | --- | --- | --- |
| `root` | `Steps` | Items、根展示输入和导航请求入口。 | public |
| `items` | `PART_ItemsPresenter` | 承载 StepsPanel 和 item 容器。 | template-stable |
| `item` | `StepsItem` | 单项状态、内容和交互语义。 | public |
| `indicator` | `PART_Indicator` | 数字、状态图标、Dot、自定义 Icon、Progress 和 Wave 目标。 | template-stable |
| `title` | `HeaderPresenter` | 标题。 | internal-observable |
| `subtitle` | `SubHeaderPresenter` | 副标题。 | internal-observable |
| `content` | `ContentPresenter` | 步骤详情。 | internal-observable |
| `rail` | `Connector` | 当前 item 与下一个 item 的连接线。 | internal-observable |
| `navigation-arrow` | `NavigationArrow` | Navigation 类型的步骤方向提示。 | internal-observable |
| `navigation-active-indicator` | `NavigationActiveIndicator` | Navigation 当前项的水平底线或垂直右侧线。 | internal-observable |

Steps 不提供控件专属的完成态或选择态伪类。状态主题读取 `EffectiveStatus`、`IsCurrent`、`CanInvoke` 以及 Avalonia 标准 `:pointerover`、`:focus-visible`、`:disabled` 伪类。

`title`、`subtitle` 和 `rail` 是 `StepsItem` 自身模板的内部语义节点。只有 `StepsItemTheme.axaml` 可以进入该模板并消费根控件投影的语义样式值；外部应用、Gallery 和 `StepsTheme.axaml` 不得依赖节点名称或通过 `/template/` selector 穿透 `StepsItem`。

## 事件与命令

### 3.2 事件契约
public event EventHandler<StepsCurrentChangeRequestedEventArgs>?
事件仅由可交互 item 的 pointer 或 keyboard 激活路径触发，参数提供目标步骤编号。调用方决定是否更新 `Current`。当前 item 被重复激活时不发出请求。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/Navigation/Steps/Views/StepsShowCase.axaml:41`

SourceKey：`steps-basic`

```axaml
<atom:Steps Current="0">
    <atom:StepsItem Header="已完成" Content="这是一段描述。" />
    <atom:StepsItem Header="进行中" Content="这是一段描述。" SubHeader="剩余 00:00:08" />
    <atom:StepsItem Header="等待中" Content="这是一段描述。" />
</atom:Steps>
```

### 迷你版本

来源：`controlgallery/AtomUIGallery/ShowCases/Navigation/Steps/Views/StepsShowCase.axaml:59`

SourceKey：`steps-small`

```axaml
<atom:Steps Current="0" SizeType="Small">
    <atom:StepsItem Header="已完成" Content="这是一段描述。" />
    <atom:StepsItem Header="进行中" Content="这是一段描述。" SubHeader="剩余 00:00:08" />
    <atom:StepsItem Header="等待中" Content="这是一段描述。" />
</atom:Steps>
```

### 垂直方向

来源：`controlgallery/AtomUIGallery/ShowCases/Navigation/Steps/Views/StepsShowCase.axaml:142`

SourceKey：`steps-vertical`

```axaml
<atom:Steps Current="1" Orientation="Vertical">
    <atom:StepsItem Header="已完成" Content="这是一段描述。" />
    <atom:StepsItem Header="进行中" Content="这是一段描述。" SubHeader="剩余 00:00:08" />
    <atom:StepsItem Header="等待中" Content="这是一段描述。" />
</atom:Steps>
```

### 垂直迷你版本

来源：`controlgallery/AtomUIGallery/ShowCases/Navigation/Steps/Views/StepsShowCase.axaml:160`

SourceKey：`steps-vertical-small`

```axaml
<atom:Steps Current="1" Orientation="Vertical" SizeType="Small">
    <atom:StepsItem Header="已完成" Content="这是一段描述。" />
    <atom:StepsItem Header="进行中" Content="这是一段描述。" SubHeader="剩余 00:00:08" />
    <atom:StepsItem Header="等待中" Content="这是一段描述。" />
</atom:Steps>
```

## 状态模型

### 4.1 唯一状态流

```text
Current / Initial / Steps.Status / StepsItem.Status / item index
    -> StepNumber / AutomaticStatus
    -> IsCurrent / EffectiveStatus / ConnectorStatus
    -> StepsItem theme / StepsItemIndicator theme
```

模板和容器生命周期只消费状态，不建立或修正状态。

### 4.2 状态算法

对索引为 `index` 的 item：

```text
StepNumber = Initial + index

AutomaticStatus =
    StepNumber == Current ? Steps.Status :
    StepNumber < Current  ? Finish :
                            Wait

EffectiveStatus = StepsItem.Status ?? AutomaticStatus
IsCurrent       = StepNumber == Current
```

`EffectiveStatus` 是 Indicator、Connector、Progress、文字和颜色唯一允许读取的状态。`IsCurrent` 与 `EffectiveStatus` 独立，因此当前 item 可以显式为 Wait、Finish 或 Error。

### 4.3 越界语义

- `Current < Initial`：没有当前 item，未覆盖 item 自动为 Wait。
- `Current >= Initial + ItemCount`：没有当前 item，未覆盖 item 自动为 Finish。
- Items 为空：保留 `Current`，不产生 item 状态。
- Items 动态变化：使用保留的输入重新计算，不执行选择恢复或 Current 归一。

### 4.4 Connector 语义

连接 item `i` 和 `i + 1` 的 Connector 使用下一个 item 的 `EffectiveStatus`，让指向当前错误或当前进行中步骤的线段跟随目标步骤状态：

```text
Connector[i].Status = Item[i + 1].EffectiveStatus
```

最后一个 item 不显示 Connector。

### 4.5 Pointer、键盘与 Wave

可交互条件：

```text
Steps.IsItemClickable
&& Steps.IsEnabled
&& StepsItem.IsEnabled
```

- Pointer 只有在同一 item 内完成 press/release 才视为 click。
- 点击非当前 item：播放目标 Indicator Wave，并发出 `CurrentChangeRequested`。
- 点击当前 item：播放 Wave，不发出请求。
- `Type=OutlineDot` 点击仍按可交互规则发出请求，但不播放 Indicator Wave。
- Enter/Space：非当前 item 发出请求，不播放 Wave。
- 程序化修改 `Current`、Items 变化、模板重套和状态重算都不播放 Wave。
- 不可交互 item 不显示 hand cursor、hover 激活视觉，也不进入 Tab 焦点序列。

## 主题与 Design Token

Steps 使用统一语义模板，而不是按 Type、Orientation 和 TitlePlacement 复制 ControlTemplate。

| 主题文件 | 职责 |
| --- | --- |
| `StepsTheme.axaml` | 根模板、ItemsPresenter、StepsPanel 和根展示输入映射。 |
| `StepsItemTheme.axaml` | 统一 item 语义模板、Panel item frame、状态颜色、Connector、内容和交互视觉。 |
| `StepsItemIndicatorTheme.axaml` | 统一 Indicator 模板、Dot、Icon、状态图标、Progress 和 Wave。 |

运行时组合：

```text
Steps
└── ItemsPresenter#PART_ItemsPresenter
    └── StepsPanel
        └── StepsItem
            └── StepsItemLayoutPanel
                ├── StepsItemIndicator#PART_Indicator
                │   └── WaveSpiritDecorator#PART_WaveSpirit
                ├── ContentPresenter#HeaderPresenter
                ├── ContentPresenter#SubHeaderPresenter
                ├── PixelAlignedBorder#Connector
                ├── ContentPresenter#ContentPresenter
                ├── StepsPanelItemFrame#ItemWrapper
                ├── PathIcon#NavigationArrow
                ├── StepsPanelArrow#PanelArrow
                └── PixelAlignedBorder#NavigationActiveIndicator
```

`StepsPanel` 负责 item 间的 flex/stack 布局；Panel 类型强制水平排列并将每个 item 等宽。`StepsItemLayoutPanel` 负责 item 内固定语义区域、Connector 线宽、Panel 外溢箭头和 Navigation active 线的排列。二者不创建状态。

Panel 类型的几何规则：

- Indicator 和普通 Connector 不参与可见布局。
- ItemWrapper 覆盖完整 item 单元，PanelArrow 在非末项的外侧拉伸为楔形箭头。
- LTR 箭头向右外溢，RTL 箭头向左外溢；末项不创建可见箭头。
- `Filled` 使用状态背景作为面板表面，并在非首项裁出左侧 notch；`Outlined` 保留共享接缝的箭头边框，非当前 Error 项保持容器背景并使用红色文字和边框，当前 Error 项才使用浅红 active 背景。
`OutlineDot` 复用 `Dot` 的布局路径，只改变 Indicator 的填充、边框和 Wave 语义。

### 5.1 Item 语义样式覆盖

`ItemHeaderForeground`、`ItemSubHeaderForeground` 和 `ItemRailBackground` 是 `Steps` 实例级的语义槽，不是对模板内部节点的公开暴露。`Steps` 将显式值投影到每个 `StepsItem`，再由 `StepsItemTheme.axaml` 在自己的模板边界内分别应用到 Header、SubHeader 和 Connector。

三项 API 分别对应步骤条标题、子标题和 rail 的语义能力，但保持 Avalonia 的强类型 `IBrush?` 契约，不公开任一模板节点。

优先级固定为：

```text
非 null 的 Steps 实例语义样式
    > 当前 Type / EffectiveStatus 对应的 StepsToken
null
    -> 完整回退到当前 Type / EffectiveStatus 对应的 StepsToken
```

运行时修改或清空任一属性必须立即更新所有已实现容器；直接声明的 `StepsItem`、由普通数据项生成的容器以及回收后重新准备的容器遵循同一投影规则。该覆盖不改变 Indicator、Content、Navigation active indicator 或其他未命名语义区域。

有效标题布局：

```text
Orientation == Vertical -> Horizontal
Type == Dot             -> Vertical
Type == OutlineDot      -> Vertical
Type == Inline          -> Vertical
Type == Navigation      -> Horizontal
其他                    -> TitlePlacement
```

Token 来源：

StepsToken 描述步骤标题、详情内容、Indicator、Dot、OutlineDot、Connector、Navigation、Inline、Panel 和 Progress ring 的组件级视觉语义。

StepsToken 不承载：

- Current、Initial、item index 或 StepNumber。
- public Status、AutomaticStatus、EffectiveStatus、IsCurrent 或 ConnectorStatus。
- item 数量、layout bounds、pointer、keyboard、focus 或 Wave 播放状态。
- Percent 当前值、CanInvoke、IsItemClickable 或 IsMotionEnabled。
- `ItemHeaderForeground`、`ItemSubHeaderForeground` 或 `ItemRailBackground` 的实例值。

## AOT 与裁剪注意事项

资源边界：

- TokenResource 和 SharedToken 只提供视觉值，不保存实例状态。
- 根展示属性使用 AXAML Ancestor Binding 投影到 item 和 internal panel。
- 固定模板关系使用 TemplateBinding、Ancestor Binding 和 selector，不使用字符串路径反射。
- item 语义样式使用静态注册的 StyledProperty 和普通 Avalonia binding 投影，不生成运行时 selector，也不依赖内部节点名称从控件外穿透模板。

性能边界：

- 单容器准备 O(1)。
- 单 item Status 变化 O(1)。
- 根状态变化和 Reset O(n)。
- 不维护第二份 item 列表、状态字典或延迟更新队列。
- 两个 Panel 在 Measure/Arrange 中不创建视觉，不修改 public 状态。

AOT 边界：

- 不新增运行时反射扫描、动态类型注册或编译期不可分析的 binding 路径。
- 不通过反射访问 Wave 播放状态；测试使用 internal 可观察入口或渲染结果。
- 新 internal panel 由静态 AXAML 和显式类型引用创建。
- 三项语义样式投影不使用反射、动态属性发现或运行时类型扫描。

## 源码索引

主要源码：

- `src/AtomUI.Desktop.Controls/Steps/Steps.cs`：public API、事件、容器生成、根输入分发和 item 状态协调。
- `src/AtomUI.Desktop.Controls/Steps/StepsItem.cs`：public item 契约、internal 派生状态、owner 生命周期和激活入口。
- `src/AtomUI.Desktop.Controls/Steps/StepsItemIndicator.cs`：Indicator 状态、Wave part、Progress 绘制和渲染失效。
- `src/AtomUI.Desktop.Controls/Steps/StepsPanel.cs`：item 间水平 flex、Navigation/Panel 等宽、Inline 和垂直 stack 布局。
- `src/AtomUI.Desktop.Controls/Steps/StepsItemLayoutPanel.cs`：Indicator、Header、SubHeader、Connector、Content、NavigationArrow、PanelArrow 和 NavigationActiveIndicator 的 item 内布局。
- `src/AtomUI.Desktop.Controls/Steps/StepsPanelArrow.cs`：internal 可拉伸 Panel 楔形箭头绘制控件，负责 LTR/RTL 三角形和边框。
- `src/AtomUI.Desktop.Controls/Steps/StepsPanelItemFrame.cs`：Panel item 的视觉外框；Filled 非首项使用左侧 notch 几何裁剪，Outlined 保持完整边框。
- `src/AtomUI.Desktop.Controls/Steps/StepsToken.cs`：Steps 控件 Token。
- `src/AtomUI.Desktop.Controls/Steps/Themes/StepsTheme.axaml`：根模板和 StepsPanel。
- `src/AtomUI.Desktop.Controls/Steps/Themes/StepsItemTheme.axaml`：统一 item 语义模板和状态样式。
- `src/AtomUI.Desktop.Controls/Steps/Themes/StepsItemIndicatorTheme.axaml`：统一 Indicator、Dot、Icon、Progress 和 Wave 模板。

测试目录：

- `tests/AtomUI.Desktop.Controls.Tests/Steps`：状态、Items、交互、Wave、Progress、布局和生命周期回归测试。

Gallery 目录：

- `controlgallery/AtomUIGallery/ShowCases/Navigation/Steps`：示例、源码片段和本地化资源。

## 相关文档

- 源设计文档：`docs/controls/desktop/navigation/steps/overview.md`
- 实现文档：`docs/controls/desktop/navigation/steps/implementation.md`
- Token 文档：`docs/controls/desktop/navigation/steps/token.md`
- 变更记录：`docs/controls/desktop/navigation/steps/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/tab-control/index-cn.md

# TabControl

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

TabControl 是 AtomUI 桌面控件体系中的标签页内容控件，用于在多个内容页面之间切换、关闭、拖动排序和溢出导航。

TabControl 不负责单纯页签条、路由系统或布局分割容器。这些职责应由业务层、组合控件或更专用的 AtomUI 控件承担。

主要源码入口：

- `src/AtomUI.Desktop.Controls/TabControl`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/Navigation/TabControl` |
| 状态 | Stable |

## 何时使用

TabControl 的设计语言围绕控件职责、可观察状态和主题契约组织，而不是围绕模板节点组织。

| 维度 | 含义 | TabControl 中的表达 |
| --- | --- | --- |
| 产品语义 | 控件在界面中承担的稳定职责。 | TabControl 是 AtomUI 桌面控件体系中的标签页内容控件，用于在多个内容页面之间切换、关闭、拖动排序和溢出导航。 |
| 内容承载 | 用户数据、展示内容、集合项或操作入口如何进入控件。 | `CloseIcon`、`ContentPadding`、`ContentTemplate`、`HeaderEndEdgePadding`、`HeaderEndExtraContent`、`HeaderEndExtraContentTemplate`、`HeaderStartEdgePadding`、`HeaderStartExtraContent` 等 15 项。 |
| 状态反馈 | public API、内部状态和伪类如何形成用户可感知反馈。 | selection/checked/active、reorder、motion、visual option。 |
| 主题语义 | ControlTheme、SharedToken、控件 Token 和模板绑定如何表达视觉。 | TabControl Token + ControlTheme。 |

## 公共 API

TabControl 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。

核心 public surface 按语义分组维护：

| 契约组 | 代表成员 | 维护含义 |
| --- | --- | --- |
| 内容与数据 | `CloseIcon`、`ContentPadding`、`ContentTemplate`、`HeaderEndEdgePadding`、`HeaderEndExtraContent`、`HeaderEndExtraContentTemplate`、`HeaderStartEdgePadding`、`HeaderStartExtraContent`、`HeaderStartExtraContentTemplate`、`HorizontalContentAlignment` 等 15 项 | 定义控件展示内容、输入数据、模板或业务对象入口。 |
| 选择与集合 | `IsSelected`、`IsTabReorderEnabled`、`TabActivationTrigger`、`SelectedIndex`、`SelectedItem`、`ItemsSource` | 维护选择触发时机、集合顺序、拖动排序和内容页状态。 |
| 交互与状态 | `IsAutoHideCloseButton`、`IsClosable`、`IsMotionEnabled`、`IsShowAddTabButton`、`IsTabAutoHideCloseButton`、`IsTabClosable` | 表达用户可观察状态、可用性、清除、加载或反馈语义。 |
| 视觉与布局 | `SizeType`、`TabAlignmentCenter`、`TabStripPlacement` | 影响尺寸、位置、颜色、形状、密度和模板视觉变量。 |
| 其他稳定入口 | `AddTabButton`、`TabScrollViewer` | 保留为 public surface，变更前需确认 Gallery 和用户 XAML 依赖。 |

稳定事件包括 `AddTabRequest`、`CloseTab`、`Closed`、`Closing`、`TabReordering`、`TabReordered`。事件触发顺序属于兼容契约，不能因内部状态重排而改变。

主要公开类型与枚举：

- 类型：`BaseOverflowMenuItem`、`BaseTabControl`、`BaseTabScrollViewer`、`BaseTabStrip`、`CardTabControl`、`CardTabStrip`、`CloseTabRequestEventArgs`、`TabClosedEventArgs`、`TabClosingEventArgs`、`TabControl`、`TabControlOverflowMenuItem`、`TabControlScrollViewer`、`TabItem`、`TabItemData` 等 22 项。
- 枚举：`TabActivationTrigger`、`TabSharp`。

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_AddTabButton` | `?` | 承载用户触发入口、导航或关闭动作。 |
| `PART_AlignWrapper` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_CardTabStripScrollViewer` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_ItemCloseButton` | `?` | 承载用户触发入口、导航或关闭动作。 |
| `PART_ItemsPresenter` | `?` | 展示用户内容、文本、图标或模板化数据。 |
| `PART_ScrollEndEdgeIndicator` | `?` | 展示指示器、进度、分页或状态反馈。 |
| `PART_ScrollMenuIndicator` | `?` | 展示指示器、进度、分页或状态反馈。 |
| `PART_ScrollStartEdgeIndicator` | `?` | 展示指示器、进度、分页或状态反馈。 |
| `PART_SelectedItemIndicator` | `?` | 展示指示器、进度、分页或状态反馈。 |
| `PART_TabsContainer` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |

控件专属或内部伪类包括 `Bottom=:bottom`、`Left=:left`、`Right=:right`、`TabPseudoClass.Bottom`、`TabPseudoClass.Left`、`TabPseudoClass.Right`、`TabPseudoClass.Top`、`Top=:top`。这些伪类属于主题 selector 可观察契约，不能在未同步主题和 Gallery 的情况下重命名或删除。

## 事件与命令

TabControl 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。
稳定事件包括 `AddTabRequest`、`CloseTab`、`Closed`、`Closing`、`TabReordering`、`TabReordered`。事件触发顺序属于兼容契约，不能因内部状态重排而改变。
- 类型：`BaseOverflowMenuItem`、`BaseTabControl`、`BaseTabScrollViewer`、`BaseTabStrip`、`CardTabControl`、`CardTabStrip`、`CloseTabRequestEventArgs`、`TabClosedEventArgs`、`TabClosingEventArgs`、`TabControl`、`TabControlOverflowMenuItem`、`TabControlScrollViewer`、`TabItem`、`TabItemData` 等 22 项。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/Navigation/TabControl/Views/TabControlShowCase.axaml:38`

Gallery key：`ExamplesContent` / item `0`

```axaml
<StackPanel Orientation="Vertical" Spacing="20">
    <atom:TabControl Name="TestControl">
        <atom:TabItem Header="标签页 1" Content="标签页内容 1" />
        <atom:TabItem Header="标签页 2" Content="标签页内容 2" />
        <atom:TabItem Header="标签页 3" Content="标签页内容 3" />
    </atom:TabControl>
</StackPanel>
```

### 通过 ItemSource 生成标签项

来源：`controlgallery/AtomUIGallery/ShowCases/Navigation/TabControl/Views/TabControlShowCase.axaml:55`

Gallery key：`ExamplesContent` / item `1`

```axaml
<StackPanel Orientation="Vertical" Spacing="20">
    <atom:TabControl ItemsSource="{Binding TabItemDataSource}">
        <atom:TabControl.ItemTemplate>
            <DataTemplate x:DataType="views:MyTabItemData">
                <Border Padding="10">
                    <TextBlock Text="{Binding Content}" />
                </Border>
            </DataTemplate>
        </atom:TabControl.ItemTemplate>
    </atom:TabControl>
</StackPanel>
```

### 禁用标签

来源：`controlgallery/AtomUIGallery/ShowCases/Navigation/TabControl/Views/TabControlShowCase.axaml:147`

Gallery key：`ExamplesContent` / item `4`

```axaml
<StackPanel Orientation="Vertical" Spacing="20">
    <atom:CardTabControl HeaderStartEdgePadding="">
        <atom:TabItem Header="标签页 1" Content="标签页内容 1" />
        <atom:TabItem Header="标签页 2" IsEnabled="False" Content="标签页内容 2" />
        <atom:TabItem Header="标签页 3" Content="标签页内容 3" />
    </atom:CardTabControl>

    <atom:TabControl>
        <atom:TabItem Header="标签页 1" Content="标签页内容 1" />
        <atom:TabItem Header="标签页 2" IsEnabled="False" Content="标签页内容 2" />
        <atom:TabItem Header="标签页 3" Content="标签页内容 3" />
    </atom:TabControl>
</StackPanel>
```

### 居中显示

来源：`controlgallery/AtomUIGallery/ShowCases/Navigation/TabControl/Views/TabControlShowCase.axaml:170`

Gallery key：`ExamplesContent` / item `5`

```axaml
<StackPanel Orientation="Vertical" Spacing="20">
    <atom:TabControl TabAlignmentCenter="True">
        <atom:TabItem Header="标签页 1" Content="标签页内容 1" />
        <atom:TabItem Header="标签页 2" Content="标签页内容 2" />
        <atom:TabItem Header="标签页 3" Content="标签页内容 3" />
    </atom:TabControl>

    <atom:CardTabControl TabAlignmentCenter="True">
        <atom:TabItem Header="标签页 1" Content="标签页内容 1" />
        <atom:TabItem Header="标签页 2" Content="标签页内容 2" />
        <atom:TabItem Header="标签页 3" Content="标签页内容 3" />
    </atom:CardTabControl>

</StackPanel>
```

## 状态模型

TabControl 的状态流按以下路径收敛：

```text
Public API / inherited command / item source / user input
  -> 控件实例状态
  -> effective state / pseudo-class / template property
  -> ControlTheme selector / presenter / renderer
  -> Gallery 可观察行为
```

状态维护规则：

- Disabled 或不可交互状态优先屏蔽 pointer、keyboard、motion 和提交类反馈。
- selection/checked/active、motion、visual option 状态由控件实例或明确的数据 owner 推导，不能在 template part 之间双向竞争。
- 模板重套用时必须把 public API 对应状态回放到新的 part、伪类和主题变量。
- 集合、弹层、异步、动效或窗口相关状态必须能处理 reset、close、cancel、detach 和 owner 释放。
- Tab 激活触发由 `TabActivationTrigger` 控制，默认值为 `PointerReleased`；按下时只记录候选 Tab，只有鼠标在同一个 Tab 上松开才激活。
- `TabActivationTrigger=PointerPressed` 表达按下立即激活；该模式仍必须通过统一选择入口更新 `SelectedIndex`、`SelectedItem`、内容页、伪类和主题状态。
- `PointerReleased` 模式下，按下 Tab A、移动到 Tab B 或 Tab 外松开不应激活新 Tab；拖动排序进入 active reorder 后，释放事件不得再触发 Tab 激活。
- `IsTabClosable` 是生成 `TabItem` 的模板级默认值；overflow 菜单使用容器最终生效的 `IsClosable`，因此控件级默认、单项覆盖和 overflow 呈现必须保持同一语义。
- 拖动排序开启后，排序结果必须提交到 `ItemsSource` 或 `Items` 的逻辑集合顺序；拖动过程采用 Chrome 式轨道内实时让位预览，被拖 Tab 只沿 Tab 轨道主轴移动并覆盖在兄弟 Tab 上方，其他 Tab 通过临时 transform 让出目标位置，不能直接把 `ItemsPresenter.Panel.Children` 当作排序数据源。
- `TabStripPlacement=Top/Bottom` 时主轴为 X 轴，被拖 Tab 的 Y 位移必须保持为 0；`TabStripPlacement=Left/Right` 时主轴为 Y 轴，被拖 Tab 的 X 位移必须保持为 0。目标位置由被拖 Tab 的前进边缘跨过被覆盖兄弟 Tab 主轴中线决定：向后拖动使用 trailing edge，向前拖动使用 leading edge，相当于覆盖兄弟 Tab 约一半宽度或高度即触发让位，而不是等待被拖 Tab 视觉中心跨过兄弟中心。
- overflow 菜单项是对应 `TabItem` 的临时替代呈现，不拥有独立的关闭语义；其 `IsClosable` 必须复制源 Tab 的有效值，关闭请求必须回到 `BaseTabControl.CloseTab` 统一处理。

## 主题与 Design Token

TabControl 的视觉模型由控件模板、ControlTheme、SharedToken 和必要的控件 Token 共同构成。

| 主题文件 | 职责 |
| --- | --- |
| `BaseOverflowMenuItemTheme.axaml` | 定义集合项、容器项或局部单元的状态视觉。 |
| `BaseTabControlTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `BaseTabItemTheme.axaml` | 定义集合项、容器项或局部单元的状态视觉。 |
| `BaseTabScrollViewerTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `CardTabControlTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `CardTabItemTheme.axaml` | 定义集合项、容器项或局部单元的状态视觉。 |
| `TabControlTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `TabItemTheme.axaml` | 定义集合项、容器项或局部单元的状态视觉。 |
| `BaseTabStripItemTheme.axaml` | 定义集合项、容器项或局部单元的状态视觉。 |
| `BaseTabStripTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `CardTabStripItemTheme.axaml` | 定义集合项、容器项或局部单元的状态视觉。 |
| `CardTabStripTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `TabStripItemTheme.axaml` | 定义集合项、容器项或局部单元的状态视觉。 |
| `TabStripTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |

TabControl 使用 `TabControlToken` 作为控件 Token scope。Token 只表达组件视觉语义，不承载 selection/checked/active、motion、visual option 运行时状态。

主题维护规则：

- 不删除或重命名已经稳定的 ControlTheme key、template part、伪类和资源 key。
- 不把可由 AXAML 表达的模板状态迁移为 C# 动态创建视觉。
- 不把 hover、pressed、selected、expanded、loading、filter、popup open 等运行时状态写入 Token。
- `BaseOverflowMenuItemTheme` 必须根据 `IsClosable` 控制 `PART_ItemCloseButton` 的可见性：不可关闭项隐藏关闭按钮，可关闭项显示关闭按钮；该规则对 `TabControl`、`CardTabControl` 及其对应 overflow item 统一生效。
- Browser 或平台特化主题必须保持同一 API 的语义一致。

Token 来源：

TabControl Token 只表达组件级视觉变量，例如尺寸、间距、颜色、圆角、阴影、图标尺寸和弹层边界。Token 不承载运行时选择、展开、加载、错误、上传任务、过滤条件或业务状态。

当前 Token scope：

- `TabControlToken`，scope id 为 `TabControl`，源码位于 `src/AtomUI.Desktop.Controls/TabControl/TabControlToken.cs`。

## AOT 与裁剪注意事项

资源和 AOT 约束：

- 不通过运行时反射扫描 public API、Token 或 Gallery 示例数据。
- 不把可静态声明的模板结构迁移到 C# 动态创建。
- 异步加载、上传、弹层和窗口生命周期必须能取消或释放。
- 缓存对象必须与控件、窗口、弹层或数据 owner 生命周期一致。
- Source generator 生成文件不手工编辑；需要修改时改输入源或 generator。

性能边界：

- 控件应优先复用 Avalonia 原生虚拟化、模板绑定和资源系统。
- 避免为每次状态变化创建不必要的视觉对象、订阅或动画对象。
- 大集合控件必须保证 container recycle 后不会泄漏旧 item 状态。
- 拖动 move 帧内只更新轻量 transform、目标 index 和自动滚动请求；主轴约束和目标 index 计算必须是纯几何计算，不得在 pointer move 中反复移动集合、重建 item 容器或重新应用模板。
- 拖动预览 transform、绘制层级、计时器和订阅应按交互会话缓存并在会话结束释放；不得因一次拖动永久保留视觉对象或数据 item。
- 拖动排序不得引入运行时反射、动态类型扫描或 AOT 不友好的事件发现路径。
- 图标槽对齐不得为无图标 Tab 创建额外图标控件、动态占位对象或 C# 运行时模板分支；应复用静态 AXAML 槽位、现有资源绑定和内部布尔状态，避免增加模板实例化和 container recycle 成本。

## 源码索引

主要源码文件：

- `src/AtomUI.Desktop.Controls/TabControl`：14 个文件，代表文件 `BaseOverflowMenuItem.cs`、`BaseTabControl.cs`、`BaseTabScrollViewer.cs`、`CardTabControl.cs`、`ScrollContentPresenterReflectionExtensions.cs` 等。
- `src/AtomUI.Desktop.Controls/TabControl/TabStrip`：6 个文件，代表文件 `BaseTabStrip.cs`、`CardTabStrip.cs`、`TabStrip.cs`、`TabStripItem.cs`、`TabStripOverflowMenuItem.cs` 等。
- `src/AtomUI.Desktop.Controls/TabControl/Themes`：19 个文件，代表文件 `BaseOverflowMenuItemTheme.axaml`、`BaseTabControlTheme.axaml`、`BaseTabControlTheme.cs`、`BaseTabItemTheme.axaml`、`BaseTabItemTheme.cs` 等。

职责边界：

- 控件主文件保留 public/protected API、Avalonia 属性注册、事件和主要生命周期入口。
- Theme 文件负责静态视觉结构、template part、selector 和资源绑定。
- Token 文件只提供组件视觉变量，不保存实例状态。
- Gallery 文件只展示用法和示例，不作为运行时逻辑 owner。
- Tab 拖动排序属于 TabControl 家族的集合与选择协作路径；实现应落在 `BaseTabControl`、Tab item 容器、滚动视口和内部拖动协作对象之间，不能把排序状态散落到 Gallery、theme 或业务数据对象中。
- overflow 菜单属于滚动视口创建的临时呈现层；`BaseOverflowMenuItem` 只承载菜单视觉和请求转发，`BaseTabControl` 仍是关闭状态、事件和集合变更的唯一 owner。
- 垂直页签图标对齐属于 TabControl 家族的 owner 级布局状态；`Left` / `Right` placement 下由 owner 统一判断同组是否存在图标，再把内部保留图标槽状态投射到 item container，不能通过 Gallery 手工补空图标或新增 public API。
- 默认 Line Tab 的 `Left` / `Right` placement 应保持紧凑的垂直节奏，减少无意义高度浪费；相邻间距和 item 自身垂直 padding 都应按 Line 紧凑模型处理。Card Tab 使用独立 `CardGutter` 和 Card padding 视觉节奏，本规则不得改变 Card 外观。

## 相关文档

- 源设计文档：`docs/controls/desktop/navigation/tab-control/overview.md`
- 实现文档：`docs/controls/desktop/navigation/tab-control/implementation.md`
- Token 文档：`docs/controls/desktop/navigation/tab-control/token.md`
- 变更记录：`docs/controls/desktop/navigation/tab-control/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/tab-strip/index-cn.md

# TabStrip

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

TabStrip 是 AtomUI 桌面控件体系中的标签条控件，用于展示、选择、关闭和拖动排序页签，不承载内容页。

TabStrip 不负责完整 TabControl 内容容器或主导航菜单。这些职责应由业务层、组合控件或更专用的 AtomUI 控件承担。

主要源码入口：

- `src/AtomUI.Desktop.Controls/TabControl/TabStrip`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/Navigation/TabStrip` |
| 状态 | Stable |

## 何时使用

TabStrip 的设计语言围绕控件职责、可观察状态和主题契约组织，而不是围绕模板节点组织。

| 维度 | 含义 | TabStrip 中的表达 |
| --- | --- | --- |
| 产品语义 | 控件在界面中承担的稳定职责。 | TabStrip 是 AtomUI 桌面控件体系中的标签条控件，用于展示、选择、关闭和拖动排序页签，不承载内容页。 |
| 内容承载 | 用户数据、展示内容、集合项或操作入口如何进入控件。 | `CloseIcon`、`HeaderEndEdgePadding`、`HeaderEndExtraContent`、`HeaderEndExtraContentTemplate`、`HeaderStartEdgePadding`、`HeaderStartExtraContent`、`HeaderStartExtraContentTemplate`、`Icon`。 |
| 状态反馈 | public API、内部状态和伪类如何形成用户可感知反馈。 | selection/checked/active、reorder、motion、visual option。 |
| 主题语义 | ControlTheme、SharedToken、控件 Token 和模板绑定如何表达视觉。 | SharedToken / 关联控件 Token + ControlTheme。 |

## 公共 API

TabStrip 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。

核心 public surface 按语义分组维护：

| 契约组 | 代表成员 | 维护含义 |
| --- | --- | --- |
| 内容与数据 | `CloseIcon`、`HeaderEndEdgePadding`、`HeaderEndExtraContent`、`HeaderEndExtraContentTemplate`、`HeaderStartEdgePadding`、`HeaderStartExtraContent`、`HeaderStartExtraContentTemplate`、`Icon` | 定义控件展示内容、输入数据、模板或业务对象入口。 |
| 选择与集合 | `IsTabReorderEnabled`、`TabActivationTrigger`、`SelectedIndex`、`SelectedItem`、`ItemsSource` | 维护页签选择触发时机、集合顺序和拖动排序状态。 |
| 交互与状态 | `IsAutoHideCloseButton`、`IsClosable`、`IsMotionEnabled`、`IsShowAddTabButton`、`IsTabAutoHideCloseButton`、`IsTabClosable` | 表达用户可观察状态、可用性、清除、加载或反馈语义。 |
| 视觉与布局 | `SizeType`、`TabAlignmentCenter`、`TabStripPlacement` | 影响尺寸、位置、颜色、形状、密度和模板视觉变量。 |

稳定事件包括 `AddTabRequest`、`Closed`、`Closing`、`TabReordering`、`TabReordered`。事件触发顺序属于兼容契约，不能因内部状态重排而改变。

主要公开类型与枚举：

- 类型：`BaseTabStrip`、`CardTabStrip`、`TabStrip`、`TabStripClosedEventArgs`、`TabStripClosingEventArgs`、`TabStripItem`、`TabStripOverflowMenuItem`、`TabStripScrollViewer`。
- 枚举：`TabActivationTrigger`、`TabSharp`。

稳定 template part：

当前控件没有显式 `[TemplatePart]` 契约；主题节点仍通过 ControlTheme key、资源 key 和 Gallery 可观察行为形成稳定边界。

控件专属或内部伪类包括 `TabPseudoClass.Bottom`、`TabPseudoClass.Left`、`TabPseudoClass.Right`、`TabPseudoClass.Top`。这些伪类属于主题 selector 可观察契约，不能在未同步主题和 Gallery 的情况下重命名或删除。

## 事件与命令

TabStrip 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。
稳定事件包括 `AddTabRequest`、`Closed`、`Closing`、`TabReordering`、`TabReordered`。事件触发顺序属于兼容契约，不能因内部状态重排而改变。
- 类型：`BaseTabStrip`、`CardTabStrip`、`TabStrip`、`TabStripClosedEventArgs`、`TabStripClosingEventArgs`、`TabStripItem`、`TabStripOverflowMenuItem`、`TabStripScrollViewer`。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/Navigation/TabStrip/Views/TabStripShowCase.axaml:37`

Gallery key：`ExamplesContent` / item `0`

```axaml
<StackPanel Orientation="Vertical" Spacing="20">
    <atom:TabStrip>
        <atom:TabStripItem IsClosable="True" Content="标签页 1" />
        <atom:TabStripItem IsClosable="True" Content="标签页 2" />
        <atom:TabStripItem IsClosable="True" Content="标签页 3" />
    </atom:TabStrip>
</StackPanel>
```

### 通过 ItemSource 生成 TabStripItem

来源：`controlgallery/AtomUIGallery/ShowCases/Navigation/TabStrip/Views/TabStripShowCase.axaml:54`

Gallery key：`ExamplesContent` / item `1`

```axaml
<StackPanel Orientation="Vertical" Spacing="20">
    <atom:TabStrip ItemsSource="{Binding TabStripItemDataSource}">
        <atom:TabStrip.ItemTemplate>
            <DataTemplate x:DataType="atom:TabItemData">
                <TextBlock Text="{Binding Header}" />
            </DataTemplate>
        </atom:TabStrip.ItemTemplate>
    </atom:TabStrip>
</StackPanel>
```

### 禁用标签

来源：`controlgallery/AtomUIGallery/ShowCases/Navigation/TabStrip/Views/TabStripShowCase.axaml:163`

Gallery key：`ExamplesContent` / item `4`

```axaml
<StackPanel Orientation="Vertical" Spacing="20">
    <atom:CardTabStrip>
        <atom:TabStripItem Content="标签页 1" />
        <atom:TabStripItem IsEnabled="False" IsClosable="True" Content="标签页 2" />
        <atom:TabStripItem Content="标签页 3" />
    </atom:CardTabStrip>

    <atom:TabStrip>
        <atom:TabStripItem Content="标签页 1" />
        <atom:TabStripItem IsEnabled="False" Content="标签页 2" />
        <atom:TabStripItem Content="标签页 3" />
    </atom:TabStrip>
</StackPanel>
```

### 居中显示

来源：`controlgallery/AtomUIGallery/ShowCases/Navigation/TabStrip/Views/TabStripShowCase.axaml:186`

Gallery key：`ExamplesContent` / item `5`

```axaml
<StackPanel Orientation="Vertical" Spacing="20">
    <atom:TabStrip TabAlignmentCenter="True">
        <atom:TabStripItem Content="标签页 1" />
        <atom:TabStripItem Content="标签页 2" />
        <atom:TabStripItem Content="标签页 3" />
    </atom:TabStrip>

    <atom:CardTabStrip TabAlignmentCenter="True">
        <atom:TabStripItem Content="标签页 1" />
        <atom:TabStripItem Content="标签页 2" />
        <atom:TabStripItem Content="标签页 3" />
    </atom:CardTabStrip>
</StackPanel>
```

## 状态模型

TabStrip 的状态流按以下路径收敛：

```text
Public API / inherited command / item source / user input
  -> 控件实例状态
  -> effective state / pseudo-class / template property
  -> ControlTheme selector / presenter / renderer
  -> Gallery 可观察行为
```

状态维护规则：

- Disabled 或不可交互状态优先屏蔽 pointer、keyboard、motion 和提交类反馈。
- selection/checked/active、motion、visual option 状态由控件实例或明确的数据 owner 推导，不能在 template part 之间双向竞争。
- 模板重套用时必须把 public API 对应状态回放到新的 part、伪类和主题变量。
- 集合、弹层、异步、动效或窗口相关状态必须能处理 reset、close、cancel、detach 和 owner 释放。
- Tab 激活触发由 `TabActivationTrigger` 控制，默认值为 `PointerReleased`；按下时只记录候选 Tab，只有鼠标在同一个 Tab 上松开才激活。
- `TabActivationTrigger=PointerPressed` 表达按下立即激活；该模式仍必须通过统一选择入口更新 `SelectedIndex`、`SelectedItem`、伪类和主题状态。
- `PointerReleased` 模式下，按下 Tab A、移动到 Tab B 或 Tab 外松开不应激活新 Tab；拖动排序进入 active reorder 后，释放事件不得再触发 Tab 激活。
- `IsTabClosable` 是生成 `TabStripItem` 的模板级默认值；overflow 菜单使用容器最终生效的 `IsClosable`，因此控件级默认、单项覆盖和 overflow 呈现必须保持同一语义。
- 拖动排序开启后，排序结果必须提交到 `ItemsSource` 或 `Items` 的逻辑集合顺序；拖动过程采用 Chrome 式轨道内实时让位预览，被拖 Tab 只沿 Tab 轨道主轴移动并覆盖在兄弟 Tab 上方，其他 Tab 通过临时 transform 让出目标位置，不能直接把 `ItemsPresenter.Panel.Children` 当作排序数据源。
- `TabStripPlacement=Top/Bottom` 时主轴为 X 轴，被拖 Tab 的 Y 位移必须保持为 0；`TabStripPlacement=Left/Right` 时主轴为 Y 轴，被拖 Tab 的 X 位移必须保持为 0。目标位置由被拖 Tab 的前进边缘跨过被覆盖兄弟 Tab 主轴中线决定：向后拖动使用 trailing edge，向前拖动使用 leading edge，相当于覆盖兄弟 Tab 约一半宽度或高度即触发让位，而不是由 pointer 的非主轴偏移决定。
- overflow 菜单项是对应 `TabStripItem` 的临时替代呈现，不拥有独立的关闭语义；其 `IsClosable` 必须复制源 Tab 的有效值，关闭请求必须回到 `BaseTabStrip.CloseTab` 统一处理。

## 主题与 Design Token

TabStrip 的视觉模型由控件模板、ControlTheme、SharedToken 和必要的控件 Token 共同构成。

当前控件未抽取到专属 AXAML 主题文件；视觉契约主要来自继承控件、共享主题和资源 key。

TabStrip 当前没有专属 Token 文档；主题通过 SharedToken、关联控件 Token 或继承主题资源表达视觉语义。运行时状态不得写入 Token 模型。

主题维护规则：

- 不删除或重命名已经稳定的 ControlTheme key、template part、伪类和资源 key。
- 不把可由 AXAML 表达的模板状态迁移为 C# 动态创建视觉。
- 不把 hover、pressed、selected、expanded、loading、filter、popup open 等运行时状态写入 Token。
- `BaseOverflowMenuItemTheme` 必须根据 `IsClosable` 控制 `PART_ItemCloseButton` 的可见性：不可关闭项隐藏关闭按钮，可关闭项显示关闭按钮；该规则对 `TabStrip`、`CardTabStrip` 及其对应 overflow item 统一生效。
- Browser 或平台特化主题必须保持同一 API 的语义一致。

Token 来源：

- TabStrip 当前没有专属 `token.md`；LLMS 生成按第 5 节视觉与主题模型、SharedToken、控件家族 Token 或主题资源说明 Token 边界。

## AOT 与裁剪注意事项

资源和 AOT 约束：

- 不通过运行时反射扫描 public API、Token 或 Gallery 示例数据。
- 不把可静态声明的模板结构迁移到 C# 动态创建。
- 异步加载、上传、弹层和窗口生命周期必须能取消或释放。
- 缓存对象必须与控件、窗口、弹层或数据 owner 生命周期一致。
- Source generator 生成文件不手工编辑；需要修改时改输入源或 generator。

性能边界：

- 控件应优先复用 Avalonia 原生虚拟化、模板绑定和资源系统。
- 避免为每次状态变化创建不必要的视觉对象、订阅或动画对象。
- 大集合控件必须保证 container recycle 后不会泄漏旧 item 状态。
- 拖动 move 帧内只更新轻量 transform、目标 index 和自动滚动请求；主轴约束和目标 index 计算必须是纯几何计算，不得在 pointer move 中反复移动集合、重建 item 容器或重新应用模板。
- 拖动预览 transform、绘制层级、计时器和订阅应按交互会话缓存并在会话结束释放；不得因一次拖动永久保留视觉对象或数据 item。
- 拖动排序不得引入运行时反射、动态类型扫描或 AOT 不友好的事件发现路径。
- 图标槽对齐不得为无图标 Tab 创建额外图标控件、动态占位对象或 C# 运行时模板分支；应复用静态 AXAML 槽位、现有资源绑定和内部布尔状态，避免增加模板实例化和 container recycle 成本。

## 源码索引

主要源码文件：

- `src/AtomUI.Desktop.Controls/TabControl/TabStrip/BaseTabStrip.cs`
- `src/AtomUI.Desktop.Controls/TabControl/TabStrip/CardTabStrip.cs`
- `src/AtomUI.Desktop.Controls/TabControl/TabStrip/TabStrip.cs`
- `src/AtomUI.Desktop.Controls/TabControl/TabStrip/TabStripItem.cs`
- `src/AtomUI.Desktop.Controls/TabControl/TabStrip/TabStripOverflowMenuItem.cs`
- `src/AtomUI.Desktop.Controls/TabControl/TabStrip/TabStripScrollViewer.cs`

职责边界：

- 控件主文件保留 public/protected API、Avalonia 属性注册、事件和主要生命周期入口。
- Theme 文件负责静态视觉结构、template part、selector 和资源绑定。
- Token 文件只提供组件视觉变量，不保存实例状态。
- Gallery 文件只展示用法和示例，不作为运行时逻辑 owner。
- Tab 拖动排序属于 `BaseTabStrip` 的集合与选择协作路径；实现应落在 `BaseTabStrip`、`TabStripItem`、滚动视口和内部拖动协作对象之间，不能把排序状态散落到 Gallery、theme 或业务数据对象中。
- overflow 菜单属于滚动视口创建的临时呈现层；`BaseOverflowMenuItem` 只承载菜单视觉和请求转发，`BaseTabStrip` 仍是关闭状态、事件和集合变更的唯一 owner。
- 垂直页签图标对齐属于 `BaseTabStrip` 的 owner 级布局状态；`Left` / `Right` placement 下由 owner 统一判断同组是否存在图标，再把内部保留图标槽状态投射到 `TabStripItem`，不能通过 Gallery 手工补空图标或新增 public API。
- 默认 Line TabStrip 的 `Left` / `Right` placement 应保持紧凑的垂直节奏；相邻间距和 item 自身垂直 padding 都应按 Line 紧凑模型处理。Card TabStrip 使用独立 `CardGutter` 和 Card padding 视觉节奏，本规则不得改变 Card 外观。

## 相关文档

- 源设计文档：`docs/controls/desktop/navigation/tab-strip/overview.md`
- 实现文档：`docs/controls/desktop/navigation/tab-strip/implementation.md`
- 变更记录：`docs/controls/desktop/navigation/tab-strip/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/auto-complete/index-cn.md

# AutoComplete

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

AutoComplete 是 AtomUI 桌面控件体系中的自动完成输入控件，用于把文本输入、候选弹层、过滤和提交事件组合在一起。

AutoComplete 不负责普通 Select、远程搜索协议或业务结果列表。这些职责应由业务层、组合控件或更专用的 AtomUI 控件承担。

主要源码入口：

- `src/AtomUI.Desktop.Controls/AutoComplete`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/DataEntry/AutoComplete` |
| 状态 | Stable |

## 何时使用

AutoComplete 的设计语言围绕控件职责、可观察状态和主题契约组织，而不是围绕模板节点组织。

| 维度 | 含义 | AutoComplete 中的表达 |
| --- | --- | --- |
| 产品语义 | 控件在界面中承担的稳定职责。 | AutoComplete 是 AtomUI 桌面控件体系中的自动完成输入控件，用于把文本输入、候选弹层、过滤和提交事件组合在一起。 |
| 内容承载 | 用户数据、展示内容、集合项或操作入口如何进入控件。 | `ClearIcon`、`ContentLeftAddOn`、`ContentLeftAddOnTemplate`、`ContentRightAddOn`、`ContentRightAddOnTemplate`、`DefaultValue`、`FilterValue`、`FilterValueSelector` 等 14 项。 |
| 状态反馈 | public API、内部状态和伪类如何形成用户可感知反馈。 | selection/checked/active、open/close、loading/async、collection/filter、input/value、motion、visual option。 |
| 主题语义 | ControlTheme、SharedToken、控件 Token 和模板绑定如何表达视觉。 | AutoComplete Token + ControlTheme。 |

## 公共 API

AutoComplete 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。

核心 public surface 按语义分组维护：

| 契约组 | 代表成员 | 维护含义 |
| --- | --- | --- |
| 内容与数据 | `ClearIcon`、`ContentLeftAddOn`、`ContentLeftAddOnTemplate`、`ContentRightAddOn`、`ContentRightAddOnTemplate`、`DefaultValue`、`FilterValue`、`FilterValueSelector`、`OptionTemplate`、`OptionsAsyncLoader` 等 14 项 | 定义控件展示内容、输入数据、模板或业务对象入口。 |
| 选择与集合 | `CaretIndex`、`ClearSelectionOnLostFocus`、`DisplayCandidateCount`、`Filter`、`IsShowCount` | 维护选择、展开、过滤、分页、分组或集合状态。 |
| 交互与状态 | `IsAllowClear`、`IsAutoFocus`、`IsAutoSize`、`IsCompletionEnabled`、`IsDropDownOpen`、`IsLoading`、`IsMotionEnabled`、`IsOperating`、`IsSearchOnEnterEnabled`、`IsPopupMatchSelectWidth`、`IsReadOnly` 等 14 项 | 表达用户可观察状态、可用性、清除、加载或反馈语义。 |
| 视觉与布局 | `MaxDropDownHeight`、`PlaceholderForeground`、`Placement`、`SearchButtonStyle`、`SizeType`、`StyleVariant` | 影响尺寸、位置、颜色、形状、密度和模板视觉变量。 |
| 动效与异步 | `AsyncLoadDebounce`、`AsyncLoadTimeout` | 约束动效开关、异步加载、播放速度、超时和任务边界。 |
| 其他稳定入口 | `Lines`、`MaxLength`、`MinimumPrefixLength` | 保留为 public surface，变更前需确认 Gallery 和用户 XAML 依赖。 |

稳定事件包括 `SelectionChanged`、`ValueChanged`；`AutoCompleteSearchEdit` 还通过内部 SearchEdit box 转发 `SearchRequested`。事件触发顺序属于兼容契约，不能因内部状态重排而改变。

主要公开类型与枚举：

- 类型：`AbstractAutoComplete`、`AutoComplete`、`AutoCompleteLineEditBox`、`AutoCompleteSearchEdit`、`AutoCompleteSearchEditBox`、`AutoCompleteTextArea`、`AutoCompleteTextAreaBox`、`CompactSpaceAwareAutoComplete`、`CompleteOptionsLoadResult`、`CompleteOptionsLoadedEventArgs`、`CompletePopulatedEventArgs`、`CompletePopulatingEventArgs`、`CompleteValueChangedEventArgs`。
- 枚举：`AutoCompletePlacementMode`。

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_Popup` | `?` | 承载弹层宿主、打开关闭或候选内容。 |

控件专属或内部伪类包括 `AutoCompletePseudoClass.CandidatePopupOpen`、`CandidatePopupOpen=:candidateopen`。这些伪类属于主题 selector 可观察契约，不能在未同步主题和 Gallery 的情况下重命名或删除。

## 事件与命令

AutoComplete 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。
稳定事件包括 `SelectionChanged`、`ValueChanged`；`AutoCompleteSearchEdit` 还通过内部 SearchEdit box 转发 `SearchRequested`。事件触发顺序属于兼容契约，不能因内部状态重排而改变。
- 类型：`AbstractAutoComplete`、`AutoComplete`、`AutoCompleteLineEditBox`、`AutoCompleteSearchEdit`、`AutoCompleteSearchEditBox`、`AutoCompleteTextArea`、`AutoCompleteTextAreaBox`、`CompactSpaceAwareAutoComplete`、`CompleteOptionsLoadResult`、`CompleteOptionsLoadedEventArgs`、`CompletePopulatedEventArgs`、`CompletePopulatingEventArgs`、`CompleteValueChangedEventArgs`。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/AutoComplete/Views/AutoCompleteShowCase.axaml:35`

Gallery key：`ExamplesContent` / item `0`

```axaml
<atom:AutoComplete Name="BasicAutoComplete"
```

### 自定义

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/AutoComplete/Views/AutoCompleteShowCase.axaml:48`

Gallery key：`ExamplesContent` / item `1`

```axaml
<atom:AutoComplete Name="CustomizedAutoComplete"
```

### 自定义选项渲染

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/AutoComplete/Views/AutoCompleteShowCase.axaml:61`

Gallery key：`ExamplesContent` / item `2`

```axaml
<atom:AutoComplete Name="CityAutoComplete"
                   OptionsSource="{Binding CityOptions}"
                   PlaceholderText="try 'a' or 'b'"
                   Width="320"
                   IsAllowClear="True"
                   Filter="{atom:ValueFilterProvider Contains}">
    <atom:AutoComplete.OptionTemplate>
        <DataTemplate x:DataType="vm:CityAutoCompleteOption">
            <Grid ColumnDefinitions="*,Auto" RowDefinitions="Auto,Auto" RowSpacing="2">
                <TextBlock Grid.Row="0" Grid.Column="0"
                           Text="{Binding Header}"
                           FontWeight="SemiBold"
                           TextTrimming="CharacterEllipsis" />
                <Border Grid.Row="0" Grid.Column="1"
                        Padding="6 0"
                        VerticalAlignment="Center"
                        Background="{atom:SharedTokenResource ColorFillSecondary}"
                        CornerRadius="{atom:SharedTokenResource BorderRadiusXS}">
                    <TextBlock Text="{Binding Country}"
                               FontSize="{atom:SharedTokenResource FontSizeSM}" />
                </Border>
                <TextBlock Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2"
                           Text="{Binding Population, StringFormat='Population {0:N0}'}"
                           FontSize="{atom:SharedTokenResource FontSizeSM}"
                           Foreground="{atom:SharedTokenResource ColorTextTertiary}" />
            </Grid>
        </DataTemplate>
    </atom:AutoComplete.OptionTemplate>
</atom:AutoComplete>
```

### SizeType

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/AutoComplete/Views/AutoCompleteShowCase.axaml:99`

Gallery key：`ExamplesContent` / item `3`

```axaml
<StackPanel Spacing="10">
    <atom:AutoComplete Width="220"
                       Name="LargeAutoComplete"
                       OptionsAsyncLoader="{Binding BasicOptionsAsyncLoader}"
                       SizeType="Large"
                       PlaceholderText="SizeType：Large" />
    <atom:AutoComplete Width="220"
                       Name="MiddleAutoComplete"
                       OptionsAsyncLoader="{Binding BasicOptionsAsyncLoader}"
                       SizeType="Middle"
                       PlaceholderText="SizeType：Middle" />
    <atom:AutoComplete Width="220"
                       Name="SmallAutoComplete"
                       OptionsAsyncLoader="{Binding BasicOptionsAsyncLoader}"
                       SizeType="Small"
                       PlaceholderText="SizeType：Small" />
    <atom:AutoComplete Width="220"
                       Name="CustomAutoComplete"
                       OptionsAsyncLoader="{Binding BasicOptionsAsyncLoader}"
                       SizeType="Custom"
                       Height="36"
                       PlaceholderText="SizeType：Custom" />
</StackPanel>
```

## 状态模型

AutoComplete 的状态流按以下路径收敛：

```text
Public API / inherited command / item source / user input
  -> 控件实例状态
  -> effective state / pseudo-class / template property
  -> ControlTheme selector / presenter / renderer
  -> Gallery 可观察行为
```

状态维护规则：

- Disabled 或不可交互状态优先屏蔽 pointer、keyboard、motion 和提交类反馈。
- selection/checked/active、open/close、loading/async、collection/filter、input/value、motion、visual option 状态由控件实例或明确的数据 owner 推导，不能在 template part 之间双向竞争。
- 模板重套用时必须把 public API 对应状态回放到新的 part、伪类和主题变量。
- 集合、弹层、异步、动效或窗口相关状态必须能处理 reset、close、cancel、detach 和 owner 释放。

## 主题与 Design Token

AutoComplete 的视觉模型由控件模板、ControlTheme、SharedToken 和必要的控件 Token 共同构成。

`AutoCompleteLineEditBox`、`AutoCompleteSearchEditBox` 和 `AutoCompleteTextAreaBox` 分别复用 `LineEdit`、`SearchEdit` 和 `TextArea` 的 `AbstractTextInput` 逻辑层与 `InputControlFrame` 输入表面。候选 popup 和过滤状态属于 AutoComplete 自身，不得重新声明输入边框、状态或 Form error owner。

| 主题文件 | 职责 |
| --- | --- |
| `AbstractAutoCompleteTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `AutoCompleteSearchEditTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `AutoCompleteTextAreaTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `AutoCompleteTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |

AutoComplete 使用 `AutoCompleteToken` 作为控件 Token scope。Token 只表达组件视觉语义，不承载 selection/checked/active、open/close、loading/async、collection/filter、input/value、motion、visual option 运行时状态。

主题维护规则：

- 不删除或重命名已经稳定的 ControlTheme key、template part、伪类和资源 key。
- 不把可由 AXAML 表达的模板状态迁移为 C# 动态创建视觉。
- 不把 hover、pressed、selected、expanded、loading、filter、popup open 等运行时状态写入 Token。
- Browser 或平台特化主题必须保持同一 API 的语义一致。

Token 来源：

AutoComplete Token 只表达组件级视觉变量，例如尺寸、间距、颜色、圆角、阴影、图标尺寸和弹层边界。Token 不承载运行时选择、展开、加载、错误、上传任务、过滤条件或业务状态。

当前 Token scope：

- `AutoCompleteToken`，scope id 为 `AutoComplete`，源码位于 `src/AtomUI.Desktop.Controls/AutoComplete/AutoCompleteToken.cs`。

## AOT 与裁剪注意事项

资源和 AOT 约束：

- 不通过运行时反射扫描 public API、Token 或 Gallery 示例数据。
- 不把可静态声明的模板结构迁移到 C# 动态创建。
- 异步加载、上传、弹层和窗口生命周期必须能取消或释放。
- 缓存对象必须与控件、窗口、弹层或数据 owner 生命周期一致。
- Source generator 生成文件不手工编辑；需要修改时改输入源或 generator。

性能边界：

- 控件应优先复用 Avalonia 原生虚拟化、模板绑定和资源系统。
- 避免为每次状态变化创建不必要的视觉对象、订阅或动画对象。
- 大集合控件必须保证 container recycle 后不会泄漏旧 item 状态。

## 源码索引

主要源码文件：

- `src/AtomUI.Desktop.Controls/AutoComplete/AbstractAutoComplete.cs`
- `src/AtomUI.Desktop.Controls/AutoComplete/AutoComplete.cs`
- `src/AtomUI.Desktop.Controls/AutoComplete/AutoCompleteLineEdit.cs`
- `src/AtomUI.Desktop.Controls/AutoComplete/AutoCompleteOption.cs`
- `src/AtomUI.Desktop.Controls/AutoComplete/AutoCompletePseudoClass.cs`
- `src/AtomUI.Desktop.Controls/AutoComplete/AutoCompleteSearchEdit.cs`
- `src/AtomUI.Desktop.Controls/AutoComplete/AutoCompleteSearchEditBox.cs`
- `src/AtomUI.Desktop.Controls/AutoComplete/AutoCompleteTextArea.cs`
- `src/AtomUI.Desktop.Controls/AutoComplete/AutoCompleteTextAreaBox.cs`
- `src/AtomUI.Desktop.Controls/AutoComplete/AutoCompleteThemeConstants.cs`
- `src/AtomUI.Desktop.Controls/AutoComplete/AutoCompleteToken.cs`
- `src/AtomUI.Desktop.Controls/AutoComplete/CompactSpaceAwareAutoComplete.cs`
- `src/AtomUI.Desktop.Controls/AutoComplete/CompletePopulatedEventArgs.cs`
- `src/AtomUI.Desktop.Controls/AutoComplete/CompletePopulatingEventArgs.cs`
- `src/AtomUI.Desktop.Controls/AutoComplete/CompleteValueChangedEventArgs.cs`
- `src/AtomUI.Desktop.Controls/AutoComplete/DataLoad/CompleteOptionsLoadResult.cs`
- `src/AtomUI.Desktop.Controls/AutoComplete/DataLoad/CompleteOptionsLoadedEventArgs.cs`
- `src/AtomUI.Desktop.Controls/AutoComplete/DataLoad/ICompleteOptionsAsyncLoader.cs`
- `src/AtomUI.Desktop.Controls/AutoComplete/IAutoCompleteOption.cs`
- `src/AtomUI.Desktop.Controls/AutoComplete/Themes/AbstractAutoCompleteTheme.axaml`
- `src/AtomUI.Desktop.Controls/AutoComplete/Themes/AbstractAutoCompleteTheme.cs`
- `src/AtomUI.Desktop.Controls/AutoComplete/Themes/AutoCompleteSearchEditTheme.axaml`
- `src/AtomUI.Desktop.Controls/AutoComplete/Themes/AutoCompleteTextAreaTheme.axaml`
- `src/AtomUI.Desktop.Controls/AutoComplete/Themes/AutoCompleteTheme.axaml`

职责边界：

- 控件主文件保留 public/protected API、Avalonia 属性注册、事件和主要生命周期入口。
- Theme 文件负责静态视觉结构、template part、selector 和资源绑定。
- Token 文件只提供组件视觉变量，不保存实例状态。
- Gallery 文件只展示用法和示例，不作为运行时逻辑 owner。

## 相关文档

- 源设计文档：`docs/controls/desktop/data-entry/auto-complete/overview.md`
- 实现文档：`docs/controls/desktop/data-entry/auto-complete/implementation.md`
- Token 文档：`docs/controls/desktop/data-entry/auto-complete/token.md`
- 变更记录：`docs/controls/desktop/data-entry/auto-complete/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/cascader/index-cn.md

# Cascader

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

Cascader 是 AtomUI 桌面数据录入体系中的级联选择控件，用于在有明确父子层级的数据中按列逐级展开并选择一个或多个选项。它面向地区、组织、分类、资源路径、权限域等“路径就是业务含义”的选择场景。

Cascader 由外层输入控件和内部级联弹层组成。外层 `Cascader` 负责输入表面、popup、选择结果、清除、Form、CompactSpace 和 AddOn 集成；内部 `CascaderView` 负责级联列、展开收起、过滤结果、异步加载和多选勾选。

Cascader 不负责树节点编辑、拖拽排序、远程协议封装、业务权限过滤、复杂图形化层级展示或任意树操作。这些能力应由业务层、TreeView、TreeSelect 或专用数据控件承担。

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/DataEntry/Cascader` |
| 状态 | Stable |

## 何时使用

Cascader 的设计语言来自输入框、路径选择和多列层级弹层的组合。

| 维度 | 含义 | 典型表达 |
| --- | --- | --- |
| 层级数据 | 选项按 parent / children 组成路径。 | `OptionsSource`、`Options`、`ICascaderOption.Children`。 |
| 逐列展开 | 每一级子选项以独立列展示，同级只保持一个展开分支。 | `CascaderViewLevelList`、`ExpandTrigger`。 |
| 选择模式 | 支持单选和多选勾选。 | `IsMultiple`、`SelectedOption`、`SelectedOptions`。 |
| 勾选展示 | 多选结果可展示全部、父级或叶子节点。 | `ShowCheckedStrategy`、`EffectiveSelectedOptions`。 |
| 父级可选 | 非叶子节点可作为选择结果。 | `IsAllowSelectParent`。 |
| 异步加载 | 展开未加载节点时按节点加载子项。 | `DataLoader`、`LoadingIcon`、内部 `CascaderView.AsyncLoadTimeout`。 |
| 搜索过滤 | 输入过滤值后展示完整路径结果。 | `Filter`、`FilterValue`、`FilterHighlightStrategy`。 |
| 输入表面 | 继承输入家族的尺寸、variant、status、Addon、清除和 Form feedback。 | `SizeType`、`StyleVariant`、`Status`、Addon、`SelectHandle`。 |

`SizeType=Custom` 不是 Cascader 的第四套专属 Token。主题层把 `Custom` 归入 `Middle` 默认字体和 padding 分支；用户显式设置高度等尺寸属性时由 Avalonia 属性优先级决定最终布局。

## 公共 API

Cascader 通过 `OptionsSource` 和 `Options` 接收 `ICascaderOption` 数据。`CascaderOption` 是轻量默认选项模型，适合代码直接构造和普通层级数据。`BindableCascaderOption` 是绑定型选项模型，继承 `AvaloniaObject` 并暴露 DirectProperty，用于选项属性需要承载 XAML binding、`DynamicResource` 或 owner scoped resource 的场景。

### 3.1 数据模型契约

| 类型 | 语义 |
| --- | --- |
| `ICascaderOption` | Cascader 数据节点契约，包含 header、icon、enabled、checked、checkbox enabled、expanded、leaf、value、children 和 parent。 |
| `CascaderOption` | 默认轻量选项模型，不继承 `AvaloniaObject`，保持普通数据对象定位。 |
| `BindableCascaderOption` | 绑定型选项模型，支持 Avalonia binding target 和 scoped resource host 生命周期。 |
| `ICascaderItemDataLoader` | 节点异步加载边界，按目标节点返回子选项集合和加载状态。 |

`ICascaderOption.Header` 是默认显示内容；`ItemKey` 优先作为稳定 identity；`Value` 是 identity 兜底和业务值承载；`Children` 是层级结构入口；`ParentNode` 用于路径回溯、展开路径和显示路径计算。

### 3.2 选项与选择 API

| API | 类型 | 语义 |
| --- | --- | --- |
| `OptionsSource` | `IEnumerable<ICascaderOption>?` | 外部选项集合。变化时同步到内部 `Options`。 |
| `Options` | `ItemCollection` | XAML 内容子项入口，也是 CascaderView 实际数据入口。 |
| `OptionTemplate` | `IDataTemplate?` | 选项显示模板，默认显示 `ICascaderOption.Header` 并继续以 option 为 DataContext。 |
| `SelectedOption` | `ICascaderOption?` | 单选当前选项，默认 `TwoWay` binding，并启用 Avalonia data validation。 |
| `SelectedOptions` | `IList<ICascaderOption>?` | 多选当前选项集合，默认 `TwoWay` binding，并启用 Avalonia data validation。 |
| `DefaultSelectOptionPath` | `TreeNodePath?` | 单选默认路径。路径段按 `ItemKey` 优先、`Value` 兜底匹配。 |
| `SelectedOptionPath` | internal `string?` | 单选显示路径文本，由已选节点 header 路径组成。 |
| `Clear()` | method | 清空单选、多选和显示路径。 |

### 3.3 行为 API

| API | 类型 | 语义 |
| --- | --- | --- |
| `IsMultiple` | `bool` | 是否启用多选勾选。启用后 `CascaderView.IsCheckable=true`。 |
| `ShowCheckedStrategy` | `TreeSelectCheckedStrategy` | 多选 tag 展示策略，不改变真实 `SelectedOptions`。 |
| `MaxCount` | inherited `int` | 多选最大可选数量。达到上限后未选项进入最大数限制状态。 |
| `IsAllowSelectParent` | `bool` | 是否允许非叶子节点作为选择结果。 |
| `ExpandTrigger` | `CascaderViewExpandTrigger` | 级联展开触发方式，默认 `Click`。 |
| `DataLoader` | `ICascaderItemDataLoader?` | 展开未加载节点时的异步数据加载器。 |
| `CascaderView.AsyncLoadTimeout` | internal view API | 内部弹层 view 的异步加载超时边界，不是外层 `Cascader` 当前 public property。 |
| `Filter` | `IValueFilter?` | 路径过滤器；为空时初始化为 contains 过滤。 |
| `FilterValue` | inherited `object?` | 当前过滤输入值。 |
| `FilterHighlightStrategy` | `TextBlockHighlightStrategy` | 过滤命中高亮策略。 |
| `FilterHighlightForeground` | `IBrush?` | 过滤命中高亮前景色。 |

### 3.4 输入表面 API

Cascader 继承 `AbstractSelect` 的输入表面契约：

| API | 语义 |
| --- | --- |
| `SizeType` | 输入尺寸密度，支持 `Large / Middle / Small / Custom`。 |
| `StyleVariant` | 输入表面样式。 |
| `Status` | 手动输入反馈状态；warning 仅在无 native validation error 时驱动 `:warning`，error 视觉优先由 `DataValidationErrors` 驱动。 |
| `PlaceholderText` / `PlaceholderForeground` | 空选择时的占位文本和颜色。 |
| `IsFilterEnabled` | 是否显示过滤输入并将输入同步为 `FilterValue`。 |
| `IsShowOverflowTip` | 单选路径或多选 tag 视觉溢出时是否显示完整内容 tooltip，默认 `true`。 |
| `OverflowTipDelay` | 溢出 tooltip 打开前的延迟时间，单位毫秒，默认 `1200`。 |
| `OverflowTipPlacement` | 溢出 tooltip 相对单选路径或多选 tag 的位置，默认 `TopEdgeAlignedLeft`。 |
| `IsAllowClear` / `SuffixIcon` / `SuffixLoadingIcon` | 清除、展开指示和 loading 指示入口。 |
| `LeftAddOn` / `RightAddOn` / `ContentLeftAddOn` / `ContentRightAddOn` | 外部和内部 AddOn 内容。 |
| `IsDropDownOpen` / `PopupPlacement` / `ShouldUseOverlayPopup` / `IsPopupMatchSelectWidth` | popup 打开、定位、宿主和宽度匹配契约。 |
| `DropDownOpening` / `DropDownOpened` / `DropDownClosing` / `DropDownClosed` | 继承自 `AbstractSelect` 的 popup 生命周期事件。 |

### 3.5 内部 view 事件契约

`CascaderView` 是内部弹层控件，当前事件用于 `Cascader` 和测试协作，不作为外层 Cascader 的主用户事件模型：

| 事件 | 语义 |
| --- | --- |
| `SelectedOptionsChanged` | 内部多选集合变化。 |
| `ItemAsyncLoaded` | 单个节点异步加载完成。 |
| `ItemExpanded` / `ItemCollapsed` | 内部节点展开 / 收起。 |
| `ItemClicked` / `ItemDoubleClicked` | 内部节点点击 / 双击。 |
| `OptionSelected` | 内部单选选项提交。 |

### 3.6 稳定 template part

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_InputControlFrame` | `CascaderAddOnDecoratedBox` | `InputControlFrame` / AddOnDecoratedBox 组合、Addon、EffectiveStatus、CompactSpace、多选状态和 content padding 承载。 |
| `PART_SingleFilterInput` | `SelectFilterTextBox` | 单选过滤输入，过滤启用时显示。 |
| `SelectedOptionsBox` | `SelectTagAwareTextBox` | 多选结果 tag 展示和多选过滤输入承载。 |
| `PART_SelectMaxCountIndicator` | `SelectMaxCountIndicator` | 最大选择数量提示。 |
| `PART_ContentRightAddOnPresenter` | `ContentPresenter` | 用户内部右侧内容承载。 |
| `PART_SelectHandle` | `SelectHandle` | 展开、loading、清除和 Form feedback 图标入口。 |
| `PART_Popup` | `Popup` | 级联弹层宿主。 |
| `PopupFrame` | `Border` | 弹层外壳、最大高度、最小宽度和 popup padding。 |
| `PART_CascaderView` | `CascaderView` | 级联弹层内容、列、过滤和选项交互。 |
| `PART_ItemsPanel` | `StackPanel` | CascaderView 内部横向列容器。 |
| `PART_RootLevelList` | `CascaderViewLevelList` | CascaderView root 列。 |
| `PART_FilterList` | `CascaderViewFilterList` | CascaderView 过滤结果列表。 |

稳定伪类来自 `AbstractSelect` 和 `CascaderViewItem`：

- `:dropdownopen`、native validation `:error`、AtomUI warning `:warning`、`:pressed`。
- `InputControlFrame` variant 伪类：`:outlined`、`:filled`、`:borderless`、`:underlined`；Cascader 专用伪类只表达 dropdown、候选和结果状态。
- `CascaderViewItem` 使用 `:expanded`、`:checked`、`:selected` 和 checkbox toggle type 伪类。

## 事件与命令

| `DropDownOpening` / `DropDownOpened` / `DropDownClosing` / `DropDownClosed` | 继承自 `AbstractSelect` 的 popup 生命周期事件。 |
### 3.5 内部 view 事件契约
`CascaderView` 是内部弹层控件，当前事件用于 `Cascader` 和测试协作，不作为外层 Cascader 的主用户事件模型：
| 事件 | 语义 |

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/Cascader/Views/CascaderShowCase.axaml:37`

Gallery key：`ExamplesContent` / item `0`

```axaml
<atom:Cascader
    Name="BasicCascader"
    PlaceholderText="请选择"
    OptionsSource="{Binding BasicCascaderViewNodes}"
    IsAllowClear="True">
    <atom:Cascader.OptionTemplate>
        <TreeDataTemplate ItemsSource="{Binding Children}"
                          x:DataType="atom:ICascaderOption">
            <atom:TextBlock Text="{Binding Header}" />
        </TreeDataTemplate>
    </atom:Cascader.OptionTemplate>
</atom:Cascader>
```

### 默认值

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/Cascader/Views/CascaderShowCase.axaml:59`

Gallery key：`ExamplesContent` / item `1`

```axaml
<atom:Cascader
    Name="DefaultValueCascader"
    PlaceholderText="请选择"
    OptionsSource="{Binding BasicCascaderViewNodes}"
    DefaultSelectOptionPath="{Binding DefaultSelectOptionPath}"
    IsAllowClear="True">
    <atom:Cascader.OptionTemplate>
        <TreeDataTemplate ItemsSource="{Binding Children}"
                          x:DataType="atom:ICascaderOption">
            <atom:TextBlock Text="{Binding Header}" />
        </TreeDataTemplate>
    </atom:Cascader.OptionTemplate>
</atom:Cascader>
```

### 悬停展开

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/Cascader/Views/CascaderShowCase.axaml:188`

Gallery key：`ExamplesContent` / item `3`

```axaml
<atom:Cascader
    Name="HoverCascader"
    PlaceholderText="请选择"
    OptionsSource="{Binding HoverCascaderNodes}"
    IsAllowClear="True"
    ExpandTrigger="Hover">
    <atom:Cascader.OptionTemplate>
        <TreeDataTemplate ItemsSource="{Binding Children}"
                          x:DataType="atom:ICascaderOption">
            <atom:TextBlock Text="{Binding Header}" />
        </TreeDataTemplate>
    </atom:Cascader.OptionTemplate>
</atom:Cascader>
```

### 禁用选项

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/Cascader/Views/CascaderShowCase.axaml:211`

Gallery key：`ExamplesContent` / item `4`

```axaml
<atom:Cascader
    Name="DisabledCascader"
    PlaceholderText="请选择"
    OptionsSource="{Binding DisabledCascaderNodes}"
    IsAllowClear="True">
    <atom:Cascader.OptionTemplate>
        <TreeDataTemplate ItemsSource="{Binding Children}"
                          x:DataType="atom:ICascaderOption">
            <atom:TextBlock Text="{Binding Header}" />
        </TreeDataTemplate>
    </atom:Cascader.OptionTemplate>
</atom:Cascader>
```

## 状态模型

核心状态流：

```text
OptionsSource / Options
      ↓
CascaderView.Options
      ↓
CascaderViewLevelList
      ↓
CascaderViewItem
      ↓
SelectedOption / SelectedOptions
      ↓
SelectedOptionPath / EffectiveSelectedOptions
      ↓
input display + Form value
```

单选模式：

- `IsMultiple=false` 时使用 `SelectedOption` 作为真实值。
- 叶子节点点击后提交选择并关闭 popup。
- `IsAllowSelectParent=true` 时，非叶子节点也可提交为选择结果。
- `SelectedOptionPath` 根据当前选项的 parent 链生成 header 路径，用于输入框展示。
- `DefaultSelectOptionPath` 只在当前选择为空或强制刷新路径时应用。

多选模式：

- `IsMultiple=true` 时使用 `SelectedOptions` 作为真实值，并让内部 `CascaderView` 进入 checkable 模式。
- `SelectedOptions` 保留真实勾选集合，`ShowCheckedStrategy` 只计算 `EffectiveSelectedOptions`，用于 tag 展示。
- `SelectedOptions` 支持外部集合替换，也支持 `INotifyCollectionChanged` 集合的原地 `Add`、`Remove`、`Replace`、`Move` 和 `Reset`；这些变化会同步刷新 tag、计数、空状态、Form value 和内部 `CascaderView` 勾选状态。
- `MaxCount` 达到上限时，未选项通过 `IsMaxSelectReached` 进入受限状态；已选项仍可取消。
- 关闭单个 tag 时，目标节点及其子孙会从 `SelectedOptions` 中移除。

展开和异步加载：

- 展开按节点 parent 链从 root 到目标逐级执行。
- 同一级只保持一个已展开分支，展开新分支会折叠同级旧分支。
- 未加载且非 leaf 的节点在有 `DataLoader` 时进入 loading，加载完成后把返回子项加入目标 `Children`。
- 控件 detached 时取消待处理异步加载，避免离开视觉树后继续处理结果。

过滤：

- `FilterValue` 非空且控件 loaded 时，CascaderView 收集所有叶子路径并按 `Filter` 过滤。
- 过滤结果显示完整路径文本，选中过滤结果后回写目标 option。
- 过滤模式下，`Up` / `Down` 在可用结果间循环移动内部候选高亮，不修改 `SelectedOption`；`Enter` 提交当前候选，尚无候选时提交第一个可用结果，没有可用结果时保持选择和 popup 状态不变。路径中任一祖先 disabled 时，该过滤结果也不可作为候选或提交。
- 过滤列表拥有独立于树列的 active candidate owner；过滤结果重建、过滤清空、popup 关闭和容器回收时清除旧候选。树列与过滤列不会同时保留两个候选视觉。
- 清空过滤值或关闭 popup 后，过滤列表、过滤计数和缓存路径会被清理。

树形键盘导航：

- popup 打开且未过滤时，`Up` / `Down` 在当前已展开列的可见 enabled item 间循环移动内部候选；候选高亮与真实选择相互独立。
- 普通树列的 active candidate 由 `CascaderView` 单一持有；鼠标移动到 enabled item 时迁移该候选并继续执行 `ExpandTrigger=Hover` 的展开逻辑，但不提前提交选择、不滚动列表。`Enter` 使用同一 active candidate 作为选择或展开目标。
- `Right` 从当前候选或第一个可见 enabled item 开始，展开可展开节点并把候选移到下一列的第一个 enabled child。
- `Left` 优先把子级候选移回父级；候选已位于展开的根级非叶节点时折叠该节点。
- `Enter` 提交 enabled、非 loading 的叶子候选；`IsAllowSelectParent=true` 时也可提交父节点，否则沿用 `Right` 的展开并进入子级行为。

Form：

- 单选 Form value 为 `SelectedOption`。
- 多选 Form value 为 `SelectedOptions`。
- Form 校验错误写入同一份 Avalonia `DataValidationErrors`；`SelectedOption` 和 `SelectedOptions` 不维护独立错误状态。
- Form clear 会按当前 `IsMultiple` 清空对应选择状态。

## 主题与 Design Token

Cascader 的默认视觉由 Cascader 根主题、`InputControlFrame` / CascaderAddOnDecoratedBox、PopupHost、CascaderView、CascaderViewItem、SelectTagAwareTextBox、SelectHandle 和 CascaderToken 协作完成。

| 主题或资源 | 职责 |
| --- | --- |
| `CascaderTheme.axaml` | 根模板、输入壳体、单选结果、多选 tag、handle、popup 和 CascaderView 绑定。 |
| `CascaderViewTheme.axaml` | 弹层内部级联列、空状态、过滤列表和默认 option template。 |
| `CascaderViewItemTheme.axaml` | option 行、checkbox、icon、header、展开 / loading icon、hover / expanded / disabled 状态。 |
| `CascaderViewLevelListTheme.axaml` | 单列列表宽度、高度、padding 和滚动行为。 |
| `CascaderToken` | Cascader 输入宽度、列宽、弹层高度、选项高度、padding、状态色和过滤高亮。 |
| `PopupHostToken` | popup margin、阴影和圆角。 |
| SharedToken | 字体、输入高度、图标尺寸、placeholder、disabled、motion 和全局 spacing。 |

单选路径和多选 tag 的完整内容提示复用共享 `OverflowTip` attached behavior。主题只把 `IsShowOverflowTip`、`OverflowTipDelay`、`OverflowTipPlacement` 和当前展示文本传给显示节点；tooltip 仅在视觉溢出时写入，且不覆盖用户手动声明的 `ToolTip.Tip`。

主题不可破坏的视觉边界：

- 输入表面必须继续由 `InputControlFrame` 承载；`CascaderAddOnDecoratedBox` 只扩展多选、dropdown 和 content padding 布局。
- `PART_SelectHandle` 的 hover / pressed / dropdown open / clear / loading 状态必须与输入壳体保持同步。
- `OptionTemplate` 的 DataContext 必须保持为 `ICascaderOption`，不能改为 header 文本。
- Popup 宽度和空状态宽度匹配语义必须保持：普通级联列使用列宽，空状态需要匹配输入宽度。
- 选项行的 checkbox、icon、header、expand / loading icon 间距由 Token 管理，不应在单个模板节点中写死。
- disabled、expanded、pointerover、checked、loading 和 active candidate 状态 selector 不能被绕过；`pointerover` 只触发 active candidate 迁移，不独立绘制第二个候选背景。

Token 来源：

`CascaderToken` 描述 Cascader 输入弹层和级联选项的尺寸、列宽、选项状态色、padding、过滤高亮和内部元素间距。它只表达控件主题常量，不表达实例选择状态、当前展开路径、当前过滤值、当前 loading 状态或最大选择数量状态。

## AOT 与裁剪注意事项

绑定型选项使用 source generator 生成 scoped resource host，不手写 `IResourceHost` / `IThemeVariantHost` 样板代码，不使用运行时反射扫描或字符串 binding。

模板内稳定关系优先放在 AXAML 中表达。右侧 count、content add-on 和 handle 状态使用 compiled ancestor binding；C# 属性同步只用于运行时生成容器和绑定型非 Visual 数据对象。

容器生命周期是绑定型选项的释放边界。`CascaderViewItem` 持有的 resource host attachment、option property subscription、container property subscription 和 children collection subscription 必须在容器 clear 或 detach 时释放。

异步加载由 `AsyncExpandLoadCoordinator` 合并同一节点并发请求并应用超时。`CascaderView` detached 时必须调用 `CancelAll()`，避免已离开视觉树的控件继续处理加载结果。

过滤路径缓存以当前 options 为边界，清除过滤或 options source 变化时必须失效，不能跨数据源复用旧路径。

## 源码索引

主要源码：

- `src/AtomUI.Desktop.Controls/Cascader/Cascader.cs`：public API、输入壳体、popup、选择结果、默认路径、Form / CompactSpace 集成和外层交互。
- `src/AtomUI.Desktop.Controls/Cascader/CascaderView.cs`：弹层内容、选项源、选择、过滤入口、空状态、默认展开路径和模板接入。
- `src/AtomUI.Desktop.Controls/Cascader/CascaderView.ExpandAndCollapse.cs`：展开、收起、同级互斥展开、子列创建和默认展开路径。
- `src/AtomUI.Desktop.Controls/Cascader/CascaderView.Check.cs`：多选勾选、子树勾选、父级半选和 `SelectedOptions` 同步。
- `src/AtomUI.Desktop.Controls/Cascader/CascaderView.AsyncItemDataLoad.cs`：异步加载、超时、取消、并发加载合并和 loaded 事件。
- `src/AtomUI.Desktop.Controls/Cascader/CascaderView.Filter.cs`：过滤路径收集、过滤结果列表、过滤计数和高亮文本。
- `src/AtomUI.Desktop.Controls/Cascader/CascaderViewLevelList.cs`：单级列表、容器准备、虚拟化上下文保存和清理。
- `src/AtomUI.Desktop.Controls/Cascader/CascaderViewItem.cs`：选项容器、checked / expanded / selected 事件、leaf 判断和绑定型选项同步。
- `src/AtomUI.Desktop.Controls/Cascader/CascaderOption.cs`：`ICascaderOption`、默认轻量 `CascaderOption` 和层级 helper。
- `src/AtomUI.Desktop.Controls/Cascader/BindableCascaderOption.cs`：绑定型选项模型，使用 scoped resource host generator。
- `src/AtomUI.Desktop.Controls/Cascader/CascaderAddOnDecoratedBox.cs`：Cascader 输入布局扩展，复用 `InputControlFrame` 的输入表面状态。
- `src/AtomUI.Desktop.Controls/Cascader/CascaderViewPanel.cs`：弹层 frame 布局与边框测量。
- `src/AtomUI.Desktop.Controls/Cascader/CascaderViewFilterList.cs`、`CascaderViewFilterListItemData.cs`：过滤结果列表和路径数据。
- `src/AtomUI.Desktop.Controls/Tooltip/OverflowTip.cs`：共享溢出 tooltip attached behavior，供单选路径和多选 tag 复用。
- `src/AtomUI.Desktop.Controls/Cascader/Themes/*.axaml`：Cascader、CascaderView、CascaderViewItem、level list 和 filter list 主题。
- `src/AtomUI.Desktop.Controls/Cascader/CascaderToken.cs`：Cascader 专属 Token。

## 相关文档

- 源设计文档：`docs/controls/desktop/data-entry/cascader/overview.md`
- 实现文档：`docs/controls/desktop/data-entry/cascader/implementation.md`
- Token 文档：`docs/controls/desktop/data-entry/cascader/token.md`
- 变更记录：`docs/controls/desktop/data-entry/cascader/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/check-box/index-cn.md

# CheckBox

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

CheckBox 是 AtomUI 桌面控件体系中的复选框控件，用于表达二元选择、多选集合和中间态。

CheckBox 不负责单选语义、开关语义或复杂表单验证系统。这些职责应由业务层、组合控件或更专用的 AtomUI 控件承担。

主要源码入口：

- `src/AtomUI.Desktop.Controls/CheckBox`
- `src/AtomUI.Controls/CheckBox`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/DataEntry/CheckBox` |
| 状态 | Stable |

## 何时使用

CheckBox 的设计语言围绕控件职责、可观察状态和主题契约组织，而不是围绕模板节点组织。

| 维度 | 含义 | CheckBox 中的表达 |
| --- | --- | --- |
| 产品语义 | 控件在界面中承担的稳定职责。 | CheckBox 是 AtomUI 桌面控件体系中的复选框控件，用于表达二元选择、多选集合和中间态。 |
| 内容承载 | 用户数据、展示内容、集合项或操作入口如何进入控件。 | `CheckedItems`、`ItemSpacing`、`ItemTemplate`、`ItemsSource`。 |
| 状态反馈 | public API、内部状态和伪类如何形成用户可感知反馈。 | selection/checked/active、collection/filter、motion、visual option。 |
| 主题语义 | ControlTheme、SharedToken、控件 Token 和模板绑定如何表达视觉。 | CheckBox Token + ControlTheme。 |

## 公共 API

CheckBox 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。

核心 public surface 按语义分组维护：

| 契约组 | 代表成员 | 维护含义 |
| --- | --- | --- |
| 内容与数据 | `CheckedItems`、`ItemSpacing`、`ItemTemplate`、`ItemsSource` | 定义控件展示内容、输入数据、模板或业务对象入口。 |
| 选择与集合 | `CheckedMarkBrush`、`CheckedMarkRenderTransform` | 维护选择、展开、过滤、分页、分组或集合状态。 |
| 交互与状态 | `IsMotionEnabled`、`IsWaveSpiritEnabled`、`State` | 表达用户可观察状态、可用性、清除、加载或反馈语义。 |
| 视觉与布局 | `LineSpacing`、`Orientation`、`TristateMarkBrush`、`TristateMarkSize` | 影响尺寸、位置、颜色、形状、密度和模板视觉变量。 |

稳定事件包括 `CheckedChanged`。事件触发顺序属于兼容契约，不能因内部状态重排而改变。

主要公开类型与枚举：

- 类型：`AbstractCheckBox`、`AbstractCheckBoxGroup`、`AbstractCheckBoxItemsControl`、`CheckBox`、`CheckBoxGroup`、`CheckBoxGroupCheckedChangedEventArgs`、`CheckBoxIndicator`、`CheckBoxIndicatorStateConverter`、`CheckBoxItemsControl`、`CheckBoxOption`。
- 枚举：无。

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_CheckBoxItems` | `SelectingItemsControl` | 承载集合项、布局面板或虚拟化内容。 |
| `PART_ItemsPresenter` | `?` | 展示用户内容、文本、图标或模板化数据。 |
| `PART_WaveSpirit` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |

当前未抽取到控件专属伪类；主题主要依赖 Avalonia 标准伪类、模板绑定和内部 StyledProperty。

## 事件与命令

CheckBox 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。
稳定事件包括 `CheckedChanged`。事件触发顺序属于兼容契约，不能因内部状态重排而改变。
- 类型：`AbstractCheckBox`、`AbstractCheckBoxGroup`、`AbstractCheckBoxItemsControl`、`CheckBox`、`CheckBoxGroup`、`CheckBoxGroupCheckedChangedEventArgs`、`CheckBoxIndicator`、`CheckBoxIndicatorStateConverter`、`CheckBoxItemsControl`、`CheckBoxOption`。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/CheckBox/Views/CheckBoxShowCase.axaml:40`

Gallery key：`ExamplesContent` / item `0`

```axaml
<StackPanel HorizontalAlignment="Left" Spacing="10">
    <atom:CheckBox Content="复选框" />
</StackPanel>
```

### 禁用

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/CheckBox/Views/CheckBoxShowCase.axaml:53`

Gallery key：`ExamplesContent` / item `1`

```axaml
<StackPanel HorizontalAlignment="Left" Spacing="10" Orientation="Vertical">
    <atom:CheckBox IsChecked="False" IsEnabled="False"
                   Content="未选中" />
    <atom:CheckBox IsChecked="{x:Null}" IsEnabled="False"
                   Content="半选" />
    <atom:CheckBox IsChecked="True" IsEnabled="False"
                   Content="选中" />
</StackPanel>
```

### 受控复选框

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/CheckBox/Views/CheckBoxShowCase.axaml:71`

Gallery key：`ExamplesContent` / item `2`

```axaml
<StackPanel HorizontalAlignment="Left" Spacing="10" Orientation="Vertical">
    <atom:CheckBox Name="ControlledCheckbox"
                   IsChecked="{Binding ControlledCheckBoxCheckedStatus}"
                   IsEnabled="{Binding ControlledCheckBoxEnabledStatus}"
                   Command="{Binding CheckBoxCommand}"
                   Content="{Binding ControlledCheckBoxText}" />
    <StackPanel Orientation="Horizontal" Spacing="10" Margin="0, 10, 0, 0">
        <atom:Button SizeType="Small" ButtonType="Primary"
                     Name="CheckStatusBtn"
                     Command="{Binding CheckStatusCommand}"
                     Content="{Binding CheckStatusBtnText}" />
        <atom:Button SizeType="Small" ButtonType="Primary"
                     Name="EnableStatusBtn"
                     Command="{Binding EnableStatusCommand}"
                     Content="{Binding EnableStatusBtnText}" />
    </StackPanel>
</StackPanel>
```

### 复选框组

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/CheckBox/Views/CheckBoxShowCase.axaml:98`

Gallery key：`ExamplesContent` / item `3`

```axaml
<StackPanel HorizontalAlignment="Left" Spacing="10" Orientation="Vertical">
    <atom:CheckBoxGroup>
        <atom:CheckBox IsChecked="True"
                       Content="苹果" />
        <atom:CheckBox IsChecked="True"
                       Content="梨" />
        <atom:CheckBox IsChecked="True"
                       Content="橙子" />
    </atom:CheckBoxGroup>
    <atom:CheckBoxGroup>
        <atom:CheckBox Content="苹果" />
        <atom:CheckBox IsChecked="True"
                       Content="梨" />
        <atom:CheckBox Content="橙子" />
    </atom:CheckBoxGroup>
    <atom:CheckBoxGroup>
        <atom:CheckBox IsChecked="True" IsEnabled="False"
                       Content="苹果" />
        <atom:CheckBox IsEnabled="False"
                       Content="梨" />
        <atom:CheckBox IsEnabled="False"
                       Content="橙子" />
    </atom:CheckBoxGroup>
    <atom:CheckBoxGroup Name="BasicCheckBoxGroup"
                        ItemsSource="{Binding CheckBoxOptions}"
                        CheckedItems="{Binding DefaultCheckBoxOptions}" />
</StackPanel>
```

## 状态模型

CheckBox 的状态流按以下路径收敛：

```text
Public API / inherited command / item source / user input
  -> 控件实例状态
  -> effective state / pseudo-class / template property
  -> ControlTheme selector / presenter / renderer
  -> Gallery 可观察行为
```

状态维护规则：

- Disabled 或不可交互状态优先屏蔽 pointer、keyboard、motion 和提交类反馈。
- selection/checked/active、collection/filter、motion、visual option 状态由控件实例或明确的数据 owner 推导，不能在 template part 之间双向竞争。
- `CheckBoxGroup.CheckedItems` 是集合选择的外部值 owner，默认 `BindingMode.TwoWay` 并启用 Avalonia data validation；绑定集合的 `Add`、`Remove`、`Clear` 或 `Reset` 必须回放到内部勾选状态和 Form value。
- 模板重套用时必须把 public API 对应状态回放到新的 part、伪类和主题变量。
- 集合、弹层、异步、动效或窗口相关状态必须能处理 reset、close、cancel、detach 和 owner 释放。

## 主题与 Design Token

CheckBox 的视觉模型由控件模板、ControlTheme、SharedToken 和必要的控件 Token 共同构成。

| 主题文件 | 职责 |
| --- | --- |
| `CheckBoxGroupTheme.axaml` | 定义布局、内容承载或框架节点视觉。 |
| `CheckBoxIndicatorTheme.axaml` | 定义布局、内容承载或框架节点视觉。 |
| `CheckBoxItemsControlTheme.axaml` | 定义集合项、容器项或局部单元的状态视觉。 |
| `CheckBoxTheme.axaml` | 定义布局、内容承载或框架节点视觉。 |

CheckBox 使用 `CheckBoxToken` 作为控件 Token scope。Token 只表达组件视觉语义，不承载 selection/checked/active、collection/filter、motion、visual option 运行时状态。

主题维护规则：

- 不删除或重命名已经稳定的 ControlTheme key、template part、伪类和资源 key。
- 不把可由 AXAML 表达的模板状态迁移为 C# 动态创建视觉。
- 不把 hover、pressed、selected、expanded、loading、filter、popup open 等运行时状态写入 Token。
- Browser 或平台特化主题必须保持同一 API 的语义一致。

Token 来源：

CheckBox Token 只表达组件级视觉变量，例如尺寸、间距、颜色、圆角、阴影、图标尺寸和弹层边界。Token 不承载运行时选择、展开、加载、错误、上传任务、过滤条件或业务状态。

当前 Token scope：

- `CheckBoxToken`，scope id 为 `CheckBox`，源码位于 `src/AtomUI.Desktop.Controls/CheckBox/CheckBoxToken.cs`。

## AOT 与裁剪注意事项

资源和 AOT 约束：

- 不通过运行时反射扫描 public API、Token 或 Gallery 示例数据。
- 不把可静态声明的模板结构迁移到 C# 动态创建。
- 异步加载、上传、弹层和窗口生命周期必须能取消或释放。
- 缓存对象必须与控件、窗口、弹层或数据 owner 生命周期一致。
- Source generator 生成文件不手工编辑；需要修改时改输入源或 generator。

性能边界：

- 控件应优先复用 Avalonia 原生虚拟化、模板绑定和资源系统。
- 避免为每次状态变化创建不必要的视觉对象、订阅或动画对象。
- 大集合控件必须保证 container recycle 后不会泄漏旧 item 状态。

## 源码索引

主要源码文件：

- `src/AtomUI.Desktop.Controls/CheckBox/CheckBox.cs`
- `src/AtomUI.Desktop.Controls/CheckBox/CheckBoxGroup.cs`
- `src/AtomUI.Desktop.Controls/CheckBox/CheckBoxItemsControl.cs`
- `src/AtomUI.Desktop.Controls/CheckBox/CheckBoxToken.cs`
- `src/AtomUI.Desktop.Controls/CheckBox/Themes/CheckBoxGroupTheme.axaml`
- `src/AtomUI.Desktop.Controls/CheckBox/Themes/CheckBoxIndicatorTheme.axaml`
- `src/AtomUI.Desktop.Controls/CheckBox/Themes/CheckBoxItemsControlTheme.axaml`
- `src/AtomUI.Desktop.Controls/CheckBox/Themes/CheckBoxTheme.axaml`
- `src/AtomUI.Controls/CheckBox/AbstractCheckBox.cs`
- `src/AtomUI.Controls/CheckBox/AbstractCheckBoxGroup.cs`
- `src/AtomUI.Controls/CheckBox/AbstractCheckBoxItemsControl.cs`
- `src/AtomUI.Controls/CheckBox/CheckBoxGroupCheckedChangedEventArgs.cs`
- `src/AtomUI.Controls/CheckBox/CheckBoxIndicator.cs`
- `src/AtomUI.Controls/CheckBox/CheckBoxOption.cs`
- `src/AtomUI.Controls/CheckBox/Converters/CheckBoxIndicatorStateConverter.cs`

职责边界：

- 控件主文件保留 public/protected API、Avalonia 属性注册、事件和主要生命周期入口。
- Theme 文件负责静态视觉结构、template part、selector 和资源绑定。
- Token 文件只提供组件视觉变量，不保存实例状态。
- Gallery 文件只展示用法和示例，不作为运行时逻辑 owner。

## 相关文档

- 源设计文档：`docs/controls/desktop/data-entry/check-box/overview.md`
- 实现文档：`docs/controls/desktop/data-entry/check-box/implementation.md`
- Token 文档：`docs/controls/desktop/data-entry/check-box/token.md`
- 变更记录：`docs/controls/desktop/data-entry/check-box/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/color-picker/index-cn.md

# ColorPicker

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

ColorPicker 是 AtomUI 桌面控件体系中的颜色选择控件，用于选择纯色、透明度、预设色和渐变色。

ColorPicker 不负责通用绘图编辑器或业务调色板持久化协议。这些职责应由业务层、组合控件或更专用的 AtomUI 控件承担。

主要源码入口：

- `src/AtomUI.Desktop.Controls.ColorPicker`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls.ColorPicker` |
| .NET 命名空间 | `AtomUI.Desktop.Controls.ColorPicker` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/DataEntry/ColorPicker` |
| 状态 | Stable |

## 何时使用

ColorPicker 的设计语言围绕控件职责、可观察状态和主题契约组织，而不是围绕模板节点组织。

| 维度 | 含义 | ColorPicker 中的表达 |
| --- | --- | --- |
| 产品语义 | 控件在界面中承担的稳定职责。 | ColorPicker 是 AtomUI 桌面控件体系中的颜色选择控件，用于选择纯色、透明度、预设色和渐变色。 |
| 内容承载 | 用户数据、展示内容、集合项或操作入口如何进入控件。 | `ColorValue`、`ColorValueBrush`、`DefaultValue`、`EmptyColorText`、`GradientValue`、`IsTextVisible`、`MaxValue`、`MinValue` 等 10 项。 |
| 状态反馈 | public API、内部状态和伪类如何形成用户可感知反馈。 | open/close、collection/filter、input/value、motion、visual option。 |
| 主题语义 | ControlTheme、SharedToken、Control Own Token 和模板绑定如何表达视觉。 | ColorPicker Token + ControlTheme。 |

## 公共 API

ColorPicker 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。

核心 public surface 按语义分组维护：

| 契约组 | 代表成员 | 维护含义 |
| --- | --- | --- |
| 内容与数据 | `ColorValue`、`ColorValueBrush`、`DefaultValue`、`EmptyColorText`、`GradientValue`、`IsTextVisible`、`MaxValue`、`MinValue`、`Value`、`ValueSyncStrategy` | 定义控件展示内容、输入数据、模板或业务对象入口。 |
| 选择与集合 | `ColorModel`、`IsEmptyColorMode`、`IsPaletteGroupEnabled` | 维护选择、展开、过滤、分页、分组或集合状态。 |
| 交互与状态 | `IsActivated`、`IsAlphaEnabled`、`IsAlphaVisible`、`IsArrowVisible`、`IsClearEnabled`、`IsColorSpectrumSliderVisible`、`IsFormatEnabled`、`IsMotionEnabled`、`IsPerceptive`、`IsPointAtCenter` 等 15 项 | 表达用户可观察状态、可用性、清除、加载或反馈语义。 |
| 视觉与布局 | `Color`、`ColorComponent`、`ColorSpectrumComponents`、`HsvColor`、`MarginToAnchor`、`Placement`、`PlacementAnchor`、`PlacementGravity`、`Shape`、`Size` 等 12 项 | 影响尺寸、位置、颜色、形状、密度和模板视觉变量。 |
| 动效与异步 | `MouseEnterDelay`、`MouseLeaveDelay` | 约束动效开关、异步加载、播放速度、超时和任务边界。 |
| 其他稳定入口 | `ActivatedThumb`、`Components`、`DecreaseButton`、`Format`、`IncreaseButton`、`MaxHue`、`MaxSaturation`、`Maximum`、`MinHue`、`MinSaturation` 等 14 项 | 保留为 public surface，变更前需确认 Gallery 和用户 XAML 依赖。 |

`ColorPicker.Value` 和 `GradientColorPicker.Value` 是用户拥有的当前值：纯色选择器使用 `Color?`，渐变选择器使用 `LinearGradientBrush?`。两个 `Value` CLR wrapper 均可公开设置，默认 `TwoWay` 绑定，并接入 Avalonia `DataValidationErrors`。清除按钮、Form clear 和外部绑定写入 `null` 都必须让 `Value` 变为 `null`，不能只清空触发器文字或色块视觉。

稳定事件包括 `ClearRequest`、`GradientActiveStopChanged`。事件触发顺序属于兼容契约，不能因内部状态重排而改变。

主要公开类型与枚举：

- 类型：`AbstractColorPicker`、`AbstractColorPickerSliderTrack`、`AbstractColorPickerView`、`AbstractColorSlider`、`AtomUIColorPickerThemesProvider`、`ColorBlock`、`ColorChangedEventArgs`、`ColorPicker`、`ColorPickerCollapse`、`ColorPickerInput`、`ColorPickerLangResourceExtension`、`ColorPickerPalette`、`ColorPickerPaletteColorSelectedEventArgs`、`ColorPickerPaletteGroup` 等 36 项。
- 枚举：`ColorFormat`、`ColorPickerLangResourceKind`、`ColorPickerTokenKind`、`ColorPickerValueSyncMode`。

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_AlphaInput` | `?` | 承载文本输入、过滤、显示或编辑入口。 |
| `PART_BValueInput` | `?` | 承载文本输入、过滤、显示或编辑入口。 |
| `PART_CheckedMark` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_ClearColor` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_ColorFormatComboBox` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_ColorIndicator` | `?` | 展示指示器、进度、分页或状态反馈。 |
| `PART_ColorPreview` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_ColorPreviewFrame` | `?` | 承载根视觉、边框、背景或尺寸基线。 |
| `PART_ColorText` | `?` | 承载文本输入、过滤、显示或编辑入口。 |
| `PART_ColorTextPanel` | `?` | 承载集合项、布局面板或虚拟化内容。 |
| `PART_DecreaseButton` | `?` | 承载用户触发入口、导航或关闭动作。 |
| `PART_EmptyColorFrame` | `?` | 承载根视觉、边框、背景或尺寸基线。 |
| `PART_Frame` | `?` | 承载根视觉、边框、背景或尺寸基线。 |
| `PART_GValueInput` | `?` | 承载文本输入、过滤、显示或编辑入口。 |
| `PART_GradientColorSlider` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_HValueInput` | `?` | 承载文本输入、过滤、显示或编辑入口。 |
| `PART_HexValueInput` | `?` | 承载文本输入、过滤、显示或编辑入口。 |
| `PART_HsvValuePanel` | `?` | 承载集合项、布局面板或虚拟化内容。 |
| `PART_IncreaseButton` | `?` | 承载用户触发入口、导航或关闭动作。 |
| `PART_InnerEllipse` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_InputTarget` | `?` | 承载文本输入、过滤、显示或编辑入口。 |
| `PART_LayoutRoot` | `?` | 承载根视觉、边框、背景或尺寸基线。 |
| `PART_PaletteGroup` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_Popup` | `?` | 承载弹层宿主、打开关闭或候选内容。 |
| 其他 part | 10 项 | 参见源码和主题文件；维护时按同一生命周期规则检查。 |

控件专属或内部伪类包括 `ColorPickerPseudoClass.FlyoutOpen`、`ColorSliderPseudoClass.DarkSelector`、`ColorSliderPseudoClass.LightSelector`、`ColorSpectrumPseudoClass.DarkSelector`、`ColorSpectrumPseudoClass.LargeSelector`、`ColorSpectrumPseudoClass.LightSelector`、`ColorSpectrumPseudoClass.Pressed`、`DarkSelector=:dark-selector`、`FlyoutOpen=:flyout-open`、`LargeSelector=:large-selector`、`LightSelector=:light-selector`、`Pressed=:pressed`。这些伪类属于主题 selector 可观察契约，不能在未同步主题和 Gallery 的情况下重命名或删除。

## 事件与命令

ColorPicker 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。
稳定事件包括 `ClearRequest`、`GradientActiveStopChanged`。事件触发顺序属于兼容契约，不能因内部状态重排而改变。
- 类型：`AbstractColorPicker`、`AbstractColorPickerSliderTrack`、`AbstractColorPickerView`、`AbstractColorSlider`、`AtomUIColorPickerThemesProvider`、`ColorBlock`、`ColorChangedEventArgs`、`ColorPicker`、`ColorPickerCollapse`、`ColorPickerInput`、`ColorPickerLangResourceExtension`、`ColorPickerPalette`、`ColorPickerPaletteColorSelectedEventArgs`、`ColorPickerPaletteGroup` 等 36 项。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/ColorPicker/Views/ColorPickerShowCase.axaml:35`

Gallery key：`ExamplesContent` / item `0`

```axaml
<atom:ColorPicker DefaultValue="#1677ff"/>
```

### Value 绑定

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/ColorPicker/Views/ColorPickerShowCase.axaml:47`

Gallery key：`ExamplesContent` / item `1`

```axaml
<StackPanel Orientation="Vertical" Spacing="12">
    <StackPanel Orientation="Horizontal" Spacing="10">
        <atom:ColorPicker Value="{Binding BoundColorValue}"
                          IsTextVisible="True"
                          IsClearEnabled="True" />
        <atom:GradientColorPicker Value="{Binding BoundGradientValue}"
                                  IsTextVisible="True"
                                  IsClearEnabled="True" />
    </StackPanel>
    <StackPanel Orientation="Horizontal" Spacing="8">
        <atom:TextBlock VerticalAlignment="Center"
                        Text="绑定颜色：" />
        <atom:TextBlock VerticalAlignment="Center"
                        Text="{Binding BoundColorValueText}" />
    </StackPanel>
    <StackPanel Orientation="Horizontal" Spacing="8">
        <atom:TextBlock VerticalAlignment="Center"
                        Text="绑定渐变：" />
        <atom:TextBlock VerticalAlignment="Center"
                        Text="{Binding BoundGradientValueText}" />
    </StackPanel>
    <WrapPanel ItemSpacing="10" LineSpacing="8">
        <atom:Button SizeType="Small"
                     Command="{Binding SetBoundColorValueCommand}"
                     Content="设置颜色" />
        <atom:Button SizeType="Small"
                     Command="{Binding SetBoundGradientValueCommand}"
                     Content="设置渐变" />
        <atom:Button SizeType="Small"
                     Command="{Binding ClearBoundColorValueCommand}"
                     Content="清空颜色" />
        <atom:Button SizeType="Small"
                     Command="{Binding ClearBoundGradientValueCommand}"
                     Content="清空渐变" />
    </WrapPanel>
</StackPanel>
```

### 触发器尺寸

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/ColorPicker/Views/ColorPickerShowCase.axaml:93`

Gallery key：`ExamplesContent` / item `2`

```axaml
<StackPanel Orientation="Vertical" Spacing="10">
    <StackPanel Orientation="Horizontal" Spacing="12" VerticalAlignment="Center">
        <atom:TextBlock Width="64"
                        VerticalAlignment="Center"
                        Foreground="{atom:SharedTokenResource ColorTextSecondary}"
                        Text="小号" />
        <StackPanel Orientation="Horizontal" Spacing="10">
            <atom:ColorPicker DefaultValue="#1677ff" SizeType="Small"/>
            <atom:ColorPicker DefaultValue="#1677ff" SizeType="Small" IsTextVisible="True"/>
        </StackPanel>
    </StackPanel>

    <StackPanel Orientation="Horizontal" Spacing="12" VerticalAlignment="Center">
        <atom:TextBlock Width="64"
                        VerticalAlignment="Center"
                        Foreground="{atom:SharedTokenResource ColorTextSecondary}"
                        Text="中号" />
        <StackPanel Orientation="Horizontal" Spacing="10">
            <atom:ColorPicker DefaultValue="#1677ff" SizeType="Middle"/>
            <atom:ColorPicker DefaultValue="#1677ff" SizeType="Middle" IsTextVisible="True"/>
        </StackPanel>
    </StackPanel>

    <StackPanel Orientation="Horizontal" Spacing="12" VerticalAlignment="Center">
        <atom:TextBlock Width="64"
                        VerticalAlignment="Center"
                        Foreground="{atom:SharedTokenResource ColorTextSecondary}"
                        Text="大号" />
        <StackPanel Orientation="Horizontal" Spacing="10">
            <atom:ColorPicker DefaultValue="#1677ff" SizeType="Large"/>
            <atom:ColorPicker DefaultValue="#1677ff" SizeType="Large" IsTextVisible="True"/>
        </StackPanel>
    </StackPanel>

    <StackPanel Orientation="Horizontal" Spacing="12" VerticalAlignment="Center">
        <atom:TextBlock Width="64"
                        VerticalAlignment="Center"
                        Foreground="{atom:SharedTokenResource ColorTextSecondary}"
                        Text="Custom" />
        <StackPanel Orientation="Horizontal" Spacing="10">
            <atom:ColorPicker DefaultValue="#1677ff"
                              SizeType="Custom"
                              Width="48"
                              Height="48" />
            <atom:ColorPicker DefaultValue="#1677ff"
                              SizeType="Custom"
                              IsTextVisible="True"
                              Height="48"
                              FontSize="18" />
        </StackPanel>
    </StackPanel>
</StackPanel>
```

### 线性渐变

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/ColorPicker/Views/ColorPickerShowCase.axaml:155`

Gallery key：`ExamplesContent` / item `3`

```axaml
<StackPanel Orientation="Vertical" Spacing="10">
    <atom:GradientColorPicker IsTextVisible="True">
        <atom:GradientColorPicker.DefaultValue>
            <LinearGradientBrush>
                <GradientStop Color="#108ee9" Offset="0"/>
                <GradientStop Color="#87d068" Offset="1"/>
            </LinearGradientBrush>
        </atom:GradientColorPicker.DefaultValue>
    </atom:GradientColorPicker>
</StackPanel>
```

## 状态模型

ColorPicker 的状态流按以下路径收敛：

```text
Public API / inherited command / item source / user input
  -> 控件实例状态
  -> effective state / pseudo-class / template property
  -> ControlTheme selector / presenter / renderer
  -> Gallery 可观察行为
```

状态维护规则：

- Disabled 或不可交互状态优先屏蔽 pointer、keyboard、motion 和提交类反馈。
- open/close、collection/filter、input/value、motion、visual option 状态由控件实例或明确的数据 owner 推导，不能在 template part 之间双向竞争。
- `Value`、trigger 色块、trigger 文本、picker presenter 和 Form 值必须由同一份 current value 派生；清空状态以 `Value=null` 为源头。
- 模板重套用时必须把 public API 对应状态回放到新的 part、伪类和主题变量。
- 集合、弹层、异步、动效或窗口相关状态必须能处理 reset、close、cancel、detach 和 owner 释放。

## 主题与 Design Token

ColorPicker 的视觉模型由控件模板、ControlTheme、SharedToken 和 ColorPicker Own Token 共同构成。

| 主题文件 | 职责 |
| --- | --- |
| `AbstractColorPickerTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `ColorBlockTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `ColorPickerPaletteGroupTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `ColorPickerTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `AbstractColorPickerSliderTrackTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `ColorPickerSliderTrackTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `ColorSliderTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `ColorSliderThumbTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `GradientColorPickerTrackTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `GradientColorSliderTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `AbstractColorPickerViewTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `ColorPickerInput.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `ColorPickerViewTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `ColorSpectrumTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `GradientColorPickerViewTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `GradientColorPickerTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `PaletteColorItemTheme.axaml` | 定义集合项、容器项或局部单元的状态视觉。 |

ColorPicker 拥有独立 Control identity；`ColorPickerToken` 只表达 ColorPicker Own Token 语义，不承载 open/close、collection/filter、input/value、motion 或 visual option 运行时状态。Control 级 Global Token 覆盖与 Own Token 通过 `ColorPickerTokenResource` 统一读取。

主题维护规则：

- 不删除或重命名已经稳定的 ControlTheme key、template part、伪类和资源 key。
- 不把可由 AXAML 表达的模板状态迁移为 C# 动态创建视觉。
- 不把 hover、pressed、selected、expanded、loading、filter、popup open 等运行时状态写入 Token。
- Browser 或平台特化主题必须保持同一 API 的语义一致。

Token 来源：

ColorPicker Token 只表达组件级视觉变量，例如尺寸、间距、颜色、圆角、阴影、图标尺寸和弹层边界。Token 不承载运行时选择、展开、加载、错误、上传任务、过滤条件或业务状态。

当前 Token scope：

- `ColorPickerToken`，scope id 为 `ColorPicker`，源码位于 `src/AtomUI.Desktop.Controls.ColorPicker/ColorPickerToken.cs`。

## AOT 与裁剪注意事项

资源和 AOT 约束：

- 不通过运行时反射扫描 public API、Token 或 Gallery 示例数据。
- 不把可静态声明的模板结构迁移到 C# 动态创建。
- 异步加载、上传、弹层和窗口生命周期必须能取消或释放。
- 缓存对象必须与控件、窗口、弹层或数据 owner 生命周期一致。
- Source generator 生成文件不手工编辑；需要修改时改输入源或 generator。

性能边界：

- 控件应优先复用 Avalonia 原生虚拟化、模板绑定和资源系统。
- 避免为每次状态变化创建不必要的视觉对象、订阅或动画对象。
- 大集合控件必须保证 container recycle 后不会泄漏旧 item 状态。

## 源码索引

主要源码文件：

- `src/AtomUI.Desktop.Controls.ColorPicker`：代表文件包括 `AbstractColorPicker.cs`、`AtomUIColorPickerThemesProvider.cs`、`ThemeManagerBuilderExtensions.cs`、`ColorBlock.cs`、`ColorChangedEventArgs.cs` 等。
- `src/AtomUI.Desktop.Controls.ColorPicker/ColorSlider`：10 个文件，代表文件 `AbstractColorPickerSliderTrack.cs`、`AbstractColorSlider.cs`、`ColorPickerSliderTrack.cs`、`ColorSlider.cs`、`ColorSliderPseudoClass.cs` 等。
- `src/AtomUI.Desktop.Controls.ColorPicker/ColorView`：6 个文件，代表文件 `AbstractColorPickerView.cs`、`ColorPickerInput.cs`、`ColorPickerView.cs`、`ColorSpectrum.cs`、`ColorSpectrumPseudoClass.cs` 等。
- `src/AtomUI.Desktop.Controls.ColorPicker/GeneratedFiles/AtomUI.Generator/AtomUI.Generator.Localization`：生成 Catalog descriptor、语言模块注册入口和 `ColorPickerLangResource` 扩展。
- `src/AtomUI.Desktop.Controls.ColorPicker/GeneratedFiles/AtomUI.Generator/AtomUI.Generator.ResourceHost.ScopedResourceHostGenerator`：1 个文件，代表文件 `GenerateScopedResourceHostAttribute.g.cs`。
- `src/AtomUI.Desktop.Controls.ColorPicker/GeneratedFiles/AtomUI.Generator/AtomUI.Generator.TokenResourceKeyGenerator`：生成 `GeneratedControlPackageRegistration.g.cs`、`GeneratedThemeSchema.g.cs` 和 `TokenResourceConst.g.cs`。
- `src/AtomUI.Desktop.Controls.ColorPicker/GeneratedFiles/AtomUI.Generator/AtomUI.Generator.ThemeAssetManifestGenerator`：生成独立主题叶子的 `GeneratedControlThemeAssetManifest.g.cs`。
- `src/AtomUI.Desktop.Controls.ColorPicker/Localization`：`ColorPickerLangResourceKind.cs` 定义稳定 Catalog，`en-US.xlf`、`zh-CN.xlf`、`zh-TW.xlf` 提供内置翻译。
- `src/AtomUI.Desktop.Controls.ColorPicker/Properties`：1 个文件，代表文件 `AssemblyInfo.cs`。
- `src/AtomUI.Desktop.Controls.ColorPicker/Themes`：21 个文件，代表文件 `AbstractColorPickerTheme.axaml`、`AbstractColorPickerTheme.cs`、`ColorBlockTheme.axaml`、`ColorPickerPaletteGroupTheme.axaml`、`ColorPickerTheme.axaml` 等。
- `src/AtomUI.Desktop.Controls.ColorPicker/Utils`：5 个文件，代表文件 `ColorPickerHelpers.cs`、`Hsv.cs`、`IncrementAmount.cs`、`Rgb.cs`、`TransparentBgBrushUtils.cs`。

职责边界：

- 控件主文件保留 public/protected API、Avalonia 属性注册、事件和主要生命周期入口。
- Theme 文件负责静态视觉结构、template part、selector 和资源绑定。
- Token 文件只提供组件视觉变量，不保存实例状态。
- Gallery 文件只展示用法和示例，不作为运行时逻辑 owner。

## 相关文档

- 源设计文档：`docs/controls/desktop/data-entry/color-picker/overview.md`
- 实现文档：`docs/controls/desktop/data-entry/color-picker/implementation.md`
- Token 文档：`docs/controls/desktop/data-entry/color-picker/token.md`
- 变更记录：`docs/controls/desktop/data-entry/color-picker/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/date-picker/index-cn.md

# DatePicker

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

DatePicker 是 AtomUI 桌面控件体系中的日期选择控件，用于单日期、周、月份、季度、年份以及日期范围的日历面板选择，并通过可选的包含式日期边界约束面板导航和用户选择。

DatePicker 不负责业务日程系统、时间选择或完整日期时间解析服务。这些职责应由业务层、组合控件或更专用的 AtomUI 控件承担。

主要源码入口：

- `src/AtomUI.Desktop.Controls/DatePicker`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/DataEntry/DatePicker` |
| 状态 | Stable |

## 何时使用

DatePicker 的设计语言围绕控件职责、可观察状态和主题契约组织，而不是围绕模板节点组织。

| 维度 | 含义 | DatePicker 中的表达 |
| --- | --- | --- |
| 产品语义 | 控件在界面中承担的稳定职责。 | DatePicker 是 AtomUI 桌面控件体系中的日期选择控件，用于单日期、日期范围和日历面板选择。 |
| 内容承载 | 用户数据、展示内容、集合项或操作入口如何进入控件。 | `HeaderBackground`。 |
| 状态反馈 | public API、内部状态和伪类如何形成用户可感知反馈。 | selection/checked/active、visual option。 |
| 主题语义 | ControlTheme、SharedToken、控件 Token 和模板绑定如何表达视觉。 | DatePicker Token + ControlTheme。 |

## 公共 API

DatePicker 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。

核心 public surface 按语义分组维护：

| 契约组 | 代表成员 | 维护含义 |
| --- | --- | --- |
| 内容与数据 | `HeaderBackground` | 定义控件展示内容、输入数据、模板或业务对象入口。 |
| 选择与集合 | `PickerMode`、`RangeEndSelectedDate`、`RangeStartSelectedDate`、`SelectedDateTime` | 维护提交值、范围端点和选择颗粒度；`SelectedDateTime`、`RangeStartSelectedDate`、`RangeEndSelectedDate` 默认 `TwoWay` 绑定并启用 Avalonia data validation。 |
| 日期边界 | `MinDate`、`MaxDate` | 以包含边界限制可选 picker unit 和面板导航范围；默认值均为 `null`，表示对应方向无边界。 |
| 弹层显示游标 | `PickerDisplayDate`；内部 `Calendar.DisplayDate`、`DisplayDateStart`、`DisplayDateEnd` | 维护弹出面板打开时显示到哪个日期区域，不代表已选值。 |
| 交互与状态 | `IsFloatingArrowPosition`、`IsHorizontalFlipped`、`IsNeedConfirm`、`IsShowNow`、`IsShowTime`、`IsTodayHighlighted` | 表达用户可观察状态、可用性、清除、加载或反馈语义。 |
| 视觉与布局 | `RangePickerIndicatorOffsetEnd`、`RangePickerIndicatorOffsetStart` | 影响尺寸、位置、颜色、形状、密度和模板视觉变量。 |
| 其他稳定入口 | `ClockIdentifier`、`DefaultDateTime`、`Format` | 保留为 public surface，变更前需确认 Gallery 和用户 XAML 依赖。 |

日期边界 public API：

| 属性 | 类型 | 默认值 | 契约 |
| --- | --- | --- | --- |
| `MinDate` | `DateTime?` | `null` | 最小可选日期，边界包含在有效范围内；忽略时间部分并按 `PickerMode` 归一化。 |
| `MaxDate` | `DateTime?` | `null` | 最大可选日期，边界包含在有效范围内；忽略时间部分并按 `PickerMode` 归一化。 |

`RangeDatePicker` 通过 `AddOwner` 复用 `DatePicker.MinDateProperty` 和 `DatePicker.MaxDateProperty`，单值与范围选择使用同一日期边界契约。日期边界只限制可选值和面板导航，不公开内部 `Calendar.BlackoutDates`，也不承担动态业务禁用规则。

当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。

主要公开类型与枚举：

- 类型：`Calendar`、`CalendarItem`、`ChoosingStatusEventArgs`、`DatePicker`、`DatePickerPresenter`、`DateSelectedEventArgs`、`DualMonthArrowDecoratedBox`、`DualMonthCalendarItem`、`DualMonthRangeCalendar`、`DualMonthRangeDatePickerPresenter`、`RangeCalendar`、`RangeCalendarItem`、`RangeDatePicker`、`RangeDatePickerPresenter` 等 19 项。
- 枚举：`DatePickerMode`，取值为 `Date`、`Week`、`Month`、`Quarter`、`Year`。

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_CalendarItem` | `CalendarItem` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_CalendarView` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_ClearUpButton` | `?` | 承载用户触发入口、导航或关闭动作。 |
| `PART_ConfirmButton` | `?` | 承载用户触发入口、导航或关闭动作。 |
| `PART_ContentRightAddOnPresenter` | `?` | 展示用户内容、文本、图标或模板化数据。 |
| `PART_HeaderButton` | `HeadTextButton` | 承载用户触发入口、导航或关闭动作。 |
| `PART_HeaderFrame` | `?` | 承载根视觉、边框、背景或尺寸基线。 |
| `PART_HeaderLayout` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_InfoInputBox` | `?` | 承载文本输入、过滤、显示或编辑入口。 |
| `PART_ItemFrame` | `?` | 承载根视觉、边框、背景或尺寸基线。 |
| `PART_ItemRootLayout` | `?` | 承载根视觉、边框、背景或尺寸基线。 |
| `PART_MonthView` | `Grid` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_MonthViewLayout` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_NextButton` | `IconButton` | 承载用户触发入口、导航或关闭动作。 |
| `PART_NextMonthButton` | `IconButton` | 承载用户触发入口、导航或关闭动作。 |
| `PART_NowButton` | `?` | 承载用户触发入口、导航或关闭动作。 |
| `PART_Popup` | `?` | 承载弹层宿主、打开关闭或候选内容。 |
| `PART_PreviousButton` | `IconButton` | 承载用户触发入口、导航或关闭动作。 |
| `PART_PreviousMonthButton` | `IconButton` | 承载用户触发入口、导航或关闭动作。 |
| `PART_RangePickerArrow` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_RangePickerIndicator` | `?` | 展示指示器、进度、分页或状态反馈。 |
| `PART_SecondaryHeaderButton` | `?` | 承载用户触发入口、导航或关闭动作。 |
| `PART_SecondaryInfoInputBox` | `?` | 承载文本输入、过滤、显示或编辑入口。 |
| `PART_SecondaryMonthView` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| 其他 part | 7 项 | 参见源码和主题文件；维护时按同一生命周期规则检查。 |

控件专属或内部伪类包括 `Blackout=:blackout`、`BtnFocusedPC`、`CalendarDayButtonPseudoClass.Blackout`、`CalendarDayButtonPseudoClass.DayFocused`、`CalendarDayButtonPseudoClass.RangeEnd`、`CalendarDayButtonPseudoClass.RangeMiddle`、`CalendarDayButtonPseudoClass.RangePreviewEnd`、`CalendarDayButtonPseudoClass.RangePreviewMiddle`、`CalendarDayButtonPseudoClass.RangePreviewStart`、`CalendarDayButtonPseudoClass.RangeStart`、`CalendarDayButtonPseudoClass.Today`、`CalendarDayButtonPseudoClass.WeekHoverStart`、`CalendarDayButtonPseudoClass.WeekHoverMiddle`、`CalendarDayButtonPseudoClass.WeekHoverEnd`、`CalendarDayButtonPseudoClass.WeekSelectionStart`、`CalendarDayButtonPseudoClass.WeekSelectionMiddle`、`CalendarDayButtonPseudoClass.WeekSelectionEnd`、`CalendarDisabledPC`、`DayFocused=:dayfocused`、`RangeEnd=:range-end`、`RangeMiddle=:range-middle`、`RangePreviewEnd=:range-preview-end`、`RangePreviewMiddle=:range-preview-middle`、`RangePreviewStart=:range-preview-start` 等。这些伪类属于主题 selector 可观察契约，不能在未同步主题和 Gallery 的情况下重命名或删除。

## 事件与命令

DatePicker 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。
当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。
- 类型：`Calendar`、`CalendarItem`、`ChoosingStatusEventArgs`、`DatePicker`、`DatePickerPresenter`、`DateSelectedEventArgs`、`DualMonthArrowDecoratedBox`、`DualMonthCalendarItem`、`DualMonthRangeCalendar`、`DualMonthRangeDatePickerPresenter`、`RangeCalendar`、`RangeCalendarItem`、`RangeDatePicker`、`RangeDatePickerPresenter` 等 19 项。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/DatePicker/Views/DatePickerShowCase.axaml:35`

Gallery key：`ExamplesContent` / item `0`

```axaml
<StackPanel Orientation="Vertical" Spacing="10">
    <atom:DatePicker PickerMode="Date"
                     PlaceholderText="选择日期" />
    <atom:DatePicker PickerMode="Week"
                     PlaceholderText="选择周" />
    <atom:DatePicker PickerMode="Month"
                     PlaceholderText="选择月份" />
    <atom:DatePicker PickerMode="Quarter"
                     PlaceholderText="选择季度" />
    <atom:DatePicker PickerMode="Year"
                     PlaceholderText="选择年份" />
</StackPanel>
```

### 切换不同的选择器

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/DatePicker/Views/DatePickerShowCase.axaml:57`

Gallery key：`ExamplesContent` / item `1`

```axaml
<StackPanel Orientation="Horizontal" Spacing="12">
    <atom:Select Width="104"
                 Mode="Single"
                 OptionsSource="{Binding PickerTypeOptions}"
                 SelectedOption="{Binding SelectedPickerOption}" />
    <Grid>
        <atom:TimePicker Width="240"
                         PlaceholderText="{Binding SelectedPickerPlaceholderText}"
                         IsVisible="{Binding IsTimePickerVisible}" />
        <atom:DatePicker Width="240"
                         PlaceholderText="{Binding SelectedPickerPlaceholderText}"
                         IsVisible="{Binding IsDatePickerVisible}"
                         PickerMode="{Binding SelectedPickerMode}" />
    </Grid>
</StackPanel>
```

### SelectedDateTime 绑定

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/DatePicker/Views/DatePickerShowCase.axaml:83`

Gallery key：`ExamplesContent` / item `2`

```axaml
<StackPanel Orientation="Vertical" Spacing="16">
    <StackPanel Orientation="Vertical" Spacing="12">
        <atom:DatePicker Width="240"
                         SelectedDateTime="{Binding BoundSelectedDateTime}"
                         PlaceholderText="选择日期" />
        <StackPanel Orientation="Horizontal" Spacing="8">
            <atom:TextBlock VerticalAlignment="Center"
                            Text="选中值：" />
            <atom:TextBlock VerticalAlignment="Center"
                            Text="{Binding BoundSelectedDateTimeText}" />
        </StackPanel>
        <StackPanel Orientation="Horizontal" Spacing="8">
            <atom:Button SizeType="Small"
                         Click="SetBoundSelectedDateTimeTomorrow"
                         Content="设置为明天" />
            <atom:Button SizeType="Small"
                         Click="ClearBoundSelectedDateTime"
                         Content="清空" />
        </StackPanel>
    </StackPanel>
    <StackPanel Orientation="Vertical" Spacing="12">
        <atom:RangeDatePicker PickerMode="Date"
                              IsShowTime="False"
                              RangeStartSelectedDate="{Binding BoundRangeStartSelectedDate}"
                              RangeEndSelectedDate="{Binding BoundRangeEndSelectedDate}"
                              PlaceholderText="开始日期"
                              SecondaryPlaceholderText="结束日期" />
        <StackPanel Orientation="Horizontal" Spacing="8">
            <atom:TextBlock VerticalAlignment="Center"
                            Text="选中范围：" />
            <atom:TextBlock VerticalAlignment="Center"
                            Text="{Binding BoundRangeSelectedDateText}" />
        </StackPanel>
        <StackPanel Orientation="Horizontal" Spacing="8">
            <atom:Button SizeType="Small"
                         Click="SetBoundSelectedDateRangeThisWeek"
                         Content="设置为本周" />
            <atom:Button SizeType="Small"
                         Click="ClearBoundSelectedDateRange"
                         Content="清空" />
        </StackPanel>
    </StackPanel>
</StackPanel>
```

### 弹出面板显示日期

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/DatePicker/Views/DatePickerShowCase.axaml:137`

Gallery key：`ExamplesContent` / item `3`

```axaml
<atom:DatePicker PickerDisplayDate="2026-10-20"
```

## 状态模型

DatePicker 的状态流按以下路径收敛：

```text
Public API / inherited command / item source / user input
  -> 控件实例状态
  -> effective state / pseudo-class / template property
  -> ControlTheme selector / presenter / renderer
  -> Gallery 可观察行为
```

状态维护规则：

- Disabled 或不可交互状态优先屏蔽 pointer、keyboard、motion 和提交类反馈。
- selection/checked/active、visual option 状态由控件实例或明确的数据 owner 推导，不能在 template part 之间双向竞争。
- `PickerMode` 决定选择颗粒度和初始面板：`Date`、`Week` 使用月视图，`Month`、`Quarter` 使用年视图，`Year` 使用十年视图。目标颗粒度不能继续降级到更细面板。
- `SelectedDateTime`、`DefaultDateTime` 和 `PickerDisplayDate` 必须保持语义分离：`SelectedDateTime` 是已提交值，`DefaultDateTime` 是默认选中值/reset 值，`PickerDisplayDate` 只作为弹出面板打开时的显示锚点。
- `SelectedDateTime` 是单值 DatePicker 的受控 Form 值入口，默认 `BindingMode.TwoWay`，并通过 Avalonia `DataValidationErrors` 参与原生数据校验。
- `MinDate` 和 `MaxDate` 是包含式 picker unit 边界。两者先忽略时间部分，再按当前 `PickerMode` 归一化；`null` 表示对应方向不受限制。
- 当归一化后的 `MinDate` 晚于 `MaxDate` 时，有效范围收敛为 `MinDate` 所在的一个 picker unit，但控件不得修改或回写调用方设置的原始属性值。
- 外部受控值越界时，`SelectedDateTime`、`RangeStartSelectedDate` 和 `RangeEndSelectedDate` 保持不变，输入框继续显示外部值；Calendar 不标记越界值为选中，确认操作不可提交该值。用户选择有效日期后，才按既有 TwoWay 契约更新受控值。
- 设置 `PickerDisplayDate` 后不得写入 `SelectedDateTime`，不得改变输入框文本、Form value 或清除按钮状态；当已有已选值时，弹出面板仍优先围绕已选值展示。
- DatePicker / RangeDatePicker 必须把 `DataValidationErrors`、FormStatus 和显式 Status 投射到 shared `InputControlFrame`；range indicator 等附属视觉读取 `EffectiveStatus`，native error 优先于 Form/显式 warning/error 状态。Calendar 和 picker panel 只拥有日期选择、范围预览和面板交互状态。
- `PickerMode=Week` 的月视图是带周序号列的 8 列 week panel，不是普通日期面板的 7 个日期按钮逐个选中；选中视觉和 hover 视觉都必须按整周连续行渲染，不能退回单个日期按钮的普通 pointerover 背景。
- 非 `Date` 颗粒度仍使用 `DateTime?` 保存提交值：`Week` 保存 ISO 周起始日，`Month` 保存当月 1 日，`Quarter` 保存季度首月 1 日，`Year` 保存当年 1 月 1 日。
- `IsShowTime` 只在 `PickerMode=Date` 时形成有效时间选择；其他颗粒度忽略时间面板和时间拼接。
- 范围选择的 committed 状态和 hover preview 状态必须分开：`:selected`、`:range-start`、`:range-end`、`:range-middle` 只来自真实端点；hover 只写入 `:range-preview-start`、`:range-preview-end`、`:range-preview-middle`，其中 preview start/end 在视觉上按临时端点显示，但不能污染真实提交状态。
- 模板重套用时必须把 public API 对应状态回放到新的 part、伪类和主题变量。
- 集合、弹层、异步、动效或窗口相关状态必须能处理 reset、close、cancel、detach 和 owner 释放。

## 主题与 Design Token

DatePicker 的视觉模型由 `InputControlFrame` 输入表面、InfoPicker 输入子控件、控件模板、ControlTheme、SharedToken 和必要的控件 Token 共同构成。输入表面状态由 shared frame 统一表达，DatePicker 主题只扩展日期、范围和弹层内容。

| 主题文件 | 职责 |
| --- | --- |
| `CalendarButtonTheme.axaml` | 定义局部操作入口、按钮或 handle 的状态视觉。 |
| `CalendarDayButtonTheme.axaml` | 定义局部操作入口、按钮或 handle 的状态视觉。 |
| `CalendarItemTheme.axaml` | 定义集合项、容器项或局部单元的状态视觉。 |
| `CalendarTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `InfoPickerTextBoxTheme.axaml` | 通过 `StyleVariant=Borderless` 提供内部日期文本输入的无 chrome 布局。 |
| `InputControlFrameTheme.axaml` | 提供输入表面 variant、effective status、focus、disabled、error、warning、CompactSpace 和 motion。 |
| `DualMonthCalendarItemTheme.axaml` | 定义集合项、容器项或局部单元的状态视觉。 |
| `DualMonthRangeCalendarTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `RangeCalendarTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `DatePickerPresenterTheme.axaml` | 定义布局、内容承载或框架节点视觉。 |
| `DatePickerTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `DualMonthRangeDatePickerPresenterTheme.axaml` | 定义布局、内容承载或框架节点视觉。 |
| `RangeDatePickerPresenterTheme.axaml` | 定义布局、内容承载或框架节点视觉。 |
| `RangeDatePickerTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `TimedRangeDatePickerPresenterTheme.axaml` | 定义布局、内容承载或框架节点视觉。 |

DatePicker 使用 `DatePickerToken` 作为控件 Token scope。Token 只表达组件视觉语义，不承载 selection/checked/active、visual option 运行时状态。

主题维护规则：

- 不删除或重命名已经稳定的 ControlTheme key、template part、伪类和资源 key。
- 不把可由 AXAML 表达的模板状态迁移为 C# 动态创建视觉。
- 不把 hover、pressed、selected、expanded、loading、filter、popup open 等运行时状态写入 Token。
- 日期边界外的 Calendar cell 保持可见并使用 disabled 状态视觉，不通过隐藏 cell 表达不可选择状态。
- Browser 或平台特化主题必须保持同一 API 的语义一致。

Token 来源：

DatePicker Token 只表达组件级视觉变量，例如尺寸、间距、颜色、圆角、阴影、图标尺寸和弹层边界。Token 不承载运行时选择、展开、加载、错误、上传任务、过滤条件或业务状态。

当前 Token scope：

- `DatePickerToken`，scope id 为 `DatePicker`，源码位于 `src/AtomUI.Desktop.Controls/DatePicker/DatePickerToken.cs`。

## AOT 与裁剪注意事项

资源和 AOT 约束：

- 不通过运行时反射扫描 public API、Token 或 Gallery 示例数据。
- 不把可静态声明的模板结构迁移到 C# 动态创建。
- 异步加载、上传、弹层和窗口生命周期必须能取消或释放。
- 缓存对象必须与控件、窗口、弹层或数据 owner 生命周期一致。
- Source generator 生成文件不手工编辑；需要修改时改输入源或 generator。
- 日期边界模型使用静态注册的 Avalonia 属性和 internal 纯值计算，不引入反射、动态类型发现、运行时代码生成或 trimming 注解。

性能边界：

- 控件应优先复用 Avalonia 原生虚拟化、模板绑定和资源系统。
- 避免为每次状态变化创建不必要的视觉对象、订阅或动画对象。
- 大集合控件必须保证 container recycle 后不会泄漏旧 item 状态。
- effective range 计算和单值/端点校验保持 O(1)；面板刷新复用现有 `CalendarViewState`、panel model 和 generated button，不为每个 cell 创建日期约束对象或额外订阅。

## 源码索引

主要源码文件：

- `src/AtomUI.Desktop.Controls/DatePicker`：DatePicker 控件家族根目录，代表文件 `DatePicker.cs`、`RangeDatePicker.cs`、`DatePickerPresenter.cs`、`DatePickerFormattingHelper.cs`、`DatePickerDateRangeConstraint.cs`、`DatePickerToken.cs`、`DualMonthRangeDatePickerPresenter.cs` 等。
- `src/AtomUI.Desktop.Controls/DatePicker/CalendarView`：CalendarView runtime。`State` 保存归一化状态和 action，`Models` 保存纯 panel model，`Rendering` 将 model 应用到 generated buttons，`Infrastructure` 封装 culture 和 pointer tracking。
- `src/AtomUI.Desktop.Controls/DatePicker/Localization`：`DatePickerLangResourceKind.cs` 定义稳定 Catalog，`en-US.xlf`、`zh-CN.xlf`、`zh-TW.xlf` 提供内置翻译。
- `src/AtomUI.Desktop.Controls/DatePicker/Themes`：19 个文件，代表文件 `CalendarButtonTheme.axaml`、`CalendarButtonTheme.cs`、`CalendarDayButtonTheme.axaml`、`CalendarItemTheme.axaml`、`CalendarItemTheme.cs` 等。
- `src/AtomUI.Desktop.Controls/Primitives/InfoPickerInput/InfoPickerTextBox.cs` 与 `Themes/InfoPickerTextBoxTheme.axaml`：DatePicker 输入框使用的 internal 子控件及其文本 presenter、padding 基础视觉。

职责边界：

- 控件主文件保留 public/protected API、Avalonia 属性注册、事件和主要生命周期入口。
- Theme 文件负责静态视觉结构、template part、selector 和资源绑定。
- Token 文件只提供组件视觉变量，不保存实例状态。
- Gallery 文件只展示用法和示例，不作为运行时逻辑 owner。
- CalendarView 的深度重构必须以 [CalendarView 系统性优化设计](calendar-view-system-optimization.md) 中定义的单向状态模型、panel model、renderer 和生命周期规则为边界。

## 相关文档

- 源设计文档：`docs/controls/desktop/data-entry/date-picker/overview.md`
- 实现文档：`docs/controls/desktop/data-entry/date-picker/implementation.md`
- Token 文档：`docs/controls/desktop/data-entry/date-picker/token.md`
- 变更记录：`docs/controls/desktop/data-entry/date-picker/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/form/index-cn.md

# Form

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

Form 是 AtomUI 桌面数据录入体系中的表单容器，用于组织多个 `FormItem`，向子输入控件传播尺寸、状态、样式和验证配置，并统一处理表单值收集、验证、提交、重置和动态表单项删除。

Form 的职责是把输入控件接入表单语义。它不负责业务对象持久化、远程提交协议、权限控制、复杂 ViewModel 状态管理或跨页面流程编排；这些能力应由应用层或业务层承载。

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/DataEntry/Form` |
| 状态 | Stable |

## 何时使用

Form 的设计语言来自表单布局、标签语义、输入控件状态和验证反馈的组合。

| 维度 | 含义 | 典型表达 |
| --- | --- | --- |
| 布局 | 表单项在水平、垂直或行内布局中排列。 | `FormLayout`、`LabelColInfo`、`WrapperColInfo`。 |
| 标签 | 标签文本、冒号、必填标记、可选标记和 tooltip 表达字段语义。 | `LabelText`、`IsShowColon`、`RequiredMark`、`Tooltip`。 |
| 输入表面 | 表单统一向子控件传播尺寸和输入外观。 | `SizeType`、`StyleVariant`、`IsMotionEnabled`。 |
| 验证 | 表单项按触发时机和策略执行验证器。 | `ValidateTrigger`、`ValidateStrategy`、`ValidateDebounce`。 |
| 反馈 | 错误、警告、成功和验证中状态投射到输入控件及反馈图标。 | `ValidateStatus`、`ValidateResult`、`IsValidateFeedbackEnabled`。 |
| 操作 | 提交、重置和动态删除由专用按钮或事件进入 Form。 | `SubmitButton`、`ResetButton`、`ItemDeleteButton`。 |

`SizeType=Custom` 不是 Form 的第四套专属 token。Form 会把 `CustomizableSizeType` 传递给支持 `ICustomizableSizeTypeAware` 的子控件；对于只支持旧 `ISizeTypeAware` 的子控件，`Custom` 按 `Middle` 映射，保持旧控件的尺寸基线稳定。

## 公共 API

Form 的公共契约由 Form、FormItem、FormItemDecorator、FormValidateFeedback、SubmitButton、ResetButton、验证器和表单感知接口组成。

### 3.1 Form 容器契约

| API | 语义 |
| --- | --- |
| `FormLayout` | 控制表单项整体排列。`Horizontal` / `Vertical` 使用纵向表单项列表，`Inline` 使用横向表单项列表。 |
| `LabelColInfo` / `WrapperColInfo` | 水平布局下标签列和内容列的响应式 GridLength 信息。 |
| `LabelAlign` / `LabelWrapping` | 标签对齐和换行策略。 |
| `IsShowColon` | 水平布局下是否显示标签冒号。 |
| `RequiredMark`、`CustomRequireMark`、`CustomOptionalMark` | 必填或可选标记策略。 |
| `SizeType` | 表单统一尺寸密度，支持 `Large/Middle/Small/Custom`。 |
| `StyleVariant` | 传递给支持输入外观变体的子控件。 |
| `IsMotionEnabled` | 传递给支持动效开关的子控件。 |
| `ValidateTrigger` | 表单默认验证触发时机，默认 `OnChanged`，即字段值变化时触发验证。 |
| `IsValidateFeedbackEnabled` | 是否创建并传递验证反馈控件。 |
| `InitialValues` | 表单加载时按 `FieldName` 写入初始值。 |
| `IsFormValid` | 基于表单项验证状态聚合的只读有效性状态。 |
| `Validate()` / `ValidateAsync()` | 手动验证当前可验证表单项。 |
| `Submit()` | 验证成功后收集 `FormValues` 并触发提交。 |
| `Reset()` | 清空表单项值、取消未完成验证并重置验证状态。 |
| `SetFormValues()` | 按 `FieldName` 批量写入表单项值。 |
| `DeleteFormItem()` | 删除指定表单项。 |

事件契约：

| 事件 | 语义 |
| --- | --- |
| `AboutToValidate` | 表单验证开始前触发。 |
| `Validated` | 验证结果需要对外通知时触发；独立验证失败时携带消息，提交成功时携带 values。 |
| `Submitted` | 提交成功后触发，携带收集到的 `FormValues`。 |
| `ItemValueChanged` | 任一表单项值变化后触发，并通知其他表单项。 |
| `ResetCompleted` | 重置流程完成后触发。 |

### 3.2 FormItem 契约

`FormItem` 是单个字段的表单项容器。它承载标签、字段名、验证器、帮助文案、额外内容、内容控件和当前验证状态。

稳定契约：

- `FieldName` 是表单值读写和验证消息聚合的字段键。
- `Content` 必须实现 `IFormItemAware`，除非该表单项显式关闭内容类型验证。
- `Validators` 按 `ValidateStrategy` 执行；error 结果写入内容控件的 `DataValidationErrors`，并同步投射到 `ValidateStatus`、`ValidateResult`、错误消息和 feedback。
- `Warning`、`Validating` 和 `Success` 是 Form 扩展状态，写入内容控件的 `FormStatus`；它们不写成 `DataValidationErrors` error。
- `ValidateTrigger` 可以继承 Form 默认值，也可以在 FormItem 上覆盖。
- `ValidateDebounce` 只影响延迟触发的表单项验证，不改变手动验证和提交验证的契约。
- `Help` 与验证消息共同决定表单项辅助信息区域是否保留空间。

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_BodyLayout` | `Grid` | FormItem 标签列和内容列布局。 |
| `PART_LabelLayout` | `Panel` | 标签、必填标记、tooltip、可选标记和冒号承载。 |
| `PART_ContentLayout` | `Panel` | 输入内容、Extra、错误消息和帮助文本承载。 |

### 3.3 子控件接入契约

表单内容控件通过以下接口接入 Form：

| 接口 | 职责 |
| --- | --- |
| `IFormItemAware` | 提供值读写、清空、值变化通知和扩展验证状态通知；error 真源仍是内容控件的 `DataValidationErrors`。 |
| `IFormItemFeedbackAware` | 接收 `FormValidateFeedback` 控件，用于在输入框内部展示反馈图标。 |
| `ICustomizableSizeTypeAware` | 接收 `CustomizableSizeType`，包含 `Custom` 尺寸。 |
| `ISizeTypeAware` | 旧尺寸接口；Form 将 `Custom` 映射为 `Middle` 后传递。 |
| `IInputControlStyleVariantAware` | 接收 Form 的 `StyleVariant`。 |
| `IMotionAwareControl` | 接收 Form 的 `IsMotionEnabled`。 |

`FormItemDecorator` 用于把一个或多个输入控件组合成单个表单项内容。它自身实现 Form 接入接口，并把 value、`FormStatus`、feedback、size、motion 和 style variant 转发给子控件；涉及 error 时必须保持 Form-owned `DataValidationErrors` 与子控件 `NativeValidationStatus` 目标一致。

### 3.4 按钮契约

`SubmitButton` 和 `ResetButton` 继承 Button，通过冒泡路由事件与最近的 Form 协作。`SubmitButton.IsWatchValidateResult=true` 时会查找逻辑树或视觉树中的 Form，并根据 `Form.IsFormValid` 控制自身 `IsEnabled`。

## 事件与命令

事件契约：
| 事件 | 语义 |
`SubmitButton` 和 `ResetButton` 继承 Button，通过冒泡路由事件与最近的 Form 协作。`SubmitButton.IsWatchValidateResult=true` 时会查找逻辑树或视觉树中的 Form，并根据 `Form.IsFormValid` 控制自身 `IsEnabled`。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/Form/Views/FormShowCase.axaml:46`

Gallery key：`ExamplesContent` / item `0`

```axaml
<atom:Form LabelColInfo="8*"
           WrapperColInfo="16*"
           MinWidth="600"
           HorizontalAlignment="Left"
           Name="BasicForm"
           AttachedToVisualTree="HandleBasicFormAttached">
    <atom:FormItem LabelText="用户名"
                   FieldName="username"
                   IsRequired="True">
        <atom:FormItem.Validators>
            <atom:FormValidatorProvider>
                <atom:FormStringNotEmptyValidator Message="请输入用户名！" />
            </atom:FormValidatorProvider>
        </atom:FormItem.Validators>
        <atom:LineEdit />
    </atom:FormItem>
    <atom:FormItem LabelText="密码"
                   FieldName="password"
                   IsRequired="True">
        <atom:FormItem.Validators>
            <atom:FormValidatorProvider>
                <atom:FormStringNotEmptyValidator Message="请输入密码！" />
            </atom:FormValidatorProvider>
        </atom:FormItem.Validators>
        <atom:LineEdit RevealPassword="False"
                       PasswordChar="•"
                       IsEnableRevealButton="True" />
    </atom:FormItem>
    <atom:FormItem FieldName="remember">
        <atom:CheckBox Content="记住我" />
    </atom:FormItem>

    <atom:FormActionsItem>
        <atom:SubmitButton />
    </atom:FormActionsItem>
</atom:Form>
```

### 表单布局

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/Form/Views/FormShowCase.axaml:152`

Gallery key：`ExamplesContent` / item `2`

```axaml
<StackPanel Orientation="Vertical" Spacing="20">
    <StackPanel Orientation="Horizontal" Spacing="10">
        <atom:TextBlock VerticalAlignment="Center" Text="表单布局：" />
        <atom:OptionButtonGroup ButtonStyle="Outline" OptionCheckedChanged="HandleFormLayoutOptionCheckedChanged">
            <atom:OptionButton  Tag="{x:Static atom:FormLayout.Horizontal}" Content="水平" />
            <atom:OptionButton Tag="{x:Static atom:FormLayout.Vertical}" Content="垂直" />
            <atom:OptionButton IsChecked="True" Tag="{x:Static atom:FormLayout.Inline}" Content="行内" />
        </atom:OptionButtonGroup>
    </StackPanel>

    <atom:Form LabelColInfo="8*"
               WrapperColInfo="16*"
               Name="LayoutCaseForm"
               AttachedToVisualTree="HandleLayoutCaseFormAttached"
               DetachedFromVisualTree="HandleLayoutCaseFormDetached"
               FormLayout="{Binding FormLayout}"
               HorizontalAlignment="Left"
               MinWidth="600">
        <atom:FormItem LabelText="字段 A" FieldName="FieldA">
            <atom:LineEdit MinWidth="160"/>
        </atom:FormItem>
        <atom:FormItem LabelText="字段 B" FieldName="Field B">
            <atom:LineEdit MinWidth="160"/>
        </atom:FormItem>

        <atom:FormActionsItem>
            <atom:SubmitButton />
        </atom:FormActionsItem>
    </atom:Form>
</StackPanel>
```

### 标签可换行

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/Form/Views/FormShowCase.axaml:290`

Gallery key：`ExamplesContent` / item `4`

```axaml
<atom:Form LabelColInfo="120"
           WrapperColInfo="*"
           MinWidth="600"
           HorizontalAlignment="Left"
           LabelAlign="Left"
           IsShowColon="False"
           LabelWrapping="Wrap">
    <atom:FormItem LabelText="普通标签"
                   FieldName="username"
                   IsRequired="True">
        <atom:FormItem.Validators>
            <atom:FormValidatorProvider>
                <atom:FormStringNotEmptyValidator Message="请输入用户名！" />
            </atom:FormValidatorProvider>
        </atom:FormItem.Validators>
        <atom:LineEdit />
    </atom:FormItem>
    <atom:FormItem LabelText="一段超长标签文本"
                   FieldName="password"
                   IsRequired="True">
        <atom:FormItem.Validators>
            <atom:FormValidatorProvider>
                <atom:FormStringNotEmptyValidator Message="请输入用户名！" />
            </atom:FormValidatorProvider>
        </atom:FormItem.Validators>
        <atom:LineEdit />
    </atom:FormItem>
    <atom:FormItem LabelText="一段超长标签文本"
                   FieldName="password1">
        <atom:LineEdit />
    </atom:FormItem>

    <atom:FormActionsItem>
        <atom:SubmitButton />
    </atom:FormActionsItem>
</atom:Form>
```

### 必填样式

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/Form/Views/FormShowCase.axaml:634`

Gallery key：`ExamplesContent` / item `8`

```axaml
<StackPanel Orientation="Vertical" Spacing="20">
    <StackPanel Orientation="Horizontal" Spacing="10">
        <atom:TextBlock VerticalAlignment="Center" Text="必填标记：" />
        <atom:OptionButtonGroup ButtonStyle="Outline" OptionCheckedChanged="HandleFormSizeTypeChanged">
            <atom:OptionButton Tag="{x:Static atom:CustomizableSizeType.Small}" Content="小号" />
            <atom:OptionButton Tag="{x:Static atom:CustomizableSizeType.Middle}" IsChecked="True" Content="中号" />
            <atom:OptionButton Tag="{x:Static atom:CustomizableSizeType.Large}" Content="大号" />
        </atom:OptionButtonGroup>
    </StackPanel>

    <atom:Form LabelColInfo="8*"
               WrapperColInfo="16*"
               MinWidth="600"
               HorizontalAlignment="Left"
               SizeType="{Binding FormSizeType}">
        <atom:FormItem LabelText="输入框"
                       FieldName="input">
            <atom:LineEdit />
        </atom:FormItem>
        <atom:FormItem LabelText="数字输入框"
                       FieldName="inputNumber">
            <atom:NumericUpDown Width="200" />
        </atom:FormItem>
        <atom:FormItem LabelText="开关"
                       FieldName="switch">
            <atom:ToggleSwitch />
        </atom:FormItem>

        <atom:FormItem LabelText="提及"
                       FieldName="mentions">
            <atom:Mentions />
        </atom:FormItem>

        <atom:FormItem LabelText="选择器"
                       FieldName="select">
            <atom:Select HorizontalAlignment="Stretch"
                         Mode="Multiple"
                         OptionsSource="{Binding RequiredStyleSelectOptions}" />
        </atom:FormItem>

        <atom:FormItem LabelText="级联选择"
                       FieldName="cascader">
            <atom:Cascader HorizontalAlignment="Stretch" />
        </atom:FormItem>

        <atom:FormItem LabelText="树选择"
                       FieldName="treeSelect">
            <atom:TreeSelect HorizontalAlignment="Stretch" />
        </atom:FormItem>

        <atom:FormItem LabelText="日期选择器"
                       FieldName="datePicker">
            <atom:DatePicker PlaceholderText="请选择" />
        </atom:FormItem>

        <atom:FormItem LabelText="范围选择器"
                       FieldName="rangePicker">
            <atom:RangeDatePicker PlaceholderText="开始日期"
                                  SecondaryPlaceholderText="结束日期" />
        </atom:FormItem>

        <atom:FormActionsItem>
            <atom:SubmitButton />
        </atom:FormActionsItem>
    </atom:Form>
</StackPanel>
```

## 状态模型

Form 的核心状态流：

```text
Form config
  → Prepare / sync FormItem style values
  → FormItem content via IFormItemAware
  → value changed / blur / submit validation trigger
  → FormItem validators
  → DataValidationErrors for error
  → ValidateStatus / ValidateResult / feedback / messages as projection
  → Form IsFormValid aggregation
  → SubmitButton watch state and submit result
```

验证触发模型：

- `OnChanged` 是 Form 的默认触发时机；内容控件触发 `IFormItemAware.ValueChanged` 后按 `ValidateDebounce` 延迟验证，使提交后或编辑中的错误能够随输入及时更新。
- `OnSubmit` 仅在手动验证或提交时验证，适合显式要求只在提交入口展示错误的表单。
- `OnBlur` 在 FormItem 失去焦点时按 `ValidateDebounce` 延迟验证。
- 手动 `Validate()`、`ValidateAsync()` 和 `Submit()` 直接进入验证流程，不依赖输入变化触发。

验证策略模型：

| 策略 | 行为 |
| --- | --- |
| `StopWhenFirstFailed` | 顺序执行验证器，遇到第一个 error 后停止，同时保留 error 前已产生的 warning。 |
| `Sequential` | 顺序执行所有验证器。 |
| `Parallel` | 并行执行所有验证器并聚合结果。 |

验证结果模型：

- `Error` 会使 Form 聚合为无效状态，并阻止 `Submit()` 继续提交。
- error 状态以内容控件的 `DataValidationErrors.HasErrors` 为最高优先级；Form validators 产生的 error 也写入同一 native validation 通道。
- `Warning` 会展示警告状态和警告消息，但 Form 聚合只把 error 作为提交阻断条件。
- `Validating` 和 `Default` 在 `IsFormValid` 聚合中不视为有效完成状态。
- 重置会取消未完成验证、清空 Form-owned 消息并把表单项扩展状态恢复为 `Default`；它只能清理由 Form 写入的 validation error，不能清掉 binding 或 ViewModel 写入的 native error。

提交与重置模型：

```text
Submit()
  → cancel previous form validation run
  → ValidateAsync()
  → if success, collect FormValues by FieldName
  → Validated(success, values)
  → Submitted(values)
```

```text
Reset()
  → cancel pending form validation
  → mark IsResetting
  → FormItem.ResetItemValue()
  → NotifyReset()
  → IsFormValid = false
  → ResetCompleted
  → clear IsResetting on dispatcher
```

## 主题与 Design Token

Form 视觉由 Form 根模板、FormItem 模板、FormValidateFeedback 模板和按钮主题协作完成。

| 主题或资源 | 职责 |
| --- | --- |
| `FormTheme.axaml` | Form 根模板、ItemsPresenter、表单项排列方向、反馈模板和默认操作图标。 |
| `FormItemTheme.axaml` | 标签、内容、辅助信息、删除按钮、必填标记、冒号和尺寸 selector。 |
| `FormItemDecoratorTheme.axaml` | 组合表单控件的水平内容和 Extra 布局。 |
| `FormValidateFeedbackTheme.axaml` | 根据 `ValidateStatus` 选择成功、警告、错误或验证中反馈内容。 |
| `SubmitButtonTheme.axaml` | 提交按钮默认 primary 风格和本地化文案。 |
| `ResetButtonTheme.axaml` | 重置按钮默认 default 风格和本地化文案。 |
| `FormToken` | 标签颜色、必填标记颜色、标签字体和表单项间距。 |
| `SharedToken` | 输入高度、图标尺寸、状态色、字体和通用 spacing。 |

视觉兼容边界：

- `FormItem` 标签列和内容列必须继续由 `PART_BodyLayout`、`PART_LabelLayout`、`PART_ContentLayout` 协作。
- `FormLayout=Inline` 必须保持横向表单项排列和 `InlineItemSpacing`。
- `FormLayout=Horizontal/Vertical` 必须保持纵向表单项排列。
- `SizeType=Custom` 的 FormItem 标签最小高度默认走 `Middle` 分支，子输入控件的自定义尺寸由子控件自身属性决定。

Token 来源：

FormToken 是 Form 的控件级 Token scope，描述标签视觉、必填标记、冒号间距和表单项布局间距。输入控件自身高度、边框、状态色、图标尺寸、字体基础值和通用 spacing 来自 SharedToken 或对应输入控件 Token。

FormToken 不承载以下状态：

- `FieldName`、`InitialValues`、提交 values 或业务数据。
- `ValidateStatus`、`ValidateResult`、错误消息、警告消息或验证中状态。
- `FormLayout`、`RequiredMark`、`ValidateTrigger`、`ValidateStrategy` 等实例配置。
- 动态表单项数量、删除按钮显示状态或响应式 breakpoint 当前值。

## AOT 与裁剪注意事项

Form 不依赖运行时反射扫描控件能力。控件协作通过显式接口、固定 template part、路由事件、Avalonia property 和 XAML 模板完成。

资源和生命周期边界：

- FormItem 内容变化时释放旧内容的 `ValueChanged` 订阅、feedback 引用和尺寸/style/motion relay binding。
- FormItem detach 时释放 media breakpoint 订阅并取消验证。
- FormItem feedback 变化或禁用 feedback 时释放旧 feedback 状态 relay binding。
- Form 级验证运行通过 `_validationTokenSource` 取消旧提交或验证流程。
- SubmitButton watch 模式在 logical/visual detach 时释放 Form `IsFormValid` 订阅。

AOT 边界：

- `FormToken` 通过 token generator 显式注册，生成 token kind 和资源扩展。
- API 与 Token 契约由控件文档、源码 public surface、Token 类型或生成数据维护。
- Form 验证器是显式类型和接口调用，不依赖动态成员发现。
- AXAML 模板、DataTemplate 和 Form validator provider 是显式 XAML 入口，不依赖运行时 API 扫描。

## 源码索引

主要源码：

- `src/AtomUI.Controls/Form/FormEnums.cs`：布局、必填标记、验证触发、验证策略和验证状态枚举。
- `src/AtomUI.Controls/Form/IForm.cs`：Form 容器操作接口。
- `src/AtomUI.Controls/Form/IFormItem.cs`：FormItem 字段级值读写和验证接口。
- `src/AtomUI.Controls/Form/IFormItemAware.cs`：输入控件接入 Form 的值、清空、状态和事件接口。
- `src/AtomUI.Controls/Form/IFormValidateFeedback.cs`、`FormValidateFeedback.cs`：验证反馈控件契约和基础实现。
- `src/AtomUI.Controls/Form/IFormValidator.cs`、`Validators/*.cs`：验证器契约和内置验证器。
- `src/AtomUI.Controls/Form/FormValues.cs`、`FormValidateMessage.cs`、事件参数类型：表单值和验证事件数据结构。
- `src/AtomUI.Desktop.Controls/Form/Form.cs`：桌面 Form 容器、表单项配置同步、验证聚合、提交、重置、动态删除和 ItemsControl 容器逻辑。
- `src/AtomUI.Desktop.Controls/Form/FormItem.cs`：字段级布局、模板接入、内容接入、Form 配置接收、反馈创建和响应式列宽。
- `src/AtomUI.Desktop.Controls/Form/FormItem.Validation.cs`：FormItem 验证运行、debounce、取消、策略执行、结果应用和消息 inlines 构建。
- `src/AtomUI.Desktop.Controls/Form/FormItemDecorator.cs`：组合输入控件的表单能力转发。
- `src/AtomUI.Desktop.Controls/Form/FormSizeTypeBindingHelper.cs`：`CustomizableSizeType` 到新旧尺寸接口的转发规则。
- `src/AtomUI.Desktop.Controls/Form/SubmitButton.cs`、`ResetButton.cs`：表单操作按钮和路由事件。
- `src/AtomUI.Desktop.Controls/Form/FormToken.cs`：Form 控件 Token。
- `src/AtomUI.Desktop.Controls/Form/Themes/*.axaml`：Form、FormItem、Decorator、Feedback 和操作按钮主题。

## 相关文档

- 源设计文档：`docs/controls/desktop/data-entry/form/overview.md`
- 实现文档：`docs/controls/desktop/data-entry/form/implementation.md`
- Token 文档：`docs/controls/desktop/data-entry/form/token.md`
- 变更记录：`docs/controls/desktop/data-entry/form/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/line-edit/index-cn.md

# LineEdit

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

LineEdit 是 AtomUI 桌面数据录入体系中的文本输入控件家族，用于承载单行文本、密码输入、搜索输入和多行文本输入。它以 Avalonia `TextBox` 文本编辑能力为基础，通过 `AbstractTextInput` 统一文本输入逻辑，通过 `InputControlFrame` 统一输入表面视觉，再接入 AtomUI 的尺寸、native validation error 投射、清除按钮、密码 reveal、字数统计、Addon、CompactSpace、Form 和 Token 体系。

LineEdit 家族包含以下稳定入口：

| 类型 | 定位 |
| --- | --- |
| `AbstractTextInput` | 公开但不作为直接实例化入口的抽象文本输入基类，统一尺寸、输入表面状态、清除、reveal、字数统计、Form、native validation、CompactSpace 和文本 viewport 度量。 |
| `TextBox` | AtomUI 基础文本框，继承 `AbstractTextInput`，提供单行文本编辑和基础输入模板。 |
| `LineEdit` | 标准单行输入框，继承 `AbstractTextInput`，提供外部 AddOn、单行布局和搜索/组合控件复用入口。 |
| `SearchEdit` | 搜索输入框，在 `LineEdit` 基础上加入搜索按钮、搜索按钮样式、加载态和搜索事件；独立契约见 [SearchEdit 桌面版架构设计](../search-edit/overview.md)。 |
| `TextArea` | 多行输入框，继承 `AbstractTextInput`，复用输入状态、尺寸、清除、字数统计、Form 和 TextArea 专属 resize 模型。 |
| `InputControlFrame` | internal 输入表面组合控件，统一 variant、effective status、边框、背景、圆角、阴影、交互伪类、CompactSpace 和动效；不作为公共控件入口。 |

继承与组合关系固定为：

```text
Avalonia.Controls.TextBox
        ↓
AbstractTextInput
   ├── TextBox
   ├── LineEdit
   └── TextArea

LineEdit → SearchEdit
TextBox  → EmbeddedTextBox
LineEdit → AutoCompleteLineEditBox
TextArea → AutoCompleteTextAreaBox

InputControlFrame
        ↓
AddOnDecoratedBox
        ├── SearchEditDecoratedBox
        ├── TextAreaDecoratedBox
        ├── SelectAddOnDecoratedBox
        ├── TreeSelectAddOnDecoratedBox
        ├── CascaderAddOnDecoratedBox
        └── ButtonSpinnerDecoratedBox
```

`AbstractTextInput` 只承载文本输入逻辑和状态归一；`InputControlFrame` 只承载输入表面视觉。`AddOnDecoratedBox` 及其派生类型负责 AddOn、内部内容和专用布局扩展，不重复声明输入表面状态 owner。

LineEdit 家族只负责文本输入与文本相关辅助操作，不负责结构化选择、日期时间选择、数值步进、异步候选数据管理、富文本编辑或表达式计算。

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/DataEntry/LineEdit` |
| 状态 | Stable |

## 何时使用

LineEdit 的设计语言来自可编辑文本表面和输入辅助内容的组合。

| 维度 | 含义 | 典型表达 |
| --- | --- | --- |
| 文本语义 | 用户输入或查看原始文本。 | `Text`、`PlaceholderText`、`IsReadOnly`、`PasswordChar`。 |
| 输入密度 | 控件在表单和工具栏中的尺寸等级。 | `SizeType=Large/Middle/Small/Custom`。 |
| 输入表面 | 边框和背景的视觉强度。 | `Outlined`、`Filled`、`Borderless`、`Underlined`。 |
| 反馈状态 | 输入的校验和业务状态。 | `Default`、`Error`、`Warning`。 |
| 辅助内容 | 外部附加、内部前后缀、清除、密码显示、字数统计和 Form feedback。 | AddOn、`InnerLeftContent`、`InnerRightContent`、`IsAllowClear`、`IsEnableRevealButton`、`IsShowCount`。 |
| 专用输入 | 搜索、多行、自动高度和可拖拽调整高度。 | `SearchEdit`、`TextArea`、`IsAutoSize`、`IsResizable`。 |

`Custom` 尺寸不是第四套预设 Token。它以 `Middle` 作为未显式设置时的视觉基线，并允许用户通过 `Height`、`FontSize`、`Padding` 等常规属性覆盖实际尺寸。

## 公共 API

LineEdit 家族的公共 API 由 Avalonia 文本编辑 API 与 AtomUI 输入扩展 API 组成。Avalonia API 的文本编辑、选择、滚动、密码、换行、只读和数据校验语义必须保持兼容；AtomUI 扩展只负责外观、辅助内容和集成能力。

基础文本 API：

| API | 语义 |
| --- | --- |
| `Text` | 当前输入文本，支持双向绑定。 |
| `PlaceholderText` | 空文本状态下的占位提示。 |
| `IsReadOnly` | 文本可见但禁止编辑。 |
| `PasswordChar` / `RevealPassword` | 密码输入和 reveal 状态。 |
| `MaxLength` | 文本最大长度，字数统计显示依赖该值。 |
| `SelectionStart` / `SelectionEnd` / `CaretIndex` | 文本选择和光标位置。 |

AtomUI 输入扩展 API：

| API | 所属类型 | 语义 |
| --- | --- | --- |
| `SizeType` | `TextBox`、`LineEdit`、`SearchEdit`、`TextArea` | 输入尺寸密度，类型为 `CustomizableSizeType`。 |
| `IsCustomFontSize` | `TextBox`、`LineEdit`、`SearchEdit` | 为 `true` 时不由 SizeType 样式覆盖 `FontSize`。 |
| `IsAllowClear` | `TextBox`、`LineEdit`、`SearchEdit`、`TextArea` | 是否允许展示清除按钮。 |
| `ClearIcon` | `TextBox`、`LineEdit`、`SearchEdit`、`TextArea` | 清除按钮图标。 |
| `IsEnableRevealButton` | `TextBox`、`LineEdit`、`SearchEdit` | 是否展示密码 reveal 按钮。 |
| `IsShowCount` | `TextBox`、`LineEdit`、`SearchEdit`、`TextArea` | 是否展示字数统计。 |
| `IsMotionEnabled` | 全家族 | 是否启用内部按钮动效。 |
| `StyleVariant` | `TextBox`、`LineEdit`、`SearchEdit`、`TextArea` | 输入表面样式，由 `AbstractTextInput` 统一归一并传递给 `InputControlFrame`。 |
| `Status` | `TextBox`、`LineEdit`、`SearchEdit`、`TextArea` | 显式输入反馈状态；最终视觉由 `InputControlFrame.EffectiveStatus` 计算，native validation error 仍以 `DataValidationErrors` 为唯一真源。 |
| `LeftAddOn` / `LeftAddOnTemplate` | `LineEdit`、`SearchEdit` | 外部左侧附加内容和模板。 |
| `RightAddOn` / `RightAddOnTemplate` | `LineEdit` | 外部右侧附加内容和模板；SearchEdit 的右侧外部 add-on 位置由搜索按钮占用。 |
| `InnerLeftContentTemplate` / `InnerRightContentTemplate` | `LineEdit`、`TextArea` | 内部前后缀模板。 |

SearchEdit 专项 API：

| API | 语义 |
| --- | --- |
| `SearchButtonStyle` | 搜索按钮样式，支持 `Default` 和 `Primary`。 |
| `SearchButtonText` | 搜索按钮文字。 |
| `IsOperating` | 搜索进行中状态；显示按钮 loading 并阻止重复搜索请求。 |
| `IsSearchOnEnterEnabled` | 是否允许 Enter 键触发搜索请求，默认启用。 |
| `SearchRequested` | 按钮或 Enter 键触发的搜索请求路由事件。 |

TextArea 专项 API：

| API | 语义 |
| --- | --- |
| `Lines` | 固定行数模式下的显示行数。 |
| `MinLines` / `MaxLines` | 自动高度和 resize 的行数边界。 |
| `IsAutoSize` | 是否根据文本内容自动调整高度。 |
| `IsResizable` | 是否展示右下角 resize handle。 |

稳定 template part：

| Template Part | 类型 | 所属主题 | 职责 |
| --- | --- | --- | --- |
| `PART_InputControlFrame` | `InputControlFrame` / `AddOnDecoratedBox` 派生类型 | 全家族输入主题 | 输入表面、边框、背景、状态视觉、CompactSpace 和动效承载；派生 decorated box 只增加 AddOn 或专用布局。 |
| `PART_ScrollViewer` / `ScrollViewer` | `ScrollViewer` | 全家族 | 文本滚动区域。 |
| `PART_TextPresenter` | `InputTextPresenter` | 全家族 | 文本显示、光标、选择和密码 reveal。 |
| `Placeholder` | `TextBlock` | 全家族 | 空文本占位提示。 |
| `PART_ClearButton` | `InputClearIconButton` | 全家族 | 清除当前文本。 |
| `PART_RevealButton` | `RevealButton` | `TextBox`、`LineEdit`、`SearchEdit` | 切换密码 reveal。 |
| `FormFeedBack` / `PART_FormFeedBack` | `ContentPresenter` | `LineEdit`、`TextBox`、`TextArea` | Form feedback 内容承载。 |
| `InnerRightContentPresenter` / `PART_InnerRightContentPresenter` | `ContentPresenter` | `LineEdit`、`SearchEdit`、`TextArea` | 内部右侧内容承载。 |
| `TextCountIndicator` | `TextBlock` | 全家族 | 字数统计显示。 |
| `PART_ResizeHandle` | `ResizeHandle` | `TextAreaTheme` | TextArea 高度拖拽入口。 |

## 事件与命令

| `SearchRequested` | 按钮或 Enter 键触发的搜索请求路由事件。 |

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/LineEdit/Views/LineEditBasicShowCase.axaml:13`

Gallery key：`ExamplesContent` / item `0`

```axaml
<atom:LineEdit PlaceholderText="基础用法" />
```

### 变体

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/LineEdit/Views/LineEditBasicShowCase.axaml:32`

Gallery key：`ExamplesContent` / item `2`

```axaml
<StackPanel Orientation="Vertical" Spacing="10">
    <atom:Separator Title="普通" />
    <atom:LineEdit PlaceholderText="线框风格" StyleVariant="Outlined" />
    <atom:LineEdit PlaceholderText="填充风格" StyleVariant="Filled" />
    <atom:LineEdit PlaceholderText="无边框" StyleVariant="Borderless" />
    <atom:LineEdit PlaceholderText="下划线" StyleVariant="Underlined" />
    <atom:SearchEdit PlaceholderText="填充风格" StyleVariant="Filled" />
    <atom:Separator Title="左侧附加和右侧附加" />
    <atom:LineEdit LeftAddOn="http://" RightAddOn=".com" Text="我的站点" />
    <atom:LineEdit LeftAddOn="http://" RightAddOn=".com" Text="我的站点" StyleVariant="Filled" />
    <atom:LineEdit LeftAddOn="http://" RightAddOn=".com" Text="我的站点" StyleVariant="Borderless" />
    <atom:LineEdit LeftAddOn="http://" RightAddOn=".com" Text="我的站点" StyleVariant="Underlined" />
</StackPanel>
```

### 带清除图标

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/LineEdit/Views/LineEditBasicShowCase.axaml:64`

Gallery key：`ExamplesContent` / item `4`

```axaml
<StackPanel Orientation="Vertical" Spacing="10">
    <atom:LineEdit PlaceholderText="带清除图标的输入框" IsAllowClear="True" />
    <atom:TextArea PlaceholderText="带清除图标的文本域" IsAllowClear="True" />
</StackPanel>
```

### 密码框

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/LineEdit/Views/LineEditBasicShowCase.axaml:73`

Gallery key：`ExamplesContent` / item `5`

```axaml
<StackPanel Orientation="Vertical" Spacing="10">
    <atom:LineEdit PlaceholderText="输入密码"
                   Width="400"
                   RevealPassword="False"
                   PasswordChar="•"
                   HorizontalAlignment="Left"
                   IsEnableRevealButton="True" />
    <atom:LineEdit PlaceholderText="输入密码"
                   Width="400"
                   RevealPassword="False"
                   HorizontalAlignment="Left"
                   PasswordChar="•"
                   IsEnableRevealButton="True"
                   IsAllowClear="True" />
</StackPanel>
```

## 状态模型

LineEdit 家族的交互优先级：

```text
Disabled
> Native Error
> Form Error
> Form Warning
> Explicit Warning
> Explicit Error
> Focus
> PointerOver
> Normal
```

输入状态由三个来源组成：

| 来源 | owner | 语义 |
| --- | --- | --- |
| `NativeValidationStatus` | `DataValidationErrors` | Avalonia 原生校验结果；`Error` 是 native error 的唯一视觉真源。 |
| `FormStatus` | Form 集成层 | Form 写入的 warning、success、validating 及其 feedback；Form error 通过 Form-owned native validation entry 写入 `DataValidationErrors`。 |
| `ExplicitStatus` | `AbstractTextInput.Status` | 用户显式设置的 error/warning 请求，不覆盖 native error，也不修改 Form 自己写入的状态。 |

有效状态由 `InputControlFrame` 计算：

```text
Native Error / Form Error
    > Form Warning
    > Explicit Warning
    > Explicit Error
    > Default
```

Form reset 只清理 Form 自己写入的状态；`Status=Error` 不写入 native error；`Warning` 伪类只在最终有效状态为 Warning 时出现。

`Disabled` 表示不可交互，文本使用 disabled 文本色，内部按钮不可作为操作入口。`IsReadOnly` 保持文本可见和可选中，但不允许编辑或清除。

清除按钮有效状态：

```text
TextBox / LineEdit / SearchEdit:
IsEffectiveShowClearButton =
  IsAllowClear
  && !IsReadOnly
  && !AcceptsReturn
  && !string.IsNullOrEmpty(Text)

TextArea:
IsEffectiveShowClearButton =
  IsAllowClear
  && !IsReadOnly
  && !string.IsNullOrEmpty(Text)
```

Form 集成以 `Text` 作为表单值。错误校验状态以 Avalonia `DataValidationErrors` 为真源，Form validator 产生的 error 应写入同一 native validation 通道；`IFormItemAware.NotifyValidateStatus` 只负责同步 `Warning`、`Success`、`Validating` 等 Form 扩展状态和 feedback 可见性。feedback 内容通过 `IFormItemFeedbackAware` 进入模板中的 feedback presenter。

CompactSpace 只影响相邻输入框之间的有效圆角和边框折叠，不改变文本编辑语义。

## 主题与 Design Token

LineEdit 家族使用输入壳体和文本 presenter 分层：

| 主题 | 职责 |
| --- | --- |
| `AbstractTextInput` shared template contract | 统一文本输入属性、placeholder、clear/reveal/count、Form feedback、native validation、CompactSpace 和 viewport metrics 接入。 |
| `InputControlFrameTheme.axaml` | Outlined、Filled、Borderless、Underlined、hover、focus-within、pressed、disabled、error、warning、corner、shadow、CompactSpace 和 motion。 |
| `TextBoxTheme.axaml` | 基础文本编辑模板、文本 presenter、TextBox 专属 padding、clear/reveal、字数统计和 frame 组合。 |
| `LineEditTheme.axaml` | 单行文本布局、外部 AddOn、内部前后缀和 `InputControlFrame` 组合。 |
| `SearchEditTheme.axaml` | 搜索输入模板、搜索按钮状态传递和 `SearchEditDecoratedBox` 组合。 |
| `SearchEditDecoratedBoxTheme.axaml` | 搜索按钮与 frame 的一体化布局；不重复实现 frame 状态 selector。 |
| `TextAreaTheme.axaml` | 多行文本、字数统计、固定行数和 `InputControlFrame` 组合。 |
| `TextAreaDecoratedBoxTheme.axaml` | TextArea 内部 padding、右侧附加内容、scroll viewer 和 resize 相关布局。 |
| `InputClearIconButtonTheme.axaml` / `RevealButtonTheme.axaml` | 内部 action 按钮视觉。 |

共享输入表面值由 `SharedToken` 和 `InputControlFrameTheme` 消费。`TextBoxToken`、`LineEditToken`、`TextAreaToken` 只保留各自稳定的文本尺寸、padding 和多行 resize 专属语义，不再承载输入表面边框、背景、focus shadow、error/warning 或 disabled 状态。

Token 来源：

LineEdit 输入家族使用三个控件级 Token scope：

| Token | Scope | 职责 |
| --- | --- | --- |
| `TextBoxToken` | `TextBox` | TextBox 专属内容 padding。 |
| `LineEditToken` | `LineEdit` | 单行输入框字号。 |
| `TextAreaToken` | `TextArea` | 多行输入框字号、右侧附加 padding 和 resize handle 视觉。 |

TextBox / LineEdit / TextArea Token 不承载文本值、placeholder、清除状态、密码 reveal、Form 状态、SearchEdit 运行状态、focus/hover/pressed 状态或 CompactSpace 运行时状态。输入表面边框、背景、圆角、shadow、error/warning、disabled 和 motion 由 `InputControlFrameTheme` 与 `SharedToken` 处理；运行时状态由 `AbstractTextInput`、Form 和 frame 状态模型处理。

## AOT 与裁剪注意事项

LineEdit 家族不依赖运行时反射发现模板结构。固定 control-to-template 状态通过稳定 part、`TemplateBinding` 和 typed ancestor binding 表达；运行时协作通过接口和显式 owner 引用完成。

资源和生命周期边界：

- 固定模板状态不创建 C# relay binding；`ContentRightAddOn` 内容边界内使用 typed ancestor binding，生命周期由模板拥有。
- `_feedbackStatusSubscription` 必须在 `FormFeedback` 变化和 logical detach 时释放，并在 logical attach 时重新建立。
- clear button click 订阅必须在新模板接入前解绑旧按钮。
- preedit 和文本 viewport source 订阅必须在新模板接入前释放旧 source；普通 logical detach 不释放当前模板仍需使用的订阅。
- 文本 viewport source 的 `Viewport`、`Padding` 和 presenter `Margin` 订阅必须由 TextBox/TextArea 持有，并在模板重套用时成组替换。
- TextArea resize 不创建全局订阅；拖拽状态保存在控件实例字段中。
- SharedToken 与 frame 主题表达输入表面值；TextBox/LineEdit/TextArea Token 只表达稳定的字体、padding 和 resize 视觉语义，不承载文本值、清除状态、Form 状态或搜索运行状态。

AOT 边界：

- Token 类型通过 generator 显式注册。
- API 与 Token 契约由控件文档、源码 public surface、Token 类型或生成数据维护，不依赖运行时反射扫描。
- 文档中描述的 template part 名称应与 AXAML 和 C# 查找代码保持一致。

## 源码索引

主要源码：

- `src/AtomUI.Desktop.Controls/Input/AbstractTextInput.cs`：文本输入逻辑基类，统一 `StyleVariant`、`Status`、`SizeType`、清除、reveal、字数统计、Form、native validation、CompactSpace 和 viewport metrics。
- `src/AtomUI.Desktop.Controls/Primitives/InputControlFrame.cs`：internal 输入表面组合控件，统一边框、背景、圆角、shadow、交互状态、有效状态和 CompactSpace 视觉。
- `src/AtomUI.Desktop.Controls/Input/TextBox.cs`：AtomUI 基础 TextBox，继承 `AbstractTextInput`，提供基础文本编辑模板。
- `src/AtomUI.Desktop.Controls/Input/LineEdit.cs`：标准单行输入框，继承 `AbstractTextInput`，提供外部 AddOn、内部右侧内容绑定和单行布局。
- `src/AtomUI.Desktop.Controls/Input/SearchEdit.cs`：搜索输入框，提供搜索按钮样式、搜索按钮文本、加载态和搜索点击事件。
- `src/AtomUI.Desktop.Controls/Input/TextArea.cs`：多行输入框，继承 `AbstractTextInput`，提供固定行数、自动高度和 resize。
- `src/AtomUI.Desktop.Controls/Input/InputTextPresenter.cs`：输入文本 presenter，处理 Avalonia 12 selection foreground 缓存刷新。
- `src/AtomUI.Desktop.Controls/Input/SearchEditDecoratedBox.cs`：SearchEdit 输入壳体与搜索按钮协作。
- `src/AtomUI.Desktop.Controls/Input/TextAreaDecoratedBox.cs`：TextArea 输入壳体、scroll viewer 和 resize 相关协作。
- `src/AtomUI.Desktop.Controls/Input/TextViewportMetrics.cs`：输入控件向同程序集消费方发布有效文本 viewport 宽度的内部度量契约。
- `src/AtomUI.Desktop.Controls/Input/ResizeHandle.cs`：TextArea resize 拖拽入口。
- `src/AtomUI.Desktop.Controls/Input/TextBoxToken.cs`：TextBox 专属内容 padding Token。
- `src/AtomUI.Desktop.Controls/Input/LineEditToken.cs`：单行输入字号 Token。
- `src/AtomUI.Desktop.Controls/Input/TextAreaToken.cs`：TextArea 字号、右侧 padding 和 resize Token。
- `src/AtomUI.Desktop.Controls/Input/Themes/*.axaml`：TextBox、LineEdit、SearchEdit、TextArea 和内部按钮主题。

## 相关文档

- 源设计文档：`docs/controls/desktop/data-entry/line-edit/overview.md`
- 实现文档：`docs/controls/desktop/data-entry/line-edit/implementation.md`
- Token 文档：`docs/controls/desktop/data-entry/line-edit/token.md`
- 变更记录：`docs/controls/desktop/data-entry/line-edit/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/mentions/index-cn.md

# Mentions

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

Mentions 是 AtomUI 桌面数据录入体系中的提及输入控件，用于在多行文本输入中识别触发前缀，展示候选项弹层，并把用户选择的候选项插入到文本中。它以 `TextArea` / `AbstractTextInput` 为逻辑输入层，以 `InputControlFrame` 为输入表面，结合 `Popup`、`CandidateList`、同步/异步数据加载、过滤、Form 和 Token 体系，提供类似 `@user`、`#tag` 的文本提及体验。

Mentions 的职责是编辑单个字符串值，并在触发上下文中完成候选项选择和文本插入。它不负责远程服务协议、权限判断、富文本 token 渲染、结构化 mention 实体存储、消息发送或搜索结果管理。业务层需要保存结构化 mention 信息时，应在 `Value` 文本和业务数据之间建立自己的解析模型。

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/DataEntry/Mentions` |
| 状态 | Stable |

## 何时使用

Mentions 的设计语言来自文本区域、触发符和候选弹层的组合。

| 维度 | 含义 | 典型表达 |
| --- | --- | --- |
| 文本语义 | 用户编辑包含 mention 标记的普通文本。 | `Value`、`DefaultValue`、`PlaceholderText`。 |
| 触发语义 | 输入触发符后进入候选选择上下文。 | `TriggerPrefix`、`CandidateTriggered`、`FilterValue`。 |
| 候选数据 | 候选项可来自本地集合或异步 loader。 | `OptionsSource`、`OptionsAsyncLoader`、`Populating`、`Populated`。 |
| 输入表面 | 输入框边框、背景和反馈状态。 | `StyleVariant`、`Status`、`SizeType`。 |
| 弹层位置 | 候选弹层相对触发位置向上或向下展开。 | `Placement=Top/Bottom`。 |
| 多行输入 | 输入区域可固定行数或自动高度。 | `Lines`、`MinLines`、`MaxLines`、`IsAutoSize`。 |
| 加载状态 | 异步候选加载时弹层展示 loading。 | `IsLoading`、`AsyncLoadDebounce`、`AsyncLoadTimeout`。 |

`Custom` 尺寸不是 Mentions 的第四套专属 Token。它沿用 TextArea 的 Custom size 规则：未显式设置时以 `Middle` 为视觉基线，用户可通过常规尺寸属性覆盖实际大小。

## 公共 API

Mentions 的公共 API 由文本值、触发符、候选数据、过滤、弹层、输入表面和 Form 集成组成。属性不应被机械理解为独立功能；它们共同描述 mention 输入的状态机。

文本与输入 API：

| API | 类型 | 语义 |
| --- | --- | --- |
| `Value` | `string?` | 当前文本值，默认 `TwoWay` 绑定到内部 `MentionTextArea.Text`，并接入 Avalonia `DataValidationErrors`。 |
| `DefaultValue` | `string?` | 初始化时 `Value` 为空时写入的默认文本。 |
| `PlaceholderText` | `string?` | 空文本状态的占位提示。 |
| `IsReadOnly` | `bool` | 只读状态，内部文本区域不可编辑。 |
| `IsAllowClear` / `ClearIcon` | `bool` / `PathIcon?` | 清除按钮入口和图标。 |
| `IsAutoFocus` | `bool` | 控件加载后请求内部文本区域焦点。 |
| `IsAutoSize` | `bool` | 内部 TextArea 根据内容自动调整高度。 |
| `Lines` / `MinLines` / `MaxLines` | `int` | 多行输入显示行数和自动高度边界。 |

候选与触发 API：

| API | 类型 | 语义 |
| --- | --- | --- |
| `TriggerPrefix` | `IList<string>?` | mention 触发前缀。当前触发识别只支持长度为 1 的字符串。默认值为 `["@"]`。 |
| `Split` | `string?` | 插入候选项时使用的分隔符。为空时默认用空格包裹 mention 文本。 |
| `OptionsSource` | `IEnumerable<IMentionOption>?` | 本地候选项集合。集合变更会同步到内部缓存和候选视图。 |
| `OptionsAsyncLoader` | `IMentionOptionsAsyncLoader?` | 异步候选加载器。存在时，触发候选弹层会优先使用异步加载。 |
| `OptionTemplate` | `IDataTemplate?` | 候选项显示模板。默认模板显示 `IMentionOption.Header`。 |
| `Filter` | `IValueFilter?` | 候选过滤器。为空时使用 contains 过滤。 |
| `FilterValueSelector` | `DefaultFilterValueSelector?` | 从候选项提取过滤文本的 selector。 |
| `DisplayCandidateCount` | `int` | 弹层可显示的候选项数量，用于计算最大高度。默认 `10`。 |

弹层与异步 API：

| API | 类型 | 语义 |
| --- | --- | --- |
| `Placement` | `MentionsPlacementMode` | 候选弹层相对触发位置的展开方向，默认 `Bottom`。 |
| `IsDropDownOpen` | `bool` | 候选弹层打开状态。 |
| `ShouldUseOverlayPopup` | `bool` | 是否使用 overlay popup 宿主，默认 `true`。 |
| `AsyncLoadDebounce` | `TimeSpan` | 异步加载前的防抖时间，默认 `TimeSpan.Zero`，值必须非负。 |
| `AsyncLoadTimeout` | `TimeSpan` | 异步加载超时时间，默认 `10` 秒。 |
| `IsLoading` | `bool` | 异步或事件填充过程中的内部 loading 状态，只读公开。 |

输入表面 API：

| API | 类型 | 语义 |
| --- | --- | --- |
| `SizeType` | `CustomizableSizeType` | 输入尺寸密度。 |
| `StyleVariant` | `InputControlStyleVariant` | 输入表面样式。 |
| `Status` | `InputControlStatus` | 显式输入反馈状态；最终视觉由 `InputControlFrame.EffectiveStatus` 计算，native validation error 以 `DataValidationErrors` 为唯一真源。 |
| `ContentLeftAddOn` / `ContentRightAddOn` | `object?` | 内部左右附加内容，传递给内部 `MentionTextArea`。 |
| `ContentLeftAddOnTemplate` / `ContentRightAddOnTemplate` | `IDataTemplate?` | 内部左右附加内容模板。 |
| `IsMotionEnabled` | `bool` | 内部输入壳体、候选列表和 popup 动效开关。 |

候选项契约：

| 类型 | 语义 |
| --- | --- |
| `IMentionOption.Key` | 候选项稳定 key，可作为过滤候选值回退。 |
| `IMentionOption.Header` | 候选项显示内容，默认模板显示该值。 |
| `IMentionOption.Value` | 插入文本时优先使用的值。 |
| `IMentionOption.IsEnabled` | 候选项启用状态，供候选列表视觉和选择逻辑使用。 |

事件契约：

| 事件 | 语义 |
| --- | --- |
| `CandidateTriggered` | 触发符被识别时触发，事件参数包含触发字符。 |
| `Populating` | 候选弹层准备填充前触发，可取消默认填充并由业务手动更新候选集合。 |
| `Populated` | 候选视图刷新后触发，事件参数包含只读候选视图。 |
| `OptionsLoaded` | 异步 loader 完成后触发，包含加载结果或错误状态。 |
| `DropDownOpening` / `DropDownClosing` | 弹层打开/关闭前触发，可取消。 |
| `DropDownOpened` / `DropDownClosed` | 弹层打开/关闭后触发。 |

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_TextArea` | `MentionTextArea` | 文本输入、触发符识别、过滤值同步和候选插入。 |
| `PART_Popup` | `Popup` | 候选弹层宿主。 |
| `PART_CandidateList` | `ICandidateList` / `CandidateList` | 候选项展示、键盘导航、提交和取消。 |
| `PopupFrame` | `Border` | 候选弹层背景、圆角、宽度、高度和 padding。 |
| `LoadingIndicator` | `Spin` | 异步候选加载状态展示。 |

`EmptyIndicator`、`EmptyIndicatorTemplate`、`IsShowEmptyIndicator` 和 `EmptyIndicatorPadding` 是当前 public surface 的一部分；默认 `MentionsTheme` 尚未把这些属性绑定到候选弹层视觉结构中，维护时不能把它们描述为已经稳定生效的默认视觉契约。

## 事件与命令

| `IsLoading` | `bool` | 异步或事件填充过程中的内部 loading 状态，只读公开。 |
事件契约：
| 事件 | 语义 |
| `CandidateTriggered` | 触发符被识别时触发，事件参数包含触发字符。 |
| `Populated` | 候选视图刷新后触发，事件参数包含只读候选视图。 |

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/Mentions/Views/MentionsShowCase.axaml:36`

Gallery key：`ExamplesContent` / item `0`

```axaml
<atom:Mentions Name="BasicMentions"
```

### Value 绑定

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/Mentions/Views/MentionsShowCase.axaml:51`

Gallery key：`ExamplesContent` / item `1`

```axaml
<StackPanel Orientation="Vertical" Spacing="12">
    <atom:Mentions HorizontalAlignment="Stretch"
                   OptionsSource="{Binding BasicMentionOptions}"
                   IsAllowClear="True"
                   Value="{Binding BoundValue}" />
    <StackPanel Orientation="Horizontal" Spacing="8">
        <atom:TextBlock VerticalAlignment="Center"
                        Text="绑定值：" />
        <atom:TextBlock VerticalAlignment="Center"
                        Text="{Binding BoundValueText}" />
    </StackPanel>
    <StackPanel Orientation="Horizontal" Spacing="10">
        <atom:Button SizeType="Small"
                     Command="{Binding SetBoundValueCommand}"
                     Content="设置提及" />
        <atom:Button SizeType="Small"
                     Command="{Binding ClearBoundValueCommand}"
                     Content="清空" />
    </StackPanel>
</StackPanel>
```

### 变体

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/Mentions/Views/MentionsShowCase.axaml:116`

Gallery key：`ExamplesContent` / item `3`

```axaml
<StackPanel Orientation="Vertical" Spacing="10">
    <atom:Mentions HorizontalAlignment="Stretch" StyleVariant="Outlined" PlaceholderText="线框风格"/>
    <atom:Mentions HorizontalAlignment="Stretch" StyleVariant="Filled" PlaceholderText="填充风格"/>
    <atom:Mentions HorizontalAlignment="Stretch" StyleVariant="Borderless" PlaceholderText="无边框"/>
    <atom:Mentions HorizontalAlignment="Stretch" StyleVariant="Underlined" PlaceholderText="下划线"/>
</StackPanel>
```

### 异步加载

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/Mentions/Views/MentionsShowCase.axaml:132`

Gallery key：`ExamplesContent` / item `4`

```axaml
<atom:Mentions HorizontalAlignment="Stretch" OptionsAsyncLoader="{Binding MentionOptionAsyncLoader}"/>
```

## 状态模型

Mentions 的核心状态流：

```text
Value / DefaultValue
      ↓
MentionTextArea.Text
      ↓
Text input + caret position
      ↓
TriggerPrefix scan
      ↓
FilterValue + CandidateOpenRequest
      ↓
Populate / async load / filter
      ↓
CandidateList view
      ↓
Commit option
      ↓
Insert mention text back into Value
```

触发符识别从当前 `CaretIndex` 向前扫描，遇到空白或控制字符停止。只有长度为 1 的 `TriggerPrefix` 项会被识别。触发后，`MentionTextArea` 计算触发字符位置和过滤文本，并向外请求打开候选弹层。

候选填充优先级：

```text
OptionsAsyncLoader != null
  → async LoadAsync(FilterValue, token)
  → OptionsLoaded
  → OptionsSource = result.Data
  → PopulateComplete()

OptionsAsyncLoader == null
  → Populating(FilterValue)
  → if !Cancel PopulateComplete()
```

弹层状态优先级：

```text
Disabled / invisible / window deactivated
> DropDownClosing cancellation
> DropDownOpening cancellation
> IsDropDownOpen
> Candidate trigger state
```

键盘行为：

- 弹层打开时，按键优先交给 `CandidateList.HandleKeyDown()`。
- `Escape` 取消候选并关闭弹层。
- `Enter` 在弹层打开时提交当前候选。
- `F4` 切换弹层打开状态。
- 弹层关闭时，`Down` 可打开弹层，除非该按键被 XY focus 导航占用。

Form 集成以 `Value` 作为表单值。`Value` 是用户拥有的受控文本值，默认双向绑定；错误校验状态通过 `DataValidationErrors` 投射到 shared `InputControlFrame.EffectiveStatus`，驱动外层输入表面和内部 `MentionTextArea`；`NotifyValidateStatus` 只同步 warning、success、validating 等 Form 扩展状态。Form feedback 控件传递给内部 `MentionTextArea`。

## 主题与 Design Token

Mentions 的默认视觉由 `MentionsTheme.axaml` 和内部 TextArea 主题协作：

| 主题或资源 | 职责 |
| --- | --- |
| `MentionsTheme.axaml` | 根模板、`MentionTextArea`、popup、loading、候选列表和 MentionsToken 资源绑定。 |
| `TextAreaTheme.axaml` | 输入表面、placeholder、清除按钮、Form feedback、多行高度和状态视觉。 |
| `CandidateList` 主题 | 候选项容器、选中项、键盘导航和提交取消事件。 |
| `PopupHostToken` | popup 阴影、圆角和 anchor margin。 |
| `MentionsToken` | popup 内容 padding、候选项高度和最小宽度。 |

输入框本体不由 Mentions 自己重写边框和文本 presenter，而是通过内部 `MentionTextArea` 复用 TextArea 的 `AbstractTextInput` 与 `InputControlFrame` 体系。Mentions 的控件级 Token 只服务候选弹层，不承载文本高度、输入边框、状态颜色或 Form feedback。

Token 来源：

MentionsToken 是 Mentions 的控件级 Token scope，只描述候选弹层的结构尺寸。输入框本体由内部 `MentionTextArea` 复用 TextArea / `AbstractTextInput` / `InputControlFrame` / SharedToken 体系；候选列表项的选择、hover、disabled 和文本状态由 CandidateList 主题处理。

MentionsToken 不承载以下状态：

- `Value`、`FilterValue`、`TriggerPrefix`、`OptionsSource`、`OptionsAsyncLoader` 等数据状态。
- `IsDropDownOpen`、`IsLoading`、`IsReadOnly`、`IsAutoSize`、`Status` 等运行状态。
- 候选项 selected、hover、disabled、commit、cancel 等交互状态。
- Popup 当前 offset、placement、实际高度或异步加载结果。

## AOT 与裁剪注意事项

Mentions 不依赖运行时反射发现模板结构。模板协作通过固定 template part、接口、事件和 owner 引用完成。

资源和生命周期边界：

- `_collectionChangeSubscription` 使用弱订阅，但 `OptionsSource` 变化时仍必须 dispose。
- `_subscriptionsOnOpen` 只在 popup 打开期间存在，popup 关闭时必须释放。
- `_deactivationSubscription` 必须在 detach 时释放。
- `_delayTimer` 更换时必须停止旧 timer 并解除 Tick。
- `CandidateList` 替换时必须清空旧列表 `ItemsSource`，避免旧视图保留。
- 异步加载通过 `AsyncSearchLoadCoordinator` 处理超时、取消和跳过旧结果，不在控件里保留业务任务。

AOT 边界：

- `MentionsToken` 通过 token generator 显式注册，生成 `MentionsTokenKind` 和 `MentionsTokenResourceExtension`。
- API 与 Token 契约由控件文档、源码 public surface、Token 类型或生成数据维护。
- `OptionTemplate` 和 `EmptyIndicatorTemplate` 是 XAML 模板入口，不依赖运行时成员扫描。

## 源码索引

主要源码：

- `src/AtomUI.Desktop.Controls/Mentions/Mentions.cs`：public API、状态流、候选加载、过滤、弹层生命周期、键盘处理和 Form 接口。
- `src/AtomUI.Desktop.Controls/Mentions/MentionTextArea.cs`：内部 TextArea，负责触发符扫描、过滤值同步和候选插入。
- `src/AtomUI.Desktop.Controls/Mentions/MentionOption.cs`：候选项接口和默认 record。
- `src/AtomUI.Desktop.Controls/Mentions/DataLoad/IMentionOptionsAsyncLoader.cs`：异步候选加载接口。
- `src/AtomUI.Desktop.Controls/Mentions/DataLoad/MentionOptionsLoadResult.cs`：异步加载结果。
- `src/AtomUI.Desktop.Controls/Mentions/*EventArgs.cs`：候选触发、填充、加载和选项事件参数。
- `src/AtomUI.Desktop.Controls/Mentions/MentionsPlacementMode.cs`：弹层展开方向。
- `src/AtomUI.Desktop.Controls/Mentions/MentionPseudoClass.cs`：`:candidateopen` 伪类常量。
- `src/AtomUI.Desktop.Controls/Mentions/MentionsToken.cs`：候选弹层尺寸 Token。
- `src/AtomUI.Desktop.Controls/Mentions/Themes/MentionsTheme.axaml`：Mentions 模板、popup、loading 和候选列表视觉。

## 相关文档

- 源设计文档：`docs/controls/desktop/data-entry/mentions/overview.md`
- 实现文档：`docs/controls/desktop/data-entry/mentions/implementation.md`
- Token 文档：`docs/controls/desktop/data-entry/mentions/token.md`
- 变更记录：`docs/controls/desktop/data-entry/mentions/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/numeric-up-down/index-cn.md

# NumericUpDown

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

NumericUpDown 是 AtomUI 桌面数据录入体系中的数值输入控件，用于在文本输入、步进按钮、键盘和鼠标滚轮之间建立统一的十进制数值编辑体验。它以 Avalonia `NumericUpDown` 为基础，保留原生数值、格式化、步进和范围约束语义，并接入 AtomUI 的输入外观、Addon、CompactSpace、Form 和 Token 体系。

NumericUpDown 的职责是编辑单个 `decimal?` 数值，或在 string mode 下以 `StringValue` 保存高精度原始输入文本并同步可解析的 `Value`。它不承担表达式计算、单位换算、多值范围输入、校验消息展示、数据源管理或异步选择职责。

Gallery 中该控件以 `NumberUpDown` 页面展示；实际控件类型为 `AtomUI.Desktop.Controls.NumericUpDown`。

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/DataEntry/NumberUpDown` |
| 状态 | Stable |

## 何时使用

NumericUpDown 的设计语言来自输入框与微调按钮的组合。

| 维度 | 含义 | 典型表达 |
| --- | --- | --- |
| 数值语义 | 输入值是可增减的数值，而不是普通文本。 | `Value`、`Minimum`、`Maximum`、`Increment`、`FormatString`。 |
| 输入密度 | 控件在表单、工具栏或紧凑布局中的尺寸等级。 | `Large`、`Middle`、`Small`、`Custom`。 |
| 输入表面 | 控件边框和背景的视觉强度。 | `Outlined`、`Filled`、`Borderless`。 |
| 反馈状态 | 输入的校验和业务状态。 | `Default`、`Error`、`Warning`。 |
| 展示模式 | 控件以输入框或三段式拨轮呈现。 | `Mode=Input`、`Mode=Spinner`。 |
| 辅助内容 | 输入框前后附加说明、单位、协议、图标或清除入口。 | `LeftAddOn`、`RightAddOn`、`InnerLeftContent`、`InnerRightContent`、`IsAllowClear`。 |

步进能力是输入框语义的一部分，不是独立操作按钮组。`Input` 模式使用右侧浮动 Handle，在普通状态下提供数值增减入口；`Spinner` 模式使用左减号、中间输入、右加号的三段式结构。

## 公共 API

NumericUpDown 的公共 API 由继承的数值编辑 API 与 AtomUI 输入扩展 API 组成。继承 API 的语义必须与 Avalonia `NumericUpDown` 保持一致，AtomUI 扩展只负责外观、辅助内容和集成能力。

数值编辑 API：

| API | 类型 | 语义 |
| --- | --- | --- |
| `Value` | `decimal?` | 控件的数值值，支持双向绑定和数据校验。 |
| `Text` | `string?` | 当前显示文本，受转换器、格式化和输入状态影响。 |
| `Minimum` / `Maximum` | `decimal` | 可接受数值范围。 |
| `Increment` | `decimal` | 单次步进增减值。 |
| `ClipValueToMinMax` | `bool` | 文本转换为数值时是否裁剪到范围。 |
| `NumberFormat` | `NumberFormatInfo?` | 数值格式化和解析使用的区域格式。 |
| `FormatString` | `string` | 非编辑状态下的显示格式。 |
| `ParsingNumberStyle` | `NumberStyles` | 文本解析规则。 |
| `TextConverter` | `IValueConverter?` | 自定义 `Text` 与 `Value` 的双向转换器。 |
| `AllowSpin` | `bool` | 是否允许按钮、键盘和鼠标滚轮触发步进。 |
| `ShowButtonSpinner` | `bool` | 是否显示步进按钮；`Mode=Input` 控制浮动 Handle，`Mode=Spinner` 控制左右 action 段。 |

String mode API：

| API | 类型 | 语义 |
| --- | --- | --- |
| `IsStringMode` | `bool` | 启用原始字符串值保存模式。 |
| `StringValue` | `string?` | string mode 下的原始输入文本。 |

AtomUI 输入扩展 API：

| API | 类型 | 语义 |
| --- | --- | --- |
| `Mode` | `NumericUpDownMode` | 控件展示模式，默认 `Input`，可设置为 `Spinner`。 |
| `SizeType` | `CustomizableSizeType` | 输入框尺寸密度；`Custom` 未显式覆盖时以 `Middle` 为视觉基线。 |
| `IsCustomFontSize` | `bool` | 为 `true` 时内部 `TextBox` 不由 `SizeType` 字号样式覆盖 `FontSize`。 |
| `StyleVariant` | `InputControlStyleVariant` | 输入表面样式。 |
| `Status` | `InputControlStatus` | 显式输入反馈状态；最终视觉由 `InputControlFrame.EffectiveStatus` 计算，native validation error 以 `DataValidationErrors` 为唯一真源。 |
| `IsAllowClear` | `bool` | 是否展示清除按钮。 |
| `ClearIcon` | `PathIcon?` | 清除按钮图标。 |
| `IsKeyboardEnabled` | `bool` | 是否允许方向键和 PageUp / PageDown 触发步进。 |
| `IsMotionEnabled` | `bool` | 是否启用输入壳体、Handle 和清除按钮相关动效。 |
| `LeftAddOn` / `RightAddOn` | `object?` | 外部前后附加内容。 |
| `LeftAddOnTemplate` / `RightAddOnTemplate` | `object?` | 外部附加内容模板；实际模板契约为 `IDataTemplate?`。 |
| `InnerLeftContent` / `InnerRightContent` | `object?` | 内部前后缀内容。 |
| `InnerLeftContentTemplate` / `InnerRightContentTemplate` | `object?` | 内部前后缀模板；实际模板契约为 `IDataTemplate?`。 |

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_Spinner` | `ButtonSpinner` | `InputControlFrame` / AddOnDecoratedBox 组合、外部 AddOn、内部前后缀、步进入口和 CompactSpace 布局承载。 |
| `PART_TextBox` | `TextBox` | 文本输入、占位符、只读、数据校验和文本双向绑定。 |
| `PART_ClearButton` | `InputClearIconButton` | 清除 `Value` 的内部按钮。 |
| `PART_InnerRightContentPresenter` | `ContentPresenter` | 用户 `InnerRightContent` 的内部右侧内容承载。 |
| `PART_DecreaseButton` | `IconButton` | `Mode=Spinner` 下触发减小步进。 |
| `PART_IncreaseButton` | `IconButton` | `Mode=Spinner` 下触发增加步进。 |

## 事件与命令

NumericUpDown 的事件与命令以控件文档、源码 public surface 和 Avalonia 基类契约为准；生成器不从源码发明额外事件。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/NumberUpDown/Views/NumberUpDownShowCase.axaml:42`

Gallery key：`ExamplesContent` / item `0`

```axaml
<atom:NumericUpDown Value="3"
```

### 拨轮

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/NumberUpDown/Views/NumberUpDownShowCase.axaml:55`

Gallery key：`ExamplesContent` / item `1`

```axaml
<StackPanel Orientation="Vertical"
            Spacing="{atom:SharedTokenResource UniformlyMargin}">
    <atom:NumericUpDown Mode="Spinner"
                        Minimum="1"
                        Maximum="10"
                        Value="3"
                        FormatString="0"
                        PlaceholderText="Outlined"
                        Width="150"
                        HorizontalAlignment="Left" />
    <atom:NumericUpDown Mode="Spinner"
                        Minimum="1"
                        Maximum="10"
                        Value="3"
                        FormatString="0"
                        PlaceholderText="Filled"
                        Width="150"
                        HorizontalAlignment="Left"
                        StyleVariant="Filled" />
</StackPanel>
```

### 隐藏步进按钮

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/NumberUpDown/Views/NumberUpDownShowCase.axaml:86`

Gallery key：`ExamplesContent` / item `2`

```axaml
<StackPanel Orientation="Vertical"
            Spacing="{atom:SharedTokenResource UniformlyMargin}">
    <Grid ColumnDefinitions="*,*">
        <atom:NumericUpDown Grid.Column="0"
                            Minimum="1"
                            Maximum="10"
                            Value="3"
                            ShowButtonSpinner="False" />
    </Grid>
    <Grid ColumnDefinitions="*,*">
        <atom:NumericUpDown Grid.Column="0"
                            Mode="Spinner"
                            Minimum="1"
                            Maximum="10"
                            Value="3"
                            ShowButtonSpinner="False" />
    </Grid>
</StackPanel>
```

### 字符串模式（高精度）

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/NumberUpDown/Views/NumberUpDownShowCase.axaml:114`

Gallery key：`ExamplesContent` / item `3`

```axaml
<StackPanel Orientation="Vertical" Spacing="10">
    <atom:NumericUpDown IsStringMode="True"
                        StringValue="{Binding StringModeValue, Mode=TwoWay}"
                        Increment="0.0001"
                        Minimum="0"
                        Maximum="100"
                        PlaceholderText="输入重量" />
    <TextBlock>
        <Run Text="原始值：" />
        <Run Text="{Binding StringModeValue}" />
    </TextBlock>
</StackPanel>
```

## 状态模型

NumericUpDown 的交互优先级：

```text
Disabled
> ReadOnly
> Spin availability
> Pressed
> PointerOver
> Focus
> Normal
```

`Disabled` 表示控件不可交互，TextBox 文本使用禁用文字色，输入壳体使用禁用背景，浮动 Handle 不显示，清除按钮不应作为可操作入口出现。

`IsReadOnly` 保留文本可见但禁止编辑和步进。`AllowSpin=false` 禁止按钮、键盘和鼠标滚轮步进，但不等价于禁用控件。`IsKeyboardEnabled=false` 只拦截 `Up`、`Down`、`PageUp` 和 `PageDown` 的步进行为。

数值状态：

```text
Text input
  ↓ parse by TextConverter or ParsingNumberStyle
Value
  ↓ format by TextConverter / FormatString / NumberFormat
Text display
```

String mode 保留原始输入文本，并在能解析时同步 `Value`。Form 集成仍以 `Value` 作为表单值。

清除按钮有效状态：

```text
IsEffectiveShowClearButton =
  IsAllowClear
  && !IsReadOnly
  && !string.IsNullOrEmpty(Text)
```

`Mode` 只改变展示结构，不改变 `Value`、`Text`、`Minimum`、`Maximum`、`Increment`、`AllowSpin`、`ShowButtonSpinner`、键盘、滚轮、Form 或 string mode 的数值语义。

`SizeType=Custom` 进入自定义尺寸路径。用户未显式设置 `Height`、`FontSize`、`Padding` 等尺寸属性时，主题层应以 `Middle` 作为默认视觉基线；用户显式接管 `FontSize` 时，应通过 `IsCustomFontSize=true` 防止内部 `TextBox` 的 `SizeType` 字号样式覆盖用户设置。

## 主题与 Design Token

NumericUpDown 采用按需模板模型。`Mode=Input` 使用默认输入框模板，以 `ButtonSpinner` 作为输入壳体；`Mode=Spinner` 使用独立三段式拨轮模板，只在用户显式启用 spinner 模式时创建左右步进按钮和分隔视觉。

视觉层级要求：

- `Mode=Input` 的默认模板不得预埋 spinner 模式左右按钮或无职责 wrapper；`ShowButtonSpinner=false` 时必须隐藏浮动 Handle。
- `Mode=Spinner` 使用独立 `ControlTemplate`，不通过同一模板内两套视觉树加 `IsVisible` 切换实现；`ShowButtonSpinner=false` 时必须隐藏左右 action 段。
- `ButtonSpinner` 是默认输入组合边界，复用 `InputControlFrame` 的输入表面和有效状态，不应被普通 `Border` 或 `Grid` 包装替代。
- `PART_TextBox` 的 `BorderThickness=0` 是为了避免内层 TextBox 与外层输入壳体重复绘制边框。
- `PART_ClearButton` 与 `PART_InnerRightContentPresenter` 共用内部右侧 stack，必须保留顺序：清除按钮在用户内部右侧内容之前。
- 浮动 Handle 由 `ButtonSpinnerDecoratedBox` 控制透明度和偏移，不应在 NumericUpDown 模板中动态创建或移除。

状态视觉由共享主题分层承担：

| 主题 | 职责 |
| --- | --- |
| `NumericUpDownTheme.axaml` | 装配控件结构和传递状态。 |
| `NumericUpDownSpinnerTheme.axaml` | 装配 NumericUpDown 专用 inline spinner 模板及其 action 按钮状态。 |
| `ButtonSpinnerTheme.axaml` | 装配 spinner 壳体和 Handle 内容。 |
| `ButtonSpinnerDecoratedBoxTheme.axaml` | 输入壳体、Addon、浮动 Handle 透明度和偏移。 |
| `ButtonSpinnerHandleTheme.axaml` | Handle 背景、边框、图标尺寸和交互视觉。 |
| `TextBoxTheme.axaml` | 文本编辑器、placeholder、disabled 文本色和内部文本 presenter。 |
| `InputControlFrameTheme.axaml` | 输入 variant、effective status、focus、hover、pressed、error、warning、disabled 和 motion 外观。 |

Token 来源：

NumericUpDownToken 是 NumericUpDown 的控件级 Token scope。它继承 `ButtonSpinnerToken`，以独立 `NumericUpDown` scope 提供步进 Handle、字体和尺寸语义；输入表面边框、背景、圆角、focus shadow、error/warning、disabled 和 motion 统一由 `InputControlFrameTheme` 与 `SharedToken` 提供。

该设计使 NumericUpDown 能复用 ButtonSpinner 输入壳体体系，同时保留控件级 Token scope。生成的 `NumericUpDownTokenKind` 表达 NumericUpDown scope 下可展示和可覆盖的 Token；默认主题中的输入壳体和 Handle 仍通过 `ButtonSpinnerTokenResource` 消费共享 ButtonSpinner 语义值。

NumericUpDownToken 不承载以下状态：

- `Value`、`Text`、`StringValue`、`Minimum`、`Maximum`、`Increment` 等实例数值状态。
- `IsStringMode`、`IsKeyboardEnabled`、`IsAllowClear` 等行为状态。
- `IsPointerOver`、`IsPressed`、`IsFocused`、`IsEnabled` 等交互状态。
- `EffectiveContentPadding`、`HandleOpacity`、`HandleOffset` 等模板运行时状态。

这些状态分别由 C# 状态模型、共享输入主题和 ButtonSpinnerDecoratedBox 内部属性处理。

## AOT 与裁剪注意事项

NumericUpDown 不通过反射访问 ButtonSpinner、TextBox 或 AddOnDecoratedBox 内部状态。跨控件协同通过公开属性、稳定 template part 和接口完成。

`Mode=Input` 是默认路径，必须避免 spinner 模式额外节点和额外按钮事件订阅。共享视觉问题应修在 `InputControlFrameTheme` 或 ButtonSpinner 布局主题层；NumericUpDown 专用的 inline 按钮视觉应修在 NumericUpDownSpinnerTheme 中，不能在 NumericUpDown 主题中复制跨模板 selector。

Token 通过动态资源进入主题。NumericUpDown 不把实例状态、当前值、文本、按钮 enabled 状态或模板切换状态写入 Token。

`IsCustomFontSize` 只通过局部绑定影响内部 `TextBox` 的字号样式选择，不应引入全局资源、反射访问或跨模板缓存。

## 源码索引

主要源码：

- `src/AtomUI.Desktop.Controls/NumericUpDown/NumericUpDown.cs`：public API、数值同步、custom size、string mode、清除按钮、键盘处理、Form / CompactSpace / Motion 接口。
- `src/AtomUI.Desktop.Controls/NumericUpDown/NumericUpDownSpinner.cs`：internal Spinner 子控件，复用 ButtonSpinner 的数值步进语义并承载 NumericUpDown 专用 inline 模板。
- `src/AtomUI.Desktop.Controls/NumericUpDown/NumericUpDownToken.cs`：NumericUpDown Token scope。
- `src/AtomUI.Desktop.Controls/NumericUpDown/Themes/NumericUpDownTheme.axaml`：输入模式和 spinner 模式外层模板、状态传递。
- `src/AtomUI.Desktop.Controls/NumericUpDown/Themes/NumericUpDownSpinnerTheme.axaml`：NumericUpDownSpinner 的 inline 模板、分隔线、加减按钮和按钮状态视觉。
- `src/AtomUI.Desktop.Controls/ButtonSpinner/ButtonSpinner.cs`：输入壳体和 spin 入口。
- `src/AtomUI.Desktop.Controls/ButtonSpinner/ButtonSpinnerDecoratedBox.cs`：在 shared frame 上扩展浮动 Handle、Addon、CompactSpace 和 spinner 布局。
- `src/AtomUI.Desktop.Controls/ButtonSpinner/ButtonSpinnerHandle.cs`：Handle 按钮和上下箭头。
- `src/AtomUI.Desktop.Controls/Input/TextBox.cs`：文本编辑器。
- `src/AtomUI.Desktop.Controls/Input/InputClearIconButton.cs`：清除按钮。

## 相关文档

- 源设计文档：`docs/controls/desktop/data-entry/numeric-up-down/overview.md`
- 实现文档：`docs/controls/desktop/data-entry/numeric-up-down/implementation.md`
- Token 文档：`docs/controls/desktop/data-entry/numeric-up-down/token.md`
- 变更记录：`docs/controls/desktop/data-entry/numeric-up-down/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/otp-line-edit/index-cn.md

# OtpLineEdit

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

OtpLineEdit 是 AtomUI 桌面数据录入体系中的一次性验证码输入控件，用于短信验证码、邮件验证码、二次验证代码、恢复代码短段输入等场景。它以单个文本值作为数据契约，以多个视觉 cell 表达逐位输入体验，并接入 AtomUI 的输入尺寸、输入表面、Form、native validation error、主题 Token 和 Gallery 文档体系。

OtpLineEdit 不负责发送验证码、倒计时、后端校验、风险控制、二维码扫描、密钥绑定或业务 Captcha 逻辑。业务侧的“发送验证码”按钮、倒计时和接口状态应组合在 Form 或业务控件中，验证码输入本身由 OtpLineEdit 承担。

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/DataEntry/LineEdit/Views/LineEditShowCase.axaml` |
| 状态 | Design |

## 何时使用

OtpLineEdit 的设计语言来自“单一验证码值”和“分格输入表面”的组合。

| 维度 | 含义 | 典型表达 |
| --- | --- | --- |
| 验证码语义 | 用户输入一个短文本验证码，提交时作为一个整体值。 | `Text`、`Length`、`Completed`。 |
| 分格表达 | 每个字符拥有独立视觉 cell，增强位数感知和粘贴反馈。 | cell、focus cell、separator。 |
| 输入约束 | 控制允许输入的字符范围和写入前格式化。 | `InputMode`、`Formatter`、`MaxLength` 派生自 `Length`。 |
| 安全显示 | 可用掩码隐藏每个字符，但不改变提交值。 | `IsMasked`、`MaskChar`。 |
| 输入密度 | 控件在表单、弹窗和验证流程中的尺寸等级。 | `SizeType=Large/Middle/Small/Custom`。 |
| 输入表面 | cell 边框和背景的视觉强度。 | `Outlined`、`Filled`、`Borderless`、`Underlined`。 |
| 反馈状态 | 输入的校验和业务状态。 | `Default`、`Error`、`Warning`，error 以 `DataValidationErrors` 为真源。 |

`OTP` 作为产品文案使用全大写；代码类型和 API 使用 `.NET` PascalCase 缩写形式 `Otp`，例如 `OtpLineEdit`、`OtpLineEditInputMode`、`OtpLineEditCompletedEventArgs`。

## 公共 API

OtpLineEdit 的公共 API 以文本值、长度、输入约束、显示辅助和输入外观为核心。`Text` 是唯一对外值源，支持双向绑定；视觉 cell 不暴露独立值属性。

基础值 API：

| API | 语义 |
| --- | --- |
| `Text` | 当前验证码文本，支持双向绑定，是 Form、验证和完成事件的唯一值源。 |
| `Length` | 验证码位数，默认 `6`；控件按该值生成 cell，并把 `Text` 限制在该长度内。 |
| `PlaceholderText` | 空 cell 的占位提示；多字符占位按 cell 展示策略分配。 |
| `IsReadOnly` | 允许查看和复制当前验证码文本，但禁止编辑、删除、粘贴和清除。 |
| `IsAllowClear` | 是否显示清除入口；清除动作必须进入统一 `Clear()` 语义。 |

输入约束 API：

| API | 语义 |
| --- | --- |
| `InputMode` | 输入字符范围，支持文本、数字、字母数字等稳定模式。 |
| `Formatter` | 写入 `Text` 前的归一化函数，用于大写转换、空白裁剪或业务字符过滤。 |
| `IsMasked` | 是否以掩码显示已输入字符。 |
| `MaskChar` | 掩码字符，默认使用密码输入常见圆点。 |

视觉与布局 API：

| API | 语义 |
| --- | --- |
| `SizeType` | 输入尺寸密度，类型为 `CustomizableSizeType`。 |
| `StyleVariant` | 输入表面样式，复用 `InputControlStyleVariant`。 |
| `Status` | 显式输入反馈状态；最终视觉由 `InputControlFrame.EffectiveStatus` 计算，native validation error 以 `DataValidationErrors` 为唯一真源。 |
| `Separator` | 分隔符内容，仅参与视觉展示，不进入 `Text`。 |
| `SeparatorInterval` | 分隔符间隔，例如 `3` 表示 `123-456`。 |
| `SeparatorTemplate` | 分隔符内容模板。 |

事件与方法：

| API | 语义 |
| --- | --- |
| `Completed` | 当归一化后的 `Text.Length == Length` 时触发，事件参数携带完整文本。 |
| `Clear()` | 清空 `Text`，重置 active cell 到起始位置，并通知 Form value changed。 |
| `Focus()` | 使控件进入输入状态，active cell 定位到第一个空 cell；全部填满时定位到最后一个 cell。 |

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_RootPanel` | `Panel` | 承载 cell、separator 和清除入口的根布局区域。 |
| `PART_CellsHost` | `ItemsControl` 或等价 host | 根据 `Length` 生成 cell 和 separator。 |
| `PART_ClearButton` | `InputClearIconButton` | 清除当前验证码文本。 |
| `PART_FormFeedBack` | `ContentPresenter` | Form feedback 内容承载。 |

稳定伪类：

| 伪类 | 语义 |
| --- | --- |
| `:focus` | 控件拥有键盘焦点，active cell 显示焦点视觉。 |
| `:error` | Avalonia native validation error 状态，由 `DataValidationErrors` 驱动。 |
| `:warning` | AtomUI warning 扩展状态，仅在无 native error 时生效。 |
| `:readonly` | 只读状态。 |
| `:filled` | 所有 cell 均有字符。 |
| `:empty` | `Text` 为空。 |

## 事件与命令

| `Text` | 当前验证码文本，支持双向绑定，是 Form、验证和完成事件的唯一值源。 |
事件与方法：
| `Completed` | 当归一化后的 `Text.Length == Length` 时触发，事件参数携带完整文本。 |

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### OTP 双向绑定

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/LineEdit/Views/LineEditShowCase.axaml:182`

SourceKey：`line-edit-otp-two-way`

```axaml
<StackPanel HorizontalAlignment="Left" Spacing="12">
    <atom:OtpLineEdit Text="{Binding OtpLineEditBoundValue, Mode=TwoWay}"
                      InputMode="Numeric" />
    <StackPanel Orientation="Horizontal" Spacing="8">
        <atom:Button SizeType="Small"
                     Content="设为 654321"
                     Command="{Binding SetOtpLineEditBoundValueCommand}" />
        <atom:Button SizeType="Small"
                     Content="清空"
                     Command="{Binding ClearOtpLineEditBoundValueCommand}" />
    </StackPanel>
    <atom:TextBlock Text="{Binding OtpLineEditBoundValueSummary}" />
</StackPanel>
```

### OTP 表单验证

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/LineEdit/Views/LineEditShowCase.axaml:207`

SourceKey：`line-edit-otp-form`

```axaml
<atom:Form LabelColInfo="8*"
           WrapperColInfo="16*"
           MinWidth="480"
           HorizontalAlignment="Left">
    <atom:FormItem LabelText="验证码"
                   FieldName="otp">
        <atom:FormItem.Validators>
            <atom:FormValidatorProvider>
                <atom:FormStringNotEmptyValidator Message="请输入验证码" />
            </atom:FormValidatorProvider>
        </atom:FormItem.Validators>
        <atom:OtpLineEdit Length="6"
                          InputMode="Numeric"
                          PlaceholderText="000000" />
    </atom:FormItem>
    <atom:FormActionsItem>
        <atom:SubmitButton />
    </atom:FormActionsItem>
</atom:Form>
```

### 一次性密码框

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/LineEdit/Views/LineEditShowCase.axaml:239`

SourceKey：`line-edit-otp-ant-design`

```axaml
<StackPanel Orientation="Vertical"
            HorizontalAlignment="Left"
            Spacing="28">
    <StackPanel Orientation="Vertical" Spacing="14">
        <atom:TextBlock Text="With formatter (Upcase)"
                        FontSize="16"
                        FontWeight="SemiBold" />
        <atom:OtpLineEdit Formatter="{Binding OtpLineEditUppercaseFormatter}" />
    </StackPanel>

    <StackPanel Orientation="Vertical" Spacing="14">
        <atom:TextBlock Text="With Disabled"
                        FontSize="16"
                        FontWeight="SemiBold" />
        <atom:OtpLineEdit IsEnabled="False" />
    </StackPanel>

    <StackPanel Orientation="Vertical" Spacing="14">
        <atom:TextBlock Text="With Length (8)"
                        FontSize="16"
                        FontWeight="SemiBold" />
        <atom:OtpLineEdit Length="8" />
    </StackPanel>

    <StackPanel Orientation="Vertical" Spacing="14">
        <atom:TextBlock Text="With variant"
                        FontSize="16"
                        FontWeight="SemiBold" />
        <atom:OtpLineEdit StyleVariant="Filled" />
    </StackPanel>

    <StackPanel Orientation="Vertical" Spacing="14">
        <atom:TextBlock Text="With custom display character"
                        FontSize="16"
                        FontWeight="SemiBold" />
        <atom:OtpLineEdit IsMasked="True"
                          MaskChar="●" />
    </StackPanel>

    <StackPanel Orientation="Vertical" Spacing="14">
        <atom:TextBlock Text="With custom separator"
                        FontSize="16"
                        FontWeight="SemiBold" />
        <atom:OtpLineEdit Separator="/"
                          SeparatorInterval="1" />
    </StackPanel>

    <StackPanel Orientation="Vertical" Spacing="14">
        <atom:TextBlock Text="With custom function separator"
                        FontSize="16"
                        FontWeight="SemiBold" />
        <atom:OtpLineEdit Separator="—"
                          SeparatorInterval="1">
            <atom:OtpLineEdit.SeparatorTemplate>
                <DataTemplate x:DataType="atom:OtpLineEditSeparatorContext">
                    <TextBlock Text="{Binding Content}"
                               Foreground="{Binding CellIndex, Converter={StaticResource OtpLineEditSeparatorBrushConverter}}" />
                </DataTemplate>
            </atom:OtpLineEdit.SeparatorTemplate>
        </atom:OtpLineEdit>
    </StackPanel>
</StackPanel>
```

## 状态模型

OtpLineEdit 的交互优先级：

```text
Disabled
> ReadOnly
> Native Error
> Form Error
> Form Warning
> Explicit Warning
> Explicit Error
> Focus
> PointerOver
> Normal
```

值归一化流程：

```text
User input / Paste / Form.SetValue / Binding update
      ↓
Formatter
      ↓
InputMode filter
      ↓
Length clamp
      ↓
Text
      ↓
Cell presentation + Form value changed + Completed check
```

输入行为：

- 单字符输入写入 active cell 对应位置，随后 active cell 移到下一个空 cell。
- 多字符输入或粘贴从 active cell 起顺序分发，超过 `Length` 的字符被丢弃。
- 点击后方空 cell 时，active cell 回到第一个空 cell，避免跳过中间空位。
- `Backspace` 在当前 cell 有值时清除当前字符；当前 cell 为空时回到前一位并清除。
- `Delete` 清除当前 cell，不改变后续字符顺序。
- `Left` / `Right` 在有效 cell 范围内移动 active cell。
- `Home` / `End` 分别定位到起始 cell 和最后一个可编辑 cell。

完成状态：

```text
IsCompleted = !string.IsNullOrEmpty(Text) && Text.Length == Length
```

`Completed` 只由从未完成状态进入完成状态的有效写入触发。外部绑定写入已完成文本时同样遵守完成事件语义，但重新写入相同文本不重复触发。

Form 集成以 `Text` 作为表单值。错误校验状态以 Avalonia `DataValidationErrors` 为真源；Form validator 产生的 error 写入 OtpLineEdit 根控件的 native validation 通道。内部 cell 不维护独立 error 状态，所有 cell 的 error 视觉来自根控件 effective status 投射。

## 主题与 Design Token

OtpLineEdit 使用“根输入控件 + cell presenter + separator + action 区”的视觉分层：

| 主题 | 职责 |
| --- | --- |
| `OtpLineEditTheme.axaml` | 根模板、cell host、清除入口、Form feedback、focus/error/warning/disabled 视觉投射。 |
| `OtpLineEditCellTheme.axaml` | 单个 cell 的输入表面、字符显示、placeholder、mask 和 active 状态。 |

视觉状态必须与 AtomUI 输入体系一致：

- `SizeType` 控制 cell 高度、宽度、字号和间距。
- `StyleVariant` 与 `LineEdit` 保持一致，支持 `Outlined`、`Filled`、`Borderless` 和 `Underlined` 四种输入表面；`Filled` 使用填充背景，`Borderless` 移除边框，`Underlined` 只保留下边线。
- `Status` 形成 `ExplicitStatus`，在无 native error 且无更高优先级 Form 状态时参与有效状态计算。
- `DataValidationErrors.HasErrors=true` 时，根控件和所有 cell 呈现 error 视觉。
- `IsMasked=true` 只改变字符展示，不改变 `Text`、复制、Form 值或 Completed 事件。
- `Separator` 只占据视觉布局位置，不参与输入、复制、验证或长度计算。

`OtpLineEditToken` 定义 cell 宽度和 cell 间距。separator 的默认外边距属于模板布局常量；边框、背景、focus shadow、disabled、error、warning 等输入表面语义由 `InputControlFrameTheme` 与 SharedToken 统一提供，cell 文本字号复用 SharedToken 的输入字号。

Token 来源：

OtpLineEdit 使用 `OtpLineEditToken` 表达 OTP 分格输入的专属布局语义。它只定义 cell 宽度和 cell 间距，不承载验证码文本、separator 内容、active cell、mask、placeholder、Form 状态、validation error、focus、hover、pressed 或 disabled 等运行时状态。

输入表面的颜色、边框、背景、focus shadow、disabled、error 和 warning 语义统一复用 `InputControlFrameTheme` 和 SharedToken。OtpLineEditToken 不复制这些已有输入体系 Token。

## AOT 与裁剪注意事项

资源和生命周期边界：

- 动态生成 cell 是 `Length` 驱动的控件内部结构，必须由 owner 管理创建、复用和释放。
- cell 输入由根控件 routed input 处理，不为每个 cell 建立 owner 外部订阅。
- clear button click 订阅必须在新模板接入前解绑旧按钮。
- Form feedback subscription 必须在 feedback 对象变化和 logical detach 时释放，并在 logical attach 时重新建立。
- separator context 不持有 owner 强引用，避免模板内容长期保留控件实例。

性能边界：

- `Text` 变化只进行 `Length` 范围内的 projection，不扫描外部视觉树。
- 粘贴和多字符输入只写入一次 `Text`，不逐 cell 触发多次 public value changed。
- `Length` 合理限制为短验证码输入场景；超长分格文本不是 OtpLineEdit 的职责。
- `Formatter` 由业务提供时应保持同步、快速和无副作用；控件不为 formatter 引入异步状态。

AOT 边界：

- 不通过运行时反射扫描 public API、template part 或 token。
- Token 类型通过 generator 显式注册。
- API 与 Token 契约由控件文档、源码 public surface、Token 类型或生成数据维护，不依赖 NativeAOT 不友好的运行时成员扫描。
- separator template 和 formatter 不依赖运行时动态类型发现。

## 源码索引

主要源码：

- `src/AtomUI.Desktop.Controls/OtpLineEdit/OtpLineEdit.cs`：公共 API、Avalonia 属性注册、事件、Form 接口、状态入口和模板生命周期。
- `src/AtomUI.Desktop.Controls/OtpLineEdit/OtpLineEditCell.cs`：内部 cell 控件，承载单字符显示、placeholder、active、mask 和事件回调。
- `src/AtomUI.Desktop.Controls/OtpLineEdit/OtpLineEditSeparatorContext.cs`：separator 模板上下文，提供前后 cell index 和 display index。
- `src/AtomUI.Desktop.Controls/OtpLineEdit/OtpLineEditToken.cs`：cell 宽度和 cell 间距 Token。
- `src/AtomUI.Desktop.Controls/OtpLineEdit/Themes/OtpLineEditTheme.axaml`：根模板、cell host、清除按钮和 feedback 区。
- `src/AtomUI.Desktop.Controls/OtpLineEdit/Themes/OtpLineEditCellTheme.axaml`：cell 的字符显示、mask、placeholder、active/focus/error 状态。

测试与 Gallery：

- `tests/AtomUI.Desktop.Controls.Tests/OtpLineEdit/OtpLineEditBehaviorTests.cs`：值归一化、输入、粘贴、删除、导航和 completed。
- `tests/AtomUI.Desktop.Controls.Tests/OtpLineEdit/OtpLineEditFormValueTests.cs`：Form value、reset、validation 和 `DataValidationErrors`。
- `tests/AtomUIGallery.Tests/ShowCases/LineEditShowCasePageTests.cs`：LineEdit 页面内的 OtpLineEdit 示例入口、v6.0.8 标记和 Form validator Provider 写法。
- `controlgallery/AtomUIGallery/ShowCases/DataEntry/LineEdit/Views/LineEditShowCase.axaml`：Gallery 示例和源码片段来源。

## 相关文档

- 源设计文档：`docs/controls/desktop/data-entry/otp-line-edit/overview.md`
- 实现文档：`docs/controls/desktop/data-entry/otp-line-edit/implementation.md`
- Token 文档：`docs/controls/desktop/data-entry/otp-line-edit/token.md`
- 变更记录：`docs/controls/desktop/data-entry/otp-line-edit/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/radio-button/index-cn.md

# RadioButton

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

RadioButton 控件家族用于在互斥选项集合中选择一个值。`RadioButton` 提供标准单选指示器，`RadioButtonGroup` 管理普通选项组，`OptionButton` 将选项呈现为按钮，`OptionButtonGroup` 管理共享边框的按钮组选项。

RadioButton 不负责多选集合、开关语义或复杂导航菜单。这些职责应由业务层、组合控件或更专用的 AtomUI 控件承担。

主要源码入口：

- `src/AtomUI.Desktop.Controls/RadioButton`
- `src/AtomUI.Controls/RadioButton`
- `src/AtomUI.Desktop.Controls/OptionButtonGroup`
- `src/AtomUI.Controls/OptionButtonGroup`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/DataEntry/RadioButton` |
| 状态 | Stable |

## 何时使用

RadioButton 的设计语言围绕控件职责、可观察状态和主题契约组织，而不是围绕模板节点组织。

| 维度 | 含义 | RadioButton 中的表达 |
| --- | --- | --- |
| 产品语义 | 控件在界面中承担的稳定职责。 | 普通单选指示器与按钮式单选组共享互斥选择语义。 |
| 内容承载 | 用户数据、展示内容、集合项或操作入口如何进入控件。 | `CheckedItem`、`SelectedItem`、`Items`、`ItemsSource`、`ItemTemplate`。 |
| 状态反馈 | public API、内部状态和伪类如何形成用户可感知反馈。 | checked/selected、disabled、pointer、motion、ButtonStyle 和方向组合状态。 |
| 主题语义 | ControlTheme、SharedToken、控件 Token 和模板绑定如何表达视觉。 | RadioButtonToken、OptionButtonToken 与对应 ControlTheme。 |

## 公共 API

RadioButton 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。

核心 public surface 按语义分组维护：

| 契约组 | 代表成员 | 维护含义 |
| --- | --- | --- |
| 内容与数据 | `Content`、`Icon`、`Items`、`ItemsSource`、`ItemTemplate` | 定义选项内容、图标、数据和模板入口。 |
| 选择与集合 | `IsChecked`、`CheckedItem`、`SelectedIndex`、`SelectedItem` | 维护普通单选组和按钮式单选组的当前值。 |
| 交互与状态 | `IsEnabled`、`IsMotionEnabled`、`IsWaveSpiritEnabled`、`ButtonStyle` | 表达可用性、动效和 Outline/Solid 状态视觉。 |
| 视觉与布局 | `Orientation`、`ItemSpacing`、`LineSpacing`、`SizeType`、`CornerRadius`、`BorderThickness` | 控制普通组排列、按钮组方向、尺寸和组合几何。 |

`RadioButtonGroup.CheckedChanged` 通知普通组当前项变化；`OptionButtonGroup.OptionCheckedChanged` 通知按钮组选项进入 checked 状态。两者不互相代理，继承的选择和输入事件继续遵循 Avalonia 事件语义。

主要公开类型与枚举：

- 类型：`AbstractRadioButton`、`AbstractRadioButtonGroup`、`RadioButton`、`RadioButtonGroup`、`RadioButtonGroupCheckedChangedEventArgs`、`RadioButtonOption`、`RadioIndicator`、`AbstractOptionButton`、`AbstractOptionButtonGroup`、`OptionButton`、`OptionButtonGroup`、`OptionButtonData`、`OptionCheckedChangedEventArgs`。
- 枚举：`OptionButtonStyle`、`OptionButtonPositionTrait`。`OptionButtonPositionTrait` 用于组合位置协作，不作为 Group 的方向配置入口。

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_ItemsPresenter` | `ItemsPresenter` | 承载 RadioButtonGroup 或 OptionButtonGroup 的集合容器。 |
| `PART_WaveSpirit` | `WaveSpiritDecorator` | 承载 RadioButton 或 OptionButton 的点击 Wave，使用有效圆角。 |

当前未抽取到控件专属伪类；主题主要依赖 Avalonia 标准伪类、模板绑定和内部 StyledProperty。

## 事件与命令

RadioButton 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。
`RadioButtonGroup.CheckedChanged` 通知普通组当前项变化；`OptionButtonGroup.OptionCheckedChanged` 通知按钮组选项进入 checked 状态。两者不互相代理，继承的选择和输入事件继续遵循 Avalonia 事件语义。
- 类型：`AbstractRadioButton`、`AbstractRadioButtonGroup`、`RadioButton`、`RadioButtonGroup`、`RadioButtonGroupCheckedChangedEventArgs`、`RadioButtonOption`、`RadioIndicator`、`AbstractOptionButton`、`AbstractOptionButtonGroup`、`OptionButton`、`OptionButtonGroup`、`OptionButtonData`、`OptionCheckedChangedEventArgs`。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/RadioButton/Views/RadioButtonShowCase.axaml:36`

Gallery key：`ExamplesContent` / item `0`

```axaml
<StackPanel HorizontalAlignment="Left" Spacing="10" Orientation="Horizontal">
    <atom:RadioButton Content="单选框" />
</StackPanel>
```

### 禁用

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/RadioButton/Views/RadioButtonShowCase.axaml:49`

Gallery key：`ExamplesContent` / item `1`

```axaml
<StackPanel HorizontalAlignment="Left" Orientation="Vertical">
    <StackPanel Orientation="Horizontal">
        <atom:RadioButton x:Name="ToggleDisabledRadioUnChecked"
                          IsEnabled="{Binding ToggleDisabledRadioUnCheckedEnabled}"
                          Content="单选框 1" />
        <atom:RadioButton x:Name="ToggleDisabledRadioChecked"
                          IsChecked="True"
                          IsEnabled="{Binding ToggleDisabledRadioCheckedEnabled}"
                          Content="单选框 2" />
    </StackPanel>
    <atom:Button ButtonType="Primary"
                 x:Name="ToggleDisabledButton"
                 Margin="0, 20, 0, 0"
                 Command="{Binding ToggleDisabledCommand}"
                 Content="切换禁用" />
</StackPanel>
```

### 垂直单选框组

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/RadioButton/Views/RadioButtonShowCase.axaml:111`

Gallery key：`ExamplesContent` / item `3`

```axaml
<atom:RadioButtonGroup Orientation="Vertical" HorizontalAlignment="Left">
    <atom:RadioButton Content="选项 A" />
    <atom:RadioButton Content="选项 B" />
    <atom:RadioButton Content="选项 C" />
    <atom:RadioButton Content="选项 D" />
</atom:RadioButtonGroup>
```

### 通过 ItemsSource 生成单选框组

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/RadioButton/Views/RadioButtonShowCase.axaml:127`

Gallery key：`ExamplesContent` / item `4`

```axaml
<atom:RadioButtonGroup HorizontalAlignment="Left"
```

## 状态模型

RadioButton 的状态流按以下路径收敛：

```text
Public API / inherited command / item source / user input
  -> 控件实例状态
  -> effective state / pseudo-class / template property
  -> ControlTheme selector / presenter / renderer
  -> Gallery 可观察行为
```

状态维护规则：

- Disabled 或不可交互状态优先屏蔽 pointer、keyboard、motion 和提交类反馈。
- selection/checked/active、input/value、motion、visual option 状态由控件实例或明确的数据 owner 推导，不能在 template part 之间双向竞争。
- `RadioButtonGroup.CheckedItem` 是单选组的外部值 owner，默认 `BindingMode.TwoWay` 并启用 Avalonia data validation；用户选择和 ViewModel 更新必须收敛到同一份当前项状态。
- `OptionButtonGroup` 以 SelectingItemsControl 的选择状态作为按钮组选中 source of truth，用户 checked、键盘导航和外部 `SelectedIndex` / `SelectedItem` 必须收敛到同一选择。
- `OptionButtonGroup.Orientation` 是排列方向、组合圆角、分隔线方向和方向键导航的唯一 owner，默认值为 `Horizontal`。
- 模板重套用时必须把 public API 对应状态回放到新的 part、伪类和主题变量。
- 集合、弹层、异步、动效或窗口相关状态必须能处理 reset、close、cancel、detach 和 owner 释放。

## 主题与 Design Token

RadioButton 的视觉模型由控件模板、ControlTheme、SharedToken 和必要的控件 Token 共同构成。

| 主题文件 | 职责 |
| --- | --- |
| `RadioButtonGroupTheme.axaml` | 定义局部操作入口、按钮或 handle 的状态视觉。 |
| `RadioButtonTheme.axaml` | 定义局部操作入口、按钮或 handle 的状态视觉。 |
| `RadioIndicatorTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `OptionButtonGroupTheme.axaml` | 定义按钮组 ItemsPresenter、方向布局入口、尺寸和组级边框资源。 |
| `OptionButtonTheme.axaml` | 定义按钮内容、Outline/Solid、checked/disabled、方向对齐和 Wave 视觉。 |

普通单选控件使用 `RadioButtonToken`，按钮式选项使用 `OptionButtonToken`。Token 只表达组件视觉语义，不承载 checked/selected、Orientation、GroupPositionTrait 或 EffectiveCornerRadius 运行时状态。

主题维护规则：

- 不删除或重命名已经稳定的 ControlTheme key、template part、伪类和资源 key。
- 不把可由 AXAML 表达的模板状态迁移为 C# 动态创建视觉。
- 不把 hover、pressed、selected、expanded、loading、filter、popup open 等运行时状态写入 Token。
- Browser 或平台特化主题必须保持同一 API 的语义一致。

Token 来源：

RadioButton 和 OptionButton Token 只表达组件级视觉变量，例如指示器尺寸、内容 Padding、字体和交互状态颜色。Token 不承载 checked/selected、Orientation、GroupPositionTrait、EffectiveCornerRadius 或容器 Bounds 等运行时状态。

当前 Token scope：

- `RadioButtonToken`，scope id 为 `RadioButton`，源码位于 `src/AtomUI.Desktop.Controls/RadioButton/RadioButtonToken.cs`。
- `OptionButtonToken`，scope id 为 `OptionButton`，源码位于 `src/AtomUI.Desktop.Controls/OptionButtonGroup/OptionButtonToken.cs`。

## AOT 与裁剪注意事项

资源和 AOT 约束：

- 不通过运行时反射扫描 public API、Token 或 Gallery 示例数据。
- 不把可静态声明的模板结构迁移到 C# 动态创建。
- 异步加载、上传、弹层和窗口生命周期必须能取消或释放。
- 缓存对象必须与控件、窗口、弹层或数据 owner 生命周期一致。
- Source generator 生成文件不手工编辑；需要修改时改输入源或 generator。

性能边界：

- 控件应优先复用 Avalonia 原生虚拟化、模板绑定和资源系统。
- 避免为每次状态变化创建不必要的视觉对象、订阅或动画对象。
- 大集合控件必须保证 container recycle 后不会泄漏旧 item 状态。
- OptionButtonGroup 的方向和容器位置更新为 O(realized item count)，只在方向或集合结构变化时执行。
- Group renderer 保持 O(item count)，不得在 Render 热路径创建容器列表、事件订阅或方向策略对象。
- 标准 StackPanel 负责横向自然宽度与纵向等宽排列，不为方向能力新增 VisualTree 层级。

## 源码索引

主要源码文件：

- `src/AtomUI.Desktop.Controls/RadioButton/RadioButton.cs`
- `src/AtomUI.Desktop.Controls/RadioButton/RadioButtonGroup.cs`
- `src/AtomUI.Desktop.Controls/RadioButton/RadioButtonToken.cs`
- `src/AtomUI.Desktop.Controls/RadioButton/Themes/RadioButtonGroupTheme.axaml`
- `src/AtomUI.Desktop.Controls/RadioButton/Themes/RadioButtonTheme.axaml`
- `src/AtomUI.Desktop.Controls/RadioButton/Themes/RadioIndicatorTheme.axaml`
- `src/AtomUI.Controls/RadioButton/AbstractRadioButton.cs`
- `src/AtomUI.Controls/RadioButton/AbstractRadioButtonGroup.cs`
- `src/AtomUI.Controls/RadioButton/RadioButtonGroupCheckedChangedEventArgs.cs`
- `src/AtomUI.Controls/RadioButton/RadioButtonGroupManager.cs`
- `src/AtomUI.Controls/RadioButton/RadioButtonOption.cs`
- `src/AtomUI.Controls/RadioButton/RadioIndicator.cs`
- `src/AtomUI.Desktop.Controls/OptionButtonGroup/OptionButton.cs`
- `src/AtomUI.Desktop.Controls/OptionButtonGroup/OptionButtonGroup.cs`
- `src/AtomUI.Desktop.Controls/OptionButtonGroup/OptionButtonToken.cs`
- `src/AtomUI.Desktop.Controls/OptionButtonGroup/Themes/OptionButtonGroupTheme.axaml`
- `src/AtomUI.Desktop.Controls/OptionButtonGroup/Themes/OptionButtonTheme.axaml`
- `src/AtomUI.Controls/OptionButtonGroup/AbstractOptionButton.cs`
- `src/AtomUI.Controls/OptionButtonGroup/AbstractOptionButtonGroup.cs`
- `src/AtomUI.Controls/OptionButtonGroup/OptionButtonData.cs`
- `src/AtomUI.Controls/OptionButtonGroup/OptionButtonGroupEnums.cs`
- `src/AtomUI.Controls/OptionButtonGroup/OptionCheckedChangedEventArgs.cs`

职责边界：

- 控件主文件保留 public/protected API、Avalonia 属性注册、事件和主要生命周期入口。
- Theme 文件负责静态视觉结构、template part、selector 和资源绑定。
- Token 文件只提供组件视觉变量，不保存实例状态。
- Gallery 文件只展示用法和示例，不作为运行时逻辑 owner。

## 相关文档

- 源设计文档：`docs/controls/desktop/data-entry/radio-button/overview.md`
- 实现文档：`docs/controls/desktop/data-entry/radio-button/implementation.md`
- Token 文档：`docs/controls/desktop/data-entry/radio-button/token.md`
- 变更记录：`docs/controls/desktop/data-entry/radio-button/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/rate/index-cn.md

# Rate

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

Rate 是 AtomUI 桌面控件体系中的评分控件，用于以图标序列表达评分、半选和只读展示。

Rate 不负责进度条、投票系统或任意图标列表。这些职责应由业务层、组合控件或更专用的 AtomUI 控件承担。

主要源码入口：

- `src/AtomUI.Controls/Rate`
- `src/AtomUI.Desktop.Controls/Rate`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/DataEntry/Rate` |
| 状态 | Stable |

## 何时使用

Rate 的设计语言围绕控件职责、可观察状态和主题契约组织，而不是围绕模板节点组织。

| 维度 | 含义 | Rate 中的表达 |
| --- | --- | --- |
| 产品语义 | 控件在界面中承担的稳定职责。 | Rate 是 AtomUI 桌面控件体系中的评分控件，用于以图标序列表达评分、半选和只读展示。 |
| 内容承载 | 用户数据、展示内容、集合项或操作入口如何进入控件。 | `DefaultValue`、`Value`。 |
| 状态反馈 | public API、内部状态和伪类如何形成用户可感知反馈。 | selection/checked/active、input/value、motion、visual option。 |
| 主题语义 | ControlTheme、SharedToken、控件 Token 和模板绑定如何表达视觉。 | Rate Token + ControlTheme。 |

## 公共 API

Rate 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。

核心 public surface 按语义分组维护：

| 契约组 | 代表成员 | 维护含义 |
| --- | --- | --- |
| 内容与数据 | `DefaultValue`、`Value` | 定义控件展示内容、输入数据、模板或业务对象入口。 |
| 选择与集合 | `Count`、`SelectedState` | 维护选择、展开、过滤、分页、分组或集合状态。 |
| 交互与状态 | `IsAllowClear`、`IsAllowHalf`、`IsKeyboardEnabled`、`IsMotionEnabled` | 表达用户可观察状态、可用性、清除、加载或反馈语义。 |
| 视觉与布局 | `Background`、`FontSize`、`FontStyle`、`Foreground`、`SizeType`、`StarBgColor`、`StarColor` | 影响尺寸、位置、颜色、形状、密度和模板视觉变量。 |
| 其他稳定入口 | `Character`、`FontFamily`、`FontWeight` | 保留为 public surface，变更前需确认 Gallery 和用户 XAML 依赖。 |

当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。

主要公开类型与枚举：

- 类型：`AbstractRate`、`Rate`、`RateCharacter`、`RateItem`、`RateItemsControl`、`RateValueChangedEventArgs`。
- 枚举：无。

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_ItemsPresenter` | `?` | 展示用户内容、文本、图标或模板化数据。 |
| `PART_RateItems` | `ItemsControl` | 承载集合项、布局面板或虚拟化内容。 |

当前未抽取到控件专属伪类；主题主要依赖 Avalonia 标准伪类、模板绑定和内部 StyledProperty。

## 事件与命令

Rate 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。
当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。
- 类型：`AbstractRate`、`Rate`、`RateCharacter`、`RateItem`、`RateItemsControl`、`RateValueChangedEventArgs`。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/Rate/Views/RateShowCase.axaml:36`

Gallery key：`ExamplesContent` / item `0`

```axaml
<atom:Rate/>
```

### 双向绑定

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/Rate/Views/RateShowCase.axaml:48`

Gallery key：`ExamplesContent` / item `1`

```axaml
<StackPanel Orientation="Vertical" Spacing="10">
    <atom:Rate Value="{Binding TwoWayValue}" />
    <atom:TextBlock Text="{Binding TwoWayValueSummary}" />
    <StackPanel Orientation="Horizontal" Spacing="10">
        <atom:Button SizeType="Small"
                     Command="{Binding SetFourStarsCommand}"
                     Content="设为 4 星" />
        <atom:Button SizeType="Small"
                     Command="{Binding ClearTwoWayValueCommand}"
                     Content="清空" />
    </StackPanel>
</StackPanel>
```

### 半星

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/Rate/Views/RateShowCase.axaml:70`

Gallery key：`ExamplesContent` / item `2`

```axaml
<atom:Rate DefaultValue="3.5" IsAllowHalf="True"/>
```

### 显示文案

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/Rate/Views/RateShowCase.axaml:81`

Gallery key：`ExamplesContent` / item `3`

```axaml
<StackPanel Orientation="Vertical" Spacing="10">
    <atom:Rate Name="ToolTipRate"
               ToolTips="{Binding Tooltips}"
               ValueChanged="HandleValueChanged"/>
    <TextBlock Text="{Binding ActiveTooltip}"/>
</StackPanel>
```

## 状态模型

Rate 的状态流按以下路径收敛：

```text
Public API / inherited command / item source / user input
  -> 控件实例状态
  -> effective state / pseudo-class / template property
  -> ControlTheme selector / presenter / renderer
  -> Gallery 可观察行为
```

状态维护规则：

- Disabled 或不可交互状态优先屏蔽 pointer、keyboard、motion 和提交类反馈。
- selection/checked/active、input/value、motion、visual option 状态由控件实例或明确的数据 owner 推导，不能在 template part 之间双向竞争。
- `Rate.Value` 是评分控件的外部值 owner，默认 `BindingMode.TwoWay` 并启用 Avalonia data validation；用户评分、键盘调整、Form value 和 ViewModel 更新必须收敛到同一份数值状态。
- 模板重套用时必须把 public API 对应状态回放到新的 part、伪类和主题变量。
- 集合、弹层、异步、动效或窗口相关状态必须能处理 reset、close、cancel、detach 和 owner 释放。

## 主题与 Design Token

Rate 的视觉模型由控件模板、ControlTheme、SharedToken 和必要的控件 Token 共同构成。

| 主题文件 | 职责 |
| --- | --- |
| `RateItemTheme.axaml` | 定义集合项、容器项或局部单元的状态视觉。 |
| `RateItemsControlTheme.axaml` | 定义集合项、容器项或局部单元的状态视觉。 |
| `RateTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |

Rate 使用 `RateToken` 作为控件 Token scope。Token 只表达组件视觉语义，不承载 selection/checked/active、input/value、motion、visual option 运行时状态。

主题维护规则：

- 不删除或重命名已经稳定的 ControlTheme key、template part、伪类和资源 key。
- 不把可由 AXAML 表达的模板状态迁移为 C# 动态创建视觉。
- 不把 hover、pressed、selected、expanded、loading、filter、popup open 等运行时状态写入 Token。
- Browser 或平台特化主题必须保持同一 API 的语义一致。

Token 来源：

Rate Token 只表达组件级视觉变量，例如尺寸、间距、颜色、圆角、阴影、图标尺寸和弹层边界。Token 不承载运行时选择、展开、加载、错误、上传任务、过滤条件或业务状态。

当前 Token scope：

- `RateToken`，scope id 为 `Rate`，源码位于 `src/AtomUI.Desktop.Controls/Rate/RateToken.cs`。

## AOT 与裁剪注意事项

资源和 AOT 约束：

- 不通过运行时反射扫描 public API、Token 或 Gallery 示例数据。
- 不把可静态声明的模板结构迁移到 C# 动态创建。
- 异步加载、上传、弹层和窗口生命周期必须能取消或释放。
- 缓存对象必须与控件、窗口、弹层或数据 owner 生命周期一致。
- Source generator 生成文件不手工编辑；需要修改时改输入源或 generator。

性能边界：

- 控件应优先复用 Avalonia 原生虚拟化、模板绑定和资源系统。
- 避免为每次状态变化创建不必要的视觉对象、订阅或动画对象。
- 大集合控件必须保证 container recycle 后不会泄漏旧 item 状态。

## 源码索引

主要源码文件：

- `src/AtomUI.Controls/Rate/AbstractRate.cs`
- `src/AtomUI.Controls/Rate/RateCharacter.cs`
- `src/AtomUI.Controls/Rate/RateItem.cs`
- `src/AtomUI.Controls/Rate/RateItemsControl.cs`
- `src/AtomUI.Controls/Rate/RateValueChangedEventArgs.cs`
- `src/AtomUI.Desktop.Controls/Rate/Rate.cs`
- `src/AtomUI.Desktop.Controls/Rate/RateToken.cs`
- `src/AtomUI.Desktop.Controls/Rate/Themes/RateItemTheme.axaml`
- `src/AtomUI.Desktop.Controls/Rate/Themes/RateItemsControlTheme.axaml`
- `src/AtomUI.Desktop.Controls/Rate/Themes/RateTheme.axaml`

职责边界：

- 控件主文件保留 public/protected API、Avalonia 属性注册、事件和主要生命周期入口。
- Theme 文件负责静态视觉结构、template part、selector 和资源绑定。
- Token 文件只提供组件视觉变量，不保存实例状态。
- Gallery 文件只展示用法和示例，不作为运行时逻辑 owner。

## 相关文档

- 源设计文档：`docs/controls/desktop/data-entry/rate/overview.md`
- 实现文档：`docs/controls/desktop/data-entry/rate/implementation.md`
- Token 文档：`docs/controls/desktop/data-entry/rate/token.md`
- 变更记录：`docs/controls/desktop/data-entry/rate/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/search-edit/index-cn.md

# SearchEdit

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

SearchEdit 是 AtomUI 桌面数据录入体系中的搜索输入框，用于把单行文本输入和明确的搜索触发按钮组合成一个一体化输入控件。它继承 `LineEdit` 的文本编辑、输入表面、native validation error 投射、清除按钮、密码 reveal、内部前后缀、Form 和 CompactSpace 能力，并在右侧加入固定搜索按钮。

SearchEdit 的职责是承载搜索关键字和搜索触发事件。它不负责候选项管理、自动完成、远程请求、过滤算法、搜索结果展示或异步任务编排。需要候选项和 popup 的搜索输入时，应使用 `AutoCompleteSearchEdit`；需要普通文本输入时，应使用 `LineEdit`。

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/DataEntry/LineEdit` |
| 状态 | Stable |

## 何时使用

SearchEdit 的设计语言来自输入框与搜索 action 的一体化组合。

| 维度 | 含义 | 典型表达 |
| --- | --- | --- |
| 搜索语义 | 用户输入查询文本，并通过按钮或 Enter 键请求搜索。 | `Text`、`PlaceholderText`、`SearchRequested`。 |
| 输入密度 | 控件在表单、工具栏和筛选区中的尺寸等级。 | `SizeType=Large/Middle/Small/Custom`。 |
| 输入表面 | 输入框边框和背景强度。 | `Outlined`、`Filled`、`Borderless`、`Underlined`。 |
| 搜索按钮强调度 | 搜索 action 是普通附加按钮还是主操作按钮。 | `SearchButtonStyle=Default/Primary`。 |
| 运行状态 | 搜索操作进行中，按钮显示 loading 并阻止重复请求。 | `IsOperating=true`。 |
| 输入反馈 | 搜索条件的校验或业务状态。 | `Status=Default/Error/Warning`。 |

`Custom` 尺寸不是 SearchEdit 的第四套专属 Token。它以 `Middle` 作为未显式设置时的视觉基线，并允许用户通过 `Height`、`FontSize`、`Padding` 等常规属性覆盖实际尺寸。

## 公共 API

SearchEdit 的公共 API 由继承的 `LineEdit` 文本输入 API 和搜索专项 API 组成。继承 API 的文本编辑、选择、滚动、只读、清除、reveal、Form 和 CompactSpace 语义必须保持与 LineEdit 一致。

SearchEdit 专项 API：

| API | 类型 | 语义 |
| --- | --- | --- |
| `SearchButtonStyle` | `SearchEditButtonStyle` | 搜索按钮样式，支持 `Default` 和 `Primary`。 |
| `SearchButtonText` | `string` | 搜索按钮显示文本；未设置时按钮以搜索图标为主要视觉。 |
| `IsOperating` | `bool` | 搜索进行中状态；为 `true` 时显示按钮 loading 并阻止重复触发搜索请求。 |
| `IsSearchOnEnterEnabled` | `bool` | 是否允许未处理的 Enter `KeyUp` 触发搜索请求，默认值为 `true`。 |
| `SearchButtonTheme` | `ControlTheme?` | 搜索按钮 Semantic Part Theme；`TargetType` 必须兼容 public `Button`。 |
| `SearchRequested` | `RoutedEvent<SearchRequestedEventArgs>` | 搜索请求事件；按钮和 Enter 键共用该事件，并提供查询文本快照与触发来源。 |

继承输入 API 的 SearchEdit 约束：

| API | 语义 |
| --- | --- |
| `Text` / `PlaceholderText` | 搜索关键字和空文本提示。 |
| `SizeType` | 输入尺寸密度，类型为 `CustomizableSizeType`。 |
| `StyleVariant` | 输入表面样式。 |
| `Status` | 显式输入反馈状态；最终视觉由 `InputControlFrame.EffectiveStatus` 计算，native validation error 以 `DataValidationErrors` 为唯一真源。 |
| `IsAllowClear` / `ClearIcon` | 搜索文本清除入口。 |
| `InnerLeftContent` / `InnerRightContent` | 输入框内部前后缀内容。 |
| `LeftAddOn` / `LeftAddOnTemplate` | 输入框左侧外部附加内容。 |
| `IsMotionEnabled` | 内部按钮和输入壳体动效开关。 |

SearchEdit 的右侧外部 add-on 位置由搜索按钮占用。维护时不应把用户 `RightAddOn` 与搜索按钮混用；需要放在输入框内部右侧的内容应使用 `InnerRightContent`。

稳定 template part：

| Template Part | 类型 | 所属主题 | 职责 |
| --- | --- | --- | --- |
| `PART_InputControlFrame` | `SearchEditDecoratedBox` | `SearchEditTheme.axaml` | 输入表面、effective status、CompactSpace 和搜索按钮组合入口；具体 decorated box 只扩展搜索布局。 |
| `PART_RightAddOn` | `Button` | `SearchEditDecoratedBoxTheme.axaml` | public Button 语义部件，承载图标、文字、loading、按钮样式和点击事件。 |
| `PART_ContentFrame` | `Border` | `SearchEditDecoratedBoxTheme.axaml` | 文本输入框视觉边框和背景。 |
| `PART_ScrollViewer` | `ScrollViewer` | `SearchEditTheme.axaml` | 文本滚动区域。 |
| `PART_TextPresenter` | `InputTextPresenter` | `SearchEditTheme.axaml` | 文本显示、光标、选择和密码 reveal。 |
| `Placeholder` | `TextBlock` | `SearchEditTheme.axaml` | 空文本占位提示。 |
| `PART_ClearButton` | `InputClearIconButton` | `SearchEditTheme.axaml` | 清除当前搜索文本。 |
| `PART_RevealButton` | `RevealButton` | `SearchEditTheme.axaml` | 密码 reveal 兼容入口。 |
| `InnerRightContentPresenter` | `ContentPresenter` | `SearchEditTheme.axaml` | 输入框内部右侧内容承载。 |

## 事件与命令

| `SearchRequested` | `RoutedEvent<SearchRequestedEventArgs>` | 搜索请求事件；按钮和 Enter 键共用该事件，并提供查询文本快照与触发来源。 |
| `PART_RightAddOn` | `Button` | `SearchEditDecoratedBoxTheme.axaml` | public Button 语义部件，承载图标、文字、loading、按钮样式和点击事件。 |

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/LineEdit/Views/LineEditBasicShowCase.axaml:13`

Gallery key：`ExamplesContent` / item `0`

```axaml
<atom:LineEdit PlaceholderText="基础用法" />
```

### 变体

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/LineEdit/Views/LineEditBasicShowCase.axaml:32`

Gallery key：`ExamplesContent` / item `2`

```axaml
<StackPanel Orientation="Vertical" Spacing="10">
    <atom:Separator Title="普通" />
    <atom:LineEdit PlaceholderText="线框风格" StyleVariant="Outlined" />
    <atom:LineEdit PlaceholderText="填充风格" StyleVariant="Filled" />
    <atom:LineEdit PlaceholderText="无边框" StyleVariant="Borderless" />
    <atom:LineEdit PlaceholderText="下划线" StyleVariant="Underlined" />
    <atom:SearchEdit PlaceholderText="填充风格" StyleVariant="Filled" />
    <atom:Separator Title="左侧附加和右侧附加" />
    <atom:LineEdit LeftAddOn="http://" RightAddOn=".com" Text="我的站点" />
    <atom:LineEdit LeftAddOn="http://" RightAddOn=".com" Text="我的站点" StyleVariant="Filled" />
    <atom:LineEdit LeftAddOn="http://" RightAddOn=".com" Text="我的站点" StyleVariant="Borderless" />
    <atom:LineEdit LeftAddOn="http://" RightAddOn=".com" Text="我的站点" StyleVariant="Underlined" />
</StackPanel>
```

### 带清除图标

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/LineEdit/Views/LineEditBasicShowCase.axaml:64`

Gallery key：`ExamplesContent` / item `4`

```axaml
<StackPanel Orientation="Vertical" Spacing="10">
    <atom:LineEdit PlaceholderText="带清除图标的输入框" IsAllowClear="True" />
    <atom:TextArea PlaceholderText="带清除图标的文本域" IsAllowClear="True" />
</StackPanel>
```

### 密码框

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/LineEdit/Views/LineEditBasicShowCase.axaml:73`

Gallery key：`ExamplesContent` / item `5`

```axaml
<StackPanel Orientation="Vertical" Spacing="10">
    <atom:LineEdit PlaceholderText="输入密码"
                   Width="400"
                   RevealPassword="False"
                   PasswordChar="•"
                   HorizontalAlignment="Left"
                   IsEnableRevealButton="True" />
    <atom:LineEdit PlaceholderText="输入密码"
                   Width="400"
                   RevealPassword="False"
                   HorizontalAlignment="Left"
                   PasswordChar="•"
                   IsEnableRevealButton="True"
                   IsAllowClear="True" />
</StackPanel>
```

## 状态模型

SearchEdit 的文本输入行为继承 LineEdit，搜索请求由按钮或 Enter 键进入统一管线：

```text
Button#PART_RightAddOn.Click
  ↓
SearchEditDecoratedBox.HandleSearchButtonClick
  ↓
SearchEdit.RaiseSearchRequested(Button)
  ↓
if !IsOperating raise SearchRequested

Enter KeyUp when IsSearchOnEnterEnabled && !Handled
  ↓
mark KeyUp handled
  ↓
SearchEdit.RaiseSearchRequested(EnterKey)
  ↓
if !IsOperating raise SearchRequested
```

`SearchRequestedEventArgs.Query` 保存触发时的 `Text` 快照，`Trigger` 使用 `Button` 或 `EnterKey` 区分来源。`IsOperating=true` 只表示搜索正在进行。它不改变 `Text`、不自动禁用文本编辑、不管理异步任务，也不清空搜索结果；业务层负责在搜索开始和结束时设置该属性。

状态优先级：

```text
Disabled
> Native Error
> Form Error
> Form Warning
> Explicit Warning
> Explicit Error
> Searching button loading
> Focus
> PointerOver / Pressed
> Normal
```

`IsEnabled=false` 会传递给搜索按钮，使 frame 和按钮一起进入 disabled 视觉。`DataValidationErrors`、Form warning/status 和用户 `Status` 分别通过 frame 的 native validation、`FormStatus` 和 `Status` 输入参与 `EffectiveStatus`；最终由 `InputControlFrame.EffectiveStatus` 按共享优先级投射到输入表面和搜索按钮。`SearchButtonStyle` 只控制按钮强调度，不改变文本编辑、清除、Form 或搜索事件语义。

## 主题与 Design Token

SearchEdit 使用三层主题协作：

| 主题 | 职责 |
| --- | --- |
| `SearchEditTheme.axaml` | SearchEdit 根模板、文本 presenter、placeholder、clear/reveal/inner-right 内容和 frame 组合。 |
| `InputControlFrameTheme.axaml` | 输入表面 variant、effective status、边框、背景、focus/hover/pressed、disabled、CompactSpace 和 motion。 |
| `SearchEditDecoratedBoxTheme.axaml` | 搜索按钮、左右布局、搜索按钮 style 和 z-index 关系；不重复实现 frame 状态 selector。 |
| `SearchButtonTheme.axaml` | 搜索按钮在不同输入表面中的背景、前景和状态色。 |

SearchEdit 拥有独立 `ControlTokenIdentity`，但不定义 Own Token。它继承 `LineEdit` 的行为并不意味着继承或借用
LineEdit identity；Control 级配置中的任意已注册 Global Token 都绑定到 SearchEdit 自己的 Effective Global
Token。合法但没有被当前主题直接或间接消费的 Global Token 可以没有视觉效果。

主题资源边界：

| Token 来源 | 用途 |
| --- | --- |
| `SearchEditTokenResource` | 读取 SearchEdit Effective Global Token，仅负责 SearchEdit 专属组合值；通用输入表面值由 frame theme 从 SharedToken 消费。 |
| `InputControlFrameTheme` / `SharedTokenResource` | 负责输入表面边框、背景、圆角、focus shadow、状态色、disabled 和 CompactSpace；不由 SearchEditDecoratedBox 重复实现。 |
| `ButtonTokenResource` | 由真实 Button 和 `SearchButtonTheme` 显式读取 Button Own/Effective Global Token，负责按钮基础视觉。 |
| `SharedTokenResource` | 读取真正的 Global Token，只用于不响应 SearchEdit Control 级覆盖的共享值。 |

`SearchButtonTheme` 的 `TargetType` 是 Button，但资产 owner 和组合语义属于 SearchEdit。它可以同时使用
`SearchEditTokenResource` 与 `ButtonTokenResource`；这是显式跨 Control 资源引用，不是 SearchEdit 借用 Button
或 LineEdit identity。

搜索按钮必须与输入框视觉上组成单一控件。Custom 高度下，搜索按钮的可视 `Frame` 高度必须跟随 `SearchEditDecoratedBox` 的实际布局高度，避免按钮边框和输入框边框错位。

Token 来源：

- SearchEdit 当前没有 Own Token，因此没有专属 `token.md`；LLMS 生成按第 5 节说明独立 SearchEdit identity、完整 Effective Global Token、Button Semantic Part 和显式跨 Control 资源边界。

## AOT 与裁剪注意事项

SearchEdit 不依赖运行时反射发现模板结构。跨模板协作使用固定 template part、`TemplateBinding`、selector 和 owner 引用完成。

资源和生命周期边界：

- 搜索按钮 click 订阅必须在重新套用模板时解绑旧实例。
- `OwningSearchEdit` 只保存当前模板 owner，不创建全局订阅。
- 搜索按钮高度同步使用 XAML binding，不在布局过程中写本地 `Height` 值。
- 搜索按钮状态不创建异步任务；业务异步状态由外部设置 `IsOperating`。
- SearchEdit 有独立 Control identity、没有 Own Token；运行时状态不得进入 Token schema。
- `SearchEditTokenResource` 只读取 SearchEdit 专属组合值；`InputControlFrameTheme` 读取 SharedToken 表达通用输入表面，Button 基础视觉继续显式读取 `ButtonTokenResource`。

AOT 边界：

- SearchEdit identity、强类型 TokenResource 和三个独立主题叶子通过生成 descriptor/asset manifest 注册。
- `AutoCompleteSearchEditBox` 使用显式 `StyleKeyOverride` 复用 SearchEdit 主题。
- API 与 Token 契约由控件文档、源码 public surface、Token 类型或生成数据维护，不依赖运行时反射扫描。

## 源码索引

主要源码：

- `src/AtomUI.Desktop.Controls/Input/SearchEdit.cs`：public API、默认 clear icon、模板接入、Enter 处理和搜索请求事件抛出。
- `src/AtomUI.Desktop.Controls/Input/SearchEditDecoratedBox.cs`：内部输入壳体，转接 SearchEdit 属性并订阅搜索按钮 click。
- `src/AtomUI.Desktop.Controls/Input/SearchEditPanel.cs`：搜索输入布局面板，负责左侧 AddOn、搜索按钮和内容框重叠边框排布。
- `src/AtomUI.Desktop.Controls/Input/Themes/SearchEditTheme.axaml`：SearchEdit 根模板、文本区域、内部 action 和 focus/status selector。
- `src/AtomUI.Desktop.Controls/Input/Themes/SearchEditDecoratedBoxTheme.axaml`：搜索按钮和输入框一体化模板。
- `src/AtomUI.Desktop.Controls/Input/Themes/SearchButtonTheme.axaml`：搜索按钮在不同输入表面下的状态视觉。
- `src/AtomUI.Desktop.Controls/Input/Themes/SearchButtonTheme.cs`：SearchButton Semantic Part typed theme。
- `src/AtomUI.Desktop.Controls/AutoComplete/AutoCompleteSearchEdit.cs`：AutoComplete 搜索输入入口，复用 SearchEdit 搜索按钮属性。
- `src/AtomUI.Desktop.Controls/AutoComplete/AutoCompleteSearchEditBox.cs`：AutoComplete 内部 SearchEdit box，使用 SearchEdit style key。

## 相关文档

- 源设计文档：`docs/controls/desktop/data-entry/search-edit/overview.md`
- 实现文档：`docs/controls/desktop/data-entry/search-edit/implementation.md`
- 变更记录：`docs/controls/desktop/data-entry/search-edit/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/select/index-cn.md

# Select

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

Select 是 AtomUI 桌面数据录入体系中的选项选择控件，用于在受控选项集合中完成单选、多选和标签化选择。它由输入壳体、已选结果区域、过滤输入、候选弹层、候选列表、异步加载和 Form / CompactSpace 集成组成，面向表单、筛选器、配置项和可搜索选项输入场景。

Select 的职责是选择一个或多个 `ISelectOption`，或在 `Tags` 模式下基于用户输入临时创建动态选项。它不负责远程服务协议、权限过滤、业务对象持久化、复杂树形选择、级联选择或富文本标签编辑；这些能力应由业务层或 TreeSelect / Cascader 等专用控件承担。

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/DataEntry/Select` |
| 状态 | Stable |

## 何时使用

Select 的设计语言来自输入框、已选结果和候选弹层的组合。

| 维度 | 含义 | 典型表达 |
| --- | --- | --- |
| 选择模式 | 控件选择单个选项、多个选项或允许用户创建标签。 | `Mode=Single/Multiple/Tags`。 |
| 选项数据 | 候选项可来自本地集合、内容子项或异步 loader。 | `OptionsSource`、`Options`、`OptionsLoader`。 |
| 选择结果 | 单选结果显示为文本，多选和 tags 显示为可关闭标签。 | `SelectedOption`、`SelectedOptions`、`MaxTagCount`。 |
| 搜索过滤 | 输入搜索文本后过滤候选项。 | `IsFilterEnabled`、`FilterValue`、`Filter`、`FilterValueSelector`。 |
| 输入表面 | 控件边框、背景、尺寸、状态和附加内容。 | `StyleVariant`、`Status`、`SizeType`、Addon。 |
| 弹层语义 | 候选列表按 placement 打开，并可匹配控件宽度。 | `IsDropDownOpen`、`Placement`、`IsPopupMatchSelectWidth`。 |
| 异步加载 | 打开控件时按上下文加载候选数据。 | `OptionsLoader`、`OptionsAsyncLoadContext`、`IsLoading`。 |

`Custom` 尺寸不是 Select 的第四套专属 Token。主题层把 `SizeType=Custom` 归入 `Middle` 的默认字体和 padding 分支；用户显式设置尺寸属性时由 Avalonia 属性优先级决定最终布局。

## 公共 API

Select 的公共 API 分布在 `AbstractSelect` 和 `Select` 两层。`AbstractSelect` 提供输入壳体、弹层、清除、状态、尺寸、Addon、Form 和 CompactSpace 契约；`Select` 提供选项、选择、过滤、分组、异步加载和模式契约。

选项与选择 API：

| API | 类型 | 语义 |
| --- | --- | --- |
| `Mode` | `SelectMode` | 选择模式，默认 `Single`。 |
| `OptionsSource` | `IEnumerable<ISelectOption>?` | 外部候选项集合。Select 只读取该集合，不把 Tags 运行时动态选项写回该集合。 |
| `Options` | `ItemCollection` | XAML 内容子项入口。它表达用户声明的静态候选项，不承载 Tags 模式运行时动态选项。 |
| `OptionTemplate` | `IDataTemplate?` | 候选项显示模板，默认显示 `ISelectOption.Header`。 |
| `SelectedOption` | `ISelectOption?` | 单选模式当前选项，默认 `TwoWay` 绑定并启用 Avalonia data validation。 |
| `SelectedOptions` | `IList<ISelectOption>?` | 多选和 Tags 模式当前选项集合，默认 `TwoWay` 绑定并启用 Avalonia data validation。 |
| `DefaultValues` | `IList<object>?` | 加载后按值匹配默认选中项。 |
| `DefaultValueCompareFn` | `Func<object, ISelectOption, bool>?` | 默认值匹配自定义比较函数。 |
| `SelectionChanged` | event | `SelectedOption` 或 `SelectedOptions` 改变时触发。 |

过滤与列表 API：

| API | 类型 | 语义 |
| --- | --- | --- |
| `IsFilterEnabled` | `bool` | 是否启用搜索过滤。`Tags` 模式始终形成有效过滤能力。 |
| `FilterValue` | `object?` | 当前过滤输入值。 |
| `Filter` | `IValueFilter?` | 候选项过滤器。为空时使用 ListView 默认过滤行为。 |
| `FilterValueSelector` | `DefaultFilterValueSelector?` | 从候选项中提取过滤文本。默认使用 `HeaderFilterPropertySelector`。 |
| `IsGroupEnabled` | `bool` | 是否启用候选项分组。 |
| `GroupPropertySelector` | `DefaultFilterValueSelector?` | 分组值 selector。 |
| `IsDefaultActiveFirstOption` | `bool` | 当前 public surface 的一部分；维护时需要先确认候选激活路径再改变其语义。 |
| `IsHideSelectedOptions` | `bool` | 多选候选列表中隐藏已选项。 |
| `AutoScrollToSelectedOptions` | `bool` | 候选列表打开或选择同步时滚动到已选项。 |
| `DisplayPageSize` | `int` | 候选弹层可视行数，用于计算最大高度，默认 `10`。 |
| `MaxCount` | `int` | 多选最大可选数量，默认 `int.MaxValue`。 |

选择绑定语义：

- `SelectedOption` 是 `Mode=Single` 的唯一选择状态入口；用户选择、清除、Form 写值和 ViewModel 写值都通过该属性同步。
- `SelectedOptions` 是 `Mode=Multiple/Tags` 的唯一选择状态入口；用户选择变化会回写绑定源，绑定源替换集合或对 `INotifyCollectionChanged` 集合执行 `Add` / `Remove` / `Reset` 时，Select 会同步标签、候选列表和计数状态。
- `OptionsSource` 始终只是候选项来源。即使 `SelectedOptions` 绑定到可变集合，Tags 模式运行时动态选项也只进入选择集合和内部有效候选集合，不写回 `OptionsSource`。

弹层与异步 API：

| API | 类型 | 语义 |
| --- | --- | --- |
| `IsDefaultOpen` | `bool` | 当前 public surface 的一部分；默认打开行为需与 `IsDropDownOpen` 状态保持兼容。 |
| `IsDropDownOpen` | `bool` | 候选弹层打开状态。 |
| `Placement` | `SelectPopupPlacement` | 候选弹层展开位置，默认 `BottomEdgeAlignedLeft`。 |
| `IsPopupMatchSelectWidth` | `bool` | 弹层最小宽度是否匹配 Select 宽度，默认 `true`。 |
| `ShouldUseOverlayPopup` | `bool` | 是否使用 overlay popup 宿主，默认 `true`。 |
| `OptionsLoader` | `ISelectOptionsAsyncLoader?` | 异步候选加载器。 |
| `OptionsAsyncLoadContext` | `object?` | 传给异步 loader 的上下文。 |
| `AsyncLoadTimeout` | `TimeSpan` | 异步加载超时时间，默认 `10` 秒。 |
| `IsLoading` | `bool` | 异步加载或 loading 状态，只读公开。 |
| `OptionsLoading` / `OptionsLoaded` | event | 开始加载和加载完成通知。 |
| `DropDownOpening` / `DropDownClosing` | event | 弹层打开/关闭前通知，可取消。 |
| `DropDownOpened` / `DropDownClosed` | event | 弹层打开/关闭后通知。 |

输入表面 API：

| API | 类型 | 语义 |
| --- | --- | --- |
| `SizeType` | `CustomizableSizeType` | 输入尺寸密度，支持 `Large/Middle/Small/Custom`。 |
| `StyleVariant` | `InputControlStyleVariant` | 输入表面样式。 |
| `Status` | `InputControlStatus` | 显式输入反馈状态；最终视觉由 `InputControlFrame.EffectiveStatus` 计算，native validation error 以 `DataValidationErrors` 为唯一真源。 |
| `PlaceholderText` / `PlaceholderForeground` | `string?` / `IBrush?` | 空选择时的占位文本和颜色。 |
| `IsAllowClear` / `ClearIcon` | `bool` / `PathIcon?` | 清除入口和图标。 |
| `SuffixIcon` / `SuffixLoadingIcon` | `PathIcon?` | 普通展开指示和 loading 指示。 |
| `LeftAddOn` / `RightAddOn` | `object?` | 外部左右 AddOn。 |
| `ContentLeftAddOn` / `ContentRightAddOn` | `object?` | 内部左右内容。 |
| `IsMotionEnabled` | `bool` | 输入壳体、handle、候选列表和 popup 动效开关。 |
| `IsShowOverflowTip` | `bool` | 选中结果文本或多选 tag 视觉溢出时是否显示完整内容 tooltip，默认 `true`。 |
| `OverflowTipDelay` | `int` | 溢出 tooltip 打开前的延迟时间，单位毫秒，默认 `1200`。 |
| `OverflowTipPlacement` | `PlacementMode` | 溢出 tooltip 相对选中结果文本或多选 tag 的位置，默认 `TopEdgeAlignedLeft`。 |

多选标签 API：

| API | 类型 | 语义 |
| --- | --- | --- |
| `MaxTagCount` | `int?` | 最大直接展示标签数量。 |
| `IsResponsiveTagMode` | `bool` | 使用响应式标签布局。 |
| `MaxTagPlaceholder` | `string?` | 当前 public surface 的一部分；默认模板使用剩余数量标签表达折叠信息。 |
| `IsShowMaxCountIndicator` | `bool` | 是否展示最大数量指示。 |

候选项契约：

| 类型 | 语义 |
| --- | --- |
| `ISelectOption.Header` | 候选项显示内容。 |
| `ListItemData.Content` | 候选项值和默认值匹配的主要输入。 |
| `ListItemData.ItemKey` | 候选项稳定标识，优先用于选项替换后的选择映射。 |
| `ListItemData.IsEnabled` | 候选项启用状态。 |
| `ISelectOption.IsDynamicAdded` | Tags 模式下用户输入生成的运行时动态选项标记。 |

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_InputControlFrame` | `SelectAddOnDecoratedBox` | `InputControlFrame` / AddOnDecoratedBox 组合、Addon、EffectiveStatus、CompactSpace 和 hover/pressed 状态承载；选择专用节点只扩展内容布局。 |
| `PART_SingleFilterInput` | `SelectFilterTextBox` | 单选模式结果显示和搜索输入。 |
| `SelectedOptionsBox` | `SelectResultOptionsBox` | 多选和 Tags 模式已选标签与搜索输入。 |
| `PART_SelectMaxCountIndicator` | `SelectMaxCountIndicator` | 最大选择数量提示。 |
| `PART_ContentRightAddOnPresenter` | `ContentPresenter` | 用户内部右侧内容承载。 |
| `PART_SelectHandle` | `SelectHandle` | 展开、loading、清除和 Form feedback 图标入口。 |
| `PART_Popup` | `Popup` | 候选弹层宿主。 |
| `PopupFrame` | `Border` | 懒创建的候选弹层外壳。 |
| `PART_CandidateList` | `SelectCandidateList` | 懒创建的候选项列表、键盘导航和提交/取消。 |

Select 的稳定伪类包括 `:dropdownopen`；输入表面通过 `InputControlFrame` 统一表达 `StyleVariant`、`EffectiveStatus`、`:pressed`、`:disabled` 和 CompactSpace，选择专用伪类只表达 popup、候选和结果状态。

## 事件与命令

| `SelectionChanged` | event | `SelectedOption` 或 `SelectedOptions` 改变时触发。 |
| `OptionsLoading` / `OptionsLoaded` | event | 开始加载和加载完成通知。 |
| `DropDownOpening` / `DropDownClosing` | event | 弹层打开/关闭前通知，可取消。 |
| `DropDownOpened` / `DropDownClosed` | event | 弹层打开/关闭后通知。 |

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/Select/Views/SelectShowCase.axaml:38`

Gallery key：`ExamplesContent` / item `0`

```axaml
<WrapPanel ItemSpacing="10" LineSpacing="10">
    <atom:Select Mode="Single" PlaceholderText="请选择" Width="120"
                 DefaultValues="lucy"
                 OptionsSource="{Binding BasicSelectedOptions}" />

    <atom:Select Mode="Single" PlaceholderText="请选择" IsEnabled="False" Width="120"
                 DefaultValues="lucy"
                 OptionsSource="{Binding SingleLucyOptions}" />

    <atom:Select Name="AsyncLoadSelect"
                 Mode="Single"
                 PlaceholderText="请选择"
                 Width="120"
                 DefaultValues="lucy"
                 OptionsSource="{Binding SingleLucyOptions}"
                 OptionsLoader="{Binding SelectOptionsAsyncLoader}" />

    <atom:Select Mode="Single" PlaceholderText="请选择" IsAllowClear="True" Width="120"
                 OptionsSource="{Binding SingleLucyOptions}" />

    <atom:Select Name="DefaultSelectedSelect"
                 Mode="Single"
                 PlaceholderText="请选择"
                 IsAllowClear="True"
                 Width="120"
                 OptionsSource="{Binding BasicSelectedOptions}"
                 SelectedOption="{Binding DefaultSelectedOption}" />

</WrapPanel>
```

### 双向绑定

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/Select/Views/SelectShowCase.axaml:79`

Gallery key：`ExamplesContent` / item `1`

```axaml
<WrapPanel ItemSpacing="24" LineSpacing="16">
    <StackPanel Spacing="8" MinWidth="260">
        <TextBlock Text="SelectedOption"
                   FontWeight="SemiBold" />
        <atom:Select Mode="Single"
                     PlaceholderText="请选择"
                     IsAllowClear="True"
                     Width="220"
                     OptionsSource="{Binding BasicSelectedOptions}"
                     SelectedOption="{Binding BoundSelectedOption}" />
        <WrapPanel ItemSpacing="8">
            <atom:Button SizeType="Small"
                         Command="{Binding SetBoundSelectedOptionCommand}"
                         Content="设为 Lucy" />
            <atom:Button SizeType="Small"
                         Command="{Binding ClearBoundSelectedOptionCommand}"
                         Content="清空" />
        </WrapPanel>
        <TextBlock Text="ViewModel 值" />
        <TextBlock Text="{Binding BoundSelectedOptionText}" />
    </StackPanel>

    <StackPanel Spacing="8" MinWidth="320">
        <TextBlock Text="SelectedOptions"
                   FontWeight="SemiBold" />
        <atom:Select Mode="Multiple"
                     PlaceholderText="请选择人员"
                     IsAllowClear="True"
                     IsFilterEnabled="True"
                     Width="300"
                     OptionsSource="{Binding BasicSelectedOptions}"
                     SelectedOptions="{Binding BoundSelectedOptions}" />
        <WrapPanel ItemSpacing="8">
            <atom:Button SizeType="Small"
                         Command="{Binding SetBoundSelectedOptionsCommand}"
                         Content="设为 Jack + Yiminghe" />
            <atom:Button SizeType="Small"
                         Command="{Binding ClearBoundSelectedOptionsCommand}"
                         Content="清空" />
        </WrapPanel>
        <TextBlock Text="ViewModel 值" />
        <TextBlock Text="{Binding BoundSelectedOptionsText}"
                   TextWrapping="Wrap" />
    </StackPanel>
</WrapPanel>
```

### 带搜索框的选择器

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/Select/Views/SelectShowCase.axaml:135`

Gallery key：`ExamplesContent` / item `2`

```axaml
<atom:Select Mode="Single"
```

### 自定义搜索

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/Select/Views/SelectShowCase.axaml:150`

Gallery key：`ExamplesContent` / item `3`

```axaml
<atom:Select Name="CustomSearchSelect"
```

## 状态模型

Select 的核心状态流：

```text
OptionsSource / Options / OptionsLoader
      ↓
用户选项源
      ↓
Effective options = 用户选项源 + Tags 运行时动态选项
      ↓
SelectCandidateList
      ↓
SelectedOption / SelectedOptions
      ↓
SingleFilterInput / SelectedOptionsBox
      ↓
Form value + SelectionChanged
```

选择模式语义：

- `Single` 使用 `SelectedOption` 作为唯一表单值，内部单行过滤输入负责展示当前 `Header`。
- `Multiple` 使用 `SelectedOptions` 作为表单值，已选项以 `SelectTag` 展示。
- `Tags` 以 `Multiple` 为基础，始终启用有效过滤，并在过滤结果为空且输入非空时创建 `IsDynamicAdded=true` 的运行时动态选项。

弹层交互优先级：

```text
Disabled / invisible / window deactivated
> DropDownClosing cancellation
> DropDownOpening cancellation
> IsDropDownOpen
> pointer / keyboard open request
```

键盘行为：

- 弹层打开时按键优先交给 `SelectCandidateList.HandleKeyDown()`。
- `Enter` 在单选模式提交候选项，在多选模式切换候选项选中状态。
- `Escape` 取消候选并关闭弹层。
- `F4` 或 `Alt+Up/Down` 切换弹层。
- 弹层关闭时，`Up/Down`、`Enter`、`Space` 可打开弹层。
- 多选和 Tags 模式下，过滤输入为空时 `Backspace/Delete` 删除最后一个已选项。

候选交互：

- `SelectCandidateList` 维护唯一 active candidate，鼠标移动和键盘导航必须更新同一个候选状态。
- 鼠标在可用候选项上实际移动时，该项成为 active candidate；鼠标路径不滚动候选列表，也不提交公共选择。
- `Up/Down` 在当前有效候选视图中移动 active candidate，并可以把键盘候选滚动到可见区域。
- `Enter` 只提交或切换当前 active candidate，候选视觉目标和提交目标必须一致。
- active candidate 与已确认选择相互独立；已选项继续保持 selected 视觉和公共选择语义。
- `:pointerover` 只表达指针命中事实，不能与 `IsCandidateSelected` 分别绘制两个候选高亮。

完整状态、Theme、虚拟化、性能和验证边界见 [Select 候选交互设计](candidate-interaction-design.md)。

清除行为通过 `SelectHandle.ClearRequestedEvent` 冒泡到 Select，并调用 `ClearValue()` 清空 `SelectedOption` 和 `SelectedOptions`。

## 主题与 Design Token

Select 的默认视觉由 Select 专属主题、`InputControlFrame` / AddOnDecoratedBox、ListView 和 PopupHost 协作完成。

| 主题或资源 | 职责 |
| --- | --- |
| `SelectTheme.axaml` | 根模板、输入壳体、单选输入、多选结果区域、handle、popup 和基础 selector。 |
| `InputControlFrameTheme.axaml` | 输入框 variant、effective status、hover、pressed、disabled、CompactSpace 和 motion。 |
| `SelectAddOnDecoratedBoxTheme.axaml` | dropdown open、多选 padding、handle 和选择结果布局；不重复实现 frame 状态 selector。 |
| `SelectResultOptionsBoxTheme.axaml` | 多选标签布局、响应式标签布局和搜索输入承载。 |
| `SelectCandidateListTheme.axaml` | 候选列表基础 ListView 主题和默认候选模板。 |
| `SelectCandidateListItemTheme.axaml` | 候选项 active、selected、disabled 和隐藏已选项视觉；active 只由统一候选状态驱动。 |
| `SelectTagTheme.axaml` | 多选标签高度、背景、关闭按钮和禁用态。 |
| `SelectHandleTheme.axaml` | 展开、loading、清除、过滤指示和 Form feedback 图标。 |
| `PopupHostToken` | popup 阴影、圆角和 anchor margin。 |
| `SelectToken` | 多选标签、候选项、popup padding 和输入 padding。 |

选中结果的完整内容提示复用 `OverflowTip` attached behavior。模板只在单选文本和多选 tag 上声明 `IsShowOverflowTip` / `OverflowTipDelay` / `OverflowTipPlacement`，实际 tooltip 只在文本视觉宽度不足时写入 `ToolTip.Tip`，且不会覆盖用户手动设置的 tooltip。

候选弹层内容采用懒创建模型。`PART_Popup` 属于模板稳定 part；`PopupFrame` 和 `PART_CandidateList` 在打开前由 C# 创建并设置 `TemplatedParent`，关闭或重新套用模板时释放引用和事件订阅。

Token 来源：

SelectToken 是 Select 的控件级 Token scope，描述多选标签、候选项、候选弹层 padding 和 Select 输入内容 padding。输入表面的通用边框、圆角、状态色、focus ring、disabled 背景、CompactSpace 和 AddOn 结构来自 `InputControlFrameTheme`、SharedToken 和 PopupHostToken；SelectToken 不复制这些共享职责。

SelectToken 不承载以下状态：

- `OptionsSource`、`Options`、`SelectedOption`、`SelectedOptions`、`DefaultValues` 等数据状态。
- `FilterValue`、`IsDropDownOpen`、`IsLoading`、`Status`、`Mode` 等运行状态。
- 候选项 hover、pressed、selected、disabled 的当前实例状态。
- Popup 当前宽度、高度、offset、placement 或异步加载结果。

## AOT 与裁剪注意事项

Select 不依赖运行时反射发现模板结构。模板协作通过固定 template part、显式类型、属性绑定和事件完成。

资源和生命周期边界：

- `SubscriptionsOnOpen` 只在 popup 打开期间持有可见性订阅，popup 关闭时清空。
- `_deactivationSubscription` 在 attach 时创建，detach 时释放。
- `_selectHandleInputStateBindings` 每次模板接入前释放旧绑定，仅持有 frame layout part → SelectHandle 的 hover / pressed sibling 状态转发，不承载输入表面状态归一。
- `_candidateList` 的事件订阅和 `ItemsSource` 必须在 `ClearPopupContent()` 中释放。
- active candidate 在 popup 关闭、popup 内容释放、detach、过滤上下文变化和候选失效时清除；容器回收只清理本地投影。
- `SelectHandle` 订阅 `FormFeedback.ValidateStatus` 时必须在 feedback 变化和 logical detach 时释放，并在 logical attach 时按当前 feedback 状态重新建立。
- 异步加载通过 `AsyncSearchLoadCoordinator` 处理超时、取消和跳过旧结果。

AOT 边界：

- `SelectToken` 通过 token generator 显式注册，生成 `SelectTokenKind` 和 `SelectTokenResourceExtension`。
- API 与 Token 契约由控件文档、源码 public surface、Token 类型或生成数据维护。
- `OptionTemplate`、AddOn 模板和 EmptyIndicator 模板是 XAML 模板入口，不依赖运行时成员扫描。

候选交互热路径边界：

- 同一候选项内的 PointerMoved 使用 source index 比较后 O(1) 返回。
- 候选迁移只更新旧、新两个已准备容器；不得逐项写入全部 `ItemCount`。
- 鼠标路径不滚动，键盘路径每次候选变化最多执行一次滚动。
- 事件源解析和状态投影不创建长期订阅、每项 handler、timer 或运行时动态发现。

## 源码索引

主要源码：

- `src/AtomUI.Desktop.Controls/Select/AbstractSelect.cs`：输入壳体、弹层状态、公共输入属性、Form / CompactSpace / Motion 接口和 popup 生命周期。
- `src/AtomUI.Desktop.Controls/Select/Select.cs`：public Select API、protected 扩展 hook、用户选项源同步、有效候选选项同步、选择同步、过滤输入、Tags 动态选项、键盘和指针处理。
- `src/AtomUI.Desktop.Controls/Select/Select.AsyncOptionsLoad.cs`：异步候选加载和私有加载完成流程。
- `src/AtomUI.Desktop.Controls/Select/SelectOption.cs`：`ISelectOption` 和默认 `SelectOption`。
- `src/AtomUI.Desktop.Controls/Select/SelectCandidateList.cs`：统一 active candidate、鼠标与键盘候选导航、提交取消、最大选择数和隐藏已选项。
- `src/AtomUI.Desktop.Controls/Select/SelectCandidateListItem.cs`：active candidate 和 committed selection 的容器状态投影。
- `src/AtomUI.Desktop.Controls/Select/SelectResultOptionsBox.cs`：多选结果标签和过滤输入承载。
- `src/AtomUI.Desktop.Controls/Select/SelectHandle.cs`：右侧展开、loading、清除和 Form feedback 图标。
- `src/AtomUI.Desktop.Controls/Tooltip/OverflowTip.cs`：共享溢出 tooltip attached behavior，供单选结果和多选 tag 复用。
- `src/AtomUI.Desktop.Controls/Select/DataLoad/*`：异步候选加载接口、结果和事件参数。
- `src/AtomUI.Desktop.Controls/Select/SelectToken.cs`：Select 控件 Token。
- `src/AtomUI.Desktop.Controls/Select/Themes/*.axaml`：Select 根模板、候选列表、结果标签、handle、输入壳体和 token 样式。

## 相关文档

- 源设计文档：`docs/controls/desktop/data-entry/select/overview.md`
- 实现文档：`docs/controls/desktop/data-entry/select/implementation.md`
- Token 文档：`docs/controls/desktop/data-entry/select/token.md`
- 变更记录：`docs/controls/desktop/data-entry/select/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/slider/index-cn.md

# Slider

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

Slider 是桌面数据录入体系中的连续或离散数值选择控件。它支持单值模式和任意数量 handle 的 Range 模式，并通过轨道、thumb、mark、键盘和 Tooltip 表达数值选择。

Slider 继承 Avalonia `RangeBase`，使用 `Minimum`、`Maximum`、`Value`、`SmallChange` 和 `LargeChange` 的基础语义。Range 模式不使用独立的起止值类型，而是通过 `RangeValues` 维护有序的多值集合。

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/DataEntry/Slider` |
| 状态 | Stable |

## 何时使用

| 维度 | 含义 | 典型表达 |
| --- | --- | --- |
| 数值范围 | 用户在有限区间中选择值。 | `Minimum`、`Maximum`、`Value`。 |
| 多点范围 | 多个 thumb 表达多个边界和活动 segment。 | `IsRangeMode`、`RangeValues`。 |
| Handle 状态 | 每个 thumb 可独立 disabled、聚焦和拖动。 | `DisabledHandles`。 |
| 整体平移 | 首尾 handle 之间的范围整体移动。 | `IsDraggableTrack`。 |
| 操作方向 | 数值沿水平或垂直轨道变化。 | `Orientation`。 |
| 方向语义 | 视觉方向与数值增长方向可反转。 | `IsDirectionReversed`。 |
| 离散步进 | 数值吸附到固定 tick。 | `IsSnapToTickEnabled`、`TickFrequency`。 |
| 轨道语义 | 局部 segment 和整体活动范围可独立绘制。 | `TrackBarBrush`、`TracksBrush`。 |
| 刻度说明 | mark 展示关键点和标签。 | `Marks`、`SliderMark`、`IsIncluded`。 |
| 反馈展示 | 每个 handle 的 Tooltip 展示当前值。 | `ValueFormatTemplate`。 |

## 公共 API

### RangeBase 数值 API

| API | 类型 | 语义 |
| --- | --- | --- |
| `Minimum` | `double` | 允许的最小值。 |
| `Maximum` | `double` | 允许的最大值。 |
| `Value` | `double` | 单值模式的当前值。 |
| `SmallChange` | `double` | 方向键单次调整量。 |
| `LargeChange` | `double` | PageUp / PageDown 单次调整量。 |

### Slider API

| API | 类型 | 语义 |
| --- | --- | --- |
| `Orientation` | `Orientation` | 轨道方向，默认 `Horizontal`。 |
| `IsDirectionReversed` | `bool` | 反转数值增长方向。 |
| `IsSnapToTickEnabled` | `bool` | 是否把指针和键盘输入吸附到 tick。 |
| `IsRangeMode` | `bool` | 是否使用 `RangeValues` 和多 handle 视觉。 |
| `RangeValues` | `IReadOnlyList<double>?` | Range 模式下所有 handle 的当前值，至少两个值。 |
| `DisabledHandles` | `IReadOnlyList<bool>?` | 按索引禁用指定 handle。 |
| `IsDraggableTrack` | `bool` | 是否允许整体活动范围平移。 |
| `TrackBarBrush` | `IBrush?` | 相邻 handle 之间局部轨道的画刷。 |
| `TracksBrush` | `IBrush?` | 首个到最后一个 handle 的整体轨道画刷。 |
| `Marks` | `List<SliderMark>?` | 刻度点和标签集合。 |
| `ValueFormatTemplate` | `string` | Tooltip 文本格式，默认 `{0:0}`。 |
| `IsIncluded` | `bool` | 是否绘制活动轨道和 mark 激活态。 |
| `IsMotionEnabled` | `bool` | 是否启用轨道、thumb 和 mark 动效。 |
| `IsWaveSpiritEnabled` | `bool` | 是否启用 wave spirit 能力。 |

Range 模式使用示例：

```xml
<atom:Slider
    IsRangeMode="True"
    RangeValues="0, 10, 20" />
```

```xml
<atom:Slider
    IsRangeMode="True"
    RangeValues="20, 50, 80"
    DisabledHandles="False, True, False"
    IsDraggableTrack="True" />
```

`RangeValues` 进入控件后会过滤非法值、裁剪到最小最大值并按数值升序归一化。RangeValues 少于两个值时使用 `[Minimum, Minimum]`。`DisabledHandles` 缺失项为 enabled，超出 handle 数量的项忽略。集合采用快照语义，修改时重新赋值集合实例。

### 模板部件与伪类

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_Track` | `SliderTrack` | 动态 handle 管理、轨道和 mark 渲染、布局和拖动几何。 |

| 伪类 | 语义 |
| --- | --- |
| `:horizontal` | 当前为水平布局。 |
| `:vertical` | 当前为垂直布局。 |
| `:pressed` | Slider 处于 pressed 状态。 |

SliderThumb 是 SliderTrack 管理的动态视觉节点，不作为固定数量的公共 Template Part 暴露。

## 事件与命令

Slider 的事件与命令以控件文档、源码 public surface 和 Avalonia 基类契约为准；生成器不从源码发明额外事件。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/Slider/Views/SliderShowCase.axaml:34`

Gallery key：`ExamplesContent` / item `0`

```axaml
<StackPanel Orientation="Vertical" Spacing="20">
    <atom:Slider
        Maximum="100"
        Minimum="0"
        TickFrequency="5"
        IsEnabled="{Binding NormalEnabled}"
        Value="50" />

    <atom:Slider
        Maximum="100"
        Minimum="0"
        IsRangeMode="True"
        TickFrequency="5"
        IsEnabled="{Binding NormalEnabled}"
        RangeValues="{Binding DefaultRangeValues, Mode=OneWay}" />
    <atom:Slider
        Name="Slider1"
        Marks="{Binding SliderMarks}"
        Maximum="100"
        Minimum="0"
        IsEnabled="{Binding NormalEnabled}"
        IsRangeMode="True"
        TickFrequency="5"
        RangeValues="{Binding DefaultRangeValues, Mode=OneWay}" />

    <StackPanel Orientation="Horizontal" Spacing="2">
        <atom:TextBlock VerticalAlignment="Center" Text="Enabled:" />
        <atom:ToggleSwitch VerticalAlignment="Center" SizeType="Small" IsChecked="{Binding NormalEnabled, Mode=TwoWay}" />
    </StackPanel>
</StackPanel>
```

### 多点组合

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/Slider/Views/SliderShowCase.axaml:74`

Gallery key：`ExamplesContent` / item `1`

```axaml
<Grid MinHeight="160">
    <atom:Slider
        VerticalAlignment="Center"
        Maximum="100"
        Minimum="0"
        IsRangeMode="True"
        TrackBarBrush="{x:Null}"
        RangeValues="{Binding MultiHandleRangeValues, Mode=OneWay}">
        <atom:Slider.TracksBrush>
            <LinearGradientBrush StartPoint="0%,50%" EndPoint="100%,50%">
                <GradientStop Color="#52C41A" Offset="0" />
                <GradientStop Color="#FAAD14" Offset="0.5" />
                <GradientStop Color="#FF4D4F" Offset="1" />
            </LinearGradientBrush>
        </atom:Slider.TracksBrush>
    </atom:Slider>
</Grid>
```

### 禁用指定滑块

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/Slider/Views/SliderShowCase.axaml:101`

Gallery key：`ExamplesContent` / item `2`

```axaml
<StackPanel MinHeight="160"
            VerticalAlignment="Center"
            Spacing="24">
    <atom:Slider
        Maximum="100"
        Minimum="0"
        IsRangeMode="True"
        RangeValues="{Binding DisabledHandleRangeValues, Mode=OneWay}"
        DisabledHandles="{Binding DisabledHandles}" />
    <WrapPanel Orientation="Horizontal"
               ItemSpacing="20"
               LineSpacing="12">
        <atom:CheckBox
            Content="Disabled Handle 1"
            IsChecked="{Binding IsHandle1Disabled, Mode=TwoWay}" />
        <atom:CheckBox
            Content="Disabled Handle 2"
            IsChecked="{Binding IsHandle2Disabled, Mode=TwoWay}" />
        <atom:CheckBox
            Content="Disabled Handle 3"
            IsChecked="{Binding IsHandle3Disabled, Mode=TwoWay}" />
    </WrapPanel>
</StackPanel>
```

### RangeValues 绑定

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/Slider/Views/SliderShowCase.axaml:135`

Gallery key：`ExamplesContent` / item `3`

```axaml
<StackPanel Orientation="Vertical" Spacing="12">
    <atom:Slider
        Width="260"
        Maximum="100"
        Minimum="0"
        IsRangeMode="True"
        IsSnapToTickEnabled="True"
        TickFrequency="5"
        RangeValues="{Binding BoundRangeValues}" />
    <StackPanel Orientation="Horizontal" Spacing="8">
        <atom:TextBlock VerticalAlignment="Center"
                        Text="绑定范围：" />
        <atom:TextBlock VerticalAlignment="Center"
                        Text="{Binding BoundRangeValuesText}" />
    </StackPanel>
    <StackPanel Orientation="Horizontal" Spacing="10">
        <atom:Button SizeType="Small"
                     Command="{Binding SetBoundRangeValuesCommand}"
                     Content="设为 35-85" />
        <atom:Button SizeType="Small"
                     Command="{Binding ClearBoundRangeValuesCommand}"
                     Content="清空" />
    </StackPanel>
</StackPanel>
```

## 状态模型

Slider 的核心状态流：

```text
Minimum / Maximum
      ↓
Value 或归一化后的 RangeValues
      ↓
SliderTrack.EffectiveRangeValues
      ↓
动态 SliderThumb[] + RenderContextData
      ↓
thumb arrange + track render + Tooltip
```

单值模式：

```text
IsRangeMode=false
  Value → 一个 enabled handle
```

Range 模式：

```text
IsRangeMode=true
  RangeValues[0] → handle[0]
  RangeValues[1] → handle[1]
  RangeValues[n] → handle[n]
```

范围模式行为：

- 每两个相邻 handle 之间形成一个活动轨道段。
- 首个 handle 到最后一个 handle 形成整体活动范围。
- 点击轨道时选择最近的 enabled handle。
- disabled handle 不响应 pointer、keyboard 和 focus。
- disabled handle 作为 enabled handle 的固定边界。
- `IsDraggableTrack=true` 时整体平移所有 handle。
- 任意 handle disabled 时整体轨道拖动关闭。

键盘行为：

- `Left`、`Right`、`Up`、`Down` 使用 `SmallChange`。
- `PageUp`、`PageDown` 使用 `LargeChange`。
- `Home`、`End` 移动当前焦点 handle 到有效边界。
- `IsDirectionReversed` 影响 pointer 和 keyboard 的数值方向。

Form 集成：

- 单值模式 Form 值为 `double`。
- Range 模式 Form 值为 `IReadOnlyList<double>`。
- Form 设置值、pointer 写值和 keyboard 写值共用同一套归一化路径。

连续 pointer 输入使用轨道本地坐标直接映射为 `double` 值，默认不按整数或像素量化。只有 `IsSnapToTickEnabled=true` 时，pointer 和 keyboard 写入才在提交前吸附到 `TickFrequency` 对应的 tick。

## 主题与 Design Token

Slider 的默认视觉由三层主题组成：

| 主题 | 职责 |
| --- | --- |
| `SliderTheme.axaml` | 根模板、SliderTrack、属性传递和方向状态。 |
| `SliderTrackTheme.axaml` | 轨道尺寸、rail 尺寸、mark 尺寸、mark 标签和轨道过渡。 |
| `SliderThumbTheme.axaml` | thumb 尺寸、圆点、边框、outline、focus、hover 和 disabled。 |

视觉模型：

- `SliderTrack` 绘制 rail、`TracksBrush`、`TrackBarBrush`、mark 点和 mark 文本。
- `SliderThumb` 绘制圆点、边框和 focus / hover outline。
- `TrackBarBrush` 只表达相邻 handle 之间的 segment。
- `TracksBrush` 只表达首尾 handle 之间的整体范围。
- `IsIncluded=false` 时不绘制两种活动轨道和 mark 激活态。
- disabled handle 使用对应 thumb disabled token，不改变其他 handle 的颜色。
- 全部 handle disabled 时使用 Slider 整体 disabled 状态。
- 水平布局使用 `SliderPaddingHorizontal`，垂直布局使用 `SliderPaddingVertical`。

`TrackBarBrush` 和 `TracksBrush` 是实例级画刷，不属于 SliderToken。SliderToken 只提供默认视觉值。

Token 来源：

SliderToken 是 Slider 的控件级 Token scope，定义轨道尺寸、rail 尺寸、thumb 尺寸、mark 尺寸、track / rail / mark / thumb 颜色、outline 和 orientation padding。

SliderToken 不承载：

- `Value`、`RangeValues`、`Minimum`、`Maximum`、`TickFrequency` 等实例数值。
- `DisabledHandles`、`IsDraggableTrack` 和当前拖动状态。
- `Marks` 集合、mark 文本和命中状态。
- Tooltip 文本、focus、hover、pressed 和 pointer capture 状态。
- `TrackBarBrush`、`TracksBrush` 这类实例级画刷。

这些状态由 Slider、SliderTrack、SliderThumb、Gallery ViewModel 和主题 selector 分别持有。

## AOT 与裁剪注意事项

- 动态 thumb 的创建、事件订阅、templated parent 和释放由 `SliderTrack` 成对管理。
- Slider 的模板 handler、SliderTrack 的全局 input subscription 和 pointer capture 都有明确释放入口。
- 外部 `RangeValues` 与 `DisabledHandles` 不建立集合变更订阅，调用方必须替换快照触发更新。
- pointer move 不创建或替换 thumb；只提交值快照并触发布局、Tooltip 与渲染更新。
- `RenderContextData`、pen 和 mark 文本度量由 SliderTrack 持有，不进入公共状态。
- 实现不使用反射、动态类型发现、字符串属性路径或运行时程序集扫描，保持 trimming 和 NativeAOT 友好。
- `UseLayoutRounding=false` 只应用于默认 `SliderThumbTheme`，用于保留连续拖动得到的 fractional Bounds；自定义主题需要自行保持同一平滑布局契约。

## 源码索引

- `src/AtomUI.Desktop.Controls/Slider/Slider.cs`：公共属性、Range 值归一化入口、pointer / keyboard 交互协调、Tooltip、Form、数据校验和自动化 peer 创建。
- `src/AtomUI.Desktop.Controls/Slider/SliderTrack.cs`：`EffectiveRangeValues` 投影、动态 thumb 生命周期、handle 布局、track / tracks / mark 渲染和几何换算。
- `src/AtomUI.Desktop.Controls/Slider/SliderThumb.cs`：单个 handle 的 pointer capture、drag routed event、focus / pressed / disabled 状态和绘制。
- `src/AtomUI.Desktop.Controls/Slider/SliderRangeMath.cs`：Range 值归一化、handle 边界、整体 offset、值与坐标比例及 segment 几何的纯计算。
- `src/AtomUI.Desktop.Controls/Slider/SliderToken.cs`：Slider Token scope、尺寸、颜色、padding 和 outline 默认值计算。
- `src/AtomUI.Desktop.Controls/Slider/SliderAutomationPeer.cs`：Slider 自动化 peer。
- `src/AtomUI.Desktop.Controls/Slider/SliderThumbAutomationPeer.cs`：单个动态 thumb 的自动化 peer。
- `src/AtomUI.Desktop.Controls/Slider/Themes/SliderTheme.axaml`：根模板、`PART_Track`、属性传递和方向样式。
- `src/AtomUI.Desktop.Controls/Slider/Themes/SliderTrackTheme.axaml`：track 尺寸、mark Token 和轨道 transition。
- `src/AtomUI.Desktop.Controls/Slider/Themes/SliderThumbTheme.axaml`：thumb 尺寸、边框、outline、focus、hover、disabled 和布局取整策略。
- `tests/AtomUI.Desktop.Controls.Tests/Slider/SliderBehaviorTests.cs`：值计算、动态 thumb、pointer、布局和生命周期回归。
- `controlgallery/AtomUIGallery/ShowCases/DataEntry/Slider/`：Slider Gallery 示例、ViewModel 和本地化资源。

## 相关文档

- 源设计文档：`docs/controls/desktop/data-entry/slider/overview.md`
- 实现文档：`docs/controls/desktop/data-entry/slider/implementation.md`
- Token 文档：`docs/controls/desktop/data-entry/slider/token.md`
- 变更记录：`docs/controls/desktop/data-entry/slider/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/time-picker/index-cn.md

# TimePicker

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

TimePicker 是 AtomUI 桌面控件体系中的时间选择控件，用于在输入壳体和时间面板之间选择时分秒。

TimePicker 不负责日期选择、时区转换或业务排班模型。这些职责应由业务层、组合控件或更专用的 AtomUI 控件承担。

主要源码入口：

- `src/AtomUI.Desktop.Controls/TimePicker`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/DataEntry/TimePicker` |
| 状态 | Stable |

## 何时使用

TimePicker 的设计语言围绕控件职责、可观察状态和主题契约组织，而不是围绕模板节点组织。

| 维度 | 含义 | TimePicker 中的表达 |
| --- | --- | --- |
| 产品语义 | 控件在界面中承担的稳定职责。 | TimePicker 是 AtomUI 桌面控件体系中的时间选择控件，用于在输入壳体和时间面板之间选择时分秒。 |
| 内容承载 | 用户数据、展示内容、集合项或操作入口如何进入控件。 | `IsShowHeader`、`ItemFormat`、`ItemHeight`。 |
| 状态反馈 | public API、内部状态和伪类如何形成用户可感知反馈。 | selection/checked/active。 |
| 主题语义 | ControlTheme、SharedToken、控件 Token 和模板绑定如何表达视觉。 | TimePicker Token + ControlTheme。 |

## 公共 API

TimePicker 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。

核心 public surface 按语义分组维护：

| 契约组 | 代表成员 | 维护含义 |
| --- | --- | --- |
| 内容与数据 | `IsShowHeader`、`ItemFormat`、`ItemHeight` | 定义控件展示内容、输入数据、模板或业务对象入口。 |
| 选择与集合 | `RangeEndSelectedTime`、`RangeStartSelectedTime`、`SelectedTime`、`SelectorRowCount` | 维护选择、展开、过滤、分页、分组或集合状态。 |
| 交互与状态 | `IsNeedConfirm`、`IsShowNow`、`ShouldLoop` | 表达用户可观察状态、可用性、清除、加载或反馈语义。 |
| 其他稳定入口 | `ClockIdentifier`、`DefaultTime`、`MinuteIncrement`、`PanelType`、`PickerDisplayTime`、`RangeEndDefaultTime`、`RangeStartDefaultTime`、`SecondIncrement` | 保留为 public surface，变更前需确认 Gallery 和用户 XAML 依赖。 |

当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。

`SelectedTime` 是 `TimePicker` 的受控 Form 值属性，默认绑定模式为 `TwoWay`，并启用 Avalonia data validation。Form、绑定验证和输入表面错误视觉必须基于 `DataValidationErrors` 投射，不允许另建与 native validation 并行的错误状态。TimePicker/RangeTimePicker 还必须把 `FormStatus` 和显式 `Status` 投射到 shared `InputControlFrame`；TimeView 只拥有时间面板和选择交互状态。

主要公开类型与枚举：

- 类型：`CellDbClickedEventArgs`、`CellHoverEventArgs`、`DateTimePickerPanel`、`RangeTimePicker`、`TimePicker`、`TimePickerPresenter`、`TimeSelectedEventArgs`、`TimeView`、`TimeViewCell`。
- 枚举：`ClockIdentifierType`。
- 本地化 Catalog：`TimePickerLangResourceKind`。

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_ButtonsFrame` | `?` | 承载用户触发入口、导航或关闭动作。 |
| `PART_ButtonsLayout` | `?` | 承载用户触发入口、导航或关闭动作。 |
| `PART_ConfirmButton` | `?` | 承载用户触发入口、导航或关闭动作。 |
| `PART_ContentPresenter` | `?` | 展示用户内容、文本、图标或模板化数据。 |
| `PART_FirstSpacer` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_HeaderText` | `?` | 承载文本输入、过滤、显示或编辑入口。 |
| `PART_HourHost` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_HourSelector` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_MainFrame` | `?` | 承载根视觉、边框、背景或尺寸基线。 |
| `PART_MainLayout` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_MinuteHost` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_MinuteSelector` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_NowButton` | `?` | 承载用户触发入口、导航或关闭动作。 |
| `PART_PeriodHost` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_PeriodSelector` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_PickerContainer` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_RootLayout` | `?` | 承载根视觉、边框、背景或尺寸基线。 |
| `PART_SecondHost` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_SecondSelector` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_SecondSpacer` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_ThirdSpacer` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_TimeView` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |

当前未抽取到控件专属伪类；主题主要依赖 Avalonia 标准伪类、模板绑定和内部 StyledProperty。

## 事件与命令

TimePicker 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。
当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。
- 类型：`CellDbClickedEventArgs`、`CellHoverEventArgs`、`DateTimePickerPanel`、`RangeTimePicker`、`TimePicker`、`TimePickerPresenter`、`TimeSelectedEventArgs`、`TimeView`、`TimeViewCell`。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/TimePicker/Views/TimePickerShowCase.axaml:35`

Gallery key：`ExamplesContent` / item `0`

```axaml
<atom:TimePicker PlaceholderText="选择时间" IsNeedConfirm="False" IsShowNow="True" />
```

### SelectedTime 绑定

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/TimePicker/Views/TimePickerShowCase.axaml:47`

Gallery key：`ExamplesContent` / item `1`

```axaml
<StackPanel Orientation="Vertical" Spacing="16">
    <StackPanel Orientation="Vertical" Spacing="12">
        <atom:TimePicker SelectedTime="{Binding BoundSelectedTime}"
                         PlaceholderText="选择时间" />
        <StackPanel Orientation="Horizontal" Spacing="8">
            <atom:TextBlock VerticalAlignment="Center"
                            Text="选中值：" />
            <atom:TextBlock VerticalAlignment="Center"
                            Text="{Binding BoundSelectedTimeText}" />
        </StackPanel>
        <StackPanel Orientation="Horizontal" Spacing="8">
            <atom:Button SizeType="Small"
                         Click="SetBoundSelectedTimeToNoon"
                         Content="设置为中午" />
            <atom:Button SizeType="Small"
                         Click="ClearBoundSelectedTime"
                         Content="清空" />
        </StackPanel>
    </StackPanel>
    <StackPanel Orientation="Vertical" Spacing="12">
        <atom:RangeTimePicker RangeStartSelectedTime="{Binding BoundRangeStartSelectedTime}"
                              RangeEndSelectedTime="{Binding BoundRangeEndSelectedTime}"
                              PlaceholderText="开始时间"
                              SecondaryPlaceholderText="结束时间" />
        <StackPanel Orientation="Horizontal" Spacing="8">
            <atom:TextBlock VerticalAlignment="Center"
                            Text="选中范围：" />
            <atom:TextBlock VerticalAlignment="Center"
                            Text="{Binding BoundRangeSelectedTimeText}" />
        </StackPanel>
        <StackPanel Orientation="Horizontal" Spacing="8">
            <atom:Button SizeType="Small"
                         Click="SetBoundSelectedTimeRangeToWorkHours"
                         Content="设置工作时间" />
            <atom:Button SizeType="Small"
                         Click="ClearBoundSelectedTimeRange"
                         Content="清空" />
        </StackPanel>
    </StackPanel>
</StackPanel>
```

### 弹出面板显示时间

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/TimePicker/Views/TimePickerShowCase.axaml:98`

Gallery key：`ExamplesContent` / item `2`

```axaml
<atom:TimePicker PlaceholderText="选择时间"
```

### 12 小时和 24 小时格式

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/TimePicker/Views/TimePickerShowCase.axaml:111`

Gallery key：`ExamplesContent` / item `3`

```axaml
<atom:TimePicker PlaceholderText="选择时间" IsNeedConfirm="True" IsShowNow="True"
```

## 状态模型

TimePicker 的状态流按以下路径收敛：

```text
Public API / inherited command / item source / user input
  -> 控件实例状态
  -> effective state / pseudo-class / template property
  -> ControlTheme selector / presenter / renderer
  -> Gallery 可观察行为
```

状态维护规则：

- Disabled 或不可交互状态优先屏蔽 pointer、keyboard、motion 和提交类反馈。
- selection/checked/active 状态由控件实例或明确的数据 owner 推导，不能在 template part 之间双向竞争。
- `SelectedTime` 是单时间选择的唯一用户值 owner；外部绑定、Form set/get、清除和弹层提交都必须收敛到该属性。
- `PickerDisplayTime` 只定义弹出面板打开时的显示锚点；它不得写入 `SelectedTime`，也不得改变 `DefaultTime` 的 reset 语义。
- 模板重套用时必须把 public API 对应状态回放到新的 part、伪类和主题变量。
- 集合、弹层、异步、动效或窗口相关状态必须能处理 reset、close、cancel、detach 和 owner 释放。

## 主题与 Design Token

TimePicker 的视觉模型由 `InputControlFrame` 输入表面、InfoPicker 输入子控件、控件模板、ControlTheme、SharedToken 和必要的控件 Token 共同构成。输入表面状态由 shared frame 统一表达，TimePicker 主题只扩展时间面板、范围和弹层内容。

| 主题文件 | 职责 |
| --- | --- |
| `RangeTimePickerTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `InfoPickerTextBoxTheme.axaml` | 通过 `StyleVariant=Borderless` 提供内部时间文本输入的无 chrome 布局。 |
| `InputControlFrameTheme.axaml` | 提供输入表面 variant、effective status、focus、disabled、error、warning、CompactSpace 和 motion。 |
| `TimePickerPresenterTheme.axaml` | 定义布局、内容承载或框架节点视觉。 |
| `TimePickerTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `TimeViewCellTheme.axaml` | 定义集合项、容器项或局部单元的状态视觉。 |
| `TimeViewTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |

TimePicker 使用 `TimePickerToken` 作为控件 Token scope。Token 只表达组件视觉语义，不承载 selection/checked/active 运行时状态。

主题维护规则：

- 不删除或重命名已经稳定的 ControlTheme key、template part、伪类和资源 key。
- 不把可由 AXAML 表达的模板状态迁移为 C# 动态创建视觉。
- 不把 hover、pressed、selected、expanded、loading、filter、popup open 等运行时状态写入 Token。
- Browser 或平台特化主题必须保持同一 API 的语义一致。

Token 来源：

TimePicker Token 只表达组件级视觉变量，例如尺寸、间距、颜色、圆角、阴影、图标尺寸和弹层边界。Token 不承载运行时选择、展开、加载、错误、上传任务、过滤条件或业务状态。

当前 Token scope：

- `TimePickerToken`，scope id 为 `TimePicker`，源码位于 `src/AtomUI.Desktop.Controls/TimePicker/TimePickerToken.cs`。

## AOT 与裁剪注意事项

资源和 AOT 约束：

- 不通过运行时反射扫描 public API、Token 或 Gallery 示例数据。
- 不把可静态声明的模板结构迁移到 C# 动态创建。
- 异步加载、上传、弹层和窗口生命周期必须能取消或释放。
- 缓存对象必须与控件、窗口、弹层或数据 owner 生命周期一致。
- Source generator 生成文件不手工编辑；需要修改时改输入源或 generator。

性能边界：

- 控件应优先复用 Avalonia 原生虚拟化、模板绑定和资源系统。
- 避免为每次状态变化创建不必要的视觉对象、订阅或动画对象。
- 大集合控件必须保证 container recycle 后不会泄漏旧 item 状态。

## 源码索引

主要源码文件：

- `src/AtomUI.Desktop.Controls/TimePicker/Localization/TimePickerLangResourceKind.cs`
- `src/AtomUI.Desktop.Controls/TimePicker/Localization/en-US.xlf`
- `src/AtomUI.Desktop.Controls/TimePicker/Localization/zh-CN.xlf`
- `src/AtomUI.Desktop.Controls/TimePicker/Localization/zh-TW.xlf`
- `src/AtomUI.Desktop.Controls/TimePicker/RangeTimePicker.cs`
- `src/AtomUI.Desktop.Controls/TimePicker/Themes/RangeTimePickerTheme.axaml`
- `src/AtomUI.Desktop.Controls/TimePicker/Themes/TimePickerPresenterTheme.axaml`
- `src/AtomUI.Desktop.Controls/TimePicker/Themes/TimePickerTheme.axaml`
- `src/AtomUI.Desktop.Controls/TimePicker/Themes/TimeViewCellTheme.axaml`
- `src/AtomUI.Desktop.Controls/TimePicker/Themes/TimeViewTheme.axaml`
- `src/AtomUI.Desktop.Controls/TimePicker/TimePicker.cs`
- `src/AtomUI.Desktop.Controls/TimePicker/TimePickerPresenter.cs`
- `src/AtomUI.Desktop.Controls/TimePicker/TimePickerToken.cs`
- `src/AtomUI.Desktop.Controls/TimePicker/TimeView/DateTimePickerPanel.cs`
- `src/AtomUI.Desktop.Controls/TimePicker/TimeView/TimeView.cs`
- `src/AtomUI.Desktop.Controls/TimePicker/TimeView/TimeViewCell.cs`
- `src/AtomUI.Desktop.Controls/Primitives/InfoPickerInput/InfoPickerTextBox.cs` 与 `Themes/InfoPickerTextBoxTheme.axaml`：TimePicker 输入框使用的 internal 子控件及其文本 presenter、padding 基础视觉。

职责边界：

- 控件主文件保留 public/protected API、Avalonia 属性注册、事件和主要生命周期入口。
- Theme 文件负责静态视觉结构、template part、selector 和资源绑定。
- Token 文件只提供组件视觉变量，不保存实例状态。
- Gallery 文件只展示用法和示例，不作为运行时逻辑 owner。

## 相关文档

- 源设计文档：`docs/controls/desktop/data-entry/time-picker/overview.md`
- 实现文档：`docs/controls/desktop/data-entry/time-picker/implementation.md`
- Token 文档：`docs/controls/desktop/data-entry/time-picker/token.md`
- 变更记录：`docs/controls/desktop/data-entry/time-picker/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/toggle-switch/index-cn.md

# ToggleSwitch

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

ToggleSwitch 是 AtomUI 桌面数据录入体系中的布尔状态切换控件，用于表达立即生效的开/关选择。它以 Avalonia `ToggleButton` 的选中状态为基础，接入 AtomUI 的尺寸、加载态、文字或图标内容、WaveSpirit、Form 和 Token 体系。

ToggleSwitch 的职责是编辑单个 `bool?` 状态。它不承担多选、单选组、延迟提交、异步命令调度、复杂权限判断或表单校验消息展示职责。

桌面 public 类型 `ToggleSwitch` 继承 `AtomUI.Controls.AbstractToggleSwitch`。`ToggleSwitch` 本体只注册桌面 `ToggleSwitchToken` scope，公共 API、布局、交互和 Form 集成由 `AbstractToggleSwitch` 承载。

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/DataEntry/ToggleSwitch` |
| 状态 | Stable |

## 何时使用

ToggleSwitch 的设计语言来自胶囊轨道、滑动把手和可选内容的组合。

| 维度 | 含义 | 典型表达 |
| --- | --- | --- |
| 状态语义 | 当前值是启用或关闭。 | `IsChecked=True/False/null`。 |
| 输入密度 | 控件在表单、设置面板和工具栏中的尺寸等级。 | `SizeType=Middle/Small/Custom/Large`。 |
| 内容语义 | 开关内部可显示文字或图标。 | `OnContent`、`OffContent`、对应模板。 |
| 操作反馈 | 控件可处于加载、禁用、hover、pressed 和 checked 状态。 | `IsLoading`、`:disabled`、`:pointerover`、`:pressed`、`IsChecked`。 |
| 动效语义 | 切换时把手、内容和背景平滑过渡。 | `IsMotionEnabled`、`IsWaveSpiritEnabled`。 |

`IsLoading=true` 表示开关动作正在处理。此时控件保持当前 `IsChecked` 状态，禁止继续切换，并在把手内显示加载指示。

## 公共 API

ToggleSwitch 的公共契约由 Avalonia `ToggleButton` 状态和 AtomUI 扩展属性组成。

| API | 类型 | 语义 |
| --- | --- | --- |
| `IsChecked` | `bool?` | 当前开关值，继承自 `ToggleButton`，支持三态数据绑定。 |
| `GrooveBackground` | `IBrush?` | 轨道背景，通常由主题 selector 根据 checked、hover、disabled 和 loading 状态设置。 |
| `OnContent` / `OffContent` | `object?` | checked / unchecked 状态下显示的内容。 |
| `OnContentTemplate` / `OffContentTemplate` | `IDataTemplate?` | checked / unchecked 内容模板。 |
| `SizeType` | `CustomizableSizeType` | 尺寸密度，支持 `Middle`、`Small`、`Large` 和 `Custom`。 |
| `IsLoading` | `bool` | 是否显示加载态并禁止切换。 |
| `IsMotionEnabled` | `bool` | 是否启用把手、内容偏移、背景和透明度动效。 |
| `IsWaveSpiritEnabled` | `bool` | checked 状态变化时是否播放 wave spirit。 |

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_WaveSpirit` | `WaveSpiritDecorator` | checked 变化时播放胶囊波纹。 |
| `PART_MainContainer` | `Canvas` | 承载内容 presenter 和把手，并提供裁剪边界。 |
| `PART_OnContentPresenter` | `ContentPresenter` | checked 状态下内容承载。 |
| `PART_OffContentPresenter` | `ContentPresenter` | unchecked 状态下内容承载。 |
| `PART_SwitchKnob` | `SwitchKnob` | 把手、加载指示和把手动画承载。 |

公共接口契约：

| 接口 | 语义 |
| --- | --- |
| `ICustomizableSizeTypeAware` | 接入支持 `Custom` 的尺寸模型。 |
| `ICustomHitTest` | 只在有效轨道区域命中，loading 和 disabled 时不可命中。 |
| `IWaveSpiritAwareControl` | 接入 WaveSpirit 开关。 |
| `IFormItemAware` | Form 以 `IsChecked` 作为表单值。 |

## 事件与命令

ToggleSwitch 的事件与命令以控件文档、源码 public surface 和 Avalonia 基类契约为准；生成器不从源码发明额外事件。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/ToggleSwitch/Views/ToggleSwitchShowCase.axaml:37`

Gallery key：`ExamplesContent` / item `0`

```axaml
<StackPanel HorizontalAlignment="Left" Spacing="10" Orientation="Horizontal">
    <atom:ToggleSwitch />
</StackPanel>
```

### 禁用

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/ToggleSwitch/Views/ToggleSwitchShowCase.axaml:50`

Gallery key：`ExamplesContent` / item `1`

```axaml
<StackPanel HorizontalAlignment="Left" Spacing="10" Orientation="Vertical">
    <atom:ToggleSwitch x:Name="ToggleDisabledSwitch"
                       IsEnabled="{Binding IsDisabledDemoEnabled}" />
    <atom:Button ButtonType="Primary"
                 Click="HandleToggleDisabledButtonClick"
                 Content="切换禁用" />
</StackPanel>
```

### 尺寸

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/ToggleSwitch/Views/ToggleSwitchShowCase.axaml:101`

Gallery key：`ExamplesContent` / item `3`

```axaml
<StackPanel HorizontalAlignment="Left" Spacing="10" Orientation="Vertical">
    <atom:ToggleSwitch />
    <atom:ToggleSwitch SizeType="Small" />
    <atom:ToggleSwitch Name="CustomSizeTypeToggleSwitch"
                       SizeType="Custom"
                       IsChecked="True"
                       OnContent="自定义"
                       OffContent="自定义" />
</StackPanel>
```

### 加载中

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/ToggleSwitch/Views/ToggleSwitchShowCase.axaml:120`

Gallery key：`ExamplesContent` / item `4`

```axaml
<StackPanel HorizontalAlignment="Left" Spacing="10" Orientation="Vertical">
    <atom:ToggleSwitch IsLoading="{Binding IsLoadingDemoLoading}" IsChecked="true" x:Name="ToggleSwitchDefault" />
    <atom:ToggleSwitch SizeType="Small" IsLoading="{Binding IsLoadingDemoLoading}" x:Name="ToggleSwitchSmall" />
    <atom:Button ButtonType="Primary"
                 Click="HandleToggleLoadingButtonClick"
                 Content="切换加载" />
</StackPanel>
```

## 状态模型

ToggleSwitch 的交互优先级：

```text
Disabled
> Loading
> Pressed
> PointerOver
> Checked / Unchecked
> Normal
```

`Disabled` 表示控件不可交互，主题降低整体透明度，把手保持当前状态。`Loading` 表示业务处理中，控件命中测试返回 false，pointer press/release 不进入基类切换逻辑，并把 cursor 设置为默认箭头。

checked 状态流：

```text
Pointer / keyboard / binding / Form.SetValue
      ↓
IsChecked
      ↓
CalculateElementsOffset
      ↓
KnobRect + content offset + GrooveBackground selector
```

按下状态会临时拉伸把手宽度，并同步调整当前可见内容偏移。释放后重新计算标准把手和内容位置。

Form 集成以 `IsChecked` 作为值。`ClearFormValue()` 会把 `IsChecked` 设置为 `null`，因此绑定方必须明确是否允许三态。

## 主题与 Design Token

ToggleSwitch 使用控件本体绘制轨道，模板负责内容和把手。

| 主题 | 职责 |
| --- | --- |
| `ToggleSwitchTheme.axaml` | 主模板、状态 selector、SizeType 分支、轨道背景、WaveSpirit 和 SwitchKnob token 传递。 |
| `SwitchKnobTheme.axaml` | 把手加载透明度、加载动画周期和把手宽度动效。 |

视觉状态主要由主题 selector 表达：

- `IsChecked=True` 使用 `SwitchColor`，hover 使用 `ColorPrimaryHover`。
- `IsChecked=False` 使用 SharedToken 的文本弱化色，hover 使用更强弱化色。
- `IsLoading=True` 和 disabled 使用 `SwitchDisabledOpacity`。
- `SizeType=Small` 使用 small token 组。
- `SizeType=Middle`、`SizeType=Custom` 和 `SizeType=Large` 共享普通 token 组。

`Custom` 不是独立 Token 组；未显式覆盖时与 `Middle` 保持同一视觉基线。

Token 来源：

ToggleSwitchToken 是 ToggleSwitch 的控件级 Token scope，描述轨道尺寸、把手尺寸、内容边距、图标尺寸、开关颜色、禁用透明度、内容字体、把手阴影和加载指示。

ToggleSwitchToken 不承载以下状态：

- `IsChecked`、`IsLoading`、`IsPressed`、`IsPointerOver`、`IsEnabled` 等实例状态。
- `OnContent`、`OffContent` 或内容模板。
- Form value、校验状态或业务异步状态。
- 当前把手位置、内容偏移、加载旋转角度或动画运行状态。

## AOT 与裁剪注意事项

ToggleSwitch 不通过反射访问模板结构。模板结构由稳定 part 和 AXAML `TemplateBinding` 表达。

资源与生命周期边界：

- on/off 图标内容的 relay binding 必须在内容替换时释放。
- `IsLoading` 设置的 cursor local value 必须在退出 loading 时 dispose。
- `SwitchKnob` 的 loading `CancellationTokenSource` 必须在停止 loading 和 detach 时释放。
- 隐藏祖先下的 loading animation 不得继续推进 animation clock。
- Token 只表达尺寸、颜色、阴影、字体和加载动画周期，不承载 `IsChecked`、`IsLoading` 或内容实例状态。

当前图标内容绑定使用 C# relay binding，因为目标对象来自用户提供的 runtime content，不是稳定模板 part。模板内部固定关系应继续优先使用 AXAML binding 和 selector。

AOT 边界：

- Token 类型通过 generator 显式注册。
- API 与 Token 契约由控件文档、源码 public surface、Token 类型或生成数据维护。
- 文档中列出的 part 名称必须与 AXAML 和 C# 查找代码一致。

## 源码索引

主要源码：

- `src/AtomUI.Desktop.Controls/Switch/ToggleSwitch.cs`：public 桌面控件，注册 `ToggleSwitchToken` resource scope。
- `src/AtomUI.Controls/Switch/AbstractToggleSwitch.cs`：公共 API、测量、布局、内容绑定、loading、hit test、Form 和渲染主逻辑。
- `src/AtomUI.Controls/Switch/SwitchKnob.cs`：把手绘制、加载指示绘制、把手宽度动画和加载动画生命周期。
- `src/AtomUI.Desktop.Controls/Switch/ToggleSwitchToken.cs`：ToggleSwitch 控件级 Token。
- `src/AtomUI.Desktop.Controls/Switch/Themes/ToggleSwitchTheme.axaml`：主模板、状态 selector、SizeType 分支和 Token 引用。
- `src/AtomUI.Desktop.Controls/Switch/Themes/SwitchKnobTheme.axaml`：把手主题、加载动画周期和动效。

## 相关文档

- 源设计文档：`docs/controls/desktop/data-entry/toggle-switch/overview.md`
- 实现文档：`docs/controls/desktop/data-entry/toggle-switch/implementation.md`
- Token 文档：`docs/controls/desktop/data-entry/toggle-switch/token.md`
- 变更记录：`docs/controls/desktop/data-entry/toggle-switch/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/transfer/index-cn.md

# Transfer

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

Transfer 是 AtomUI 桌面控件体系中的穿梭框控件，用于在源列表和目标列表之间移动、搜索和选择数据项。

Transfer 不负责表格编辑器、树形穿梭或远程数据同步协议。这些职责应由业务层、组合控件或更专用的 AtomUI 控件承担。

主要源码入口：

- `src/AtomUI.Desktop.Controls/Transfer`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/DataEntry/Transfer` |
| 状态 | Stable |

## 何时使用

Transfer 的设计语言围绕控件职责、可观察状态和主题契约组织，而不是围绕模板节点组织。

| 维度 | 含义 | Transfer 中的表达 |
| --- | --- | --- |
| 产品语义 | 控件在界面中承担的稳定职责。 | Transfer 是 AtomUI 桌面控件体系中的穿梭框控件，用于在源列表和目标列表之间移动、搜索和选择数据项。 |
| 内容承载 | 用户数据、展示内容、集合项或操作入口如何进入控件。 | `Content`、`ContentTemplate`、`FilterPlaceholderText`、`FilterValueSelector`、`FooterTemplate`、`ItemTemplate`、`SelectionsIcon`、`SelectionsIconTemplate` 等 22 项。 |
| 状态反馈 | public API、内部状态和伪类如何形成用户可感知反馈。 | selection/checked/active、collection/filter、input/value、motion、visual option。 |
| 主题语义 | ControlTheme、SharedToken、控件 Token 和模板绑定如何表达视觉。 | Transfer Token + ControlTheme。 |

## 公共 API

Transfer 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。

核心 public surface 按语义分组维护：

| 契约组 | 代表成员 | 维护含义 |
| --- | --- | --- |
| 内容与数据 | `Content`、`ContentTemplate`、`FilterPlaceholderText`、`FilterValueSelector`、`FooterTemplate`、`ItemTemplate`、`SelectionsIcon`、`SelectionsIconTemplate`、`SourceTitle`、`SourceTitleTemplate` 等 22 项 | 定义控件展示内容、输入数据、模板或业务对象入口。 |
| 选择与集合 | `TargetKeys`、`SelectedKeys`、`Filter`、`IsAllSelected`、`IsFilterEnabled`、`PageSize` | 维护目标集合、当前面板选择、过滤、分页和集合状态。 |
| 交互与状态 | `IsMasked`、`IsMotionEnabled`、`IsOneWay`、`IsPaginationEnabled`、`IsShowSearch`、`IsShowSelectAll`、`IsShowSelectAllCheckbox`、`IsShowSelectDropdownMenu`、`IsStretchView`、`Status` | 表达用户可观察状态、可用性、清除、加载或反馈语义。 |
| 视觉与布局 | `ListHeight`、`ListWidth`、`SizeType` | 影响尺寸、位置、颜色、形状、密度和模板视觉变量。 |
| 其他稳定入口 | `Footer`、`TargetView`、`TargetViewFooter`、`ViewType` | 保留为 public surface，变更前需确认 Gallery 和用户 XAML 依赖。 |

`TargetKeys` 表示已经移动到目标面板的条目 key，是 Transfer 的提交值；`SelectedKeys` 表示当前源面板和目标面板内被选中的条目 key。两者默认 `BindingMode.TwoWay`，并支持 `INotifyCollectionChanged` 集合的原地 `Add`、`Remove`、`Replace`、`Move` 和 `Reset`。当绑定集合可写时，Transfer 交互优先原地更新已有集合，避免替换绑定源造成外部状态不同步。

稳定事件包括 `SelectActionRequest`。事件触发顺序属于兼容契约，不能因内部状态重排而改变。

主要公开类型与枚举：

- 类型：`AbstractTransfer`、`ListTransfer`、`TransferItemDecorator`、`TransferItemsRemovedEventArgs`、`TransferListItem`、`TransferListView`、`TransferRemoveItemButton`、`TransferSelectActionEventArgs`、`TransferSelectDropdown`、`TransferSelectionChangedEventArgs`、`TransferTreeView`、`TransferTreeViewItem`、`TransferTreeViewItemHeader`、`TransferViewCreatedEventArgs` 等 18 项。
- 枚举：`TransferDirection`、`TransferSelectAction`、`TransferViewType`。

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_IconPresenter` | `?` | 展示用户内容、文本、图标或模板化数据。 |

当前未抽取到控件专属伪类；主题主要依赖 Avalonia 标准伪类、模板绑定和内部 StyledProperty。

## 事件与命令

Transfer 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。
稳定事件包括 `SelectActionRequest`。事件触发顺序属于兼容契约，不能因内部状态重排而改变。
- 类型：`AbstractTransfer`、`ListTransfer`、`TransferItemDecorator`、`TransferItemsRemovedEventArgs`、`TransferListItem`、`TransferListView`、`TransferRemoveItemButton`、`TransferSelectActionEventArgs`、`TransferSelectDropdown`、`TransferSelectionChangedEventArgs`、`TransferTreeView`、`TransferTreeViewItem`、`TransferTreeViewItemHeader`、`TransferViewCreatedEventArgs` 等 18 项。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/Transfer/Views/TransferShowCase.axaml:37`

Gallery key：`ExamplesContent` / item `0`

```axaml
<atom:ListTransfer Name="BasicListTransfer"
```

### 单向模式

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/Transfer/Views/TransferShowCase.axaml:51`

Gallery key：`ExamplesContent` / item `1`

```axaml
<StackPanel Spacing="20">
    <atom:ListTransfer Name="OneWayTransferList"
                       SourceTitle="源列表"
                       TargetTitle="目标列表"
                       IsOneWay="True"
                       IsEnabled="{Binding OneWayTransferEnabled}"
                       ItemsSource="{Binding OneWayTransferItems}" />
    <atom:ToggleSwitch IsChecked="{Binding OneWayTransferEnabled, Mode=TwoWay}"
                       OnContent="禁用"
                       OffContent="启用" />
</StackPanel>
```

### 搜索

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/Transfer/Views/TransferShowCase.axaml:72`

Gallery key：`ExamplesContent` / item `2`

```axaml
<StackPanel Spacing="20">
    <atom:ListTransfer Name="SearchTransferList"
                       IsFilterEnabled="True"
                       FilterPlaceholderText="在此搜索"
                       FilterValueSelector="{Binding TransferFilterValueSelector}"
                       ItemsSource="{Binding SearchTransferItems}" />
</StackPanel>
```

### 受控 key

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/Transfer/Views/TransferShowCase.axaml:90`

Gallery key：`ExamplesContent` / item `3`

```axaml
<StackPanel Spacing="20">
    <atom:ListTransfer Name="ControlledTransferList"
                       SourceTitle="源列表"
                       TargetTitle="目标列表"
                       ItemsSource="{Binding ControlledTransferItems}"
                       TargetKeys="{Binding ControlledTransferTargetKeys}"
                       SelectedKeys="{Binding ControlledTransferSelectedKeys}" />
    <StackPanel Orientation="Horizontal" Spacing="8">
        <atom:Button SizeType="Small"
                     Click="AddControlledTransferTargetKey"
                     Content="添加 key 3 到目标" />
        <atom:Button SizeType="Small"
                     Click="ClearControlledTransferTargetKeys"
                     Content="清空目标 key" />
        <atom:Button SizeType="Small"
                     Click="SelectControlledTransferSourceKey"
                     Content="选中 key 4" />
    </StackPanel>
    <StackPanel Orientation="Horizontal" Spacing="8">
        <atom:TextBlock Text="目标 key：" />
        <atom:TextBlock Text="{Binding ControlledTransferTargetKeys.Count}" />
        <atom:TextBlock Text="选中 key：" />
        <atom:TextBlock Text="{Binding ControlledTransferSelectedKeys.Count}" />
    </StackPanel>
</StackPanel>
```

## 状态模型

Transfer 的状态流按以下路径收敛：

```text
Public API / inherited command / item source / user input
  -> 控件实例状态
  -> effective state / pseudo-class / template property
  -> ControlTheme selector / presenter / renderer
  -> Gallery 可观察行为
```

状态维护规则：

- Disabled 或不可交互状态优先屏蔽 pointer、keyboard、motion 和提交类反馈。
- selection/checked/active、collection/filter、input/value、motion、visual option 状态由控件实例或明确的数据 owner 推导，不能在 template part 之间双向竞争。
- `TargetKeys` 是目标集合的 public owner，源/目标面板数据由 `ItemsSource` 与 `TargetKeys` 推导；`SelectedKeys` 是当前选择的 public owner，内部源面板选择和目标面板选择按 key 是否存在于 `TargetKeys` 自动拆分。
- 模板重套用时必须把 public API 对应状态回放到新的 part、伪类和主题变量。
- 集合、弹层、异步、动效或窗口相关状态必须能处理 reset、close、cancel、detach 和 owner 释放。

## 主题与 Design Token

Transfer 的视觉模型由控件模板、ControlTheme、SharedToken 和必要的控件 Token 共同构成。

| 主题文件 | 职责 |
| --- | --- |
| `AbstractTransferTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `ListTransferTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `TransferItemDecoratorTheme.axaml` | 定义集合项、容器项或局部单元的状态视觉。 |
| `TransferListItemTheme.axaml` | 定义集合项、容器项或局部单元的状态视觉。 |
| `TransferListViewTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `TransferSelectDropdownTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `TransferTreeViewItemHeaderTheme.axaml` | 定义集合项、容器项或局部单元的状态视觉。 |
| `TransferTreeViewItemTheme.axaml` | 定义集合项、容器项或局部单元的状态视觉。 |
| `TransferTreeViewTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `TreeTransferTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |

Transfer 使用 `TransferToken` 作为控件 Token scope。Token 只表达组件视觉语义，不承载 selection/checked/active、collection/filter、input/value、motion、visual option 运行时状态。

主题维护规则：

- 不删除或重命名已经稳定的 ControlTheme key、template part、伪类和资源 key。
- 不把可由 AXAML 表达的模板状态迁移为 C# 动态创建视觉。
- 不把 hover、pressed、selected、expanded、loading、filter、popup open 等运行时状态写入 Token。
- Browser 或平台特化主题必须保持同一 API 的语义一致。

Token 来源：

Transfer Token 只表达组件级视觉变量，例如尺寸、间距、颜色、圆角、阴影、图标尺寸和弹层边界。Token 不承载运行时选择、展开、加载、错误、上传任务、过滤条件或业务状态。

当前 Token scope：

- `TransferToken`，scope id 为 `Transfer`，源码位于 `src/AtomUI.Desktop.Controls/Transfer/TransferToken.cs`。

## AOT 与裁剪注意事项

资源和 AOT 约束：

- 不通过运行时反射扫描 public API、Token 或 Gallery 示例数据。
- 不把可静态声明的模板结构迁移到 C# 动态创建。
- 异步加载、上传、弹层和窗口生命周期必须能取消或释放。
- 缓存对象必须与控件、窗口、弹层或数据 owner 生命周期一致。
- Source generator 生成文件不手工编辑；需要修改时改输入源或 generator。

性能边界：

- 控件应优先复用 Avalonia 原生虚拟化、模板绑定和资源系统。
- 避免为每次状态变化创建不必要的视觉对象、订阅或动画对象。
- 大集合控件必须保证 container recycle 后不会泄漏旧 item 状态。

## 源码索引

主要源码文件：

- `src/AtomUI.Desktop.Controls/Transfer`：21 个文件，代表文件 `AbstractTransfer.cs`、`ITransferTreeView.cs`、`ITransferView.cs`、`ListTransfer.cs`、`TransferDirection.cs` 等。
- `src/AtomUI.Desktop.Controls/Transfer/Localization`：`TransferLangResourceKind.cs` 定义稳定 Catalog，`en-US.xlf`、`zh-CN.xlf`、`zh-TW.xlf` 提供内置翻译。
- `src/AtomUI.Desktop.Controls/Transfer/Themes`：12 个文件，代表文件 `AbstractTransferTheme.axaml`、`AbstractTransferTheme.cs`、`ListTransferTheme.axaml`、`TransferItemDecoratorTheme.axaml`、`TransferListItemTheme.axaml` 等。

职责边界：

- 控件主文件保留 public/protected API、Avalonia 属性注册、事件和主要生命周期入口。
- Theme 文件负责静态视觉结构、template part、selector 和资源绑定。
- Token 文件只提供组件视觉变量，不保存实例状态。
- Gallery 文件只展示用法和示例，不作为运行时逻辑 owner。

## 相关文档

- 源设计文档：`docs/controls/desktop/data-entry/transfer/overview.md`
- 实现文档：`docs/controls/desktop/data-entry/transfer/implementation.md`
- Token 文档：`docs/controls/desktop/data-entry/transfer/token.md`
- 变更记录：`docs/controls/desktop/data-entry/transfer/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/tree-select/index-cn.md

# TreeSelect

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

TreeSelect 是 AtomUI 桌面数据录入体系中的树形选择控件，用于在层级数据中完成单选、多选和勾选式选择。它组合 Select 家族的输入壳体、过滤输入、弹层和 Form / CompactSpace 集成能力，并使用 TreeView 家族承载树节点、展开、勾选、图标、连线和异步加载。

TreeSelect 的职责是从 `ITreeItemNode` 层级数据中选择一个或多个节点。它不负责远程数据协议、业务权限过滤、级联业务规则、树节点编辑、拖放排序或复杂数据持久化；这些能力应由业务层、TreeView 或专用数据控件承担。

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/DataEntry/TreeSelect` |
| 状态 | Stable |

## 何时使用

TreeSelect 的设计语言来自输入框、树形候选弹层和已选结果的组合。

| 维度 | 含义 | 典型表达 |
| --- | --- | --- |
| 树形数据 | 候选项按父子层级展示。 | `ItemsSource`、`Items`、`ItemTemplate`。 |
| 选择模式 | 控件支持单选、多选和 checkbox 勾选。 | `IsMultiple`、`IsTreeCheckable`、`IsTreeCheckStrictly`。 |
| 结果展示 | 单选显示节点文本，多选以 tag 展示有效选中节点。 | `SelectedItem`、`SelectedItems`、`ShowCheckedStrategy`。 |
| 搜索过滤 | 输入过滤值后高亮、加粗、展开路径或隐藏不匹配节点。 | `IsFilterEnabled`、`FilterValue`、`Filter`、`FilterStrategy`。 |
| 树视觉 | 控制图标、叶子图标、连线、展开指示和旋转。 | `IsShowIcon`、`IsShowLeafIcon`、`IsShowLine`、`IsShowTreeLine`。 |
| 输入表面 | 继承 Select 家族的边框、尺寸、状态和 AddOn。 | `SizeType`、`StyleVariant`、`Status`、Addon。 |
| 弹层语义 | 候选树在 popup 中打开，可匹配输入宽度。 | `IsDropDownOpen`、`PopupPlacement`、`IsPopupMatchSelectWidth`。 |

`SizeType=Custom` 不是 TreeSelect 的第四套专属 Token。主题层把 `Custom` 归入 `Middle` 的默认字体和 padding 分支；用户显式设置高度等尺寸属性时由 Avalonia 属性优先级决定最终布局。

## 公共 API

TreeSelect 继承 `AbstractSelect` 的输入壳体、弹层、清除、状态、尺寸、Addon、Form 和 CompactSpace 契约；自身提供树数据、树选择、过滤和勾选策略契约。

树数据 API：

| API | 类型 | 语义 |
| --- | --- | --- |
| `ItemsSource` | `IEnumerable<ITreeItemNode>?` | 外部树节点集合。变化时同步到内部 `Items`。 |
| `Items` | `ItemCollection` | XAML 内容子项入口，也是候选树实际数据入口。 |
| `ItemTemplate` | `IDataTemplate?` | 树节点显示模板，默认显示 `ITreeItemNode.Header`。 |
| `DataLoader` | `ITreeItemNodeLoader?` | 展开节点时加载子节点的数据加载边界。 |
| `TreeDefaultExpandedPaths` | `IList<TreeNodePath>?` | 默认展开路径集合。 |
| `IsDefaultExpandAll` | `bool` | 默认展开全部节点。 |

选择 API：

| API | 类型 | 语义 |
| --- | --- | --- |
| `SelectedItem` | `ITreeItemNode?` | 单选模式当前节点；默认 `BindingMode.TwoWay`，启用 Avalonia `DataValidationErrors`。 |
| `SelectedItems` | `IList<ITreeItemNode>?` | 多选或勾选模式当前节点集合；默认 `BindingMode.TwoWay`，启用 Avalonia `DataValidationErrors`，支持 `INotifyCollectionChanged` 原地变更刷新。 |
| `IsMultiple` | `bool` | 是否使用多选结果模型。 |
| `IsTreeCheckable` | `bool` | 是否使用 checkbox 勾选。启用后 TreeSelect 使用多选结果模型。 |
| `IsTreeCheckStrictly` | `bool` | 勾选时父子节点是否严格独立。 |
| `ShowCheckedStrategy` | `TreeSelectCheckedStrategy` | 多选 tag 的展示策略，决定展示全部、父节点或叶子节点。 |
| `MaxCount` | `int` | 继承自 `AbstractSelect` 的最大可选数量。 |
| `Clear()` | method | 清空 `SelectedItem` 和 `SelectedItems`。 |

过滤与树视觉 API：

| API | 类型 | 语义 |
| --- | --- | --- |
| `Filter` | `IValueFilter?` | 树节点过滤器；为空时初始化为 contains 过滤。 |
| `FilterValueSelector` | `DefaultFilterValueSelector?` | 从节点中提取过滤文本。 |
| `FilterStrategy` | `TreeFilterStrategy` | 过滤后的高亮、加粗、展开路径和隐藏不匹配策略。 |
| `FilterHighlightForeground` | `IBrush?` | 过滤命中高亮前景色。 |
| `AutoScrollToSelectedItem` | `bool` | 候选树打开或同步时滚动到选中节点。 |
| `IsShowOverflowTip` | `bool` | 单选结果文本或多选 tag 视觉溢出时是否显示完整内容 tooltip，默认 `true`。 |
| `OverflowTipDelay` | `int` | 溢出 tooltip 打开前的延迟时间，单位毫秒，默认 `1200`。 |
| `OverflowTipPlacement` | `PlacementMode` | 溢出 tooltip 相对单选结果文本或多选 tag 的位置，默认 `TopEdgeAlignedLeft`。 |
| `IsShowIcon` / `IsShowLeafIcon` | `bool` | 是否显示节点图标和叶子节点图标。 |
| `IsShowLine` / `IsShowTreeLine` | `bool` | 是否显示层级连线。 |
| `IsSwitcherRotation` | `bool` | 展开图标是否旋转。 |

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_InputControlFrame` | `TreeSelectAddOnDecoratedBox` | `InputControlFrame` / AddOnDecoratedBox 组合、Addon、EffectiveStatus、CompactSpace 和多选布局承载。 |
| `PART_SingleFilterInput` | `SelectFilterTextBox` | 单选模式结果显示和搜索输入。 |
| `SelectedItemsBox` | `SelectTagAwareTextBox` | 多选和勾选模式已选 tag 展示。 |
| `PART_SelectMaxCountIndicator` | `SelectMaxCountIndicator` | 最大选择数量提示。 |
| `PART_ContentRightAddOnPresenter` | `ContentPresenter` | 用户内部右侧内容承载。 |
| `PART_SelectHandle` | `SelectHandle` | 展开、loading、清除和 Form feedback 图标入口。 |
| `PART_Popup` | `Popup` | 候选弹层宿主。 |
| `PopupFrame` | `Border` | 懒创建的候选弹层外壳。 |
| `PART_TreeView` | `TreeSelectTreeView` | 懒创建的树候选控件。 |

## 事件与命令

TreeSelect 的事件与命令以控件文档、源码 public surface 和 Avalonia 基类契约为准；生成器不从源码发明额外事件。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/TreeSelect/Views/TreeSelectShowCase.axaml:36`

Gallery key：`ExamplesContent` / item `0`

```axaml
<atom:TreeSelect Name="BasicTreeSelect"
```

### 绑定

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/TreeSelect/Views/TreeSelectShowCase.axaml:54`

Gallery key：`ExamplesContent` / item `1`

```axaml
<StackPanel Orientation="Vertical" Spacing="16">
    <StackPanel Spacing="8">
        <TextBlock Text="单选绑定"
                   FontWeight="SemiBold" />
        <atom:TreeSelect Name="BindingSingleTreeSelect"
                         HorizontalAlignment="Stretch"
                         IsDefaultExpandAll="True"
                         IsAllowClear="True"
                         IsFilterEnabled="True"
                         ItemsSource="{Binding BindingSingleTreeNodes}"
                         SelectedItem="{Binding BoundSelectedItem}"
                         PlaceholderText="请选择" />
        <WrapPanel ItemSpacing="8">
            <atom:Button SizeType="Small"
                         Command="{Binding SetBoundSelectedItemCommand}"
                         Content="选择你的叶子" />
            <atom:Button SizeType="Small"
                         Command="{Binding ClearBoundSelectedItemCommand}"
                         Content="清空" />
        </WrapPanel>
        <TextBlock Text="ViewModel 值：" />
        <TextBlock Text="{Binding BoundSelectedItemText}" />
    </StackPanel>

    <StackPanel Spacing="8">
        <TextBlock Text="多选绑定"
                   FontWeight="SemiBold" />
        <atom:TreeSelect Name="BindingMultipleTreeSelect"
                         HorizontalAlignment="Stretch"
                         IsDefaultExpandAll="True"
                         IsAllowClear="True"
                         IsFilterEnabled="True"
                         IsMultiple="True"
                         ItemsSource="{Binding BindingMultipleTreeNodes}"
                         SelectedItems="{Binding BoundSelectedItems}"
                         PlaceholderText="请选择" />
        <WrapPanel ItemSpacing="8">
            <atom:Button SizeType="Small"
                         Command="{Binding SetBoundSelectedItemsCommand}"
                         Content="选择两个节点" />
            <atom:Button SizeType="Small"
                         Command="{Binding ClearBoundSelectedItemsCommand}"
                         Content="清空" />
        </WrapPanel>
        <TextBlock Text="ViewModel 值：" />
        <TextBlock Text="{Binding BoundSelectedItemsText}"
                   TextWrapping="Wrap" />
    </StackPanel>
</StackPanel>
```

### 多选

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/TreeSelect/Views/TreeSelectShowCase.axaml:112`

Gallery key：`ExamplesContent` / item `2`

```axaml
<atom:TreeSelect Name="MultiSelectionTreeSelect"
```

### 由树数据生成

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/TreeSelect/Views/TreeSelectShowCase.axaml:129`

Gallery key：`ExamplesContent` / item `3`

```axaml
<atom:TreeSelect Name="ItemsSourceTreeSelect"
```

## 状态模型

TreeSelect 的核心状态流：

```text
ItemsSource / Items
      ↓
TreeSelectTreeView
      ↓
SelectedItem / SelectedItems
      ↓
SingleFilterInput / SelectedItemsBox
      ↓
Form value + MaxCount state
```

选择模式语义：

- 单选模式使用 `SelectedItem` 作为表单值，候选节点点击后关闭弹层。
- 多选模式使用 `SelectedItems` 作为表单值，候选树使用多选 selection，并通过 tag 展示结果。
- `IsTreeCheckable=true` 使用 checkbox 作为节点切换入口，TreeView selection 不再作为主要选择入口。
- `ShowCheckedStrategy` 只影响多选 tag 展示集合，不改变 `SelectedItems` 的真实值。
- `SelectedItems` 是用户拥有的受控集合。集合引用替换和 `ObservableCollection` 等 `INotifyCollectionChanged` 原地 `Add`、`Remove`、`Reset` 都必须刷新 tag、`SelectedCount`、Form value changed、候选树 selection / checked items 和最大选择数状态。

过滤行为：

- `FilterValue` 来自单选搜索输入。
- `FilterStrategy` 控制命中高亮、加粗、展开路径和隐藏不匹配节点。
- 弹层关闭后，单选模式清空过滤值并恢复结果显示。

清除行为通过 `SelectHandle.ClearRequestedEvent` 冒泡到 TreeSelect，并调用 `Clear()` 清空当前选择。

## 主题与 Design Token

TreeSelect 的默认视觉由 TreeSelect 专属主题、`InputControlFrame` / Select 家族 AddOnDecoratedBox、TreeView 和 PopupHost 协作完成。

| 主题或资源 | 职责 |
| --- | --- |
| `TreeSelectTheme.axaml` | 根模板、输入壳体、单选输入、多选结果区域、handle、popup 和基础 selector。 |
| `SelectTagAwareTextBox` / `SelectTag` 主题 | 多选 tag 布局、高度、关闭按钮和禁用态。 |
| `TreeView` / `TreeViewItem` 主题 | 树节点缩进、展开、勾选、图标、连线和过滤视觉。 |
| `InputControlFrameTheme` / `SharedToken` | 输入壳体边框、圆角、effective status、focus 和 CompactSpace 视觉。 |
| `PopupHostToken` | popup 阴影、圆角和 anchor margin。 |
| `TreeSelectToken` | TreeSelect 候选弹层最小宽度。 |
| `SelectToken` | TreeSelect 复用的 popup padding、多选 tag 和输入内容 padding。 |

单选结果文本和多选 tag 的完整内容提示复用共享 `OverflowTip` attached behavior。主题通过 `IsShowOverflowTip`、`OverflowTipDelay` 和 `OverflowTipPlacement` 控制提示开关、延迟和位置，实际 tooltip 仅在文本视觉溢出时托管到 `ToolTip`。

右侧 count、content add-on 和 handle 的稳定 template part 状态由 AXAML compiled ancestor binding 表达。C# 中只保留 frame layout part 到 SelectHandle 的 hover / pressed sibling 状态转发，因为该关系不是 templated parent 绑定，不能用 `TemplateBinding` 表达；该转发不改变 `InputControlFrame` 对输入表面状态的唯一 ownership。

Token 来源：

TreeSelectToken 是 TreeSelect 的控件级 Token scope，目前只承载 TreeSelect 候选弹层的最小宽度下限。输入表面的通用边框、圆角、effective status、focus ring、disabled 背景和 AddOn 结构来自 `InputControlFrameTheme`、SharedToken 和 PopupHostToken；多选 tag、popup padding 和输入内容 padding 复用 SelectToken。

TreeSelectToken 不承载以下状态：

- `ItemsSource`、`Items`、`SelectedItem`、`SelectedItems` 等数据状态。
- `FilterValue`、`IsDropDownOpen`、`IsTreeCheckable`、`IsMultiple`、`Status` 等运行状态。
- 树节点 hover、pressed、selected、checked、expanded、disabled 的当前实例状态。
- Popup 当前宽度、高度、offset、placement 或异步加载结果。

## AOT 与裁剪注意事项

TreeSelect 不依赖运行时反射发现模板结构。模板协作通过固定 template part、显式类型、AXAML binding、C# indexer binding 和事件完成。

资源和生命周期边界：

- `_selectHandleInputStateBindings` 每次模板接入前释放旧绑定，仅持有 frame layout part → SelectHandle 的 hover / pressed sibling 状态转发，不承载输入表面状态归一。
- `_treeView` 的事件订阅和 `ItemsSource` 必须在 `ClearPopupContent()` 中释放。
- 懒创建的 `PopupFrame` 和 `TreeSelectTreeView` 必须设置 `TemplatedParent`，并在清理时置空。
- `Items.CollectionChanged` 是控件实例持有自身集合的订阅，生命周期与控件实例一致。
- `_selectedItemsCollectionChangedSource` 只订阅当前 `SelectedItems` 中实现 `INotifyCollectionChanged` 的集合；`SelectedItems` 替换、控件 detach 时必须释放旧订阅。
- TreeView 异步加载由 TreeView 家族处理，TreeSelect 不直接持有异步任务状态。

AOT 边界：

- `TreeSelectToken` 通过 token generator 显式注册，生成 `TreeSelectTokenKind` 和 `TreeSelectTokenResourceExtension`。
- `ItemTemplate` 是 XAML 模板入口，不依赖运行时成员扫描。
- API 与 Token 契约由控件文档、源码 public surface、Token 类型或生成数据维护。

## 源码索引

主要源码：

- `src/AtomUI.Desktop.Controls/TreeSelect/TreeSelect.cs`：public TreeSelect API、生命周期、弹层树懒创建、选择同步、过滤输入、Form 映射和私有算法。
- `src/AtomUI.Desktop.Controls/TreeSelect/TreeSelectAddOnDecoratedBox.cs`：TreeSelect 输入布局扩展，复用 `InputControlFrame` 并承载多选和选择空状态。
- `src/AtomUI.Desktop.Controls/TreeSelect/TreeSelectTreeView.cs`：TreeSelect 候选树，使用 TreeView 样式键并创建专用容器。
- `src/AtomUI.Desktop.Controls/TreeSelect/TreeViewSelectTreeViewItem.cs`：候选树节点容器，承接最大选择数状态。
- `src/AtomUI.Desktop.Controls/TreeSelect/Converters/*`：树节点显示转换辅助。
- `src/AtomUI.Desktop.Controls/TreeSelect/TreeSelectToken.cs`：TreeSelect 控件 Token。
- `src/AtomUI.Desktop.Controls/TreeSelect/Themes/TreeSelectTheme.axaml`：根模板、输入壳体、右侧内容、结果区域、popup 和 selector。
- `src/AtomUI.Desktop.Controls/Tooltip/OverflowTip.cs`：共享溢出 tooltip attached behavior，供单选结果和多选 tag 复用。

## 相关文档

- 源设计文档：`docs/controls/desktop/data-entry/tree-select/overview.md`
- 实现文档：`docs/controls/desktop/data-entry/tree-select/implementation.md`
- Token 文档：`docs/controls/desktop/data-entry/tree-select/token.md`
- 变更记录：`docs/controls/desktop/data-entry/tree-select/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/upload/index-cn.md

# Upload

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

Upload 是 AtomUI 桌面控件体系中的上传协调控件，用于管理文件选择、目录选择、拖拽提交、任务状态、列表展示、Form 值投影和上传操作入口。

Upload 不负责具体网络传输协议、文件存储服务或业务附件模型。这些职责由业务层或 `IFileUploadTransport` 承担。

主要源码入口：

- `src/AtomUI.Desktop.Controls/Upload`
- `src/AtomUI.Controls.Shared/Net`
- `controlgallery/AtomUIGallery/ShowCases/DataEntry/Upload`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/DataEntry/Upload` |
| 状态 | Stable |

## 何时使用

Upload 的设计语言围绕“单一文件状态 owner + 可组合操作入口 + 独立列表视图”组织，而不是围绕某个固定模板节点组织。

| 维度 | 含义 | Upload 中的表达 |
| --- | --- | --- |
| 产品语义 | 控件在界面中承担的稳定职责。 | `Upload` 是上传状态协调器，统一管理文件、队列、选择入口、拖拽入口和列表视图。 |
| 内容承载 | 用户数据、展示内容、集合项或操作入口如何进入控件。 | `Files` 保存真实文件项；`TriggerContent`、`UploadTrigger`、`UploadDropZone` 和 `UploadList` 负责组合展示。 |
| 状态反馈 | public API、内部状态和伪类如何形成用户可感知反馈。 | `UploadFileItem.Status`、`Progress`、`ErrorMessage`、`Result` 是任务状态来源；Form 错误走 `DataValidationErrors`。 |
| 主题语义 | ControlTheme、SharedToken、控件 Token 和模板绑定如何表达视觉。 | Upload Token + ControlTheme；滚动边界在 `UploadList` 内部，并使用 AtomUI 自动隐藏滚动条。 |

## 公共 API

Upload 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。

### 3.1 单一状态 owner

Upload 以 `Files` 作为唯一上传文件状态 owner。触发器、拖拽区和列表只是组合视图或动作入口，不保存第二份任务状态。

- `Upload.Files` 表示所有可观察上传文件。
- `UploadTrigger` 通过 `SourceKind=Files|Directories` 选择文件或目录。
- `UploadDropZone` 独占拖动协商和 Drop 快照，并把输入批次提交给最近的 `Upload`。
- `UploadDefaultDropArea` 只提供默认视觉，不读取拖动数据或维护文件状态。
- `UploadList` 渲染 `Files`，并独立管理滚动区域。
- `PictureCard` / `PictureCircle` 的上传入口通过 append slot 呈现，不作为 `UploadFileItem`。

### 3.2 核心 public surface

| 契约组 | 代表成员 | 维护含义 |
| --- | --- | --- |
| 文件状态 | `Files`、`UploadFileItem` | 唯一文件状态 owner，支持绑定、Form 投影和列表渲染。 |
| 文件选择 | `UploadTrigger`、`UploadSourceKind`、`SelectFilesAsync`、`SelectDirectoriesAsync` | 文件与目录选择是独立动作入口；`IsMultipleEnabled` 统一决定用户是否可提交多个顶层 StorageItem。 |
| 拖拽提交 | `UploadDropZone`、`UploadDirectoryDropMode`、`UploadDragState` | DropZone 负责平台协商和快照，`Upload` 负责统一准入与文件状态。 |
| 用户输入范围 | `IsMultipleEnabled` | 统一限制文件选择、目录选择和 Drop 的顶层 StorageItem 数量，不限制单个目录的文件展开结果或显式程序化批量输入。 |
| 文件准入 | `AllowedFileTypes`、`CountOverflowBehavior`、`AdmissionPolicy` | picker、directory、drop 和 programmatic 输入共享同一准入与数量语义。 |
| 输入结果 | `InputBatchCompleted`、`UploadInputBatchCompletedEventArgs` | 每个输入批次在 UI 线程统一报告接受项、拒绝项以及 Completed、Cancelled 或 Failed 终态。 |
| 文件内容 | `UploadFileInfo`、`IUploadFileSource` | Transport 通过可打开内容源读取文件，不假定本地路径可访问。 |
| 上传队列 | `UploadTransport`、`AutoUpload`、`MaxConcurrentTasks`、`UploadQueue` | 上传调度与视觉控件解耦，生命周期由 `Upload` 统一释放。 |
| 列表展示 | `UploadList`、`ListType`、`ListMaxHeight`、`ListScrollBarVisibility` | 列表内部滚动，触发区保持固定。 |
| 触发入口 | `TriggerContent`、`UploadTrigger`、Picture append slot | 文件/目录触发器由用户布局组合，PictureCard/PictureCircle 通过显示源 append slot 呈现。 |
| 状态反馈 | `SuccessAutoRemoveDelay`、`PendingText`、`FileValueMode` | 成功自动移除、待上传文案和 Form 值投影可配置。 |
| 视觉与动效 | `IsMotionEnabled`、Upload Token | 只表达视觉状态，不保存业务任务状态。 |

输入与准入 API 的默认契约：

| 成员 | 默认值 | 语义 |
| --- | --- | --- |
| `Upload.AllowedFileTypes` | `null` | 不按文件名 pattern 或 MIME 拒绝文件；空集合含义相同。 |
| `Upload.CountOverflowBehavior` | `RejectExcess` | 按候选稳定顺序接受剩余容量内的文件，并拒绝超出项。 |
| `Upload.AdmissionPolicy` | `null` | 不执行额外业务准入；非空策略固定在非 UI 执行上下文调用。 |
| `Upload.MaxCount` | `int.MaxValue` | 输入管线可提交的最大 effective 文件数。 |
| `Upload.IsMultipleEnabled` | `false` | 文件选择器只选择一个文件、目录选择器只选择一个目录，Drop 只处理第一个顶层 StorageItem；单个目录仍可展开多个文件。 |
| `UploadDropZone.IsOpenFileDialogOnClick` | `true` | 主指针点击 DropZone 时按 `SourceKind` 打开选择器。 |
| `UploadDropZone.SourceKind` | `Files` | 点击时打开文件选择器。 |
| `UploadDropZone.IsFileDropEnabled` | `true` | DropZone 参与文件 DragDrop 协商。 |
| `UploadDropZone.DirectoryDropMode` | `Reject` | 外部拖入目录时拒绝目录。 |
| `UploadDropZone.MaxDirectoryDepth` | `32` | 递归目录根深度为 `0`，属性值不得小于 `0`。 |
| `UploadDropZone.MaxEnumeratedItems` | `10000` | 每个顶层目录树最多观察的文件和目录总数，属性值必须大于 `0`。 |
| `UploadDropZone.DragState` | `None` | 只读拖动协商状态。 |
| `UploadDropZone.IsDropProcessing` | `false` | 只读活动 Drop 批次状态。 |

`Upload.EnqueueFilesAsync`、文件选择器、目录选择器和拖动输入分别产生 `Programmatic`、`FilePicker`、`DirectoryPicker` 和 `DragDrop` 批次。每个实际启动的批次只触发一次 `InputBatchCompleted`；事件在输入处理进入终态、剩余批次资源完成释放且接受项已经提交后于 UI 线程触发。正常批次在事件后完成 Task，取消批次在事件后传播 `OperationCanceledException`，失败批次在事件后传播原始异常或清理聚合异常。

`IsMultipleEnabled` 只约束用户输入批次的顶层 StorageItem。值为 `false` 时，文件和目录 picker 都使用 `AllowMultiple=false`，Drop 按快照稳定顺序处理第一个顶层项目，并以 `MultipleSelectionNotAllowed` 拒绝和释放其余项目；第一个项目是目录时，目录内部仍按 `DirectoryDropMode` 展开多个文件。值为 `true` 时，picker 与 Drop 均允许多个顶层项目。`EnqueueFilesAsync(IEnumerable<UploadFileInfo>)` 是显式程序化批量 API，不受该属性限制，但仍受文件准入、`MaxCount` 和 `CountOverflowBehavior` 约束。

`UploadInputBatchStatus` 通过 `Completed`、`Cancelled` 和 `Failed` 区分互斥终态。只有 `Failed` 携带 `UploadInputFailureReason`；取消和批次级失败不再构造虚假的 rejected item。`UploadRejectedItem` 只公开稳定的 `UploadRejectionReason`、业务 rejection code 和业务 message，不公开平台 `Exception`。`UploadAdmissionDecision` 通过 `Accept()` 和 `Reject(...)` 创建，不能构造“接受但携带拒绝信息”的矛盾状态。

`UploadFileInfo` 是不可变输入描述。`Name` 和 `Source` 必须存在；`Path`、`Size`、`ContentType`、`DateCreated` 与 `DateModified` 都允许为空，因为跨进程或受限平台存储项不保证提供本地路径和完整元数据。`IUploadFileSource.OpenReadAsync` 是 transport 读取内容的唯一稳定入口。

### 3.3 公共状态类型

```csharp
public class UploadFileItem : AvaloniaObject
{
    public Guid Id { get; set; }
    public string? Name { get; set; }
    public Uri? Path { get; set; }
    public long Size { get; set; }
    public FileUploadStatus Status { get; set; }
    public double Progress { get; set; }
    public string? ErrorMessage { get; set; }
    public FileUploadResult? Result { get; set; }
    public object? UserData { get; set; }
    public string? PendingText { get; set; }
    public bool IsImageFile { get; set; }
}

public enum UploadSourceKind
{
    Files,
    Directories
}

public enum UploadFileValueMode
{
    AllFiles,
    SuccessfulFiles,
    Results
}
```

### 3.4 推荐用法

```xml
<atom:Upload Files="{Binding Attachments}"
             UploadTransport="{Binding UploadTransport}"
             ListMaxHeight="180"
             ListScrollBarVisibility="Auto"
             SuccessAutoRemoveDelay="0:0:3">
    <atom:Upload.TriggerContent>
        <StackPanel Orientation="Horizontal" Spacing="8">
            <atom:UploadTrigger SourceKind="Files">
                <atom:Button Content="上传文件" />
            </atom:UploadTrigger>
            <atom:UploadTrigger SourceKind="Directories">
                <atom:Button Content="上传文件夹" />
            </atom:UploadTrigger>
        </StackPanel>
    </atom:Upload.TriggerContent>
</atom:Upload>
```

## 事件与命令

Upload 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。
| 输入结果 | `InputBatchCompleted`、`UploadInputBatchCompletedEventArgs` | 每个输入批次在 UI 线程统一报告接受项、拒绝项以及 Completed、Cancelled 或 Failed 终态。 |
`Upload.EnqueueFilesAsync`、文件选择器、目录选择器和拖动输入分别产生 `Programmatic`、`FilePicker`、`DirectoryPicker` 和 `DragDrop` 批次。每个实际启动的批次只触发一次 `InputBatchCompleted`；事件在输入处理进入终态、剩余批次资源完成释放且接受项已经提交后于 UI 线程触发。正常批次在事件后完成 Task，取消批次在事件后传播 `OperationCanceledException`，失败批次在事件后传播原始异常或清理聚合异常。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 拖拽上传

来源：`controlgallery/AtomUIGallery/ShowCases/DataEntry/Upload/Views/UploadShowCase.axaml:183`

Gallery key：`ExamplesContent` / item `5`

```axaml
<atom:Upload Name="DragAndDropUpload"
             IsMultipleEnabled="True"
             UploadTransport="{Binding UploadTransport}"
             UploadTaskFailed="HandleUploadFailed"
             UploadTaskCompleted="HandleUploadCompleted">
    <atom:Upload.TriggerContent>
        <atom:UploadDropZone>
            <atom:UploadDefaultDropArea />
        </atom:UploadDropZone>
    </atom:Upload.TriggerContent>
</atom:Upload>
```

## 状态模型

Upload 的状态流只允许按以下路径收敛：

```text
Public API / UploadTrigger / UploadDropZone
  -> UploadInputPipeline
  -> top-level input limit / directory traversal / file admission
  -> UI count policy / accepted file commit
  -> Files collection
  -> UploadQueue / FileUploadScheduler
  -> UploadFileItem.Status / Progress / Result
  -> UploadList item containers
  -> Gallery 可观察行为
```

状态维护规则：

- `Files` 是唯一文件状态 owner；实现中按文件 id 保存的 accepted `UploadFileInfo` 只承担内容源 lease，不形成第二份可观察任务状态。
- `UploadQueue` 只负责把 `UploadFileItem` 映射到 `FileUploadTask` 并转发调度结果，不直接操作视觉容器。
- `UploadList` 只渲染 `Files`，不得创建、删除或隐藏真实任务状态。
- 文件选择和目录选择由 `UploadTrigger.SourceKind` 决定，可以在同一 `Upload` 下并存。
- `IsMultipleEnabled` 是 picker 与 Drop 共享的顶层输入策略，不得复用为目录展开数量或最终文件容量限制。
- `SuccessAutoRemoveDelay` 的延迟任务必须在 remove、reset、detach 和状态离开 success 时取消。
- Form 值由 `FileValueMode` 投影，错误状态以 Avalonia `DataValidationErrors` 为准。

## 主题与 Design Token

Upload 的视觉模型由控件模板、ControlTheme、SharedToken 和控件 Token 共同构成。

| 主题文件 | 职责 |
| --- | --- |
| `UploadTheme.axaml` | 根模板，连接 `TriggerContent`、list 和 picture display source。 |
| `UploadTriggerTheme.axaml` | 触发器 shell，只承载用户内容和点击动作，不硬编码 Button。 |
| `UploadDropZoneTheme.axaml` | 拖拽行为 shell，只承载用户内容和内容对齐。 |
| `UploadDefaultDropAreaTheme.axaml` | 默认拖动视觉，保留边框、图标、标题、副标题、状态 selector 和动效，不处理拖动数据。 |
| `UploadListTheme.axaml` | 上传列表 shell，内部拥有自动隐藏的 `atom:ScrollViewer` 和滚动边界。 |
| `UploadTextListItemTheme.axaml` | Text 列表项状态视觉。 |
| `UploadTextListItemHeaderTheme.axaml` | Text 列表项头部状态视觉。 |
| `UploadPictureListItemTheme.axaml` | Picture 列表项状态视觉。 |
| `UploadPicturePendingContentTheme.axaml` | Picture pending 内容，优先显示 `UploadFileItem.PendingText`。 |
| `UploadPicturePreviewContentTheme.axaml` | Picture preview 内容。 |
| `UploadPictureUploadingContentTheme.axaml` | Picture uploading 内容。 |
| `UploadPictureDefaultContentTheme.axaml` | Picture fallback 内容。 |
| `UploadPictureShapeListTheme.axaml` | PictureCard/PictureCircle 列表布局。 |
| `UploadPictureShapeListItemTheme.axaml` | PictureCard/PictureCircle 列表项状态视觉。 |
| `UploadPictureShapePendingContentTheme.axaml` | Shape pending 内容，优先显示 `UploadFileItem.PendingText`。 |
| `UploadPictureShapePreviewContentTheme.axaml` | Shape preview 内容。 |
| `UploadPictureShapeUploadingContentTheme.axaml` | Shape uploading 内容。 |
| `UploadPictureShapeDefaultContentTheme.axaml` | Shape fallback 内容。 |

主题维护规则：

- Trigger 不作为文件项渲染，PictureCard/PictureCircle 使用不进入 `Files` 的 display append slot 保持同一 wrap flow。
- Text/Picture 根模板必须在 trigger 与 list 之间保留 Shared spacing，避免触发按钮和第一条文件项贴在一起。
- 滚动区域只包裹列表，不包裹 trigger。
- 可由 AXAML 表达的模板状态必须优先留在 AXAML。
- Token 只表达视觉变量，不承载上传状态、队列状态或 Form 错误。

Token 来源：

Upload Token 只表达组件级视觉变量，例如尺寸、间距、颜色、圆角、阴影、图标尺寸和弹层边界。Token 不承载运行时选择、展开、加载、错误、上传任务、过滤条件或业务状态。

当前 Token scope：

- `UploadToken`，scope id 为 `Upload`，源码位于 `src/AtomUI.Desktop.Controls/Upload/UploadToken.cs`。

## AOT 与裁剪注意事项

资源边界：

- 异步上传任务、取消源、delay、drag/drop 事件、collection change 和 item container 绑定必须有确定释放点。
- DragOver 不读取文件值或元数据；Drop 只物化一次顶层 StorageItem 数据。
- 输入批次通过 arrival gate 串行执行；每个批次只建立一次显式 worker dispatch，目录展开、元数据读取和准入按候选稳定顺序处理，不创建无界并发任务；数量决策与集合变更保持在同一个 UI commit 边界。
- `UploadInputBatchOperation` 不保存泛化 `IDisposable`；StorageItem 和 source lease 使用独立的引用相等 owner set，释放遍历不得因单个异常提前中止。
- `UploadFileItem` 不持有视觉控件，避免文件项生命周期反向保留控件树。
- `UploadQueue` 不捕获 `Upload` 外的视觉对象；回调只写 item 状态。
- scheduler 的取消完成语义必须等待 transport execution task 真正退出，source lease 才能被上层释放。
- scheduler maintenance 必须先把全部 Pending task 转入 Cancelled、向全部 Running task 发出取消，再观察调用方取消令牌；并发 transport/并发度变更按调用顺序生效。
- C# binding 仅用于 AXAML 无法表达的动态关系，并必须挂到明确 owner 上释放。

性能边界：

- 列表滚动由 `UploadList` 内部 `atom:ScrollViewer` 管理，并启用 lite/auto-hide；避免用户外层 `ScrollViewer` 把 trigger、drop-zone 和 list 一起滚动。
- `UploadList` 依赖 ItemsControl container 生命周期，不为每次进度变化重建列表项。
- 大量文件状态更新时只更新对应 `UploadFileItem` 属性，不替换 `Files` 集合。

AOT 边界：

- 不通过运行时反射扫描 public API、Token、API 契约摘要或上传模型。
- 拖动输入使用 typed DataTransfer、StorageItem 和显式策略，不依赖平台私有反射或动态发现。
- 新增 public 类型应显式引用并由源码、Gallery 和测试覆盖。
- Source generator 生成文件不手工编辑；LLMS 产物也不在本次运行时代码任务中手工修改。

## 源码索引

源码结构按“状态 owner、输入入口、输入管线、队列协调、列表视图、主题模板”分层。

| 路径 | 职责 |
| --- | --- |
| `src/AtomUI.Desktop.Controls/Upload/Upload.cs` | 保留 public/protected API、Avalonia 属性注册、构造、display source 桥接、`IFormItemAware` 和顶层上传协调。 |
| `src/AtomUI.Desktop.Controls/Upload/UploadAppendContentItem.cs` | internal picture append visual slot，用于把 `TriggerContent` 放入 PictureCard/PictureCircle 的同一 wrap flow；不进入 `Files`。 |
| `src/AtomUI.Desktop.Controls/Upload/Upload.FileSelection.cs` | 封装文件选择和目录选择动作，并把同一个 `IsMultipleEnabled` 值投射到两种 picker 的 `AllowMultiple`。 |
| `src/AtomUI.Desktop.Controls/Upload/IUploadStorageProviderAdapter.cs` | 隔离 Avalonia storage picker 调用，向文件选择和目录选择提供可测试的 typed StorageItem 边界。 |
| `src/AtomUI.Desktop.Controls/Upload/Upload.InputPipeline.cs` | 连接 `Upload` 状态 owner 与输入管线，负责 UI 线程 option 快照、typed commit、批次事件和 ReplaceExisting source handoff。 |
| `src/AtomUI.Desktop.Controls/Upload/UploadInputPipeline.cs` | 串行执行 picker、directory、drop 和 programmatic 批次，在一次 worker 边界内协调遍历与准入，再把数量策略和集合变更交给 UI commit。 |
| `src/AtomUI.Desktop.Controls/Upload/UploadInputBatchOperation.cs` | 分别持有单个批次的 typed StorageItem 与 source lease ownership，验证 transfer，并在完成事件前释放全部未移交资源。 |
| `src/AtomUI.Desktop.Controls/Upload/UploadStorageItemEnumerator.cs` | 在目录展开前应用顶层多选限制，再仅使用 `IStorageFolder.GetItemsAsync()` 按稳定顺序展开 storage items，并隔离目录分支错误。 |
| `src/AtomUI.Desktop.Controls/Upload/UploadInputCandidate.cs` | 非 owning 地引用批次已拥有的 storage file，并提供基础元数据读取入口。 |
| `src/AtomUI.Desktop.Controls/Upload/UploadFileAdmissionService.cs` | 在非 UI 执行上下文执行 `AllowedFileTypes` 和 `AdmissionPolicy`，生成明确的接受或拒绝结果。 |
| `src/AtomUI.Desktop.Controls/Upload/UploadStorageFileSource.cs` | 以 `IStorageFile` lease 实现 `IUploadFileSource`，允许 transport 在 lease 有效期内打开读取流。 |
| `src/AtomUI.Desktop.Controls/Upload/Upload.AutoRemove.cs` | 管理成功自动移除的延迟任务、取消和生命周期释放。 |
| `src/AtomUI.Desktop.Controls/Upload/UploadFileItem.cs` | public 文件状态模型，承载文件元数据、状态、进度、错误、结果和 pending 文案。 |
| `src/AtomUI.Desktop.Controls/Upload/UploadSourceKind.cs` | public 触发来源枚举，区分文件选择和目录选择。 |
| `src/AtomUI.Desktop.Controls/Upload/UploadFileValueMode.cs` | public Form 值投影枚举，控制提交全部文件、成功文件或上传结果。 |
| `src/AtomUI.Desktop.Controls/Upload/UploadTrigger.cs` | public 可组合触发器，查找最近的 `Upload` 并触发文件或目录选择。 |
| `src/AtomUI.Desktop.Controls/Upload/UploadDropZone.cs` | public 可组合拖拽区，拥有 DragDrop 协商、状态和 Drop 快照。 |
| `src/AtomUI.Desktop.Controls/Upload/UploadDefaultDropArea.cs` | public 默认拖动视觉，只保存图标、标题、副标题和动效属性。 |
| `src/AtomUI.Desktop.Controls/Upload/UploadQueue.cs` | internal 上传队列协调器，映射 `UploadFileItem` 与 `FileUploadTask`。 |
| `src/AtomUI.Desktop.Controls/Upload/UploadList.cs` | 文件列表视图，渲染 effective 文件视图并维护列表滚动。 |
| `src/AtomUI.Desktop.Controls/Upload/AbstractUploadListItem.cs` | 列表项基类，只绑定 `UploadFileItem` 并发出 item action，不拥有任务状态。 |
| `src/AtomUI.Controls.Shared/Net/FileUploadScheduler.cs` | 共享上传调度器，跟踪 pending/running 任务、并发和取消。 |
| `src/AtomUI.Controls.Shared/Net/FileUploadTask.cs` | 共享上传任务对象，承载传输回调和 cancellation 状态。 |
| `src/AtomUI.Controls.Shared/Net/IUploadFileSource.cs` | public 文件内容读取契约，不要求本地路径。 |
| `src/AtomUI.Controls.Shared/Net/IUploadFileSourceLease.cs` | internal AtomUI-owned source lease 标记，只由接受该 lease 的 owner 释放。 |

主题结构：

| 路径 | 职责 |
| --- | --- |
| `src/AtomUI.Desktop.Controls/Upload/Themes/UploadTheme.axaml` | 根模板，连接 `TriggerContent`、list 和 picture display source，并保持触发区与列表的稳定间距。 |
| `src/AtomUI.Desktop.Controls/Upload/Themes/UploadTriggerTheme.axaml` | 触发器 shell，只承载用户内容和点击表面。 |
| `src/AtomUI.Desktop.Controls/Upload/Themes/UploadDropZoneTheme.axaml` | 拖拽行为 shell，只承载用户内容和内容对齐。 |
| `src/AtomUI.Desktop.Controls/Upload/Themes/UploadDefaultDropAreaTheme.axaml` | 默认拖动视觉，保持 Frame、内容 presenter、Token 和动效结构。 |
| `src/AtomUI.Desktop.Controls/Upload/Themes/UploadListTheme.axaml` | 列表 shell，内部拥有自动隐藏的 `atom:ScrollViewer` 和滚动边界。 |
| `src/AtomUI.Desktop.Controls/Upload/Themes/PictureList/*` | Picture 列表项视觉，pending 内容优先读取 `UploadFileItem.PendingText`。 |
| `src/AtomUI.Desktop.Controls/Upload/Themes/PictureShapeList/*` | PictureCard/PictureCircle 列表布局和 item 视觉；append slot 由 display source 承载并进入同一 wrap flow。 |

## 相关文档

- 源设计文档：`docs/controls/desktop/data-entry/upload/overview.md`
- 实现文档：`docs/controls/desktop/data-entry/upload/implementation.md`
- Token 文档：`docs/controls/desktop/data-entry/upload/token.md`
- 变更记录：`docs/controls/desktop/data-entry/upload/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/avatar/index-cn.md

# Avatar

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

Avatar 展示用户、组织或对象的图像、文字或图标标识。它不负责资料卡、图片编辑、上传或大图预览。

包边界如下：

| 类型 | NuGet 包 | .NET 命名空间 | 职责 |
| --- | --- | --- | --- |
| `AbstractAvatar`、`Avatar`、`AvatarShape` | `AtomUI.Controls` | `AtomUI.Controls` | 单个头像、统一图片来源与加载状态 |
| `AvatarGroup` | `AtomUI.Desktop.Controls` | `AtomUI.Desktop.Controls` | 桌面端重叠排列、折叠计数与 Flyout |

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Controls` / `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Controls` / `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/DataDisplay/Avatar` |
| 状态 | Stable |

## 何时使用

Avatar 使用有限面积内的强识别内容表达主体身份。图片是首选内容，文字缩写和图标是稳定的降级内容；Circle 与 Square
只改变轮廓，不改变身份语义。Small、Middle、Large 与 Custom 提供一致的尺寸层级，`AvatarGroup` 通过重叠排列和折叠计数
表达“多个主体属于同一上下文”，不改变单个 Avatar 的加载和内容规则。

加载过程本身不应造成内容闪烁：旧图片或可用的 Text/Icon 在新请求完成前保持可见，成功提交后才切换为新图片，最终失败则
回到 Text/Icon。加载状态通过状态属性和伪类表达，而不是由模板猜测图片是否存在。

## 公共 API

`Avatar` 的公共 API 由 `AbstractAvatar` 提供：

| 契约 | 默认值 | 语义 |
| --- | --- | --- |
| `Source: ImageSource?` | `null` | 唯一主图片来源，支持 HTTP、File、Asset、Storage、Bytes、Stream 和已有 `IImage` |
| `FallbackSource: ImageSource?` | `null` | 主来源终态失败后的单次备用来源 |
| `RequestOptions: ImageRequestOptions?` | `null` | cache read/storage policy、partition、variant、timeout 和 HTTP headers |
| `Text: string?` | `null` | 无已加载图片时优先于 Icon 展示的文字内容，也是 content property |
| `Icon: PathIcon?` | `null` | 无已加载图片和 Text 时展示的图标 |
| `Gap: double` | `4` | 文本与头像边缘的最小逻辑间距，用于文本缩放 |
| `SizeType: CustomizableSizeType` | `Middle` | Small、Middle、Large 或 Custom 尺寸语义 |
| `Size: double` | `NaN` | 显式尺寸；非 `NaN` 时进入 Custom，恢复 `NaN` 时恢复原 SizeType |
| `Shape: AvatarShape` | `Circle` | Circle 或 Square |
| `IsMotionEnabled: bool` | SharedToken | 控制主题动效，不改变加载状态机 |

只读加载状态：

- `LoadState: ImageLoadState`
- `LoadError: ImageLoadError?`
- `LoadProgress: ImageLoadProgress?`
- `IsLoading`、`IsLoaded`、`IsFailed`

命令式入口与事件：

- `Reload()` 以单次 `ImageCacheReadPolicy.RefreshSource` 覆盖重新请求当前来源，不修改已绑定的 RequestOptions。
- `ImageOpened` 只在主来源或 fallback 成功并成为当前图片时触发。
- `ImageFailed` 只在最终失败时触发；主来源失败但 fallback 成功不触发最终失败事件。

Avatar 不公开 URL、Bitmap 或 loader 属性族，也不允许单个控件替换应用级 loader。

本地或网络 SVG 的一致性模式由 `UseImageLoading(options => options.Svg.ConformanceMode = ...)` 在 Application loader 构建时统一
选择；默认 `Compatible` 接受通过完整安全/复杂度校验的重复 `id`，`Strict` 将其报告为 `InvalidImageData`。Avatar 不增加 SVG
专属属性，`RequestOptions`、`Source` 和 `FallbackSource` 也不能覆盖应用级模式。DTD、script、外部资源、危险 CSS 和资源预算在
两种模式下始终执行同一套拒绝规则。

`AvatarGroup` 的桌面端 API 包括 `Children`、`MaxDisplayCount`、`FoldAvatarFlyoutTriggerType`、
`FoldInfoAvatarBackground`、`FoldInfoAvatarForeground`、`SizeType`、`Size` 和 `Shape`。Group 只组织子控件，不接管
每个 Avatar 的 Source、加载状态、结果租约或 Reload。

`PART_TextPresenter` 是稳定 template part。其他命名视觉节点服务主题实现，不构成额外 public API。

LLMS 语义区域：

| Part | AtomUI 节点 | 职责 | 相关 API | 相关 Token | 稳定性 |
| --- | --- | --- | --- | --- | --- |
| `root` | `Avatar` | 归一图片来源、内容优先级、尺寸、形状和加载状态。 | `Source`、`FallbackSource`、`RequestOptions`、`Text`、`Icon`、`SizeType`、`Size`、`Shape` | `AvatarToken`、SharedToken | public |
| `surface` | `Frame` | 绘制背景、边框和圆角。 | `Shape`、`SizeType`、`Size` | Avatar background、border、size Token | internal-observable |
| `image` | `ImagePresenter` | 展示当前有效 `IImage`，不自行加载来源。 | `LoadState`、`IsLoaded` | Avatar size/radius Token | internal-observable |
| `text` | `Viewbox` + `PART_TextPresenter` | 在 Gap 定义的内容区内展示 Text，只在空间不足时等比缩小并保持居中。 | `Text`、`Gap` | Font、size Token | template-stable |
| `icon` | `IconPresenter` | 在无图片和 Text 时展示 Icon。 | `Icon` | Icon size Token | internal-observable |
| `group` | `AvatarGroup` | 排列子 Avatar、折叠超出项并管理 Flyout。 | `Children`、`MaxDisplayCount`、`FoldAvatarFlyoutTriggerType` | AvatarGroup spacing/fold Token | public |

## 事件与命令

命令式入口与事件：
- `ImageFailed` 只在最终失败时触发；主来源失败但 fallback 成功不触发最终失败事件。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 自动设置字号

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/Avatar/Views/AvatarShowCase.axaml:107`

Gallery key：`ExamplesContent` / item `3`

```axaml
<StackPanel Orientation="Horizontal" Spacing="10">
    <atom:Avatar Background="{Binding AvatarBackground}"
                 Gap="{Binding AvatarGap}"
                 SizeType="Large"
                 Text="{Binding AvatarText}"/>
    <atom:Button Name="ChangeUserButton"
                 ButtonType="Default"
                 SizeType="Small"
                 Command="{Binding ChangeUserCommand}"
                 VerticalAlignment="Center" Content="切换用户" />
    <atom:Button Name="ChangeGapButton"
                 ButtonType="Default"
                 SizeType="Small"
                 Command="{Binding ChangeGapCommand}"
                 VerticalAlignment="Center" Content="切换间距" />
</StackPanel>
```

## 状态模型

可见内容优先级固定为：

```text
已加载 Image > Text > Icon
```

主来源处于 Loading 或 Failed、fallback 尚未成功时，不清空可用的 `Text` 或 `Icon`。只有图片成功提交后才切换为 Image。
Source 清空、来源替换、控件 detach 或最终失败会释放不再使用的图片租约。

加载状态由以下伪类表达：

| 伪类 | 条件 |
| --- | --- |
| `:loading` | 当前 generation 正在加载主来源或 fallback |
| `:loaded` | 当前 generation 已成功提交图片 |
| `:failed` | 主来源和可用 fallback 均终态失败 |
| `:fallback` | 当前已加载图片来自 `FallbackSource` |

尺寸伪类 `:small`、`:middle`、`:large`、`:custom-size` 继续表达主题尺寸选择。图片加载伪类和尺寸伪类相互独立。

Avatar 通过 `ImageLoadController` 使用当前 `Application` 的 `IImageLoader`：

1. attach 后根据 Source、RequestOptions、有效 Bounds 和 render scaling 创建请求。
2. 解码目标使用物理像素并向上量化到 16 px bucket，避免微小布局变化反复解码。
3. Source、fallback、options、尺寸 bucket 或 attach 状态变化时递增 generation 并取消旧 waiter。
4. 同来源 Reload 或尺寸变化可在 Loading 期间保留旧图片，成功后原子替换；最终失败后释放旧租约。
5. 结果回到 UI dispatcher 后再次校验 generation 和 attach 状态，旧结果只能释放，不能回写。

HTTP 条件重验证、非网络来源 Reload 强制重读、source/decode 两级请求合并、缓存、安全限制和应用销毁由统一 loader 负责，Avatar
不复制 transport、cache 或 scheduler。

## 主题与 Design Token

`AvatarTheme.axaml` 位于 `AtomUI.Controls/Avatar/Themes`，使用以下稳定视觉节点：

| 节点 | 职责 |
| --- | --- |
| `Frame` | 背景、边框和圆角 |
| `IconPresenter` | Icon 内容 |
| `ImagePresenter` | 已加载 `IImage` |
| `PART_TextPresenter` | Avalonia 原生 `TextBlock`，提供文字的自然排版尺寸 |
| Text `Viewbox` | 在 Gap 定义的水平内容区内向下缩放并居中文字 |

`PART_TextPresenter` 不依赖 Desktop Controls，保证 `Avatar` 可以由 `AtomUI.Controls` 独立提供。Circle 形状根据最终宽度设置
圆角；Text `Viewbox` 使用 `Gap` 投影出的左右内边距作为可用区域，并直接消费 TextBlock 的自然排版尺寸。空间不足时只做
等比缩小，短文本不放大，缩放前后都由模板布局保持水平和垂直居中。

`AvatarGroupTheme.axaml` 位于 `AtomUI.Desktop.Controls/Avatar/Themes`，消费同一个 `AvatarToken`，但只负责 group spacing、
overlap、折叠头像颜色和桌面 Flyout 视觉。

Token 来源：

Avatar Token 只表达组件级视觉变量，例如尺寸、间距、颜色、圆角、阴影、图标尺寸和弹层边界。Token 不承载运行时选择、展开、加载、错误、上传任务、过滤条件或业务状态。

当前 Token scope：

- `AvatarToken`，scope id 为 `Avatar`，源码位于 `src/AtomUI.Controls/Avatar/AvatarToken.cs`。

## AOT 与裁剪注意事项

- 不进行同步网络或文件 I/O。
- 不使用固定延迟等待布局；Arrange 和尺寸 bucket 是唯一尺寸就绪信号。
- 文字适配使用无状态的 Border/Viewbox 布局，不重复测量字形，不持有 template part 事件订阅。
- 不创建控件私有 `HttpClient`、cache 或 scheduler。
- Theme、codec 和 loader 通过静态注册保留，不使用反射扫描。
- Browser 使用统一 loader 的 Browser 能力矩阵；Avatar 不增加平台分支 API。
- SVG 一致性模式是封闭的应用级 enum；Avatar 不持有 validator delegate，不增加反射或 NativeAOT 动态发现边界。
- 快速 Source 切换只允许当前 generation 回写，旧结果必须及时释放。

## 源码索引

`AtomUI.Controls` 拥有单头像实现：

```text
src/AtomUI.Controls/Avatar/
├── AbstractAvatar.cs
├── Avatar.cs
├── AvatarPseudoClass.cs
├── AvatarShape.cs
├── AvatarToken.cs
└── Themes/AvatarTheme.axaml
```

`AtomUI.Desktop.Controls` 只拥有桌面组合控件：

```text
src/AtomUI.Desktop.Controls/Avatar/
├── AvatarGroup.cs
├── AvatarGroupFoldInfo.cs
└── Themes/AvatarGroupTheme.axaml
```

`Avatar` 和 `AvatarToken` 不在 Desktop 包中保留副本。Desktop 通过对 Controls 的正常项目引用复用类型和 Token resource。

## 相关文档

- 源设计文档：`docs/controls/desktop/data-display/avatar/overview.md`
- 实现文档：`docs/controls/desktop/data-display/avatar/implementation.md`
- Token 文档：`docs/controls/desktop/data-display/avatar/token.md`
- 变更记录：`docs/controls/desktop/data-display/avatar/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/badge/index-cn.md

# Badge

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

Badge 是 AtomUI 桌面控件体系中的徽标控件，用于在目标元素上展示数量、状态点和角标提示。

Badge 不负责通知系统、Tooltip 或复杂业务状态机。这些职责应由业务层、组合控件或更专用的 AtomUI 控件承担。

主要源码入口：

- `src/AtomUI.Controls/Badge`
- `src/AtomUI.Desktop.Controls/Badge`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/DataDisplay/Badge` |
| 状态 | Stable |

## 何时使用

Badge 的设计语言围绕控件职责、可观察状态和主题契约组织，而不是围绕模板节点组织。

| 维度 | 含义 | Badge 中的表达 |
| --- | --- | --- |
| 产品语义 | 控件在界面中承担的稳定职责。 | Badge 是 AtomUI 桌面控件体系中的徽标控件，用于在目标元素上展示数量、状态点和角标提示。 |
| 内容承载 | 用户数据、展示内容、集合项或操作入口如何进入控件。 | `Text`。 |
| 状态反馈 | public API、内部状态和伪类如何形成用户可感知反馈。 | input/value、motion、visual option。 |
| 主题语义 | ControlTheme、SharedToken、控件 Token 和模板绑定如何表达视觉。 | Badge Token + ControlTheme。 |

## 公共 API

Badge 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。

核心 public surface 按语义分组维护：

| 契约组 | 代表成员 | 维护含义 |
| --- | --- | --- |
| 内容与数据 | `Text` | 定义控件展示内容、输入数据、模板或业务对象入口。 |
| 选择与集合 | `Count`、`IsAdornerMode`、`OverflowCount` | 维护选择、展开、过滤、分页、分组或集合状态。 |
| 交互与状态 | `BadgeIsVisible`、`IsMotionEnabled`、`IsZeroVisible`、`Status` | 表达用户可观察状态、可用性、清除、加载或反馈语义。 |
| 视觉与布局 | `BadgeColor`、`BadgeDotColor`、`DotColor`、`Offset`、`Placement`、`RibbonColor`、`Size` | 影响尺寸、位置、颜色、形状、密度和模板视觉变量。 |
| 动效与异步 | `MotionDuration` | 约束动效开关、异步加载、播放速度、超时和任务边界。 |
| 其他稳定入口 | `DecoratedTarget` | 保留为 public surface，变更前需确认 Gallery 和用户 XAML 依赖。 |

当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。

主要公开类型与枚举：

- 类型：`AbstractCountBadge`、`AbstractCountBadgeAdorner`、`AbstractDotBadge`、`AbstractDotBadgeAdorner`、`AbstractRibbonBadge`、`AbstractRibbonBadgeAdorner`、`BadgeZoomBadgeInMotion`、`BadgeZoomBadgeOutMotion`、`CountBadge`、`CountBadgeAdorner`、`DotBadge`、`DotBadgeAdorner`、`DotBadgeIndicator`、`RibbonBadge` 等 15 项。
- 枚举：`CountBadgeSize`、`DotBadgeStatus`、`RibbonBadgePlacement`。

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_LabelPart` | `TextBlock` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_MotionActor` | `BaseMotionActor` | 稳定模板协作入口，重命名前必须同步主题和实现。 |

当前未抽取到控件专属伪类；主题主要依赖 Avalonia 标准伪类、模板绑定和内部 StyledProperty。

## 事件与命令

Badge 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。
当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/Badge/Views/BadgeShowCase.axaml:35`

Gallery key：`ExamplesContent` / item `0`

```axaml
<StackPanel Orientation="Horizontal" Spacing="20">
    <atom:CountBadge Count="5">
        <Border Width="40"
                Height="40"
                Background="rgb(191,191,191)"
                CornerRadius="8" />
    </atom:CountBadge>
    <atom:CountBadge Count="0" IsZeroVisible="True">
        <Border Width="40"
                Height="40"
                Background="rgb(191,191,191)"
                CornerRadius="8" />
    </atom:CountBadge>
</StackPanel>
```

### 封顶数字

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/Badge/Views/BadgeShowCase.axaml:58`

Gallery key：`ExamplesContent` / item `1`

```axaml
<StackPanel Orientation="Horizontal" Spacing="20">
    <atom:CountBadge Count="99">
        <Border Width="40"
                Height="40"
                Background="rgb(191,191,191)"
                CornerRadius="8" />
    </atom:CountBadge>
    <atom:CountBadge Count="100">
        <Border Width="40"
                Height="40"
                Background="rgb(191,191,191)"
                CornerRadius="8" />
    </atom:CountBadge>
    <atom:CountBadge Count="99" OverflowCount="10">
        <Border Width="40"
                Height="40"
                Background="rgb(191,191,191)"
                CornerRadius="8" />
    </atom:CountBadge>
    <atom:CountBadge Count="1000" OverflowCount="999">
        <Border Width="40"
                Height="40"
                Background="rgb(191,191,191)"
                CornerRadius="8" />
    </atom:CountBadge>
</StackPanel>
```

### 偏移量

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/Badge/Views/BadgeShowCase.axaml:93`

Gallery key：`ExamplesContent` / item `2`

```axaml
<StackPanel Orientation="Horizontal" Spacing="20">
    <atom:CountBadge Count="5" Offset="10, 10">
        <Border Width="40"
                Height="40"
                Background="rgb(191,191,191)"
                CornerRadius="8" />
    </atom:CountBadge>
</StackPanel>
```

### 尺寸

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/Badge/Views/BadgeShowCase.axaml:109`

Gallery key：`ExamplesContent` / item `3`

```axaml
<StackPanel Orientation="Horizontal" Spacing="20">
    <atom:CountBadge Count="5">
        <Border Width="40"
                Height="40"
                Background="rgb(191,191,191)"
                CornerRadius="8" />
    </atom:CountBadge>
    <atom:CountBadge Count="5" Size="Small">
        <Border Width="40"
                Height="40"
                Background="rgb(191,191,191)"
                CornerRadius="8" />
    </atom:CountBadge>
</StackPanel>
```

## 状态模型

Badge 的状态流按以下路径收敛：

```text
Public API / inherited command / item source / user input
  -> 控件实例状态
  -> effective state / pseudo-class / template property
  -> ControlTheme selector / presenter / renderer
  -> Gallery 可观察行为
```

状态维护规则：

- Disabled 或不可交互状态优先屏蔽 pointer、keyboard、motion 和提交类反馈。
- input/value、motion、visual option 状态由控件实例或明确的数据 owner 推导，不能在 template part 之间双向竞争。
- 模板重套用时必须把 public API 对应状态回放到新的 part、伪类和主题变量。
- 集合、弹层、异步、动效或窗口相关状态必须能处理 reset、close、cancel、detach 和 owner 释放。

## 主题与 Design Token

Badge 的视觉模型由控件模板、ControlTheme、SharedToken 和必要的控件 Token 共同构成。

| 主题文件 | 职责 |
| --- | --- |
| `CountBadgeAdornerTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `DotBadgeAdornerTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `DotBadgeIndicatorTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `RibbonBadgeAdornerTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |

Badge 使用 `BadgeToken` 作为控件 Token scope。Token 只表达组件视觉语义，不承载 input/value、motion、visual option 运行时状态。

主题维护规则：

- 不删除或重命名已经稳定的 ControlTheme key、template part、伪类和资源 key。
- 不把可由 AXAML 表达的模板状态迁移为 C# 动态创建视觉。
- 不把 hover、pressed、selected、expanded、loading、filter、popup open 等运行时状态写入 Token。
- Browser 或平台特化主题必须保持同一 API 的语义一致。

Token 来源：

Badge Token 只表达组件级视觉变量，例如尺寸、间距、颜色、圆角、阴影、图标尺寸和弹层边界。Token 不承载运行时选择、展开、加载、错误、上传任务、过滤条件或业务状态。

当前 Token scope：

- `BadgeToken`，scope id 为 `Badge`，源码位于 `src/AtomUI.Desktop.Controls/Badge/BadgeToken.cs`。

## AOT 与裁剪注意事项

资源和 AOT 约束：

- 不通过运行时反射扫描 public API、Token 或 Gallery 示例数据。
- 不把可静态声明的模板结构迁移到 C# 动态创建。
- 异步加载、上传、弹层和窗口生命周期必须能取消或释放。
- 缓存对象必须与控件、窗口、弹层或数据 owner 生命周期一致。
- Source generator 生成文件不手工编辑；需要修改时改输入源或 generator。

性能边界：

- 控件应优先复用 Avalonia 原生虚拟化、模板绑定和资源系统。
- 避免为每次状态变化创建不必要的视觉对象、订阅或动画对象。
- 大集合控件必须保证 container recycle 后不会泄漏旧 item 状态。

## 源码索引

主要源码文件：

- `src/AtomUI.Controls/Badge/AbstractCountBadge.cs`
- `src/AtomUI.Controls/Badge/AbstractCountBadgeAdorner.cs`
- `src/AtomUI.Controls/Badge/AbstractDotBadge.cs`
- `src/AtomUI.Controls/Badge/AbstractDotBadgeAdorner.cs`
- `src/AtomUI.Controls/Badge/AbstractRibbonBadge.cs`
- `src/AtomUI.Controls/Badge/AbstractRibbonBadgeAdorner.cs`
- `src/AtomUI.Controls/Badge/BadgeColorUtils.cs`
- `src/AtomUI.Controls/Badge/BadgeMotion.cs`
- `src/AtomUI.Controls/Badge/DotBadgeIndicator.cs`
- `src/AtomUI.Desktop.Controls/Badge/BadgeToken.cs`
- `src/AtomUI.Desktop.Controls/Badge/CountBadge.cs`
- `src/AtomUI.Desktop.Controls/Badge/CountBadgeAdorner.cs`
- `src/AtomUI.Desktop.Controls/Badge/DotBadge.cs`
- `src/AtomUI.Desktop.Controls/Badge/DotBadgeAdorner.cs`
- `src/AtomUI.Desktop.Controls/Badge/RibbonBadge.cs`
- `src/AtomUI.Desktop.Controls/Badge/RibbonBadgeAdorner.cs`
- `src/AtomUI.Desktop.Controls/Badge/Themes/CountBadgeAdornerTheme.axaml`
- `src/AtomUI.Desktop.Controls/Badge/Themes/DotBadgeAdornerTheme.axaml`
- `src/AtomUI.Desktop.Controls/Badge/Themes/DotBadgeIndicatorTheme.axaml`
- `src/AtomUI.Desktop.Controls/Badge/Themes/RibbonBadgeAdornerTheme.axaml`

职责边界：

- 控件主文件保留 public/protected API、Avalonia 属性注册、事件和主要生命周期入口。
- Theme 文件负责静态视觉结构、template part、selector 和资源绑定。
- Token 文件只提供组件视觉变量，不保存实例状态。
- Gallery 文件只展示用法和示例，不作为运行时逻辑 owner。

## 相关文档

- 源设计文档：`docs/controls/desktop/data-display/badge/overview.md`
- 实现文档：`docs/controls/desktop/data-display/badge/implementation.md`
- Token 文档：`docs/controls/desktop/data-display/badge/token.md`
- 变更记录：`docs/controls/desktop/data-display/badge/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/calendar/index-cn.md

# Calendar

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

`Calendar` 是按日期组织业务展示内容的桌面日历控件，遵循本专题定义的月面板、年面板、Header、范围限制、禁用规则、周序号和单元格定制语义。它同时保留桌面端可用的焦点与方向键导航。`LunarCalendar` 继承这些稳定语义，并在同一公历状态模型上增加中国农历、二十四节气、传统节日、周末以及应用提供的节假日/调休投影。

Calendar 只负责“查看并选择一个日期或月份”的面板体验，不负责日期输入弹层、范围选择、多日期选择、时间编辑或复杂日程排布。日期输入由 DatePicker 等控件承担，范围和日程数据由业务层承担；Calendar 只提供轻量 `RangeBars` 标记能力，用于在日期网格中表达连续日期业务条。新 Calendar 的内部 `CalendarView` 与 DatePicker 的旧 CalendarView 子系统完全隔离。

主要源码入口：

- `src/AtomUI.Desktop.Controls/Calendar`
- `controlgallery/AtomUIGallery/ShowCases/DataDisplay/Calendar`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/DataDisplay/Calendar` |
| 状态 | Stable |

## 何时使用

Calendar 的设计语言围绕日期面板的产品语义、可观察状态和主题契约组织，而不是围绕某个模板节点组织。

| 维度 | 含义 | Calendar 中的表达 |
| --- | --- | --- |
| 产品语义 | 控件在界面中承担的稳定职责。 | 以 Month/Year 两种面板展示日期或月份，并提交单一选中值。 |
| 内容承载 | 业务数据和模板如何进入控件。 | `Value`、`ValidRange`、`DisabledDate`、`CellTemplate`、`FullCellTemplate` 与 `HeaderTemplate`。 |
| 状态反馈 | API、内部状态与伪类如何形成反馈。 | `today`、`selected`、`outside`、`disabled`、`focused`、`fullscreen`、`mini`、`show-week`。 |
| 主题语义 | SharedToken、`CalendarToken`、`LunarCalendarToken`、ControlTheme 与模板如何表达视觉。 | Calendar 基础主题消费九个 Calendar 专属 Token；LunarCalendar 只增加农历内容所需的增量 Token。 |

设计上的首要不变量是：Cell 定制不能夺走日期值、选中、禁用、焦点和命中测试语义；这些语义由 Cell 容器保留，模板只改变内容呈现方式。

## 公共 API

Calendar 的公共契约由 Avalonia 属性、事件、模板、上下文类型、枚举、伪类和 ControlTheme 共同组成。

### 3.1 属性

| 属性 | 默认值 | 语义 |
| --- | --- | --- |
| `Value` | 构造时的 `DateTime.Today` | 当前选中日期和面板锚点；写入和提交均规范化到 `.Date`。 |
| `Mode` | `CalendarMode.Month` | `Month` 显示 6×7 日期网格；`Year` 显示 3×4 月份网格。 |
| `Fullscreen` | `true` | 完整布局或紧凑 Mini 布局；不改变日期算法和选择语义。 |
| `ShowWeek` | `false` | Month 日期面板是否显示周序号列。 |
| `ValidRange` | `null` | 首尾包含的日期范围；范围外日期禁用。 |
| `DisabledDate` | `null` | 业务禁用谓词；与 `ValidRange` 共同决定 Cell 是否可用。 |
| `CellTemplate` | `null` | 替换默认值下方的业务内容区域，但保留默认日期/月值和 Cell 状态。 |
| `FullCellTemplate` | `null` | 替换 Cell 的完整内部内容；优先于 `CellTemplate`。 |
| `HeaderTemplate` | `null` | 自定义 Header；为空时使用默认 Year/Month/Mode Header。 |
| `RangeBars` | empty | 声明连续日期范围条；仅在 Fullscreen Month 日期网格 overlay 中渲染。 |

`ValidRange` 使用 `CalendarDateRange(start, end)`，两端均包含，构造函数把时间规范化到日期并拒绝 `end < start`。`CalendarCellContext` 提供 `Value`、`Today`、`CellType`、`DisplayValue`、`IsToday`、`IsInView`、`IsSelected` 和 `IsDisabled`。`CalendarHeaderContext` 提供当前 `Value`/`Mode` 以及提交值和模式的命令。

`CalendarRangeBar` 提供 `StartDate`、`EndDate`、`Label`、`Background` 和 `Height`。日期端点按 `.Date` 投影，首尾包含；`Background` 支持普通 brush、binding、DynamicResource 和 TokenResource。范围条模型、分段算法和资源生命周期见 [Calendar 范围条设计](range-bar-design.md)。

`Value` 与 `Mode` 的默认 Avalonia binding mode 均为 `TwoWay`，用户通过 Cell 或 Header 提交的新状态可以写回绑定源。

### 3.2 事件

| 事件 | 语义 |
| --- | --- |
| `PanelChanged` | 用户选择跨自然月/自然年，或用户切换 Month/Year 模式时触发。 |
| `ValueChanged` | 用户提交后日期值实际变化时触发；程序直接设置 `Value` 不触发。 |
| `Selected` | 每次有效用户选择触发，即使选择的日期与当前 `Value` 相同。 |

用户选择的提交顺序固定为 `PanelChanged` → `ValueChanged` → `Selected`，不存在的事件从序列中省略。事件发生时新值已经写入 `Value`。程序直接设置 `Value` 或 `Mode` 只更新属性和渲染，不模拟用户事件。

`CalendarSelectSource` 用于区分 `Year`、`Month`、`Date` 和 `Customize` 来源。周序号 Cell 可选择；激活后以该行周首日作为日期值，并使用 `Date` 来源。

### 3.3 模板、伪类与主题契约

稳定的根模板协作入口为 `PART_HeaderPresenter`、`PART_BodyPresenter`、`PART_CalendarView`、`PART_RangeBarPanel`、`PART_DefaultHeader` 和 `PART_CustomHeader`；默认 Header 内部使用 `PART_YearSelect`、`PART_MonthSelect`、`PART_ModeSwitch`，View 内部使用 `PART_WeekHeader` 和 `PART_CellHost`，Cell 内部使用 `PART_Item`、`PART_CellInner`、`PART_ItemContent` 和 `PART_Value`。

根伪类包括 `:fullscreen`、`:mini`、`:month`、`:year`、`:show-week`；Cell 伪类包括 `:date`、`:month`、`:week`、`:today`、`:selected`、`:outside`、`:disabled`、`:focused`。四个内部 ControlTheme 的 key 与伪类是主题兼容契约，变更必须同步源码、Gallery 和文档。

### 3.4 LunarCalendar 扩展契约

`LunarCalendar` 是 `Calendar` 的公开派生控件，继续使用 `Value`、`Mode`、`Fullscreen`、`ShowWeek`、`ValidRange`、`DisabledDate`、三个模板入口、`RangeBars` 和三个选择事件。它增加 `ShowSolarTerms`、`ShowTraditionalFestivals`、`ShowHolidays`、`HighlightWeekends`、`HolidayProvider`、只读 `SelectedLunarDateInfo` 和 `RefreshHolidayData()`。

农历算法保证范围为 `1900-01-01` 至 `2100-12-31`。`Value` 在 LunarCalendar 上收敛到该范围；内部有效选择范围为支持范围与 `ValidRange` 的交集。法定节假日和调休不内置，由 `ILunarCalendarHolidayProvider` 以同步面板数据提供。完整模型、默认值、枚举、Provider 规则和显示优先级见 [LunarCalendar 农历能力设计](lunar-calendar-design.md)。

## 事件与命令

Calendar 的公共契约由 Avalonia 属性、事件、模板、上下文类型、枚举、伪类和 ControlTheme 共同组成。
`ValidRange` 使用 `CalendarDateRange(start, end)`，两端均包含，构造函数把时间规范化到日期并拒绝 `end < start`。`CalendarCellContext` 提供 `Value`、`Today`、`CellType`、`DisplayValue`、`IsToday`、`IsInView`、`IsSelected` 和 `IsDisabled`。`CalendarHeaderContext` 提供当前 `Value`/`Mode` 以及提交值和模式的命令。
### 3.2 事件
| 事件 | 语义 |
用户选择的提交顺序固定为 `PanelChanged` → `ValueChanged` → `Selected`，不存在的事件从序列中省略。事件发生时新值已经写入 `Value`。程序直接设置 `Value` 或 `Mode` 只更新属性和渲染，不模拟用户事件。
`LunarCalendar` 是 `Calendar` 的公开派生控件，继续使用 `Value`、`Mode`、`Fullscreen`、`ShowWeek`、`ValidRange`、`DisabledDate`、三个模板入口、`RangeBars` 和三个选择事件。它增加 `ShowSolarTerms`、`ShowTraditionalFestivals`、`ShowHolidays`、`HighlightWeekends`、`HolidayProvider`、只读 `SelectedLunarDateInfo` 和 `RefreshHolidayData()`。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/Calendar/Views/CalendarShowCase.axaml:44`

Gallery key：`ExamplesContent` / item `0`

```axaml
<atom:Calendar Value="{Binding SampleDate}"
```

### 跨日期事件

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/Calendar/Views/CalendarShowCase.axaml:170`

Gallery key：`ExamplesContent` / item `2`

```axaml
<atom:Calendar Value="{Binding CrossDateEventsSampleDate}"
               MinWidth="640"
               HorizontalAlignment="Stretch">
    <atom:Calendar.RangeBars>
        <atom:CalendarRangeBar StartDate="2026-01-08"
                               EndDate="2026-01-10"
                               Label="发布窗口"
                               Background="{atom:SharedTokenResource ColorPrimary}" />
        <atom:CalendarRangeBar StartDate="2026-01-14"
                               EndDate="2026-01-14"
                               Label="设计评审"
                               Background="{atom:SharedTokenResource ColorSuccess}" />
        <atom:CalendarRangeBar StartDate="2026-01-21"
                               EndDate="2026-01-24"
                               Label="维护窗口"
                               Background="{atom:SharedTokenResource ColorWarning}" />
        <atom:CalendarRangeBar StartDate="2026-01-30"
                               EndDate="2026-01-31"
                               Label="缺陷修复"
                               Background="{atom:SharedTokenResource ColorError}" />
    </atom:Calendar.RangeBars>
</atom:Calendar>
```

### 卡片模式

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/Calendar/Views/CalendarShowCase.axaml:201`

Gallery key：`ExamplesContent` / item `3`

```axaml
<Border Width="300"
        HorizontalAlignment="Left"
        BorderBrush="{atom:SharedTokenResource ColorBorderSecondary}"
        BorderThickness="{atom:SharedTokenResource BorderThickness}"
        CornerRadius="{atom:SharedTokenResource BorderRadiusLG}">
    <atom:Calendar Value="{Binding SampleDate}" Fullscreen="False" />
</Border>
```

### 农历日历

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/Calendar/Views/CalendarShowCase.axaml:217`

Gallery key：`ExamplesContent` / item `4`

```axaml
<atom:LunarCalendar Value="{Binding LunarCalendarSampleDate}"
```

## 状态模型

状态流按单一 owner 收敛：

```text
Public API / Header / Cell input
  -> Calendar（Value、Mode、事件顺序）
  -> CalendarHeader + CalendarView（不可变投影）
  -> CalendarViewCell（状态、模板、命中测试）
  -> ControlTheme selector / Gallery 可观察行为
```

- `Calendar` 是唯一业务状态 owner；`CalendarView` 不保存第二份公开选中值。
- Month 模式按当前月生成 42 个日期 Cell；Year 模式按当前年生成 12 个月 Cell。
- `ShowWeek` 只作用于 Month 日期网格，增加每行一个周序号 Cell。周序号 Cell 可被点击并提交该行周首日，但不作为 `CalendarCellContext` 的日期/月模板项。
- 日期禁用由 `ValidRange` 与 `DisabledDate` 的并集决定；月份禁用按“月份首日和末日都在范围外”或业务规则判定，不能只检查当前日。
- 方向键只移动面板内的 roving focus；应跳过禁用 Cell 和周序号 Cell，无合法目标时保留当前焦点。Enter/Space 才提交选择。
- `Fullscreen`/Mini 只改变布局密度和 Header 控件尺寸，不改变值、事件顺序、禁用和模板优先级。
- `RangeBars` 只改变日期网格上方的 overlay 业务标记层，不改变 Cell 外间距、选择状态、禁用状态、鼠标指针、事件顺序或 Automation。
- AtomUI 语言服务改变会同步更新日期格式、周标题、月份名称、Header 的 Month/Year 文本与年份后缀。
- LunarCalendar 不建立第二份农历选中值；`SelectedLunarDateInfo`、Cell 农历内容和 Header 农历标签都从当前公历 Value/面板数据单向投影。
- Fullscreen/Card × Month/Year 四种组合共享 CalendarView 的网格拓扑、焦点、容器池和 Automation，农历 adapter 只改变专用 Cell、Header 文案和布局 metrics。

## 主题与 Design Token

Calendar 使用源码中的 `CalendarToken` 以及 SharedToken。专属 Token 只表达九个组件视觉语义：`FullBg`、`FullPanelBg`、`ItemActiveBg`、`YearControlWidth`、`MonthControlWidth`、`YearMonthCellWidth`、`MiniContentHeight`、`FullCellMinHeight`、`RangeBarHeight`。运行时状态通过伪类和 selector 表达，不写入 Token。

LunarCalendar 使用独立 exact Control identity 和 `LunarCalendarToken`，只补充双行 Cell、农历次级文本、卡片内容高度、周末/节假日标记、Fullscreen Cell 高度和范围条避让所需语义。LunarCalendar root 不通过深层 selector 修改 CalendarHeader、CalendarView、ComboBox、OptionButtonGroup 或普通 CalendarViewCell 的模板内部。

| Theme 文件 | 稳定职责 |
| --- | --- |
| `CalendarTheme.axaml` | 根背景、Header/CustomHeader 选择、CalendarView 与范围条 overlay 接线；Fullscreen 拉伸，Mini 提供无外框的卡片内容布局，外部容器负责边框与宽度。 |
| `CalendarHeaderTheme.axaml` | Year Select、Month Select、Month/Year `OptionButtonGroup` 模式切换。Mini 时 Header 交互控件应使用 Small 尺寸。 |
| `CalendarViewTheme.axaml` | WeekHeader、CellHost，以及 Fullscreen/Mini 的布局差异。 |
| `CalendarViewCellTheme.axaml` | 默认日期值、Cell/FullCell 模板消费、状态 selector 和命中测试视觉。 |

`CellTemplate` 必须保留默认值显示，并与内置范围条 overlay 共存；`FullCellTemplate` 覆盖完整 Cell 内部内容且优先级最高，但不替换 Calendar body overlay。两者都不能删除禁用、选中、焦点和 outside 的容器状态。

普通 Calendar 在 `Fullscreen=false` 时使用 256 高内容区，包含 WeekHeader 与六行 CellHost；LunarCalendar 由自身 `MiniContentHeight` 按双行 Cell 尺寸和共享间距派生有效内容高度。body 的顶部分隔线和纵向 Padding 位于该内容区之外。Mini 的 selected/today/disabled 状态分别使用主色实心、主色单线描边和禁用背景；Fullscreen selected 保持 `ItemActiveBg` 与主色日期值，不复用 Mini 的实心主色规则。

Token 来源：

Calendar 的 Token 收敛为九个组件视觉语义。日期值、周标题、范围条间距、范围条圆角、Padding、Border、Typography 与 Motion 均从 SharedToken 派生；Fullscreen 单元最小高度通过 `FullCellMinHeight` 固化 Calendar 完整单元的测量规则，Year 模式月份单元宽度通过 `YearMonthCellWidth` 固化面板单元测量规则，范围条默认高度通过 `RangeBarHeight` 固化 Calendar overlay 的默认条高。

当前 Calendar Token 源：

- `CalendarToken`，源码位于 `src/AtomUI.Desktop.Controls/Calendar/CalendarToken.cs`。
- AXAML 通过生成的 `CalendarTokenResource` 访问这些值。

LunarCalendar 使用独立 exact Control identity 和 `LunarCalendarToken`。它只补充农历双行内容、卡片内容高度、周末/节假日状态以及 Fullscreen RangeBars 避让所需语义，不复制 Calendar 的根背景、Header、普通 Cell 选中态或 RangeBars 默认条高。

## AOT 与裁剪注意事项

- 日期/月/周计算在 model 失效时完成，不在 Measure/Arrange 热路径重复执行。
- `DisabledDate` 对同一次 model 构建的每个候选值最多调用一次；异常不得被静默吞掉。
- Container pool 只复用无业务所有权的视觉容器；模板、Context、Focus 和 Automation 必须随 Bind/Unbind 完整更新。
- RangeBars 集合使用 owner-managed 非 Visual `AvaloniaObject` 范式；`CalendarRangeBar.Background` 的动态资源和 TokenResource 由 generated scoped resource host 承载，Calendar 负责 attach/release。
- `CalendarRangeBarPanel` 不遍历 Cell visual tree，不在 pointer move 热路径中计算，也不拥有业务数据生命周期。
- Token 通过 `CalendarTokenResource`、`LunarCalendarTokenResource` 和 SharedToken 进入 AXAML；运行时状态由伪类 selector 表达。
- 不使用运行时反射扫描 API、Token、日期类型或 Gallery 数据；属性静态注册、强类型上下文和生成资源保持 NativeAOT 兼容。
- LanguageManager、VisualTree、Template part 等外部订阅必须有成对释放路径，避免 detach 后保留 Calendar。
- 农历年表、二十四节气表和传统节日 resolver 是静态只读数据与纯函数；不依赖第三方农历运行库、系统时区、网络、反射或字符串 binding。面板数据有界且不在 Measure/Arrange/Render 热路径构建。

## 源码索引

```text
src/AtomUI.Desktop.Controls/Calendar/
├── Calendar.cs
├── CalendarCellContext.cs
├── CalendarDateRange.cs
├── CalendarEnums.cs
├── CalendarEventArgs.cs
├── CalendarHeaderContext.cs
├── CalendarRangeBar.cs
├── CalendarToken.cs
├── Internal/
│   ├── CalendarHeader.cs
│   ├── CalendarHeaderOptions.cs
│   ├── CalendarPseudoClass.cs
│   ├── CalendarRangeBarPanel.cs
│   ├── CalendarRelayCommand.cs
│   ├── CalendarView.cs
│   ├── CalendarViewAutomationPeer.cs
│   ├── CalendarViewCell.cs
│   ├── CalendarViewCellAutomationPeer.cs
│   ├── CalendarViewCellBuilder.cs
│   ├── CalendarViewCellModel.cs
│   └── CalendarViewMode.cs
├── Localization/
│   ├── CalendarControlLangResourceKind.cs
│   ├── en-US.xlf
│   ├── zh-CN.xlf
│   └── zh-TW.xlf
└── Themes/
    ├── CalendarTheme.axaml(.cs)
    ├── CalendarHeaderTheme.axaml(.cs)
    ├── CalendarViewTheme.axaml(.cs)
    └── CalendarViewCellTheme.axaml(.cs)
```

`CalendarToken.cs` 是当前 Calendar ControlTheme 实际消费的专属 Token 源。LunarCalendar 的公开类型、算法、数据表、internal adapter、专用 Cell、主题和本地化也统一归属本源码目录；稳定职责结构见 [LunarCalendar 农历能力设计](lunar-calendar-design.md)。DatePicker 的日期面板实现位于 DatePicker 模块，不与本目录 CalendarView 或 Token 混用。

## 相关文档

- 源设计文档：`docs/controls/desktop/data-display/calendar/overview.md`
- 实现文档：`docs/controls/desktop/data-display/calendar/implementation.md`
- Token 文档：`docs/controls/desktop/data-display/calendar/token.md`
- 变更记录：`docs/controls/desktop/data-display/calendar/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/card/index-cn.md

# Card

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

Card 是桌面端数据展示类控件，用于在独立的内容面板中组织信息。它提供标题、额外操作、封面媒体、内容区域、底部操作区、元信息、栅格内容、标签页内容、加载占位、悬停反馈和无边框样式。

Card 的职责是提供稳定的信息容器视觉和组合入口。它不负责集合筛选、排序、虚拟化、表单提交、复杂选择、弹层管理或远程数据加载；这些能力应由业务层、ListView、DataGrid、Form、Popup/Flyout 或专用控件承担。

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/DataDisplay/Card` |
| 状态 | Stable |

## 何时使用

Card 的设计语言来自 参考设计体系的卡片容器：一个有明确边界的内容表面承载同一主题的信息，Header 表示信息组名称，Extra 表示辅助操作，Cover 表示主要媒体，Actions 表示底部轻量操作。

| 维度 | 含义 | 典型表达 |
| --- | --- | --- |
| 信息容器 | 用边框、圆角、背景和阴影形成独立面板。 | `StyleVariant`、`BoxShadow`、`BorderThickness`、`CornerRadius`。 |
| 层级标题 | 用 Header/Extra 表示信息组标题和辅助入口。 | `Header`、`HeaderTemplate`、`Extra`、`ExtraTemplate`。 |
| 媒体展示 | 用封面区域承载图片或自定义媒体。 | `Cover`、`CoverTemplate`。 |
| 内容形态 | 支持普通内容、元信息、栅格内容和标签页内容。 | `Content`、`CardMetaContent`、`CardGridContent`、`CardTabsContent`。 |
| 操作区 | 底部均分操作按钮，适合轻量命令。 | `Actions`、`CardActionButton`。 |
| 密度规格 | 用尺寸控制 Header 和内容 padding。 | `SizeType`。 |
| 加载反馈 | 内容获取过程中显示 Skeleton 占位。 | `IsLoading`。 |

## 公共 API

Card 的公共 API 分为根容器属性、内容扩展入口和子控件家族。

根容器属性：

| API | 类型 | 语义 |
| --- | --- | --- |
| `Extra` / `ExtraTemplate` | `object?` / `IDataTemplate?` | Header 右侧辅助内容及模板。 |
| `StyleVariant` | `CardStyleVariant` | 卡片外观，`Outlined` 使用边框，`Borderless` 使用无边框阴影。 |
| `SizeType` | `SizeType` | 尺寸规格，默认 `Middle`，影响 Header 和内容 padding。 |
| `IsLoading` | `bool` | 是否在内容区域显示 Skeleton 加载占位。 |
| `IsInnerMode` | `bool` | 内部卡片模式，用于嵌套信息结构。 |
| `IsHoverable` | `bool` | 是否启用指针悬停阴影和手型光标。 |
| `Cover` / `CoverTemplate` | `object?` / `IDataTemplate?` | 顶部封面内容及模板。 |
| `Actions` | `Controls` | 底部操作区集合。 |
| `BoxShadow` | `BoxShadows` | 卡片表面阴影，通常由主题和状态设置。 |
| `IsMotionEnabled` | `bool` | 是否启用 Card 相关过渡动效。 |

`CardStyleVariant` 的稳定取值：

| 值 | 语义 |
| --- | --- |
| `Outlined` | 默认有边框卡片。 |
| `Borderless` | 无边框卡片，使用阴影表达容器边界。 |

子控件家族：

| 类型 | 职责 |
| --- | --- |
| `CardMetaContent` | 承载头像、标题和描述内容的元信息布局。 |
| `CardGridContent` | 在 Card body 中生成 Grid 面板，承载 `CardGridItem`。 |
| `CardGridItem` | 栅格内容项，支持行列位置、span、hover 和尺寸同步。 |
| `CardTabsContent` | 在 Card 中承载 TabControl 内容和 tab bar extra。 |
| `CardActionButton` | 底部操作区推荐按钮类型，基于 `IconButton`。 |

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_ActionPanel` | `CardActionPanel` | 底部 Actions 的实际布局和渲染承载。 |
| `PART_ItemsPresenter` | `ItemsPresenter` | `CardGridContent` 中 Grid items panel 的接入点。 |
| `PART_TabControl` | `TabControl` | `CardTabsContent` 内部 TabControl 接入点。 |

稳定伪类和内部状态：

| 状态 | 语义 |
| --- | --- |
| `:headerless` | Header、HeaderTemplate、Extra 和 ExtraTemplate 都为空时启用，用于隐藏 Header 区域。 |
| `ContentType` | 内部状态，区分 `Default`、`Meta`、`Grid` 和 `Tabs` 内容形态，驱动主题 padding、圆角和边框。 |

Card 没有专用 public routed event 或命令。

## 事件与命令

Card 没有专用 public routed event 或命令。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 带标签页

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/Card/Views/CardAdvancedShowCase.axaml:54`

Gallery key：`ExamplesContent` / item `1`

```axaml
<StackPanel Orientation="Vertical" Spacing="20">
    <atom:Card Header="卡片标题" HorizontalAlignment="Stretch" SizeType="Large">
        <atom:Card.Extra>
            <atom:HyperLinkButton Content="更多" />
        </atom:Card.Extra>
        <atom:CardTabsContent>
            <atom:TabItem Header="标签页 1" Content="内容 1" />
            <atom:TabItem Header="标签页 2" Content="内容 2" />
        </atom:CardTabsContent>
    </atom:Card>

    <atom:Card HorizontalAlignment="Stretch">
        <atom:CardTabsContent>
            <atom:CardTabsContent.TabBarExtraContent>
                <atom:HyperLinkButton Content="更多" />
            </atom:CardTabsContent.TabBarExtraContent>
            <atom:TabItem Header="文章" Content="文章内容" />
            <atom:TabItem Header="应用" Content="应用内容" />
            <atom:TabItem Header="项目" Content="项目内容" />
        </atom:CardTabsContent>
    </atom:Card>
</StackPanel>
```

### 基础卡片

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/Card/Views/CardBasicShowCase.axaml:12`

Gallery key：`ExamplesContent` / item `0`

```axaml
<WrapPanel ItemSpacing="20" LineSpacing="20">
    <atom:Card Header="大尺寸卡片" SizeType="Large" Width="300">
        <atom:Card.Extra>
            <atom:HyperLinkButton Content="更多" />
        </atom:Card.Extra>
        <StackPanel Orientation="Vertical" Spacing="3">
            <atom:TextBlock Text="卡片内容" />
            <atom:TextBlock Text="卡片内容" />
            <atom:TextBlock Text="卡片内容" />
        </StackPanel>
    </atom:Card>

    <atom:Card Header="默认尺寸卡片" SizeType="Middle" Width="300">
        <atom:Card.Extra>
            <atom:HyperLinkButton Content="更多" />
        </atom:Card.Extra>
        <StackPanel Orientation="Vertical" Spacing="3">
            <atom:TextBlock Text="卡片内容" />
            <atom:TextBlock Text="卡片内容" />
            <atom:TextBlock Text="卡片内容" />
        </StackPanel>
    </atom:Card>

    <atom:Card Header="小尺寸卡片" SizeType="Small" Width="300">
        <atom:Card.Extra>
            <atom:HyperLinkButton Content="更多" />
        </atom:Card.Extra>
        <StackPanel Orientation="Vertical" Spacing="3">
            <atom:TextBlock Text="卡片内容" />
            <atom:TextBlock Text="卡片内容" />
            <atom:TextBlock Text="卡片内容" />
        </StackPanel>
    </atom:Card>
</WrapPanel>
```

### 无边框

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/Card/Views/CardBasicShowCase.axaml:52`

Gallery key：`ExamplesContent` / item `1`

```axaml
<Border Padding="20" Background="{Binding BorderlessFrameBg}">
    <atom:Card Header="卡片标题" Width="300" StyleVariant="Borderless" HorizontalAlignment="Left">
        <atom:Card.Extra>
            <atom:HyperLinkButton Content="更多" />
        </atom:Card.Extra>
        <StackPanel Orientation="Vertical" Spacing="3">
            <atom:TextBlock Text="卡片内容" />
            <atom:TextBlock Text="卡片内容" />
            <atom:TextBlock Text="卡片内容" />
        </StackPanel>
    </atom:Card>
</Border>
```

### 简单卡片

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/Card/Views/CardBasicShowCase.axaml:69`

Gallery key：`ExamplesContent` / item `2`

```axaml
<atom:Card Width="300" HorizontalAlignment="Left">
    <StackPanel Orientation="Vertical" Spacing="3">
        <atom:TextBlock Text="卡片内容" />
        <atom:TextBlock Text="卡片内容" />
        <atom:TextBlock Text="卡片内容" />
    </StackPanel>
</atom:Card>
```

## 状态模型

Card 的内容类型状态流：

```text
Content changed
  -> inspect direct content type
  -> ContentType = Meta / Grid / Tabs / Default
  -> sync border, corner radius and body padding
```

`ContentType` 只探测直接赋给 `Content` 的对象。`CardMetaContent`、`CardGridContent` 和 `CardTabsContent` 应直接作为 Card 内容使用；把它们包裹在其他容器中时，Card 按普通内容处理。

Actions 状态流：

```text
Card.Actions changed
  -> PART_ActionPanel.Actions
  -> UniformGrid columns = action count
  -> IsActionsPanelVisible
```

加载状态：

- `IsLoading=true` 时，根模板中的 Skeleton 负责展示加载占位，并保留内容区域的主题边界。
- `IsLoading=false` 时显示实际 `Content` 和 `ContentTemplate`。

悬停和动效：

- `IsHoverable=true` 时，Card 进入 pointer over 后使用 `CardShadows`，并隐藏边框以避免边框和阴影同时强化。
- `IsMotionEnabled=true` 时启用阴影和操作区文字颜色过渡。
- 初始加载时过渡会先禁用，再在 Loaded 后启用，避免首次渲染出现不必要动画。

## 主题与 Design Token

Card 的默认视觉由根 Card 主题和多个子控件主题组成。

| 主题文件 | 职责 |
| --- | --- |
| `CardTheme.axaml` | 根模板、Header/Extra、Cover、Content、Skeleton、ActionPanel 接入和 Card 状态样式。 |
| `CardActionPanelTheme.axaml` | 底部操作区背景、边框、圆角、最小高度和文字颜色过渡。 |
| `CardActionButtonTheme.axaml` | Card 操作按钮的拉伸布局、图标尺寸和图标颜色。 |
| `CardGridContentTheme.axaml` | 栅格内容 ItemsPresenter 和 Grid items panel。 |
| `CardGridItemTheme.axaml` | 栅格项内容承载、padding、阴影和 hover 状态。 |
| `CardTabsContentTheme.axaml` | 内部 TabControl、ContentPadding 和 header edge padding。 |
| `CardMetaContentTheme.axaml` | Avatar、标题和描述的元信息布局。 |

Token 关系：

```text
SharedToken
   ↓
CardToken
   ↓
CardTheme / CardActionPanelTheme / CardGridItemTheme / CardTabsContentTheme / CardMetaContentTheme
```

根容器边框、圆角、背景和部分文字色来自 SharedToken。Header 高度、字体、padding、body padding、操作区背景、tabs margin、extra 色、卡片阴影、操作图标尺寸和 grid item 阴影来自 CardToken。

Token 来源：

CardToken 是 Card 的控件级 Token scope，描述卡片 Header、Body、Actions、Tabs、Extra、阴影、Grid item 和 action icon 的组件语义值。

CardToken 不承载以下状态：

- `Content`、`Header`、`Extra`、`Cover`、`Actions` 等实际内容。
- `ContentType`、`IsLoading`、`IsHoverable`、`IsInnerMode`、`StyleVariant` 等实例行为状态。
- `HeaderBorderThickness`、`EffectiveBorderThickness`、`EffectiveCornerRadius`、`IsActionsPanelVisible` 等内部派生状态。
- `CardGridItem.Row`、`Column`、`RowSpan`、`ColumnSpan` 等布局位置。
- `TabControl` 的 selected item、current content 或 tab collection。

## AOT 与裁剪注意事项

Card 不依赖运行时反射发现模板结构。模板协作通过固定 part 名称、强类型控件和 Avalonia property binding 完成。

资源与生命周期边界：

- `CardToken` 通过 token generator 注册，Theme 通过 `CardTokenResource` 使用。
- `CardActionButton`、`CardGridItem`、`CardMetaContent` 等是 Visual 控件，Token resource 由主题和视觉树生命周期管理。
- `Card` 到动态 `Content` 的 Size/Motion 同步使用 `BindUtils.RelayBind`，因为目标不是稳定 template part；绑定 owner 是 `_contentStateBindings`。
- `CardGridContent.PrepareCardGridItem` 的扩展 disposable 由 `_cardGridItemDisposables` 管理，并在 container clear/recycle 时释放。
- 不在 layout hot path 中读取全局资源或创建反射路径。

AOT 边界：

- 不新增字符串 path binding、`ReflectionBinding`、assembly scan、`Activator.CreateInstance(Type)` 或动态成员访问。
- API 与 Token 契约由控件文档、源码 public surface、Token 类型或生成数据维护。
- Card 的动态内容同步只使用 `AvaloniaProperty` 强类型绑定。

## 源码索引

主要源码：

- `src/AtomUI.Desktop.Controls/Card/Card.cs`：根 Card 公共 API、Actions 集合、内容类型识别、Header/Content 边框和圆角派生状态、Size/Motion 内容同步。
- `src/AtomUI.Desktop.Controls/Card/CardActionPanel.cs`：内部操作区，承载 Actions，维护 UniformGrid children，并渲染分隔线。
- `src/AtomUI.Desktop.Controls/Card/CardActionButton.cs`：Card 操作区推荐按钮类型，提供主题 key 边界。
- `src/AtomUI.Desktop.Controls/Card/CardMetaContent.cs`：头像、标题和描述内容的元信息承载控件。
- `src/AtomUI.Desktop.Controls/Card/CardGridContent.cs`：Card grid 内容的 ItemsControl，负责 Grid panel 行列定义同步和容器准备/清理。
- `src/AtomUI.Desktop.Controls/Card/CardGridItem.cs`：Grid item 容器，负责 content、row/column/span 和 hover/size/motion 状态。
- `src/AtomUI.Desktop.Controls/Card/CardTabsContent.cs`：Card tabs 内容桥接控件，负责把 Items 集合复制到内部 TabControl。
- `src/AtomUI.Desktop.Controls/Card/CardPseudoClass.cs`：Card 稳定伪类常量。
- `src/AtomUI.Desktop.Controls/Card/CardToken.cs`：Card 控件 Token。
- `src/AtomUI.Desktop.Controls/Card/Themes/*.axaml`：根主题、操作区、操作按钮、Meta、Grid、Tabs 等主题资源。

## 相关文档

- 源设计文档：`docs/controls/desktop/data-display/card/overview.md`
- 实现文档：`docs/controls/desktop/data-display/card/implementation.md`
- Token 文档：`docs/controls/desktop/data-display/card/token.md`
- 变更记录：`docs/controls/desktop/data-display/card/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/carousel/index-cn.md

# Carousel

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

Carousel 是 AtomUI 桌面控件体系中的轮播控件，用于在多个页面或媒体项之间自动或手动切换。

Carousel 不负责虚拟化列表、图片编辑器或导航路由系统。这些职责应由业务层、组合控件或更专用的 AtomUI 控件承担。

主要源码入口：

- `src/AtomUI.Desktop.Controls/Carousel`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/DataDisplay/Carousel` |
| 状态 | Stable |

## 何时使用

Carousel 的设计语言围绕控件职责、可观察状态和主题契约组织，而不是围绕模板节点组织。

| 维度 | 含义 | Carousel 中的表达 |
| --- | --- | --- |
| 产品语义 | 控件在界面中承担的稳定职责。 | Carousel 是 AtomUI 桌面控件体系中的轮播控件，用于在多个页面或媒体项之间自动或手动切换。 |
| 内容承载 | 用户数据、展示内容、集合项或操作入口如何进入控件。 | 继承 ItemsControl 的 `Items` / `ItemsSource` 与 `CarouselPage` 容器。 |
| 状态反馈 | public API、内部状态和伪类如何形成用户可感知反馈。 | selection/checked/active、loading/async、collection/filter、motion、visual option。 |
| 主题语义 | ControlTheme、SharedToken、控件 Token 和模板绑定如何表达视觉。 | Carousel Token + ControlTheme。 |

## 公共 API

Carousel 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。

核心 public surface 按语义分组维护：

| 契约组 | 代表成员 | 维护含义 |
| --- | --- | --- |
| 内容与数据 | `Items`、`ItemsSource`、`CarouselPage` | 继承 ItemsControl 的页面集合入口和轮播页容器。 |
| 选择与集合 | `IsSelected`、`PageInEasing`、`PageOutEasing`、`PageTransitionDuration` | 维护选择、展开、过滤、分页、分组或集合状态。 |
| 交互与状态 | `IsAutoPlay`、`IsInfinite`、`IsMotionEnabled`、`IsShowNavButtons`、`IsShowPagination`、`IsShowTransitionProgress`、`IsSwipeEnabled` | 表达用户可观察状态、可用性、清除、加载或反馈语义。 |
| 视觉与布局 | `PaginationPosition` | 影响尺寸、位置、颜色、形状、密度和模板视觉变量。 |
| 动效与异步 | `AutoPlaySpeed`、`TransitionEffect` | 约束动效开关、异步加载、播放速度、超时和任务边界。 |

当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。

主要公开类型与枚举：

- 类型：`Carousel`、`CarouselNavButton`、`CarouselPage`、`CarouselPageIndicator`、`CarouselPagination`、`VirtualizingCarouselPanel`。
- 枚举：`CarouselPaginationPosition`、`CarouselTransitionEffect`。

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_Frame` | `?` | 承载根视觉、边框、背景或尺寸基线。 |
| `PART_ItemsPresenter` | `?` | 展示用户内容、文本、图标或模板化数据。 |
| `PART_NextButton` | `?` | 承载用户触发入口、导航或关闭动作。 |
| `PART_Pagination` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_PreviousButton` | `?` | 承载用户触发入口、导航或关闭动作。 |
| `PART_ScrollViewer` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |

当前未抽取到控件专属伪类；主题主要依赖 Avalonia 标准伪类、模板绑定和内部 StyledProperty。

## 事件与命令

Carousel 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。
当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/Carousel/Views/CarouselShowCase.axaml:45`

Gallery key：`ExamplesContent` / item `0`

```axaml
<atom:Carousel SelectedIndex="2">
    <atom:CarouselPage>1</atom:CarouselPage>
    <atom:CarouselPage>2</atom:CarouselPage>
    <atom:CarouselPage>3</atom:CarouselPage>
    <atom:CarouselPage>4</atom:CarouselPage>
</atom:Carousel>
```

### 卡片形态位置

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/Carousel/Views/CarouselShowCase.axaml:62`

Gallery key：`ExamplesContent` / item `1`

```axaml
<StackPanel Orientation="Vertical" Spacing="20">
    <StackPanel Orientation="Horizontal" Spacing="5">
        <atom:TextBlock VerticalAlignment="Center" Text="Pagination Position:" />
        <atom:OptionButtonGroup ButtonStyle="Outline"
                                Name="PositionOptionGroup"
                                OptionCheckedChanged="HandlePositionOptionChanged">
            <atom:OptionButton Content="顶部" />
            <atom:OptionButton IsChecked="True" Content="底部" />
            <atom:OptionButton Content="左侧" />
            <atom:OptionButton Content="右侧" />
        </atom:OptionButtonGroup>
    </StackPanel>

    <atom:Carousel PaginationPosition="{Binding PaginationPosition}">
        <atom:CarouselPage>1</atom:CarouselPage>
        <atom:CarouselPage>2</atom:CarouselPage>
        <atom:CarouselPage>3</atom:CarouselPage>
        <atom:CarouselPage>4</atom:CarouselPage>
    </atom:Carousel>

</StackPanel>
```

### 自动滚动

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/Carousel/Views/CarouselShowCase.axaml:92`

Gallery key：`ExamplesContent` / item `2`

```axaml
<atom:Carousel IsAutoPlay="True" IsInfinite="False">
    <atom:CarouselPage>1</atom:CarouselPage>
    <atom:CarouselPage>2</atom:CarouselPage>
    <atom:CarouselPage>3</atom:CarouselPage>
    <atom:CarouselPage>4</atom:CarouselPage>
</atom:Carousel>
```

### 淡入切换

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/Carousel/Views/CarouselShowCase.axaml:107`

Gallery key：`ExamplesContent` / item `3`

```axaml
<atom:Carousel TransitionEffect="Fade">
    <atom:CarouselPage Background="#B3001B">1</atom:CarouselPage>
    <atom:CarouselPage Background="#255C99">2</atom:CarouselPage>
    <atom:CarouselPage Background="#262626">3</atom:CarouselPage>
    <atom:CarouselPage Background="#CCAD8F">4</atom:CarouselPage>
</atom:Carousel>
```

## 状态模型

Carousel 的状态流按以下路径收敛：

```text
Public API / inherited command / item source / user input
  -> 控件实例状态
  -> effective state / pseudo-class / template property
  -> ControlTheme selector / presenter / renderer
  -> Gallery 可观察行为
```

状态维护规则：

- Disabled 或不可交互状态优先屏蔽 pointer、keyboard、motion 和提交类反馈。
- selection/checked/active、loading/async、collection/filter、motion、visual option 状态由控件实例或明确的数据 owner 推导，不能在 template part 之间双向竞争。
- 模板重套用时必须把 public API 对应状态回放到新的 part、伪类和主题变量。
- 集合、弹层、异步、动效或窗口相关状态必须能处理 reset、close、cancel、detach 和 owner 释放。

## 主题与 Design Token

Carousel 的视觉模型由控件模板、ControlTheme、SharedToken 和必要的控件 Token 共同构成。

| 主题文件 | 职责 |
| --- | --- |
| `CarouselNavButtonTheme.axaml` | 定义局部操作入口、按钮或 handle 的状态视觉。 |
| `CarouselPageIndicatorTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `CarouselPageTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `CarouselPaginationTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `CarouselTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |

Carousel 使用 `CarouselToken` 作为控件 Token scope。Token 只表达组件视觉语义，不承载 selection/checked/active、loading/async、collection/filter、motion、visual option 运行时状态。

主题维护规则：

- 不删除或重命名已经稳定的 ControlTheme key、template part、伪类和资源 key。
- 不把可由 AXAML 表达的模板状态迁移为 C# 动态创建视觉。
- 不把 hover、pressed、selected、expanded、loading、filter、popup open 等运行时状态写入 Token。
- Browser 或平台特化主题必须保持同一 API 的语义一致。

Token 来源：

Carousel Token 只表达组件级视觉变量，例如尺寸、间距、颜色、圆角、阴影、图标尺寸和弹层边界。Token 不承载运行时选择、展开、加载、错误、上传任务、过滤条件或业务状态。

当前 Token scope：

- `CarouselToken`，scope id 为 `Carousel`，源码位于 `src/AtomUI.Desktop.Controls/Carousel/CarouselToken.cs`。

## AOT 与裁剪注意事项

资源和 AOT 约束：

- 不通过运行时反射扫描 public API、Token 或 Gallery 示例数据。
- 不把可静态声明的模板结构迁移到 C# 动态创建。
- 异步加载、上传、弹层和窗口生命周期必须能取消或释放。
- 缓存对象必须与控件、窗口、弹层或数据 owner 生命周期一致。
- Source generator 生成文件不手工编辑；需要修改时改输入源或 generator。

性能边界：

- 控件应优先复用 Avalonia 原生虚拟化、模板绑定和资源系统。
- 避免为每次状态变化创建不必要的视觉对象、订阅或动画对象。
- 隐藏祖先下的 Carousel 不推进 autoplay timer 或选中页 progress animation。
- 大集合控件必须保证 container recycle 后不会泄漏旧 item 状态。

## 源码索引

主要源码文件：

- `src/AtomUI.Desktop.Controls/Carousel/Carousel.cs`
- `src/AtomUI.Desktop.Controls/Carousel/CarouselEnums.cs`
- `src/AtomUI.Desktop.Controls/Carousel/CarouselNavButton.cs`
- `src/AtomUI.Desktop.Controls/Carousel/CarouselPage.cs`
- `src/AtomUI.Desktop.Controls/Carousel/CarouselPageIndicator.cs`
- `src/AtomUI.Desktop.Controls/Carousel/CarouselPagination.cs`
- `src/AtomUI.Desktop.Controls/Carousel/CarouselToken.cs`
- `src/AtomUI.Desktop.Controls/Carousel/Themes/CarouselNavButtonTheme.axaml`
- `src/AtomUI.Desktop.Controls/Carousel/Themes/CarouselPageIndicatorTheme.axaml`
- `src/AtomUI.Desktop.Controls/Carousel/Themes/CarouselPageTheme.axaml`
- `src/AtomUI.Desktop.Controls/Carousel/Themes/CarouselPaginationTheme.axaml`
- `src/AtomUI.Desktop.Controls/Carousel/Themes/CarouselTheme.axaml`
- `src/AtomUI.Desktop.Controls/Carousel/VirtualizingCarouselPanel.cs`

职责边界：

- 控件主文件保留 public/protected API、Avalonia 属性注册、事件和主要生命周期入口。
- Theme 文件负责静态视觉结构、template part、selector 和资源绑定。
- Token 文件只提供组件视觉变量，不保存实例状态。
- Gallery 文件只展示用法和示例，不作为运行时逻辑 owner。

## 相关文档

- 源设计文档：`docs/controls/desktop/data-display/carousel/overview.md`
- 实现文档：`docs/controls/desktop/data-display/carousel/implementation.md`
- Token 文档：`docs/controls/desktop/data-display/carousel/token.md`
- 变更记录：`docs/controls/desktop/data-display/carousel/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/collapse/index-cn.md

# Collapse

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

Collapse 是 AtomUI 桌面控件体系中的折叠面板控件，用于在多个可展开内容区之间展示和隐藏内容。

Collapse 不负责树控件、菜单或 Tabs 内容切换。这些职责应由业务层、组合控件或更专用的 AtomUI 控件承担。

主要源码入口：

- `src/AtomUI.Desktop.Controls/Collapse`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/DataDisplay/Collapse` |
| 状态 | Stable |

## 何时使用

Collapse 的设计语言围绕控件职责、可观察状态和主题契约组织，而不是围绕模板节点组织。

| 维度 | 含义 | Collapse 中的表达 |
| --- | --- | --- |
| 产品语义 | 控件在界面中承担的稳定职责。 | Collapse 是 AtomUI 桌面控件体系中的折叠面板控件，用于在多个可展开内容区之间展示和隐藏内容。 |
| 内容承载 | 用户数据、展示内容、集合项或操作入口如何进入控件。 | `AddOnContent`、`AddOnContentTemplate`、`ContentPadding`、`ExpandIcon`、`ExpandIconPosition`、`HeaderPadding`、`IsShowExpandIcon`、`ItemContentPadding` 等 9 项。 |
| 状态反馈 | public API、内部状态和伪类如何形成用户可感知反馈。 | selection/checked/active、motion、visual option。 |
| 主题语义 | ControlTheme、SharedToken、控件 Token 和模板绑定如何表达视觉。 | Collapse Token + ControlTheme。 |

## 公共 API

Collapse 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。

核心 public surface 按语义分组维护：

| 契约组 | 代表成员 | 维护含义 |
| --- | --- | --- |
| 内容与数据 | `AddOnContent`、`AddOnContentTemplate`、`ContentPadding`、`ExpandIcon`、`ExpandIconPosition`、`HeaderPadding`、`IsShowExpandIcon`、`ItemContentPadding`、`ItemHeaderPadding` | 定义控件展示内容、输入数据、模板或业务对象入口。 |
| 选择与集合 | `IsSelected` | 维护选择、展开、过滤、分页、分组或集合状态。 |
| 交互与状态 | `IsAccordion`、`IsBorderless`、`IsGhostStyle`、`IsMotionEnabled` | 表达用户可观察状态、可用性、清除、加载或反馈语义。 |
| 视觉与布局 | `SizeType` | 影响尺寸、位置、颜色、形状、密度和模板视觉变量。 |
| 其他稳定入口 | `TriggerType` | 保留为 public surface，变更前需确认 Gallery 和用户 XAML 依赖。 |

当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。

主要公开类型与枚举：

- 类型：`Collapse`、`CollapseItem`。
- 枚举：`CollapseExpandIconPosition`、`CollapseTriggerType`。

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_AddOnContentPresenter` | `?` | 展示用户内容、文本、图标或模板化数据。 |
| `PART_ContentMotionActor` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_ContentPresenter` | `?` | 展示用户内容、文本、图标或模板化数据。 |
| `PART_ExpandButton` | `?` | 承载用户触发入口、导航或关闭动作。 |
| `PART_Frame` | `?` | 承载根视觉、边框、背景或尺寸基线。 |
| `PART_HeaderDecorator` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_HeaderPresenter` | `?` | 展示用户内容、文本、图标或模板化数据。 |
| `PART_ItemsPresenter` | `ItemsPresenter` | 展示用户内容、文本、图标或模板化数据。 |
| `PART_MainLayout` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |

当前未抽取到控件专属伪类；主题主要依赖 Avalonia 标准伪类、模板绑定和内部 StyledProperty。

## 事件与命令

Collapse 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。
当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 无边框

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/Collapse/Views/CollapseAppearanceShowCase.axaml:12`

Gallery key：`ExamplesContent` / item `0`

```axaml
<atom:Collapse IsBorderless="True">
    <atom:CollapseItem Header="这是面板标题 1">
        <atom:TextBlock TextWrapping="Wrap" Text="狗是一种被驯养的动物。它以忠诚和可靠著称，在世界各地许多家庭中都是受欢迎的成员。" />
    </atom:CollapseItem>
    <atom:CollapseItem Header="这是面板标题 2">
        <atom:TextBlock TextWrapping="Wrap" Text="狗是一种被驯养的动物。它以忠诚和可靠著称，在世界各地许多家庭中都是受欢迎的成员。" />
    </atom:CollapseItem>
    <atom:CollapseItem Header="这是面板标题 3">
        <atom:TextBlock TextWrapping="Wrap" Text="狗是一种被驯养的动物。它以忠诚和可靠著称，在世界各地许多家庭中都是受欢迎的成员。" />
    </atom:CollapseItem>
</atom:Collapse>
```

### 幽灵折叠面板

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/Collapse/Views/CollapseAppearanceShowCase.axaml:28`

Gallery key：`ExamplesContent` / item `1`

```axaml
<atom:Collapse IsGhostStyle="True">
    <atom:CollapseItem Header="这是面板标题 1">
        <atom:TextBlock TextWrapping="Wrap" Text="狗是一种被驯养的动物。它以忠诚和可靠著称，在世界各地许多家庭中都是受欢迎的成员。" />
    </atom:CollapseItem>
    <atom:CollapseItem Header="这是面板标题 2">
        <atom:TextBlock TextWrapping="Wrap" Text="狗是一种被驯养的动物。它以忠诚和可靠著称，在世界各地许多家庭中都是受欢迎的成员。" />
    </atom:CollapseItem>
    <atom:CollapseItem Header="这是面板标题 3">
        <atom:TextBlock TextWrapping="Wrap" Text="狗是一种被驯养的动物。它以忠诚和可靠著称，在世界各地许多家庭中都是受欢迎的成员。" />
    </atom:CollapseItem>
</atom:Collapse>
```

### 自定义标题和内容间距

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/Collapse/Views/CollapseAppearanceShowCase.axaml:44`

Gallery key：`ExamplesContent` / item `2`

```axaml
<StackPanel Orientation="Vertical" Spacing="20">
    <atom:Collapse ItemHeaderPadding="5" ItemContentPadding="5">
        <atom:CollapseItem Header="这是面板标题 1">
            <atom:TextBlock TextWrapping="Wrap" Text="狗是一种被驯养的动物。它以忠诚和可靠著称，在世界各地许多家庭中都是受欢迎的成员。" />
        </atom:CollapseItem>
        <atom:CollapseItem Header="这是面板标题 2">
            <atom:TextBlock TextWrapping="Wrap" Text="狗是一种被驯养的动物。它以忠诚和可靠著称，在世界各地许多家庭中都是受欢迎的成员。" />
        </atom:CollapseItem>
        <atom:CollapseItem Header="这是面板标题 3">
            <atom:TextBlock TextWrapping="Wrap" Text="狗是一种被驯养的动物。它以忠诚和可靠著称，在世界各地许多家庭中都是受欢迎的成员。" />
        </atom:CollapseItem>
    </atom:Collapse>

    <atom:Collapse ItemHeaderPadding="0" ItemContentPadding="0" IsGhostStyle="True">
        <atom:CollapseItem Header="这是面板标题 1">
            <atom:TextBlock TextWrapping="Wrap" Text="狗是一种被驯养的动物。它以忠诚和可靠著称，在世界各地许多家庭中都是受欢迎的成员。" />
        </atom:CollapseItem>
        <atom:CollapseItem Header="这是面板标题 2">
            <atom:TextBlock TextWrapping="Wrap" Text="狗是一种被驯养的动物。它以忠诚和可靠著称，在世界各地许多家庭中都是受欢迎的成员。" />
        </atom:CollapseItem>
        <atom:CollapseItem Header="这是面板标题 3">
            <atom:TextBlock TextWrapping="Wrap" Text="狗是一种被驯养的动物。它以忠诚和可靠著称，在世界各地许多家庭中都是受欢迎的成员。" />
        </atom:CollapseItem>
    </atom:Collapse>
</StackPanel>
```

### 折叠面板

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/Collapse/Views/CollapseBasicShowCase.axaml:12`

Gallery key：`ExamplesContent` / item `0`

```axaml
<atom:Collapse>
    <atom:CollapseItem Header="这是面板标题 1">
        <atom:TextBlock TextWrapping="Wrap" Text="狗是一种被驯养的动物。它以忠诚和可靠著称，在世界各地许多家庭中都是受欢迎的成员。" />
    </atom:CollapseItem>
    <atom:CollapseItem Header="这是面板标题 2">
        <atom:TextBlock TextWrapping="Wrap" Text="狗是一种被驯养的动物。它以忠诚和可靠著称，在世界各地许多家庭中都是受欢迎的成员。" />
    </atom:CollapseItem>
    <atom:CollapseItem Header="这是面板标题 3">
        <atom:TextBlock TextWrapping="Wrap" Text="狗是一种被驯养的动物。它以忠诚和可靠著称，在世界各地许多家庭中都是受欢迎的成员。" />
    </atom:CollapseItem>
</atom:Collapse>
```

## 状态模型

Collapse 的状态流按以下路径收敛：

```text
Public API / inherited command / item source / user input
  -> 控件实例状态
  -> effective state / pseudo-class / template property
  -> ControlTheme selector / presenter / renderer
  -> Gallery 可观察行为
```

`Collapse` 继续使用 Avalonia `SelectingItemsControl` 的 selection model 作为唯一展开状态 owner，`CollapseItem.IsSelected` 是该状态投影到容器后的公开绑定入口。控件不得维护 active-key 集合、当前展开项缓存或另一套展开状态。

状态维护规则：

- 普通模式使用 `Multiple | Toggle`：每个 item 可独立展开和收起。
- 手风琴模式使用 `Single | Toggle`：打开目标项时关闭旧项，点击当前项时允许全部收起。
- 普通模式切换到手风琴模式时，按视觉索引保留第一个已展开项，保证切换后的单一展开状态确定且稳定。
- Header、Icon、keyboard 和 pointer 输入最终进入同一个 selection 操作，不在输入处理器中直接维护展开状态。
- Disabled 或不可交互状态优先屏蔽 pointer、keyboard 和 motion，不改变 selection。
- 内容可见性、箭头方向和动效目标只从 `IsSelected` 派生；模板节点之间不得双向同步展开状态。
- 模板重套用、items reset/replace/clear 和模式切换后必须保持 selection model、容器与内容视觉一致。

普通模式与手风琴模式使用 Core 共用内容展开机制：
内容按正常尺寸排版，通过高度和透明度呈现收放，反转从当前帧接续。手风琴在 selection 提交时同步产生旧项收起和
新项展开目标，两项使用同一进度交换空间，互斥不依赖动画完成事件。关闭动效及模板生命周期边界直接投影当前状态，
释放仅限该机制拥有的动画和内部布局控制，保留自定义尺寸与变换。
共享设计来源：`docs/architecture/systems/control-infrastructure/content-expansion.md`。

## 主题与 Design Token

Collapse 的视觉模型由控件模板、ControlTheme、SharedToken 和必要的控件 Token 共同构成。

| 主题文件 | 职责 |
| --- | --- |
| `CollapseItemTheme.axaml` | 定义集合项、容器项或局部单元的状态视觉。 |
| `CollapseTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |

Collapse 使用 `CollapseToken` 作为控件 Token scope。Token 只表达组件视觉语义，不承载 selection/checked/active、motion、visual option 运行时状态。

Collapse 的分隔线采用结构化所有权：

- `PART_Frame` 绘制外框、圆角并裁剪整体内容。
- 非末 `CollapseItem` 的 item shell 固定绘制底部分隔线。
- 默认 bordered 模式下，`PART_ContentFrame` 固定绘制内容顶部边线。
- Borderless 模式保留 item 间分隔线，但不绘制外框和内容顶部边线。
- Ghost 模式不绘制外框、item 分隔线和内容顶部边线。
- 分隔线厚度不得依赖 `IsSelected`、动效进行状态或动效完成时机。

主题维护规则：

- 不删除或重命名已经稳定的 ControlTheme key、template part、伪类和资源 key。
- 不把可由 AXAML 表达的模板状态迁移为 C# 动态创建视觉。
- 不把 hover、pressed、selected、expanded、loading、filter、popup open 等运行时状态写入 Token。
- Browser 或平台特化主题必须保持同一 API 的语义一致。

Token 来源：

Collapse Token 只表达组件级视觉变量，例如尺寸、间距、颜色、圆角、阴影、图标尺寸和弹层边界。Token 不承载运行时选择、展开、加载、错误、上传任务、过滤条件或业务状态。

当前 Token scope：

- `CollapseToken`，scope id 为 `Collapse`，源码位于 `src/AtomUI.Desktop.Controls/Collapse/CollapseToken.cs`。

## AOT 与裁剪注意事项

资源和 AOT 约束：

- 不通过运行时反射扫描 public API、Token 或 Gallery 示例数据。
- 不把可静态声明的模板结构迁移到 C# 动态创建。
- 异步加载、上传、弹层和窗口生命周期必须能取消或释放。
- 缓存对象必须与控件、窗口、弹层或数据 owner 生命周期一致。
- Source generator 生成文件不手工编辑；需要修改时改输入源或 generator。

性能边界：

- 单项展开不得遍历全部容器重算边框。
- 模式切换只处理当前 selection indexes；item 位置只在集合结构或容器索引变化时更新。
- 边框、背景、padding 和 icon placement 使用 AXAML、selector 和 template binding 表达。
- 不新增运行时反射、字符串 binding、全局事件、timer、active-key 缓存或长期状态对象。
- template reapply 必须解绑旧展开按钮；detach 和新动效开始前必须取消旧 content motion。

## 源码索引

主要源码文件：

- `src/AtomUI.Desktop.Controls/Collapse/Collapse.cs`
- `src/AtomUI.Desktop.Controls/Collapse/CollapseItem.cs`
- `src/AtomUI.Desktop.Controls/Collapse/CollapseToken.cs`
- `src/AtomUI.Desktop.Controls/Collapse/ICollapseItemData.cs`
- `src/AtomUI.Core/MotionScene/ContentExpansionAnimator.cs`：三个控件共用的内容测量、进度插值和执行资源 owner。
- `src/AtomUI.Desktop.Controls/Collapse/Themes/CollapseItemTheme.axaml`
- `src/AtomUI.Desktop.Controls/Collapse/Themes/CollapseTheme.axaml`

职责边界：

- 控件主文件保留 public/protected API、Avalonia 属性注册、事件和主要生命周期入口。
- Theme 文件负责静态视觉结构、template part、selector 和资源绑定。
- Token 文件只提供组件视觉变量，不保存实例状态。
- Gallery 文件只展示用法和示例，不作为运行时逻辑 owner。

## 相关文档

- 源设计文档：`docs/controls/desktop/data-display/collapse/overview.md`
- 实现文档：`docs/controls/desktop/data-display/collapse/implementation.md`
- Token 文档：`docs/controls/desktop/data-display/collapse/token.md`
- 变更记录：`docs/controls/desktop/data-display/collapse/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/data-grid/index-cn.md

# DataGrid

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

DataGrid 是 AtomUI 桌面控件体系中的数据表格控件，用于列模型、行选择、排序、过滤、分组、编辑、冻结列、连续虚拟滚动和分页展示。本地集合与远端服务通过同一个范围数据源契约接入。

DataGrid 不负责简单列表、树视图或业务数据访问层。这些职责应由业务层、组合控件或更专用的 AtomUI 控件承担。

主要源码入口：

- `src/AtomUI.Desktop.Controls.DataGrid`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls.DataGrid` |
| .NET 命名空间 | `AtomUI.Desktop.Controls.DataGrid` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/DataDisplay/DataGrid` |
| 状态 | Stable |

## 何时使用

DataGrid 的设计语言围绕控件职责、可观察状态和主题契约组织，而不是围绕模板节点组织。

| 维度 | 含义 | DataGrid 中的表达 |
| --- | --- | --- |
| 产品语义 | 控件在界面中承担的稳定职责。 | DataGrid 以不可变 Query 描述数据语义，以 Range Source 支持本地或远端数据，并只物化当前视口。 |
| 内容承载 | 用户数据、展示内容、集合项或操作入口如何进入控件。 | `ItemsSource : IDataGridSource?`、`Query`、`GroupExpansion`、列 `FieldId`、`CellTemplate` 和 `CellEditingTemplate`。 |
| 状态反馈 | public API、内部状态和伪类如何形成用户可感知反馈。 | AppliedQuery、LoadState、Selection、CurrentRowKey、sort/filter/group projection、motion 和 visual option。 |
| 主题语义 | ControlTheme、SharedToken、Control Own Token 和模板绑定如何表达视觉。 | DataGrid Token + ControlTheme。 |

## 公共 API

DataGrid 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。

核心 public surface 按语义分组维护：

| 契约组 | 代表成员 | 维护含义 |
| --- | --- | --- |
| 内容与数据 | `ItemsSource`、`Query`、`AppliedQuery`、`GroupExpansion`、`TotalItemCount`、`TotalEntryCount`、`AutoGenerateColumns`、`CellTemplate`、`CellEditingTemplate` | 定义数据输入、查询、范围 presentation、模板和业务对象入口；`ItemsSource` 的类型是 `IDataGridSource?`。 |
| 选择与当前项 | `Selection`、`CurrentRowKey`、`SelectionChanged`、`ClipboardCopyMode` | 以稳定 row key、query scope 和 index interval 维护可跨 range/page 的声明式状态。 |
| 交互与加载 | `CanUserFilterColumns`、`CanUserReorderRows`、`CanUserResizeColumns`、`CanUserSortColumns`、`QueryChanged`、`LoadState`、`LoadError`、`IsDataStale`、`Reload()` | 表达用户查询意图、异步生命周期、错误与可提交能力。 |
| 视觉与布局 | `BottomPaginationAlign`、`ColumnWidth`、`HorizontalAlignment`、`HorizontalScrollBarVisibility`、`MaxColumnWidth`、`MinColumnWidth`、`RowHeight`、`SeparatorBrush`、`SizeType`、`SublevelIndent` 等 14 项 | 影响尺寸、位置、颜色、形状、密度和模板视觉变量。 |
| 列查询契约 | `FieldId`、`CanUserSort`、`SupportedSortDirections`、只读 `SortState`、过滤候选展示属性 | 让列声明协议字段与能力覆盖，显示 Binding 不参与查询 identity。 |
| 其他稳定入口 | `CellTheme`、`CustomOperatingIndicator`、`EmptyIndicator`、`Footer`、`FormatString`、`GridLinesVisibility`、`Level`、`Maximum`、`Minimum` 等 | 保留非数据架构 public surface，变更前需确认 Gallery 和用户 XAML 依赖。 |

`ItemsSource` 只接受 `IDataGridSource?`，不是旧集合型 `IEnumerable` 入口。本地集合使用 `DataGridLocalSource.Create(...)`
建立强类型 range source，远端实现使用同一接口翻译 Query 并按请求范围返回数据；DataGrid 不拥有或 dispose 外部 Source。

稳定事件包括 `QueryChanged`、`SelectionChanged`、`RowReordering` 和 `RowReordered`。`QueryChanged` 是不可取消的观察事件，在 Query 的内存提交与视觉投影之后、对应 Source 请求之前触发；其他事件的参数和触发顺序同样属于契约。

行拖动重排由 `CanUserReorderRows`、`DataGridRowReorderColumn`、`RowReordering`、`RowReordered` 和可选
`IDataGridMovableSource` 共同表达。DataGrid 使用 source row key 与目标 before/after key 提交移动，不把 view、window 或 display
索引解释为 Source 集合索引。Source 不支持移动、当前 Query 语义禁止移动、编辑未能提交或 snapshot 失效时，handle 不进入
可提交状态，也不发布 `RowReordered`。

分页公共契约由 `PageSize`、`PaginationVisibility`、`TopPaginationAlign`、`BottomPaginationAlign` 和
`IsHideOnSinglePage` 共同表达。`PageSize=0` 表示连续模式；非零值形成 `DataGridPageRequest`，其中 data start 使用 long、单页
count 使用 int。分页总数只来自已提交 Source result 的 `TotalDataCount`，顶部和底部 Pagination 只投影同一份 applied page
state。翻页成功后垂直 offset 归零；失败时页码、rows、totals、scrollbar 和 Query 共同回退到最后成功 presentation。

列宽公共契约由 `DataGrid.ColumnWidth`、`DataGridColumn.Width`、控件级与列级最小/最大宽度，以及
`DataGridLengthUnitType` 共同表达。`ColumnWidth` 默认为 `Auto`；单列可以使用 `Pixel`、`Auto`、
`SizeToHeader`、`SizeToCells` 或 `Star`。内容驱动模式先形成期望宽度，star 模式再按权重分配有限列视口中的
剩余空间。列宽状态与统一分配算法由 `DataGrid` 持有，普通表头、分组表头和 rows/cells presenter 只提供当前
布局可证明的有限宽度或内容测量结果。空数据时列宽求解不能依赖已隐藏的 rows presenter，完整契约见
[DataGrid 列宽分配设计](column-sizing-design.md)。

列过滤候选项与过滤条件分离。列 `Filters` 只负责菜单或树形弹层中的候选内容，可以绑定 ViewModel 集合或数据库返回的
DTO；`DataGrid.Query.Filters` 是已应用条件的唯一 owner。Flyout checked state、FilterIndicator 激活态与 Source request 都从
Query 投影，用户确认后一次性生成新的不可变 Query。过滤 DTO 的显示成员可以使用 generated data member accessor；Query 中的
值则转换为确定性的 `DataGridScalar`，不保留可变 object、predicate 或反射 path。

过滤交互模式使用显式枚举表达：`FilterPresenterMode` 表达菜单或树形弹层，`FilterSelectionMode` 表达单选或多选，`FilterApplyMode` 表达确认、关闭或选择变化时应用过滤。枚举状态比布尔开关更适合作为长期 public API，因为它能把展示方式、选择方式和提交时机拆成三个正交状态，避免布尔组合在状态流中产生歧义。

主要公开类型与枚举：

- 类型：`DataGrid`、`DataGridQuery`、`IDataGridSource`、`DataGridSourceSchema`、`DataGridFieldSchema`、`DataGridFieldDisplayAccessor`、`DataGridRange`、`DataGridPageRequest`、`DataGridRangeResult`、`DataGridSelectionState`、`DataGridColumn`、`DataGridRow` 和 `DataGridCell` 等。
- 枚举：`DataGridSortDirection`、`DataGridSortUpdateMode`、`DataGridLoadState`、`DataGridSourceEntryKind`、`DataGridClipboardCopyMode`、`DataGridEditingUnit`、`DataGridLengthUnitType`、`DataGridPaginationVisibility` 和 `DataGridRowDetailsVisibilityMode` 等。

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_Ascending` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_BottomGridLine` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_BottomPagination` | `Pagination` | 投影 applied page state 并转发底部翻页意图。 |
| `PART_ColumnHeadersPresenter` | `DataGridColumnHeadersPresenter` | 测量普通列头，并在空数据布局中提供有限列视口宽度。 |
| `PART_ContentFrame` | `?` | 承载根视觉、边框、背景或尺寸基线。 |
| `PART_ContentPresenter` | `?` | 展示用户内容、文本、图标或模板化数据。 |
| `PART_Descending` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_FocusVisual` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_Frame` | `?` | 承载根视觉、边框、背景或尺寸基线。 |
| `PART_GroupColumnHeadersPresenter` | `DataGridGroupColumnHeadersPresenter` | 测量分组列头，并与普通列头共享列宽输入契约。 |
| `PART_HeaderPresenter` | `?` | 展示用户内容、文本、图标或模板化数据。 |
| `PART_HorizontalIndicator` | `?` | 展示指示器、进度、分页或状态反馈。 |
| `PART_IndicatorIconButton` | `?` | 承载用户触发入口、导航或关闭动作。 |
| `PART_ItemsPresenter` | `?` | 展示用户内容、文本、图标或模板化数据。 |
| `PART_RightGridLine` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_RootLayout` | `?` | 承载根视觉、边框、背景或尺寸基线。 |
| `PART_RowPresenter` | `DataGridRowsPresenter` | 承载已物化行；空数据时保持隐藏，不作为 star 求解的必要前置。 |
| `PART_SortIndicator` | `?` | 展示指示器、进度、分页或状态反馈。 |
| `PART_TopPagination` | `Pagination` | 投影 applied page state 并转发顶部翻页意图。 |
| `PART_VerticalIndicator` | `?` | 展示指示器、进度、分页或状态反馈。 |
| `PART_VerticalSeparator` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |

控件专属或内部伪类包括 `DataGridPseudoClass.EmptyColumns`、`DataGridPseudoClass.EmptyRows`、`DataGridPseudoClass.FirstColumnHeader`、`DataGridPseudoClass.GroupItemLeaf`、`DataGridPseudoClass.LastColumnHeader`、`DataGridPseudoClass.MiddleColumnHeader`、`DataGridPseudoClass.RowHover`、`EmptyColumns=:empty-columns`、`EmptyRows=:empty-rows`、`FirstColumnHeader=:first-column-header`、`GroupItemLeaf=:is-leaf-item`、`LastColumnHeader=:last-column-header` 等 14 项。这些伪类属于主题 selector 可观察契约，不能在未同步主题和 Gallery 的情况下重命名或删除。

## 事件与命令

DataGrid 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。
稳定事件包括 `QueryChanged`、`SelectionChanged`、`RowReordering` 和 `RowReordered`。`QueryChanged` 是不可取消的观察事件，在 Query 的内存提交与视觉投影之后、对应 Source 请求之前触发；其他事件的参数和触发顺序同样属于契约。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

- `controlgallery/AtomUIGallery/ShowCases/DataDisplay/DataGrid/Views/DataGridShowCase.axaml`

## 状态模型

DataGrid 的状态流按以下路径收敛：

```text
Public API / inherited command / Source invalidation / user input
  -> 控件实例状态
  -> effective state / pseudo-class / template property
  -> ControlTheme selector / presenter / renderer
  -> Gallery 可观察行为
```

状态维护规则：

- Disabled 或不可交互状态优先屏蔽 pointer、keyboard、motion 和提交类反馈。
- Query、Selection、current、loading、motion 和 visual option 状态由 DataGrid 或明确 Source capability 单向推导，不能在 template part 之间双向竞争。
- 排序、过滤和分组只由 `Query` 拥有；列、Header、Cell 和 Flyout 只投影相应字段状态。
- `Filters` 候选项替换、reset 或 clear 时可以重新物化 Flyout 内容，但不能直接改变已应用 Query；用户确认或显式 API 才提交新的 Query。
- 分页状态以 applied PageRequest 和 `TotalItemCount` 为 owner；上下 Pagination 不能互相覆盖，也不能在模板重建时反向重置 Query 或 Source。
- Loading 没有可展示的已提交 presentation，实际挂起时驱动 Spin；Refreshing 保持旧 presentation 的几何与完整不透明度，
  不自动启动 Spin。两种状态都禁止 edit/delete/move，成功时原子交换，失败时完整回退。
- 连续滚轮、惯性或 scrollbar thumb 输入只保留最新有效 `DesiredViewport`。新目标先接管仍需要的 block，再使旧视口 scope
  失效；不再被任何有效 scope 使用的排队或执行中请求立即收到取消，旧 prefetch 不能排在新 visible range 之前。
- 同步 Source 与 cache hit 在当前调用中直接进入 Ready，不发布瞬时 Loading/Refreshing；调用方显式设置 `IsOperating=true`
  时仍可在任意 LoadState 显示 Spin。
- 行拖动状态以当前 `DataGrid` 的单一拖拽会话为 owner；handle 和 RowsPresenter 只投影输入与 ghost row，不能保存
  跨 DataGrid 共享的静态拖拽状态。Pointer capture、源 row key、Source snapshot 和目标邻接 key 必须属于同一个会话。
- `RowReordering` 在超过拖动阈值后且创建 ghost row 前触发一次；事件取消或事件回调改变 DataGrid、源行、
  Source、Query、snapshot 或移动能力时，本次 Pointer 会话保持取消状态，不得在后续移动帧重复开始。
- `RowReordered` 只在 movable Source 成功提交顺序变化，并且 ghost、capture、动画与会话状态全部清理后触发。
- 模板重套用时必须把 public API 对应状态回放到新的 part、伪类和主题变量。
- 集合、弹层、异步、动效或窗口相关状态必须能处理 reset、close、cancel、detach 和 owner 释放。

## 主题与 Design Token

DataGrid 的视觉模型由控件模板、ControlTheme、SharedToken 和 DataGrid Own Token 共同构成。

| 主题文件 | 职责 |
| --- | --- |
| `DataGridCellTheme.axaml` | 定义集合项、容器项或局部单元的状态视觉。 |
| `DataGridColumnGroupHeaderTheme.axaml` | 定义集合项、容器项或局部单元的状态视觉。 |
| `DataGridColumnHeaderTheme.axaml` | 定义集合项、容器项或局部单元的状态视觉。 |
| `DataGridHeaderViewItemTheme.axaml` | 定义集合项、容器项或局部单元的状态视觉。 |
| `DataGridOperationButtons.axaml` | 定义局部操作入口、按钮或 handle 的状态视觉。 |
| `DataGridRowExpanderTheme.axaml` | 定义集合项、容器项或局部单元的状态视觉。 |
| `DataGridRowGroupHeaderTheme.axaml` | 定义集合项、容器项或局部单元的状态视觉。 |
| `DataGridRowHeaderTheme.axaml` | 定义集合项、容器项或局部单元的状态视觉。 |
| `DataGridRowReorderHandle.axaml` | 定义集合项、容器项或局部单元的状态视觉。 |
| `DataGridRowTheme.axaml` | 定义集合项、容器项或局部单元的状态视觉。 |
| `DataGridTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `DataGridTopLeftColumnHeaderTheme.axaml` | 定义集合项、容器项或局部单元的状态视觉。 |
| `DataGridFilterIndicatorTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `DataGridFilterMenuItemTheme.axaml` | 定义集合项、容器项或局部单元的状态视觉。 |
| `DataGridFilterTreeItemTheme.axaml` | 定义集合项、容器项或局部单元的状态视觉。 |
| `DataGridMenuFilterFlyoutPresenterTheme.axaml` | 定义弹层、窗口或 overlay 宿主视觉。 |
| `DataGridSortIndicatorTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `DataGridTreeFilterFlyoutPresenterTheme.axaml` | 定义弹层、窗口或 overlay 宿主视觉。 |

DataGrid 拥有独立 Control identity；`DataGridToken` 只表达 DataGrid Own Token 语义，不承载 selection/checked/active、collection/filter、motion 或 visual option 运行时状态。Control 级 Global Token 覆盖与 Own Token 通过 `DataGridTokenResource` 统一读取。

主题维护规则：

- 不删除或重命名已经稳定的 ControlTheme key、template part、伪类和资源 key。
- 不把可由 AXAML 表达的模板状态迁移为 C# 动态创建视觉。
- 不把 hover、pressed、selected、expanded、loading、filter、popup open 等运行时状态写入 Token。
- Browser 或平台特化主题必须保持同一 API 的语义一致。

Token 来源：

DataGrid Token 只表达组件级视觉变量，例如尺寸、间距、颜色、圆角、阴影、图标尺寸和弹层边界。Token 不承载运行时选择、展开、加载、错误、上传任务、过滤条件或业务状态。

当前 Token scope：

- `DataGridToken`，scope id 为 `DataGrid`，源码位于 `src/AtomUI.Desktop.Controls.DataGrid/DataGridToken.cs`。

## AOT 与裁剪注意事项

### 9.1 热路径

- Scroll input 只更新/合并 DesiredViewport，handler 返回后由 coordinator 调度 request。
- 每次不同的 DesiredViewport 只替换一个 active scope；旧排队项不会作为无界 task 留在 semaphore 前等待。
- PointerMoved 只更新 drag session、ghost offset、target key 和必要自动滚动 intent。
- Measure/Arrange/prepare/recycle 只做 committed index lookup 与 container state 投影。
- Query no-op 不请求、不刷新 rows；sort state 没有 per-cell 长期订阅。
- Ready 模板不增加 visual node；prefetch 不触发布局。
- 同步 Source/cache hit 不发布瞬时 Loading/Refreshing；异步首次加载驱动 Spin，已有 snapshot 的异步刷新不启动自动遮罩。

### 9.2 复杂度与资源上限

| 路径 | 上限 |
| --- | --- |
| fixed-height offset lookup | O(1) |
| variable-height offset lookup | O(log M)，M 由 cache/pin/显式状态约束 |
| realized row/group controls | visible + editing row + drag row |
| request concurrency | 默认最多 2 |
| active viewport scope | 每个 generation 1 个 |
| queued obsolete viewport work | 0；scope supersede 时从可发现队列移除并取消 |
| prefetch priority | 低于所有 visible 缺口，并绑定产生它的 committed scope |
| range size | Schema.MaximumRangeSize，最大 4096 |
| cache | 有限 block LRU，与 total 无关 |
| snapshot-expiry auto retry | 每个用户操作最多 1 次 |

所有 request、cache、pool、subscription 和 sparse state 必须在长时间往返滚动后达到稳态，不能随滚动次数或曾访问 row 数持续
增长。

### 9.3 AOT

- Query/Source public 类型只位于 DataGrid package，不引入 Core/Shared 反向依赖。
- LocalSource 使用静态泛型 descriptor，并把 typed getter 显式投影为 schema `DisplayAccessor`；generated
  `IDataMemberAccessor` 可通过 `FromDataMember<TItem>` 复用。自动列只消费该 accessor 并生成 compiled binding，绝不把
  FieldId 当作 CLR path。
- 没有 `DisplayAccessor` 的远端 schema 字段只支持显式 Column/Binding；`PropertyChangedName` 与 FieldId 相互独立，binding
  dispose/recycle 时解除 `INotifyPropertyChanged` 订阅。
- 不使用 PropertyInfo.GetValue、Expression.Compile、assembly scan、runtime registration、动态泛型构造或字符串 Binding。
- 不新增 linker root、trimming suppression 或反射 fallback。
- Source generator 输出只通过输入源和 generator 更新。

## 源码索引

稳定 ownership 按职责组织：

```text
src/AtomUI.Desktop.Controls.DataGrid/
├── DataGrid.cs                         public contract + lifecycle
├── DataGrid.Query.cs                   Query commit and visual projection
├── DataGrid.RangeLoading.cs            viewport-to-coordinator integration
├── DataGrid.Virtualization.cs          desired/committed viewport + container bridge
├── Data/
│   ├── Query/                          immutable values, scalar and validation
│   ├── Source/                         schema, request, result and capability contracts
│   │   └── Local/                      typed local source and projection
│   └── Virtualization/                 viewport scope, request scheduler, snapshot, cache, index and heights
├── Column/                             column contract, header, sorting/filtering interaction
├── Column/Filters/                     filter indicator, flyout and candidate presentation
├── Row/                                row, group header, details and row presenters
├── Cell/                               cell state and horizontal virtualization
├── EventArgs/                          public event payloads
├── Themes/                             static templates, selectors and resource binding
├── Localization/                       generated-catalog-backed localized strings
└── Utils/                              narrow shared algorithms and converters
```

维护规则：

- 主文件保留 public/protected API、Avalonia 属性注册、事件和主要生命周期入口。
- partial 与 helper 按稳定 owner 拆分，不按 public/private 或单个触发点拆分。
- Theme 文件负责静态视觉结构、Template Part、selector 和资源绑定；不在 C# 中动态复制同一结构。
- Token 只保存组件视觉变量，不保存 Query、loading、selection、expanded 或 popup runtime state。
- Gallery 只展示用法和验证行为，不成为运行时状态 owner。
- GeneratedFiles 由对应 generator 维护，不手工编辑。

## 相关文档

- 源设计文档：`docs/controls/desktop/data-display/data-grid/overview.md`
- 实现文档：`docs/controls/desktop/data-display/data-grid/implementation.md`
- Token 文档：`docs/controls/desktop/data-display/data-grid/token.md`
- 变更记录：`docs/controls/desktop/data-display/data-grid/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/descriptions/index-cn.md

# Descriptions

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

Descriptions 是桌面端数据展示类控件，用于按标签和值展示对象属性、订单信息、配置详情、用户资料等结构化只读信息。它通过 `DescriptionItem` 集合、响应式列数、水平/纵向布局、边框模式和 Header/Extra 区域表达“属性说明列表”的展示语义。

Descriptions 的职责是把描述项数据排版为稳定的网格视觉。它不负责编辑、选择、排序、过滤、远程数据加载、表单提交、虚拟化列表或复杂主从详情关系；这些能力应由业务层、DataGrid、ListView、Form 或专用编辑控件承担。

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/DataDisplay/Descriptions` |
| 状态 | Stable |

## 何时使用

Descriptions 的设计语言来自 参考设计体系的描述列表：标签提供字段名，内容提供字段值，列数和跨度提供信息密度，边框模式提供表格化阅读边界。

| 维度 | 含义 | 典型表达 |
| --- | --- | --- |
| 展示密度 | 用尺寸和列数控制同屏信息量。 | `SizeType`、`ColumnInfo`。 |
| 结构层级 | 用 Header 和 Extra 表达当前信息组名称和辅助操作入口。 | `Header`、`Extra`。 |
| 标签和值 | 每个 `DescriptionItem` 表达一对 label/content。 | `Label`、`Content`。 |
| 跨列能力 | 通过 `Span` 和 `IsFilled` 表达长内容或整行内容。 | `Span`、`IsFilled`。 |
| 视觉边界 | 普通模式轻量展示，边框模式强调表格结构。 | `IsBordered`。 |
| 方向模型 | 水平方向把 label 和 content 放在同一行，纵向方向上下排列。 | `Layout`。 |

Descriptions 是展示控件，不提供 hover、pressed、selected、checked、loading 或 popup 等交互状态。

## 公共 API

Descriptions 的公共 API 分为控件级属性、内容集合入口和描述项模型。

控件级属性：

| API | 类型 | 语义 |
| --- | --- | --- |
| `IsBordered` | `bool` | 是否使用边框模式。水平边框模式会为每个 item 生成独立 label/content cell。 |
| `IsShowColon` | `bool` | 普通 horizontal/vertical 非边框模式下是否显示冒号，默认 `true`。 |
| `ColumnInfo` | `ResponsiveInt?` | 响应式列数配置。为空时使用控件内置断点默认列数。 |
| `Header` / `HeaderTemplate` | `object?` / `IDataTemplate?` | 描述列表标题区域内容及模板。 |
| `Extra` / `ExtraTemplate` | `object?` / `IDataTemplate?` | 标题行右侧辅助内容及模板。 |
| `Layout` | `Orientation` | 描述项 label/content 排列方向，默认 `Horizontal`。 |
| `SizeType` | `SizeType` | 展示尺寸，默认 `Large`，支持 `Large/Middle/Small` 三种主题分支。 |
| `ItemsSource` | `IEnumerable?` | 外部描述项数据源。当前实现只物化其中的 `DescriptionItem`。 |
| `Items` | `DescriptionItems` | XAML 内容集合入口，也是布局算法的主数据源。 |

描述项模型：

| API | 类型 | 语义 |
| --- | --- | --- |
| `DescriptionItem.Label` | `string` | 字段标签文本，作为 Avalonia 属性支持 XAML 绑定。 |
| `DescriptionItem.Content` | `object?` | 字段内容，作为 Avalonia 属性支持 XAML 绑定，允许普通文本或复杂 Avalonia 内容。 |
| `DescriptionItem.IsFilled` | `bool` | 当前项是否填满本行剩余列，作为 Avalonia 属性参与布局刷新。 |
| `DescriptionItem.Span` | `ResponsiveInt` | 当前项跨列数，默认 `1`，作为 Avalonia 属性支持响应式配置和布局刷新。 |

`DescriptionItem` 是非视觉 `AvaloniaObject` 描述对象，不是 `Control`、`StyledElement` 或 template part。它的职责是承载 item 级 Avalonia 属性，让下面这种 XAML 绑定成为稳定契约：

```xml
<atom:DescriptionItem Label="Name" Content="{Binding Name}" />
```

Descriptions 仍然负责把 `DescriptionItem` 转换成内部视觉控件。`DescriptionItem` 自身不能直接进入视觉树，也不能承载需要视觉树生命周期才能安全释放的主题或资源状态。

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `HeaderLayout` | `DockPanel` | Header/Extra 行容器，固定存在，通过 `IsHeaderLayoutVisible` 控制显示。 |
| `ExtraPresenter` | `ContentPresenter` | Extra 内容和模板承载。 |
| `HeaderPresenter` | `ContentPresenter` | Header 内容和模板承载。 |
| `ContentFrame` | `Border` | 内容区域边框、圆角和裁剪承载。 |
| `PART_GridLayout` | `Grid` | 生成的描述项视觉子控件布局容器。 |

Descriptions 没有 public routed event、命令或专用伪类。内部生成的 `DescriptionDefaultItem`、`DescriptionBorderedItemLabel` 和 `DescriptionBorderedItemContent` 是主题承载类型，不是公开用户 API。

## 事件与命令

Descriptions 没有 public routed event、命令或专用伪类。内部生成的 `DescriptionDefaultItem`、`DescriptionBorderedItemLabel` 和 `DescriptionBorderedItemContent` 是主题承载类型，不是公开用户 API。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/Descriptions/Views/DescriptionsShowCase.axaml:33`

Gallery key：`ExamplesContent` / item `0`

```axaml
<atom:Descriptions Header="用户信息">
    <atom:DescriptionItem Label="用户名" Content="周毛毛" />
    <atom:DescriptionItem Label="电话" Content="1810000000" />
    <atom:DescriptionItem Label="居住地" Content="浙江杭州" />
    <atom:DescriptionItem Label="备注" Content="暂无" />
    <atom:DescriptionItem Label="地址"
                          Content="中国浙江省杭州市西湖区万塘路 18 号" />
</atom:Descriptions>
```

### 边框

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/Descriptions/Views/DescriptionsShowCase.axaml:50`

Gallery key：`ExamplesContent` / item `1`

```axaml
<atom:Descriptions IsBordered="True">
    <atom:DescriptionItem Label="产品" Content="云数据库" />
    <atom:DescriptionItem Label="计费方式" Content="预付费" />
    <atom:DescriptionItem Label="自动续费" Content="是" />
    <atom:DescriptionItem Label="订购时间" Content="2018-04-24 18:00:00" />
    <atom:DescriptionItem Label="使用时间" Content="2019-04-24 18:00:00" Span="2" />
    <atom:DescriptionItem Label="状态" Content="运行中" Span="3" />
    <atom:DescriptionItem Label="协商金额" Content="$80.00" />
    <atom:DescriptionItem Label="折扣" Content="$20.00" />
    <atom:DescriptionItem Label="官方收据" Content="$60.00" />
    <atom:DescriptionItem Label="配置信息">
        <atom:DescriptionItem.Content>
            <StackPanel Orientation="Vertical" Spacing="5">
                <TextBlock Text="数据盘类型：MongoDB" />
                <TextBlock Text="数据库版本：3.4" />
                <TextBlock Text="套餐：dds.mongo.mid" />
                <TextBlock Text="存储空间：10 GB" />
                <TextBlock Text="副本因子：3" />
                <TextBlock Text="地域：华东 1" />
            </StackPanel>
        </atom:DescriptionItem.Content>
    </atom:DescriptionItem>
</atom:Descriptions>
```

### 自定义尺寸

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/Descriptions/Views/DescriptionsShowCase.axaml:82`

Gallery key：`ExamplesContent` / item `2`

```axaml
<StackPanel Orientation="Vertical" Spacing="30">
    <StackPanel Orientation="Horizontal" Spacing="10">
        <atom:RadioButton Name="DefaultSizeRadioButton"
                          IsChecked="True"
                          Tag="{x:Static atom:SizeType.Large}"
                          IsCheckedChanged="SizeTypeCheckChanged"
                          Content="大号" />
        <atom:RadioButton Name="MiddleSizeRadioButton"
                          Tag="{x:Static atom:SizeType.Middle}"
                          IsCheckedChanged="SizeTypeCheckChanged"
                          Content="中号" />
        <atom:RadioButton Name="SmallSizeRadioButton"
                          Tag="{x:Static atom:SizeType.Small}"
                          IsCheckedChanged="SizeTypeCheckChanged"
                          Content="小号" />
    </StackPanel>
    <atom:Descriptions IsBordered="True"
                       SizeType="{Binding DescriptionsSizeType}"
                       Header="自定义尺寸">
        <atom:Descriptions.Extra>
            <atom:Button ButtonType="Primary" Content="编辑" />
        </atom:Descriptions.Extra>
        <atom:DescriptionItem Label="产品" Content="云数据库" />
        <atom:DescriptionItem Label="计费方式" Content="预付费" />
        <atom:DescriptionItem Label="自动续费" Content="是" />
        <atom:DescriptionItem Label="订购时间" Content="2018-04-24 18:00:00" />
        <atom:DescriptionItem Label="使用时间" Content="2019-04-24 18:00:00" Span="2" />
        <atom:DescriptionItem Label="状态" Content="运行中" Span="3" />
        <atom:DescriptionItem Label="协商金额" Content="$80.00" />
        <atom:DescriptionItem Label="折扣" Content="$20.00" />
        <atom:DescriptionItem Label="官方收据" Content="$60.00" />
        <atom:DescriptionItem Label="配置信息">
            <atom:DescriptionItem.Content>
                <StackPanel Orientation="Vertical" Spacing="5">
                    <TextBlock Text="数据盘类型：MongoDB" />
                    <TextBlock Text="数据库版本：3.4" />
                    <TextBlock Text="套餐：dds.mongo.mid" />
                    <TextBlock Text="存储空间：10 GB" />
                    <TextBlock Text="副本因子：3" />
                    <TextBlock Text="地域：华东 1" />
                </StackPanel>
            </atom:DescriptionItem.Content>
        </atom:DescriptionItem>
    </atom:Descriptions>

    <atom:Descriptions Header="自定义尺寸"
                       SizeType="{Binding DescriptionsSizeType}">
        <atom:Descriptions.Extra>
            <atom:Button ButtonType="Primary" Content="编辑" />
        </atom:Descriptions.Extra>
        <atom:DescriptionItem Label="用户名" Content="周毛毛" />
        <atom:DescriptionItem Label="电话" Content="1810000000" />
        <atom:DescriptionItem Label="居住地" Content="浙江杭州" />
        <atom:DescriptionItem Label="备注" Content="暂无" />
        <atom:DescriptionItem Label="地址"
                              Content="中国浙江省杭州市西湖区万塘路 18 号" />
    </atom:Descriptions>
</StackPanel>
```

### 响应式

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/Descriptions/Views/DescriptionsShowCase.axaml:150`

Gallery key：`ExamplesContent` / item `3`

```axaml
<atom:Descriptions IsBordered="True"
                   SizeType="{Binding DescriptionsSizeType}"
                   Header="响应式描述列表"
                   ColumnInfo="xs: 1, sm: 2, md: 3, lg: 3, xl: 4, xxl: 4">
    <atom:DescriptionItem Label="产品" Content="云数据库" />
    <atom:DescriptionItem Label="计费" Content="预付费" />
    <atom:DescriptionItem Label="时间" Content="18:00:00" />
    <atom:DescriptionItem Label="金额" Content="$80.00" />
    <atom:DescriptionItem Label="折扣" Content="$20.00" Span="xl: 2, xxl: 2" />
    <atom:DescriptionItem Label="官方" Content="$60.00" Span="xl: 2, xxl: 2" />
    <atom:DescriptionItem Label="配置信息" Span="xs: 1, sm: 2, md: 3, lg: 3, xl: 2, xxl: 2">
        <atom:DescriptionItem.Content>
            <StackPanel Orientation="Vertical">
                <TextBlock Text="数据盘类型：MongoDB" />
                <TextBlock Text="数据库版本：3.4" />
                <TextBlock Text="套餐：dds.mongo.mid" />
            </StackPanel>
        </atom:DescriptionItem.Content>
    </atom:DescriptionItem>
    <atom:DescriptionItem Label="硬件信息" Span="xs: 1, sm: 2, md: 3, lg: 3, xl: 2, xxl: 2">
        <atom:DescriptionItem.Content>
            <StackPanel Orientation="Vertical">
                <TextBlock Text="CPU：6 核 3.5 GHz" />
                <TextBlock Text="副本因子：3" />
                <TextBlock Text="地域：华东 1" />
            </StackPanel>
        </atom:DescriptionItem.Content>
    </atom:DescriptionItem>
</atom:Descriptions>
```

## 状态模型

Descriptions 的核心状态流：

```text
ItemsSource / Items
      ↓
DescriptionItem collection
      ↓
generated item controls
      ↓
responsive column count + item span
      ↓
PART_GridLayout row/column/column-span
```

列数解析：

- `ColumnInfo` 为空时，断点默认列数为 `ExtraSmall=1`、`Small=2`、`ExtraExtraExtraLarge=4`，其他断点为 `3`。
- `ColumnInfo` 不为空时，按 `ResponsiveInt.Resolve()` 使用移动端优先级解析列数。
- 水平边框模式下内部有效列数为展示列数的两倍，因为 label 和 content 分别占 grid cell。
- 非水平边框模式下内部有效列数等于展示列数。

布局语义：

- 普通水平模式使用一个 `DescriptionDefaultItem` 展示 label、冒号和 content。
- 水平边框模式为每个 item 生成一个 label cell 和一个 content cell。
- 纵向模式使用一个 `DescriptionDefaultItem`，label 在上、content 在下；当 `IsBordered=true` 时通过模板显示内部边框和分隔线。
- `Span` 限制在当前行剩余列范围内，最小为 `1`。
- 最后一个 item 或 `IsFilled=true` 的 item 会填满当前行剩余列。

Header/Extra 状态：

```text
Header != null || Extra != null
      ↓
IsHeaderLayoutVisible
      ↓
HeaderLayout.IsVisible
```

## 主题与 Design Token

Descriptions 的默认视觉由根主题、默认项主题和边框 cell 主题组成。

| 主题文件 | 职责 |
| --- | --- |
| `DescriptionsTheme.axaml` | 根模板、Header/Extra、ContentFrame、`PART_GridLayout`、边框模式内容框和非边框 RowSpacing。 |
| `DescriptionDefaultItemTheme.axaml` | 普通项 horizontal/vertical/vertical bordered 三种模板和冒号、label、content 视觉。 |
| `DescriptionBorderedItemLabelTheme.axaml` | 水平边框模式 label cell 视觉。 |
| `DescriptionBorderedItemContentTheme.axaml` | 水平边框模式 content cell 视觉。 |

Token 关系：

```text
SharedToken
   ↓
DescriptionsToken
   ↓
DescriptionsTheme / DescriptionDefaultItemTheme / bordered cell themes
```

根 `ContentFrame` 的边框、圆角、裁剪来自 SharedToken。label 背景、label/content/title/extra 颜色、Header margin、item padding 和冒号 margin 来自 DescriptionsToken。

Token 来源：

DescriptionsToken 是 Descriptions 的控件级 Token scope，描述描述列表的 label 背景、文本颜色、标题颜色、Header 间距、item padding、冒号间距、内容颜色和 Extra 颜色。

DescriptionsToken 不承载以下状态：

- `Items`、`ItemsSource`、`DescriptionItem.Content` 等数据状态。
- `ColumnInfo`、当前断点、有效列数、row/column/column-span 等布局状态。
- `IsBordered`、`Layout`、`IsShowColon`、`IsFilled`、`Span` 等实例行为状态。
- `Header`、`Extra` 的实际内容或模板。
- `IsLastRow`、`IsLastColumn`、`EffectiveBorderThickness` 等生成视觉派生状态。

## AOT 与裁剪注意事项

Descriptions 不依赖运行时反射发现模板结构。模板协作通过固定 part 名称、显式内部类型和 Avalonia property binding 完成。

资源与生命周期边界：

- `DescriptionsToken` 通过 token generator 注册，Theme 通过 `DescriptionsTokenResource` 使用。
- 生成的内部控件是 Visual，Token resource 由主题和视觉树生命周期管理。
- `DescriptionItem` 是非视觉 `AvaloniaObject`，只能承载 item 级数据属性和普通 binding target 语义。
- `IsShowColon`、`IsBordered` 和 `SizeType` 的内部同步使用 Avalonia property binding，不使用 `BindUtils.RelayBind`。
- 媒体断点事件订阅由 `OnAttachedToVisualTree` / `OnDetachedFromVisualTree` 配对管理。
- 不在 `Render`、layout hot path 或数据物化路径中读取全局资源。

### 8.1 非视觉 DescriptionItem 的泄露边界

`DescriptionItem` 继承 `AvaloniaObject` 后，binding expression、property changed subscription 和资源查找都有了真实生命周期成本。实现必须把 acquire/release 写成对称路径，不能只依赖 GC 或视觉树清理。

需要防范的泄露链：

```text
DescriptionItem.PropertyChanged subscription
  → Descriptions / generated control
  → old ShowCase visual tree
```

```text
generated control / presenter binding
  → old DescriptionItem
  → old owner / content visual
```

```text
Application.ResourcesChanged
  → DynamicResourceExpression
  → DescriptionItem.ValueStore
  → owner / generated control / ShowCase
```

标准防范规则：

- item attach 时注册属性变化订阅，item detach 时解除同一个订阅。
- generated control 的 binding、事件和 disposable 归属 generated control 或 item attach 生命周期；视觉重建前必须统一释放。
- item 到 generated control 的映射只服务当前模板和布局周期；生成视觉清理路径必须同时清理映射和 item 订阅。
- 主题资源优先绑定在生成出来的 Visual 控件上，让视觉树生命周期管理 `DynamicResource`。
- `DescriptionItem` 上禁止直接放 `DynamicResource` 或 token-resource binding，除非它实现 scoped `IResourceHost` / `IThemeVariantHost`，并有 owner attach/detach 与 WeakReference 测试。

### 8.2 DynamicResource Scoped Host 规则

如果 `DescriptionItem` 必须承载动态资源，它必须作为 owning `Descriptions` 的 scoped resource host：

- `TryGetResource()` 先查询 owning `Descriptions`，再 fallback 到 `Application.Current`。
- owner 变化时先 unsubscribe 旧 owner 的 `ResourcesChanged` / `ActualThemeVariantChanged`，再保存并订阅新 owner。
- owner detach、Items remove、Items reset、template reapply 和控件 detach 都必须能释放 owner 订阅。
- owner 资源和主题变化需要通过 `DescriptionItem` 转发，确保动态资源仍能更新。
- 必须补 WeakReference 测试，模拟 XAML `DynamicResource` anchor，证明 item 离开集合后不会被 `Application.ResourcesChanged` root 住。

AOT 边界：

- 不新增字符串 path binding、反射扫描、`Activator.CreateInstance(Type)` 或动态成员访问。
- API 与 Token 契约由控件文档、源码 public surface、Token 类型或生成数据维护。
- `ItemsSource` 只接受已经构造好的 `DescriptionItem`，不做基于反射的对象属性展开。

## 源码索引

主要源码：

- `src/AtomUI.Desktop.Controls/Descriptions/Descriptions.cs`：公共 API、Items 集合、ItemsSource 物化、媒体断点订阅、生成视觉子项和布局算法。
- `src/AtomUI.Desktop.Controls/Descriptions/DescriptionItem.cs`：非视觉 `AvaloniaObject` 描述项模型和 `DescriptionItems` 集合类型。
- `src/AtomUI.Desktop.Controls/Descriptions/DescriptionDefaultItem.cs`：普通项和纵向边框项的内部承载控件。
- `src/AtomUI.Desktop.Controls/Descriptions/DescriptionBorderedCell.cs`：水平边框模式 label/content cell 的共享内部基类。
- `src/AtomUI.Desktop.Controls/Descriptions/DescriptionBorderedItemLabel.cs`：水平边框模式 label cell 类型。
- `src/AtomUI.Desktop.Controls/Descriptions/DescriptionBorderedItemContent.cs`：水平边框模式 content cell 类型。
- `src/AtomUI.Desktop.Controls/Descriptions/DescriptionsToken.cs`：Descriptions 控件 Token。
- `src/AtomUI.Desktop.Controls/Descriptions/Themes/*.axaml`：根模板、普通项模板、边框 cell 模板和 token 样式。

## 相关文档

- 源设计文档：`docs/controls/desktop/data-display/descriptions/overview.md`
- 实现文档：`docs/controls/desktop/data-display/descriptions/implementation.md`
- Token 文档：`docs/controls/desktop/data-display/descriptions/token.md`
- 变更记录：`docs/controls/desktop/data-display/descriptions/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/empty/index-cn.md

# Empty

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

Empty 是 AtomUI 桌面控件体系中的空状态控件，用于表达无数据、无结果或占位状态。

Empty 不负责加载状态、错误状态或业务异常处理。这些职责应由业务层、组合控件或更专用的 AtomUI 控件承担。

主要源码入口：

- `src/AtomUI.Controls/Empty`
- `src/AtomUI.Desktop.Controls/Empty`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/DataDisplay/Empty` |
| 状态 | Stable |

## 何时使用

Empty 的设计语言围绕控件职责、可观察状态和主题契约组织，而不是围绕模板节点组织。

| 维度 | 含义 | Empty 中的表达 |
| --- | --- | --- |
| 产品语义 | 控件在界面中承担的稳定职责。 | Empty 是 AtomUI 桌面控件体系中的空状态控件，用于表达无数据、无结果或占位状态。 |
| 内容承载 | 用户数据、展示内容、集合项或操作入口如何进入控件。 | `Description`、`ImagePath`、`ImageSource`、`IsDescriptionVisible`、`PresetImage`。 |
| 状态反馈 | public API、内部状态和伪类如何形成用户可感知反馈。 | collection/filter、visual option。 |
| 主题语义 | ControlTheme、SharedToken、控件 Token 和模板绑定如何表达视觉。 | Empty Token + ControlTheme。 |

## 公共 API

Empty 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。

核心 public surface 按语义分组维护：

| 契约组 | 代表成员 | 维护含义 |
| --- | --- | --- |
| 内容与数据 | `Description`、`ImagePath`、`ImageSource`、`IsDescriptionVisible`、`PresetImage` | 定义控件展示内容、输入数据、模板或业务对象入口。 |
| 视觉与布局 | `SizeType` | 影响尺寸、位置、颜色、形状、密度和模板视觉变量。 |

当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。

主要公开类型与枚举：

- 类型：`AbstractEmpty`、`Empty`。
- 枚举：`PresetEmptyImage`。

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_SvgImage` | `Avalonia.Svg.Svg` | 稳定模板协作入口，重命名前必须同步主题和实现。 |

当前未抽取到控件专属伪类；主题主要依赖 Avalonia 标准伪类、模板绑定和内部 StyledProperty。

## 事件与命令

Empty 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。
当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/Empty/Views/EmptyShowCase.axaml:35`

Gallery key：`ExamplesContent` / item `0`

```axaml
<atom:Empty PresetImage="Default" />
```

### 尺寸

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/Empty/Views/EmptyShowCase.axaml:46`

Gallery key：`ExamplesContent` / item `1`

```axaml
<StackPanel Orientation="Vertical">
    <StackPanel Orientation="Horizontal" Spacing="10">
        <atom:Empty PresetImage="Simple" SizeType="Small" />
        <atom:Empty PresetImage="Simple" SizeType="Middle" />
        <atom:Empty PresetImage="Simple" SizeType="Large" />
    </StackPanel>
</StackPanel>
```

### 无描述

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/Empty/Views/EmptyShowCase.axaml:79`

Gallery key：`ExamplesContent` / item `3`

```axaml
<atom:Empty PresetImage="Default" IsDescriptionVisible="False" />
```

## 状态模型

Empty 的状态流按以下路径收敛：

```text
Public API / inherited command / item source / user input
  -> 控件实例状态
  -> effective state / pseudo-class / template property
  -> ControlTheme selector / presenter / renderer
  -> Gallery 可观察行为
```

状态维护规则：

- Disabled 或不可交互状态优先屏蔽 pointer、keyboard、motion 和提交类反馈。
- collection/filter、visual option 状态由控件实例或明确的数据 owner 推导，不能在 template part 之间双向竞争。
- 模板重套用时必须把 public API 对应状态回放到新的 part、伪类和主题变量。
- 集合、弹层、异步、动效或窗口相关状态必须能处理 reset、close、cancel、detach 和 owner 释放。

## 主题与 Design Token

Empty 的视觉模型由控件模板、ControlTheme、SharedToken 和必要的控件 Token 共同构成。

| 主题文件 | 职责 |
| --- | --- |
| `EmptyTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |

Empty 使用 `EmptyToken` 作为控件 Token scope。Token 只表达组件视觉语义，不承载 collection/filter、visual option 运行时状态。

主题维护规则：

- 不删除或重命名已经稳定的 ControlTheme key、template part、伪类和资源 key。
- 不把可由 AXAML 表达的模板状态迁移为 C# 动态创建视觉。
- 不把 hover、pressed、selected、expanded、loading、filter、popup open 等运行时状态写入 Token。
- Browser 或平台特化主题必须保持同一 API 的语义一致。

Token 来源：

Empty Token 只表达组件级视觉变量，例如尺寸、间距、颜色、圆角、阴影、图标尺寸和弹层边界。Token 不承载运行时选择、展开、加载、错误、上传任务、过滤条件或业务状态。

当前 Token scope：

- `EmptyToken`，scope id 为 `Empty`，源码位于 `src/AtomUI.Desktop.Controls/Empty/EmptyToken.cs`。

## AOT 与裁剪注意事项

资源和 AOT 约束：

- 不通过运行时反射扫描 public API、Token 或 Gallery 示例数据。
- 不把可静态声明的模板结构迁移到 C# 动态创建。
- 异步加载、上传、弹层和窗口生命周期必须能取消或释放。
- 缓存对象必须与控件、窗口、弹层或数据 owner 生命周期一致。
- Source generator 生成文件不手工编辑；需要修改时改输入源或 generator。

性能边界：

- 控件应优先复用 Avalonia 原生虚拟化、模板绑定和资源系统。
- 避免为每次状态变化创建不必要的视觉对象、订阅或动画对象。
- 大集合控件必须保证 container recycle 后不会泄漏旧 item 状态。

## 源码索引

主要源码文件：

- `src/AtomUI.Controls/Empty/AbstractEmpty.cs`
- `src/AtomUI.Controls/Empty/BuiltInImageBuilder.cs`
- `src/AtomUI.Controls/Empty/PresetEmptyImage.cs`
- `src/AtomUI.Desktop.Controls/Empty/Empty.cs`
- `src/AtomUI.Desktop.Controls/Empty/EmptyToken.cs`
- `src/AtomUI.Desktop.Controls/Empty/Themes/EmptyTheme.axaml`

职责边界：

- 控件主文件保留 public/protected API、Avalonia 属性注册、事件和主要生命周期入口。
- Theme 文件负责静态视觉结构、template part、selector 和资源绑定。
- Token 文件只提供组件视觉变量，不保存实例状态。
- Gallery 文件只展示用法和示例，不作为运行时逻辑 owner。

## 相关文档

- 源设计文档：`docs/controls/desktop/data-display/empty/overview.md`
- 实现文档：`docs/controls/desktop/data-display/empty/implementation.md`
- Token 文档：`docs/controls/desktop/data-display/empty/token.md`
- 变更记录：`docs/controls/desktop/data-display/empty/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/expander/index-cn.md

# Expander

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

Expander 是桌面端数据展示类单面板折叠容器，用于在有限空间内展示一段可展开或收起的内容。它以 Avalonia `Expander` 为基础，扩展 AtomUI 的尺寸、展开图标、附加内容、触发区域、边框模式、Ghost 模式、展开方向、动效和 Token 体系。

Expander 的职责是管理一个 Header 与一个 Content 区域之间的展开关系。它不是多面板集合控件，不负责手风琴互斥展开、列表虚拟化、数据项生成、表单校验、远程加载或复杂主从详情关系。需要多面板协调时应使用 Collapse；需要静态分组时应使用 GroupBox；需要列表或树形数据展示时应使用 ListView、TreeView 或 DataGrid。

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/DataDisplay/Expander` |
| 状态 | Stable |

## 何时使用

Expander 的设计语言来自 参考设计体系的轻量折叠面板：Header 表达当前内容主题，展开图标表达折叠状态和方向，Content 承载可延迟阅读的内容。

| 维度 | 含义 | 典型表达 |
| --- | --- | --- |
| 层级收纳 | Header 始终可见，Content 按状态显示或隐藏。 | `Header`、`Content`、`IsExpanded`。 |
| 展开方向 | 内容可以向下、向上、向左或向右展开。 | `ExpandDirection`。 |
| 触发区域 | 可配置整行 Header 触发或仅图标触发。 | `TriggerType`。 |
| 图标语义 | 展开图标表示折叠状态、方向和可操作入口。 | `ExpandIcon`、`IsShowExpandIcon`、`ExpandIconPosition`。 |
| 视觉强度 | 普通、Borderless 和 Ghost 模式表达不同容器强度。 | `IsBorderless`、`IsGhostStyle`。 |
| 密度 | 尺寸和自定义 padding 控制 Header/Content 的空间密度。 | `SizeType`、`HeaderPadding`、`ContentPadding`。 |

Expander 应保持数据展示控件的克制视觉。Header 不是工具栏，AddOnContent 只承担轻量辅助入口，不改变折叠容器的主语义。

## 公共 API

Expander 的公共契约分为继承的 Avalonia `Expander` 契约和 AtomUI 扩展契约。

继承契约：

| API | 语义 |
| --- | --- |
| `Header` / `HeaderTemplate` | Header 区域内容和模板。 |
| `Content` / `ContentTemplate` | 可展开内容区域和模板。 |
| `IsExpanded` | 展开状态。 |
| `ExpandDirection` | 展开方向，支持 `Down`、`Up`、`Left`、`Right`。 |
| `IsEnabled` | 禁用后 Header 和展开按钮进入禁用状态。 |
| `BorderThickness` | 普通模式根边框厚度来源。 |

AtomUI 扩展契约：

| API | 类型 | 语义 |
| --- | --- | --- |
| `SizeType` | `CustomizableSizeType` | 控件尺寸，支持 `Large`、`Middle`、`Small`、`Custom`。`Custom` 默认沿用 Middle 主题分支，显式 `HeaderPadding` / `ContentPadding` 可覆盖默认 spacing。 |
| `IsShowExpandIcon` | `bool` | 是否显示展开图标。 |
| `ExpandIcon` | `PathIcon?` | 自定义展开图标；为空时使用 `RightOutlined`。 |
| `AddOnContent` / `AddOnContentTemplate` | `object?` / `IDataTemplate?` | Header 右侧辅助内容及模板。 |
| `IsGhostStyle` | `bool` | 使用 Ghost 视觉，移除容器边框并使用内容背景作为 Header 背景。 |
| `IsBorderless` | `bool` | 使用无边框视觉，移除容器边框。 |
| `TriggerType` | `ExpanderTriggerType` | 触发区域，`Header` 表示 Header 区域可触发，`Icon` 表示仅图标可触发。 |
| `ExpandIconPosition` | `ExpanderIconPosition` | 展开图标位置，支持 `Start` 和 `End`。 |
| `HeaderPadding` | `Thickness?` | 显式 Header 内边距；为空时由 SizeType 和 ExpanderToken 决定。 |
| `ContentPadding` | `Thickness?` | 显式 Content 内边距；为空时由 SizeType 和 ExpanderToken 决定。 |
| `IsMotionEnabled` | `bool` | 是否启用展开/收起动效。 |

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_Frame` | `PixelAlignedBorder` | 根边框、裁剪和整体布局承载。 |
| `PART_MainLayout` | `DockPanel` | Header 与 Content 的 dock 布局。 |
| `PART_HeaderLayoutTransform` | `LayoutTransformControl` | 横向展开方向下旋转 Header。 |
| `PART_HeaderDecorator` | `PixelAlignedBorder` | Header 背景和 padding 承载，也是 Header 点击范围；不绘制 Header/Content 分隔线。 |
| `PART_HeaderLayout` | `Grid` | 展开图标、Header、AddOnContent 的列布局。 |
| `PART_ExpandButton` | `IconButton` | 展开图标显示和图标触发入口。 |
| `PART_HeaderPresenter` | `ContentPresenter` | Header 内容和模板承载。 |
| `PART_AddOnContentPresenter` | `ContentPresenter` | AddOnContent 内容和模板承载。 |
| `PART_ContentMotionActor` | `LayoutAwareMotionActor` | Content 分隔线和 Content 的共同展开/收起动效承载。 |
| `PART_ContentPresenter` | `ContentPresenter` | Content 内容、模板和 padding 承载。 |

`PART_ContentMotionActor` 内包含一个未命名 `PixelAlignedBorder`。该视觉固定拥有 Content 靠近 Header 一侧的分隔线，但不作为新的 template part 或 selector 契约暴露。

稳定伪类和主题状态：

| 伪类 / selector 状态 | 语义 |
| --- | --- |
| `:expanded` | 继承展开状态，用于展开图标旋转。 |
| `:up` / `:down` / `:left` / `:right` | 展开方向状态。 |
| `:custom-header-padding` | `HeaderPadding` 非空，Header padding 和图标间距使用显式值。 |
| `:custom-content-padding` | `ContentPadding` 非空，Content padding 使用显式值。 |
| `[IsBorderless=True]` / `[IsGhostStyle=True]` | 视觉强度分支。 |
| `[TriggerType=Header]` / `[TriggerType=Icon]` | Cursor 和点击路径分支。 |

Expander 没有专用 routed event 或 command。展开状态通过继承的 `IsExpanded` 表达。

## 事件与命令

Expander 没有专用 routed event 或 command。展开状态通过继承的 `IsExpanded` 表达。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 无边框

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/Expander/Views/ExpanderAppearanceShowCase.axaml:12`

Gallery key：`ExamplesContent` / item `0`

```axaml
<atom:Expander IsBorderless="True" Header="这是面板标题 1">
    <atom:TextBlock TextWrapping="Wrap" Text="狗是一种被驯养的动物。它以忠诚和可靠著称，在世界各地许多家庭中都是受欢迎的成员。" />
</atom:Expander>
```

### 幽灵展开器

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/Expander/Views/ExpanderAppearanceShowCase.axaml:20`

Gallery key：`ExamplesContent` / item `1`

```axaml
<atom:Expander IsGhostStyle="True" Header="这是面板标题 1">
    <atom:TextBlock TextWrapping="Wrap" Text="狗是一种被驯养的动物。它以忠诚和可靠著称，在世界各地许多家庭中都是受欢迎的成员。" />
</atom:Expander>
```

### 自定义标题和内容间距

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/Expander/Views/ExpanderAppearanceShowCase.axaml:28`

Gallery key：`ExamplesContent` / item `2`

```axaml
<StackPanel Orientation="Vertical" Spacing="20">
    <atom:Expander Header="这是面板标题 1" HeaderPadding="5" ContentPadding="5">
        <atom:TextBlock TextWrapping="Wrap" Text="狗是一种被驯养的动物。它以忠诚和可靠著称，在世界各地许多家庭中都是受欢迎的成员。" />
    </atom:Expander>

    <atom:Expander Header="这是面板标题 1" IsGhostStyle="True" HeaderPadding="5" ContentPadding="5">
        <atom:TextBlock TextWrapping="Wrap" Text="狗是一种被驯养的动物。它以忠诚和可靠著称，在世界各地许多家庭中都是受欢迎的成员。" />
    </atom:Expander>
</StackPanel>
```

### 展开器

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/Expander/Views/ExpanderBasicShowCase.axaml:12`

Gallery key：`ExamplesContent` / item `0`

```axaml
<atom:Expander Header="这是面板标题 1">
    <atom:TextBlock TextWrapping="Wrap" Text="狗是一种被驯养的动物。它以忠诚和可靠著称，在世界各地许多家庭中都是受欢迎的成员。" />
</atom:Expander>
```

## 状态模型

核心状态流：

```text
Header pointer / ExpandButton click
        ↓
TriggerType gate
        ↓
IsExpanded
        ↓
Content motion / stable visibility
        ↓
PART_ContentMotionActor.IsVisible
```

触发语义：

- `TriggerType=Header` 时，鼠标左键按下且命中 `PART_HeaderDecorator` 会切换 `IsExpanded`。
- `TriggerType=Icon` 时，Header 点击不切换状态，只能通过 `PART_ExpandButton.Click` 切换。
- 禁用状态下模板将 `IsEnabled` 传递给 `PART_ExpandButton`，主题同步禁用前景。

展开方向：

- `ExpandDirection=Down`：Header 停留顶部，Content 向下展开。
- `ExpandDirection=Up`：Header dock 到底部，Content 向上展开。
- `ExpandDirection=Left` / `Right`：Header 通过 `LayoutTransformControl` 旋转，整体对齐到对应侧。
- 展开图标在 `:expanded` 下按展开方向旋转，必须与内容运动方向一致。

动效状态：

- `IsMotionEnabled=false` 时直接同步 `PART_ContentMotionActor.IsVisible` 和透明度。
- Core 共用内容展开机制在上下方向改变内容视口高度，左右方向改变宽度，内容按正常尺寸排版并由视口裁剪。
- 播放中反转必须从当前已呈现的尺寸和透明度接续；最终状态以最新 `IsExpanded` 为准，旧请求不能覆盖新请求。
- 四个方向共同保持内容与标题的锚定边、分隔线相邻关系、自然内容尺寸及首尾帧连续性。具体执行路径由本控件实现文档描述。

共享设计来源：`docs/architecture/systems/control-infrastructure/content-expansion.md`。

自定义 padding：

```text
HeaderPadding != null
  → :custom-header-padding
  → PART_HeaderDecorator.Padding = HeaderPadding
  → EffectiveExpandButtonMargin 按 HeaderPadding.Left/Right 推导

ContentPadding != null
  → :custom-content-padding
  → PART_ContentPresenter.Padding = ContentPadding
```

自定义 HeaderPadding 分支必须保持展开图标与 Header 文本的水平间距和垂直居中关系，不应继续套用默认尺寸 token 的图标间距。

## 主题与 Design Token

Expander 的默认视觉由 `ExpanderTheme.axaml` 和 `ExpanderToken` 共同定义。

主题职责：

- 提供 Header、Content、MotionActor 和 Frame 的稳定模板结构。
- 根据 `SizeType` 选择 Header/Content padding 和字体大小。
- 根据 `ExpandDirection` 设置 Header dock、旋转和图标旋转。
- Content 靠近 Header 的一侧固定承担分隔线，分隔线不依赖 `IsExpanded` 或 motion 时序。
- 根据 `IsBorderless` / `IsGhostStyle` 同时移除根边框和 Content 分隔线，并保持既有背景规则。
- 根据 `TriggerType` 设置可点击区域 cursor。
- 根据自定义 padding 伪类覆盖 Header/Content padding 和展开图标间距。

Token 关系：

```text
SharedToken
   ↓
ExpanderToken
   ↓
ExpanderTheme
   ↓
Frame + Header + ExpandButton + Content Border + Content
```

SharedToken 提供全局边框、字体、动效时长、图标大小和基础颜色。ExpanderToken 提供 Header/Content padding、Header/Content 背景、圆角和展开图标默认外边距。

Token 来源：

ExpanderToken 是 Expander 的控件级 Token scope，描述 Header/Content 的默认 padding、背景、整体圆角和展开图标默认外边距。

ExpanderToken 不承载以下状态：

- `Header`、`Content`、`AddOnContent` 等实例内容。
- `IsExpanded`、`ExpandDirection`、`TriggerType`、`ExpandIconPosition` 等实例行为状态。
- `HeaderPadding` / `ContentPadding` 的显式用户覆盖值。
- `EffectiveBorderThickness`、`ContentBorderThickness`、`EffectiveExpandButtonMargin` 等运行时派生状态。
- motion 运行状态、cancellation 或临时 transform。

## AOT 与裁剪注意事项

资源边界：

- `ExpanderToken` 通过 token generator 注册，主题通过 `ExpanderTokenResource` 消费。
- SharedToken 提供图标大小、动效开关、动效时长、边框厚度和基础颜色。
- 默认图标使用显式 `new RightOutlined()`，不做运行时图标扫描。

生命周期边界：

- `_expandButton.Click` 在模板重套用时先解除旧订阅，再订阅新 part。
- `_contentExpansion` 随当前 actor 建立；单次执行的取消源由动画执行器在完成或取消后释放。
- motion actor 临时值必须在状态归一时清理。

AOT 边界：

- 不新增字符串 path binding、运行时反射扫描或动态类型创建。
- 模板内固定关系优先使用 AXAML `TemplateBinding`、selector 和 TokenResource。
- C# 只处理 AXAML 无法表达的运行时状态流，例如 motion cancellation 和 HeaderPadding 派生 margin。

性能边界：

- Header/Content 默认视觉和未命名 Content 分隔线由静态 AXAML 提供，不在运行时动态构造模板视觉。
- 展开/收起只操作单个 `LayoutAwareMotionActor`，不遍历复杂子树。
- 展开、收起和 motion completion 不重新计算分隔线，也不创建边框 transition 或透明占位。
- 自定义 padding 的 margin 计算是常量时间，不进入渲染热路径。

## 源码索引

主要源码：

- `src/AtomUI.Desktop.Controls/Expander/Expander.cs`：公共 API、内部 effective state、template part 接入、触发区域、默认图标、边框状态、动效状态机和自定义 padding 度量。
- `src/AtomUI.Desktop.Controls/Expander/ExpanderPseudoClass.cs`：方向、自定义 padding 和展开状态相关伪类常量。
- `src/AtomUI.Desktop.Controls/Expander/ExpanderToken.cs`：Expander 控件 Token。
- `src/AtomUI.Desktop.Controls/Expander/Themes/ExpanderTheme.axaml`：控件模板、SizeType 分支、方向分支、图标位置分支、触发分支、Borderless/Ghost 分支和 token 引用。
- `tests/AtomUI.Desktop.Controls.Tests/Expander/ExpanderBehaviorTests.cs`：Expander 行为和布局回归测试。
- `src/AtomUI.Core/MotionScene/ContentExpansionAnimator.cs`：共用内容测量、进度插值与执行资源 owner。
- `tests/AtomUI.Desktop.Controls.Tests/Motion`：共用展开机制的帧级几何和边界回归。

## 相关文档

- 源设计文档：`docs/controls/desktop/data-display/expander/overview.md`
- 实现文档：`docs/controls/desktop/data-display/expander/implementation.md`
- Token 文档：`docs/controls/desktop/data-display/expander/token.md`
- 变更记录：`docs/controls/desktop/data-display/expander/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/group-box/index-cn.md

# GroupBox

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

GroupBox 是桌面端数据展示类分组容器，用于在一块有边框的区域中展示一组相关内容，并通过 Header 标题、图标和标题位置表达分组语义。

GroupBox 的职责是组织和标注内容区域，不负责内容项布局、折叠展开、表单校验、数据绑定集合管理或异步加载。需要折叠行为时应使用 Collapse/Expander 类控件；需要卡片信息组织时应使用 Card。

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/DataDisplay/GroupBox` |
| 状态 | Stable |

## 何时使用

GroupBox 的视觉语义接近 fieldset：边框提供分组边界，Header 嵌入在边框上方的视觉通道中，内容区域在边框内部保持稳定留白。

Header 可以位于左侧、居中或右侧，但标题本身始终是分组名称，不应承担操作入口或复杂工具栏职责。Header 图标用于强化分组主题，不改变 GroupBox 的交互模型。

## 公共 API

GroupBox 继承 `ContentControl`，公共 API 分为 Header API 和继承的容器 API。

Header API：

| API | 类型 | 语义 |
| --- | --- | --- |
| `HeaderTitle` | `string?` | Header 显示的分组标题。 |
| `HeaderTitleColor` | `IBrush?` | Header 标题颜色，默认来自 `ColorText`。 |
| `HeaderIcon` | `PathIcon?` | Header 标题前的图标；为 `null` 时图标节点不可见。 |
| `HeaderTitlePosition` | `GroupBoxTitlePosition` | Header 内容水平位置，支持 `Left`、`Right`、`Center`。 |
| `HeaderFontSize` | `double` | Header 标题字号。 |
| `HeaderFontStyle` | `FontStyle` | Header 标题字体样式。 |
| `HeaderFontWeight` | `FontWeight` | Header 标题字重。 |

继承 API：

- `Content` / `ContentTemplate` 定义分组内容。
- `Background` 定义内容区域背景，允许透明或半透明。
- `BorderBrush` / `BorderThickness` 定义分组边框。
- `CornerRadius` 定义边框圆角。
- `Padding` 定义内容区域内边距，默认由 GroupBox Token 提供。

GroupBox 在未显式设置 `Height` / `MaxHeight` 等外部约束时，会根据模板根节点的测量结果自动确定高度。该高度包含 Header 通道、内容区域 `Padding` 和内容自身 `DesiredSize`。内容容器仍需遵循 Avalonia 布局语义主动汇报期望尺寸，例如使用 `StackPanel`、`Grid` 或显式尺寸；裸 `Panel` / `Canvas` 等不会自然按子元素累加高度的容器不会被 GroupBox 特殊改写。

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_Frame` | `Border` | 承载整体布局根节点，并提供边框 bounds 参考。 |
| `PART_HeaderContainer` | `Panel` | Header 行容器。 |
| `PART_HeaderContent` | `Decorator` | Header 内容实际 bounds，用于标题缺口和对齐计算。 |
| `PART_HeaderIconPresenter` | `IconPresenter` | Header 图标展示。 |
| `PART_HeaderPresenter` | `TextBlock` | Header 标题展示。 |
| `PART_ContentPresenter` | `ContentPresenter` | 内容承载。 |

## 事件与命令

GroupBox 的事件与命令以控件文档、源码 public surface 和 Avalonia 基类契约为准；生成器不从源码发明额外事件。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/GroupBox/Views/GroupBoxShowCase.axaml:36`

Gallery key：`ExamplesContent` / item `0`

```axaml
<atom:GroupBox HeaderTitle="标题信息">
    <Panel Height="100">
        <atom:TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="分组框内容" />
    </Panel>
</atom:GroupBox>
```

### 自动高度

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/GroupBox/Views/GroupBoxShowCase.axaml:52`

Gallery key：`ExamplesContent` / item `1`

```axaml
<atom:GroupBox HeaderTitle="自动高度">
    <StackPanel Spacing="8">
        <atom:TextBlock Text="下面的 GroupBox 没有设置 Height，内容区域会随着文本行数自动增长。" TextWrapping="Wrap" />
        <atom:TextBlock Text="当内容来自 StackPanel、Grid 或显式尺寸控件时，GroupBox 会使用内容的 DesiredSize 计算整体高度。" TextWrapping="Wrap" />
        <atom:TextBlock Text="如果父容器设置了固定高度或 MaxHeight，则仍然会按 Avalonia 布局约束进行裁剪或滚动。" TextWrapping="Wrap" />
    </StackPanel>
</atom:GroupBox>
```

### 标题位置

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/GroupBox/Views/GroupBoxShowCase.axaml:69`

Gallery key：`ExamplesContent` / item `2`

```axaml
<StackPanel Orientation="Vertical" Spacing="10">
    <atom:GroupBox HeaderTitle="标题信息">
        <Panel Height="40">
            <atom:TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="分组框内容" />
        </Panel>
    </atom:GroupBox>
    <atom:GroupBox HeaderTitle="标题信息" HeaderTitlePosition="Center">
        <Panel Height="40">
            <atom:TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="分组框内容" />
        </Panel>
    </atom:GroupBox>
    <atom:GroupBox HeaderTitle="标题信息" HeaderTitlePosition="Right">
        <Panel Height="40">
            <atom:TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="分组框内容" />
        </Panel>
    </atom:GroupBox>
</StackPanel>
```

### 标题样式

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/GroupBox/Views/GroupBoxShowCase.axaml:96`

Gallery key：`ExamplesContent` / item `3`

```axaml
<StackPanel Orientation="Vertical" Spacing="10">
    <atom:GroupBox HeaderTitle="标题信息" HeaderFontStyle="Italic">
        <Panel Height="40">
            <atom:TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="分组框内容" />
        </Panel>
    </atom:GroupBox>
    <atom:GroupBox HeaderTitle="标题信息" HeaderTitlePosition="Center" HeaderFontWeight="Bold">
        <Panel Height="40">
            <atom:TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="分组框内容" />
        </Panel>
    </atom:GroupBox>
    <atom:GroupBox HeaderTitle="标题信息" HeaderTitlePosition="Right" HeaderFontStyle="Oblique">
        <Panel Height="40">
            <atom:TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="分组框内容" />
        </Panel>
    </atom:GroupBox>
    <atom:GroupBox HeaderTitle="标题信息" HeaderTitlePosition="Center" HeaderFontStyle="Oblique"
                   HeaderTitleColor="Coral" HeaderFontWeight="Medium">
        <Panel Height="40">
            <atom:TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="分组框内容" />
        </Panel>
    </atom:GroupBox>
</StackPanel>
```

## 状态模型

GroupBox 自身没有 hover、pressed、loading、selected、expanded 或 checked 状态。它不拦截输入事件，也不为 Header 提供默认点击行为。

GroupBox 的有效状态来自公共属性、模板测量结果和主题资源：

```text
HeaderTitle / HeaderIcon / HeaderTitlePosition
      + Header font properties
      + template measured bounds
        ↓
Header content bounds
        ↓
Frame border bounds + Header gap bounds
        ↓
Background / BorderBrush / BorderThickness / CornerRadius render state
```

`HeaderIcon`、Header 字体、标题位置和 Header 内容变化会影响缺口尺寸或位置。内容尺寸变化会通过模板根 `PART_Frame` 参与 GroupBox 的 measure pass，使自动高度随内容 `DesiredSize` 增长，同时仍尊重父容器可用空间和显式高度约束。`Background`、`BorderBrush`、`BorderThickness`、`CornerRadius` 改变会影响自绘边框和背景。

## 主题与 Design Token

GroupBox 的默认 Theme 位于 `src/AtomUI.Desktop.Controls/GroupBox/Themes/GroupBoxTheme.axaml`。Theme 提供 Header、Frame 和 Content 的可测量结构，并设置默认背景、边框、圆角、标题颜色、标题字号和间距。

标题缺口是 GroupBox 的视觉契约：Header 内容区域必须从上边框中形成缺口。缺口不能依赖用 `Background` 覆盖边框线，因为 `Background` 允许为透明或半透明。

Theme 职责：

- 设置 `Background`、`BorderBrush`、`BorderThickness`、`CornerRadius` 默认值。
- 设置 `HeaderTitleColor`、`HeaderFontSize`。
- 通过 GroupBox Token 设置 Header 容器外边距、Header 内容内边距、Header 图标间距和内容内边距。
- 根据 `HeaderTitlePosition` 设置 `PART_HeaderContent` 的水平对齐。

Theme 不负责动态构建 Header 或绘制边框。Header 缺口属于控件渲染模型。

Token 来源：

GroupBox Token 将全局 SharedToken 转换为 GroupBox 可消费的组件级结构值，主要覆盖 Header 和内容区域的间距。颜色、边框厚度、圆角和字体基础值直接来自 SharedToken，不在 GroupBox Token 中重复定义。

GroupBox Token 不表达实例状态，也不负责 Header 缺口的运行时 bounds。Header 缺口由模板测量结果和控件渲染模型共同决定。

## AOT 与裁剪注意事项

GroupBox 渲染应使用 Avalonia 绘制 API 和稳定 template part，不使用反射读取模板内部状态。

Header 缺口通过几何排除实现，不增加遮挡用背景层。该方式保持 VisualTree 简洁，也避免透明背景下依赖父背景颜色。

Token 通过动态资源进入 Theme，不应在 `Render` 中主动查找全局资源。

## 源码索引

主要源码：

- `src/AtomUI.Desktop.Controls/GroupBox/GroupBox.cs`：公共 API、template part 获取、测量 bounds、渲染和失效逻辑。
- `src/AtomUI.Desktop.Controls/GroupBox/GroupBoxToken.cs`：GroupBox 控件 Token。
- `src/AtomUI.Desktop.Controls/GroupBox/Themes/GroupBoxTheme.axaml`：模板结构、Header 对齐、TokenResource 引用和默认视觉属性。

## 相关文档

- 源设计文档：`docs/controls/desktop/data-display/group-box/overview.md`
- 实现文档：`docs/controls/desktop/data-display/group-box/implementation.md`
- Token 文档：`docs/controls/desktop/data-display/group-box/token.md`
- 变更记录：`docs/controls/desktop/data-display/group-box/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/image-previewer/index-cn.md

# ImagePreviewer

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

`ImagePreviewer` 提供单封面入口与完整图片预览；`ImageGroupPreviewer` 提供多封面入口。两者共享同一套 immutable item、当前项、
预加载、标题、窗口或 overlay、加载状态和结果租约模型。

Previewer 是应用级图片加载系统的消费者，不拥有网络栈、文件读取器、codec、缓存或全局并发调度器。所有来源解析、内容验证、
请求合并、缓存读写和解码都进入当前 `Application` 的 `IImageLoader`；Previewer 只拥有控件 entry、waiter、显示状态和结果 lease。

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/DataDisplay/ImagePreviewer` |
| 状态 | Stable |

## 何时使用

ImagePreviewer 以“页面封面 -> 沉浸预览”的两阶段体验表达图片查看。关闭态封面提供稳定尺寸和加载反馈；打开态聚焦当前完整图，
相邻项只作为低优先级预加载。单封面与多封面只改变入口布局，不改变当前项、标题、缩放、导航、来源验证和加载语义。

Loading、Failed 和 Loaded 都是明确状态。封面 Skeleton、viewer Spin 和错误内容只呈现状态，不拥有加载行为。Desktop native dialog
与 Browser overlay 共享同一 item/entry 模型，平台差异只位于宿主能力和标题栏组合。

加载架构遵守以下原则：

- 来源地址不是内容身份；路径或 URI 相同不等于内容未变化。
- 普通读取先验证来源，再决定能否复用内容和解码结果；不能根据旧来源键直接返回 decoded image。
- `ImageSourceKey -> ImageSourceVersion -> ImageContentId -> ImageDecodeKey` 是唯一身份链。
- 缓存读取策略与缓存写入位置正交，Reload 只改变一次请求的读取策略。
- Previewer 的集合、宿主和 entry 生命周期不等于 Application cache 生命周期。
- Full 与 Thumbnail 通道的 generation、取消、状态、尺寸桶和结果 lease 完全隔离。
- borrowed image 的所有权始终属于调用方，loader、cache 和 Previewer 都不能销毁它。

## 公共 API

### 3.1 封闭图片来源模型

所有加载型入口只接收 `ImageSource`。`ImageSource` 是 AtomUI 定义的封闭来源层次；基类不提供外部继承入口，每个具体来源均为
不可变 sealed 类型：

```csharp
[TypeConverter(typeof(ImageSourceConverter))]
public abstract class ImageSource
{
    public string? DisplayName { get; }

    public static ImageSource Parse(string source);
    public static bool TryParse(string? source, out ImageSource? result);
}

public sealed class HttpImageSource : ImageSource
{
    public HttpImageSource(Uri uri);
    public Uri Uri { get; }
}

public sealed class FileImageSource : ImageSource
{
    public FileImageSource(
        string path,
        ImageFileValidationMode validation = ImageFileValidationMode.Metadata);

    public string Path { get; }
    public ImageFileValidationMode Validation { get; }
}

public sealed class AssetImageSource : ImageSource
{
    public AssetImageSource(Uri uri);
    public Uri Uri { get; }
}

public sealed class StorageFileImageSource : ImageSource
{
    public StorageFileImageSource(IStorageFile file, string? revision = null);
    public IStorageFile File { get; }
    public string? Revision { get; }
}

public sealed class BytesImageSource : ImageSource
{
    public BytesImageSource(ReadOnlyMemory<byte> bytes, string? displayName = null);
    public ReadOnlyMemory<byte> Bytes { get; }
}

public sealed class StreamImageSource : ImageSource
{
    public StreamImageSource(
        Func<CancellationToken, ValueTask<Stream>> openStream,
        string? identity = null,
        string? revision = null,
        string? displayName = null);
    public Func<CancellationToken, ValueTask<Stream>> OpenStream { get; }
    public string? Identity { get; }
    public string? Revision { get; }
}

public sealed class BorrowedImageSource : ImageSource
{
    public BorrowedImageSource(IImage image, string? displayName = null);
    public IImage Image { get; }
}
```

`BytesImageSource` 防御性复制输入；`StreamImageSource` 的工厂每次必须返回新的可读流；`BorrowedImageSource` 只借用调用方的
`IImage`。字符串转换只接受 `http`、`https`、`avares`、`file` 和平台绝对路径，未知 scheme 不回退为文件路径。

文件来源提供两种验证强度：

```csharp
public enum ImageFileValidationMode
{
    Metadata,
    ContentHash
}
```

- `Metadata` 是默认值，使用已打开句柄可观察的文件 identity、长度和修改/变化令牌判断内容是否变化。
- `ContentHash` 每次请求都读取正文并计算内容摘要，用于必须识别元数据保持不变的内容替换场景。

### 3.2 图片项模型

Public 数据输入只有 `ItemsSource: IEnumerable<ImagePreviewItem>?`。`ImagePreviewItem` 是不可变配置 record：

```csharp
public sealed record ImagePreviewItem
{
    public ImagePreviewItem(ImageSource source);

    public ImageSource Source { get; init; }
    public ImageSource? ThumbnailSource { get; init; }
    public ImageSource? FallbackSource { get; init; }
    public ImageRequestOptions? RequestOptions { get; init; }
    public string? Title { get; init; }
    public object? Tag { get; init; }
}
```

`Source` 是完整图来源。`ThumbnailSource` 是可选封面来源；为空时封面使用 `Source` 按封面尺寸解码。完整图加载顺序为
`Source -> FallbackSource`，缩略图加载顺序为 `ThumbnailSource -> Source -> FallbackSource`，同一通道内相同来源 identity
不重复请求。

`ImagePreviewItem` 不保存加载状态，不持有 cancellation token、图片或 Visual。相同 item 可以出现在多个控件中，各控件的
entry 状态互不影响；底层加载器仍可按内容身份共享请求和缓存。`Tag` 只供业务关联，加载器和标题解析器不通过反射读取它。

### 3.3 请求与缓存策略

缓存读取语义与成功结果的允许存储位置是两个独立维度：

```csharp
public sealed record ImageRequestOptions
{
    public ImageCacheReadPolicy CacheRead { get; init; } =
        ImageCacheReadPolicy.ValidateSource;

    public ImageCacheStoragePolicy CacheStorage { get; init; } =
        ImageCacheStoragePolicy.MemoryAndDisk;

    public string? CachePartition { get; init; }
    public string? Variant { get; init; }
    public TimeSpan? Timeout { get; init; }
    public IReadOnlyDictionary<string, string>? Headers { get; init; }
}

public enum ImageCacheReadPolicy
{
    ValidateSource,
    RefreshSource,
    PreferCache,
    CacheOnly
}

public enum ImageCacheStoragePolicy
{
    None,
    Memory,
    MemoryAndDisk
}
```

| `ImageCacheReadPolicy` | 已有来源映射 | 来源访问 | cache miss | 结果验证语义 |
| --- | --- | --- | --- | --- |
| `ValidateSource` | 仅在来源规则确认有效后复用 | 按 File、HTTP、Storage 等规则探测或重验证 | 访问来源 | `Current`、`Revalidated` 或 `NotRequired` |
| `RefreshSource` | 不把现有映射视为完成结果 | 强制访问来源；HTTP 可以使用 validator 发条件请求 | 访问来源 | `Current`、`Revalidated` 或 `NotRequired` |
| `PreferCache` | 映射和内容都存在时直接复用 | 仅 miss 时访问来源 | 访问来源 | 未验证命中为 `Unverified` |
| `CacheOnly` | 只使用已有映射和内容 | 禁止 | `CacheMiss` | 未验证命中为 `Unverified` |

`PreferCache` 不隐含后台刷新。一次 `LoadAsync` 不会在返回后静默替换结果；需要新内容时由调用方显式发起
`RefreshSource` 请求。`CacheStorage` 只决定本次成功结果允许写入 memory 或 disk；HTTP cache directive、安全策略和平台能力
只能进一步收紧，不能被 per-request 设置放宽。persistent cache 未启用时，`MemoryAndDisk` 的有效目标是 memory。

`ImageSourceValidation.Current` 表示内容满足该来源选择的验证契约，不表示所有来源都完成了逐字节证明。File 的
`Metadata` 模式以文件 identity 和 metadata token 为契约；需要逐字节证明时使用 `ContentHash`。

所有调用方提供、可能影响 HTTP 表示的 header 都以名称和不可逆值摘要进入来源 identity；header 原值不进入可打印 key、
持久文件名或默认 diagnostics。存在认证 header 而未提供 `CachePartition` 时，请求禁止共享、禁止读取共享缓存并强制不存储。

### 3.4 AbstractImagePreviewer 公共契约

| 契约组 | 成员与默认值 | 语义 |
| --- | --- | --- |
| 集合 | `ItemsSource=null` | 唯一图片项输入 |
| 当前项 | `CurrentIndex=0`、`CurrentItem` | `CurrentIndex` 默认 TwoWay；显示时 clamp，但不静默改写外部值 |
| 封面 | `CoverIndex=0`、`CoverWidth=NaN`、`CoverHeight=NaN` | CoverIndex 非负并在显示时 clamp，与 CurrentIndex 独立 |
| 预加载 | `PreloadCount=1` | 当前项前后各预加载的完整图片数量 |
| 切换显示 | `ImageSwitchMode=Immediate` | `Immediate` 在目标无图时同周期清空旧图并报告 loading；`WaitForLoaded` 在有效保留帧存在时持续显示旧图直到目标完成或失败 |
| 当前加载状态 | `CurrentLoadState/Error/Progress`、`IsCurrentLoading/Loaded/Failed` | 当前完整图 entry 的只读投影 |
| 占位内容 | `LoadingContent/Template`、`ErrorContent/Template` | 只替换主题 presenter 内容，不改变状态机 |
| 打开状态 | `IsOpen=false` | 默认 TwoWay，统一驱动 native dialog 或 Browser overlay |
| 预览交互 | `IsImageMovable=true`、`ImageScaleStep=0.5`、`ImageMinScale=1`、`ImageMaxScale=50` | 拖拽、缩放、旋转和 fit-to-window 的边界 |
| 窗口 | `IsDialogModal=false`、`IsDialogTopmost=false` | Desktop native window 行为；Browser 使用 overlay |
| 标题 | `PreviewTitle`、`PreviewTitleIcon`、`PreviewTitleResolver` | 当前项标题和可选 PathIcon |
| 动效 | `IsMotionEnabled` | 控制主题动效，不改变加载与集合语义 |

命令和事件：

- `OpenDialog()` 打开当前宿主；无有效 item 时不打开。
- `ReloadCurrent()` 只刷新当前完整图通道。
- `ReloadItem(index)` 只刷新指定 entry 的完整图通道；越界抛出 `ArgumentOutOfRangeException`。
- `DialogOpened`、可取消的 `DialogClosing` 和 `DialogClosed` 描述宿主生命周期。
- `ImageOpened`、`ImageFailed` 只投影当前完整图状态，事件参数包含 immutable item 与索引。

### 3.5 ImagePreviewer 与 ImageGroupPreviewer

单封面 `ImagePreviewer` 增加：

- `CoverIndicatorContent`、`CoverIndicatorContentTemplate`；
- `IsShowCoverMask=true`；
- `CoverLoadState/Error/Progress`；
- `IsCoverLoading`、`IsCoverLoaded`、`IsCoverFailed`；
- `ReloadCover()`，只刷新当前封面的 Thumbnail 通道。

`ImageGroupPreviewer` 只增加 `ItemsPanel`。关闭态下它为每个 effective item 请求缩略图；点击某个封面时先把
`CurrentIndex` 设置为该项索引，再打开预览。单封面控件的 CoverIndex 只决定关闭态封面；点击单封面只打开预览，不隐式把
CurrentIndex 同步为 CoverIndex。

稳定 template part 包括 `PART_CoverItemsControl`、`PART_ImageViewerScene`、`PART_ImageRenderer`、
`PART_LoadingPresenter`、`PART_ErrorPresenter`、`PART_PreviousButton`、`PART_NextButton`、`PART_TitleLayout`、
`PART_IconPresenter` 和 `PART_CloseButton`。

LLMS 语义区域：

| Part | AtomUI 节点 | 职责 | 相关 API | 相关 Token | 稳定性 |
| --- | --- | --- | --- | --- | --- |
| `root` | `AbstractImagePreviewer` | 归一 items、current、open state、宿主和加载策略 | `ItemsSource`、`CurrentIndex`、`IsOpen`、`PreloadCount` | SharedToken、ImagePreviewerToken | public |
| `cover` | `ImagePreviewer` / `PART_CoverItemsControl` | 展示单封面或 group 缩略图及 loading/error 状态 | Cover size/state、`ItemsPanel` | Cover size、mask、radius Token | public/template-stable |
| `host` | `ImagePreviewerDialog` / `ImagePreviewerOverlayHost` | 承载 Desktop window 或 Browser overlay | dialog、modal、topmost、title、motion API | Window、overlay、motion Token | internal-observable |
| `viewer` | `ImageViewer` / `PART_ImageViewerScene` | 当前项导航、fit、拖拽、缩放和旋转 | interaction、scale、CurrentIndex | Viewer background、toolbar Token | internal-observable |
| `renderer` | `PART_ImageRenderer` | 只渲染 entry 已持有的 `IImage` | current load state | 无独立加载 Token | template-stable |
| `loading` | `PART_LoadingPresenter` | 呈现 Skeleton 或 Spin，不拥有请求 | LoadingContent/Template | Loading、Skeleton、Spin Token | template-stable |
| `error` | `PART_ErrorPresenter` | 呈现最终失败内容，不改变状态机 | ErrorContent/Template | Error semantic Token | template-stable |

## 事件与命令

命令和事件：
- `ImageOpened`、`ImageFailed` 只投影当前完整图状态，事件参数包含 immutable item 与索引。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/ImagePreviewer/Views/ImagePreviewerShowCase.axaml:36`

Gallery key：`ExamplesContent` / item `0`

```axaml
<atom:ImagePreviewer Width="200"
```

### 远程图片加载

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/ImagePreviewer/Views/ImagePreviewerShowCase.axaml:48`

Gallery key：`ExamplesContent` / item `1`

```axaml
<atom:ImagePreviewer Width="200"
```

### 容错

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/ImagePreviewer/Views/ImagePreviewerShowCase.axaml:60`

Gallery key：`ExamplesContent` / item `2`

```axaml
<atom:ImagePreviewer Width="200"
```

### 20 张远程图片

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/ImagePreviewer/Views/ImagePreviewerShowCase.axaml:72`

Gallery key：`ExamplesContent` / item `3`

```axaml
<atom:ImagePreviewer Width="200"
```

## 状态模型

### 4.1 内容身份与来源验证

内部身份链固定为：

```text
ImageSourceKey --validate--> ImageSourceVersion --maps-to--> ImageContentId
ImageContentId + ImageDecodeSpec ---------------------------> ImageDecodeKey
```

| 术语 | 定义 | 是否包含解码尺寸 |
| --- | --- | --- |
| `ImageSourceKey` | 规范来源地址或 identity、representation header 摘要、Variant、partition 摘要和 reader contract | 否 |
| `ImageSourceVersion` | 某次来源验证观察到的版本令牌 | 否 |
| `ImageContentId` | 对读取到的精确编码字节计算的 SHA-256 内容身份 | 否 |
| `ImageDecodeSpec` | codec、安全策略、目标物理像素、颜色和方向等影响输出的参数 | 按 codec 决定 |
| `ImageDecodeKey` | partition、ContentId 与 DecodeSpec 组成的解码身份 | 按 codec 决定 |
| `ImageSourceSnapshot` | SourceKey、SourceVersion、ContentId、freshness/validator 和提交 generation 的不可变映射 | 否 |

`Timeout`、Priority、progress callback、CacheRead 和 CacheStorage 不进入内容身份。它们只控制单个 waiter 的执行方式。

来源验证策略：

| Source | SourceVersion 或验证契约 | 跨请求来源映射 |
| --- | --- | --- |
| File | 已打开句柄的 file identity、长度和 modify/change stamp；严格模式读取哈希 | Desktop 可持久化 |
| HTTP | FreshUntil、ETag、Last-Modified 和 Vary | 服从响应缓存指令 |
| Asset | 当前 Application build/resource identity 内不可变 | 可持久化 |
| StorageFile | 显式 revision，或平台可观察 basic properties | 仅稳定 identity/revision 可持久化 |
| Bytes | 防御性副本直接计算 ContentId | 不建立来源持久映射 |
| Stream | 显式 identity + revision；否则每次打开并计算 ContentId | 仅稳定 identity/revision 可持久化 |
| Borrowed image | 对象 identity；不进入 encoded/decoded cache | 不持久化 |

File 的 metadata probe 和正文读取基于同一个已打开句柄，避免路径探测与正文读取之间的替换竞态。HTTP fresh snapshot 可直接
复用；过期或 `no-cache` snapshot 必须重验证，优先使用 ETag，其次 Last-Modified；`304` 延续原 ContentId，`200` 对新正文
重新计算 ContentId；`no-store` 不保留 source、encoded 或 decoded 条目。

调用方为 Stream 或 Storage 提供 revision，即承诺 revision 不变时内容不变；无法作出该承诺时必须省略 revision，让 loader
重新读取并以 ContentId 判断内容复用。

### 4.2 集合与双通道状态

`ItemsSource` 物化为控件内部的 `ImagePreviewEntry` 集合。每个 entry 拥有两个互相隔离的通道：

| 通道 | 内容 | 状态与资源 |
| --- | --- | --- |
| Full | 预览完整图 | FullState/Error/Progress、generation、waiter CTS、物理像素 bucket、`ImageLoadResult` lease |
| Thumbnail | 页面封面 | ThumbnailState/Error/Progress、generation、waiter CTS、独立 bucket 与 lease |

集合支持 enumerable replacement，以及 `INotifyCollectionChanged` 的 Add、Remove、Move、Replace 和 Reset。未变的 immutable item
可以复用 entry 与现有 lease；被移除或替换的 entry 立即取消并 dispose。detach 期间解除集合订阅，reattach 时重新物化当前集合，
补齐离线变更。

集合动作只影响 Previewer 自己的 entry、waiter 和 lease，绝不调用全局或分区 cache clear：

| 集合或宿主动作 | Previewer 行为 | Application cache |
| --- | --- | --- |
| Add | 创建 entry，按 open/cover/preload 状态请求 | 正常共享 |
| Remove / Replace | 取消并 dispose 旧 entry | 不清理 |
| Move | 移动 entry，保留其通道与 lease | 不清理 |
| Reset / Clear | 复用仍存在的 item，其余 entry 全部 dispose | 不清理 |
| ItemsSource replacement | 重新物化，释放不再使用的 entry | 不清理 |
| host close | 释放 Full waiter/lease，恢复关闭态 Thumbnail 策略 | 不清理 |
| detach | 释放 Full/Thumbnail waiter/lease 并退订集合 | 不清理 |

缓存正确性不依赖集合 Clear。相同路径的文件被替换后，`ValidateSource` 产生新的 SourceVersion；新字节产生新的 ContentId，进而
产生新的 DecodeKey。旧 decoded entry 不能作为新内容命中，只能保留为受预算约束的 LRU 条目直至驱逐。

### 4.3 加载优先级、尺寸与预加载

| 场景 | Source 顺序 | `ImageRequestPriority` | 默认 CacheRead |
| --- | --- | --- | --- |
| 当前完整图 | `Source -> FallbackSource` | `Critical` | item options；缺省为 `ValidateSource` |
| 单封面或 group 缩略图 | `ThumbnailSource -> Source -> FallbackSource` | `High` | item options |
| 相邻完整图预加载 | `Source -> FallbackSource` | `Preload` | item options |

打开态加载当前完整图和 `PreloadCount` 邻近窗口；离开窗口的未完成 Full waiter 被取消。邻项按与 CurrentIndex 的距离从近到远
提交。关闭宿主时释放所有 Full lease，再恢复关闭态封面请求；Thumbnail lease 保留供页面封面继续显示。

完整图解码目标来自 TopLevel ClientSize，封面目标来自 CoverWidth/CoverHeight 或实际 Bounds，均乘 render scaling 并向上量化
到 16 px bucket。Full 与 Thumbnail 分别按 bucket 保持请求幂等；布局、窗口尺寸或 DPI 变化产生更合适的 bucket 时保留当前图片并
异步升级，成功后原子替换。明确得到 0 x 0 时不发起无意义的封面请求。SVG 等尺寸无关 codec 不把显示尺寸写入 DecodeSpec。

### 4.4 Fallback、Reload 与结果事件

Fallback 是每个 item、每个通道的局部策略。一个 item 失败不能删除其他 item、替换 ItemsSource 或改变 CurrentIndex。取消不触发
fallback，也不提交 Failed。

`ReloadCurrent()`、`ReloadItem(index)` 和 `ReloadCover()` 使用 item 的 RequestOptions 副本，只把目标通道本次请求的
`CacheRead` 覆盖为 `RefreshSource`。调用方 options 不被修改；另一个通道和其他 item 的 generation、waiter、state 与 lease
不受影响。

只有当前 Full entry 从非 Loaded 进入 Loaded 时触发 `ImageOpened`，从非 Failed 进入 Failed 时触发 `ImageFailed`。预加载项和
封面状态不冒充当前项事件。

加载结果把“从哪里取得”和“来源是否已验证”分开表达：

```csharp
public enum ImageLoadOrigin
{
    Borrowed,
    DecodedMemory,
    EncodedMemory,
    Persistent,
    Network,
    Local
}

public enum ImageSourceValidation
{
    NotRequired,
    Current,
    Revalidated,
    Unverified
}
```

成功结果至少包含 `Image`、`ContentId`、`Origin`、`SourceValidation`、原始/解码尺寸、media type 和阶段耗时。borrowed image 的
ContentId 为空，Origin 为 `Borrowed`，SourceValidation 为 `NotRequired`。结果 lease 独立于 cache membership；条目被驱逐时，
仍被控件持有的 image 在最后一个 lease 释放前保持有效。

### 4.5 切换显示、标题与宿主

`ImageSwitchMode` 决定目标图片切换时是否保留上一张图片。`Immediate` 在目标没有可显示图片时于同一 UI 更新周期清空旧图，并把
Idle/Loading 的待完成目标投影为 loading；`WaitForLoaded` 在有效保留帧存在时持续显示旧图，目标加载状态仍保持为 true，但 loading
presenter 由 `:loading:not(:has-image)` 门控而不覆盖旧图。失败呈现、事件时序、请求与租约语义不因模式变化；运行时切换模式立即重算
预览窗口与单封面状态。
完整显示矩阵和保留帧生命周期见 [ImagePreviewer 切换显示设计](switch-display-design.md)。

标题优先级固定为：

```text
宿主显式 Window.Title / PreviewTitle
  > ImagePreviewItem.Title
  > IImagePreviewTitleResolver
  > 空标题
```

默认 resolver 返回 `Item.Source.DisplayName`。resolver 上下文只包含 immutable item、显示用 CurrentIndex 和 Count，不发起 I/O。
`PreviewTitleIcon` 只显示在预览标题栏，不投射到普通 Window icon。

Desktop 支持 native window 时使用 `ImagePreviewerDialog`；Browser 等无 native window 平台使用
`ImagePreviewerOverlayHost`。两种宿主共享 ItemsSource、CurrentIndex TwoWay、交互、占位、动效、加载和关闭语义。

## 主题与 Design Token

稳定主题节点包括：

- `PART_CoverItemsControl`：group 封面集合；
- `PART_ImageViewerScene`、`PART_ImageRenderer`：预览坐标空间与图片 renderer；
- `PART_LoadingPresenter`、`PART_ErrorPresenter`：封面和 viewer 的状态占位；
- `PART_PreviousButton`、`PART_NextButton` 与 toolbar 操作按钮；
- `PART_TitleLayout`、`PART_IconPresenter`：dialog 标题与图标；
- `PART_CloseButton`：overlay 关闭入口。

Renderer 只消费 entry 已提交的 `IImage`，不得自行打开 Source 或访问 loader/cache。Loading 时封面使用稳定尺寸 Skeleton，viewer
使用居中 Spin；Failed 时使用本地化默认错误内容或用户模板。viewer 的加载指示器由 `:loading:not(:has-image)` 门控，只在无可
显示图片时呈现。Loading/error 自定义模板只替换内容，不能拥有请求、entry 或结果 lease。

本设计不改变现有缩放、拖拽、旋转、导航、标题栏、封面 mask、Token 和 Light/Dark 视觉契约。Token 的来源、计算和消费位置见
[ImagePreviewer Token 设计](token.md)。

Token 来源：

ImagePreviewer Token 只表达组件级视觉变量，例如尺寸、间距、颜色、圆角、阴影、图标尺寸和弹层边界。Token 不承载运行时选择、展开、加载、错误、上传任务、过滤条件或业务状态。

当前 Token scope：

- `ImagePreviewerToken`，scope id 为 `ImagePreviewer`，源码位于 `src/AtomUI.Desktop.Controls/ImagePreviewer/ImagePreviewerToken.cs`。

## AOT 与裁剪注意事项

- ItemsSource、entry、host 和 loader 之间没有 Visual -> business item 的反向长期引用。
- Previewer collection、host 和 entry 生命周期只管理 waiter 与 result lease；任何 Add/Remove/Replace/Move/Reset/Clear、host close
  或 detach 都不能触发 Application cache clear。
- cache membership、source/decode operation reference 和 result lease 是三个独立持有关系；LRU 驱逐先移除 membership，活动引用
  归零后再释放 owned image。
- 显示 tracker 重算热路径零分配；目标项与保留帧订阅严格经单一变更通道配对，`Immediate` 模式不持有保留帧（额外持有恒为 0），
  `WaitForLoaded` 至多持有 1 个保留帧 entry 及其解码图租约。
- entry Dispose 的重置通知使用静态缓存 EventArgs，无订阅者时零开销。
- 任何 cancellation、result dispose 或 event callback 都不在 Shared coordinator/cache lock 内由 Previewer执行。
- 不进行同步 HTTP/File I/O，不使用固定延迟，不创建私有 cache/scheduler。
- `ImageSource` reader、validator、codec 和 metadata serializer 使用静态注册或 source-generated serializer；Public data model、
  AXAML property 和 title resolver 都不依赖反射扫描。
- Browser overlay 与 Desktop dialog 共用同一 entry/load model；平台差异只位于宿主能力。
- Application dispose 统一取消底层 loader；控件仍负责尽快取消 waiter 和释放自身租约。
- borrowed `IImage` 始终由调用方拥有，loader、cache、entry 和 Application dispose 都不能销毁它。

## 源码索引

`src/AtomUI.Desktop.Controls/ImagePreviewer/` 的稳定职责如下：

| 类型 | 职责 |
| --- | --- |
| `AbstractImagePreviewer` | public API、ItemsSource 物化、current entry、预加载策略和 dialog/overlay 生命周期 |
| `ImagePreviewItem` | immutable public 配置，包含 `ImageSource` 类型的 Source、Thumbnail、Fallback，以及 Options、Title 和 Tag |
| `ImagePreviewEntry` | internal Full/Thumbnail 状态、generation、取消、结果租约 owner；Dispose 前广播重置通知 |
| `ImagePreviewDisplayTracker` | internal 宿主显示状态机：目标项、tracker 会话标识、会话内单调目标序号与 WaitForLoaded 保留帧跟踪、显示图三值解析（见[切换显示设计](switch-display-design.md)） |
| `ImagePreviewer` | 单封面选择、状态投影和 `ReloadCover()` |
| `ImageGroupPreviewer` | 多封面 ItemsControl、点击索引和关闭态缩略图请求 |
| `ImagePreviewerDialog` | Desktop native window、标题算法、CurrentIndex relay 和 viewer 组合 |
| `ImagePreviewerOverlayHost` | Browser/无原生窗口平台的 overlay 宿主 |
| `ImageViewer` | 导航、变换、fit-to-window 和 loading/error 状态呈现 |
| `ImagePreviewRenderer` | 只渲染 entry 已持有的 `IImage` |
| `IImagePreviewTitleResolver` | 无 I/O 的标题解析扩展点 |

底层 `IImageLoader`、`ImageSource`、Request、Result、source snapshot、encoded/decoded store、scheduler、coordinator、transport
和 codec 位于 `AtomUI.Controls.Shared/ImageLoading/`。Previewer 目录不保留这些类型的副本。

## 相关文档

- 源设计文档：`docs/controls/desktop/data-display/image-previewer/overview.md`
- 实现文档：`docs/controls/desktop/data-display/image-previewer/implementation.md`
- Token 文档：`docs/controls/desktop/data-display/image-previewer/token.md`
- 变更记录：`docs/controls/desktop/data-display/image-previewer/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/info-flyout/index-cn.md

# InfoFlyout

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

InfoFlyout 是 AtomUI 桌面控件体系中的信息浮出层家族，用于从目标元素弹出普通内容、菜单内容或树形内容。

InfoFlyout 不负责 Dialog 模态系统、Tooltip 简短提示或导航菜单本体。这些职责应由业务层、组合控件或更专用的 AtomUI 控件承担。

主要源码入口：

- `src/AtomUI.Desktop.Controls/Flyouts`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/DataDisplay/InfoFlyout` |
| 状态 | Stable |

## 何时使用

InfoFlyout 的设计语言围绕控件职责、可观察状态和主题契约组织，而不是围绕模板节点组织。

| 维度 | 含义 | InfoFlyout 中的表达 |
| --- | --- | --- |
| 产品语义 | 控件在界面中承担的稳定职责。 | InfoFlyout 是 AtomUI 桌面控件体系中的信息浮出层家族，用于从目标元素弹出普通内容、菜单内容或树形内容。 |
| 内容承载 | 用户数据、展示内容、集合项或操作入口如何进入控件。 | `Content`、`ItemContainerTheme`、`ItemTemplate`、`ItemsSource`。 |
| 状态反馈 | public API、内部状态和伪类如何形成用户可感知反馈。 | open/close、collection/filter、motion、visual option。 |
| 主题语义 | ControlTheme、SharedToken、控件 Token 和模板绑定如何表达视觉。 | InfoFlyout Token + ControlTheme。 |

## 公共 API

InfoFlyout 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。

核心 public surface 按语义分组维护：

| 契约组 | 代表成员 | 维护含义 |
| --- | --- | --- |
| 内容与数据 | `Content`、`ItemContainerTheme`、`ItemTemplate`、`ItemsSource` | 定义控件展示内容、输入数据、模板或业务对象入口。 |
| 选择与集合 | `DisplayPageSize` | 维护选择、展开、过滤、分页、分组或集合状态。 |
| 交互与状态 | `IsArrowVisible`、`IsLightDismissEnabled`、`IsMotionEnabled`、`IsPointAtCenter`、`ShouldUseOverlayPopup` | 表达用户可观察状态、可用性、清除、加载或反馈语义。 |
| 视觉与布局 | `ArrowPosition`、`MarginToAnchor`、`OverlayHostShadow`、`Placement`、`PlacementAnchor`、`PlacementGravity`、`PopupRootShadow`、`RequestedPlacement`、`SizeType` | 影响尺寸、位置、颜色、形状、密度和模板视觉变量。 |
| 弹层与窗口 | `Flyout`、`FlyoutPresenterTheme` | 控制 popup、flyout、dialog、window 或 overlay 宿主协作。 |
| 动效与异步 | `CloseMotion`、`MotionDuration`、`MouseEnterDelay`、`MouseLeaveDelay`、`OpenMotion` | 约束动效开关、异步加载、播放速度、超时和任务边界。 |
| 其他稳定入口 | `AnchorTarget`、`Trigger`、`TriggerType` | 保留为 public surface，变更前需确认 Gallery 和用户 XAML 依赖。 |

稳定事件包括 `MenuItemClicked`、`TreeViewItemClicked`。事件触发顺序属于兼容契约，不能因内部状态重排而改变。

主要公开类型与枚举：

- 类型：`Flyout`、`FlyoutHost`、`FlyoutMenuItemClickedEventArgs`、`FlyoutPresenter`、`FlyoutStateHelper`、`FlyoutTreeViewItemClickedEventArgs`、`MenuFlyout`、`MenuFlyoutPresenter`、`TreeViewFlyout`、`TreeViewFlyoutPresenter`。
- 枚举：`FlyoutTriggerType`。

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_ArrowDecorator` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_ContentPresenter` | `?` | 展示用户内容、文本、图标或模板化数据。 |
| `PART_ItemsPresenter` | `?` | 展示用户内容、文本、图标或模板化数据。 |

当前未抽取到控件专属伪类；主题主要依赖 Avalonia 标准伪类、模板绑定和内部 StyledProperty。

## 事件与命令

InfoFlyout 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。
稳定事件包括 `MenuItemClicked`、`TreeViewItemClicked`。事件触发顺序属于兼容契约，不能因内部状态重排而改变。
- 类型：`Flyout`、`FlyoutHost`、`FlyoutMenuItemClickedEventArgs`、`FlyoutPresenter`、`FlyoutStateHelper`、`FlyoutTreeViewItemClickedEventArgs`、`MenuFlyout`、`MenuFlyoutPresenter`、`TreeViewFlyout`、`TreeViewFlyoutPresenter`。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/InfoFlyout/Views/InfoFlyoutShowCase.axaml:35`

Gallery key：`ExamplesContent` / item `0`

```axaml
<StackPanel Orientation="Vertical" Spacing="10">
    <atom:FlyoutHost Trigger="Hover" ShouldUseOverlayPopup="False">
        <atom:FlyoutHost.Flyout>
            <atom:Flyout>
                <TextBlock Width="200" Height="100" Padding="20" Text="这是最基础的示例。" />
            </atom:Flyout>
        </atom:FlyoutHost.Flyout>
        <atom:Button ButtonType="Primary" Content="移入我" />
    </atom:FlyoutHost>
</StackPanel>
```

### 三种触发方式

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/InfoFlyout/Views/InfoFlyoutShowCase.axaml:55`

Gallery key：`ExamplesContent` / item `1`

```axaml
<StackPanel Orientation="Horizontal" Spacing="10">
    <atom:FlyoutHost Trigger="Hover" ShouldUseOverlayPopup="False">
        <atom:FlyoutHost.Flyout>
            <atom:Flyout>
                <TextBlock Width="200" Height="100" Padding="20" Text="这是最基础的示例。" />
            </atom:Flyout>
        </atom:FlyoutHost.Flyout>
        <atom:Button Content="移入我" />
    </atom:FlyoutHost>
    <atom:FlyoutHost Trigger="Focus" ShouldUseOverlayPopup="False">
        <atom:FlyoutHost.Flyout>
            <atom:Flyout>
                <TextBlock Width="220" Height="100" Padding="20" Text="这是最基础的示例。" />
            </atom:Flyout>
        </atom:FlyoutHost.Flyout>
        <atom:Button Content="聚焦我" />
    </atom:FlyoutHost>
    <atom:FlyoutHost Trigger="Click" ShouldUseOverlayPopup="False">
        <atom:FlyoutHost.Flyout>
            <atom:Flyout>
                <TextBlock Width="200" Height="100" Padding="20" Text="这是最基础的示例。" />
            </atom:Flyout>
        </atom:FlyoutHost.Flyout>
        <atom:Button Content="点击我" />
    </atom:FlyoutHost>
</StackPanel>
```

## 状态模型

InfoFlyout 的状态流按以下路径收敛：

```text
Public API / inherited command / item source / user input
  -> 控件实例状态
  -> effective state / pseudo-class / template property
  -> ControlTheme selector / presenter / renderer
  -> Gallery 可观察行为
```

状态维护规则：

- Disabled 或不可交互状态优先屏蔽 pointer、keyboard、motion 和提交类反馈。
- open/close、collection/filter、motion、visual option 状态由控件实例或明确的数据 owner 推导，不能在 template part 之间双向竞争。
- 模板重套用时必须把 public API 对应状态回放到新的 part、伪类和主题变量。
- 集合、弹层、异步、动效或窗口相关状态必须能处理 reset、close、cancel、detach 和 owner 释放。

## 主题与 Design Token

InfoFlyout 的视觉模型由控件模板、ControlTheme、SharedToken 和必要的控件 Token 共同构成。

| 主题文件 | 职责 |
| --- | --- |
| `FlyoutHostTheme.axaml` | 定义弹层、窗口或 overlay 宿主视觉。 |
| `MenuFlyoutPresenterTheme.axaml` | 定义弹层、窗口或 overlay 宿主视觉。 |
| `TreeViewFlyoutPresenterTheme.axaml` | 定义弹层、窗口或 overlay 宿主视觉。 |

InfoFlyout 使用 `FlyoutHostToken`、`TreeFlyoutToken` 作为控件 Token scope。Token 只表达组件视觉语义，不承载 open/close、collection/filter、motion、visual option 运行时状态。

主题维护规则：

- 不删除或重命名已经稳定的 ControlTheme key、template part、伪类和资源 key。
- 不把可由 AXAML 表达的模板状态迁移为 C# 动态创建视觉。
- 不把 hover、pressed、selected、expanded、loading、filter、popup open 等运行时状态写入 Token。
- Browser 或平台特化主题必须保持同一 API 的语义一致。

Token 来源：

InfoFlyout Token 只表达组件级视觉变量，例如尺寸、间距、颜色、圆角、阴影、图标尺寸和弹层边界。Token 不承载运行时选择、展开、加载、错误、上传任务、过滤条件或业务状态。

当前 Token scope：

- `FlyoutHostToken`，scope id 为 `FlyoutHost`，源码位于 `src/AtomUI.Desktop.Controls/Flyouts/FlyoutHostToken.cs`。
- `TreeFlyoutToken`，scope id 为 `TreeFlyout`，源码位于 `src/AtomUI.Desktop.Controls/Flyouts/TreeFlyoutToken.cs`。

## AOT 与裁剪注意事项

资源和 AOT 约束：

- 不通过运行时反射扫描 public API、Token 或 Gallery 示例数据。
- 不把可静态声明的模板结构迁移到 C# 动态创建。
- 异步加载、上传、弹层和窗口生命周期必须能取消或释放。
- 缓存对象必须与控件、窗口、弹层或数据 owner 生命周期一致。
- Source generator 生成文件不手工编辑；需要修改时改输入源或 generator。

性能边界：

- 控件应优先复用 Avalonia 原生虚拟化、模板绑定和资源系统。
- 避免为每次状态变化创建不必要的视觉对象、订阅或动画对象。
- 大集合控件必须保证 container recycle 后不会泄漏旧 item 状态。

## 源码索引

主要源码文件：

- `src/AtomUI.Desktop.Controls/Flyouts/Flyout.cs`
- `src/AtomUI.Desktop.Controls/Flyouts/FlyoutHost.cs`
- `src/AtomUI.Desktop.Controls/Flyouts/FlyoutHostToken.cs`
- `src/AtomUI.Desktop.Controls/Flyouts/FlyoutPresenter.cs`
- `src/AtomUI.Desktop.Controls/Flyouts/FlyoutStateHelper.cs`
- `src/AtomUI.Desktop.Controls/Flyouts/MenuFlyout.cs`
- `src/AtomUI.Desktop.Controls/Flyouts/MenuFlyoutPresenter.cs`
- `src/AtomUI.Desktop.Controls/Flyouts/PopupFlyoutBaseReflectionExtensions.cs`
- `src/AtomUI.Desktop.Controls/Flyouts/Themes/FlyoutHostTheme.axaml`
- `src/AtomUI.Desktop.Controls/Flyouts/Themes/MenuFlyoutPresenterTheme.axaml`
- `src/AtomUI.Desktop.Controls/Flyouts/Themes/MenuFlyoutPresenterTheme.cs`
- `src/AtomUI.Desktop.Controls/Flyouts/Themes/TreeViewFlyoutPresenterTheme.axaml`
- `src/AtomUI.Desktop.Controls/Flyouts/Themes/TreeViewFlyoutPresenterTheme.cs`
- `src/AtomUI.Desktop.Controls/Flyouts/TreeFlyoutToken.cs`
- `src/AtomUI.Desktop.Controls/Flyouts/TreeViewFlyout.cs`
- `src/AtomUI.Desktop.Controls/Flyouts/TreeViewFlyoutPresenter.cs`

职责边界：

- 控件主文件保留 public/protected API、Avalonia 属性注册、事件和主要生命周期入口。
- Theme 文件负责静态视觉结构、template part、selector 和资源绑定。
- Token 文件只提供组件视觉变量，不保存实例状态。
- Gallery 文件只展示用法和示例，不作为运行时逻辑 owner。

## 相关文档

- 源设计文档：`docs/controls/desktop/data-display/info-flyout/overview.md`
- 实现文档：`docs/controls/desktop/data-display/info-flyout/implementation.md`
- Token 文档：`docs/controls/desktop/data-display/info-flyout/token.md`
- 变更记录：`docs/controls/desktop/data-display/info-flyout/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/list-box/index-cn.md

# ListBox

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

ListBox 是桌面端数据展示类轻量列表选择控件，用于展示一组纵向排列的简单选项、候选项、搜索结果或可点击条目。它以 Avalonia `ListBox` 为基础，扩展 AtomUI 的尺寸、选中指示器、空状态、文本过滤高亮、Token 和 CandidateList 复用能力。

ListBox 的职责是管理列表容器生成、单选或多选、键盘导航、条目点击、空状态展示、文本过滤展示和虚拟化容器状态回放。它不负责分组、排序、分页、复杂数据视图刷新、远程搜索请求、树形层级、表格列模型或业务命令编排；这些场景应使用 `ListView`、TreeView、DataGrid 或组合控件。

ListBox 支持两种条目提供方式：

- 直接放置 `ListBoxItem`。
- 通过 `ItemsSource` 绑定数据项，并由 ListBox 生成 `ListBoxItem` 容器。

直接子元素适合静态少量条目；`ItemsSource` 适合数据驱动列表、候选列表和需要模板复用的场景。

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/DataDisplay/List` |
| 状态 | Stable |

## 何时使用

ListBox 表达的是“紧凑纵向条目 + 当前选择目标”的信息浏览语义。用户应能通过行距、hover 背景、selected 背景、可选选中标记、禁用状态和过滤高亮理解当前可操作条目。

设计语言由以下维度组成：

| 维度 | 含义 | 典型表达 |
| --- | --- | --- |
| 列表结构 | 一组同级条目按纵向排列。 | ScrollViewer、ItemsPresenter、条目间距。 |
| 选择状态 | 当前业务选择目标。 | selected 背景、`SelectedItem` / `SelectedItems`、选中指示器。 |
| 交互状态 | 鼠标、触摸、键盘导致的临时反馈。 | pointerover 背景、键盘移动选择。 |
| 禁用状态 | 条目或列表不可交互。 | disabled 文本色、禁用选择更新。 |
| 过滤状态 | 搜索或过滤命中的文本。 | 高亮文本、可选隐藏未命中项、过滤空状态。 |
| 空状态 | 无数据或过滤无结果。 | `EmptyIndicator` 内容区域。 |

ListBox 的视觉强度应保持克制。条目是可扫描的信息行，不应被绘制成卡片；选中指示器是补充信号，不应替代 selected 背景和文本状态。

## 公共 API

ListBox 的公共契约由 ListBox API、ListBoxItem API、事件 API、template part 和主题入口组成。

ListBox 核心 API：

| API | 语义 |
| --- | --- |
| `IsSelectable` | 是否允许用户交互更新选择。关闭时清空当前选择。 |
| `SelectionMode` / `SelectedIndex` / `SelectedItem` / `SelectedItems` / `Selection` | 继承 Avalonia `ListBox` 的选择模型。 |
| `SizeType` | 控制整体圆角、空状态 padding 和条目高度、padding。 |
| `IsBorderless` | 是否隐藏 root 边框。 |
| `ItemHoverBg` / `ItemSelectedBg` | 条目 hover 和 selected 背景入口。 |
| `IsShowSelectedIndicator` | 是否在选中条目右侧显示选中标记。 |
| `SelectedIndicator` | 选中标记图标模板入口。 |
| `IsMotionEnabled` | 是否启用条目背景和前景过渡动效。 |
| `EmptyIndicator` / `EmptyIndicatorTemplate` / `EmptyIndicatorPadding` / `IsShowEmptyIndicator` | 空状态展示入口。 |
| `Filter` / `FilterValue` / `FilterValueSelector` | 文本过滤入口。 |
| `FilterHighlightStrategy` | 过滤文本高亮和隐藏未命中项的策略入口。 |
| `FilterResultCount` | 当前过滤命中数量。 |
| `IsFiltering` | 当前是否处于过滤状态。 |
| `FilterHighlightForeground` | 过滤高亮前景色入口。 |

ListBoxItem 容器 API：

| API | 语义 |
| --- | --- |
| `Clicked` | 条目 pointer press 触发的 routed event。 |
| `IsSelected` | 继承 Avalonia `ListBoxItem` 的选择状态。 |
| `Content` / `ContentTemplate` | 条目内容和模板。 |

事件 API：

| 事件 | 语义 |
| --- | --- |
| `ItemClicked` | ListBoxItem `Clicked` 冒泡到 ListBox 后触发，携带被点击容器。 |
| `ItemCountChanged` | `ItemCount` 变化后触发。 |

稳定 template part：

| Template Part | 所属控件 | 职责 |
| --- | --- | --- |
| `Frame` | `ListBox` | root 背景、边框、圆角和 padding 边界。 |
| `PART_ScrollViewer` | `ListBox` | 列表滚动容器。 |
| `ItemsPresenter` | `ListBox` | 条目容器承载入口。 |
| `EmptyIndicator` | `ListBox` | 空状态内容展示。 |
| `Frame` | `ListBoxItem` | 条目背景、圆角和 padding 边界。 |
| `SelectedIndicator` | `ListBoxItem` | 选中标记展示入口。 |
| `ContentPresenter` | `ListBoxItem` | 普通内容展示入口。 |

ListBoxItem 内部还包含过滤文本高亮节点。该节点属于过滤展示实现，不作为用户替换模板时的 public template part。

伪类契约主要继承 Avalonia `ListBoxItem`：

- `:pointerover` 表示条目 hover。
- `:selected` 表示条目被选中。
- `:disabled` 表示条目不可用。

## 事件与命令

ListBox 的公共契约由 ListBox API、ListBoxItem API、事件 API、template part 和主题入口组成。
| `Clicked` | 条目 pointer press 触发的 routed event。 |
事件 API：
| 事件 | 语义 |

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 筛选

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/List/Views/ListAdvancedShowCase.axaml:12`

Gallery key：`ExamplesContent` / item `0`

```axaml
<atom:ListView Name="FilteredList"
```

### 排序

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/List/Views/ListAdvancedShowCase.axaml:22`

Gallery key：`ExamplesContent` / item `1`

```axaml
<atom:ListView Name="OrderedList"
```

### 简单列表框控件

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/List/Views/ListAdvancedShowCase.axaml:30`

Gallery key：`ExamplesContent` / item `2`

```axaml
<atom:ListBox>
    <atom:ListBoxItem Content="赛车向人群喷出燃烧的燃料。" />
    <atom:ListBoxItem Content="日本公主将嫁给平民。" />
    <atom:ListBoxItem Content="澳大利亚人在内陆车祸后步行 100 公里。" />
    <atom:ListBoxItem Content="男子因婚礼女孩失踪案被起诉。" />
    <atom:ListBoxItem Content="洛杉矶抗击大规模山火。" />
</atom:ListBox>
```

### 简单列表框控件

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/List/Views/ListAdvancedShowCase.axaml:42`

Gallery key：`ExamplesContent` / item `3`

```axaml
<atom:ListBox ItemsSource="{Binding BasicListBoxItems}"
```

## 状态模型

ListBox 的状态模型由选择状态、交互状态、过滤状态、空状态、尺寸状态和动效状态组成。

选择行为：

- `IsSelectable=false` 时，ListBox 不响应 pointer 或 keyboard 选择更新，并清空 `SelectedIndex`、`SelectedItem` 和 `SelectedItems`。
- `SelectionMode` 继承 Avalonia `ListBox` 语义，单选使用 `SelectedItem`，多选使用 `SelectedItems`。
- 方向键按 Avalonia navigation direction 移动选择，`Shift` 支持范围选择，平台 select-all 手势在多选模式下选择全部。
- `IsShowSelectedIndicator=true` 时，选中容器右侧显示 `SelectedIndicator`。

点击行为：

- `ListBoxItem.Clicked` 是 routed event，ListBox 通过 class handler 收敛到 `ItemClicked`。
- 派生控件可重写 `NotifyListBoxItemClicked` 承接点击行为，例如 CandidateList 在单选场景中提交候选项。

过滤行为：

- `Filter`、`FilterValue` 和 `FilterValueSelector` 共同决定条目是否命中。
- `IsFiltering=true` 时条目进入过滤展示状态，并通过 `FilterHighlightStrategy` 决定高亮和隐藏未命中项。
- `FilterResultCount` 表示命中数量；过滤模式下空状态依据 `FilterResultCount == 0` 判断。
- 过滤展示主要面向文本内容。复杂自定义 `ItemTemplate` 需要维护者明确处理过滤态展示入口，避免过滤态绕过用户模板。

空状态行为：

- `ItemCount == 0` 时显示空状态。
- `IsFiltering && FilterResultCount == 0` 时显示过滤空状态。
- `IsShowEmptyIndicator=false` 时不显示空状态内容。

动效行为：

- ListBoxItem 初始化阶段禁用 transitions，loaded 后启用，避免初始状态产生非预期动画。
- `IsMotionEnabled=false` 时不应用背景和前景过渡。

## 主题与 Design Token

ListBox 主题按 root 和 item 两层组织。

```text
ListBoxTheme
  Frame
  PART_ScrollViewer
  ItemsPresenter
  EmptyIndicator

ListBoxItemTheme
  Frame
  SelectedIndicator
  ContentPresenter
  HighlightableTextBlock
```

视觉规则：

- root 边框由 `BorderBrush`、`BorderThickness`、`CornerRadius` 和 `IsBorderless` 共同决定。
- `SizeType` 控制 root 圆角、空状态 padding、条目最小高度和条目 padding。
- 默认条目背景透明，hover 使用 `ItemHoverBg`，selected 使用 `ItemSelectedBg`。
- disabled 内容使用 SharedToken disabled 文本色。
- 选中指示器默认使用 默认 `CheckOutlined`，颜色使用 SharedToken 主色，尺寸使用 SharedToken icon size。
- 空状态默认使用 `Empty` 的 simple preset image。

ListBoxToken 提供内容 padding、条目文字颜色、条目状态背景、条目 padding、条目 margin、选中指示器 margin 和过滤高亮色。Token 详情见 [ListBox Token 设计](token.md)。

Token 来源：

ListBoxToken 是 ListBox 的组件级设计变量层。它把全局颜色、尺寸、间距和状态色转换为 ListBox root、ListBoxItem、selected indicator 和 filter highlighter 可消费的语义值。

ListBoxToken 服务以下主题和控件：

- `ListBoxTheme.axaml`
- `ListBoxItemTheme.axaml`
- `CandidateListTheme.axaml`
- `CandidateListItemTheme.axaml`
- `CascaderViewFilterListTheme.axaml`
- `ListBox` / `ListBoxItem` / `CandidateList` / `CandidateListItem`

ListBoxToken 不承载 `SelectedItem`、`SelectedItems`、`IsSelected`、`IsFiltering`、`FilterValue`、`FilterResultCount`、`IsEffectiveEmptyVisible`、`VirtualIndex` 等实例状态。这些状态由 C# 状态模型、容器属性和主题 selector 处理。

## AOT 与裁剪注意事项

ListBox 不通过反射访问模板内部结构。模板接入依赖稳定 part 名称和 Avalonia 属性绑定。

同生命周期的 root 到 item 状态同步使用 Avalonia `[!]` direct binding，不使用 `BindUtils.RelayBind` 和额外 disposable。容器被 ItemsControl 管理，binding 生命周期随容器生命周期结束。

选中指示器、过滤高亮和空状态内容是 AXAML 静态模板节点。隐藏状态依赖 `IsVisible`，不通过 C# 动态创建和销毁模板子节点。

`IsVisible=false` 的节点不参与 measure、arrange 和 render；因此过滤空状态、隐藏未命中项和默认隐藏选中指示器可以优先使用静态模板 + visible state 模式。

过滤和虚拟化路径不得引入固定延时、dispatcher timing hack 或全局订阅。新增缓存必须有明确失效点，并在 collection change、container clear 或 detach 相关路径中清理。

## 源码索引

主要源码：

- `src/AtomUI.Desktop.Controls/ListBox/ListBox.cs`：public API、事件、容器生成、空状态、过滤、选择拦截、键盘导航和虚拟化上下文管理。
- `src/AtomUI.Desktop.Controls/ListBox/ListBoxItem.cs`：条目容器、点击 routed event、选中指示器状态、过滤文本展示状态、pointer selection 协同和动效启停。
- `src/AtomUI.Desktop.Controls/ListBox/ListBoxItemClickedEventArgs.cs`：`ItemClicked` 事件参数。
- `src/AtomUI.Desktop.Controls/ListBox/ListBoxToken.cs`：ListBox 专属 Token 定义。
- `src/AtomUI.Desktop.Controls/ListBox/Themes/ListBoxTheme.axaml`：root 模板、ScrollViewer、ItemsPresenter、EmptyIndicator、默认 ItemTemplate 和 root 样式。
- `src/AtomUI.Desktop.Controls/ListBox/Themes/ListBoxItemTheme.axaml`：条目模板、选中指示器、普通内容、过滤高亮文本和条目状态样式。
- `src/AtomUI.Desktop.Controls/Primitives/CandidateList/CandidateList.cs`：基于 ListBox 的候选项列表扩展。
- `src/AtomUI.Controls.Shared/IListVirtualizingContextAware.cs`：虚拟化上下文保存、恢复和清理接口。

## 相关文档

- 源设计文档：`docs/controls/desktop/data-display/list-box/overview.md`
- 实现文档：`docs/controls/desktop/data-display/list-box/implementation.md`
- Token 文档：`docs/controls/desktop/data-display/list-box/token.md`
- 变更记录：`docs/controls/desktop/data-display/list-box/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/list-view/index-cn.md

# ListView

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

ListView 是桌面端数据展示类列表视图控件，用于展示一组数据项，并在同一控件内提供选择、分组、排序、过滤、分页、空状态和操作中状态。它以 Avalonia `ItemsControl` 为基础，使用 AtomUI 的 `ListCollectionView` 管理数据视图，并用 `ListViewItem` 作为条目容器。

ListView 的职责是把用户提供的 `ItemsSource` 归一到可排序、可过滤、可分组、可分页的列表视图，并把视图状态同步到容器、选择模型和分页器。它不负责树形层级、表格列模型、远程数据请求、跨控件业务命令编排或候选列表提交逻辑；这些场景应分别使用 TreeView、DataGrid、业务组合控件或 Select / AutoComplete 等候选列表控件。

ListView 支持两类数据入口：

- `ItemsSource` 绑定任意 `IEnumerable`，控件会归一为具备 source-entry 映射能力的 AtomUI `ListCollectionView`。只有实现 internal entry bridge 的 collection view 可以直接接入。
- 直接 `Items` 适合少量静态条目，直接条目需要符合 ListView 的数据项语义。

推荐数据项实现 `IListItemData`，以便默认模板、禁用状态、分组字段和默认内容展示保持一致。`IListItemData` 不承载 ListView 运行时选择状态；复杂业务对象应提供 `ItemTemplate`、`GroupItemTemplate`、`FilterValueSelector` 和 `SortDescriptions`。

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/DataDisplay/List` |
| 状态 | Stable |

## 何时使用

ListView 表达的是“数据视图 + 可操作列表项”的信息浏览语义。用户应能通过紧凑纵向结构、分组标题、hover 背景、selected 背景、可选选中标记、分页位置和操作中遮罩理解当前数据范围与交互目标。

设计语言由以下维度组成：

| 维度 | 含义 | 典型表达 |
| --- | --- | --- |
| 数据视图 | 原始集合经过过滤、排序、分组和分页后形成当前可见列表。 | `ListCollectionView`、`TotalItemCount`、`PageIndex`、`PageSize`。 |
| 列表结构 | 同级条目纵向排列，默认使用虚拟化面板。 | ScrollViewer、ItemsPresenter、VirtualizingStackPanel。 |
| 分组结构 | 组标题作为不可选择的列表项参与视图展示。 | `IsGroupEnabled`、`GroupItemTemplate`、组标题前景色。 |
| 选择状态 | 以 source entry 为单位表示的当前业务选中目标或选中集合。 | `Selection`、`SelectedIndex`、`SelectedIndexes`、`SelectedItem`、`SelectedItems`、selected 背景、选中指示器。 |
| 过滤状态 | 外部条件筛选当前视图数据。 | `Filter`、`FilterValue`、`IsFiltering`、`FilterContextChanged`。 |
| 分页状态 | 当前视图仅展示指定页数据。 | `TopPagination`、`BottomPagination`、`PaginationVisibility`。 |
| 空状态 | 无数据或过滤后无结果。 | `EmptyIndicator` 内容区域。 |
| 操作中状态 | 数据处理或外部操作期间覆盖列表。 | `Spin`、`OperatingMsg`、`CustomOperatingIndicator`。 |

ListView 的视觉强度应保持数据展示控件的克制感。条目不是卡片，分组标题不是额外列表控件，分页器也不改变列表数据语义，只表达当前视图页边界。

## 公共 API

ListView 的公共契约由数据视图 API、选择 API、分页 API、视觉 API、事件 API、template part、伪类和主题入口组成。

数据视图 API：

| API | 语义 |
| --- | --- |
| `ItemsSource` / `Items` | 数据入口。`ItemsSource` 会归一为 `IListCollectionView`。 |
| `SortDescriptions` | 排序描述集合，交给 `IListCollectionView.SortDescriptions` 执行。 |
| `Filter` / `FilterValue` / `FilterValueSelector` | 过滤谓词、过滤条件和值选择器。 |
| `IsFiltering` | 当前是否存在有效过滤描述。 |
| `TotalItemCount` | 当前视图总数据量，用于空状态、伪类和分页同步。 |

分组 API：

| API | 语义 |
| --- | --- |
| `IsGroupEnabled` | 是否把当前视图按 group key 分组。 |
| `GroupPropertySelector` | 从数据项提取 group key；默认读取 `IGroupHeader.Group`。 |
| `GroupItemTemplate` | 组标题项模板，数据上下文为 collection view 插入的组标题数据项。 |

ListView public 分组模型是单层分组模型。`IsGroupEnabled=true` 时，ListView 会向 collection view 写入一个 `ListGroupDescription`，该描述由 `GroupPropertySelector` 提供 group key。`IListCollectionView.GroupDescriptions` 支持列表形式，但 ListView 当前 public API 不暴露多层分组配置；维护者不应把多层分组行为误写成 ListView public 契约。

选择 API：

| API | 语义 |
| --- | --- |
| `IsSelectable` | 是否允许用户通过 pointer 或 keyboard 更新选择。关闭时清空当前选择。 |
| `SelectionMode` | 选择模式，支持单选、多选、Toggle 和 AlwaysSelected 语义。 |
| `Selection` | ListView 持有的选择状态 owner。调用者可以执行选择命令，但不能替换模型或设置其数据源。 |
| `SelectedIndex` / `SelectedIndexes` | 选中 entry 的 source-index 输入与只读集合投影；排序、过滤、分组和分页不改变索引语义。 |
| `SelectedItem` / `SelectedItems` | 从选中 entries 派生的只读 item 投影；结果允许包含重复引用或重复值。 |
| `SelectedValue` / `SelectedValueBinding` | 从 `SelectedItem` 派生只读值，不按值反向查找条目。 |
| `ItemKeySelector` | 可选的稳定 `EntityKey` 提取器，仅用于 Reset 或 ItemsSource 替换后的选择恢复。 |
| `AutoScrollToSelectedItem` | 首次模板和视觉树就绪后滚动到 anchor 项。 |
| `IsTextSearchEnabled` | 是否启用文本增量搜索。 |
| `WrapSelection` | 键盘方向导航是否循环。 |

分页 API：

| API | 语义 |
| --- | --- |
| `PageIndex` | 当前页索引，内部与 `IListCollectionView.PageIndex` 同步。 |
| `PageSize` | 页大小，`0` 表示不分页。 |
| `TopPagination` / `BottomPagination` | 可注入的顶部和底部分页器。 |
| `PaginationVisibility` | 分页器显示位置：`None`、`Top`、`Bottom`、`Both`。 |
| `TopPaginationAlign` / `BottomPaginationAlign` | 对应分页器对齐方式。 |
| `IsHideOnSinglePage` | 透传给分页器的单页隐藏策略。 |

视觉与操作 API：

| API | 语义 |
| --- | --- |
| `SizeType` | 控制 root 圆角、空状态 padding、条目高度和 padding。 |
| `IsBorderless` | 是否隐藏 root 边框。 |
| `ItemHoverBg` / `ItemSelectedBg` | 条目 hover 和 selected 背景入口。 |
| `IsShowSelectedIndicator` / `SelectedIndicator` | 选中标记开关和图标模板。 |
| `IsMotionEnabled` | 是否启用条目和分页器动效。 |
| `EmptyIndicator` / `EmptyIndicatorTemplate` / `EmptyIndicatorPadding` / `IsShowEmptyIndicator` | 空状态展示入口。 |
| `IsOperating` / `OperatingMsg` / `CustomOperatingIndicator` / `CustomOperatingIndicatorTemplate` | 操作中遮罩和自定义指示器入口。 |
| `ItemClickMode` | 条目点击事件触发时机。 |

事件 API：

| 事件 | 语义 |
| --- | --- |
| `ItemClicked` | `ListViewItem.Clicked` 冒泡到 ListView 后触发。 |
| `ItemCountChanged` | `TotalItemCount` 变化后触发。 |
| `FilterContextChanged` | 过滤描述重新配置后触发。 |
| `SelectionChanged` | 选择模型变化后按 Avalonia routed event 形式触发。 |
| `IsSelectedChanged` | 容器 attached selected 状态变化的 routed event。 |

稳定模板命名和职责：

| 名称 | 所属控件 | 职责 |
| --- | --- | --- |
| `Frame` | `ListView` | root 背景、边框、圆角、padding 和 margin 边界。 |
| `TopPaginationPresenter` | `ListView` | 顶部分页器展示入口。 |
| `BottomPaginationPresenter` | `ListView` | 底部分页器展示入口。 |
| `PART_ScrollViewer` | `ListView` | 列表滚动容器。 |
| `ItemsPresenter` | `ListView` | 条目容器承载入口。 |
| `EmptyIndicator` | `ListView` | 空状态内容展示。 |
| `Frame` | `ListViewItem` | 条目背景、圆角和 padding 边界。 |
| `SelectedIndicator` | `ListViewItem` | 选中标记展示入口。 |
| `ContentPresenter` | `ListViewItem` | 普通内容或组标题内容展示入口。 |

伪类契约：

- `:empty` 表示 `TotalItemCount == 0`。
- `:singleitem` 表示 `TotalItemCount == 1`。
- `ListViewItem` 继承 Avalonia 选择、hover、pressed、focus 和 disabled 相关伪类语义，其中 `:selected` 由 `ListView.IsSelected` attached property 驱动。

## 事件与命令

ListView 的公共契约由数据视图 API、选择 API、分页 API、视觉 API、事件 API、template part、伪类和主题入口组成。
| `Selection` | ListView 持有的选择状态 owner。调用者可以执行选择命令，但不能替换模型或设置其数据源。 |
| `ItemClickMode` | 条目点击事件触发时机。 |
事件 API：
| 事件 | 语义 |
| `SelectionChanged` | 选择模型变化后按 Avalonia routed event 形式触发。 |
| `IsSelectedChanged` | 容器 attached selected 状态变化的 routed event。 |

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 筛选

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/List/Views/ListAdvancedShowCase.axaml:12`

Gallery key：`ExamplesContent` / item `0`

```axaml
<atom:ListView Name="FilteredList"
```

### 排序

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/List/Views/ListAdvancedShowCase.axaml:22`

Gallery key：`ExamplesContent` / item `1`

```axaml
<atom:ListView Name="OrderedList"
```

### 简单列表框控件

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/List/Views/ListAdvancedShowCase.axaml:30`

Gallery key：`ExamplesContent` / item `2`

```axaml
<atom:ListBox>
    <atom:ListBoxItem Content="赛车向人群喷出燃烧的燃料。" />
    <atom:ListBoxItem Content="日本公主将嫁给平民。" />
    <atom:ListBoxItem Content="澳大利亚人在内陆车祸后步行 100 公里。" />
    <atom:ListBoxItem Content="男子因婚礼女孩失踪案被起诉。" />
    <atom:ListBoxItem Content="洛杉矶抗击大规模山火。" />
</atom:ListBox>
```

### 简单列表框控件

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/List/Views/ListAdvancedShowCase.axaml:42`

Gallery key：`ExamplesContent` / item `3`

```axaml
<atom:ListBox ItemsSource="{Binding BasicListBoxItems}"
```

## 状态模型

ListView 的状态模型由数据视图状态、选择状态、分页状态、过滤状态、分组状态、空状态、操作状态、尺寸状态和动效状态组成。

数据视图状态：

- `ItemsSource` 设置后归一为具备 entry bridge 的 `IListCollectionView`，ListView 监听其 `CollectionChanged`、`PropertyChanged`、`PageChanging` 和 `PageChanged`。不具备 entry bridge 的自定义 view 从其 `SourceCollection` 重新归一。
- `SortDescriptions`、`Filter` / `FilterValue` / `FilterValueSelector`、`IsGroupEnabled` / `GroupPropertySelector` 分别写入 collection view 的排序、过滤和分组描述。
- `TotalItemCount` 和 `IsEmptyDataSource` 来自 collection view，用于伪类、空状态和分页器同步。

选择行为：

- `IsSelectable=false` 时，ListView 不响应 pointer 或 keyboard 选择更新，并清空 canonical selection。
- 数据源中的每一次出现拥有独立 internal EntryId；item 引用、`Equals`、`GetHashCode` 和显示值不参与源条目标识与选择映射。
- `ListViewSelectionModel` 持有 selected EntryIds、anchor 和 active entry；`SelectedIndex(es)`、`SelectedItem(s)`、`SelectedValue` 和容器 `IsSelected` 都是同一状态的投影。
- 公开选择索引始终表达原始数据源的 source index。排序、过滤、分组和分页通过 entry projection 映射容器，不通过 item `IndexOf` 反查源位置。
- 组标题是没有 EntryId 和 source index 的视图合成节点，所有选择入口均跳过组标题。
- Reset 或 ItemsSource 替换只通过唯一、非空 item key 恢复选择；没有稳定 key 的选择清除。
- `SelectionMode.AlwaysSelected` 在存在数据且丢失选择时恢复到首项。
- 完整选择、集合变化和恢复语义见 [ListView 选择模型设计](selection-model-design.md)。

键盘和文本搜索行为：

- 方向键按 Avalonia navigation direction 移动选择，`WrapSelection` 控制边界循环。
- 多选模式下，平台 select-all 手势调用 `Selection.SelectAll()`。
- Space / Enter 会尝试从事件源更新选择。
- `IsTextSearchEnabled=true` 时，文本输入按 `TextSearch.TextBinding` 或 `DisplayMemberBinding` 做前缀搜索，搜索词由短定时器清空。

过滤行为：

- `Filter != null && FilterValue != null` 时，ListView 进入过滤态并向 collection view 写入 `ListFilterDescription`。
- `FilterValueSelector` 优先用于提取过滤值；未提供时默认读取 `IListItemData.Content`。
- 过滤改变后触发 `FilterContextChanged`，并刷新空状态。

分组行为：

- `IsGroupEnabled=true` 时，ListView 用 `GroupPropertySelector` 构造 `ListGroupDescription` 并写入 collection view。
- 默认 `GroupPropertySelector` 读取 `IGroupHeader.Group`，因此推荐分组数据项实现 `IListItemData` / `IGroupHeader` 并提供稳定、非空的 `Group` 值。
- collection view 会为每个 group key 插入一个 `GroupListItemData` 作为组标题项，其 `Content` 来自 `groupKey.ToString()`，`IsGroupItem=true`。
- 组标题项使用 `GroupItemTemplate`，普通数据项仍使用 `ItemTemplate`。
- 组标题项参与当前视图枚举和容器生成，但它表达的是视觉分隔，不是业务数据项；pointer selection 路径会跳过 `IsGroupItem=true` 的容器。
- 分组开启后，selection model 仍以业务 source entries 为选择范围；当前 view projection 只增加没有 EntryId 的组标题节点。
- 与分页同时使用时，collection view 先按完整结果建立临时分组顺序，再按当前页重建对外可枚举的 group 结构。

分页行为：

- `PageSize=0` 表示不分页；`PageSize>0` 时 collection view 只枚举当前页。
- `TopPagination` 和 `BottomPagination` 接收当前 `Total`、`PageSize`、`CurrentPage`、对齐、启用状态、动效和单页隐藏策略。
- 分页器的 `CurrentPageChanged` 请求转换为 `IListCollectionView.MoveToPage(page - 1)`。
- `PaginationVisibility` 只控制分页器可见性，不改变 collection view 的分页数据。

空状态和操作态：

- `IsShowEmptyIndicator && TotalItemCount == 0` 时显示空状态。
- 空状态显示时隐藏滚动列表区域。
- `IsOperating=true` 时，root 模板内的 `Spin` 覆盖列表和空状态，用于表达外部操作中状态。

动效行为：

- ListViewItem 初始化阶段禁用 transitions，loaded 后启用，避免容器首次准备时出现非预期动画。
- 虚拟化容器保存、恢复和清理期间会临时关闭 motion，避免回收状态产生过渡。

## 主题与 Design Token

ListView 主题按 root、分页、操作态、滚动内容、空状态和 item 两层组织。

```text
ListViewTheme
  Frame
  TopPaginationPresenter
  BottomPaginationPresenter
  Spin
    PART_ScrollViewer
      ItemsPresenter
    EmptyIndicator

ListViewItemTheme
  Frame
    SelectedIndicator
    ContentPresenter
```

视觉规则：

- root 边框由 `BorderBrush`、`BorderThickness`、`CornerRadius` 和 `IsBorderless` 共同决定。
- `SizeType` 控制 root 圆角、空状态 padding、条目最小高度、条目 padding 和条目圆角。
- 默认条目背景透明，hover 使用 `ItemHoverBg`，selected 使用 `ItemSelectedBg`。
- 组标题使用 `GroupHeaderColor`，不应用普通条目的 hover / selected 状态背景。
- selected indicator 默认使用 默认 `CheckOutlined`，颜色和尺寸来自 SharedToken。
- 空状态默认使用 `Empty` 的 simple preset image。
- 分页器 margin 使用 ListViewToken 的 `PaginationMargin`。

ListViewToken 提供内容 padding、条目文字颜色、条目状态背景、条目 padding、条目 margin、分页器 margin、组标题色和选中指示器 margin。Token 详情见 [ListView Token 设计](token.md)。

Token 来源：

ListViewToken 是 ListView 的组件级设计变量层。它把全局颜色、尺寸、间距和状态色转换为 ListView root、ListViewItem、组标题、分页器间距和 selected indicator 可消费的语义值。

ListViewToken 服务以下主题和控件：

- `ListViewTheme.axaml`
- `ListViewItemTheme.axaml`
- `ListView`
- `ListViewItem`

ListViewToken 不承载 `ItemsSource`、`SelectedIndex`、`SelectedItem`、`SelectedItems`、`IsSelected`、`IsFiltering`、`FilterValue`、`PageIndex`、`PageSize`、`IsOperating`、`IsEffectiveEmptyVisible`、`VirtualIndex` 等实例状态。这些状态由 C# 状态模型、collection view、容器属性和 theme selector 处理。

## AOT 与裁剪注意事项

ListView 不通过反射访问模板内部结构。模板接入依赖稳定 part 名称、template binding、theme selector 和 Avalonia 属性绑定。

同生命周期的 root 到 item 状态同步使用 Avalonia `[!]` binding。容器由 ItemsControl 管理，binding 生命周期随容器生命周期结束。

默认 ItemsPanel 是 `VirtualizingStackPanel`。容器回收路径必须保持本地值对称清理，避免 selected、disabled、group item 和 content 状态泄漏到新数据项。

`ListCollectionView` 在排序、过滤、分页或分组启用时维护本地数组。维护过滤路径时应避免无活跃过滤条件时引入不必要的 collection view 本地数组刷新。

选择映射使用 source entries、`EntryId -> source index` 和 `EntryId -> view index` 索引表。容器准备、选中查询和选择到容器的映射不得执行基于 item equality 的线性 `IndexOf`。完整 Refresh 允许 O(n) 重建索引表；增量变化更新受影响区间并在同一 refresh transaction 中发布。

`ListSortDescription.FromPath` 存在运行期属性路径访问能力；NativeAOT 应优先使用 `GenerateDataMemberAccessors` 生成访问器，或使用 `ListSortDescription.FromComparer` 提供显式 comparer。

分页器 relay binding 和事件订阅必须在分页器替换时释放。collection view 事件订阅必须在 view 替换时释放，自建 view 必须释放其源集合弱转发器。

文本搜索 timer 不应成为长期后台工作；停止搜索时必须解除 tick 订阅并停止 timer。

## 源码索引

主要源码：

- `src/AtomUI.Desktop.Controls/ListView/ListView.cs`：public API、事件、ItemsSource 归一、collection view 订阅、分组、过滤、排序、容器生成、空状态、操作态和点击派发。
- `src/AtomUI.Desktop.Controls/ListView/ListView.Selecting.cs`：选择模型接入、source entry / view node 映射、SelectedIndex / SelectedIndexes / SelectedItem / SelectedItems / SelectedValue、键盘导航、文本搜索和容器 selected 状态同步。
- `src/AtomUI.Desktop.Controls/ListView/ListView.Pagination.cs`：PageIndex / PageSize、分页器接入、分页器属性同步和 page change 请求。
- `src/AtomUI.Desktop.Controls/ListView/ListView.Virtualizing.cs`：容器回收时的上下文保存、恢复和本地值清理。
- `src/AtomUI.Desktop.Controls/ListView/ListViewItem.cs`：条目容器、selected attached property、点击 routed event、pointer selection、组标题状态、selected indicator 和 motion 生命周期。
- `src/AtomUI.Desktop.Controls/ListView/ListViewSelectionModel.cs`：ListView canonical selection model，持有 selected EntryIds、anchor 和 active entry，并发布 source-index 与 item 投影。
- `src/AtomUI.Desktop.Controls/ListView/ListDefaultFilter.cs`：把 collection view `FilterDescriptions` 聚合成 `Func<object, bool>`。
- `src/AtomUI.Desktop.Controls/ListView/ListPaginationVisibility.cs`：分页器可见性枚举。
- `src/AtomUI.Desktop.Controls/ListView/ListPseudoClass.cs`：`:empty` 和 `:singleitem` 伪类常量。
- `src/AtomUI.Desktop.Controls/ListView/ListViewToken.cs`：ListView 专属 Token 定义。
- `src/AtomUI.Desktop.Controls/ListView/Themes/ListViewTheme.axaml`：root 模板、分页 presenter、Spin、ScrollViewer、ItemsPresenter、EmptyIndicator、默认 item / group template 和 root 样式。
- `src/AtomUI.Desktop.Controls/ListView/Themes/ListViewItemTheme.axaml`：条目模板、selected indicator、普通内容、组标题状态和条目状态样式。
- `src/AtomUI.Controls.Shared/Data/ListCollectionViews/`：`IListCollectionView`、source entry、view node、entry/index 映射、排序、过滤、分组和数据项契约。

## 相关文档

- 源设计文档：`docs/controls/desktop/data-display/list-view/overview.md`
- 实现文档：`docs/controls/desktop/data-display/list-view/implementation.md`
- Token 文档：`docs/controls/desktop/data-display/list-view/token.md`
- 变更记录：`docs/controls/desktop/data-display/list-view/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/qr-code/index-cn.md

# QRCode

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

QRCode 是 AtomUI 桌面控件体系中的二维码控件，用于把文本或业务字符串渲染为可扫描二维码。

QRCode 不负责条码识别器、扫码硬件接口或业务短链服务。这些职责应由业务层、组合控件或更专用的 AtomUI 控件承担。

主要源码入口：

- `src/AtomUI.Desktop.Controls/QRCode`
- `src/AtomUI.Controls/QRCode`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/DataDisplay/QRCode` |
| 状态 | Stable |

## 何时使用

QRCode 的设计语言围绕控件职责、可观察状态和主题契约组织，而不是围绕模板节点组织。

| 维度 | 含义 | QRCode 中的表达 |
| --- | --- | --- |
| 产品语义 | 控件在界面中承担的稳定职责。 | QRCode 是 AtomUI 桌面控件体系中的二维码控件，用于把文本或业务字符串渲染为可扫描二维码。 |
| 内容承载 | 用户数据、展示内容、集合项或操作入口如何进入控件。 | `ExpiredContent`、`ExpiredContentTemplate`、`Icon`、`IconBgColor`、`IconSize`、`LoadingContent`、`LoadingContentTemplate`、`ScannedContent` 等 10 项。 |
| 状态反馈 | public API、内部状态和伪类如何形成用户可感知反馈。 | loading/async、input/value、visual option。 |
| 主题语义 | ControlTheme、SharedToken、控件 Token 和模板绑定如何表达视觉。 | QRCode Token + ControlTheme。 |

## 公共 API

QRCode 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。

核心 public surface 按语义分组维护：

| 契约组 | 代表成员 | 维护含义 |
| --- | --- | --- |
| 内容与数据 | `ExpiredContent`、`ExpiredContentTemplate`、`Icon`、`IconBgColor`、`IconSize`、`LoadingContent`、`LoadingContentTemplate`、`ScannedContent`、`ScannedContentTemplate`、`Value` | 定义控件展示内容、输入数据、模板或业务对象入口。 |
| 交互与状态 | `IsBordered`、`Status` | 表达用户可观察状态、可用性、清除、加载或反馈语义。 |
| 视觉与布局 | `Color`、`Size` | 影响尺寸、位置、颜色、形状、密度和模板视觉变量。 |
| 其他稳定入口 | `EccLevel` | 保留为 public surface，变更前需确认 Gallery 和用户 XAML 依赖。 |

当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。

主要公开类型与枚举：

- 类型：`AbstractQRCode`、`QRCode`。
- 枚举：`QRCodeEccLevel`、`QRCodeStatus`。
- 本地化 Catalog：`QRCodeLangResourceKind`。

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_RefreshButton` | `Button` | 承载用户触发入口、导航或关闭动作。 |

当前未抽取到控件专属伪类；主题主要依赖 Avalonia 标准伪类、模板绑定和内部 StyledProperty。

## 事件与命令

QRCode 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。
当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/QRCode/Views/QRCodeShowCase.axaml:35`

Gallery key：`ExamplesContent` / item `0`

```axaml
<StackPanel Orientation="Vertical">
    <atom:QRCode Value="{Binding QRCodeInput}" />
    <atom:LineEdit Text="{Binding QRCodeInput}" Margin="0,20,0,0" />
    <ListBox>
        <ListBox.ItemTemplate></ListBox.ItemTemplate>
    </ListBox>
</StackPanel>
```

### 带 Icon 的例子

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/QRCode/Views/QRCodeShowCase.axaml:50`

Gallery key：`ExamplesContent` / item `1`

```axaml
<StackPanel Orientation="Vertical">
    <atom:QRCode Value="https://atomui.net" Icon="avares://AtomUIGallery/Assets/ATOMUI-LOGO.png" />
</StackPanel>
```

### 不同的状态

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/QRCode/Views/QRCodeShowCase.axaml:62`

Gallery key：`ExamplesContent` / item `2`

```axaml
<WrapPanel ItemSpacing="20" LineSpacing="20" Orientation="Horizontal">
    <atom:QRCode Value="https://atomui.net" Status="Loading" />
    <atom:QRCode Value="https://atomui.net" Status="Expired" />
    <atom:QRCode Value="https://atomui.net" Status="Scanned" />
</WrapPanel>
```

### 自定义颜色

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/QRCode/Views/QRCodeShowCase.axaml:137`

Gallery key：`ExamplesContent` / item `5`

```axaml
<StackPanel Orientation="Horizontal" Spacing="10">
    <atom:QRCode Value="https://atomui.net"
                 Color="{atom:SharedTokenResource ColorSuccessText}" />
    <atom:QRCode Value="https://atomui.net"
                 Color="{atom:SharedTokenResource ColorInfoText}"
                 Background="{atom:SharedTokenResource ColorBgLayout}" />
</StackPanel>
```

## 状态模型

QRCode 的状态流按以下路径收敛：

```text
Public API / inherited command / item source / user input
  -> 控件实例状态
  -> effective state / pseudo-class / template property
  -> ControlTheme selector / presenter / renderer
  -> Gallery 可观察行为
```

状态维护规则：

- Disabled 或不可交互状态优先屏蔽 pointer、keyboard、motion 和提交类反馈。
- loading/async、input/value、visual option 状态由控件实例或明确的数据 owner 推导，不能在 template part 之间双向竞争。
- 模板重套用时必须把 public API 对应状态回放到新的 part、伪类和主题变量。
- 集合、弹层、异步、动效或窗口相关状态必须能处理 reset、close、cancel、detach 和 owner 释放。

## 主题与 Design Token

QRCode 的视觉模型由控件模板、ControlTheme、SharedToken 和必要的控件 Token 共同构成。

| 主题文件 | 职责 |
| --- | --- |
| `QRCodeTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |

QRCode 使用 `QRCodeToken` 作为控件 Token scope。Token 只表达组件视觉语义，不承载 loading/async、input/value、visual option 运行时状态。

主题维护规则：

- 不删除或重命名已经稳定的 ControlTheme key、template part、伪类和资源 key。
- 不把可由 AXAML 表达的模板状态迁移为 C# 动态创建视觉。
- 不把 hover、pressed、selected、expanded、loading、filter、popup open 等运行时状态写入 Token。
- Browser 或平台特化主题必须保持同一 API 的语义一致。

Token 来源：

QRCode Token 只表达组件级视觉变量，例如尺寸、间距、颜色、圆角、阴影、图标尺寸和弹层边界。Token 不承载运行时选择、展开、加载、错误、上传任务、过滤条件或业务状态。

当前 Token scope：

- `QRCodeToken`，scope id 为 `QRCode`，源码位于 `src/AtomUI.Desktop.Controls/QRCode/QRCodeToken.cs`。

## AOT 与裁剪注意事项

资源和 AOT 约束：

- 不通过运行时反射扫描 public API、Token 或 Gallery 示例数据。
- 不把可静态声明的模板结构迁移到 C# 动态创建。
- 异步加载、上传、弹层和窗口生命周期必须能取消或释放。
- 缓存对象必须与控件、窗口、弹层或数据 owner 生命周期一致。
- Source generator 生成文件不手工编辑；需要修改时改输入源或 generator。

性能边界：

- 控件应优先复用 Avalonia 原生虚拟化、模板绑定和资源系统。
- 避免为每次状态变化创建不必要的视觉对象、订阅或动画对象。
- 大集合控件必须保证 container recycle 后不会泄漏旧 item 状态。

## 源码索引

主要源码文件：

- `src/AtomUI.Desktop.Controls/QRCode/Localization/QRCodeLangResourceKind.cs`
- `src/AtomUI.Desktop.Controls/QRCode/Localization/en-US.xlf`
- `src/AtomUI.Desktop.Controls/QRCode/Localization/zh-CN.xlf`
- `src/AtomUI.Desktop.Controls/QRCode/Localization/zh-TW.xlf`
- `src/AtomUI.Desktop.Controls/QRCode/QRCode.cs`
- `src/AtomUI.Desktop.Controls/QRCode/QRCodeToken.cs`
- `src/AtomUI.Desktop.Controls/QRCode/Themes/QRCodeTheme.axaml`
- `src/AtomUI.Controls/QRCode/AbstractQRCode.cs`
- `src/AtomUI.Controls/QRCode/QRCodeEnums.cs`

职责边界：

- 控件主文件保留 public/protected API、Avalonia 属性注册、事件和主要生命周期入口。
- Theme 文件负责静态视觉结构、template part、selector 和资源绑定。
- Token 文件只提供组件视觉变量，不保存实例状态。
- Gallery 文件只展示用法和示例，不作为运行时逻辑 owner。

## 相关文档

- 源设计文档：`docs/controls/desktop/data-display/qr-code/overview.md`
- 实现文档：`docs/controls/desktop/data-display/qr-code/implementation.md`
- Token 文档：`docs/controls/desktop/data-display/qr-code/token.md`
- 变更记录：`docs/controls/desktop/data-display/qr-code/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/segmented/index-cn.md

# Segmented

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

Segmented 是桌面端数据展示类的紧凑单选控件，用于在少量互斥选项之间切换当前视图、展示模式、时间粒度或状态筛选。它通过分段轨道、选项 item、选中滑块、图标和文本表达“当前只有一个有效选择”的语义。

Segmented 的职责是承载一组有限选项并维护单选状态。它不是完整导航系统、Tab 容器、列表选择器、弹层选择器、表单编辑器或大数据集合控件；复杂页面导航、动态搜索、虚拟化、多选和异步加载应由专用控件或业务层承担。

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/DataDisplay/Segmented` |
| 状态 | Stable |

## 何时使用

Segmented 的设计语言来自 参考设计体系的分段控制器：浅色轨道承载一组互斥选项，选中滑块跟随当前项移动，选项文本和图标在 hover、pressed、selected、disabled 状态下提供即时反馈。

| 维度 | 含义 | 典型表达 |
| --- | --- | --- |
| 单选语义 | 同一组中只有一个选项处于选中态。 | `SelectedIndex`、`SelectedItem`、`:selected`。 |
| 视觉反馈 | 用滑块和 item 状态区分 hover、pressed、selected。 | `SelectedThumbBg`、item 背景和前景色。 |
| 信息密度 | 通过尺寸类型控制高度、字体、图标和内边距。 | `SizeType=Large/Middle/Small/Custom`。 |
| 排列方向 | 同一选择模型支持横向分段和纵向分段。 | `Orientation=Horizontal/Vertical`。 |
| 铺满能力 | 在块级场景中让可见 item 等分父容器宽度。 | `IsExpanding`。 |
| 形状变体 | 默认圆角遵循 SizeType，Round 使用稳定的胶囊几何。 | `Shape=Default/Round`。 |
| 图文组合 | 允许纯文本、纯图标或图标加文本。 | `SegmentedItem.Icon`、`Content`、`:has-icon`。 |
| 动效一致性 | 选中滑块移动和 item 背景变化受全局 motion 控制。 | `IsMotionEnabled`。 |

## 公共 API

Segmented 的公共契约由根控件、item 容器、继承的选择 API 和主题契约组成。

根控件 API：

| API | 类型 | 语义 |
| --- | --- | --- |
| `SizeType` | `CustomizableSizeType` | 控制轨道圆角、item 高度、字体、padding 和图标尺寸，支持 `Large`、`Middle`、`Small`、`Custom`。 |
| `Orientation` | `Orientation` | item 排列方向，默认 `Horizontal`；`Vertical` 使用纵向轨道和纵向滑块位移。 |
| `Shape` | `SegmentedShape` | 轨道、item 和选中滑块的形状，默认 `Default`；`Round` 使用胶囊圆角。 |
| `IsExpanding` | `bool` | 是否适配父容器可用宽度；水平模式下可见 item 等分宽度，垂直模式下轨道和 item 填满可用宽度但不扩展高度。 |
| `IsMotionEnabled` | `bool` | 是否启用选中滑块位置/尺寸动画和 item 背景动画，默认来自 `SharedToken.EnableMotion`。 |
| `SelectedIndex` / `SelectedItem` | inherited | 当前选择，来自 `SelectingItemsControl`。 |
| `SelectionChanged` | inherited event | 选择变化事件，来自 `SelectingItemsControl`。 |
| `Items` / `ItemsSource` / `ItemTemplate` | inherited | 选项集合和内容模板入口。 |

item API：

| API | 类型 | 语义 |
| --- | --- | --- |
| `SegmentedItem.Icon` | `PathIcon?` | 选项左侧图标；非空时启用 `:has-icon`。 |
| `SegmentedItem.IsSelected` | `bool` | item 选中状态，接入 Avalonia `ISelectable` 选择模型。 |
| `Content` / `ContentTemplate` | inherited | item 内容和模板。 |

稳定 template part 和主题节点：

| 模板 | 节点 | 职责 |
| --- | --- | --- |
| `SegmentedTheme.axaml` | `Frame` | 根轨道裁剪、圆角和 padding 承载。 |
| `SegmentedTheme.axaml` | `PART_ItemsPresenter` | item presenter 和 `SegmentedStackPanel` 承载。 |
| `SegmentedItemTheme.axaml` | `Frame` | item 背景、圆角和 padding 承载。 |
| `SegmentedItemTheme.axaml` | `IconPresenter` | `SegmentedItem.Icon` 的视觉承载。 |
| `SegmentedItemTheme.axaml` | `Content` | item 内容承载。 |

稳定伪类：

- `:selected`：item 被选中。
- `:pressed`：item 按下态。
- `:has-icon`：item 设置了 `Icon`。
- `:pointerover` 和 `:disabled`：由 Avalonia 标准状态驱动，主题使用这些状态表达 hover 和 disabled 视觉。

`SegmentedShape` 包含 `Default` 和 `Round`。该枚举只表达 Segmented 家族的轨道形状，不复用包含 `Circle` 等无效值的其他控件 Shape 枚举。

`Segmented` 桌面层默认为非 visual 数据项创建 `SegmentedItem` 容器，并把 `SizeType`、`Shape`、`IsMotionEnabled` 传递给容器。`PrepareSegmentedItem(SegmentedItem, object?, int)` 是受保护扩展点，用于派生控件补充容器准备逻辑。

## 事件与命令

| `SelectionChanged` | inherited event | 选择变化事件，来自 `SelectingItemsControl`。 |

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/Segmented/Views/SegmentedShowCase.axaml:36`

Gallery key：`ExamplesContent` / item `0`

```axaml
<StackPanel HorizontalAlignment="Left" Orientation="Vertical" Spacing="10">
    <atom:Segmented Margin="20">
        <atom:SegmentedItem Content="每日" />
        <atom:SegmentedItem Content="每周" />
        <atom:SegmentedItem Content="每月" />
        <atom:SegmentedItem Content="每季度" />
        <atom:SegmentedItem Content="每年" />
    </atom:Segmented>
</StackPanel>
```

### 块级分段控制器

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/Segmented/Views/SegmentedShowCase.axaml:71`

Gallery key：`ExamplesContent` / item `2`

```axaml
<StackPanel HorizontalAlignment="Stretch" Orientation="Vertical">
    <atom:Segmented IsExpanding="True" Margin="20">
        <atom:SegmentedItem>123</atom:SegmentedItem>
        <atom:SegmentedItem>456</atom:SegmentedItem>
        <atom:SegmentedItem Content="长文本-长文本-长文本-长文本" />
    </atom:Segmented>
</StackPanel>
```

### 禁用

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/Segmented/Views/SegmentedShowCase.axaml:113`

Gallery key：`ExamplesContent` / item `4`

```axaml
<StackPanel HorizontalAlignment="Stretch" Orientation="Vertical" Spacing="10">
    <atom:Segmented Margin="20">
        <atom:SegmentedItem IsEnabled="False" Content="地图" />
        <atom:SegmentedItem IsEnabled="False" Content="公交" />
        <atom:SegmentedItem IsEnabled="False" Content="卫星" />
    </atom:Segmented>
    <atom:Segmented>
        <atom:SegmentedItem Content="每日" />
        <atom:SegmentedItem IsEnabled="False" Content="每周" />
        <atom:SegmentedItem Content="每月" />
        <atom:SegmentedItem IsEnabled="False" Content="每季度" />
        <atom:SegmentedItem Content="每年" />
    </atom:Segmented>
</StackPanel>
```

### 动态数据

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/Segmented/Views/SegmentedShowCase.axaml:138`

SourceKey：`segmented-dynamic`

```axaml
<StackPanel HorizontalAlignment="Left"
            Orientation="Vertical"
            Spacing="8"
            Margin="20">
    <atom:Segmented ItemsSource="{Binding DynamicOptions}" />
    <atom:Button ButtonType="Primary"
                 HorizontalAlignment="Left"
                 IsEnabled="{Binding IsDynamicOptionsLoaded, Converter={x:Static BoolConverters.Not}}"
                 Command="{Binding LoadMoreOptionsCommand}"
                 Content="Load more options" />
</StackPanel>
```

## 状态模型

Segmented 的核心状态流：

```text
Items / ItemsSource
      ↓
SegmentedItem containers
      ↓
ISelectable selected state
      ↓
SelectedItem / SelectedIndex
      ↓
selected thumb position + size
```

选择语义：

- 构造阶段固定 `SelectionMode=Single`。
- 模板应用时，如果没有显式 `SelectedIndex`、`SelectedItem` 或已选中容器，并且 `Items.Count > 0`，控件选择第一个 item。
- 已绑定或已显式设置的选择必须在模板应用后保留。
- 鼠标左键释放在 item 上触发选择，避免按下时立即改变选择造成的交互跳变。
- `SelectionChanged` 后如果控件仍附加在视觉树，会重新计算选中滑块的位置和尺寸。
- `Left` / `Up` 选择前一个有效 item，`Right` / `Down` 选择后一个有效 item，并在首尾之间循环。
- 键盘选择跳过 disabled 和 hidden item，选择完成后焦点进入对应容器。

尺寸语义：

- `Large`、`Middle`、`Small` 进入对应主题分支。
- `Custom` 复用 Middle 作为默认视觉基线；用户可在实例或 item 上显式设置 `Padding`、`MinHeight`、`FontSize`、`CornerRadius` 等 Avalonia 属性形成自定义尺寸。
- 根 `SizeType` 会同步给生成的 `SegmentedItem`，保证 item 的主题分支和根控件一致。

方向与展开布局语义：

| `Orientation` | `IsExpanding` | 布局语义 |
| --- | --- | --- |
| `Horizontal` | `false` | item 按自然宽度横向排列，根控件默认左对齐。 |
| `Horizontal` | `true` | 根控件填满父容器宽度，可见 item 等分可用宽度。 |
| `Vertical` | `false` | item 按自然高度纵向排列，轨道宽度取可见 item 的最大自然宽度。 |
| `Vertical` | `true` | 根轨道和 item 填满父容器宽度，item 仍按自然高度纵向排列，不填满父容器高度。 |

隐藏 item 不参与测量累计、排列、expanding 计数或键盘导航。

Form 语义：

- Segmented 实现 `IFormItemAware`。
- `SetFormValue(value)` 设置 `SelectedItem`。
- `GetFormValue()` 返回 `SelectedItem`。
- `ClearFormValue()` 清空 `SelectedItem`。
- `SelectedItem` 变化会通知 Form value changed。

## 主题与 Design Token

Segmented 的视觉由根主题、item 主题、专属 Token 和 SharedToken 共同决定。

| 主题文件 | 职责 |
| --- | --- |
| `SegmentedTheme.axaml` | 根模板、轨道 padding/background、选中滑块资源、SizeType/Shape 圆角分支、方向/展开对齐和滑块动画。 |
| `SegmentedItemTheme.axaml` | item 模板、图标/内容布局、hover/pressed/selected/disabled 状态、SizeType/Shape 分支和图标尺寸。 |

视觉关系：

```text
SharedToken
   ↓
SegmentedToken
   ↓
SegmentedTheme / SegmentedItemTheme
   ↓
track + selected thumb + item states
```

根控件在 `Render()` 中绘制轨道背景和选中滑块。item 模板绘制每个选项自身的背景、图标、内容和状态颜色。选中滑块位置来自当前选中容器相对根控件的坐标，尺寸来自当前选中容器最终排列后的 `Bounds.Size`。

`Shape=Default` 时，根、item 和滑块圆角继续由 SizeType 对应的 SharedToken 决定。`Shape=Round` 时，Shape 分支在 SizeType 分支之后统一覆盖根、item 和滑块圆角为胶囊几何；该覆盖不新增 Design Token，也不改变模板结构。

Token 来源：

SegmentedToken 是 Segmented 的控件级 Token scope，描述分段轨道、选项文本状态、选项背景状态、选中滑块背景和 item 尺寸的主题语义。

SegmentedToken 不承载以下状态：

- `Items`、`ItemsSource`、`ItemTemplate`、`Content` 等数据状态。
- `SelectedIndex`、`SelectedItem`、`:selected`、`:pressed`、`:has-icon` 等实例或伪类状态本身。
- `SelectedThumbPos`、`SelectedThumbSize` 等运行时布局派生状态。
- `Orientation`、`IsExpanding`、可见 item 数量、排列轴和等分宽度等布局状态。
- `Shape` 和 Round 胶囊圆角；它们是实例形状状态和几何覆盖，不是主题尺度。
- `IsMotionEnabled` 或 transition 时长开关；motion 时长来自 SharedToken。

## AOT 与裁剪注意事项

Segmented 不依赖运行时反射或动态成员访问。主题协作通过固定模板节点、显式控件类型、Token resource 和 Avalonia property binding 完成。

资源与生命周期边界：

- `SegmentedToken` 通过 token generator 注册，Theme 通过 `SegmentedTokenResource` 使用。
- `SegmentedStackPanel.Orientation` 和 `IsExpanding` 在 AXAML 中绑定到最近的 `Segmented` ancestor。
- 容器的 `SizeType`、`Shape` 和 `IsMotionEnabled` 是生成容器与 owner 的固定关系，生命周期由容器准备和 Avalonia 绑定系统管理。
- `SelectionChanged` 订阅必须在 attach/detach 中成对管理。
- 选中滑块动画只在 `IsMotionEnabled=true` 时通过 transitions 启用。

方向和 Shape 不新增 Visual、缓存、timer、subscription 或运行时对象图。Panel 的 measure/arrange 仍为 O(n)，Shape 只增加一个 owner-to-container AvaloniaProperty 绑定；滑块继续使用值类型的 `Point` 和 `Size`，不拆分为方向专用 motion 对象。

AOT 边界：

- 不新增字符串反射、动态类型扫描、`Activator.CreateInstance(Type)` 或运行时属性名访问。
- API 与 Token 契约由控件文档、源码 public surface、Token 类型或生成数据维护。
- 主题内数据模板使用显式 `x:DataType`。

## 源码索引

共享源码：

- `src/AtomUI.Controls/Segmented/SegmentedShape.cs`：Segmented 专用 `Default` / `Round` 形状枚举。
- `src/AtomUI.Controls/Segmented/AbstractSegmented.cs`：共享根控件，定义公共属性、内部滑块属性、选择生命周期、方向键选择、容器准备、Form 接口和 render 绘制。
- `src/AtomUI.Controls/Segmented/AbstractSegmentedItem.cs`：共享 item，定义 `IsSelected`、`Icon`、内部 `SizeType` / `Shape` / `IsMotionEnabled`、图标伪类和 pointer release 选择。
- `src/AtomUI.Controls/Segmented/SegmentedStackPanel.cs`：内部方向感知 items panel，执行横向/纵向排列、水平 expanding 和可导航容器协作。
- `src/AtomUI.Controls/Segmented/SegmentedPseudoClass.cs`：Segmented 专用伪类常量。

桌面源码：

- `src/AtomUI.Desktop.Controls/Segmented/Segmented.cs`：桌面公开根控件，注册 Token scope，创建 `SegmentedItem` 容器。
- `src/AtomUI.Desktop.Controls/Segmented/SegmentedItem.cs`：桌面公开 item，注册 Token scope。
- `src/AtomUI.Desktop.Controls/Segmented/SegmentedToken.cs`：Segmented 控件 Token。
- `src/AtomUI.Desktop.Controls/Segmented/Themes/SegmentedTheme.axaml`：根模板、轨道、滑块、方向/expanding、SizeType/Shape 和 motion 样式。
- `src/AtomUI.Desktop.Controls/Segmented/Themes/SegmentedItemTheme.axaml`：item 模板、状态样式、SizeType/Shape 和图标样式。

Gallery 和测试：

- `controlgallery/AtomUIGallery/ShowCases/DataDisplay/Segmented/`：Segmented 示例、源码片段和本地化文案。
- `tests/AtomUI.Desktop.Controls.Tests/Segmented/SegmentedSelectionInitializationTests.cs`：选择初始化、Form value 和 expanding 布局回归测试。
- `tests/AtomUI.Desktop.Controls.Tests/SizeType/CustomizableSizeTypeContractTests.cs`：`ICustomizableSizeTypeAware` 契约测试。
- `tests/AtomUIGallery.Tests/ShowCases/SegmentedShowCasePageTests.cs`：Gallery 页面结构和示例快照测试。

## 相关文档

- 源设计文档：`docs/controls/desktop/data-display/segmented/overview.md`
- 实现文档：`docs/controls/desktop/data-display/segmented/implementation.md`
- Token 文档：`docs/controls/desktop/data-display/segmented/token.md`
- 变更记录：`docs/controls/desktop/data-display/segmented/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/statistic/index-cn.md

# Statistic

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

Statistic 是 AtomUI 桌面控件体系中的统计数值控件，用于展示标题、数值、前后缀、计时或动态计数。

Statistic 不负责图表系统、表格聚合或实时数据订阅服务。这些职责应由业务层、组合控件或更专用的 AtomUI 控件承担。

主要源码入口：

- `src/AtomUI.Desktop.Controls/Statistic`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/DataDisplay/Statistic` |
| 状态 | Stable |

## 何时使用

Statistic 的设计语言围绕控件职责、可观察状态和主题契约组织，而不是围绕模板节点组织。

| 维度 | 含义 | Statistic 中的表达 |
| --- | --- | --- |
| 产品语义 | 控件在界面中承担的稳定职责。 | Statistic 是 AtomUI 桌面控件体系中的统计数值控件，用于展示标题、数值、前后缀、计时或动态计数。 |
| 内容承载 | 用户数据、展示内容、集合项或操作入口如何进入控件。 | `AnimatingValue`、`ContentFontSize`、`ContentForeground`、`EndValue`、`Value`、`ValuePrefixAddOn`、`ValuePrefixAddOnTemplate`、`ValueSuffixAddOn` 等 9 项。 |
| 状态反馈 | public API、内部状态和伪类如何形成用户可感知反馈。 | loading/async、collection/filter、input/value、motion、visual option。 |
| 主题语义 | ControlTheme、SharedToken、控件 Token 和模板绑定如何表达视觉。 | Statistic Token + ControlTheme。 |

## 公共 API

Statistic 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。

核心 public surface 按语义分组维护：

| 契约组 | 代表成员 | 维护含义 |
| --- | --- | --- |
| 内容与数据 | `AnimatingValue`、`ContentFontSize`、`ContentForeground`、`EndValue`、`Value`、`ValuePrefixAddOn`、`ValuePrefixAddOnTemplate`、`ValueSuffixAddOn`、`ValueSuffixAddOnTemplate` | 定义控件展示内容、输入数据、模板或业务对象入口。 |
| 选择与集合 | `GroupSeparator` | 维护选择、展开、过滤、分页、分组或集合状态。 |
| 交互与状态 | `IsLoading` | 表达用户可观察状态、可用性、清除、加载或反馈语义。 |
| 动效与异步 | `RefreshDuration` | 约束动效开关、异步加载、播放速度、超时和任务边界。 |
| 其他稳定入口 | `DecimalSeparator`、`Format`、`Precision` | 保留为 public surface，变更前需确认 Gallery 和用户 XAML 依赖。 |

当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。

主要公开类型与枚举：

- 类型：`AbstractStatistic`、`Statistic`、`StatisticCountUp`、`TimerStatistic`。
- 枚举：无。

稳定 template part：

当前控件没有显式 `[TemplatePart]` 契约；主题节点仍通过 ControlTheme key、资源 key 和 Gallery 可观察行为形成稳定边界。

当前未抽取到控件专属伪类；主题主要依赖 Avalonia 标准伪类、模板绑定和内部 StyledProperty。

## 事件与命令

Statistic 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。
当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/Statistic/Views/StatisticShowCase.axaml:34`

Gallery key：`ExamplesContent` / item `0`

```axaml
<UniformGrid Columns="2" Rows="2">
    <atom:Statistic Header="活跃用户" Value="112893" />
    <StackPanel Orientation="Vertical" Spacing="16">
        <atom:Statistic Header="账户余额（CNY）" Value="112893" Precision="2" />
        <atom:Button ButtonType="Primary" Content="充值" />
    </StackPanel>
    <atom:Statistic Header="活跃用户" Value="112893" IsLoading="True" />
</UniformGrid>
```

### 动画数字

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/Statistic/Views/StatisticShowCase.axaml:94`

Gallery key：`ExamplesContent` / item `3`

```axaml
<UniformGrid Columns="2" Rows="2">
    <atom:Statistic Header="活跃用户" Value="112893">
        <atom:Statistic.Content>
            <atom:StatisticCountUp EndValue="{Binding Values}" Precision="2" />
        </atom:Statistic.Content>
    </atom:Statistic>
    <atom:Statistic Header="账户余额（CNY）" Value="112893">
        <atom:Statistic.Content>
            <atom:StatisticCountUp EndValue="{Binding Values}" Precision="2" />
        </atom:Statistic.Content>
    </atom:Statistic>
</UniformGrid>
```

### 计时器

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/Statistic/Views/StatisticShowCase.axaml:114`

Gallery key：`ExamplesContent` / item `4`

```axaml
<UniformGrid Columns="2" Rows="3" RowSpacing="10">
    <atom:TimerStatistic Value="{Binding Deadline}" />
    <atom:TimerStatistic Header="毫秒" Value="{Binding Deadline}" Format="hh\:mm\:ss\.fff" />
    <atom:TimerStatistic Header="倒计时" Value="{Binding TenSecondsLater}" />
    <atom:TimerStatistic Header="正计时" Value="{Binding Before}" />
    <atom:TimerStatistic Header="天级倒计时" Value="{Binding Deadline}" Format="d\ \天\ h\ \时\ m\ \分\ s\ \秒" />
    <atom:TimerStatistic Header="天级正计时" Value="{Binding Before}" Format="d\ \天\ h\ \时\ m\ \分\ s\ \秒" />
</UniformGrid>
```

## 状态模型

Statistic 的状态流按以下路径收敛：

```text
Public API / inherited command / item source / user input
  -> 控件实例状态
  -> effective state / pseudo-class / template property
  -> ControlTheme selector / presenter / renderer
  -> Gallery 可观察行为
```

状态维护规则：

- Disabled 或不可交互状态优先屏蔽 pointer、keyboard、motion 和提交类反馈。
- loading/async、collection/filter、input/value、motion、visual option 状态由控件实例或明确的数据 owner 推导，不能在 template part 之间双向竞争。
- 模板重套用时必须把 public API 对应状态回放到新的 part、伪类和主题变量。
- 集合、弹层、异步、动效或窗口相关状态必须能处理 reset、close、cancel、detach 和 owner 释放。

## 主题与 Design Token

Statistic 的视觉模型由控件模板、ControlTheme、SharedToken 和必要的控件 Token 共同构成。

| 主题文件 | 职责 |
| --- | --- |
| `AbstractStatisticTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `StatisticCountUpTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `StatisticTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `TimerStatisticTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |

Statistic 使用 `StatisticToken` 作为控件 Token scope。Token 只表达组件视觉语义，不承载 loading/async、collection/filter、input/value、motion、visual option 运行时状态。

主题维护规则：

- 不删除或重命名已经稳定的 ControlTheme key、template part、伪类和资源 key。
- 不把可由 AXAML 表达的模板状态迁移为 C# 动态创建视觉。
- 不把 hover、pressed、selected、expanded、loading、filter、popup open 等运行时状态写入 Token。
- Browser 或平台特化主题必须保持同一 API 的语义一致。

Token 来源：

Statistic Token 只表达组件级视觉变量，例如尺寸、间距、颜色、圆角、阴影、图标尺寸和弹层边界。Token 不承载运行时选择、展开、加载、错误、上传任务、过滤条件或业务状态。

当前 Token scope：

- `StatisticToken`，scope id 为 `Statistic`，源码位于 `src/AtomUI.Desktop.Controls/Statistic/StatisticToken.cs`。

## AOT 与裁剪注意事项

资源和 AOT 约束：

- 不通过运行时反射扫描 public API、Token 或 Gallery 示例数据。
- 不把可静态声明的模板结构迁移到 C# 动态创建。
- 异步加载、上传、弹层和窗口生命周期必须能取消或释放。
- 缓存对象必须与控件、窗口、弹层或数据 owner 生命周期一致。
- Source generator 生成文件不手工编辑；需要修改时改输入源或 generator。

性能边界：

- 控件应优先复用 Avalonia 原生虚拟化、模板绑定和资源系统。
- 避免为每次状态变化创建不必要的视觉对象、订阅或动画对象。
- 隐藏祖先下的 TimerStatistic 不运行刷新 timer；恢复时必须从绝对时间重算，不补发隐藏期间的 tick。
- 大集合控件必须保证 container recycle 后不会泄漏旧 item 状态。

## 源码索引

主要源码文件：

- `src/AtomUI.Desktop.Controls/Statistic/AbstractStatistic.cs`
- `src/AtomUI.Desktop.Controls/Statistic/Statistic.cs`
- `src/AtomUI.Desktop.Controls/Statistic/StatisticCountUp.cs`
- `src/AtomUI.Desktop.Controls/Statistic/StatisticToken.cs`
- `src/AtomUI.Desktop.Controls/Statistic/StatisticUtils.cs`
- `src/AtomUI.Desktop.Controls/Statistic/Themes/AbstractStatisticTheme.axaml`
- `src/AtomUI.Desktop.Controls/Statistic/Themes/AbstractStatisticTheme.cs`
- `src/AtomUI.Desktop.Controls/Statistic/Themes/StatisticCountUpTheme.axaml`
- `src/AtomUI.Desktop.Controls/Statistic/Themes/StatisticTheme.axaml`
- `src/AtomUI.Desktop.Controls/Statistic/Themes/TimerStatisticTheme.axaml`
- `src/AtomUI.Desktop.Controls/Statistic/TimerStatistic.cs`

职责边界：

- 控件主文件保留 public/protected API、Avalonia 属性注册、事件和主要生命周期入口。
- Theme 文件负责静态视觉结构、template part、selector 和资源绑定。
- Token 文件只提供组件视觉变量，不保存实例状态。
- Gallery 文件只展示用法和示例，不作为运行时逻辑 owner。

## 相关文档

- 源设计文档：`docs/controls/desktop/data-display/statistic/overview.md`
- 实现文档：`docs/controls/desktop/data-display/statistic/implementation.md`
- Token 文档：`docs/controls/desktop/data-display/statistic/token.md`
- 变更记录：`docs/controls/desktop/data-display/statistic/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/tag/index-cn.md

# Tag

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

Tag 家族覆盖展示标签与选择标签。`Tag` 用于展示状态、分类、可关闭标记和预设色语义；`CheckableTag` 用标签视觉表达二态选择；`CheckableTagGroup` 在有限选项集合上提供可取消单选和多选。普通 Tag 的颜色模型由 `TagColor` 和 `TagVariant` 两个正交维度组成，按照当前三种变体规范的 `Color × Variant` 语义生成最终视觉。

Tag 不负责复杂筛选器、按钮或徽标计数。这些职责应由业务层、组合控件或更专用的 AtomUI 控件承担。

主要源码入口：

- `src/AtomUI.Controls/Tag`
- `src/AtomUI.Desktop.Controls/Tag`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/DataDisplay/Tag` |
| 状态 | Stable |

## 何时使用

Tag 的设计语言围绕控件职责、可观察状态和主题契约组织，而不是围绕模板节点组织。

| 维度 | 含义 | Tag 中的表达 |
| --- | --- | --- |
| 产品语义 | 控件在界面中承担的稳定职责。 | Tag 表达展示语义，CheckableTag 表达二态选择，CheckableTagGroup 表达一组选项的单选或多选。 |
| 内容承载 | 用户数据、展示内容、集合项或操作入口如何进入控件。 | Tag 使用 `Text`/`Icon`；CheckableTag 使用 `Content`/`Icon`；Group 使用 `Options` 和 `ItemTemplate`。 |
| 状态反馈 | public API、内部状态和伪类如何形成用户可感知反馈。 | Tag 使用颜色分类和 `Variant`；CheckableTag 使用 `IsChecked`；Group 使用 `CheckedItem(s)`。 |
| 主题语义 | ControlTheme、SharedToken、控件 Token 和模板绑定如何表达视觉。 | Tag Token + ControlTheme。 |

## 公共 API

Tag 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。

核心 public surface 按语义分组维护：

| 契约组 | 代表成员 | 维护含义 |
| --- | --- | --- |
| 内容与数据 | `CloseIcon`、`Icon`、`Text` | 定义控件展示内容、输入数据、模板或业务对象入口。 |
| 交互与状态 | `IsClosable`、`Variant` | 表达关闭能力和 Filled/Solid/Outlined 视觉形态。 |
| 视觉与布局 | `TagColor` | 选择 Default、Preset、Status 或 Custom 颜色类别。 |

稳定事件包括 `Closed`。事件触发顺序属于兼容契约，不能因内部状态重排而改变。

主要公开类型与枚举：

- 类型：`AbstractTag`、`Tag`。
- 枚举：`TagStatus`、`TagVariant`。

`Variant` 的默认值为 `TagVariant.Filled`。Tag 不提供全局 Variant 配置，也不把 Variant 写入 `ThemeConfig`；应用需要统一样式时使用 Avalonia Style，控件实例的本地值优先于样式值。

选择标签的 public surface 按以下边界维护：

| 控件 | 主要契约 | 默认与状态 owner |
| --- | --- | --- |
| `CheckableTag` | `IsChecked`、`IsEnabled`、`Content`、`ContentTemplate`、`Command`、`Icon`、`IsMotionEnabled` | 复用 ToggleButton 二态状态；不继承普通 Tag 的颜色、Variant 或关闭 API。 |
| `CheckableTagGroup` | `Options`、`IsMultiple`、`CheckedItem`、`CheckedItems`、Default 值、`ItemTemplate`、布局属性和 `CheckedChanged` | 默认可取消单选；Group 是公开选择值的唯一 owner。 |
| `ICheckableTagOption` / `CheckableTagOption` | `Value`、`Content` | Value 非空且在同一 Group 中唯一。 |

Group 的 `CheckedItem` 和 `CheckedItems` 默认支持 TwoWay binding；前者只在单选模式生效，后者只在多选模式生效。完整模式转换、Default 初始化和事件契约见选择模型专项文档。

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_CloseButton` | `AbstractIconButton` | 承载用户触发入口、导航或关闭动作。 |

控件专属或内部伪类包括 `CustomColor=:custom-color`、`PresetColor=:preset-color`、`StatusColor=:status-color`、`TagPseudoClass.CustomColor`、`TagPseudoClass.PresetColor`、`TagPseudoClass.StatusColor`。这些伪类属于主题 selector 可观察契约，不能在未同步主题和 Gallery 的情况下重命名或删除。

## 事件与命令

Tag 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。
稳定事件包括 `Closed`。事件触发顺序属于兼容契约，不能因内部状态重排而改变。
Group 的 `CheckedItem` 和 `CheckedItems` 默认支持 TwoWay binding；前者只在单选模式生效，后者只在多选模式生效。完整模式转换、Default 初始化和事件契约见选择模型专项文档。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 可选择标签

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/Tag/Views/TagShowCase.axaml:59`

Gallery key：`ExamplesContent` / item `1`

```axaml
<atom:Form LabelColInfo="6*"
           WrapperColInfo="18*"
           MaxWidth="560"
           HorizontalAlignment="Stretch">
    <atom:FormItem LabelText="可选择">
        <atom:CheckableTag Content="是"
                           IsChecked="{Binding IsCheckableTagChecked, Mode=TwoWay}" />
    </atom:FormItem>
    <atom:FormItem LabelText="单选">
        <atom:CheckableTagGroup Options="{Binding CheckableTagOptions}"
                                CheckedItem="{Binding SingleCheckedTag, Mode=TwoWay}" />
    </atom:FormItem>
    <atom:FormItem LabelText="多选">
        <atom:CheckableTagGroup IsMultiple="True"
                                Options="{Binding CheckableTagOptions}"
                                CheckedItems="{Binding MultipleCheckedTags, Mode=TwoWay}" />
    </atom:FormItem>
</atom:Form>
```

### 形态变体

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/Tag/Views/TagShowCase.axaml:303`

Gallery key：`ExamplesContent` / item `5`

```axaml
<StackPanel Orientation="Vertical">
    <atom:TextBlock FontWeight="Bold" FontSize="14" Margin="0, 0, 0, 10" Text="默认颜色" />
    <WrapPanel HorizontalAlignment="Left">
        <atom:Tag Variant="Filled" Text="浅色填充" />
        <atom:Tag Variant="Solid" Text="实色填充" />
        <atom:Tag Variant="Outlined" Text="描边" />
    </WrapPanel>

    <atom:TextBlock FontWeight="Bold" FontSize="14" Margin="0, 20, 0, 10" Text="预设颜色" />
    <WrapPanel HorizontalAlignment="Left">
        <atom:Tag TagColor="blue" Variant="Filled" Text="蓝色" />
        <atom:Tag TagColor="blue" Variant="Solid" Text="蓝色" />
        <atom:Tag TagColor="blue" Variant="Outlined" Text="蓝色" />
    </WrapPanel>

    <atom:TextBlock FontWeight="Bold" FontSize="14" Margin="0, 20, 0, 10" Text="状态颜色" />
    <WrapPanel HorizontalAlignment="Left">
        <atom:Tag TagColor="success" Variant="Filled" Text="成功" />
        <atom:Tag TagColor="success" Variant="Solid" Text="成功" />
        <atom:Tag TagColor="success" Variant="Outlined" Text="成功" />
    </WrapPanel>

    <atom:TextBlock FontWeight="Bold" FontSize="14" Margin="0, 20, 0, 10" Text="自定义颜色" />
    <WrapPanel HorizontalAlignment="Left">
        <atom:Tag TagColor="#1677ff" Variant="Filled" Text="#1677ff" />
        <atom:Tag TagColor="#1677ff" Variant="Solid" Text="#1677ff" />
        <atom:Tag TagColor="#1677ff" Variant="Outlined" Text="#1677ff" />
    </WrapPanel>
</StackPanel>
```

## 状态模型

Tag 的状态流按以下路径收敛：

```text
TagColor + Variant + ThemeSnapshot
  -> ColorCategory(Default / Preset / Status / Custom)
  -> Foreground / Background / BorderBrush
  -> effective visual state / pseudo-class
  -> ControlTheme selector / presenter / renderer
  -> Gallery 可观察行为
```

状态维护规则：

- `TagColor` 只负责颜色类别，`Variant` 只负责视觉形态；颜色解析不得反向修改 Variant。
- `default`、`null`、空值和无效颜色必须恢复 Default 颜色状态，不能残留之前的 Brush 或伪类。
- `info` 和 `processing` 使用同一组 Info 语义 Token；`default` 使用 Tag 基础 Token。
- 所有 Variant 保持相同边框厚度，Filled 和部分 Solid 通过透明边框表达无可见边界。
- 模板重套用时必须把 public API 对应状态回放到新的 part、伪类和主题变量。

CheckableTag 家族的状态流保持独立：

```text
CheckableTag.IsChecked
  -> ToggleButton input state
  -> checked pseudo-class / ControlTheme

Options + IsMultiple + CheckedItem(s)
  -> Group value normalization
  -> internal SelectionModel
  -> CheckableTag.IsChecked
```

Group 的内部 SelectedItem(s) 只保存归一后的 option wrapper，不是 public state；子项交互必须先回到 Group，再由 Group 通过 `SetCurrentValue` 更新公开值。

## 主题与 Design Token

Tag 的视觉模型由控件模板、ControlTheme、SharedToken 和必要的控件 Token 共同构成。

| 主题文件 | 职责 |
| --- | --- |
| `TagTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `CheckableTagTheme.axaml` | 提供二态标签的内容结构以及 checked、focus、disabled 等状态视觉。 |
| `CheckableTagGroupTheme.axaml` | 组合内部选择控件、ItemsPresenter 和 WrapPanel。 |

Tag 使用 `TagToken` 作为控件 Token scope。Token 只表达组件视觉语义，不承载 input/value、visual option 运行时状态。

Tag 的视觉组合由以下矩阵表达：

| 颜色类别 | Filled | Solid | Outlined |
| --- | --- | --- | --- |
| Default | 浅背景、透明边框、默认文字 | `ColorBgSolid` 背景、对比文字 | 默认背景、默认边框、默认文字 |
| Preset | palette 1 背景、palette 7 文字 | palette 6 背景、浅色文字、palette 6 边框 | palette 1 背景、palette 3 边框、palette 7 文字 |
| Status | 状态浅背景、状态主色文字 | 状态主色背景、浅色文字、状态主色边框 | 状态浅背景、状态边框、状态主色文字 |
| Custom | HSL 亮度 0.95 背景、原色文字 | 原色背景、浅色文字 | HSL 亮度 0.95 背景、原色边框和文字 |

主题维护规则：

- `TagVariant`、`Variant=Filled` 默认值、`TagColor` 颜色分类、ControlTheme key、template part 和伪类是稳定契约。
- `IsBordered` 不属于当前契约，不得重新引入；旧 `bordered` 和 `color="xxx-inverse"` 兼容入口不属于当前设计。
- 不把可由 AXAML 表达的模板状态迁移为 C# 动态创建视觉。
- 不在控件中增加全局 Tag 配置或 ThemeConfig 组件配置；全局默认样式由 Avalonia Style 承担。
- 不把 hover、pressed、selected、expanded、loading、filter、popup open 等运行时状态写入 Token。
- Browser 或平台特化主题必须保持同一 API 的语义一致。

Token 来源：

Tag Token 只表达组件级视觉变量，例如尺寸、间距、颜色、圆角、阴影、图标尺寸和弹层边界。Token 不承载运行时选择、展开、加载、错误、上传任务、过滤条件或业务状态。

当前 Token scope：

- `TagToken`，scope id 为 `Tag`，源码位于 `src/AtomUI.Desktop.Controls/Tag/TagToken.cs`。

## AOT 与裁剪注意事项

资源和 AOT 约束：

- 不通过运行时反射扫描 public API、Token 或 Gallery 示例数据。
- 不把可静态声明的模板结构迁移到 C# 动态创建。
- 异步加载、上传、弹层和窗口生命周期必须能取消或释放。
- 缓存对象必须与控件、窗口、弹层或数据 owner 生命周期一致。
- Source generator 生成文件不手工编辑；需要修改时改输入源或 generator。
- CheckableTagGroup 的 option 解析和属性同步使用静态类型、AvaloniaProperty 和强类型 getter，不使用 ReflectionBinding、字符串 path 或运行时类型扫描。

性能边界：

- 控件应优先复用 Avalonia 原生虚拟化、模板绑定和资源系统。
- 避免为每次状态变化创建不必要的视觉对象、订阅或动画对象。
- 大集合控件必须保证 container recycle 后不会泄漏旧 item 状态。
- Group 只在 Options、模式或公开选择值变化时执行 O(N) 归一和同步；单项交互不能通过遍历 VisualTree 查找业务值。
- 外部 CheckedItems 必须复制为内部快照，不能直接作为 internal SelectedItems 持有或原地修改。

## 源码索引

主要源码文件：

- `src/AtomUI.Controls/Tag/AbstractTag.cs`
- `src/AtomUI.Controls/Tag/TagEnums.cs`
- `src/AtomUI.Controls/Tag/TagPseudoClass.cs`
- `src/AtomUI.Desktop.Controls/Tag/Tag.cs`
- `src/AtomUI.Desktop.Controls/Tag/TagToken.cs`
- `src/AtomUI.Desktop.Controls/Tag/Themes/TagTheme.axaml`
- `src/AtomUI.Desktop.Controls/Tag/Themes/TagTheme.cs`

职责边界：

- 控件主文件保留 public/protected API、Avalonia 属性注册、事件和主要生命周期入口。
- Theme 文件负责静态视觉结构、template part、selector 和资源绑定。
- Token 文件只提供组件视觉变量，不保存实例状态。
- Gallery 文件只展示用法和示例，不作为运行时逻辑 owner。

## 相关文档

- 源设计文档：`docs/controls/desktop/data-display/tag/overview.md`
- 实现文档：`docs/controls/desktop/data-display/tag/implementation.md`
- Token 文档：`docs/controls/desktop/data-display/tag/token.md`
- 变更记录：`docs/controls/desktop/data-display/tag/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/timeline/index-cn.md

# Timeline

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

Timeline 是 AtomUI 桌面控件体系中的时间轴控件，用于沿垂直或水平方向按顺序展示事件节点、状态和时间信息。控件使用同一套 Item、Indicator、Panel 和主题结构表达两种方向，不创建方向专用的平行控件家族。

Timeline 不负责日历、列表排序或流程引擎。这些职责应由业务层、组合控件或更专用的 AtomUI 控件承担。

主要源码入口：

- `src/AtomUI.Controls/Timeline`
- `src/AtomUI.Desktop.Controls/Timeline`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/DataDisplay/Timeline` |
| 状态 | Stable |

## 何时使用

Timeline 的设计语言围绕控件职责、可观察状态和主题契约组织，而不是围绕模板节点组织。

| 维度 | 含义 | Timeline 中的表达 |
| --- | --- | --- |
| 产品语义 | 控件在界面中承担的稳定职责。 | Timeline 是 AtomUI 桌面控件体系中的时间轴控件，用于按顺序展示事件节点、状态和时间信息。 |
| 内容承载 | 用户数据、展示内容、集合项或操作入口如何进入控件。 | `Content`、`Label`、`IndicatorIcon`、`Pending`、`PendingIcon`。 |
| 状态反馈 | public API、内部状态和伪类如何形成用户可感知反馈。 | `IsReverse`、Pending 状态、可见项顺序和首尾节点状态。 |
| 布局语义 | 主轴方向和内容相对轴线的位置如何组合。 | `Orientation` 决定主轴，`Mode` 决定交叉轴上的 `Start`、`End` 或交替布局。 |
| 主题语义 | ControlTheme、SharedToken、控件 Token 和模板绑定如何表达视觉。 | Timeline Token、方向 selector、Item 模板和 Indicator renderer。 |

## 公共 API

Timeline 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。

核心 public surface 按语义分组维护：

| 契约组 | 代表成员 | 维护含义 |
| --- | --- | --- |
| 内容与数据 | `Content`、`Label`、`IndicatorIcon`、`Pending`、`PendingIcon` | 定义单项内容、时间标签、节点图标和待处理节点入口。 |
| 集合顺序 | `Items`、`ItemsSource`、`IsReverse` | 维护源顺序、最终视觉顺序和 Pending 项的相邻关系。 |
| 方向与模式 | `Orientation`、`Mode` | 决定主轴方向以及内容位于轴线的 Start、End 或交替侧。 |
| 内部派生状态 | `IsLabelLayout`、`IsOdd`、`IsFirst`、`IsLast`、`NextIsPending` | 由 Timeline 根据可见项视觉顺序单向投影到 Item 和模板。 |
| 视觉与布局 | `IndicatorColor`、`IndicatorIcon` | 影响节点颜色、形状和轴线渲染。 |
| 其他稳定入口 | `Label`、`Pending` | 保留为 public surface，变更前需确认 Gallery 和用户 XAML 依赖。 |

当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。

主要公开类型与枚举：

- 类型：`AbstractTimeline`、`AbstractTimelineItem`、`Timeline`、`TimelineItem`。
- 枚举：`TimelineMode`。

`TimelineIndicator`、`TimelineItemPanel` 和 `TimelineStackPanel` 是影响可观察布局与渲染的 internal 协作类型，不属于用户可直接依赖的 public surface。

方向与模式的公共契约为：

```csharp
public enum TimelineMode
{
    Start,
    End,
    Alternate
}
```

- `Orientation` 使用 `Avalonia.Layout.Orientation`，默认值为 `Vertical`。
- `Mode` 默认值为 `TimelineMode.Start`。
- `Start` 和 `End` 是相对 Timeline 轴线的逻辑位置，不是固定的物理 Left/Right。
- `Alternate` 从最终视觉顺序中的第一个可见项开始按 `Start`、`End` 交替。
- Timeline 不提供单个 `TimelineItem` 的 placement 覆盖属性；位置策略由 Timeline 统一拥有。

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_IconPresenter` | `IconPresenter` | 展示用户内容、文本、图标或模板化数据。 |

控件专属或内部伪类包括 `OrderEvenPC`、`OrderFirstPC`、`OrderLastPC`、`OrderOddPC`、`PendingItemPC`。这些伪类属于主题 selector 可观察契约，不能在未同步主题和 Gallery 的情况下重命名或删除。

## 事件与命令

Timeline 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。
当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/Timeline/Views/TimelineShowCase.axaml:36`

Gallery key：`ExamplesContent` / item `0`

```axaml
<atom:Timeline>
    <atom:TimelineItem Content="2024-01-01 AtomUI 正式启动" />
    <atom:TimelineItem IndicatorColor="green" Content="2024-08-12 经过 7 个多月的开发，AtomUI 正式开源。欢迎大家关注我们。" />
    <atom:TimelineItem IndicatorColor="red" Content="2024-10-01 发布 0.0.1 预览版" />
</atom:Timeline>
```

### 颜色

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/Timeline/Views/TimelineShowCase.axaml:49`

Gallery key：`ExamplesContent` / item `1`

```axaml
<atom:Timeline>
    <atom:TimelineItem IndicatorColor="green" Content="2024-01-01 AtomUI 正式启动" />
    <atom:TimelineItem IndicatorColor="blue" Content="2024-01-01 AtomUI 正式启动" />
    <atom:TimelineItem IndicatorColor="Red" Content="2024-01-01 AtomUI 正式启动" />
    <atom:TimelineItem IndicatorColor="gray" Content="2024-01-01 AtomUI 正式启动" />
    <atom:TimelineItem IndicatorColor="#00CCFF" Content="2024-01-01 AtomUI 正式启动" />
</atom:Timeline>
```

### 最后节点和反转

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/Timeline/Views/TimelineShowCase.axaml:64`

Gallery key：`ExamplesContent` / item `2`

```axaml
<StackPanel>
    <atom:Timeline
        Pending="记录中..."
        IsReverse="{Binding ReverseTimelineIsReverse}"
        x:Name="ReverseTimeline">
        <atom:TimelineItem Label="2024-01-01" Content="2024-01-01 AtomUI 正式启动。1" />
        <atom:TimelineItem Label="2024-08-12" Content="2024-01-01 AtomUI 正式启动。2" />
        <atom:TimelineItem Label="2024-10-01" Content="2024-01-01 AtomUI 正式启动。3" />
    </atom:Timeline>
    <DockPanel>
        <atom:Button ButtonType="Primary"
                     x:Name="ReverseButton"
                     Click="ReverseButtonClick"
                     Content="切换反转" />
    </DockPanel>
</StackPanel>
```

### 动态模式

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/Timeline/Views/TimelineShowCase.axaml:104`

Gallery key：`ExamplesContent` / item `4`

```axaml
<StackPanel>
    <WrapPanel Margin="0,0,0,20" Orientation="Horizontal">
        <WrapPanel.Styles>
            <Style Selector="atom|RadioButton">
                <Setter Property="Margin" Value="5" />
            </Style>
        </WrapPanel.Styles>
        <atom:RadioButton IsChecked="True"
                          x:Name="ModeStart"
                          Tag="{x:Static atom:TimelineMode.Start}"
                          IsCheckedChanged="ModeChecked"
                          Content="起始" />
        <atom:RadioButton x:Name="ModeEnd"
                          Tag="{x:Static atom:TimelineMode.End}"
                          IsCheckedChanged="ModeChecked"
                          Content="结束" />
        <atom:RadioButton x:Name="ModeAlternate"
                          Tag="{x:Static atom:TimelineMode.Alternate}"
                          IsCheckedChanged="ModeChecked"
                          Content="交替" />
    </WrapPanel>
    <atom:Timeline Mode="{Binding SelectedTimelineMode}" x:Name="LabelTimeline">
        <atom:TimelineItem Label="2024-01-01" Content="AtomUI 正式启动" />
        <atom:TimelineItem Label="2015-09-01 09:12:11" Content="创建服务站点" />
        <atom:TimelineItem Content="Qinware 网站上线" />
        <atom:TimelineItem Label="2029-09-01" Content="网络问题正在解决" />
    </atom:Timeline>
</StackPanel>
```

## 状态模型

Timeline 的状态流按以下路径收敛：

```text
Orientation / Mode / IsReverse / Items / item visibility
  -> Timeline 计算可见项视觉顺序
  -> item effective mode / order / first / last / pending adjacency
  -> internal property / pseudo-class / template property
  -> ControlTheme selector / presenter / renderer
  -> Gallery 可观察行为
```

方向与模式的稳定语义：

| Orientation | Mode | 无 Label | 存在 Label |
| --- | --- | --- | --- |
| `Vertical` | `Start` | 轴线位于逻辑起始侧，Content 位于结束侧。 | Label 位于起始侧，Content 位于结束侧。 |
| `Vertical` | `End` | Content 位于逻辑起始侧，轴线位于结束侧。 | Content 位于起始侧，Label 位于结束侧。 |
| `Horizontal` | `Start` | 轴线在上，Content 在下。 | Label 在上，Content 在下。 |
| `Horizontal` | `End` | Content 在上，轴线在下。 | Content 在上，Label 在下。 |
| 任意方向 | `Alternate` | 第一可见项为 Start，后续按 End、Start 交替。 | 使用同一交替规则，并保持所有节点共用同一轴线。 |

`FlowDirection` 只影响垂直 Timeline 的逻辑起始侧和结束侧；水平 Timeline 的 Start/End 分别映射到下方和上方。`IsReverse` 只反转主轴视觉顺序，不交换 Start/End。隐藏项不占用布局槽位，也不参与交替奇偶、首尾和 Pending 相邻关系计算。

## 主题与 Design Token

Timeline 的视觉模型由控件模板、ControlTheme、SharedToken 和必要的控件 Token 共同构成。

| 主题文件 | 职责 |
| --- | --- |
| `TimelineIndicatorTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `TimelineItemTheme.axaml` | 定义集合项、容器项或局部单元的状态视觉。 |
| `TimelineTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |

Timeline 使用 `TimelineToken` 作为控件 Token scope。Token 只表达组件视觉语义，不承载方向、Mode、视觉索引或 Pending 相邻状态。水平布局的内容间距优先使用 SharedToken；方向差异由 ControlTheme selector 和布局 Panel 表达。

主题维护规则：

- 不删除或重命名已经稳定的 ControlTheme key、template part、伪类和资源 key。
- 不把可由 AXAML 表达的模板状态迁移为 C# 动态创建视觉。
- 不把 hover、pressed、selected、expanded、loading、filter、popup open 等运行时状态写入 Token。
- Browser 或平台特化主题必须保持同一 API 的语义一致。

Token 来源：

Timeline Token 只表达节点、连接线和 Item 的组件级尺寸、间距与颜色。Token 不承载 Orientation、Mode、视觉索引、首尾、Reverse、Pending 邻接或其他实例运行时状态。

当前 Token scope：

- `TimelineToken`，scope id 为 `Timeline`，源码位于 `src/AtomUI.Desktop.Controls/Timeline/TimelineToken.cs`。

## AOT 与裁剪注意事项

资源和 AOT 约束：

- 不通过运行时反射扫描 public API、Orientation、Mode、Token 或 Gallery 示例数据。
- 不把可静态声明的 TimelineItem 模板迁移到 C# 动态创建，也不为 Horizontal 创建第二套视觉树。
- 视觉顺序重算为 O(N)，只在结构状态变化时执行，不进入 Render 热路径。
- 水平 Measure/Arrange 为 O(N)，只使用已有容器和局部尺寸值。
- TimelineIndicator 的 dot Pen 和 line Pen 与 Brush/Width 缓存键保持一致，属性变化时精准失效。
- 新增属性使用静态 AvaloniaProperty 注册和 AXAML 绑定，不引入反射、动态发现或 trimming 风险。
- Source generator 和 LLMS 生成文件不手工编辑；需要修改时更新源码、主题、Gallery 和人工维护文档源。

性能边界：

- 控件优先复用 Avalonia 属性失效、模板绑定和资源系统。
- 运行时切换 Orientation 或 Mode 不能创建新 Item、Panel、Indicator、订阅或动画对象。
- 容器重用后必须覆盖 Orientation、Mode、顺序和 Pending 派生状态，不能保留旧 Item 状态。

## 源码索引

主要源码文件：

- `src/AtomUI.Controls/Timeline/AbstractTimeline.cs`
- `src/AtomUI.Controls/Timeline/AbstractTimelineItem.cs`
- `src/AtomUI.Controls/Timeline/TimeLineEnums.cs`
- `src/AtomUI.Controls/Timeline/TimelineIndicator.cs`
- `src/AtomUI.Controls/Timeline/TimelineItemPanel.cs`
- `src/AtomUI.Controls/Timeline/TimelineStackPanel.cs`
- `src/AtomUI.Desktop.Controls/Timeline/Themes/TimelineIndicatorTheme.axaml`
- `src/AtomUI.Desktop.Controls/Timeline/Themes/TimelineItemTheme.axaml`
- `src/AtomUI.Desktop.Controls/Timeline/Themes/TimelineTheme.axaml`
- `src/AtomUI.Desktop.Controls/Timeline/Timeline.cs`
- `src/AtomUI.Desktop.Controls/Timeline/TimelineItem.cs`
- `src/AtomUI.Desktop.Controls/Timeline/TimelineToken.cs`

职责边界：

- 控件主文件保留 public/protected API、Avalonia 属性注册、事件和主要生命周期入口。
- Theme 文件负责静态视觉结构、template part、selector 和资源绑定。
- Token 文件只提供组件视觉变量，不保存实例状态。
- Gallery 文件只展示用法和示例，不作为运行时逻辑 owner。

## 相关文档

- 源设计文档：`docs/controls/desktop/data-display/timeline/overview.md`
- 实现文档：`docs/controls/desktop/data-display/timeline/implementation.md`
- Token 文档：`docs/controls/desktop/data-display/timeline/token.md`
- 变更记录：`docs/controls/desktop/data-display/timeline/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/tooltip/index-cn.md

# Tooltip

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

Tooltip 是 AtomUI 桌面控件体系中的工具提示控件，用于在目标元素附近展示短文本或轻量说明。

Tooltip 不负责复杂弹层、菜单或模态对话框。这些职责应由业务层、组合控件或更专用的 AtomUI 控件承担。

主要源码入口：

- `src/AtomUI.Desktop.Controls/Tooltip`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/DataDisplay/Tooltip` |
| 状态 | Stable |

## 何时使用

Tooltip 的设计语言围绕控件职责、可观察状态和主题契约组织，而不是围绕模板节点组织。

| 维度 | 含义 | Tooltip 中的表达 |
| --- | --- | --- |
| 产品语义 | 控件在界面中承担的稳定职责。 | Tooltip 是 AtomUI 桌面控件体系中的工具提示控件，用于在目标元素附近展示短文本或轻量说明。 |
| 内容承载 | 用户数据、展示内容、集合项或操作入口如何进入控件。 | 继承 ToolTip 的 `Content` 与目标控件 tooltip 绑定。 |
| 状态反馈 | public API、内部状态和伪类如何形成用户可感知反馈。 | motion。 |
| 主题语义 | ControlTheme、SharedToken、控件 Token 和模板绑定如何表达视觉。 | Tooltip Token + ControlTheme。 |

## 公共 API

Tooltip 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。

核心 public surface 按语义分组维护：

| 契约组 | 代表成员 | 维护含义 |
| --- | --- | --- |
| 内容与数据 | `Content` | 继承 ToolTip 的轻量说明内容入口。 |
| 交互与状态 | `IsMotionEnabled` | 表达用户可观察状态、可用性、清除、加载或反馈语义。 |
| 宿主内容与呈现 | `Tip`、`TipHostWidth`、`PresetColor`、`Color`、`IsArrowVisible` | 以附加属性配置在任意目标 `Control` 上，目标控件是这些值的 owner。 |
| 宿主文本布局 | `TextWrapping`、`TextTrimming` | 附加属性；控制 Tip 文本在最大宽度约束内的换行与截断行为，默认 `Wrap` / `None`。 |
| 宿主定位与时机 | `Placement`、`HorizontalOffset`、`VerticalOffset`、`MarginToAnchor`、`IsPointAtCenter`、`ShowDelay`、`BetweenShowDelay` | 附加属性；控制弹层相对宿主的定位与悬停出现时机。 |
| 打开状态与服务开关 | `IsOpen`、`IsCustomShowAndHide`、`ServiceEnabled`、`ShowOnDisabled`、`IsUseOverlayHost` | `IsOpen` 是声明式期望打开状态（见第 4.1 节），赋值时序不影响最终物理状态。 |

公开路由事件（Direct 路由，挂接在目标控件上）：

| 事件 | 语义 |
| --- | --- |
| `ToolTipOpening` | 打开前引发，可取消；否决时把 `IsOpen` 回写为 `false`。 |
| `ToolTipClosing` | 关闭前引发。 |

主要公开类型与枚举：

- 类型：`ToolTip`、`ToolTipPseudoClass`。
- 枚举：无。

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_ArrowDecorator` | `ArrowDecoratedBox` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_ContentPresenter` | `ContentPresenter` | 展示用户内容、文本、图标或模板化数据。 |

控件专属或内部伪类包括 `Open=:open`、`ToolTipPseudoClass.Open`。这些伪类属于主题 selector 可观察契约，不能在未同步主题和 Gallery 的情况下重命名或删除。

## 事件与命令

Tooltip 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。
公开路由事件（Direct 路由，挂接在目标控件上）：
| 事件 | 语义 |

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/Tooltip/Views/TooltipShowCase.axaml:36`

Gallery key：`ExamplesContent` / item `0`

```axaml
<StackPanel Orientation="Vertical" Spacing="10">
    <atom:TextBlock
        HorizontalAlignment="Left"
        atom:ToolTip.Tip="提示文本" Text="鼠标移入时显示提示。" />
</StackPanel>
```

### 弹出位置

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/Tooltip/Views/TooltipShowCase.axaml:51`

Gallery key：`ExamplesContent` / item `1`

```axaml
<Grid>
    <Grid.Styles>
        <Style Selector="atom|Button">
            <Setter Property="Margin" Value="5" />
            <Setter Property="Width" Value="80" />
            <Setter Property="atom:ToolTip.Tip" Value="提示文本" />
        </Style>
    </Grid.Styles>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="Auto" />
    </Grid.ColumnDefinitions>

    <atom:Button Grid.Row="1" Grid.Column="0" Content="左上">
        <atom:ToolTip.Placement>LeftEdgeAlignedTop</atom:ToolTip.Placement>
    </atom:Button>
    <atom:Button Grid.Row="2" Grid.Column="0" Content="左侧">
        <atom:ToolTip.Placement>Left</atom:ToolTip.Placement>
    </atom:Button>
    <atom:Button Grid.Row="3" Grid.Column="0" Content="左下">
        <atom:ToolTip.Placement>LeftEdgeAlignedBottom</atom:ToolTip.Placement>
    </atom:Button>

    <atom:Button Grid.Row="0" Grid.Column="1" Content="上左">
        <atom:ToolTip.Placement>TopEdgeAlignedLeft</atom:ToolTip.Placement>
    </atom:Button>
    <atom:Button Grid.Row="0" Grid.Column="2" Content="上方">
        <atom:ToolTip.Placement>Top</atom:ToolTip.Placement>
    </atom:Button>
    <atom:Button Grid.Row="0" Grid.Column="3" Content="上右">
        <atom:ToolTip.Placement>TopEdgeAlignedRight</atom:ToolTip.Placement>
    </atom:Button>

    <atom:Button Grid.Row="1" Grid.Column="4" Content="右上">
        <atom:ToolTip.Placement>RightEdgeAlignedTop</atom:ToolTip.Placement>
    </atom:Button>
    <atom:Button Grid.Row="2" Grid.Column="4" Content="右侧">
        <atom:ToolTip.Placement>Right</atom:ToolTip.Placement>
    </atom:Button>
    <atom:Button Grid.Row="3" Grid.Column="4" Content="右下">
        <atom:ToolTip.Placement>RightEdgeAlignedBottom</atom:ToolTip.Placement>
    </atom:Button>

    <atom:Button Grid.Row="4" Grid.Column="1" Content="下左">
        <atom:ToolTip.Placement>BottomEdgeAlignedLeft</atom:ToolTip.Placement>
    </atom:Button>
    <atom:Button Grid.Row="4" Grid.Column="2" Content="下方">
        <atom:ToolTip.Placement>Bottom</atom:ToolTip.Placement>
    </atom:Button>
    <atom:Button Grid.Row="4" Grid.Column="3" Content="下右">
        <atom:ToolTip.Placement>BottomEdgeAlignedRight</atom:ToolTip.Placement>
    </atom:Button>

</Grid>
```

## 状态模型

Tooltip 的状态流按以下路径收敛：

```text
Public API / inherited command / item source / user input
  -> 控件实例状态
  -> effective state / pseudo-class / template property
  -> ControlTheme selector / presenter / renderer
  -> Gallery 可观察行为
```

状态维护规则：

- Disabled 或不可交互状态优先屏蔽 pointer、keyboard、motion 和提交类反馈。
- motion 状态由控件实例或明确的数据 owner 推导，不能在 template part 之间双向竞争。
- 模板重套用时必须把 public API 对应状态回放到新的 part、伪类和主题变量。
- 集合、弹层、异步、动效或窗口相关状态必须能处理 reset、close、cancel、detach 和 owner 释放。

### 4.1 打开状态调和模型

`IsOpen` 附加属性表达期望打开状态，不是赋值瞬间执行的命令。期望打开的完整条件是：`IsOpen` 为 `true`、`Tip` 内容就绪、宿主控件已挂入 visual tree。物理弹层的开关由唯一调和流程在这些输入变化时统一兑现，赋值时序不影响最终结果：

- 满足期望条件且弹层未打开：先引发可取消的 `ToolTipOpening`；被取消时把 `IsOpen` 回写为 `false`，否则打开弹层。
- `IsOpen` 转为 `false` 且弹层已打开：关闭弹层并引发 `ToolTipClosing`。
- 宿主控件从 visual tree 卸载：物理关闭弹层但保留 `IsOpen`，重新挂入后由调和流程自动重开。
- `Tip` 未就绪不重置 `IsOpen`；内容就绪后调和流程自动完成打开。
- 弹层被外部原因关闭时，`IsOpen` 回写为 `false`，期望状态与实际状态保持一致。

悬停打开由 `ToolTipService` 驱动同一个 `IsOpen` 属性；编程式声明与悬停交互共享同一状态 owner 和调和出口，不存在第二套开关路径。

## 主题与 Design Token

Tooltip 的视觉模型由控件模板、ControlTheme、SharedToken 和必要的控件 Token 共同构成。

| 主题文件 | 职责 |
| --- | --- |
| `ToolTipTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |

Tooltip 使用 `ToolTipToken` 作为控件 Token scope。Token 只表达组件视觉语义，不承载 motion 运行时状态。

主题维护规则：

- 不删除或重命名已经稳定的 ControlTheme key、template part、伪类和资源 key。
- 不把可由 AXAML 表达的模板状态迁移为 C# 动态创建视觉。
- 不把 hover、pressed、selected、expanded、loading、filter、popup open 等运行时状态写入 Token。
- Browser 或平台特化主题必须保持同一 API 的语义一致。

Token 来源：

Tooltip Token 只表达组件级视觉变量，例如尺寸、间距、颜色、圆角、阴影、图标尺寸和弹层边界。Token 不承载运行时选择、展开、加载、错误、上传任务、过滤条件或业务状态。

当前 Token scope：

- `ToolTipToken`，scope id 为 `ToolTip`，源码位于 `src/AtomUI.Desktop.Controls/Tooltip/ToolTipToken.cs`。

## AOT 与裁剪注意事项

资源和 AOT 约束：

- 不通过运行时反射扫描 public API、Token 或 Gallery 示例数据。
- 不通过 VisualTree 遍历或反射发现其他控件的 template part；输入文本可视宽度只消费输入控件发布的内部 metric。
- 不把可静态声明的模板结构迁移到 C# 动态创建。
- 异步加载、上传、弹层和窗口生命周期必须能取消或释放。
- 缓存对象必须与控件、窗口、弹层或数据 owner 生命周期一致。
- Source generator 生成文件不手工编辑；需要修改时改输入源或 generator。

性能边界：

- 控件应优先复用 Avalonia 原生虚拟化、模板绑定和资源系统。
- 避免为每次状态变化创建不必要的视觉对象、订阅或动画对象。
- 大集合控件必须保证 container recycle 后不会泄漏旧 item 状态。

## 源码索引

主要源码文件：

- `src/AtomUI.Desktop.Controls/Tooltip/Themes/ToolTipTheme.axaml`
- `src/AtomUI.Desktop.Controls/Tooltip/ToolTip.cs`
- `src/AtomUI.Desktop.Controls/Tooltip/ToolTipPseudoClass.cs`
- `src/AtomUI.Desktop.Controls/Tooltip/ToolTipToken.cs`
- `src/AtomUI.Desktop.Controls/Tooltip/OverflowTip.cs`
- `src/AtomUI.Desktop.Controls/PackageCore/ToolTipService.cs`

职责边界：

- 控件主文件保留 public/protected API、Avalonia 属性注册、事件和主要生命周期入口。
- Theme 文件负责静态视觉结构、template part、selector 和资源绑定。
- Token 文件只提供组件视觉变量，不保存实例状态。
- Gallery 文件只展示用法和示例，不作为运行时逻辑 owner。

## 相关文档

- 源设计文档：`docs/controls/desktop/data-display/tooltip/overview.md`
- 实现文档：`docs/controls/desktop/data-display/tooltip/implementation.md`
- Token 文档：`docs/controls/desktop/data-display/tooltip/token.md`
- 变更记录：`docs/controls/desktop/data-display/tooltip/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/tour/index-cn.md

# Tour

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

Tour 是 AtomUI 桌面控件体系中的漫游引导控件，用于按步骤高亮界面目标并展示说明。

Tour 不负责路由系统、教学内容管理或通用 Dialog。这些职责应由业务层、组合控件或更专用的 AtomUI 控件承担。

主要源码入口：

- `src/AtomUI.Desktop.Controls/Tour`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/DataDisplay/Tour` |
| 状态 | Stable |

## 何时使用

Tour 的设计语言围绕控件职责、可观察状态和主题契约组织，而不是围绕模板节点组织。

| 维度 | 含义 | Tour 中的表达 |
| --- | --- | --- |
| 产品语义 | 控件在界面中承担的稳定职责。 | Tour 是 AtomUI 桌面控件体系中的漫游引导控件，用于按步骤高亮界面目标并展示说明。 |
| 内容承载 | 用户数据、展示内容、集合项或操作入口如何进入控件。 | `CloseIcon`、`CoverTemplate`、`Description`、`DescriptionTemplate`、`ItemSpacing`、`ItemTemplate`、`Title`、`TitleTemplate`。 |
| 状态反馈 | public API、内部状态和伪类如何形成用户可感知反馈。 | selection/checked/active、open/close、motion、visual option。 |
| 主题语义 | ControlTheme、SharedToken、控件 Token 和模板绑定如何表达视觉。 | Tour Token + ControlTheme。 |

## 公共 API

Tour 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。

核心 public surface 按语义分组维护：

| 契约组 | 代表成员 | 维护含义 |
| --- | --- | --- |
| 内容与数据 | `CloseIcon`、`CoverTemplate`、`Description`、`DescriptionTemplate`、`ItemSpacing`、`ItemTemplate`、`Title`、`TitleTemplate` | 定义控件展示内容、输入数据、模板或业务对象入口。 |
| 选择与集合 | `ActiveIndex`、`CurrentIndex`、`IndicatorActiveColor`、`StepCount` | 维护选择、展开、过滤、分页、分组或集合状态；`CurrentIndex` 默认双向绑定。 |
| 交互与状态 | `IsArrowVisible`、`IsDisabledInteraction`、`IsMotionEnabled`、`IsOpen`、`IsPointAtCenter`、`IsScrollIntoView`、`IsShowMask` | 表达用户可观察状态、可用性、清除、加载或反馈语义；`IsOpen` 默认双向绑定。 |
| 视觉与布局 | `Background`、`GapOffsetX`、`GapOffsetY`、`GapRadius`、`IndicatorColor`、`IndicatorSize`、`MaskColor`、`Placement`、`StyleType`、`TargetRegionCornerRadius` | 影响尺寸、位置、颜色、形状、密度和模板视觉变量。 |
| 其他稳定入口 | `Cover`、`Indicator`、`Target`、`TargetRegion` | 保留为 public surface，变更前需确认 Gallery 和用户 XAML 依赖。 |

当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。

`IsOpen` 与 `CurrentIndex` 是用户可拥有的受控状态，Avalonia Binding 默认使用 `TwoWay`；开始引导、关闭引导和步骤切换都必须回写同一 public 属性路径。它们不是 Form value，不写入 `DataValidationErrors`。

主要公开类型与枚举：

- 类型：`DefaultTourIndicator`、`TextTourIndicator`、`Tour`、`TourIndicator`、`TourLayer`、`TourStep`、`TourStepNavRequestEventArgs`、`TourStepOption`、`TourStepsView`。
- 枚举：`TourPlacementMode`、`TourStyleType`。
- 本地化 Catalog：`TourLangResourceKind`。

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_ArrowDecorator` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_Popup` | `?` | 承载弹层宿主、打开关闭或候选内容。 |

当前未抽取到控件专属伪类；主题主要依赖 Avalonia 标准伪类、模板绑定和内部 StyledProperty。

## 事件与命令

Tour 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。
当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。
- 类型：`DefaultTourIndicator`、`TextTourIndicator`、`Tour`、`TourIndicator`、`TourLayer`、`TourStep`、`TourStepNavRequestEventArgs`、`TourStepOption`、`TourStepsView`。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 位置

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/Tour/Views/TourShowCase.axaml:110`

Gallery key：`ExamplesContent` / item `2`

```axaml
<StackPanel Orientation="Vertical" Spacing="10" Loaded="HandleTourExampleLoaded">
    <atom:Button ButtonType="Primary" Click="HandlePlacementBeginTour" Name="PlacementBeginTour" Content="开始引导" />
    <atom:Tour IsOpen="{Binding PlacementTourOpened, Mode=TwoWay}" IsShowMask="True">
        <atom:TourStep Title="居中"
                       Description="显示在屏幕中央。" />
        <atom:TourStep Name="PlacementRightStep"
                       Title="右侧"
                       Description="位于目标右侧。"
                       Placement="Right" />
        <atom:TourStep Name="PlacementTopStep"
                       Title="顶部"
                       Description="位于目标上方。"
                       Placement="Top" />
        <atom:TourStep Name="PlacementLeftStep"
                       Title="左侧"
                       Description="位于目标左侧。"
                       Placement="Left" />
    </atom:Tour>
</StackPanel>
```

## 状态模型

Tour 的状态流按以下路径收敛：

```text
Public API / inherited command / item source / user input
  -> 控件实例状态
  -> effective state / pseudo-class / template property
  -> ControlTheme selector / presenter / renderer
  -> Gallery 可观察行为
```

状态维护规则：

- Disabled 或不可交互状态优先屏蔽 pointer、keyboard、motion 和提交类反馈。
- selection/checked/active、open/close、motion、visual option 状态由控件实例或明确的数据 owner 推导，不能在 template part 之间双向竞争。
- `IsOpen` 与 `CurrentIndex` 是默认 `TwoWay` 的受控状态；popup、indicator 和步骤视图只能消费或回写 public 状态，不能形成局部当前步骤。
- 模板重套用时必须把 public API 对应状态回放到新的 part、伪类和主题变量。
- 集合、弹层、异步、动效或窗口相关状态必须能处理 reset、close、cancel、detach 和 owner 释放。

## 主题与 Design Token

Tour 的视觉模型由控件模板、ControlTheme、SharedToken 和必要的控件 Token 共同构成。

| 主题文件 | 职责 |
| --- | --- |
| `DefaultTourIndicatorTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `TextTourIndicatorTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `TourStepTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `TourStepsViewTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `TourTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |

Tour 使用 `TourToken` 作为控件 Token scope。Token 只表达组件视觉语义，不承载 selection/checked/active、open/close、motion、visual option 运行时状态。

主题维护规则：

- 不删除或重命名已经稳定的 ControlTheme key、template part、伪类和资源 key。
- 不把可由 AXAML 表达的模板状态迁移为 C# 动态创建视觉。
- 不把 hover、pressed、selected、expanded、loading、filter、popup open 等运行时状态写入 Token。
- Browser 或平台特化主题必须保持同一 API 的语义一致。

Token 来源：

Tour Token 只表达组件级视觉变量，例如尺寸、间距、颜色、圆角、阴影、图标尺寸和弹层边界。Token 不承载运行时选择、展开、加载、错误、上传任务、过滤条件或业务状态。

当前 Token scope：

- `TourToken`，scope id 为 `Tour`，源码位于 `src/AtomUI.Desktop.Controls/Tour/TourToken.cs`。

## AOT 与裁剪注意事项

资源和 AOT 约束：

- 不通过运行时反射扫描 public API、Token 或 Gallery 示例数据。
- 不把可静态声明的模板结构迁移到 C# 动态创建。
- 异步加载、上传、弹层和窗口生命周期必须能取消或释放。
- 缓存对象必须与控件、窗口、弹层或数据 owner 生命周期一致。
- Source generator 生成文件不手工编辑；需要修改时改输入源或 generator。

性能边界：

- 控件应优先复用 Avalonia 原生虚拟化、模板绑定和资源系统。
- 避免为每次状态变化创建不必要的视觉对象、订阅或动画对象。
- 大集合控件必须保证 container recycle 后不会泄漏旧 item 状态。

## 源码索引

主要源码文件：

- `src/AtomUI.Desktop.Controls/Tour/DefaultTourIndicator.cs`
- `src/AtomUI.Desktop.Controls/Tour/ITourAction.cs`
- `src/AtomUI.Desktop.Controls/Tour/Localization/TourLangResourceKind.cs`
- `src/AtomUI.Desktop.Controls/Tour/Localization/en-US.xlf`
- `src/AtomUI.Desktop.Controls/Tour/Localization/zh-CN.xlf`
- `src/AtomUI.Desktop.Controls/Tour/Localization/zh-TW.xlf`
- `src/AtomUI.Desktop.Controls/Tour/TextTourIndicator.cs`
- `src/AtomUI.Desktop.Controls/Tour/Themes/DefaultTourIndicatorTheme.axaml`
- `src/AtomUI.Desktop.Controls/Tour/Themes/TextTourIndicatorTheme.axaml`
- `src/AtomUI.Desktop.Controls/Tour/Themes/TourStepTheme.axaml`
- `src/AtomUI.Desktop.Controls/Tour/Themes/TourStepsViewTheme.axaml`
- `src/AtomUI.Desktop.Controls/Tour/Themes/TourTheme.axaml`
- `src/AtomUI.Desktop.Controls/Tour/Tour.cs`
- `src/AtomUI.Desktop.Controls/Tour/TourIndicator.cs`
- `src/AtomUI.Desktop.Controls/Tour/TourLayer.cs`
- `src/AtomUI.Desktop.Controls/Tour/TourPlacementMode.cs`
- `src/AtomUI.Desktop.Controls/Tour/TourStep.cs`
- `src/AtomUI.Desktop.Controls/Tour/TourStepNavRequestEventArgs.cs`
- `src/AtomUI.Desktop.Controls/Tour/TourStepOption.cs`
- `src/AtomUI.Desktop.Controls/Tour/TourStepsView.cs`
- `src/AtomUI.Desktop.Controls/Tour/TourToken.cs`

职责边界：

- 控件主文件保留 public/protected API、Avalonia 属性注册、事件和主要生命周期入口。
- Theme 文件负责静态视觉结构、template part、selector 和资源绑定。
- Token 文件只提供组件视觉变量，不保存实例状态。
- Gallery 文件只展示用法和示例，不作为运行时逻辑 owner。

## 相关文档

- 源设计文档：`docs/controls/desktop/data-display/tour/overview.md`
- 实现文档：`docs/controls/desktop/data-display/tour/implementation.md`
- Token 文档：`docs/controls/desktop/data-display/tour/token.md`
- 变更记录：`docs/controls/desktop/data-display/tour/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/tree-view/index-cn.md

# TreeView

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

TreeView 是桌面端数据展示类树形结构控件，用于展示具有父子层级的数据、文件目录、组织结构、权限结构、分类结构和可展开节点集合。它以 Avalonia `TreeView` 为基础，扩展 AtomUI 的节点数据模型、图标、连线、选择、勾选、过滤、高亮、异步加载、拖拽、空状态、动效、Form 集成和 Token 体系。

TreeView 的职责是管理树节点容器生成、层级展开、选择、勾选、过滤展示、节点异步加载和节点拖拽重排。它不负责业务路由、文件系统访问、权限计算、搜索数据源请求、远程分页、虚拟化列表、节点详情面板或业务命令编排。

TreeView 支持两种节点提供方式：

- 直接放置 `TreeViewItem`。
- 通过 `ItemsSource` 绑定实现 `ITreeItemNode` 的节点集合。

直接子元素适合静态树或少量手写节点；`ItemsSource` 适合数据驱动树、默认路径回放、异步加载和 Form / Select 体系协同。

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/DataDisplay/TreeView` |
| 状态 | Stable |

## 何时使用

TreeView 表达的是“层级结构 + 当前节点状态”的信息浏览语义。用户应能通过缩进、展开图标、节点图标、连线、hover 背景、选中背景、勾选状态和过滤高亮理解节点之间的层级关系和当前操作目标。

设计语言由以下维度组成：

| 维度 | 含义 | 典型表达 |
| --- | --- | --- |
| 层级结构 | 节点在树中的深度和父子关系。 | 缩进、switcher、连线。 |
| 展开状态 | 节点子级是否可见。 | expand / collapse 图标、展开动效。 |
| 选择状态 | 当前业务选择目标。 | selected 背景、`SelectedItem` / `SelectedItems`。 |
| 勾选状态 | 批量选择或节点状态标记。 | checkbox、radio、半选。 |
| 过滤状态 | 搜索或过滤命中的节点。 | 高亮文本、隐藏未命中节点、展开命中路径。 |
| 加载状态 | 子节点异步加载中。 | loading switcher icon。 |
| 拖拽状态 | 节点重排的当前目标。 | drag preview、drop indicator、drag-over 状态。 |

TreeView 的视觉强度应保持克制。节点 header 是可扫描的信息行，不应被绘制成卡片；连线和 switcher 是层级辅助，不应覆盖选中、禁用、过滤和拖拽状态的可读性。

## 公共 API

TreeView 的公共契约由 TreeView API、TreeViewItem API、节点数据 API、事件 API、template part 和伪类组成。

TreeView 核心 API：

| API | 语义 |
| --- | --- |
| `IsAutoExpandParent` | 子节点展开时是否自动展开父节点。 |
| `IsDraggable` | 是否启用节点拖拽重排。 |
| `IsShowIcon` | 是否显示节点图标。 |
| `IsShowLine` | 是否显示树形连线。 |
| `IsDefaultExpandAll` | 加载后是否默认展开全部节点。 |
| `NodeHoverMode` | 节点 hover 背景范围，支持 `Default`、`Block`、`WholeLine`。 |
| `SwitcherExpandIcon` / `SwitcherCollapseIcon` / `SwitcherRotationIcon` / `SwitcherLoadingIcon` / `SwitcherLeafIcon` | switcher 图标入口。 |
| `IsShowLeafIcon` | 是否显示叶子节点图标。 |
| `IsSwitcherRotation` | 是否使用旋转图标表达展开收起。 |
| `IsSelectable` | 是否允许节点选择。 |
| `IsSelectOnRightClick` | 右键节点时是否更新选择。 |
| `ToggleType` | 节点勾选模式，支持 none、checkbox、radio。 |
| `IsCheckStrictly` | checkbox 模式下是否关闭父子级级联。 |
| `DefaultSelectedPaths` / `DefaultCheckedPaths` / `DefaultExpandedPaths` | 初始选择、勾选和展开路径入口。 |
| `DataLoader` / `AsyncLoadTimeout` | 异步加载子节点入口和超时时间。 |
| `Filter` / `FilterValue` / `FilterValueSelector` / `FilterStrategy` | 过滤、高亮、展开路径和隐藏未命中节点入口。 |
| `FilterResultCount` | 当前过滤命中数量。 |
| `EmptyIndicator` / `EmptyIndicatorTemplate` / `IsShowEmptyIndicator` / `EmptyIndicatorPadding` | 空状态展示入口。 |
| `IsMotionEnabled` / `OpenMotion` / `CloseMotion` | 展开收起动效入口。 |

TreeViewItem 节点 API：

| API | 语义 |
| --- | --- |
| `Icon` | 节点图标。 |
| `IsChecked` | checkbox / radio 状态，支持半选。 |
| `IsLeaf` | 是否为叶子节点。 |
| `IsLoading` | 是否处于异步加载状态。 |
| `GroupName` | radio 分组名。 |
| `Value` | 节点值。 |
| `IsIndicatorEnabled` | 当前节点 checkbox / radio 是否可用。 |
| `ItemKey` | 路径匹配和节点身份标识。 |

`ITreeItemNode` 是数据驱动树的节点契约，提供 `Header`、`Icon`、`ItemKey`、`Children`、`IsEnabled`、`IsChecked`、`IsSelected`、`IsExpanded`、`IsIndicatorEnabled`、`GroupName`、`IsLeaf`、`Value` 和 parent node 更新能力。数据驱动场景下 `Children` 是节点结构的权威子集合；TreeViewItem 只投射节点状态和视觉交互，不作为结构数据源。

节点数据类型边界：

| 类型 | 定位 | 绑定能力 | 兼容边界 |
| --- | --- | --- | --- |
| `TreeItemNode` | 轻量数据节点，适合 C# 构造、异步加载结果和普通 ItemsSource。 | 作为 binding source 使用，不承载 Avalonia binding target 语义。 | 保持 POCO / record 形态，不改造成 `AvaloniaObject`。 |
| 用户自定义 `ITreeItemNode` | 业务自己的树节点模型。 | 可自行实现 `INotifyPropertyChanged` 或其他业务通知机制。 | TreeView 只依赖 `ITreeItemNode` 契约，不要求继承 AtomUI 节点基类。 |
| `BindableTreeItemNode` | 绑定型节点，适合在 XAML 中把节点属性作为 Avalonia binding target 或 `DynamicResource` target。 | 使用 Avalonia 属性系统，支持 `Header`、`Icon`、`IsChecked`、`IsSelected`、`IsExpanded` 等节点属性绑定。 | 独立于 `TreeItemNode`，不替代轻量节点；作为 owner-managed 非 Visual `AvaloniaObject` 必须遵守 scoped resource host 生命周期。 |

事件 API：

| 事件 | 语义 |
| --- | --- |
| `CheckedItemsChanged` | `CheckedItems` 增删变化。 |
| `TreeItemLoaded` | 异步节点加载完成。 |
| `ItemDragStarted` / `ItemDragCompleted` | 节点拖拽开始和结束。 |
| `ItemDragEnter` / `ItemDragLeave` / `ItemDragOver` | 拖拽目标变化。 |
| `ItemDropped` | 节点完成 drop 操作。 |
| `ItemExpanded` / `ItemCollapsed` | 节点展开和收起。 |
| `ItemClicked` | 节点 header 点击。 |
| `ItemContextMenuRequest` | 节点右键上下文菜单请求。 |

稳定 template part：

| Template Part | 所属控件 | 职责 |
| --- | --- | --- |
| `ItemsPresenter` | `TreeView` | 承载顶层节点容器。 |
| `EmptyIndicator` | `TreeView` | 空状态内容展示。 |
| `Header` | `TreeViewItem` | 节点 header 控件。 |
| `PART_ItemsPresenterMotionActor` | `TreeViewItem` | 子节点展开收起动效容器。 |
| `PART_NodeSwitcherButton` | `TreeViewItemHeader` | 展开、收起、加载和叶子图标入口。 |
| `PART_IconPresenter` | `TreeViewItemHeader` | 节点图标展示。 |
| `PART_HeaderContentFrame` | `TreeViewItemHeader` | header 内容背景、hover、selected、pressed 和 pointer 事件边界。 |

伪类契约：

- `TreeView :draggable` 表示启用拖拽。
- `TreeViewItem :toggle` 表示 checkbox 模式。
- `TreeViewItem :radio` 表示 radio 模式。
- `TreeViewItemHeader :toggle` / `:radio` 与节点 toggle 模式同步。

## 事件与命令

TreeView 的公共契约由 TreeView API、TreeViewItem API、节点数据 API、事件 API、template part 和伪类组成。
事件 API：
| 事件 | 语义 |
| `PART_HeaderContentFrame` | `TreeViewItemHeader` | header 内容背景、hover、selected、pressed 和 pointer 事件边界。 |

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/TreeView/Views/TreeViewShowCase.axaml:37`

Gallery key：`ExamplesContent` / item `0`

```axaml
<atom:TreeView ToggleType="CheckBox"
               DefaultExpandedPaths="{Binding BasicTreeViewDefaultExpandedPaths}"
               DefaultSelectedPaths="{Binding BasicTreeViewDefaultSelectedPaths}"
               DefaultCheckedPaths="{Binding BasicTreeViewDefaultCheckedPaths}">
    <atom:TreeViewItem Header="父节点 1" ItemKey="0-0">
        <atom:TreeViewItem Header="父节点 1-0" ItemKey="0-0-0">
            <atom:TreeViewItem Header="叶子节点" ItemKey="0-0-0-0" IsEnabled="False" />
            <atom:TreeViewItem Header="叶子节点" ItemKey="0-0-0-1" />
        </atom:TreeViewItem>
        <atom:TreeViewItem Header="父节点 1-1" ItemKey="0-0-1">
            <atom:TreeViewItem Header="示例一" ItemKey="0-0-1-0">
                <atom:TreeViewItem Header="示例二" ItemKey="0-0-1-0-0" />
            </atom:TreeViewItem>
            <atom:TreeViewItem Header="示例三" ItemKey="0-0-1-1">
                <atom:TreeViewItem Header="示例四" ItemKey="0-0-1-1-0" />
            </atom:TreeViewItem>
        </atom:TreeViewItem>
    </atom:TreeViewItem>
</atom:TreeView>
```

### 使用模板生成

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/TreeView/Views/TreeViewShowCase.axaml:66`

Gallery key：`ExamplesContent` / item `1`

```axaml
<atom:TreeView ToggleType="CheckBox"
               ItemsSource="{Binding BasicTreeNodes}"
               DefaultExpandedPaths="{Binding BasicTreeViewDefaultExpandedPaths}"
               DefaultSelectedPaths="{Binding BasicTreeViewDefaultSelectedPaths}"
               DefaultCheckedPaths="{Binding BasicTreeViewDefaultCheckedPaths}">
    <atom:TreeView.ItemTemplate>
        <TreeDataTemplate ItemsSource="{Binding Children}" DataType="atom:ITreeItemNode">
            <atom:TextBlock Text="{Binding Header}" />
        </TreeDataTemplate>
    </atom:TreeView.ItemTemplate>
</atom:TreeView>
```

### SelectedItem 绑定

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/TreeView/Views/TreeViewShowCase.axaml:89`

Gallery key：`ExamplesContent` / item `2`

```axaml
<Grid ColumnDefinitions="300,*" ColumnSpacing="24">
    <StackPanel Grid.Column="0" Spacing="8">
        <TextBlock FontWeight="SemiBold"
                   Text="SelectedItem" />
        <atom:TreeView ItemsSource="{Binding BasicTreeNodes}"
                       DefaultExpandedPaths="{Binding BasicTreeViewDefaultExpandedPaths}"
                       SelectedItem="{Binding BoundSelectedTreeNode, Mode=TwoWay}">
            <atom:TreeView.ItemTemplate>
                <TreeDataTemplate ItemsSource="{Binding Children}" DataType="atom:ITreeItemNode">
                    <atom:TextBlock Text="{Binding Header}" />
                </TreeDataTemplate>
            </atom:TreeView.ItemTemplate>
        </atom:TreeView>
    </StackPanel>

    <StackPanel Grid.Column="1" MinWidth="260" Spacing="10">
        <TextBlock FontWeight="SemiBold"
                   Text="绑定值" />
        <StackPanel Spacing="4">
            <TextBlock Text="SelectedItem" />
            <atom:TextBlock Text="{Binding BoundSelectedTreeNodeText}" />
        </StackPanel>
        <WrapPanel>
            <atom:Button Margin="0,0,8,8"
                         SizeType="Small"
                         Click="HandleSelectFirstBindingTreeNodeClick"
                         Content="选择 parent 1-0" />
            <atom:Button Margin="0,0,8,8"
                         SizeType="Small"
                         Click="HandleSelectSecondBindingTreeNodeClick"
                         Content="选择 parent 1-1" />
            <atom:Button Margin="0,0,8,8"
                         SizeType="Small"
                         Click="HandleClearBindingTreeNodeSelectionClick"
                         Content="清空" />
        </WrapPanel>
    </StackPanel>
</Grid>
```

### SelectedItems 绑定

来源：`controlgallery/AtomUIGallery/ShowCases/DataDisplay/TreeView/Views/TreeViewShowCase.axaml:139`

Gallery key：`ExamplesContent` / item `3`

```axaml
<Grid ColumnDefinitions="300,*" ColumnSpacing="24">
    <StackPanel Grid.Column="0" Spacing="8">
        <TextBlock FontWeight="SemiBold"
                   Text="SelectedItems" />
        <atom:TreeView ItemsSource="{Binding BasicTreeNodes}"
                       DefaultExpandedPaths="{Binding BasicTreeViewDefaultExpandedPaths}"
                       SelectionMode="Multiple"
                       SelectedItems="{Binding BoundSelectedTreeNodes, Mode=TwoWay}">
            <atom:TreeView.ItemTemplate>
                <TreeDataTemplate ItemsSource="{Binding Children}" DataType="atom:ITreeItemNode">
                    <atom:TextBlock Text="{Binding Header}" />
                </TreeDataTemplate>
            </atom:TreeView.ItemTemplate>
        </atom:TreeView>
    </StackPanel>

    <StackPanel Grid.Column="1" MinWidth="260" Spacing="10">
        <TextBlock FontWeight="SemiBold"
                   Text="绑定值" />
        <StackPanel Spacing="4">
            <TextBlock Text="SelectedItems" />
            <atom:TextBlock Text="{Binding BoundSelectedTreeNodesText}" TextWrapping="Wrap" />
        </StackPanel>
        <WrapPanel>
            <atom:Button Margin="0,0,8,8"
                         SizeType="Small"
                         Click="HandleSelectFirstBindingTreeNodesClick"
                         Content="选择 parent 1-0" />
            <atom:Button Margin="0,0,8,8"
                         SizeType="Small"
                         Click="HandleSelectSecondBindingTreeNodesClick"
                         Content="选择 parent 1-1" />
            <atom:Button Margin="0,0,8,8"
                         SizeType="Small"
                         Click="HandleSelectBothBindingTreeNodesClick"
                         Content="选择两个节点" />
            <atom:Button Margin="0,0,8,8"
                         SizeType="Small"
                         Click="HandleClearBindingTreeNodesSelectionClick"
                         Content="清空" />
        </WrapPanel>
    </StackPanel>
</Grid>
```

## 状态模型

TreeView 的状态模型由节点状态、选择状态、勾选状态、展开状态、过滤状态、异步加载状态、拖拽状态和空状态组成。

选择行为：

- `IsSelectable=false` 时不允许节点被选中，并清空 TreeView 当前选择。
- `IsSelectOnRightClick=false` 时，右键不更新选择。
- `SelectionMode` 继承 Avalonia `TreeView` 语义，单选使用 `SelectedItem`，多选使用 `SelectedItems`。
- Form 集成以单选 / 多选模式分别读取和写入 `SelectedItem` 或 `SelectedItems`，并保持原始节点对象 / 列表实例，不把节点值转换为字符串。

勾选行为：

- `ToggleType=None` 时不显示勾选入口。
- `ToggleType=CheckBox` 时显示 checkbox。
- `ToggleType=Radio` 时只在叶子节点显示 radio。
- `IsCheckStrictly=false` 时 checkbox 勾选会级联子树，并根据子级状态更新父级 true / false / null。
- `IsCheckStrictly=true` 时 checkbox 只同步当前节点，不级联父子级。
- `CheckedItems` 是当前勾选数据集合，变化会同步已实现容器状态并触发 `CheckedItemsChanged`。

展开行为：

- switcher 触发展开收起。
- `IsDefaultExpandAll=true` 时加载后展开全部节点，并优先于 `DefaultExpandedPaths`。
- 默认路径通过 `TreeNodePath` 和 `ItemKey` / `Value` 匹配。
- 展开收起动效由 `IsMotionEnabled`、`OpenMotion`、`CloseMotion` 和 `MotionDuration` 控制。

过滤行为：

- `Filter`、`FilterValue` 和 `FilterValueSelector` 共同决定节点是否命中。
- `FilterStrategy` 控制高亮 match、整行高亮、加粗、展开命中路径和隐藏未命中节点。
- `FilterResultCount` 表示命中节点数量。
- 过滤模式下空状态依据 `FilterResultCount` 判断。

拖拽行为：

- `IsDraggable=true` 时，左键按下并超过拖拽阈值后进入拖拽。
- TreeView 创建拖拽预览和 drop indicator。
- drop 目标支持插入到根、插入到兄弟前后、插入到目标节点内部。
- 不允许把节点 drop 到自身或自身后代内。
- 拖拽命中以已实现的 `TreeViewItem` 容器计算，结构修改以数据源为权威。
- drop 操作通过 TreeView 内部数据控制器移动 root 集合或节点 `Children`，不直接修改生成容器的 `Items`。
- 节点移动是结构重排，不是业务删除；选中、勾选和展开状态按节点身份保留。

异步加载行为：

- `DataLoader` 只在 `ItemsSource` 数据驱动场景下使用。
- 未加载节点点击 switcher 时触发加载。
- 加载中节点显示 loading switcher icon。
- 加载成功后把返回子节点写入目标节点 `Children`，并展开目标节点。

绑定型节点行为：

- `TreeItemNode` 的定位是轻量数据源节点，不承载 `DynamicResource`、Avalonia styled binding target 或资源宿主职责。
- 需要在 XAML 中直接绑定节点属性，或把节点 `Header`、`Icon`、状态属性设置为 `DynamicResource` 时，使用独立的 `BindableTreeItemNode`。
- `BindableTreeItemNode` 进入 TreeView 容器生命周期时，由 owner TreeView / TreeViewItem attach scoped resource host；离开容器、detach、re-template 或 container recycle 时释放 attach token。
- 绑定型节点属性变化应同步当前生成的 `TreeViewItem` 容器；容器交互导致的 checked、selected、expanded 等状态变化也应按契约回写节点状态。
- 自定义 `ITreeItemNode` 仍按普通数据模型处理；TreeView 不要求用户模型继承 `BindableTreeItemNode`。

## 主题与 Design Token

TreeView 主题按 root、item、header、switcher 四层组织。

```text
TreeViewTheme
  root scroll viewer
  items presenter
  empty indicator

TreeViewItemTheme
  header
  child items motion actor

TreeViewItemHeaderTheme
  switcher
  checkbox / radio
  icon
  header content frame
  filter highlighter

NodeSwitcherButtonTheme
  current icon presenter
  hover background
  rotation / loading transition
```

视觉规则：

- `NodeHoverMode=Default` 时 header 内容背景按内容宽度绘制。
- `NodeHoverMode=Block` 时 header 内容背景横向拉伸到剩余区域。
- `NodeHoverMode=WholeLine` 时背景由 TreeViewItem 行级绘制，覆盖整行宽度。
- disabled 节点应使用 disabled 文本色和弱化图标，不应保留可交互 hover 视觉。
- filter match 时显示 `FilterHighlighter`，未命中时显示普通 `HeaderPresenter`。
- `IsShowLine=true` 时，TreeViewItem 自绘树形连线。
- drag indicator 由 TreeView 自绘，不进入节点模板内部。

TreeViewToken 提供节点高度、hover / selected 背景、目录树选中颜色、节点间距、header padding、switcher / icon 间距、拖拽指示线宽和过滤高亮色。Token 详情见 [TreeView Token 设计](token.md)。

Token 来源：

TreeViewToken 是 TreeView 的组件级设计变量层。它把全局颜色、尺寸、间距、线宽和状态色转换为 TreeView 节点 header、switcher、icon、拖拽指示器和过滤高亮可消费的语义值。

TreeViewToken 服务以下主题和控件：

- `TreeViewTheme.axaml`
- `TreeViewItemTheme.axaml`
- `TreeViewItemHeaderTheme.axaml`
- `NodeSwitcherButtonTheme.axaml`
- `TreeView` drag indicator render state
- `TreeViewItem` line render state
- `TreeViewItemHeader` hover / selected / filter state

TreeViewToken 不承载 `SelectedItem`、`SelectedItems`、`CheckedItems`、`IsExpanded`、`IsChecked`、`IsFilterMode`、`FilterResultCount`、`IsDragging`、`DragIndicatorRenderInfo` 等实例状态。这些状态由 C# 状态模型、容器属性和主题 selector 处理。

## AOT 与裁剪注意事项

TreeView 不通过反射访问模板内部结构。模板接入依赖稳定 part 名称和 Avalonia 属性绑定。

`BindableTreeItemNode` 属于 owner-managed 非 Visual `AvaloniaObject`。它承载 `DynamicResource`、token-resource binding 或 XAML binding target 时，必须按 [Scoped Resource Host Source Generator 范式](../../../../modules/generator/scoped-resource-host-generator.md) 生成 scoped `IResourceHost` / `IThemeVariantHost` 生命周期样板代码。资源查找顺序必须是 owner TreeView / TreeViewItem 优先，再 fallback 到 `Application.Current`。

TreeView / TreeViewItem 对绑定型节点的 attach/release 是资源生命周期边界。不得把 generated `TreeViewItem`、header、container 或 Gallery ShowCase 永久挂回节点，也不得用清空 DataContext 或静态资源替代动态资源能力。

过滤、默认状态回放和勾选级联会触发 layout pass 以实现需要访问的子容器；这些路径必须保持有界，不能引入固定延时等待。

拖拽 preview 使用 `AdornerLayer`，完成或取消拖拽时必须从 adorner layer 移除。drag indicator Pen 和 tree line Pen 均按 brush / width 缓存，避免每帧重复创建。

拖拽性能边界：

- pointer move 期间只查询已实现容器和 header bounds，不访问整棵数据树。
- drop 阶段通过 `TreeNodeIndex` 读取节点上下文，避免按节点引用全树扫描。
- `TreeNodeIndex` 只在 root 数据源替换、节点集合增删移和异步加载结果进入时增量维护。
- 移动的主要成本限制在源 / 目标兄弟集合的 remove / insert；不能引入与整棵树节点数线性相关的 drop 热路径。
- 索引订阅必须随 root source 替换、节点移除、TreeView detach 和节点 collection 替换释放，避免保留旧节点树。

异步加载必须支持取消、超时和 detach 清理。加载结果回到 UI 线程后才能修改节点集合和容器状态。

Filter highlight runs 是 header 状态，不应写入 Token 或节点数据模型。Token 只提供默认颜色、尺寸和间距。

## 源码索引

主要源码：

- `src/AtomUI.Desktop.Controls/TreeView/TreeView.cs`：public API、事件、容器生成、生命周期、选择、空状态、Form 集成和公共方法。
- `src/AtomUI.Desktop.Controls/TreeView/TreeView.StateReplay.cs`：loaded 回放、ItemsSource 变化后的选择 / 勾选 / 展开状态恢复。
- `src/AtomUI.Desktop.Controls/TreeView/TreeView.PathTraversal.cs`：`TreeNodePath` 遍历、展开路径、容器确保和展开状态恢复。
- `src/AtomUI.Desktop.Controls/TreeView/TreeView.CheckedState.cs`：checkbox 严格 / 级联状态、`CheckedItems` 同步和半选父级计算。
- `src/AtomUI.Desktop.Controls/TreeView/TreeView.Filter.cs`：过滤、高亮、隐藏未命中、展开路径、filter context 备份和恢复。
- `src/AtomUI.Desktop.Controls/TreeView/TreeView.AsyncItemDataLoad.cs`：异步加载、加载合并、超时、取消和 `TreeItemLoaded` 派发。
- `src/AtomUI.Desktop.Controls/TreeView/TreeView.DragAndDrop.cs`：拖拽命中测试、drag preview、drop indicator、drop 操作和拖拽事件。
- `src/AtomUI.Desktop.Controls/TreeView/TreeView.DataController.cs`：内部数据控制器、drop request / result、root / children 集合移动和 parent node 同步。
- `src/AtomUI.Desktop.Controls/TreeView/TreeView.NodeIndex.cs`：节点索引、节点父级、所在集合、兄弟索引和 collection changed 增量维护。
- `src/AtomUI.Desktop.Controls/TreeView/TreeViewItem.cs`：节点容器、展开收起、line 渲染、check/radio 状态、drag bounds 和数据节点承载。
- `src/AtomUI.Desktop.Controls/TreeView/TreeViewItemHeader.cs`：header 视觉状态、pointer 状态、switcher mode、filter highlight runs 和 template part 订阅。
- `src/AtomUI.Desktop.Controls/TreeView/NodeSwitcherButton.cs`：switcher 当前图标选择、默认图标、loading load request 和 rotation 动效。
- `src/AtomUI.Desktop.Controls/TreeView/TreeItemNode.cs`、`ITreeItemNode.cs`：轻量数据节点模型、节点契约和父子关系维护。绑定型节点模型属于同一节点数据职责边界，必须保持独立类型，不改造 `TreeItemNode`。
- `src/AtomUI.Desktop.Controls/TreeView/DefaultTreeViewInteractionHandler.cs`：pointer、context menu、radio group、checked changed 和 floating tree light dismiss。
- `src/AtomUI.Desktop.Controls/TreeView/TreeViewToken.cs`：TreeView Token 定义。
- `src/AtomUI.Desktop.Controls/TreeView/Themes/`：TreeView、TreeViewItem、TreeViewItemHeader、NodeSwitcherButton 主题和模板常量。

## 相关文档

- 源设计文档：`docs/controls/desktop/data-display/tree-view/overview.md`
- 实现文档：`docs/controls/desktop/data-display/tree-view/implementation.md`
- Token 文档：`docs/controls/desktop/data-display/tree-view/token.md`
- 变更记录：`docs/controls/desktop/data-display/tree-view/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/alert/index-cn.md

# Alert

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

Alert 是 AtomUI 桌面控件体系中的警告提示控件，用于展示页面内的成功、信息、警告和错误反馈。

Alert 不负责全局消息系统、模态确认或通知中心。这些职责应由业务层、组合控件或更专用的 AtomUI 控件承担。

主要源码入口：

- `src/AtomUI.Desktop.Controls/Alert`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/Feedback/Alert` |
| 状态 | Stable |

## 何时使用

Alert 的设计语言围绕控件职责、可观察状态和主题契约组织，而不是围绕模板节点组织。

| 维度 | 含义 | Alert 中的表达 |
| --- | --- | --- |
| 产品语义 | 控件在界面中承担的稳定职责。 | Alert 是 AtomUI 桌面控件体系中的警告提示控件，用于展示页面内的成功、信息、警告和错误反馈。 |
| 内容承载 | 用户数据、展示内容、集合项或操作入口如何进入控件。 | `CloseIcon`、`Description`、`IsShowIcon`。 |
| 状态反馈 | public API、内部状态和伪类如何形成用户可感知反馈。 | 基础交互和主题状态。 |
| 主题语义 | ControlTheme、SharedToken、控件 Token 和模板绑定如何表达视觉。 | Alert Token + ControlTheme。 |

## 公共 API

Alert 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。

核心 public surface 按语义分组维护：

| 契约组 | 代表成员 | 维护含义 |
| --- | --- | --- |
| 内容与数据 | `CloseIcon`、`Description`、`IsShowIcon` | 定义控件展示内容、输入数据、模板或业务对象入口。 |
| 交互与状态 | `IsClosable`、`IsMessageMarqueeEnabled` | 表达用户可观察状态、可用性、清除、加载或反馈语义。 |
| 其他稳定入口 | `ExtraAction`、`Message`、`Type` | 保留为 public surface，变更前需确认 Gallery 和用户 XAML 依赖。 |

当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。

主要公开类型与枚举：

- 类型：`Alert`。
- 枚举：`AlertType`。

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_CloseBtn` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |

控件专属或内部伪类包括 `AlertPseudoClass.HasDescription`、`AlertPseudoClass.HasExtraAction`、`HasDescription=:has-description`、`HasExtraAction=:has-extra-action`。这些伪类属于主题 selector 可观察契约，不能在未同步主题和 Gallery 的情况下重命名或删除。

## 事件与命令

Alert 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。
当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/Feedback/Alert/Views/AlertShowCase.axaml:36`

Gallery key：`ExamplesContent` / item `0`

```axaml
<StackPanel Orientation="Vertical">
    <atom:Alert Type="Success" Message="成功文本" />
</StackPanel>
```

### 更多类型

来源：`controlgallery/AtomUIGallery/ShowCases/Feedback/Alert/Views/AlertShowCase.axaml:49`

Gallery key：`ExamplesContent` / item `1`

```axaml
<StackPanel Orientation="Vertical" Spacing="10">
    <atom:Alert Type="Success" Message="成功文本" />
    <atom:Alert Type="Info" Message="信息文本" />
    <atom:Alert Type="Warning" Message="警告文本" />
    <atom:Alert Type="Error" Message="错误文本" />
</StackPanel>
```

### 含描述信息

来源：`controlgallery/AtomUIGallery/ShowCases/Feedback/Alert/Views/AlertShowCase.axaml:83`

Gallery key：`ExamplesContent` / item `3`

```axaml
<StackPanel Orientation="Vertical" Spacing="10">
    <atom:Alert Type="Success"
                Message="成功提示文本"
                Description="成功描述 成功描述 成功描述" />
    <atom:Alert Type="Info"
                Message="信息提示文本"
                Description="信息描述 信息描述 信息描述 信息描述" />
    <atom:Alert Type="Warning"
                Message="警告提示文本"
                Description="警告描述 警告描述 警告描述 警告描述" />
    <atom:Alert Type="Error"
                Message="错误提示文本"
                Description="错误描述 错误描述 错误描述 错误描述" />
</StackPanel>
```

### 图标

来源：`controlgallery/AtomUIGallery/ShowCases/Feedback/Alert/Views/AlertShowCase.axaml:108`

Gallery key：`ExamplesContent` / item `4`

```axaml
<StackPanel Orientation="Vertical" Spacing="10">
    <atom:Alert Type="Success"
                Message="成功提示"
                IsShowIcon="True" />
    <atom:Alert Type="Info"
                Message="信息说明"
                IsShowIcon="True" />
    <atom:Alert Type="Warning"
                Message="警告"
                IsShowIcon="True"
                IsClosable="True" />
    <atom:Alert Type="Error"
                Message="错误"
                IsShowIcon="True" />

    <atom:Alert Type="Success"
                Message="成功提示"
                IsShowIcon="True"
                Description="关于成功提示文案的详细说明和建议。" />
    <atom:Alert Type="Info"
                Message="信息说明"
                IsShowIcon="True"
                Description="关于提示文案的补充说明和信息。" />
    <atom:Alert Type="Warning"
                Message="警告"
                IsClosable="True"
                IsShowIcon="True"
                Description="这是一条关于提示文案的警告通知。" />
    <atom:Alert Type="Error"
                Message="错误"
                IsShowIcon="True"
                Description="这是一条关于提示文案的错误信息。" />
</StackPanel>
```

## 状态模型

Alert 的状态流按以下路径收敛：

```text
Public API / inherited command / item source / user input
  -> 控件实例状态
  -> effective state / pseudo-class / template property
  -> ControlTheme selector / presenter / renderer
  -> Gallery 可观察行为
```

状态维护规则：

- Disabled 或不可交互状态优先屏蔽 pointer、keyboard、motion 和提交类反馈。
- 基础交互和主题状态 状态由控件实例或明确的数据 owner 推导，不能在 template part 之间双向竞争。
- 模板重套用时必须把 public API 对应状态回放到新的 part、伪类和主题变量。
- 集合、弹层、异步、动效或窗口相关状态必须能处理 reset、close、cancel、detach 和 owner 释放。

## 主题与 Design Token

Alert 的视觉模型由控件模板、ControlTheme、SharedToken 和必要的控件 Token 共同构成。

| 主题文件 | 职责 |
| --- | --- |
| `AlertTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |

Alert 使用 `AlertToken` 作为控件 Token scope。Token 只表达组件视觉语义，不承载 基础交互和主题状态 运行时状态。

主题维护规则：

- 不删除或重命名已经稳定的 ControlTheme key、template part、伪类和资源 key。
- 不把可由 AXAML 表达的模板状态迁移为 C# 动态创建视觉。
- 不把 hover、pressed、selected、expanded、loading、filter、popup open 等运行时状态写入 Token。
- Browser 或平台特化主题必须保持同一 API 的语义一致。

Token 来源：

Alert Token 只表达组件级视觉变量，例如尺寸、间距、颜色、圆角、阴影、图标尺寸和弹层边界。Token 不承载运行时选择、展开、加载、错误、上传任务、过滤条件或业务状态。

当前 Token scope：

- `AlertToken`，scope id 为 `Alert`，源码位于 `src/AtomUI.Desktop.Controls/Alert/AlertToken.cs`。

## AOT 与裁剪注意事项

资源和 AOT 约束：

- 不通过运行时反射扫描 public API、Token 或 Gallery 示例数据。
- 不把可静态声明的模板结构迁移到 C# 动态创建。
- 异步加载、上传、弹层和窗口生命周期必须能取消或释放。
- 缓存对象必须与控件、窗口、弹层或数据 owner 生命周期一致。
- Source generator 生成文件不手工编辑；需要修改时改输入源或 generator。

性能边界：

- 控件应优先复用 Avalonia 原生虚拟化、模板绑定和资源系统。
- 避免为每次状态变化创建不必要的视觉对象、订阅或动画对象。
- 大集合控件必须保证 container recycle 后不会泄漏旧 item 状态。

## 源码索引

主要源码文件：

- `src/AtomUI.Desktop.Controls/Alert/Alert.cs`
- `src/AtomUI.Desktop.Controls/Alert/AlertToken.cs`
- `src/AtomUI.Desktop.Controls/Alert/Themes/AlertTheme.axaml`

职责边界：

- 控件主文件保留 public/protected API、Avalonia 属性注册、事件和主要生命周期入口。
- Theme 文件负责静态视觉结构、template part、selector 和资源绑定。
- Token 文件只提供组件视觉变量，不保存实例状态。
- Gallery 文件只展示用法和示例，不作为运行时逻辑 owner。

## 相关文档

- 源设计文档：`docs/controls/desktop/feedback/alert/overview.md`
- 实现文档：`docs/controls/desktop/feedback/alert/implementation.md`
- Token 文档：`docs/controls/desktop/feedback/alert/token.md`
- 变更记录：`docs/controls/desktop/feedback/alert/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/drawer/index-cn.md

# Drawer

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

Drawer 是 AtomUI 桌面控件体系中的抽屉控件，用于从窗口边缘滑入承载附加内容或操作面板。

Drawer 不负责模态对话框、普通 Popup 或 SplitView 布局容器。这些职责应由业务层、组合控件或更专用的 AtomUI 控件承担。

主要源码入口：

- `src/AtomUI.Desktop.Controls/Drawer`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/Feedback/Drawer` |
| 状态 | Stable |

## 何时使用

Drawer 的设计语言围绕控件职责、可观察状态和主题契约组织，而不是围绕模板节点组织。

| 维度 | 含义 | Drawer 中的表达 |
| --- | --- | --- |
| 产品语义 | 控件在界面中承担的稳定职责。 | Drawer 是 AtomUI 桌面控件体系中的抽屉控件，用于从窗口边缘滑入承载附加内容或操作面板。 |
| 内容承载 | 用户数据、展示内容、集合项或操作入口如何进入控件。 | `Content`、`ContentTemplate`、`ExtraTemplate`、`FooterTemplate`、`Title`。 |
| 状态反馈 | public API、内部状态和伪类如何形成用户可感知反馈。 | open/close、motion、visual option。 |
| 主题语义 | ControlTheme、SharedToken、控件 Token 和模板绑定如何表达视觉。 | Drawer Token + ControlTheme。 |

## 公共 API

Drawer 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。

核心 public surface 按语义分组维护：

| 契约组 | 代表成员 | 维护含义 |
| --- | --- | --- |
| 内容与数据 | `Content`、`ContentTemplate`、`ExtraTemplate`、`FooterTemplate`、`Title` | 定义控件展示内容、输入数据、模板或业务对象入口。 |
| 交互与状态 | `IsCloseOnMaskClick`、`IsMotionEnabled`、`IsOpen`、`IsShowCloseButton`、`IsShowMask` | 表达用户可观察状态、可用性、清除、加载或反馈语义。 |
| 视觉与布局 | `DialogSize`、`Placement`、`PushOffsetPercent`、`SizeType` | 影响尺寸、位置、颜色、形状、密度和模板视觉变量。 |
| 其他稳定入口 | `Extra`、`Footer`、`OpenOn` | 保留为 public surface，变更前需确认 Gallery 和用户 XAML 依赖。 |

当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。

主要公开类型与枚举：

- 类型：`Drawer`、`DrawerContainer`、`DrawerInfoContainer`。
- 枚举：`DrawerPlacement`。

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_CloseButton` | `?` | 承载用户触发入口、导航或关闭动作。 |
| `PART_InfoContainer` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_InfoContainerMotionActor` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_Mask` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |

当前未抽取到控件专属伪类；主题主要依赖 Avalonia 标准伪类、模板绑定和内部 StyledProperty。

## 事件与命令

Drawer 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。
当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/Feedback/Drawer/Views/DrawerShowCase.axaml:40`

Gallery key：`ExamplesContent` / item `0`

```axaml
<Panel>
    <atom:ToggleSwitch />
    <atom:Drawer IsOpen="{Binding $parent[Panel].((atom:ToggleSwitch)Children[0]).IsChecked}"
                 Title="基础抽屉"
                 DialogSize="50%">
        <StackPanel Orientation="Vertical" Spacing="5">
            <atom:TextBlock Text="一些内容..." />
            <atom:TextBlock Text="一些内容..." />
            <atom:TextBlock Text="一些内容..." />
        </StackPanel>
    </atom:Drawer>
</Panel>
```

### 多层抽屉

来源：`controlgallery/AtomUIGallery/ShowCases/Feedback/Drawer/Views/DrawerShowCase.axaml:60`

Gallery key：`ExamplesContent` / item `1`

```axaml
<Panel>
    <StackPanel Height="120" Classes="ControllerPanel">
        <StackPanel Orientation="Horizontal" Spacing="5" DockPanel.Dock="Top">
            <atom:TextBlock VerticalAlignment="Center" Text="弹出位置：" />
            <atom:OptionButtonGroup ButtonStyle="Outline"
                                    Name="MultiLevelPlacementGroup"
                                    OptionCheckedChanged="HandleMultiLevelPlacementChanged">
                <atom:OptionButton Tag="{x:Static atom:DrawerPlacement.Left}" Content="左侧" />
                <atom:OptionButton Tag="{x:Static atom:DrawerPlacement.Top}" Content="顶部" />
                <atom:OptionButton IsChecked="True" Tag="{x:Static atom:DrawerPlacement.Right}" Content="右侧" />
                <atom:OptionButton Tag="{x:Static atom:DrawerPlacement.Bottom}" Content="底部" />
            </atom:OptionButtonGroup>
        </StackPanel>
        <atom:ToggleSwitch />
    </StackPanel>

    <atom:Drawer Title="一级抽屉"
                 IsOpen="{Binding $parent[Panel].((Panel)Children[0]).((atom:ToggleSwitch)Children[1]).IsChecked}"
                 Placement="{Binding MultiLevelPlacement}">
        <StackPanel Orientation="Vertical" Spacing="5">
            <atom:TextBlock Text="一些内容..." />
            <atom:TextBlock Text="一些内容..." />
            <atom:TextBlock Text="一些内容..." />
            <atom:Button ButtonType="Primary"
                         Click="HandleOpenMultilevelLevelTwoDrawer" Content="二级抽屉" />
            <atom:Drawer Title="二级抽屉"
                         Name="MultiLevelDrawerLevelTwo"
                         Placement="{Binding MultiLevelPlacement}">
                <StackPanel Orientation="Vertical" Spacing="5">
                    <atom:TextBlock Text="一些内容..." />
                    <atom:TextBlock Text="一些内容..." />
                    <atom:TextBlock Text="一些内容..." />
                </StackPanel>
            </atom:Drawer>
        </StackPanel>
    </atom:Drawer>
</Panel>
```

### 额外区域和页脚

来源：`controlgallery/AtomUIGallery/ShowCases/Feedback/Drawer/Views/DrawerShowCase.axaml:105`

Gallery key：`ExamplesContent` / item `2`

```axaml
<Panel>
    <StackPanel Classes="ControllerPanel">
        <StackPanel Orientation="Horizontal" Spacing="5" DockPanel.Dock="Top">
            <atom:TextBlock VerticalAlignment="Center" Text="弹出位置：" />
            <atom:OptionButtonGroup ButtonStyle="Outline"
                                    Name="ExtraAndFooterPlacementGroup"
                                    OptionCheckedChanged="HandleExtraAndFooterPlacementChanged">
                <atom:OptionButton Tag="{x:Static atom:DrawerPlacement.Left}" Content="左侧" />
                <atom:OptionButton Tag="{x:Static atom:DrawerPlacement.Top}" Content="顶部" />
                <atom:OptionButton IsChecked="True" Tag="{x:Static atom:DrawerPlacement.Right}" Content="右侧" />
                <atom:OptionButton Tag="{x:Static atom:DrawerPlacement.Bottom}" Content="底部" />
            </atom:OptionButtonGroup>
        </StackPanel>
        <atom:ToggleSwitch Content="打开" />
    </StackPanel>
    <atom:Drawer IsOpen="{Binding $parent[Panel].((Panel)Children[0]).((atom:ToggleSwitch)Children[1]).IsChecked}"
                 Title="基础抽屉"
                 Placement="{Binding ExtraAndFooterPlacement}">
        <atom:Drawer.Extra>
            <StackPanel Orientation="Horizontal" Spacing="10">
                <atom:Button Content="取消" />
                <atom:Button ButtonType="Primary" Content="确定" />
            </StackPanel>
        </atom:Drawer.Extra>
        <atom:Drawer.Footer>
            <StackPanel Orientation="Horizontal" Spacing="10">
                <atom:Button Content="编辑" />
                <atom:Button ButtonType="Primary" Content="上传" />
                <atom:Button ButtonType="Primary" IsDanger="True" Content="删除" />
            </StackPanel>
        </atom:Drawer.Footer>
        <StackPanel Orientation="Vertical" Spacing="5">
            <atom:TextBlock Text="一些内容..." />
            <atom:TextBlock Text="一些内容..." />
            <atom:TextBlock Text="一些内容..." />
        </StackPanel>
    </atom:Drawer>
</Panel>
```

### 无遮罩

来源：`controlgallery/AtomUIGallery/ShowCases/Feedback/Drawer/Views/DrawerShowCase.axaml:249`

Gallery key：`ExamplesContent` / item `4`

```axaml
<Panel>
    <atom:ToggleSwitch Content="打开" />
    <atom:Drawer IsOpen="{Binding $parent[Panel].((atom:ToggleSwitch)Children[0]).IsChecked}"
                 Title="基础抽屉"
                 IsShowMask="False">
        <StackPanel Orientation="Vertical" Spacing="5">
            <atom:TextBlock Text="一些内容..." />
            <atom:TextBlock Text="一些内容..." />
            <atom:TextBlock Text="一些内容..." />
        </StackPanel>
    </atom:Drawer>
</Panel>
```

## 状态模型

Drawer 的状态流按以下路径收敛：

```text
Public API / inherited command / item source / user input
  -> 控件实例状态
  -> effective state / pseudo-class / template property
  -> ControlTheme selector / presenter / renderer
  -> Gallery 可观察行为
```

状态维护规则：

- Disabled 或不可交互状态优先屏蔽 pointer、keyboard、motion 和提交类反馈。
- open/close、motion、visual option 状态由控件实例或明确的数据 owner 推导，不能在 template part 之间双向竞争。
- 模板重套用时必须把 public API 对应状态回放到新的 part、伪类和主题变量。
- 集合、弹层、异步、动效或窗口相关状态必须能处理 reset、close、cancel、detach 和 owner 释放。
- TopLevel Drawer 使用 Window visible frame：包含 managed/drawn 标题栏，排除透明 frame shadow；该规则不按 OS 或 CSD 模式分叉。
- Drawer container 始终保留在 owning `TopLevel` 的 `ScopeAwareAdornerLayer`；Window drawn decorations 只绘制 chrome，不作为 Drawer host。

## 主题与 Design Token

Drawer 的视觉模型由控件模板、ControlTheme、SharedToken 和必要的控件 Token 共同构成。

| 主题文件 | 职责 |
| --- | --- |
| `DrawerContainerTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `DrawerInfoContainerTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |

Drawer 使用 `DrawerToken` 作为控件 Token scope。Token 只表达组件视觉语义，不承载 open/close、motion、visual option 运行时状态。

主题维护规则：

- 不删除或重命名已经稳定的 ControlTheme key、template part、伪类和资源 key。
- 不把可由 AXAML 表达的模板状态迁移为 C# 动态创建视觉。
- 不把 hover、pressed、selected、expanded、loading、filter、popup open 等运行时状态写入 Token。
- Browser 或平台特化主题必须保持同一 API 的语义一致。

Token 来源：

Drawer Token 只表达组件级视觉变量，例如尺寸、间距、颜色、圆角、阴影、图标尺寸和弹层边界。Token 不承载运行时选择、展开、加载、错误、上传任务、过滤条件或业务状态。

当前 Token scope：

- `DrawerToken`，scope id 为 `Drawer`，源码位于 `src/AtomUI.Desktop.Controls/Drawer/DrawerToken.cs`。

## AOT 与裁剪注意事项

资源和 AOT 约束：

- 不通过运行时反射扫描 public API、Token 或 Gallery 示例数据。
- 不把可静态声明的模板结构迁移到 C# 动态创建。
- 异步加载、上传、弹层和窗口生命周期必须能取消或释放。
- 缓存对象必须与控件、窗口、弹层或数据 owner 生命周期一致。
- Source generator 生成文件不手工编辑；需要修改时改输入源或 generator。

性能边界：

- 控件应优先复用 Avalonia 原生虚拟化、模板绑定和资源系统。
- 避免为每次状态变化创建不必要的视觉对象、订阅或动画对象。
- 大集合控件必须保证 container recycle 后不会泄漏旧 item 状态。

## 源码索引

主要源码文件：

- `src/AtomUI.Desktop.Controls/Drawer/Drawer.cs`
- `src/AtomUI.Desktop.Controls/Drawer/DrawerContainer.cs`
- `src/AtomUI.Desktop.Controls/Drawer/DrawerInfoContainer.cs`
- `src/AtomUI.Desktop.Controls/Drawer/DrawerPlacement.cs`
- `src/AtomUI.Desktop.Controls/Drawer/DrawerToken.cs`
- `src/AtomUI.Desktop.Controls/Primitives/TopLevelMarginBinder.cs`
- `src/AtomUI.Desktop.Controls/Window/Utils/WindowVisualLayerClip.cs`
- `src/AtomUI.Desktop.Controls/Window/Utils/WindowDrawnDecorationsReflectionExtensions.cs`
- `src/AtomUI.Desktop.Controls/Drawer/Themes/DrawerContainerTheme.axaml`
- `src/AtomUI.Desktop.Controls/Drawer/Themes/DrawerInfoContainerTheme.axaml`

职责边界：

- 控件主文件保留 public/protected API、Avalonia 属性注册、事件和主要生命周期入口。
- Theme 文件负责静态视觉结构、template part、selector 和资源绑定。
- Token 文件只提供组件视觉变量，不保存实例状态。
- Gallery 文件只展示用法和示例，不作为运行时逻辑 owner。

## 相关文档

- 源设计文档：`docs/controls/desktop/feedback/drawer/overview.md`
- 实现文档：`docs/controls/desktop/feedback/drawer/implementation.md`
- Token 文档：`docs/controls/desktop/feedback/drawer/token.md`
- 变更记录：`docs/controls/desktop/feedback/drawer/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/message/index-cn.md

# Message

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

Message 是 AtomUI 桌面控件体系中的全局消息控件，用于展示轻量级、自动关闭的操作反馈。

Message 不负责持久通知、模态对话框或页面内 Alert。这些职责应由业务层、组合控件或更专用的 AtomUI 控件承担。

主要源码入口：

- `src/AtomUI.Desktop.Controls/Message`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/Feedback/Message` |
| 状态 | Stable |

## 何时使用

Message 的设计语言围绕控件职责、可观察状态和主题契约组织，而不是围绕模板节点组织。

| 维度 | 含义 | Message 中的表达 |
| --- | --- | --- |
| 产品语义 | 控件在界面中承担的稳定职责。 | Message 是 AtomUI 桌面控件体系中的全局消息控件，用于展示轻量级、自动关闭的操作反馈。 |
| 内容承载 | 用户数据、展示内容、集合项或操作入口如何进入控件。 | `Icon`、`MaxItems`。 |
| 状态反馈 | public API、内部状态和伪类如何形成用户可感知反馈。 | collection/filter、motion、visual option。 |
| 主题语义 | ControlTheme、SharedToken、控件 Token 和模板绑定如何表达视觉。 | Message Token + ControlTheme。 |

## 公共 API

Message 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。

核心 public surface 按语义分组维护：

| 契约组 | 代表成员 | 维护含义 |
| --- | --- | --- |
| 内容与数据 | `Icon`、`MaxItems` | 定义控件展示内容、输入数据、模板或业务对象入口。 |
| 交互与状态 | `IsClosed`、`IsClosing`、`IsMotionEnabled` | 表达用户可观察状态、可用性、清除、加载或反馈语义。 |
| 视觉与布局 | `Position` | 影响尺寸、位置、颜色、形状、密度和模板视觉变量。 |
| 其他稳定入口 | `Message`、`MessageType` | 保留为 public surface，变更前需确认 Gallery 和用户 XAML 依赖。 |

稳定事件包括 `MessageClosed`。事件触发顺序属于兼容契约，不能因内部状态重排而改变。

主要公开类型与枚举：

- 类型：`Message`、`MessageCard`、`WindowMessageManager`。
- 枚举：`MessageType`。

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_Frame` | `?` | 承载根视觉、边框、背景或尺寸基线。 |
| `PART_HeaderContainer` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_IconContent` | `?` | 展示图标、状态图标或操作图标。 |
| `PART_Items` | `Panel` | 承载集合项、布局面板或虚拟化内容。 |
| `PART_Message` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |

控件专属或内部伪类包括 `Error=:error`、`Information=:information`、`Loading=:loading`、`MessageCardPseudoClass.Error`、`MessageCardPseudoClass.Information`、`MessageCardPseudoClass.Loading`、`MessageCardPseudoClass.Success`、`MessageCardPseudoClass.Warning`、`Success=:success`、`Warning=:warning`。这些伪类属于主题 selector 可观察契约，不能在未同步主题和 Gallery 的情况下重命名或删除。

## 事件与命令

Message 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。
稳定事件包括 `MessageClosed`。事件触发顺序属于兼容契约，不能因内部状态重排而改变。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/Feedback/Message/Views/MessageShowCase.axaml:35`

Gallery key：`ExamplesContent` / item `0`

```axaml
<atom:Button ButtonType="Primary" Click="ShowSimpleMessage" Content="显示普通消息" />
```

### 其他消息类型

来源：`controlgallery/AtomUIGallery/ShowCases/Feedback/Message/Views/MessageShowCase.axaml:46`

Gallery key：`ExamplesContent` / item `1`

```axaml
<StackPanel Orientation="Horizontal" Spacing="10">
    <atom:Button ButtonType="Default" Click="ShowSuccessMessage" Content="成功" />
    <atom:Button ButtonType="Default" Click="ShowInfoMessage" Content="信息" />
    <atom:Button ButtonType="Default" Click="ShowWarningMessage" Content="警告" />
    <atom:Button ButtonType="Default" Click="ShowErrorMessage" Content="错误" />
</StackPanel>
```

### 带加载指示器的消息

来源：`controlgallery/AtomUIGallery/ShowCases/Feedback/Message/Views/MessageShowCase.axaml:61`

Gallery key：`ExamplesContent` / item `2`

```axaml
<atom:Button ButtonType="Default" Click="ShowLoadingMessage" Content="显示加载指示器" />
```

### 回调

来源：`controlgallery/AtomUIGallery/ShowCases/Feedback/Message/Views/MessageShowCase.axaml:72`

Gallery key：`ExamplesContent` / item `3`

```axaml
<atom:Button ButtonType="Default" Click="ShowSequentialMessage" Content="显示加载指示器" />
```

## 状态模型

Message 的状态流按以下路径收敛：

```text
Public API / inherited command / item source / user input
  -> 控件实例状态
  -> effective state / pseudo-class / template property
  -> ControlTheme selector / presenter / renderer
  -> Gallery 可观察行为
```

状态维护规则：

- Disabled 或不可交互状态优先屏蔽 pointer、keyboard、motion 和提交类反馈。
- collection/filter、motion、visual option 状态由控件实例或明确的数据 owner 推导，不能在 template part 之间双向竞争。
- 模板重套用时必须把 public API 对应状态回放到新的 part、伪类和主题变量。
- 集合、弹层、异步、动效或窗口相关状态必须能处理 reset、close、cancel、detach 和 owner 释放。

## 主题与 Design Token

Message 的视觉模型由控件模板、ControlTheme、SharedToken 和必要的控件 Token 共同构成。

| 主题文件 | 职责 |
| --- | --- |
| `MessageCardTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `WindowMessageManagerTheme.axaml` | 定义弹层、窗口或 overlay 宿主视觉。 |

Message 使用 `MessageToken` 作为控件 Token scope。Token 只表达组件视觉语义，不承载 collection/filter、motion、visual option 运行时状态。

主题维护规则：

- 不删除或重命名已经稳定的 ControlTheme key、template part、伪类和资源 key。
- 不把可由 AXAML 表达的模板状态迁移为 C# 动态创建视觉。
- 不把 hover、pressed、selected、expanded、loading、filter、popup open 等运行时状态写入 Token。
- Browser 或平台特化主题必须保持同一 API 的语义一致。

Token 来源：

Message Token 只表达组件级视觉变量，例如尺寸、间距、颜色、圆角、阴影、图标尺寸和弹层边界。Token 不承载运行时选择、展开、加载、错误、上传任务、过滤条件或业务状态。

当前 Token scope：

- `MessageToken`，scope id 为 `Message`，源码位于 `src/AtomUI.Desktop.Controls/Message/MessageToken.cs`。

## AOT 与裁剪注意事项

资源和 AOT 约束：

- 不通过运行时反射扫描 public API、Token 或 Gallery 示例数据。
- 不把可静态声明的模板结构迁移到 C# 动态创建。
- 异步加载、上传、弹层和窗口生命周期必须能取消或释放。
- 缓存对象必须与控件、窗口、弹层或数据 owner 生命周期一致。
- Source generator 生成文件不手工编辑；需要修改时改输入源或 generator。

性能边界：

- 控件应优先复用 Avalonia 原生虚拟化、模板绑定和资源系统。
- 避免为每次状态变化创建不必要的视觉对象、订阅或动画对象。
- 大集合控件必须保证 container recycle 后不会泄漏旧 item 状态。

## 源码索引

主要源码文件：

- `src/AtomUI.Desktop.Controls/Message/IMessage.cs`
- `src/AtomUI.Desktop.Controls/Message/IMessageManager.cs`
- `src/AtomUI.Desktop.Controls/Message/Message.cs`
- `src/AtomUI.Desktop.Controls/Message/MessageCard.cs`
- `src/AtomUI.Desktop.Controls/Message/MessageCardPseudoClass.cs`
- `src/AtomUI.Desktop.Controls/Message/MessageToken.cs`
- `src/AtomUI.Desktop.Controls/Message/MessageType.cs`
- `src/AtomUI.Desktop.Controls/Message/Themes/MessageCardTheme.axaml`
- `src/AtomUI.Desktop.Controls/Message/Themes/WindowMessageManagerTheme.axaml`
- `src/AtomUI.Desktop.Controls/Message/WindowMessageManager.cs`
- `src/AtomUI.Core/MotionScene/MotionExecutionState.cs`

职责边界：

- 控件主文件保留 public/protected API、Avalonia 属性注册、事件和主要生命周期入口。
- Theme 文件负责静态视觉结构、template part、selector 和资源绑定。
- Token 文件只提供组件视觉变量，不保存实例状态。
- Gallery 文件只展示用法和示例，不作为运行时逻辑 owner。

## 相关文档

- 源设计文档：`docs/controls/desktop/feedback/message/overview.md`
- 实现文档：`docs/controls/desktop/feedback/message/implementation.md`
- Token 文档：`docs/controls/desktop/feedback/message/token.md`
- 变更记录：`docs/controls/desktop/feedback/message/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/modal/index-cn.md

# Modal

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

`Dialog` 用于需要独立内容表面、明确结果和受控关闭流程的桌面对话。它支持 Overlay 与原生 Window 两种宿主、modal 与 modeless 两种交互模式、声明式 `IsOpen` 和静态异步 API。

`MessageBox` 是 `Dialog` 的语义专化，用于信息、成功、警告、错误和确认消息。它复用 Dialog 的 Session、Presenter、关闭、尺寸、资源和焦点模型，不维护第二套隐藏 Dialog。

Modal 不承担通知队列、轻量 Tooltip、Popup 菜单或业务级导航服务职责。

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/Feedback/Modal` |
| 状态 | Stable |

## 何时使用

- 对话表面由标题、内容、Footer 和操作按钮组成，Overlay 与 Window 共享同一个内容模型。
- modal 通过 mask 或原生 owner 关系阻断底层输入；modeless 保持底层可交互。
- Presenter 的真实展示边界属于 Session 生命周期的一部分。Overlay 在入场 motion 完成后触发 `Opened`，Window 在原生 `DialogWindow.Opened` 后触发；关闭任务分别等待 Overlay 退出 motion 或原生 `DialogWindow.Closed`，并在宿主移除和资源释放完成后结束。
- Overlay 关闭时，外层 Surface motion 与 `DialogSurface` 内容层的前景 opacity 动画并行；内容和按钮保持附着，直到所有关闭任务完成后才 teardown。Window 继续使用原生 Window 生命周期。
- `MessageBoxStyle` 只表达消息语义和默认图标/按钮策略，不改变 Dialog 生命周期。

## 公共 API

### 3.1 Dialog 契约组

| 契约组 | 代表成员 | 语义 |
| --- | --- | --- |
| 内容 | `Title`, `TitleIcon`, `Content`, `ContentTemplate`, `DataContext` | 定义标题和任意内容对象或模板。 |
| 打开状态 | `IsOpen`, `OpenAsync(...)` | `IsOpen` 是默认 TwoWay 的声明式意图；`OpenAsync` 表示一次完整 Session。 |
| 展示方式 | `DialogHostType`, `IsModal`, `PlacementTarget`, startup anchor/offset | 选择 Overlay/Window、交互模态和初始位置。直接实例化与静态 API 的水平、垂直 startup anchor 默认均为 `Center`；显式 `Custom` 时由对应 offset 决定位置。 |
| 尺寸与窗口能力 | `HostWidth/Height/Min/Max`, `IsResizable`, `IsClosable`, `IsMaskClosable`, `IsDragMovable`, `IsMaximizable`, `IsMinimizable`, `IsTopmost` | 同一组 Surface 正文尺寸请求映射到 Overlay 或原生 Window。`NaN` 表示初始自然尺寸；有效最小尺寸还必须满足 Dialog 的结构性下限。`IsClosable` 控制标题栏关闭入口，`IsMaskClosable` 控制 Overlay modal mask 外点关闭入口，两者正交且默认都为 `true`。 |
| 操作 | `StandardButtons`, `CustomButtons`, `DefaultStandardButton`, `EscapeStandardButton`, `ButtonsConfigure` | 生成标准按钮、加入自定义按钮并配置当前有效按钮序列。 |
| 状态与策略 | `IsLoading`, `IsConfirmLoading`, `IsFooterVisible`, `IsMotionEnabled`, `BeforeCloseAsync` | 控制加载、确认按钮 loading、Footer、motion 和关闭前校验。 |
| 结果 | `Result`, `Accept()`, `Reject()`, `Done(...)` | 所有关闭来源归一为结果与 `DialogCloseReason`。 |

静态入口只有异步形式：

- `ShowDialogAsync(...)` 创建 modeless Dialog。
- `ShowDialogModalAsync(...)` 创建 modal Dialog。
- 两者都在完整 teardown 后返回 `Task<object?>`。

同一个实例存在任何尚未进入 `Closed` 的 Session 时，再次调用 `OpenAsync` 会抛出 `InvalidOperationException`。声明式 `IsOpen=true` 可以在旧 Session 完整关闭后创建新 Session。

### 3.2 关闭事件

Dialog 公开 `Opened`、`Closing`、`Accepted`、`Rejected`、`Finished`、`Closed` 和 `ButtonClicked`。

正常关闭顺序：

```text
按钮来源时 ButtonClicked
  -> Closing
  -> BeforeCloseAsync
  -> 提交 IsOpen=false 与 Result
  -> Accepted 或 Rejected
  -> Finished
  -> 退出 motion 与 presenter teardown
  -> IDialogAwareDataContext.NotifyClosed
  -> Closed
```

`ButtonClicked.Handled=true`、`Closing.Cancel=true` 或 `BeforeCloseAsync` 返回 `false` 会取消本次普通关闭。owner close、placement target detach、外部取消和 presenter 打开失败属于强制 teardown：Session 仍触发一次 `Closing`，但忽略 `Cancel`、跳过 `BeforeCloseAsync`，也不受 confirm loading 阻止，随后执行完整释放。

事件处理器或 presenter 在结果提交后抛出的首个异常，只会在 teardown 完成后传播。

### 3.3 MessageBox 契约

`MessageBox : Dialog` 增加 `Style`、`Icon`、`OkButtonStyle`、`OkButtonText`、`CancelButtonText`、`IsCenterOnStartup`、`Confirmed`、`Cancelled`、`Confirm()` 和 `Cancel()`。

`ShowMessageBoxAsync(...)` 与 `ShowMessageBoxModalAsync(...)` 复用继承的异步 Session 语义。未显式指定 `MessageBoxOptions.MinWidth` 时保留 MessageBox Token 的默认最小宽度。`IsMaskClosable` 由 `Dialog` 继承并经 `MessageBoxOptions` 透传，语义与 Dialog 一致。

### 3.4 Template Parts

| Part | 所属类型 | 职责 |
| --- | --- | --- |
| `PART_Header` | `DialogSurface` | Overlay 标题栏、拖动与 caption 操作。 |
| `PART_ButtonBox` | `DialogSurface` | 当前标准/自定义按钮序列。 |
| `PART_Resizer` | `DialogSurface` | Overlay resize handles。 |
| `PART_LeftGroup` / `PART_CenterGroup` / `PART_RightGroup` | `DialogButtonBox` | 按按钮角色布局。 |
| `PART_MaskMotionActor` | `OverlayDialogPresenter` | modal mask 及其 motion。 |
| `PART_SurfaceMotionActor` | `OverlayDialogPresenter` | DialogSurface 入场/退出 motion。 |
| `PART_SurfaceContentLayer` | `DialogSurface` 内部模板节点 | 包围 Header、ContentFrame 和 FooterFrame；Overlay 关闭时承载前景 opacity 动画，不是 public Semantic Part。 |

当前没有 Modal 专属 pseudo class。

## 事件与命令

### 3.2 关闭事件
事件处理器或 presenter 在结果提交后抛出的首个异常，只会在 teardown 完成后传播。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/Feedback/Modal/Views/ModalShowCase.axaml:35`

Gallery key：`ExamplesContent` / item `0`

```axaml
<StackPanel Orientation="Horizontal" Spacing="10" Loaded="HandleDialogExampleLoaded">
    <Panel>
        <atom:Button ButtonType="Primary" Name="BasicOpenModalButton" Content="打开浮层模态框" />
        <atom:Dialog Name="BasicDialog"
                     IsOpen="{Binding IsBasicModalOpened, Mode=TwoWay}"
                     Title="基础模态框"
                     IsModal="False"
                     IsResizable="True"
                     IsDragMovable="True"
                     StandardButtons="Cancel,Ok"
                     DefaultStandardButton="Ok"
                     HorizontalStartupLocation="Center"
                     VerticalOffset="30%"
                     HostMinWidth="300"
                     HostMaxWidth="520"
                     HostMaxHeight="360">
            <StackPanel Orientation="Vertical">
                <TextBlock Text="一些内容..." />
                <TextBlock Text="一些内容..." />
                <TextBlock Text="一些内容..." />
            </StackPanel>
        </atom:Dialog>
    </Panel>
    <Panel>
        <atom:Button ButtonType="Primary" Name="BasicWindowOpenModalButton" Content="打开窗口模态框" />
        <atom:Dialog Name="BasicWindowDialog"
                     IsOpen="{Binding IsBasicWindowModalOpened, Mode=TwoWay}"
                     Title="基础窗口模态框"
                     IsModal="True"
                     IsResizable="True"
                     IsClosable="True"
                     IsDragMovable="True"
                     IsMaximizable="True"
                     DialogHostType="Window"
                     HorizontalStartupLocation="Center"
                     VerticalOffset="30%"
                     StandardButtons="Yes"
                     DefaultStandardButton="Yes"
                     HostMinWidth="300"
                     HostMaxWidth="520"
                     HostMaxHeight="360">
            <StackPanel Orientation="Vertical">
                <TextBlock Text="一些内容..." />
                <TextBlock Text="一些内容..." />
                <TextBlock Text="一些内容..." />
            </StackPanel>
        </atom:Dialog>
    </Panel>
</StackPanel>
```

### 异步关闭

来源：`controlgallery/AtomUIGallery/ShowCases/Feedback/Modal/Views/ModalShowCase.axaml:94`

Gallery key：`ExamplesContent` / item `1`

```axaml
<StackPanel Orientation="Horizontal" Spacing="10" Loaded="HandleDialogExampleLoaded">
    <Panel>
        <atom:Button ButtonType="Primary" Name="AsyncDialogOpenModalButton" Content="打开带异步逻辑的模态框" />
        <atom:Dialog Name="AsyncDialog"
                     IsOpen="{Binding IsAsyncDialogOpened, Mode=TwoWay}"
                     Title="异步关闭模态框"
                     IsModal="True"
                     IsDragMovable="True"
                     IsResizable="False"
                     IsMaximizable="False"
                     StandardButtons="Ok, Cancel"
                     HorizontalStartupLocation="Center"
                     VerticalStartupLocation="Center"
                     DefaultStandardButton="Ok"
                     ButtonClicked="HandleAsyncDialogButtonClicked"
                     HostMinWidth="400">
            <StackPanel>
                <TextBlock Text="模态框内容" />
            </StackPanel>
        </atom:Dialog>
    </Panel>
</StackPanel>
```

### 加载状态

来源：`controlgallery/AtomUIGallery/ShowCases/Feedback/Modal/Views/ModalShowCase.axaml:219`

Gallery key：`ExamplesContent` / item `3`

```axaml
<StackPanel Orientation="Horizontal" Spacing="10" Loaded="HandleDialogExampleLoaded">
    <Panel>
        <atom:Button ButtonType="Primary" Name="LoadingDialogOpenModalButton" Content="打开模态框" />
        <atom:Dialog Name="LoadingDialog"
                     IsOpen="{Binding IsLoadingMsgBoxOpened, Mode=TwoWay}"
                     Title="加载中模态框"
                     IsModal="True"
                     IsLoading="True"
                     IsDragMovable="True"
                     IsResizable="False"
                     IsMaximizable="False"
                     StandardButtons="Reload"
                     HorizontalStartupLocation="Center"
                     VerticalStartupLocation="Center"
                     DefaultStandardButton="Reload"
                     Opened="HandleLoadingDialogOpened"
                     ButtonClicked="HandleLoadingDialogButtonClicked"
                     HostMinWidth="400">
            <StackPanel>
                <TextBlock Text="一些内容..." />
                <TextBlock Text="一些内容..." />
                <TextBlock Text="一些内容..." />
            </StackPanel>
        </atom:Dialog>
    </Panel>
</StackPanel>
```

### 自定义页脚按钮

来源：`controlgallery/AtomUIGallery/ShowCases/Feedback/Modal/Views/ModalShowCase.axaml:255`

Gallery key：`ExamplesContent` / item `4`

```axaml
<StackPanel Orientation="Horizontal" Spacing="10" Loaded="HandleDialogExampleLoaded">
    <Panel>
        <atom:Button ButtonType="Primary" Name="CustomFooterDialogOpenButton" Content="打开模态框" />
        <atom:Dialog Name="CustomFooterDialog"
                     IsOpen="{Binding IsCustomFooterDialogOpened, Mode=TwoWay}"
                     Title="标题"
                     IsModal="True"
                     DialogHostType="Window"
                     StandardButtons="Ok, Cancel"
                     HorizontalStartupLocation="Center"
                     VerticalStartupLocation="Center"
                     DefaultStandardButton="Ok"
                     HostMinWidth="400">
            <atom:Dialog.CustomButtons>
                <atom:DialogButton Role="ActionRole" Content="自定义按钮" />
            </atom:Dialog.CustomButtons>
            <StackPanel Spacing="5">
                <TextBlock Text="一些内容..." />
                <TextBlock Text="一些内容..." />
                <TextBlock Text="一些内容..." />
                <TextBlock Text="一些内容..." />
                <TextBlock Text="一些内容..." />
            </StackPanel>
        </atom:Dialog>
    </Panel>

    <Panel>
        <atom:Button ButtonType="Primary" Name="CustomFooterMsgBoxOpenButton" Content="打开模态框" />
        <atom:MessageBox Name="CustomFooterMsgBox"
                         IsOpen="{Binding IsCustomFooterMsgBoxOpened, Mode=TwoWay}"
                         Title="确认"
                         IsModal="True"
                         Style="Confirm"
                         HostMinWidth="400">
            <atom:MessageBox.CustomButtons>
                <atom:DialogButton Role="ActionRole" Content="自定义按钮" />
            </atom:MessageBox.CustomButtons>
            <StackPanel Spacing="5">
                <TextBlock Text="一些文本 ..." />
            </StackPanel>
        </atom:MessageBox>
    </Panel>
</StackPanel>
```

## 状态模型

- `IsOpen` 表示最新声明式意图；`DialogSession` 表示一次实际展示。两者不能由 presenter 或 template part 反向拥有。
- modal Overlay 的真实 pointer 输入命中 mask，modeless Overlay 在 Surface 外穿透到底层。只有栈顶 presenter 响应 mask 与 Escape。栈顶 modal mask 外点默认以 `HostCloseRequest` 发起普通关闭；`IsMaskClosable=false` 时该次点击被吞掉且不产生任何关闭请求，不进入 `Closing`/`BeforeCloseAsync` 管道。Window host 没有 mask，外点本来就不触发关闭。
- 所有平台的 `AtomUI.Window` 都把 Overlay presenter 放在 owning `TopLevel` 的 Avalonia `OverlayLayer`。modal 活跃时通过 Window 引用计数租约隐藏 managed/drawn chrome overlay，使 mask 覆盖完整 Avalonia 可绘制窗口轮廓并阻断 caption input；位于客户端 visual tree 外的原生系统 chrome 仍由平台管理。
- Dialog 内容区内的 popup 类控件(ComboBox、Select、DatePicker、Tooltip、Flyout、ContextMenu 等)由同一 Window 的 `PopupOverlayLayer` 或原生 Popup host 承载，位于 Dialog `OverlayLayer` 之上并保持可命中；二者之间的 `LightDismissOverlayLayer` 保证外点关闭与输入穿透语义和普通页面一致。层级与所有权契约见 [Modal 内容弹层叠放设计](popup-layering-design.md)。
- Overlay mask bounds、Window visible frame 与 Dialog 正文 owner bounds 独立：mask 使用完整 layer bounds；所有平台的 Surface 正文定位、拖动、resize 和 maximize 使用 visible frame 按当前有效 drawn frame thickness 内缩后的范围，允许进入 managed/drawn title bar，但不能覆盖窗口 frame。Dialog BoxShadow 只参与绘制并允许在窗口边缘由统一 visual-layer clip 裁剪。
- Enter/Escape 根据当前有效按钮序列查找 default/escape 按钮，运行时修改标准按钮或自定义按钮会立即生效。
- `IsConfirmLoading=true` 只阻止用户发起的普通关闭，不阻止 owner close、detach、取消和失败 teardown。
- 打开后焦点进入 DialogSurface；嵌套 Dialog 关闭时恢复下层 Surface，最后一层关闭时恢复原触发控件。
- Overlay 等待 mask 与 Surface 的 opening/closing motion；关闭时同一 presenter 还等待内容层 opacity 动画，并在聚合任务完成后才断开 composition children、释放 Surface 和移除 layer。`IsMotionEnabled=false` 只跳过这些 motion，不跳过宿主附加、移除和释放。Window 不创建 Surface `MotionActor`，其打开与关闭分别等待原生 `DialogWindow.Opened` 和 `DialogWindow.Closed`。
- `IsResizable=true` 允许在有效尺寸区间内交互缩放，不表示无约束 resize。结构性最小尺寸在宿主容量允许时始终保留标题、Footer 和非零正文 viewport；`HostMin*` 只能提高该下限，`HostMax*=PositiveInfinity` 仍受 owner 或 screen capacity 限制。Overlay handle 捕获 pointer，release 或 capture lost 都会完整结束当前 resize，不复用上一次拖拽 origin。

## 主题与 Design Token

| 主题文件 | 职责 |
| --- | --- |
| `DialogTheme.axaml` | Dialog 默认属性和 Token 映射。 |
| `DialogSurfaceTheme.axaml` | 共享标题、内容、Footer、按钮与 resize 结构。 |
| `OverlayDialogPresenterTheme.axaml` | 同一 presenter 内组合 mask 与 Surface motion。 |
| `DialogButtonBoxTheme.axaml` | 三组按钮布局。 |
| `OverlayDialogHeaderTheme.axaml` | Overlay 标题栏。 |
| `OverlayDialogMaskTheme.axaml` | modal mask。 |
| `OverlayDialogResizerTheme.axaml` | Overlay resize handles。 |
| `MessageBoxTheme.axaml` / `MessageBoxContentTheme.axaml` | MessageBox 默认尺寸和语义内容。 |

`DialogToken` 提供背景、文字、间距、尺寸和 Footer 视觉。motion duration 使用 Dialog scope 的 SharedToken `MotionDurationMid`，不在代码中硬编码。

Token 来源：

Modal Token 只表达组件级视觉变量，例如尺寸、间距、颜色、圆角、阴影、图标尺寸和弹层边界。Token 不承载运行时选择、展开、加载、错误、上传任务、过滤条件或业务状态。

当前 Token scope：

- `DialogToken`，scope id 为 `Dialog`，源码位于 `src/AtomUI.Desktop.Controls/Dialog/DialogToken.cs`。

## AOT 与裁剪注意事项

- Overlay presenter 在 Dialog 已附加时以 Dialog 为 inheritance parent，否则以 placement target 为 parent。
- Window 保留 DialogSurface 到 Window `ContentPresenter` 的正常 styling parent 链，避免在未附加树中提前实例化的嵌套控件失去 ControlTheme。Dialog/owner 资源由 presenter-owned `DialogResourceBridge` 转发到 Window resources；bridge 对称转发 `ResourcesChanged`，并在 `DisposeAsync` 中移除和退订。
- runtime binding 只用于动态 presenter/Surface/按钮关系，并由 owning presenter、Surface 或 ButtonBox 对称释放。
- Presenter 为 Surface 复用单一 `MatrixTransform` 作为位置 owner。拖动 `PointerMoved` 只更新 Matrix translation 并同步不触发布局的 `Dialog.OffsetX/Y`；位置先按 DPI 取整，再二次 clamp 到 body owner bounds，避免取整重新越界。
- drawn decorations 反射兼容边界只读取 frame/titlebar 几何；Modal 不反射发现业务 host，也不新增 trimming root。实现不使用反射修改 TemplatedParent，不扫描程序集发现 Dialog API，不使用同步 DispatcherFrame。
- Session、Presenter、Surface 和 Content 的关闭回收由 Overlay/Window WeakReference 测试覆盖。
- 状态机、按钮表和 presenter 选择都是静态类型路径，保持 NativeAOT 友好。

## 源码索引

| 路径 | 职责 |
| --- | --- |
| `Dialog.cs` | public 属性、事件、内容、按钮和内部协作入口。 |
| `Dialog.Lifecycle.cs` | `IsOpen` reconcile、`OpenAsync`、事件通知和 presenter 选择。 |
| `Dialog.StaticAPI.cs` | 静态 modeless/modal 异步创建入口。 |
| `DialogSession.cs` | 单次展示状态机、关闭仲裁、取消、结果、焦点和 teardown。 |
| `IDialogPresenter.cs` | Overlay/Window 共用的最小异步协议。 |
| `DialogSurface.cs` | 标题、内容、Footer、按钮和 Overlay resize 的共享表面；负责 `PART_SurfaceContentLayer` 的 template part 生命周期。 |
| `ButtonBox/DialogButtonBox.cs` | 标准按钮生成、唯一有效按钮序列和自定义集合同步。 |
| `OverlayHost/DialogOverlayLayer.cs` | 解析 owning TopLevel 的 Avalonia `OverlayLayer` 或局部 scope fallback，并管理 owner scope 内的 presenter stack。 |
| `OverlayHost/OverlayDialogPresenter.cs` | 同时拥有 mask、Surface、placement、drag/resize 和 Overlay close motion choreography。 |
| `WindowHost/WindowDialogPresenter.cs` | 原生 Window 属性映射、modal owner、尺寸、位置和生命周期。 |
| `WindowHost/DialogWindow.cs` | 原生 caption close 仲裁和显式尺寸应用。 |
| `MessageBox/MessageBox.cs` | Dialog 派生的消息语义、静态 API 和按钮配置。 |
| `MessageBox/MessageBoxContent.cs` | MessageBox 的图标与内容组合。 |
| `Dialog/Themes` / `MessageBox/Themes` | 共享 Surface、Overlay presenter 和 MessageBox AXAML 结构。 |
| `src/AtomUI.Core/MotionScene/AbstractMotion.cs` | 共享 Motion 的 transition completion boundary；等待全部 transition 或安全超时后才报告完成。 |

对应回归测试位于 `tests/AtomUI.Desktop.Controls.Tests/Dialog` 和 `tests/AtomUI.Desktop.Controls.Tests/MessageBox`。

## 相关文档

- 源设计文档：`docs/controls/desktop/feedback/modal/overview.md`
- 实现文档：`docs/controls/desktop/feedback/modal/implementation.md`
- Token 文档：`docs/controls/desktop/feedback/modal/token.md`
- 变更记录：`docs/controls/desktop/feedback/modal/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/notification/index-cn.md

# Notification

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

Notification 是 AtomUI 桌面控件体系中的通知控件，用于在窗口角落展示可关闭的较重反馈和进度信息。

Notification 不负责即时消息气泡、页面内 Alert 或模态确认。这些职责应由业务层、组合控件或更专用的 AtomUI 控件承担。

主要源码入口：

- `src/AtomUI.Desktop.Controls/Notifications`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/Feedback/Notification` |
| 状态 | Stable |

## 何时使用

Notification 的设计语言围绕控件职责、可观察状态和主题契约组织，而不是围绕模板节点组织。

| 维度 | 含义 | Notification 中的表达 |
| --- | --- | --- |
| 产品语义 | 控件在界面中承担的稳定职责。 | Notification 是 AtomUI 桌面控件体系中的通知控件，用于在窗口角落展示可关闭的较重反馈和进度信息。 |
| 内容承载 | 用户数据、展示内容、集合项或操作入口如何进入控件。 | `Icon`、`MaxItems`、`Title`。 |
| 状态反馈 | public API、内部状态和伪类如何形成用户可感知反馈。 | selection/checked/active、loading/async、collection/filter、motion、visual option。 |
| 主题语义 | ControlTheme、SharedToken、控件 Token 和模板绑定如何表达视觉。 | Notification Token + ControlTheme。 |

## 公共 API

Notification 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。

核心 public surface 按语义分组维护：

| 契约组 | 代表成员 | 维护含义 |
| --- | --- | --- |
| 内容与数据 | `Icon`、`MaxItems`、`Title` | 定义控件展示内容、输入数据、模板或业务对象入口。 |
| 选择与集合 | `CurrentExpiration` | 维护选择、展开、过滤、分页、分组或集合状态。 |
| 交互与状态 | `IsClosed`、`IsClosing`、`IsMotionEnabled`、`IsPauseOnHover`、`IsShowProgress` | 表达用户可观察状态、可用性、清除、加载或反馈语义。 |
| 视觉与布局 | `Position`、`ProgressIndicatorBrush`、`ProgressIndicatorThickness` | 影响尺寸、位置、颜色、形状、密度和模板视觉变量。 |
| 其他稳定入口 | `CardExpiredPollingInterval`、`CleanupPollingInterval`、`Expiration`、`NotificationType` | 保留为 public surface，变更前需确认 Gallery 和用户 XAML 依赖。 |

稳定事件包括 `NotificationClosed`。事件触发顺序属于兼容契约，不能因内部状态重排而改变。

主要公开类型与枚举：

- 类型：`Notification`、`NotificationCard`、`NotificationMoveDownInMotion`、`NotificationMoveDownOutMotion`、`NotificationMoveLeftInMotion`、`NotificationMoveLeftOutMotion`、`NotificationMoveRightInMotion`、`NotificationMoveRightOutMotion`、`NotificationMoveUpInMotion`、`NotificationMoveUpOutMotion`、`NotificationProgressBar`、`NotificationProgressBarVisibleConverter`、`WindowNotificationManager`。
- 枚举：`NotificationPosition`、`NotificationType`。

`NotificationType.Default` 表达普通通知语义，默认不显示类型图标，也不投射 success/info/warning/error 状态伪类。`Information`、`Success`、`Warning` 和 `Error` 表达带类型通知语义，在未设置自定义 `Icon` 时使用对应状态图标，并参与状态颜色 selector。

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_CloseButton` | `?` | 承载用户触发入口、导航或关闭动作。 |
| `PART_Items` | `Panel` | 承载集合项、布局面板或虚拟化内容。 |
| `PART_Layout` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |

控件专属或内部伪类包括 `BottomCenter=:bottomcenter`、`BottomLeft=:bottomleft`、`BottomRight=:bottomright`、`NotificationPseudoClass.BottomCenter`、`NotificationPseudoClass.BottomLeft`、`NotificationPseudoClass.BottomRight`、`NotificationPseudoClass.TopCenter`、`NotificationPseudoClass.TopLeft`、`NotificationPseudoClass.TopRight`、`TopCenter=:topcenter`、`TopLeft=:topleft`、`TopRight=:topright`。这些伪类属于主题 selector 可观察契约，不能在未同步主题和 Gallery 的情况下重命名或删除。

## 事件与命令

Notification 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。
稳定事件包括 `NotificationClosed`。事件触发顺序属于兼容契约，不能因内部状态重排而改变。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/Feedback/Notification/Views/NotificationShowCase.axaml:32`

Gallery key：`ExamplesContent` / item `0`

```axaml
<atom:Button ButtonType="Primary" Click="ShowSimpleNotification" Content="显示通知" />
```

### 自动关闭时长

来源：`controlgallery/AtomUIGallery/ShowCases/Feedback/Notification/Views/NotificationShowCase.axaml:41`

Gallery key：`ExamplesContent` / item `1`

```axaml
<atom:Button ButtonType="Primary" Click="ShowNeverCloseNotification" Content="打开通知框" />
```

### 带图标通知

来源：`controlgallery/AtomUIGallery/ShowCases/Feedback/Notification/Views/NotificationShowCase.axaml:50`

Gallery key：`ExamplesContent` / item `2`

```axaml
<StackPanel Orientation="Horizontal" Spacing="10">
    <atom:Button ButtonType="Default" Click="ShowSuccessNotification" Content="成功" />
    <atom:Button ButtonType="Default" Click="ShowInfoNotification" Content="信息" />
    <atom:Button ButtonType="Default" Click="ShowWarningNotification" Content="警告" />
    <atom:Button ButtonType="Default" Click="ShowErrorNotification" Content="错误" />
</StackPanel>
```

### 弹出位置

来源：`controlgallery/AtomUIGallery/ShowCases/Feedback/Notification/Views/NotificationShowCase.axaml:64`

Gallery key：`ExamplesContent` / item `3`

```axaml
<StackPanel Orientation="Vertical" Spacing="10">
    <StackPanel Orientation="Horizontal" Spacing="10">
        <atom:Button ButtonType="Primary" Click="ShowTopNotification" Content="顶部" />
        <atom:Button ButtonType="Primary" Click="ShowBottomNotification" Content="底部" />
    </StackPanel>
    <atom:Separator />
    <StackPanel Orientation="Horizontal" Spacing="10">
        <atom:Button ButtonType="Primary" Click="ShowTopLeftNotification" Content="左上" />
        <atom:Button ButtonType="Primary" Click="ShowTopRightNotification" Content="右上" />
    </StackPanel>
    <atom:Separator />
    <StackPanel Orientation="Horizontal" Spacing="10">
        <atom:Button ButtonType="Primary" Click="ShowBottomLeftNotification" Content="左下" />
        <atom:Button ButtonType="Primary" Click="ShowBottomRightNotification" Content="右下" />
    </StackPanel>
</StackPanel>
```

## 状态模型

Notification 的状态流按以下路径收敛：

```text
Public API / inherited command / item source / user input
  -> 控件实例状态
  -> effective state / pseudo-class / template property
  -> ControlTheme selector / presenter / renderer
  -> Gallery 可观察行为
```

状态维护规则：

- Disabled 或不可交互状态优先屏蔽 pointer、keyboard、motion 和提交类反馈。
- selection/checked/active、loading/async、collection/filter、motion、visual option 状态由控件实例或明确的数据 owner 推导，不能在 template part 之间双向竞争。
- 模板重套用时必须把 public API 对应状态回放到新的 part、伪类和主题变量。
- 集合、弹层、异步、动效或窗口相关状态必须能处理 reset、close、cancel、detach 和 owner 释放。

## 主题与 Design Token

Notification 的视觉模型由控件模板、ControlTheme、SharedToken 和必要的控件 Token 共同构成。

| 主题文件 | 职责 |
| --- | --- |
| `NotificationCardTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `NotificationProgressBarTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `WindowNotificationManagerTheme.axaml` | 定义弹层、窗口或 overlay 宿主视觉。 |

Notification 使用 `NotificationToken` 作为控件 Token scope。Token 只表达组件视觉语义，不承载 selection/checked/active、loading/async、collection/filter、motion、visual option 运行时状态。

主题维护规则：

- 不删除或重命名已经稳定的 ControlTheme key、template part、伪类和资源 key。
- 不把可由 AXAML 表达的模板状态迁移为 C# 动态创建视觉。
- 不把 hover、pressed、selected、expanded、loading、filter、popup open 等运行时状态写入 Token。
- Browser 或平台特化主题必须保持同一 API 的语义一致。

Token 来源：

Notification Token 只表达组件级视觉变量，例如尺寸、间距、颜色、圆角、阴影、图标尺寸和弹层边界。Token 不承载运行时选择、展开、加载、错误、上传任务、过滤条件或业务状态。

当前 Token scope：

- `NotificationToken`，scope id 为 `Notification`，源码位于 `src/AtomUI.Desktop.Controls/Notifications/NotificationToken.cs`。

## AOT 与裁剪注意事项

资源和 AOT 约束：

- 不通过运行时反射扫描 public API、Token 或 Gallery 示例数据。
- 不把可静态声明的模板结构迁移到 C# 动态创建。
- 异步加载、上传、弹层和窗口生命周期必须能取消或释放。
- 缓存对象必须与控件、窗口、弹层或数据 owner 生命周期一致。
- Source generator 生成文件不手工编辑；需要修改时改输入源或 generator。

性能边界：

- 控件应优先复用 Avalonia 原生虚拟化、模板绑定和资源系统。
- 避免为每次状态变化创建不必要的视觉对象、订阅或动画对象。
- 大集合控件必须保证 container recycle 后不会泄漏旧 item 状态。

## 源码索引

主要源码文件：

- `src/AtomUI.Desktop.Controls/Notifications/INotification.cs`
- `src/AtomUI.Desktop.Controls/Notifications/INotificationManager.cs`
- `src/AtomUI.Desktop.Controls/Notifications/Notification.cs`
- `src/AtomUI.Desktop.Controls/Notifications/NotificationCard.cs`
- `src/AtomUI.Desktop.Controls/Notifications/NotificationMotions.cs`
- `src/AtomUI.Desktop.Controls/Notifications/NotificationPosition.cs`
- `src/AtomUI.Desktop.Controls/Notifications/NotificationProgressBar.cs`
- `src/AtomUI.Desktop.Controls/Notifications/NotificationPseudoClass.cs`
- `src/AtomUI.Desktop.Controls/Notifications/NotificationToken.cs`
- `src/AtomUI.Desktop.Controls/Notifications/NotificationType.cs`
- `src/AtomUI.Desktop.Controls/Notifications/Themes/NotificationCardTheme.axaml`
- `src/AtomUI.Desktop.Controls/Notifications/Themes/NotificationProgressBarTheme.axaml`
- `src/AtomUI.Desktop.Controls/Notifications/Themes/WindowNotificationManagerTheme.axaml`
- `src/AtomUI.Desktop.Controls/Notifications/Utils/NotificationProgressBarVisibleConverter.cs`
- `src/AtomUI.Desktop.Controls/Notifications/WindowNotificationManager.cs`
- `src/AtomUI.Core/MotionScene/MotionExecutionState.cs`

职责边界：

- 控件主文件保留 public/protected API、Avalonia 属性注册、事件和主要生命周期入口。
- Theme 文件负责静态视觉结构、template part、selector 和资源绑定。
- Token 文件只提供组件视觉变量，不保存实例状态。
- Gallery 文件只展示用法和示例，不作为运行时逻辑 owner。

## 相关文档

- 源设计文档：`docs/controls/desktop/feedback/notification/overview.md`
- 实现文档：`docs/controls/desktop/feedback/notification/implementation.md`
- Token 文档：`docs/controls/desktop/feedback/notification/token.md`
- 变更记录：`docs/controls/desktop/feedback/notification/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/popup-confirm/index-cn.md

# PopupConfirm

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

PopupConfirm 是 AtomUI 桌面控件体系中的气泡确认框控件，用于在目标元素附近完成轻量确认。

PopupConfirm 不负责完整 Dialog、复杂表单或菜单系统。这些职责应由业务层、组合控件或更专用的 AtomUI 控件承担。

主要源码入口：

- `src/AtomUI.Desktop.Controls/PopupConfirm`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/Feedback/PopupConfirm` |
| 状态 | Stable |

## 何时使用

PopupConfirm 的设计语言围绕控件职责、可观察状态和主题契约组织，而不是围绕模板节点组织。

| 维度 | 含义 | PopupConfirm 中的表达 |
| --- | --- | --- |
| 产品语义 | 控件在界面中承担的稳定职责。 | PopupConfirm 是 AtomUI 桌面控件体系中的气泡确认框控件，用于在目标元素附近完成轻量确认。 |
| 内容承载 | 用户数据、展示内容、集合项或操作入口如何进入控件。 | `CancelText`、`ConfirmContent`、`ConfirmContentTemplate`、`Icon`、`OkText`、`Title`。 |
| 状态反馈 | public API、内部状态和伪类如何形成用户可感知反馈。 | input/value。 |
| 主题语义 | ControlTheme、SharedToken、控件 Token 和模板绑定如何表达视觉。 | PopupConfirm Token + ControlTheme。 |

## 公共 API

PopupConfirm 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。

核心 public surface 按语义分组维护：

| 契约组 | 代表成员 | 维护含义 |
| --- | --- | --- |
| 内容与数据 | `CancelText`、`ConfirmContent`、`ConfirmContentTemplate`、`Icon`、`OkText`、`Title` | 定义控件展示内容、输入数据、模板或业务对象入口。 |
| 交互与状态 | `ConfirmStatus`、`IsShowCancelButton` | 表达用户可观察状态、可用性、清除、加载或反馈语义。 |
| 其他稳定入口 | `OkButtonType` | 保留为 public surface，变更前需确认 Gallery 和用户 XAML 依赖。 |

稳定事件包括 `Cancelled`、`Confirmed`、`PopupClick`。事件触发顺序属于兼容契约，不能因内部状态重排而改变。

主要公开类型与枚举：

- 类型：`PopupConfirm`、`PopupConfirmClickEventArgs`、`PopupConfirmContainer`、`PopupConfirmFlyout`、`PopupConfirmPseudoClass`。
- 枚举：`PopupConfirmStatus`。

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_ButtonLayout` | `?` | 承载用户触发入口、导航或关闭动作。 |
| `PART_CancelButton` | `?` | 承载用户触发入口、导航或关闭动作。 |
| `PART_Content` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_ContentPresenter` | `?` | 展示用户内容、文本、图标或模板化数据。 |
| `PART_IconPresenter` | `?` | 展示用户内容、文本、图标或模板化数据。 |
| `PART_MainLayout` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_OkButton` | `?` | 承载用户触发入口、导航或关闭动作。 |
| `PART_Title` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |

控件专属或内部伪类包括 `EmptyContent=:empty-content`、`PopupConfirmPseudoClass.EmptyContent`。这些伪类属于主题 selector 可观察契约，不能在未同步主题和 Gallery 的情况下重命名或删除。

## 事件与命令

PopupConfirm 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。
稳定事件包括 `Cancelled`、`Confirmed`、`PopupClick`。事件触发顺序属于兼容契约，不能因内部状态重排而改变。
- 类型：`PopupConfirm`、`PopupConfirmClickEventArgs`、`PopupConfirmContainer`、`PopupConfirmFlyout`、`PopupConfirmPseudoClass`。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/Feedback/PopupConfirm/Views/PopupConfirmShowCase.axaml:36`

Gallery key：`ExamplesContent` / item `0`

```axaml
<atom:PopupConfirm
    Title="删除任务"
    ConfirmContent="确定要删除这个任务吗？"
    ShouldUseOverlayPopup="False"
    OkText="确定"
    CancelText="取消"
    Placement="Top"
    IsArrowVisible="True">
    <atom:Button ButtonType="Default" IsDanger="True" Content="删除" />
</atom:PopupConfirm>
```

### 本地化文本

来源：`controlgallery/AtomUIGallery/ShowCases/Feedback/PopupConfirm/Views/PopupConfirmShowCase.axaml:56`

Gallery key：`ExamplesContent` / item `1`

```axaml
<atom:PopupConfirm
    ShouldUseOverlayPopup="False"
    Title="删除任务"
    ConfirmContent="确定要删除这个任务吗？">
    <atom:Button ButtonType="Default" IsDanger="True" Content="删除" />
</atom:PopupConfirm>
```

## 状态模型

PopupConfirm 的状态流按以下路径收敛：

```text
Public API / inherited command / item source / user input
  -> 控件实例状态
  -> effective state / pseudo-class / template property
  -> ControlTheme selector / presenter / renderer
  -> Gallery 可观察行为
```

状态维护规则：

- Disabled 或不可交互状态优先屏蔽 pointer、keyboard、motion 和提交类反馈。
- input/value 状态由控件实例或明确的数据 owner 推导，不能在 template part 之间双向竞争。
- 模板重套用时必须把 public API 对应状态回放到新的 part、伪类和主题变量。
- 集合、弹层、异步、动效或窗口相关状态必须能处理 reset、close、cancel、detach 和 owner 释放。

## 主题与 Design Token

PopupConfirm 的视觉模型由控件模板、ControlTheme、SharedToken 和必要的控件 Token 共同构成。

| 主题文件 | 职责 |
| --- | --- |
| `PopupConfirmContainerTheme.axaml` | 定义弹层、窗口或 overlay 宿主视觉。 |
| `PopupConfirmTheme.axaml` | 定义弹层、窗口或 overlay 宿主视觉。 |

PopupConfirm 使用 `PopupConfirmToken` 作为控件 Token scope。Token 只表达组件视觉语义，不承载 input/value 运行时状态。

主题维护规则：

- 不删除或重命名已经稳定的 ControlTheme key、template part、伪类和资源 key。
- 不把可由 AXAML 表达的模板状态迁移为 C# 动态创建视觉。
- 不把 hover、pressed、selected、expanded、loading、filter、popup open 等运行时状态写入 Token。
- Browser 或平台特化主题必须保持同一 API 的语义一致。

Token 来源：

PopupConfirm Token 只表达组件级视觉变量，例如尺寸、间距、颜色、圆角、阴影、图标尺寸和弹层边界。Token 不承载运行时选择、展开、加载、错误、上传任务、过滤条件或业务状态。

当前 Token scope：

- `PopupConfirmToken`，scope id 为 `PopupConfirm`，源码位于 `src/AtomUI.Desktop.Controls/PopupConfirm/PopupConfirmToken.cs`。

## AOT 与裁剪注意事项

资源和 AOT 约束：

- 不通过运行时反射扫描 public API、Token 或 Gallery 示例数据。
- 不把可静态声明的模板结构迁移到 C# 动态创建。
- 异步加载、上传、弹层和窗口生命周期必须能取消或释放。
- 缓存对象必须与控件、窗口、弹层或数据 owner 生命周期一致。
- Source generator 生成文件不手工编辑；需要修改时改输入源或 generator。

性能边界：

- 控件应优先复用 Avalonia 原生虚拟化、模板绑定和资源系统。
- 避免为每次状态变化创建不必要的视觉对象、订阅或动画对象。
- 大集合控件必须保证 container recycle 后不会泄漏旧 item 状态。

## 源码索引

主要源码文件：

- `src/AtomUI.Desktop.Controls/PopupConfirm/PopupConfirm.cs`
- `src/AtomUI.Desktop.Controls/PopupConfirm/PopupConfirmContainer.cs`
- `src/AtomUI.Desktop.Controls/PopupConfirm/PopupConfirmFlyout.cs`
- `src/AtomUI.Desktop.Controls/PopupConfirm/PopupConfirmPseudoClass.cs`
- `src/AtomUI.Desktop.Controls/PopupConfirm/PopupConfirmToken.cs`
- `src/AtomUI.Desktop.Controls/PopupConfirm/Themes/PopupConfirmContainerTheme.axaml`
- `src/AtomUI.Desktop.Controls/PopupConfirm/Themes/PopupConfirmTheme.axaml`

职责边界：

- 控件主文件保留 public/protected API、Avalonia 属性注册、事件和主要生命周期入口。
- Theme 文件负责静态视觉结构、template part、selector 和资源绑定。
- Token 文件只提供组件视觉变量，不保存实例状态。
- Gallery 文件只展示用法和示例，不作为运行时逻辑 owner。

## 相关文档

- 源设计文档：`docs/controls/desktop/feedback/popup-confirm/overview.md`
- 实现文档：`docs/controls/desktop/feedback/popup-confirm/implementation.md`
- Token 文档：`docs/controls/desktop/feedback/popup-confirm/token.md`
- 变更记录：`docs/controls/desktop/feedback/popup-confirm/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/progress-bar/index-cn.md

# ProgressBar

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

ProgressBar 是 AtomUI 桌面反馈体系中的进度展示控件，用于表达任务完成比例、状态结果、成功阈值、分段进度和环形进度。它继承 Avalonia `RangeBase` 的 `Minimum`、`Maximum` 和 `Value` 语义，并扩展 `Status`、`SizeType`、成功段、文字格式、线帽、方向、分段、圆形和仪表盘模型。

ProgressBar 的职责是展示已经由外部业务计算好的进度值。它不负责启动任务、取消任务、调度异步流程、估算剩余时间、处理下载队列或持有业务任务状态。需要任务生命周期、错误重试或进度聚合时，应由业务 ViewModel 或服务层维护，并把当前值写入 ProgressBar。

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/Feedback/ProgressBar` |
| 状态 | Stable |

## 何时使用

ProgressBar 的设计语言来自轨道、已完成段、状态色、状态图标和百分比文本的组合。

| 维度 | 含义 | 典型表达 |
| --- | --- | --- |
| 进度范围 | 在有限区间中展示完成比例。 | `Minimum`、`Maximum`、`Value`、`Percentage`。 |
| 视觉形态 | 用不同几何表达进度。 | `ProgressBar`、`StepsProgressBar`、`CircleProgress`、`DashboardProgress`。 |
| 进度方向 | 线形和步骤进度支持水平或垂直方向。 | `Orientation`、`:horizontal`、`:vertical`。 |
| 状态结果 | 表达普通、活动、成功、异常状态。 | `Status`、`:completed`、状态图标。 |
| 成功阈值 | 在整体进度中标记成功段。 | `SuccessThreshold`、`SuccessStrokeBrush`。 |
| 信息展示 | 显示百分比或自定义格式文本。 | `IsProgressInfoVisible`、`ProgressTextFormat`、`PercentPosition`。 |
| 尺寸语义 | 根据大中小规格或显式厚度计算视觉尺寸。 | `SizeType`、`IndicatorThickness`、`ChunkWidth`、`ChunkHeight`。 |
| 动效 | 进度值和颜色变化可以带 transition。 | `IsMotionEnabled`。 |

ProgressBar 的主视觉必须是进度本身。百分比文本、状态图标和成功阈值是辅助反馈，不应改变 `Value` 的业务含义。

## 公共 API

ProgressBar 家族由一个共享基类模型和四个具体控件组成。

| 控件 | 基类 | 语义 |
| --- | --- | --- |
| `ProgressBar` | `AbstractGeneralProgressBar` | 普通线形进度条，支持水平、垂直、内外百分比文本和成功段。 |
| `StepsProgressBar` | `AbstractGeneralStepsProgressBar` | 线形分段进度条，以固定 chunk 表达离散进度。 |
| `CircleProgress` | `AbstractGeneralCircleProgress` | 圆形进度条，支持普通圆弧和圆形分段。 |
| `DashboardProgress` | `AbstractGeneralDashboardProgress` | 仪表盘进度条，支持缺口方向、缺口角度和分段。 |

共享 RangeBase API：

| API | 类型 | 语义 |
| --- | --- | --- |
| `Minimum` | `double` | 进度范围最小值。 |
| `Maximum` | `double` | 进度范围最大值。 |
| `Value` | `double` | 当前进度值。 |
| `Percentage` | `double` | 只读百分比，按 `(Value - Minimum) / (Maximum - Minimum) * 100` 计算。 |

共享 Progress API：

| API | 类型 | 语义 |
| --- | --- | --- |
| `IsIndeterminate` | `bool` | 是否处于不确定进度状态，并同步 `:indeterminate` 伪类。 |
| `IsProgressInfoVisible` | `bool` | 是否显示百分比文本或状态图标区域。 |
| `ProgressTextFormat` | `string` | 百分比文本格式，默认 `{0:0}%`。 |
| `StrokeBrush` | `IBrush?` | 已完成段画刷。 |
| `TrailColor` | `Color?` | 剩余轨道颜色，设置后覆盖默认 `GrooveBrush`。 |
| `StrokeLineCap` | `PenLineCap` | 已完成段端点形态，默认 `Round`。 |
| `SizeType` | `SizeType` | 大中小规格，默认 `Large`。 |
| `Status` | `ProgressStatus` | 普通、成功、异常、活动状态。 |
| `IndicatorThickness` | `double` | 显式指示器厚度；`NaN` 时由尺寸规格计算。 |
| `SuccessThreshold` | `double` | 成功段阈值；`NaN` 时不绘制成功段。 |
| `SuccessStrokeBrush` | `IBrush?` | 成功段画刷。 |
| `IsMotionEnabled` | `bool` | 是否启用进度值和颜色 transition。 |
| `ExceptionCompletedIcon` | `PathIcon?` | 异常状态图标。 |
| `SuccessCompletedIcon` | `PathIcon?` | 成功状态图标。 |

线形扩展 API：

| API | 控件 | 类型 | 语义 |
| --- | --- | --- | --- |
| `Orientation` | `ProgressBar`、`StepsProgressBar` | `Orientation` | 水平或垂直布局。 |
| `PercentPosition` | `ProgressBar` | `PercentPosition` | 百分比文本在进度条内外及起点、中间、终点的定位。 |
| `PercentPosition` | `StepsProgressBar` | `LinePercentAlignment` | 步骤进度百分比文本的起点、中间、终点定位。 |
| `Steps` | `StepsProgressBar` | `int` | 步骤数量，最小为 1。 |
| `StepsStrokeBrush` | `StepsProgressBar` | `List<IBrush>?` | 每个已完成步骤的自定义画刷。 |
| `ChunkWidth` | `StepsProgressBar` | `double` | 步骤块宽度，`NaN` 时由 `SizeType` 计算。 |
| `ChunkHeight` | `StepsProgressBar` | `double` | 步骤块高度，`NaN` 时由 `IndicatorThickness` / `StrokeThickness` 计算。 |

圆形和仪表盘扩展 API：

| API | 控件 | 类型 | 语义 |
| --- | --- | --- | --- |
| `StepCount` | `CircleProgress`、`DashboardProgress` | `int` | 圆弧分段数量，0 表示普通连续圆弧。 |
| `StepGap` | `CircleProgress`、`DashboardProgress` | `double` | 分段间隔角度，默认 2。 |
| `DashboardGapPosition` | `DashboardProgress` | `DashboardGapPosition` | 仪表盘缺口方向，默认 `Bottom`。 |
| `GapDegree` | `DashboardProgress` | `double` | 仪表盘缺口角度，范围 `[0, 295]`，默认 75。 |

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_LayoutTransformControl` | `LayoutTransformControl` | 承载百分比文本并在垂直内嵌模式中旋转。 |
| `PART_PercentageLabel` | `Label` | 显示格式化后的进度文本。 |
| `PART_ExceptionCompletedIconPresenter` | `IconPresenter` | 显示异常状态图标。 |
| `PART_SuccessCompletedIconPresenter` | `IconPresenter` | 显示成功状态图标。 |

稳定伪类：

| 伪类 | 语义 |
| --- | --- |
| `:indeterminate` | `IsIndeterminate=true`。 |
| `:completed` | `Value` 等于 `Maximum`。 |
| `:horizontal` | 线形进度为水平布局。 |
| `:vertical` | 线形进度为垂直布局。 |
| `:labelinner` | `ProgressBar.PercentPosition.IsInner=true`。 |
| `:labelinner-start` | 内嵌百分比位于起点。 |
| `:labelinner-center` | 内嵌百分比位于中间。 |
| `:labelinner-end` | 内嵌百分比位于终点。 |

ProgressBar 当前不实现 `ICustomizeSizeTypeAware`。自定义尺寸通过 `IndicatorThickness`、`Width`、`Height`、`ChunkWidth` 和 `ChunkHeight` 等现有属性表达。

## 事件与命令

ProgressBar 的事件与命令以控件文档、源码 public surface 和 Avalonia 基类契约为准；生成器不从源码发明额外事件。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 进度条

来源：`controlgallery/AtomUIGallery/ShowCases/Feedback/ProgressBar/Views/ProgressBarShowCase.axaml:46`

Gallery key：`ExamplesContent` / item `0`

```axaml
<StackPanel Orientation="Vertical" Spacing="10">
    <atom:ProgressBar Value="30" Minimum="0" Maximum="100" />
    <atom:ProgressBar Value="50" Minimum="0" Maximum="100" />
    <atom:ProgressBar Value="70" Minimum="0" Maximum="100" Status="Exception" />
    <atom:ProgressBar Value="100" Minimum="0" Maximum="100" />
    <atom:ProgressBar Value="50" Minimum="0" Maximum="100" IsProgressInfoVisible="False" />
</StackPanel>
```

### 环形进度条

来源：`controlgallery/AtomUIGallery/ShowCases/Feedback/ProgressBar/Views/ProgressBarShowCase.axaml:64`

Gallery key：`ExamplesContent` / item `1`

```axaml
<WrapPanel Orientation="Horizontal">
    <atom:CircleProgress Value="75" Minimum="0" Maximum="100" />
    <atom:CircleProgress Value="70" Minimum="0" Maximum="100" Status="Exception" />
    <atom:CircleProgress Value="100" Minimum="0" Maximum="100" />
</WrapPanel>
```

### 迷你尺寸进度条

来源：`controlgallery/AtomUIGallery/ShowCases/Feedback/ProgressBar/Views/ProgressBarShowCase.axaml:80`

Gallery key：`ExamplesContent` / item `2`

```axaml
<WrapPanel Orientation="Horizontal" Width="180" HorizontalAlignment="Left">
    <atom:ProgressBar Value="30" Minimum="0" Maximum="100" SizeType="Middle" />
    <atom:ProgressBar Value="50" Minimum="0" Maximum="100" SizeType="Middle" />
    <atom:ProgressBar Value="70" Minimum="0" Maximum="100" Status="Exception" SizeType="Middle" />
    <atom:ProgressBar Value="100" Minimum="0" Maximum="100" SizeType="Middle" />
    <atom:ProgressBar Value="50" Minimum="0" Maximum="100" IsProgressInfoVisible="False" SizeType="Middle" />
</WrapPanel>
```

### 更小的环形进度条

来源：`controlgallery/AtomUIGallery/ShowCases/Feedback/ProgressBar/Views/ProgressBarShowCase.axaml:98`

Gallery key：`ExamplesContent` / item `3`

```axaml
<WrapPanel Orientation="Horizontal">
    <atom:CircleProgress Value="75" Minimum="0" Maximum="100" SizeType="Middle" />
    <atom:CircleProgress Value="70" Minimum="0" Maximum="100" Status="Exception" SizeType="Middle" />
    <atom:CircleProgress Value="100" Minimum="0" Maximum="100" SizeType="Middle" />
</WrapPanel>
```

## 状态模型

基础进度状态流：

```text
Minimum / Maximum / Value
      ↓
CalculateProgressRatio(value)
      ↓
Percentage + Indicator geometry
      ↓
render groove / progress / success segment + extra info
```

状态语义：

- `Percentage` 始终以 `Minimum` 为基准，不直接使用 `Value / Maximum`。
- `Value == Maximum` 时进入 completed 状态，设置 `IsCompleted` 和 `:completed`。
- `Status=Success` 使用成功色和成功图标；`Status=Exception` 使用错误色和异常图标。
- `Status=Normal` 和 `Status=Active` 在完成前使用默认进度色，完成后使用成功色。
- `IsIndeterminate` 只表达不确定进度伪类，不改变 `Value`、`Percentage` 或 RangeBase 值。
- `IsProgressInfoVisible=false` 隐藏文本和状态图标区域，但不影响轨道和进度绘制。
- disabled 状态使用禁用轨道、禁用进度色和禁用文本色。

尺寸状态：

- 线形进度默认根据 `SizeType` 映射到大、中、小三档厚度。
- `IndicatorThickness` 有值时覆盖线形和圆形的计算厚度。
- 圆形进度默认尺寸由 `SizeType` 映射为 `120 / 90 / 60`，显式 `Width` / `Height` 会参与最终尺寸。
- `StepsProgressBar` 使用 `Steps`、chunk 尺寸和固定 chunk 间距计算总尺寸。
- `StepsProgressBar.ChunkHeight` 与 `IndicatorThickness` 保持同步，是现有兼容契约的一部分。

百分比文本：

- `ProgressTextFormat` 格式化 `Percentage`。
- `ProgressBar.PercentPosition.IsInner=true` 时，文本布局跟随已完成进度区域。
- 垂直内嵌文本通过 `LayoutTransformControl` 旋转。
- 非内嵌完成态显示状态图标时，百分比文本隐藏。
- 圆形进度根据可用内圆区域决定百分比文本或状态图标是否可见。

## 主题与 Design Token

ProgressBar 的默认视觉由抽象主题和具体控件主题组合：

| 主题 | 职责 |
| --- | --- |
| `AbstractProgressBarTheme.axaml` | 共享模板入口、默认对齐、动效、状态色、completed / disabled 状态。 |
| `AbstractLineProgressTheme.axaml` | 线形模板、百分比 label、状态图标、线形尺寸和图标尺寸。 |
| `ProgressBarTheme.axaml` | 普通线形控件主题、内嵌百分比伪类、垂直文本旋转、内嵌图标隐藏规则。 |
| `StepsProgressBarTheme.axaml` | 步骤线形控件主题、方向对齐和百分比位置对应图标对齐。 |
| `AbstractCircleProgressTheme.axaml` | 圆形模板、居中文本和居中状态图标。 |
| `CircleProgressTheme.axaml` | 圆形进度具体主题入口。 |
| `DashboardProgressTheme.axaml` | 仪表盘进度具体主题入口。 |

视觉绘制由控件直接渲染完成：

- 线形进度绘制 groove、已完成段和可选成功段。
- 步骤进度绘制固定数量 chunk，再按 `Percentage` 绘制已完成 chunk。
- 圆形进度绘制完整圆或分段圆弧。
- 仪表盘进度绘制带缺口的圆弧或分段圆弧。
- 状态图标和百分比文本通过模板 part 布局，不参与进度几何绘制。

ProgressBarToken 提供默认进度色、剩余轨道色、圆形文字和图标最小尺寸、线形文本间距、线形内部 padding 和线形图标尺寸。状态色主要来自 SharedToken。

Token 来源：

ProgressBarToken 是 ProgressBar 家族的控件级 Token scope。它定义默认进度色、剩余轨道色、圆形文本和图标最小尺寸、线形状态图标尺寸、线形额外信息间距和线形内部 padding。

ProgressBarToken 不承载以下状态：

- `Value`、`Minimum`、`Maximum`、`Percentage` 等实例进度值。
- `Status`、`IsIndeterminate`、completed、disabled 等运行时状态。
- `PercentPosition`、`Orientation`、`StepCount`、`Steps`、`DashboardGapPosition` 等布局或形态状态。
- `StrokeBrush`、`SuccessStrokeBrush`、`TrailColor` 等实例画刷覆盖。
- `IndicatorThickness`、`ChunkWidth`、`ChunkHeight`、显式 `Width` / `Height` 等实例尺寸覆盖。

这些状态分别由 public API、internal effective state、主题 selector 和直接绘制逻辑处理。

## AOT 与裁剪注意事项

ProgressBar 不依赖运行时反射、字符串 binding、动态类型扫描或 C# relay binding。模板状态主要通过 template part、`TemplateBinding`、style selector 和 direct render 完成。

资源边界：

- 默认颜色来自 `ProgressBarToken` 和 SharedToken。
- `TrailColor` 会写入 `GrooveBrush` local value；清空后恢复主题资源。
- 状态图标由 `IconPresenter` 承载，默认图标在模板应用后以 template priority 设置。

性能边界：

- 线形、步骤、圆形和仪表盘均使用直接绘制，避免创建大量视觉子节点。
- 圆形和仪表盘使用 cached pen helper 更新 pen。
- 文本尺寸通过 `_extraInfoSize` 缓存，尺寸、状态或文本相关属性变化时刷新。
- 动效只绑定 `Value`、`StrokeBrush` 和 `Foreground` transition。

AOT 边界：

- 不新增反射读取 public API、token 或 template part。
- 新增 Token 必须走 `ProgressBarToken` 和 generator 支持的 token kind。
- API 与 Token 契约应在控件文档、源码 public surface、Token 类型或生成数据中维护，不依赖运行时扫描。

## 源码索引

共享实现位于 `src/AtomUI.Controls/ProgressBar/`：

- `ProgressBarEnums.cs`：`ProgressStatus`、`DashboardGapPosition`、`PercentPosition`、`LinePercentAlignment`。
- `ProgressBarPseudoClass.cs`：ProgressBar 家族使用的伪类常量。
- `AbstractProgressBar.cs`：RangeBase 集成、共享 public API、百分比计算、状态伪类、模板 part、动效入口和直接渲染调度。
- `AbstractLineProgress.cs`：线形方向、线形 extra info 测量、方向伪类、线形默认状态图标。
- `AbstractGeneralProgressBar.cs`：普通线形进度的测量、布局、成功段、内外百分比位置和百分比文字颜色。
- `AbstractGeneralStepsProgressBar.cs`：线形步骤进度的 chunk 尺寸、步骤布局、步骤绘制和百分比文本布局。
- `AbstractCircleProgress.cs`：圆形尺寸、圆形文本和状态图标布局、圆形 thickness 计算。
- `AbstractGeneralCircleProgress.cs`：圆形 groove、连续圆弧、分段圆弧和成功弧段绘制。
- `AbstractGeneralDashboardProgress.cs`：仪表盘缺口角度、连续仪表盘弧、分段仪表盘弧和成功弧段绘制。

桌面具体控件位于 `src/AtomUI.Desktop.Controls/ProgressBar/`：

- `ProgressBar.cs`：注册 `ProgressBarToken` scope 的普通线形控件入口。
- `StepsProgressBar.cs`：注册 token scope 的步骤线形控件入口。
- `CircleProgress.cs`：注册 token scope 的圆形控件入口。
- `DashboardProgress.cs`：注册 token scope 的仪表盘控件入口。
- `ProgressBarToken.cs`：控件级 Token 默认值计算。
- `Themes/*.axaml`：共享主题、线形主题、圆形主题和具体控件主题。

Gallery 示例位于 `controlgallery/AtomUIGallery/ShowCases/Feedback/ProgressBar/`。

## 相关文档

- 源设计文档：`docs/controls/desktop/feedback/progress-bar/overview.md`
- 实现文档：`docs/controls/desktop/feedback/progress-bar/implementation.md`
- Token 文档：`docs/controls/desktop/feedback/progress-bar/token.md`
- 变更记录：`docs/controls/desktop/feedback/progress-bar/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/result/index-cn.md

# Result

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

Result 是 AtomUI 桌面控件体系中的结果页控件，用于展示成功、失败、警告和信息结果状态。

Result 不负责表单验证器、通知系统或异常处理框架。这些职责应由业务层、组合控件或更专用的 AtomUI 控件承担。

主要源码入口：

- `src/AtomUI.Controls/Result`
- `src/AtomUI.Desktop.Controls/Result`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/Feedback/Result` |
| 状态 | Stable |

## 何时使用

Result 的设计语言围绕控件职责、可观察状态和主题契约组织，而不是围绕模板节点组织。

| 维度 | 含义 | Result 中的表达 |
| --- | --- | --- |
| 产品语义 | 控件在界面中承担的稳定职责。 | Result 是 AtomUI 桌面控件体系中的结果页控件，用于展示成功、失败、警告和信息结果状态。 |
| 内容承载 | 用户数据、展示内容、集合项或操作入口如何进入控件。 | `ExtraTemplate`、`Header`、`HeaderFontSize`、`HeaderTemplate`、`Icon`、`SubHeader`、`SubHeaderFontSize`、`SubHeaderTemplate`。 |
| 状态反馈 | public API、内部状态和伪类如何形成用户可感知反馈。 | visual option。 |
| 主题语义 | ControlTheme、SharedToken、控件 Token 和模板绑定如何表达视觉。 | Result Token + ControlTheme。 |

## 公共 API

Result 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。

核心 public surface 按语义分组维护：

| 契约组 | 代表成员 | 维护含义 |
| --- | --- | --- |
| 内容与数据 | `ExtraTemplate`、`Header`、`HeaderFontSize`、`HeaderTemplate`、`Icon`、`SubHeader`、`SubHeaderFontSize`、`SubHeaderTemplate` | 定义控件展示内容、输入数据、模板或业务对象入口。 |
| 交互与状态 | `Status` | 表达用户可观察状态、可用性、清除、加载或反馈语义。 |
| 其他稳定入口 | `Extra` | 保留为 public surface，变更前需确认 Gallery 和用户 XAML 依赖。 |

当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。

主要公开类型与枚举：

- 类型：`AbstractResult`、`Result`、`ResultIndicator`。
- 枚举：`ResultStatus`。

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_ErrorCodeImage` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_StatusIconPresenter` | `?` | 展示用户内容、文本、图标或模板化数据。 |

当前未抽取到控件专属伪类；主题主要依赖 Avalonia 标准伪类、模板绑定和内部 StyledProperty。

## 事件与命令

Result 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。
当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 成功

来源：`controlgallery/AtomUIGallery/ShowCases/Feedback/Result/Views/ResultShowCase.axaml:37`

Gallery key：`ExamplesContent` / item `0`

```axaml
<atom:Result Status="Success"
             Header="云服务器 ECS 购买成功！"
             SubHeader="订单号：2017182818828182881。云服务器配置需要 1-5 分钟，请稍候。">
    <atom:Result.Extra>
        <StackPanel Orientation="Horizontal" Spacing="10">
            <atom:Button ButtonType="Primary" Content="进入控制台" />
            <atom:Button Content="再次购买" />
        </StackPanel>
    </atom:Result.Extra>
</atom:Result>
```

### 信息

来源：`controlgallery/AtomUIGallery/ShowCases/Feedback/Result/Views/ResultShowCase.axaml:58`

Gallery key：`ExamplesContent` / item `1`

```axaml
<atom:Result Status="Info"
             Header="你的操作已执行。">
    <atom:Result.Extra>
        <atom:Button ButtonType="Primary" Content="进入控制台" />
    </atom:Result.Extra>
</atom:Result>
```

### 警告

来源：`controlgallery/AtomUIGallery/ShowCases/Feedback/Result/Views/ResultShowCase.axaml:75`

Gallery key：`ExamplesContent` / item `2`

```axaml
<atom:Result Status="Warning"
             Header="你的操作存在一些问题。">
    <atom:Result.Extra>
        <atom:Button ButtonType="Primary" Content="进入控制台" />
    </atom:Result.Extra>
</atom:Result>
```

### 403

来源：`controlgallery/AtomUIGallery/ShowCases/Feedback/Result/Views/ResultShowCase.axaml:92`

Gallery key：`ExamplesContent` / item `3`

```axaml
<atom:Result Status="ErrorCode403"
             Header="403"
             SubHeader="抱歉，你无权访问此页面。">
    <atom:Result.Extra>
        <atom:Button ButtonType="Primary" Content="返回首页" />
    </atom:Result.Extra>
</atom:Result>
```

## 状态模型

Result 的状态流按以下路径收敛：

```text
Public API / inherited command / item source / user input
  -> 控件实例状态
  -> effective state / pseudo-class / template property
  -> ControlTheme selector / presenter / renderer
  -> Gallery 可观察行为
```

状态维护规则：

- Disabled 或不可交互状态优先屏蔽 pointer、keyboard、motion 和提交类反馈。
- visual option 状态由控件实例或明确的数据 owner 推导，不能在 template part 之间双向竞争。
- 模板重套用时必须把 public API 对应状态回放到新的 part、伪类和主题变量。
- 集合、弹层、异步、动效或窗口相关状态必须能处理 reset、close、cancel、detach 和 owner 释放。

## 主题与 Design Token

Result 的视觉模型由控件模板、ControlTheme、SharedToken 和必要的控件 Token 共同构成。

| 主题文件 | 职责 |
| --- | --- |
| `ResultTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |

Result 使用 `ResultToken` 作为控件 Token scope。Token 只表达组件视觉语义，不承载 visual option 运行时状态。

主题维护规则：

- 不删除或重命名已经稳定的 ControlTheme key、template part、伪类和资源 key。
- 不把可由 AXAML 表达的模板状态迁移为 C# 动态创建视觉。
- 不把 hover、pressed、selected、expanded、loading、filter、popup open 等运行时状态写入 Token。
- Browser 或平台特化主题必须保持同一 API 的语义一致。

Token 来源：

Result Token 只表达组件级视觉变量，例如尺寸、间距、颜色、圆角、阴影、图标尺寸和弹层边界。Token 不承载运行时选择、展开、加载、错误、上传任务、过滤条件或业务状态。

当前 Token scope：

- `ResultToken`，scope id 为 `Result`，源码位于 `src/AtomUI.Desktop.Controls/Result/ResultToken.cs`。

## AOT 与裁剪注意事项

资源和 AOT 约束：

- 不通过运行时反射扫描 public API、Token 或 Gallery 示例数据。
- 不把可静态声明的模板结构迁移到 C# 动态创建。
- 异步加载、上传、弹层和窗口生命周期必须能取消或释放。
- 缓存对象必须与控件、窗口、弹层或数据 owner 生命周期一致。
- Source generator 生成文件不手工编辑；需要修改时改输入源或 generator。

性能边界：

- 控件应优先复用 Avalonia 原生虚拟化、模板绑定和资源系统。
- 避免为每次状态变化创建不必要的视觉对象、订阅或动画对象。
- 大集合控件必须保证 container recycle 后不会泄漏旧 item 状态。

## 源码索引

主要源码文件：

- `src/AtomUI.Controls/Result/AbstractResult.cs`
- `src/AtomUI.Controls/Result/NoFoundImage.cs`
- `src/AtomUI.Controls/Result/ResultStatus.cs`
- `src/AtomUI.Controls/Result/ServerErrorImage.cs`
- `src/AtomUI.Controls/Result/UnauthorizedImage.cs`
- `src/AtomUI.Desktop.Controls/Result/Result.cs`
- `src/AtomUI.Desktop.Controls/Result/ResultToken.cs`
- `src/AtomUI.Desktop.Controls/Result/Themes/ResultTheme.axaml`

职责边界：

- 控件主文件保留 public/protected API、Avalonia 属性注册、事件和主要生命周期入口。
- Theme 文件负责静态视觉结构、template part、selector 和资源绑定。
- Token 文件只提供组件视觉变量，不保存实例状态。
- Gallery 文件只展示用法和示例，不作为运行时逻辑 owner。

## 相关文档

- 源设计文档：`docs/controls/desktop/feedback/result/overview.md`
- 实现文档：`docs/controls/desktop/feedback/result/implementation.md`
- Token 文档：`docs/controls/desktop/feedback/result/token.md`
- 变更记录：`docs/controls/desktop/feedback/result/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/skeleton/index-cn.md

# Skeleton

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

Skeleton 是 AtomUI 桌面控件体系中的骨架屏控件，用于在内容加载前展示结构占位。

Skeleton 不负责 Spin 加载器、真实数据列表或动画演示控件。这些职责应由业务层、组合控件或更专用的 AtomUI 控件承担。

主要源码入口：

- `src/AtomUI.Desktop.Controls/Skeleton`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/Feedback/Skeleton` |
| 状态 | Stable |

## 何时使用

Skeleton 的设计语言围绕控件职责、可观察状态和主题契约组织，而不是围绕模板节点组织。

| 维度 | 含义 | Skeleton 中的表达 |
| --- | --- | --- |
| 产品语义 | 控件在界面中承担的稳定职责。 | Skeleton 是 AtomUI 桌面控件体系中的骨架屏控件，用于在内容加载前展示结构占位。 |
| 内容承载 | 用户数据、展示内容、集合项或操作入口如何进入控件。 | `AvatarShape`、`AvatarSize`、`AvatarSizeType`、`Content`、`ContentTemplate`、`HorizontalContentAlignment`、`IsShowAvatar`、`IsShowTitle` 等 12 项。 |
| 状态反馈 | public API、内部状态和伪类如何形成用户可感知反馈。 | selection/checked/active、loading/async、motion、visual option。 |
| 主题语义 | ControlTheme、SharedToken、控件 Token 和模板绑定如何表达视觉。 | Skeleton Token + ControlTheme。 |

## 公共 API

Skeleton 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。

核心 public surface 按语义分组维护：

| 契约组 | 代表成员 | 维护含义 |
| --- | --- | --- |
| 内容与数据 | `AvatarShape`、`AvatarSize`、`AvatarSizeType`、`Content`、`ContentTemplate`、`HorizontalContentAlignment`、`IsShowAvatar`、`IsShowTitle`、`ParagraphRows`、`Rows` 等 12 项 | 定义控件展示内容、输入数据、模板或业务对象入口。 |
| 选择与集合 | `IsActive` | 维护选择、展开、过滤、分页、分组或集合状态。 |
| 交互与状态 | `IsBlock`、`IsLoading`、`IsRound`、`IsShowParagraph` | 表达用户可观察状态、可用性、清除、加载或反馈语义。 |
| 视觉与布局 | `LastLineWidth`、`LineWidth`、`ParagraphLastLineWidth`、`Shape`、`Size`、`SizeType` | 影响尺寸、位置、颜色、形状、密度和模板视觉变量。 |
| 动效与异步 | `MotionDuration`、`MotionEasingCurve` | 约束动效开关、异步加载、播放速度、超时和任务边界。 |

当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。

主要公开类型与枚举：

- 类型：`AbstractSkeleton`、`Skeleton`、`SkeletonAvatar`、`SkeletonButton`、`SkeletonElement`、`SkeletonImage`、`SkeletonInput`、`SkeletonLine`、`SkeletonNode`、`SkeletonParagraph`、`SkeletonTitle`。
- 枚举：`SkeletonButtonShape`。

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_ActiveAnimationLayer` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_Avatar` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_Content` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_ContentLayer` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_LineLayout` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_Paragraph` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_RootLayout` | `?` | 承载根视觉、边框、背景或尺寸基线。 |
| `PART_Title` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |

当前未抽取到控件专属伪类；主题主要依赖 Avalonia 标准伪类、模板绑定和内部 StyledProperty。

## 事件与命令

Skeleton 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。
当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/Feedback/Skeleton/Views/SkeletonShowCase.axaml:37`

Gallery key：`ExamplesContent` / item `0`

```axaml
<atom:Skeleton IsLoading="True"/>
```

### 复杂组合

来源：`controlgallery/AtomUIGallery/ShowCases/Feedback/Skeleton/Views/SkeletonShowCase.axaml:49`

Gallery key：`ExamplesContent` / item `1`

```axaml
<atom:Skeleton IsShowAvatar="True" ParagraphRows="4" IsLoading="True"/>
```

### 动态效果

来源：`controlgallery/AtomUIGallery/ShowCases/Feedback/Skeleton/Views/SkeletonShowCase.axaml:61`

Gallery key：`ExamplesContent` / item `2`

```axaml
<atom:Skeleton IsActive="True" IsLoading="True"/>
```

### 包含子组件

来源：`controlgallery/AtomUIGallery/ShowCases/Feedback/Skeleton/Views/SkeletonShowCase.axaml:147`

Gallery key：`ExamplesContent` / item `4`

```axaml
<StackPanel Orientation="Vertical" Spacing="10">
    <atom:Skeleton IsLoading="{Binding SkeletonLoading}">
        <StackPanel Orientation="Vertical" Spacing="20">
            <atom:TextBlock FontWeight="Bold" Text="AtomUI，一套设计语言" />
            <atom:TextBlock TextWrapping="Wrap" Text="我们提供一系列设计原则、实用模式和高质量设计资源（Sketch 和 Axure），帮助人们高效而优雅地创建产品原型。" />
        </StackPanel>
    </atom:Skeleton>
    <atom:Button IsEnabled="{Binding SkeletonLoading, Converter={x:Static BoolConverters.Not}}"
                 Click="HandleLoadingButtonClicked" Content="显示骨架屏" />
</StackPanel>
```

## 状态模型

Skeleton 的状态流按以下路径收敛：

```text
Public API / inherited command / item source / user input
  -> 控件实例状态
  -> effective state / pseudo-class / template property
  -> ControlTheme selector / presenter / renderer
  -> Gallery 可观察行为
```

状态维护规则：

- Disabled 或不可交互状态优先屏蔽 pointer、keyboard、motion 和提交类反馈。
- selection/checked/active、loading/async、motion、visual option 状态由控件实例或明确的数据 owner 推导，不能在 template part 之间双向竞争。
- 模板重套用时必须把 public API 对应状态回放到新的 part、伪类和主题变量。
- 集合、弹层、异步、动效或窗口相关状态必须能处理 reset、close、cancel、detach 和 owner 释放。

## 主题与 Design Token

Skeleton 的视觉模型由控件模板、ControlTheme、SharedToken 和必要的控件 Token 共同构成。

| 主题文件 | 职责 |
| --- | --- |
| `AbstractSkeletonTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `SkeletonAvatarTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `SkeletonButtonTheme.axaml` | 定义局部操作入口、按钮或 handle 的状态视觉。 |
| `SkeletonElementTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `SkeletonImageTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `SkeletonInputTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `SkeletonLineTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `SkeletonNodeTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `SkeletonParagraphTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `SkeletonTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |

Skeleton 使用 `SkeletonToken` 作为控件 Token scope。Token 只表达组件视觉语义，不承载 selection/checked/active、loading/async、motion、visual option 运行时状态。

主题维护规则：

- 不删除或重命名已经稳定的 ControlTheme key、template part、伪类和资源 key。
- 不把可由 AXAML 表达的模板状态迁移为 C# 动态创建视觉。
- 不把 hover、pressed、selected、expanded、loading、filter、popup open 等运行时状态写入 Token。
- Browser 或平台特化主题必须保持同一 API 的语义一致。

Token 来源：

Skeleton Token 只表达组件级视觉变量，例如尺寸、间距、颜色、圆角、阴影、图标尺寸和弹层边界。Token 不承载运行时选择、展开、加载、错误、上传任务、过滤条件或业务状态。

当前 Token scope：

- `SkeletonToken`，scope id 为 `Skeleton`，源码位于 `src/AtomUI.Desktop.Controls/Skeleton/SkeletonToken.cs`。

## AOT 与裁剪注意事项

资源和 AOT 约束：

- 不通过运行时反射扫描 public API、Token 或 Gallery 示例数据。
- 不把可静态声明的模板结构迁移到 C# 动态创建。
- 异步加载、上传、弹层和窗口生命周期必须能取消或释放。
- 缓存对象必须与控件、窗口、弹层或数据 owner 生命周期一致。
- Source generator 生成文件不手工编辑；需要修改时改输入源或 generator。

性能边界：

- 控件应优先复用 Avalonia 原生虚拟化、模板绑定和资源系统。
- 避免为每次状态变化创建不必要的视觉对象、订阅或动画对象。
- 隐藏页面中保留的 active Skeleton 不得继续推进 shimmer 或产生 UI 线程 animation tick。
- 大集合控件必须保证 container recycle 后不会泄漏旧 item 状态。

## 源码索引

主要源码文件：

- `src/AtomUI.Desktop.Controls/Skeleton/AbstractSkeleton.cs`
- `src/AtomUI.Desktop.Controls/Skeleton/Skeleton.cs`
- `src/AtomUI.Desktop.Controls/Skeleton/SkeletonAvatar.cs`
- `src/AtomUI.Desktop.Controls/Skeleton/SkeletonButton.cs`
- `src/AtomUI.Desktop.Controls/Skeleton/SkeletonElement.cs`
- `src/AtomUI.Desktop.Controls/Skeleton/SkeletonImage.cs`
- `src/AtomUI.Desktop.Controls/Skeleton/SkeletonInput.cs`
- `src/AtomUI.Desktop.Controls/Skeleton/SkeletonLine.cs`
- `src/AtomUI.Desktop.Controls/Skeleton/SkeletonNode.cs`
- `src/AtomUI.Desktop.Controls/Skeleton/SkeletonParagraph.cs`
- `src/AtomUI.Desktop.Controls/Skeleton/SkeletonTitle.cs`
- `src/AtomUI.Desktop.Controls/Skeleton/SkeletonToken.cs`
- `src/AtomUI.Desktop.Controls/Skeleton/Themes/AbstractSkeletonTheme.axaml`
- `src/AtomUI.Desktop.Controls/Skeleton/Themes/AbstractSkeletonTheme.cs`
- `src/AtomUI.Desktop.Controls/Skeleton/Themes/SkeletonAvatarTheme.axaml`
- `src/AtomUI.Desktop.Controls/Skeleton/Themes/SkeletonButtonTheme.axaml`
- `src/AtomUI.Desktop.Controls/Skeleton/Themes/SkeletonElementTheme.axaml`
- `src/AtomUI.Desktop.Controls/Skeleton/Themes/SkeletonElementTheme.cs`
- `src/AtomUI.Desktop.Controls/Skeleton/Themes/SkeletonImageTheme.axaml`
- `src/AtomUI.Desktop.Controls/Skeleton/Themes/SkeletonImageTheme.cs`
- `src/AtomUI.Desktop.Controls/Skeleton/Themes/SkeletonInputTheme.axaml`
- `src/AtomUI.Desktop.Controls/Skeleton/Themes/SkeletonLineTheme.axaml`
- `src/AtomUI.Desktop.Controls/Skeleton/Themes/SkeletonNodeTheme.axaml`
- `src/AtomUI.Desktop.Controls/Skeleton/Themes/SkeletonParagraphTheme.axaml`
- `src/AtomUI.Desktop.Controls/Skeleton/Themes/SkeletonTheme.axaml`

职责边界：

- 控件主文件保留 public/protected API、Avalonia 属性注册、事件和主要生命周期入口。
- Theme 文件负责静态视觉结构、template part、selector 和资源绑定。
- Token 文件只提供组件视觉变量，不保存实例状态。
- Gallery 文件只展示用法和示例，不作为运行时逻辑 owner。

## 相关文档

- 源设计文档：`docs/controls/desktop/feedback/skeleton/overview.md`
- 实现文档：`docs/controls/desktop/feedback/skeleton/implementation.md`
- Token 文档：`docs/controls/desktop/feedback/skeleton/token.md`
- 变更记录：`docs/controls/desktop/feedback/skeleton/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/spin/index-cn.md

# Spin

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

Spin 是 AtomUI 桌面控件体系中的加载中控件，用于表示局部或整体内容正在处理。

Spin 不负责进度条、骨架屏或任务调度器。这些职责应由业务层、组合控件或更专用的 AtomUI 控件承担。

主要源码入口：

- `src/AtomUI.Controls/Spin`
- `src/AtomUI.Desktop.Controls/Spin`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/Feedback/Spin` |
| 状态 | Stable |

## 何时使用

Spin 的设计语言围绕控件职责、可观察状态和主题契约组织，而不是围绕模板节点组织。

| 维度 | 含义 | Spin 中的表达 |
| --- | --- | --- |
| 产品语义 | 控件在界面中承担的稳定职责。 | Spin 是 AtomUI 桌面控件体系中的加载中控件，用于表示局部或整体内容正在处理。 |
| 内容承载 | 用户数据、展示内容、集合项或操作入口如何进入控件。 | `CustomIndicatorTemplate`。 |
| 状态反馈 | public API、内部状态和伪类如何形成用户可感知反馈。 | motion、visual option。 |
| 主题语义 | ControlTheme、SharedToken、控件 Token 和模板绑定如何表达视觉。 | Spin Token + ControlTheme。 |

## 公共 API

Spin 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。

核心 public surface 按语义分组维护：

| 契约组 | 代表成员 | 维护含义 |
| --- | --- | --- |
| 内容与数据 | `CustomIndicatorTemplate` | 定义控件展示内容、输入数据、模板或业务对象入口。 |
| 交互与状态 | `IsMaskBackgroundEnabled`、`IsMaskBlurEnabled`、`IsMotionEnabled`、`IsSpinning`、`IsTipVisible` | 表达用户可观察状态、可用性、清除、加载或反馈语义。 |
| 视觉与布局 | `DotSize`、`IndicatorSize`、`SizeType` | 影响尺寸、位置、颜色、形状、密度和模板视觉变量。 |
| 动效与异步 | `MotionDuration`、`MotionEasingCurve` | 约束动效开关、异步加载、播放速度、超时和任务边界。 |
| 其他稳定入口 | `CustomIndicator`、`Tip` | 保留为 public surface，变更前需确认 Gallery 和用户 XAML 依赖。 |

当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。

主要公开类型与枚举：

- 类型：`AbstractSpin`、`AbstractSpinIndicator`、`Spin`、`SpinIndicator`。
- 枚举：无。

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_CustomIndicatorPresenter` | `?` | 展示用户内容、文本、图标或模板化数据。 |

当前未抽取到控件专属伪类；主题主要依赖 Avalonia 标准伪类、模板绑定和内部 StyledProperty。

## 事件与命令

Spin 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。
当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/Feedback/Spin/Views/SpinShowCase.axaml:36`

Gallery key：`ExamplesContent` / item `0`

```axaml
<atom:SpinIndicator />
```

### 尺寸

来源：`controlgallery/AtomUIGallery/ShowCases/Feedback/Spin/Views/SpinShowCase.axaml:47`

Gallery key：`ExamplesContent` / item `1`

```axaml
<StackPanel Orientation="Horizontal" Spacing="10">
    <atom:SpinIndicator SizeType="Small" VerticalAlignment="Center" />
    <atom:SpinIndicator SizeType="Middle" VerticalAlignment="Center" />
    <atom:SpinIndicator SizeType="Large" VerticalAlignment="Center" />
    <atom:SpinIndicator SizeType="Custom" IndicatorSize="44" DotSize="14" VerticalAlignment="Center" />
</StackPanel>
```

### 自定义描述

来源：`controlgallery/AtomUIGallery/ShowCases/Feedback/Spin/Views/SpinShowCase.axaml:63`

Gallery key：`ExamplesContent` / item `2`

```axaml
<StackPanel Orientation="Vertical" Spacing="10">
    <StackPanel Orientation="Horizontal" Spacing="10">
        <atom:Spin IsSpinning="True" SizeType="Small"
                   IsTipVisible="True"
                   Tip="加载中...">
            <Border Width="100" Height="100" Background="#0D000000" />
        </atom:Spin>
        <atom:Spin IsSpinning="True" SizeType="Middle"
                   IsTipVisible="True"
                   Tip="加载中...">
            <Border Width="100" Height="100" Background="#0D000000" />
        </atom:Spin>
        <atom:Spin IsSpinning="True" SizeType="Large"
                   IsTipVisible="True"
                   Tip="加载中...">
            <Border Width="100" Height="100" Background="#0D000000" />
        </atom:Spin>
    </StackPanel>
    <atom:Spin IsSpinning="True"
               IsTipVisible="True"
               Tip="加载中...">
        <atom:Alert Message="提示消息标题"
                    Description="关于这条提示上下文的更多详细信息。"
                    Type="Info" />
    </atom:Spin>
</StackPanel>
```

### 嵌入模式

来源：`controlgallery/AtomUIGallery/ShowCases/Feedback/Spin/Views/SpinShowCase.axaml:121`

Gallery key：`ExamplesContent` / item `4`

```axaml
<StackPanel Orientation="Vertical" Spacing="10">
    <atom:Spin IsSpinning="{Binding IsLoadingSwitchChecked}"
               IsTipVisible="True"
               HorizontalAlignment="Stretch"
               Tip="加载中...">
        <atom:Alert Message="提示消息标题"
                    Description="关于这条提示上下文的更多详细信息。"
                    Type="Info" />
    </atom:Spin>

    <atom:Spin IsSpinning="{Binding IsLoadingSwitchChecked}"
               IsTipVisible="True"
               Tip="加载中..."
               HorizontalAlignment="Stretch"
               IsMaskBlurEnabled="True">
        <atom:Alert Message="提示消息标题"
                    Description="关于这条提示上下文的更多详细信息。"
                    Type="Info" />
    </atom:Spin>
    <StackPanel Orientation="Horizontal" Spacing="10">
        <atom:TextBlock Text="加载状态：" />
        <atom:ToggleSwitch IsChecked="{Binding IsLoadingSwitchChecked}" />
    </StackPanel>
</StackPanel>
```

## 状态模型

Spin 的状态流按以下路径收敛：

```text
Public API / inherited command / item source / user input
  -> 控件实例状态
  -> effective state / pseudo-class / template property
  -> ControlTheme selector / presenter / renderer
  -> Gallery 可观察行为
```

状态维护规则：

- Disabled 或不可交互状态优先屏蔽 pointer、keyboard、motion 和提交类反馈。
- motion、visual option 状态由控件实例或明确的数据 owner 推导，不能在 template part 之间双向竞争。
- 模板重套用时必须把 public API 对应状态回放到新的 part、伪类和主题变量。
- 集合、弹层、异步、动效或窗口相关状态必须能处理 reset、close、cancel、detach 和 owner 释放。

## 主题与 Design Token

Spin 的视觉模型由控件模板、ControlTheme、SharedToken 和必要的控件 Token 共同构成。

| 主题文件 | 职责 |
| --- | --- |
| `SpinIndicatorTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |
| `SpinTheme.axaml` | 提供控件模板、selector、资源绑定和状态视觉。 |

Spin 使用 `SpinToken` 作为控件 Token scope。Token 只表达组件视觉语义，不承载 motion、visual option 运行时状态。

主题维护规则：

- 不删除或重命名已经稳定的 ControlTheme key、template part、伪类和资源 key。
- 不把可由 AXAML 表达的模板状态迁移为 C# 动态创建视觉。
- 不把 hover、pressed、selected、expanded、loading、filter、popup open 等运行时状态写入 Token。
- Browser 或平台特化主题必须保持同一 API 的语义一致。

Token 来源：

Spin Token 只表达组件级视觉变量，例如尺寸、间距、颜色、圆角、阴影、图标尺寸和弹层边界。Token 不承载运行时选择、展开、加载、错误、上传任务、过滤条件或业务状态。

当前 Token scope：

- `SpinToken`，scope id 为 `Spin`，源码位于 `src/AtomUI.Desktop.Controls/Spin/SpinToken.cs`。

## AOT 与裁剪注意事项

资源和 AOT 约束：

- 不通过运行时反射扫描 public API、Token 或 Gallery 示例数据。
- 不把可静态声明的模板结构迁移到 C# 动态创建。
- 异步加载、上传、弹层和窗口生命周期必须能取消或释放。
- 缓存对象必须与控件、窗口、弹层或数据 owner 生命周期一致。
- Source generator 生成文件不手工编辑；需要修改时改输入源或 generator。

性能边界：

- 控件应优先复用 Avalonia 原生虚拟化、模板绑定和资源系统。
- 避免为每次状态变化创建不必要的视觉对象、订阅或动画对象。
- 隐藏祖先下的 Spin 不得保留旋转或 dot opacity 的 Compositor 无限动画。
- 大集合控件必须保证 container recycle 后不会泄漏旧 item 状态。

## 源码索引

主要源码文件：

- `src/AtomUI.Controls/Spin/AbstractSpin.cs`
- `src/AtomUI.Controls/Spin/AbstractSpinIndicator.cs`
- `src/AtomUI.Controls/Spin/SpinIndicatorDotPanel.cs`
- `src/AtomUI.Desktop.Controls/Spin/Spin.cs`
- `src/AtomUI.Desktop.Controls/Spin/SpinIndicator.cs`
- `src/AtomUI.Desktop.Controls/Spin/SpinToken.cs`
- `src/AtomUI.Desktop.Controls/Spin/Themes/SpinIndicatorTheme.axaml`
- `src/AtomUI.Desktop.Controls/Spin/Themes/SpinTheme.axaml`

职责边界：

- 控件主文件保留 public/protected API、Avalonia 属性注册、事件和主要生命周期入口。
- Theme 文件负责静态视觉结构、template part、selector 和资源绑定。
- Token 文件只提供组件视觉变量，不保存实例状态。
- Gallery 文件只展示用法和示例，不作为运行时逻辑 owner。

## 相关文档

- 源设计文档：`docs/controls/desktop/feedback/spin/overview.md`
- 实现文档：`docs/controls/desktop/feedback/spin/implementation.md`
- Token 文档：`docs/controls/desktop/feedback/spin/token.md`
- 变更记录：`docs/controls/desktop/feedback/spin/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/splash/index-cn.md

# Splash

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

Splash 是 AtomUI 桌面控件体系中的应用启动反馈控件，用于在主窗口准备完成前展示品牌、启动状态、确定或不确定进度、错误信息和可选补充内容。

Splash 不负责应用依赖注入、主窗口创建、启动异常吞掉、重试策略或业务步骤调度。这些职责应由应用启动代码、组合 ViewModel、`SplashService` 调用方或业务层承担。

主要源码入口：

- `src/AtomUI.Desktop.Controls.Extras/Splash`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls.Extras` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/Other/Splash` |
| 状态 | Preview |

## 何时使用

Splash 的设计语言围绕“启动中但应用尚不可交互”的产品语义组织。它应比普通 `Spin` 更具品牌表达和窗口宿主语义，但不承担 `Modal` 的流程阻断和动作确认职责。

| 维度 | 含义 | Splash 中的表达 |
| --- | --- | --- |
| 产品语义 | 控件在界面中承担的稳定职责。 | 显示桌面应用启动、加载和启动失败状态。 |
| 内容承载 | 用户数据、展示内容、模板或操作入口如何进入控件。 | `Logo`、`Title`、`Subtitle`、`Message`、`Detail`、`Content`、`Footer`。 |
| 状态反馈 | public API、服务状态和模板绑定如何形成用户可感知反馈。 | `Status`、`Progress`、`IsIndeterminate`、`IsMotionEnabled`。 |
| 宿主语义 | 启动窗口、静态 API 和实例服务如何协作。 | `SplashWindow` 负责窗口，`SplashService` 负责编排，`Splash` 静态 API 做薄封装。 |
| 主题语义 | ControlTheme、SharedToken、控件 Token 和模板绑定如何表达视觉。 | Splash Token + ControlTheme。 |

## 公共 API

Splash 的公共契约由视觉控件、启动窗口、实例服务、静态便利入口和主题资源共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。

核心 public surface 按语义分组维护：

| 契约组 | 代表成员 | 维护含义 |
| --- | --- | --- |
| 品牌与标题 | `Logo`、`LogoTemplate`、`Title`、`Subtitle` | 定义启动页品牌识别和主标题区域。 |
| 状态文本 | `Message`、`Detail` | 表达当前启动阶段、错误详情或补充说明。 |
| 进度状态 | `Progress`、`IsIndeterminate`、`Status` | 表达确定进度、不确定加载、成功和错误状态。 |
| 内容扩展 | `Content`、`ContentTemplate`、`Footer`、`FooterTemplate` | 承载自定义启动内容、版本信息、版权信息或业务操作入口。 |
| 动效与窗口 | `IsMotionEnabled`、`MinimumShowDuration`、`CloseDelay`、`FadeOutDuration`、`Topmost` | 控制启动展示时长、关闭节奏和窗口级视觉体验。 |
| 服务入口 | `ISplashService`、`SplashService`、`Splash.DefaultService`、`Splash.ShowAsync()`、`Splash.CloseAsync()` | 提供实例 API 和静态 API，静态 API 委托给可替换服务实例。 |

主要公开类型与枚举：

- 类型：`Splash`、`SplashWindow`、`SplashService`、`SplashOptions`、`ISplashService`。
- 枚举：`SplashStatus`。

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_RootLayout` | `Panel` | 承载启动页根布局、背景、圆角和阴影边界。 |
| `PART_LogoPresenter` | `ContentPresenter` | 展示 `Logo` 和 `LogoTemplate`。 |
| `PART_TitleBlock` | `TextBlock` | 展示主标题。 |
| `PART_SubtitleBlock` | `TextBlock` | 展示副标题。 |
| `PART_MessageBlock` | `TextBlock` | 展示当前状态消息。 |
| `PART_DetailBlock` | `TextBlock` | 展示详细信息或错误详情。 |
| `PART_ProgressBar` | `ProgressBar` | 展示确定进度。 |
| `PART_Spin` | `Spin` | 展示不确定加载状态。 |
| `PART_ContentPresenter` | `ContentPresenter` | 展示扩展内容。 |
| `PART_FooterPresenter` | `ContentPresenter` | 展示底部内容或启动失败操作入口。 |

控件专属伪类：

| 伪类 | 含义 |
| --- | --- |
| `:loading` | `Status` 为 `Loading`。 |
| `:success` | `Status` 为 `Success`。 |
| `:error` | `Status` 为 `Error`。 |
| `:indeterminate` | `IsIndeterminate` 为 `true`。 |
| `:determinate` | `Progress` 有有效值且 `IsIndeterminate` 为 `false`。 |

## 事件与命令

Splash 的事件与命令以控件文档、源码 public surface 和 Avalonia 基类契约为准；生成器不从源码发明额外事件。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础

来源：`controlgallery/AtomUIGallery/ShowCases/Other/Splash/Views/SplashShowCase.axaml:49`

Gallery key：`ExamplesContent` / item `0`

```axaml
<atom:Splash Classes="preview-splash"
             Logo="{Binding BasicLogo}"
             Title="AtomUI"
             Subtitle="桌面启动流程"
             Message="正在准备工作区"
             Detail="正在加载主题、语言资源和缓存状态。"
             IsIndeterminate="True"
             HorizontalAlignment="Left">
    <atom:Splash.LogoTemplate>
        <DataTemplate x:DataType="vm:SplashLogoInfo">
            <Border Width="52"
                    Height="52"
                    CornerRadius="14"
                    Background="{Binding Background}">
                <atom:TextBlock Text="{Binding Text}"
                                Foreground="{Binding Foreground}"
                                FontSize="20"
                                FontWeight="Bold"
                                HorizontalAlignment="Center"
                                VerticalAlignment="Center" />
            </Border>
        </DataTemplate>
    </atom:Splash.LogoTemplate>
</atom:Splash>
```

### 确定进度

来源：`controlgallery/AtomUIGallery/ShowCases/Other/Splash/Views/SplashShowCase.axaml:83`

Gallery key：`ExamplesContent` / item `1`

```axaml
<atom:Splash Classes="preview-splash"
             Logo="{Binding BasicLogo}"
             Title="AtomUI"
             Subtitle="桌面启动流程"
             Message="正在加载模块"
             Detail="主题、图标和路由目录已就绪，正在初始化可选包。"
             Progress="{Binding ProgressValue}"
             IsIndeterminate="False"
             HorizontalAlignment="Left">
    <atom:Splash.LogoTemplate>
        <DataTemplate x:DataType="vm:SplashLogoInfo">
            <Border Width="52"
                    Height="52"
                    CornerRadius="14"
                    Background="{Binding Background}">
                <atom:TextBlock Text="{Binding Text}"
                                Foreground="{Binding Foreground}"
                                FontSize="20"
                                FontWeight="Bold"
                                HorizontalAlignment="Center"
                                VerticalAlignment="Center" />
            </Border>
        </DataTemplate>
    </atom:Splash.LogoTemplate>
</atom:Splash>
```

### 状态

来源：`controlgallery/AtomUIGallery/ShowCases/Other/Splash/Views/SplashShowCase.axaml:119`

Gallery key：`ExamplesContent` / item `2`

```axaml
<WrapPanel Orientation="Horizontal"
           ItemSpacing="20"
           LineSpacing="20">
    <atom:Splash Classes="status-splash"
                 Title="AtomUI"
                 Message="工作区已就绪"
                 Detail="满足最短展示时长后即可显示主窗口。"
                 Status="Success"
                 Progress="1"
                 IsIndeterminate="False"
                 Footer="Gallery 静态预览" />
    <atom:Splash Classes="status-splash"
                 Title="AtomUI"
                 Message="启动失败"
                 Detail="可通过 SetErrorAsync 在关闭前呈现阻塞型启动错误。"
                 Status="Error"
                 IsIndeterminate="False"
                 Footer="Gallery 静态预览" />
</WrapPanel>
```

### Logo、内容与页脚

来源：`controlgallery/AtomUIGallery/ShowCases/Other/Splash/Views/SplashShowCase.axaml:149`

Gallery key：`ExamplesContent` / item `3`

```axaml
<atom:Splash Width="420"
             MinHeight="300"
             Logo="{Binding ComposedLogo}"
             Title="AtomUI Gallery"
             Subtitle="桌面启动流程"
             Message="正在加载模块"
             Detail="主题、图标和路由目录已就绪，正在初始化可选包。"
             Progress="{Binding ProgressValue}"
             IsIndeterminate="False"
             Footer="{Binding ComposedFooter}"
             HorizontalAlignment="Left">
    <StackPanel Orientation="Horizontal"
                Spacing="8"
                HorizontalAlignment="Center">
        <atom:Tag Text="核心"
                  TagColor="success" />
        <atom:Tag Text="主题"
                  TagColor="processing" />
        <atom:Tag Text="Gallery"
                  TagColor="warning" />
    </StackPanel>
    <atom:Splash.LogoTemplate>
        <DataTemplate x:DataType="vm:SplashLogoInfo">
            <Border Width="56"
                    Height="56"
                    CornerRadius="18"
                    Background="{Binding Background}">
                <atom:TextBlock Text="{Binding Text}"
                                Foreground="{Binding Foreground}"
                                FontSize="18"
                                FontWeight="Bold"
                                HorizontalAlignment="Center"
                                VerticalAlignment="Center" />
            </Border>
        </DataTemplate>
    </atom:Splash.LogoTemplate>
    <atom:Splash.FooterTemplate>
        <DataTemplate x:DataType="vm:SplashFooterInfo">
            <StackPanel Orientation="Horizontal"
                        Spacing="8"
                        VerticalAlignment="Center">
                <atom:Tag Text="{Binding Version}"
                          TagColor="geekblue" />
                <atom:TextBlock Text="{Binding Description}"
                                Foreground="{atom:SharedTokenResource ColorTextTertiary}"
                                VerticalAlignment="Center" />
            </StackPanel>
        </DataTemplate>
    </atom:Splash.FooterTemplate>
</atom:Splash>
```

## 状态模型

Splash 的状态流按以下路径收敛：

```text
Splash visual API / SplashService API / Splash static API
  -> Splash instance properties
  -> pseudo-class / template binding
  -> ControlTheme selector / ProgressBar / Spin / TextBlock
  -> SplashWindow visible behavior
```

状态维护规则：

- `Splash` 视觉控件只持有可展示状态，不创建主窗口、不关闭应用、不吞异常。
- `Splash` 本体提供 `SetMessage`、`SetProgress`、`SetStatus` 和 `SetError` 状态写入方法。
- `SplashWindow` 只持有窗口级状态和关闭动效，不解释业务启动步骤。
- `SplashService` 是实例 API 的状态 owner，同一个服务实例一次只管理一个 `CurrentWindow`。
- `Splash` 静态 API 只委托给 `Splash.DefaultService`，不直接持有窗口或视觉节点。
- `Progress` 为 `null` 或 `IsIndeterminate=true` 时显示不确定加载；`Progress` 有效且 `IsIndeterminate=false` 时显示确定进度。
- `Status=Error` 时保留窗口，等待调用方决定重试、退出或显示补充内容。
- `CloseAsync()` 必须幂等；重复调用、取消或窗口已关闭都不能留下不可释放窗口引用。
- 所有服务 API 写入 UI 状态时必须回到 UI thread。

## 主题与 Design Token

Splash 的视觉模型由 `Splash` 控件模板、`SplashWindow` 宿主主题、SharedToken 和 `SplashToken` 共同构成。

| 主题文件 | 职责 |
| --- | --- |
| `SplashTheme.axaml` | 定义启动页视觉控件模板、状态 selector、ProgressBar/Spin 组合和内容区域。 |
| `SplashWindowTheme.axaml` | 定义桌面启动窗口宿主、透明无装饰窗口模板、阴影宿主和内容承载边界。 |

Splash 使用独立的 Control identity 和 `SplashToken` Own Token scope。Token 只表达组件视觉语义，不承载 `Status`、`Progress`、`IsIndeterminate`、启动步骤或异常对象。`SplashTheme.axaml` 通过 `SplashTokenResource` 统一读取 Splash 的 Effective Global Token 和 Own Token：标题读取 Global Token `ColorTextHeading`，普通消息读取 Global Token `ColorText`，副标题和详情读取 Own Token `SubtleForeground`。默认没有 Splash Control 级覆盖时，Effective Global Token 回退到当前主题的全局结果，因此默认 Light/Dark 视觉不变。
`SplashWindow` 使用 `{x:Type atom:SplashWindow}` 作为隐式 `ControlTheme` key；窗口模板必须保持透明内容宿主，避免默认 Window 背景破坏 Splash 表面圆角。
`SplashWindowTheme.axaml` 直接使用 `ShadowsAwareContainer#PART_SurfaceHost` 承载 `Splash`，由 `SurfaceBoxShadow` 控制窗口表面阴影，由 `SurfaceCornerRadius` 控制阴影遮罩圆角。`SplashTheme.axaml` 内部的 `PART_RootLayout` 和 `PART_SurfaceLayout` 继续负责背景、内容圆角和裁剪。

资源覆盖边界：

- 同时影响窗口阴影宿主和 Splash 内容表面的视觉资源，应写入 `SplashWindow.Resources`。
- 只影响 `Splash` 内部模板的资源，可以写入 `Splash.Resources`。
- 应用需要完整的专用启动窗口视觉时，定义自己的 `SplashWindow` 和内部 Splash 子控件；子控件通过 `StyleKeyOverride` 复用标准 Splash Theme，并由自己的 AXAML `Styles` 维护专用模板视觉。窗口或页面不得进入 Splash 模板修改内部节点。
- 不通过 C# `TokenResourceBinder` 在窗口宿主和 Splash 之间桥接 `SurfaceBoxShadow`、`SurfaceCornerRadius` 等模板可表达关系。

主题维护规则：

- 不删除或重命名已经稳定的 ControlTheme key、template part、伪类和资源 key。
- 视觉结构优先使用 AXAML、`TemplateBinding`、selector、`Spin`、`ProgressBar` 和 `ContentPresenter` 表达。
- 不把状态显示逻辑改成 C# 动态创建视觉，除非 AXAML 无法表达且生命周期 owner 明确。
- Gallery 和业务代码不得从页面、父控件 Style 或窗口 ControlTheme 通过 `/template/`、C# `.Template()` 或 `PART_*` 名称进入 Splash 模板。专用 Splash 子控件可以通过 `StyleKeyOverride` 复用标准 Splash Theme，并在自己的 AXAML `Styles` 中进入自己的一层模板；该子控件承担模板契约所有权。
- Light/Dark 主题应保持品牌区域、进度区域、错误状态和窗口表面的对比度。

Token 来源：

Splash Token 只表达组件级视觉变量，例如窗口尺寸、内容间距、品牌尺寸、文字规格、进度区域间距、圆角、阴影和状态色。Token 不承载启动步骤、`Status`、`Progress`、`IsIndeterminate`、异常对象、主窗口引用或服务状态。

当前 Token scope：

- `SplashToken`，scope id 为 `Splash`，源码位于 `src/AtomUI.Desktop.Controls.Extras/Splash/SplashToken.cs`。

## AOT 与裁剪注意事项

资源和 AOT 约束：

- 不通过运行时反射扫描 public API、Token、服务或 Gallery 示例数据。
- 不把可静态声明的模板结构迁移到 C# 动态创建。
- 不为模板稳定节点之间的 token 资源关系创建 `TokenResourceBinder` 桥接；`PART_SurfaceHost` 和 `Splash` 内部模板应通过相同的资源树解析 `SplashTokenResource`。
- 窗口级视觉覆盖写入 `SplashWindow.Resources`，确保 `PART_SurfaceHost` 与 `Splash` 内部模板都能解析；`Splash.Resources` 只用于仅影响 Splash 内部模板的覆盖。
- Gallery 和业务代码不得从页面、父控件 Style、窗口 ControlTheme 或 C# 动态 Style 穿透 Splash 模板。需要完整专用视觉时定义专用 Splash 子控件，通过 `StyleKeyOverride` 复用标准 Theme，并把专用 selector 放在该子控件自己的 AXAML `Styles` 中；每个 selector 最多进入自己的一层模板，并继续遵守稳定 template part 和状态 selector 契约。
- `SplashService` 中的延迟任务、淡出任务和取消 token 必须能取消或释放。
- 事件订阅必须与窗口或服务生命周期一致。
- `Splash.DefaultService` 替换不得保留旧服务窗口引用。
- Source generator 生成文件不手工编辑；需要修改时改输入源或 generator。

性能边界：

- Splash 不面向大集合和虚拟化场景，默认视觉树应保持短路径和低首次创建成本。
- 不为每次状态文本更新创建新的窗口、控件或动画对象。
- 动效应复用 Avalonia transition 和 AtomUI motion token，不手写高频 timer。

## 源码索引

主要源码文件：

- `src/AtomUI.Desktop.Controls.Extras/Splash/Splash.cs`
- `src/AtomUI.Desktop.Controls.Extras/Splash/Splash.StaticAPI.cs`
- `src/AtomUI.Desktop.Controls.Extras/Splash/SplashWindow.cs`
- `src/AtomUI.Desktop.Controls.Extras/Splash/SplashOptions.cs`
- `src/AtomUI.Desktop.Controls.Extras/Splash/SplashService.cs`
- `src/AtomUI.Desktop.Controls.Extras/Splash/ISplashService.cs`
- `src/AtomUI.Desktop.Controls.Extras/Splash/SplashStatus.cs`
- `src/AtomUI.Desktop.Controls.Extras/Splash/SplashToken.cs`
- `src/AtomUI.Desktop.Controls.Extras/Splash/SplashPseudoClass.cs`
- `src/AtomUI.Desktop.Controls.Extras/Splash/Themes/SplashTheme.axaml`
- `src/AtomUI.Desktop.Controls.Extras/Splash/Themes/SplashWindowTheme.axaml`
- `src/AtomUI.Desktop.Controls.Extras/AtomUIExtrasThemesProvider.cs`
- `src/AtomUI.Desktop.Controls.Extras/ThemeManagerBuilderExtensions.cs`

职责边界：

- `Splash.cs` 保留视觉控件 public/protected API、状态写入方法、Avalonia 属性注册、伪类同步和主要模板生命周期入口。
- `Splash.StaticAPI.cs` 只放静态便利入口，所有逻辑委托给 `Splash.DefaultService`。
- `SplashWindow.cs` 负责窗口级 `Splash` 内容承载属性、展示时间记录、淡出关闭和关闭请求状态；`SplashWindowTheme.axaml` 负责透明无装饰窗口默认值、窗口模板、阴影宿主和内容承载边界。
- `SplashService.cs` 是启动编排 owner，负责创建窗口、创建或复用 `Splash` 实例、应用运行时 options、更新状态、关闭窗口和 UI thread 调度。
- Theme 文件负责静态视觉结构、template part、selector 和资源绑定；`SplashTheme.axaml` 使用 `SplashTokenResource` 读取 Splash Effective Global Token 与 Own Token，不绕过 Control identity 直接读取同名 SharedToken。
- Token 文件只提供组件视觉变量，不保存实例状态。
- Gallery 文件只展示用法和示例，不作为运行时逻辑 owner。

## 相关文档

- 源设计文档：`docs/controls/desktop/feedback/splash/overview.md`
- 实现文档：`docs/controls/desktop/feedback/splash/implementation.md`
- Token 文档：`docs/controls/desktop/feedback/splash/token.md`
- 变更记录：`docs/controls/desktop/feedback/splash/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/watermark/index-cn.md

# Watermark

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

Watermark 是 AtomUI 桌面控件体系中的水印控件，用于在目标元素上绘制文本或图片水印。

Watermark 不负责背景装饰、权限控制或图片处理服务。这些职责应由业务层、组合控件或更专用的 AtomUI 控件承担。

主要源码入口：

- `src/AtomUI.Controls/Watermark`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/Feedback/Watermark` |
| 状态 | Stable |

## 何时使用

Watermark 的设计语言围绕控件职责、可观察状态和主题契约组织，而不是围绕模板节点组织。

| 维度 | 含义 | Watermark 中的表达 |
| --- | --- | --- |
| 产品语义 | 控件在界面中承担的稳定职责。 | Watermark 是 AtomUI 桌面控件体系中的水印控件，用于在目标元素上绘制文本或图片水印。 |
| 内容承载 | 用户数据、展示内容、集合项或操作入口如何进入控件。 | `Source`、`Text`。 |
| 状态反馈 | public API、内部状态和伪类如何形成用户可感知反馈。 | collection/filter、input/value、visual option。 |
| 主题语义 | ControlTheme、SharedToken、控件 Token 和模板绑定如何表达视觉。 | SharedToken / 关联控件 Token + ControlTheme。 |

## 公共 API

Watermark 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。

核心 public surface 按语义分组维护：

| 契约组 | 代表成员 | 维护含义 |
| --- | --- | --- |
| 内容与数据 | `Source`、`Text` | 定义控件展示内容、输入数据、模板或业务对象入口。 |
| 交互与状态 | `IsCrossUsed`、`IsMirrorUsed` | 表达用户可观察状态、可用性、清除、加载或反馈语义。 |
| 视觉与布局 | `FontSize`、`Foreground`、`Height`、`HorizontalOffset`、`HorizontalSpace`、`VerticalOffset`、`VerticalSpace` | 影响尺寸、位置、颜色、形状、密度和模板视觉变量。 |
| 其他稳定入口 | `Opacity`、`Rotate` | 保留为 public surface，变更前需确认 Gallery 和用户 XAML 依赖。 |

当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。

主要公开类型与枚举：

- 类型：`ImageGlyph`、`ImageGlyphExtension`、`TextGlyph`、`TextGlyphExtension`、`WatermarkGlyph`、`WatermarkGlyphExtension`。
- 枚举：无。

稳定 template part：

当前控件没有显式 `[TemplatePart]` 契约；主题节点仍通过 ControlTheme key、资源 key 和 Gallery 可观察行为形成稳定边界。

当前未抽取到控件专属伪类；主题主要依赖 Avalonia 标准伪类、模板绑定和内部 StyledProperty。

## 事件与命令

Watermark 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。
当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础用法

来源：`controlgallery/AtomUIGallery/ShowCases/Feedback/Watermark/Views/WatermarkShowCase.axaml:34`

Gallery key：`ExamplesContent` / item `0`

```axaml
<Border Height="300" HorizontalAlignment="Stretch" atom:Watermark.Glyph="{atom:TextGlyph 'AtomUI'}" />
```

### 多行水印

来源：`controlgallery/AtomUIGallery/ShowCases/Feedback/Watermark/Views/WatermarkShowCase.axaml:44`

Gallery key：`ExamplesContent` / item `1`

```axaml
<Border Height="200"
        HorizontalAlignment="Stretch">
    <atom:Watermark.Glyph>
        <atom:TextGlyph Text="AtomUI
快乐工作"
                        FontSize="18"
                        Foreground="Gray" />
    </atom:Watermark.Glyph>
</Border>
```

### 图片水印

来源：`controlgallery/AtomUIGallery/ShowCases/Feedback/Watermark/Views/WatermarkShowCase.axaml:61`

Gallery key：`ExamplesContent` / item `2`

```axaml
<Border Height="400">
    <atom:Watermark.Glyph>
        <atom:ImageGlyph Source="/Assets/ATOMUI-LOGO.png" />
    </atom:Watermark.Glyph>
</Border>
```

### 自定义配置

来源：`controlgallery/AtomUIGallery/ShowCases/Feedback/Watermark/Views/WatermarkShowCase.axaml:75`

Gallery key：`ExamplesContent` / item `3`

```axaml
<StackPanel atom:Watermark.Glyph="{atom:TextGlyph 'AtomUI'}">
    <atom:TextBlock TextWrapping="Wrap"
                    Text="数字世界的高速迭代让产品变得更加复杂，而人的意识和注意力资源是有限的。面对这种设计矛盾，追求自然交互将始终是 AtomUI 的一致方向。

自然的用户认知：根据认知心理学，外部信息约有 80% 通过视觉通道获得。界面设计中最重要的视觉元素，包括布局、色彩、插图、图标等，都应充分吸收自然规律，从而降低用户的认知成本，并带来真实、顺畅的感受。在一些场景中，适时加入听觉、触觉等其他感官通道，也可以创造更丰富、更自然的产品体验。

自然的用户行为：在与系统交互时，设计师应充分理解用户、系统角色和任务目标之间的关系，并结合上下文组织系统功能和服务。同时，可以运用行为分析、人工智能、传感器等方法辅助用户做出有效决策，减少用户的额外操作，节省用户的心智和体力资源，让人机交互更加自然。" />
    <Image Source="/Assets/watermark-sample.png" />
</StackPanel>
```

## 状态模型

Watermark 的状态流按以下路径收敛：

```text
Public API / inherited command / item source / user input
  -> 控件实例状态
  -> effective state / pseudo-class / template property
  -> ControlTheme selector / presenter / renderer
  -> Gallery 可观察行为
```

状态维护规则：

- Disabled 或不可交互状态优先屏蔽 pointer、keyboard、motion 和提交类反馈。
- collection/filter、input/value、visual option 状态由控件实例或明确的数据 owner 推导，不能在 template part 之间双向竞争。
- 模板重套用时必须把 public API 对应状态回放到新的 part、伪类和主题变量。
- 集合、弹层、异步、动效或窗口相关状态必须能处理 reset、close、cancel、detach 和 owner 释放。

## 主题与 Design Token

Watermark 的视觉模型由控件模板、ControlTheme、SharedToken 和必要的控件 Token 共同构成。

当前控件未抽取到专属 AXAML 主题文件；视觉契约主要来自继承控件、共享主题和资源 key。

Watermark 当前没有专属 Token 文档；主题通过 SharedToken、关联控件 Token 或继承主题资源表达视觉语义。运行时状态不得写入 Token 模型。

主题维护规则：

- 不删除或重命名已经稳定的 ControlTheme key、template part、伪类和资源 key。
- 不把可由 AXAML 表达的模板状态迁移为 C# 动态创建视觉。
- 不把 hover、pressed、selected、expanded、loading、filter、popup open 等运行时状态写入 Token。
- Browser 或平台特化主题必须保持同一 API 的语义一致。

Token 来源：

- Watermark 当前没有专属 `token.md`；LLMS 生成按第 5 节视觉与主题模型、SharedToken、控件家族 Token 或主题资源说明 Token 边界。

## AOT 与裁剪注意事项

资源和 AOT 约束：

- 不通过运行时反射扫描 public API、Token 或 Gallery 示例数据。
- 不把可静态声明的模板结构迁移到 C# 动态创建。
- 异步加载、上传、弹层和窗口生命周期必须能取消或释放。
- 缓存对象必须与控件、窗口、弹层或数据 owner 生命周期一致。
- Source generator 生成文件不手工编辑；需要修改时改输入源或 generator。

性能边界：

- 控件应优先复用 Avalonia 原生虚拟化、模板绑定和资源系统。
- 避免为每次状态变化创建不必要的视觉对象、订阅或动画对象。
- 大集合控件必须保证 container recycle 后不会泄漏旧 item 状态。

## 源码索引

主要源码文件：

- `src/AtomUI.Controls/Watermark/Glyphs/ImageGlyph.cs`
- `src/AtomUI.Controls/Watermark/Glyphs/ImageGlyphExtension.cs`
- `src/AtomUI.Controls/Watermark/Glyphs/TextGlyph.cs`
- `src/AtomUI.Controls/Watermark/Glyphs/TextGlyphExtension.cs`
- `src/AtomUI.Controls/Watermark/Glyphs/WatermarkGlyph.cs`
- `src/AtomUI.Controls/Watermark/Glyphs/WatermarkGlyphExtension.cs`
- `src/AtomUI.Controls/Watermark/Watermark.cs`

职责边界：

- 控件主文件保留 public/protected API、Avalonia 属性注册、事件和主要生命周期入口。
- Theme 文件负责静态视觉结构、template part、selector 和资源绑定。
- Token 文件只提供组件视觉变量，不保存实例状态。
- Gallery 文件只展示用法和示例，不作为运行时逻辑 owner。

## 相关文档

- 源设计文档：`docs/controls/desktop/feedback/watermark/overview.md`
- 实现文档：`docs/controls/desktop/feedback/watermark/implementation.md`
- 变更记录：`docs/controls/desktop/feedback/watermark/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/window/index-cn.md

# Window

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

Window 是 AtomUI 桌面控件体系中的桌面窗口控件，用于提供 AtomUI 自绘窗口、平台窗口能力和主题集成入口。

Window 不负责普通内容控件、Dialog 弹层或业务路由容器。这些职责应由业务层、组合控件或更专用的 AtomUI 控件承担。

主要源码入口：

- `src/AtomUI.Desktop.Controls/Window`

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/General/Window` |
| 状态 | Stable |

## 何时使用

Window 的设计语言围绕控件职责、可观察状态和主题契约组织，而不是围绕模板节点组织。

| 维度 | 含义 | Window 中的表达 |
| --- | --- | --- |
| 产品语义 | 控件在界面中承担的稳定职责。 | Window 是 AtomUI 桌面控件体系中的桌面窗口控件，用于提供 AtomUI 自绘窗口、平台窗口能力和主题集成入口。 |
| 内容承载 | 用户数据、展示内容、集合项或操作入口如何进入控件。 | `ContentFrameBackground`、`ContentFrameLayer`、`ContentFrameLayerOpacity`、`ContentFrameLayerTemplate`、`IsTitleBarVisible`、`LogoTemplate`、`TitleBarFrameBackground`、`TitleBarFrameLayer` 等 11 项。`TitleBarFrameLayer` 表示标题栏背景或装饰层，不作为按钮、菜单、搜索框等交互控件入口。 |
| 状态反馈 | public API、内部状态和伪类如何形成用户可感知反馈。 | open/close。 |
| 主题语义 | ControlTheme、SharedToken、控件 Token 和模板绑定如何表达视觉。 | Window Token + ControlTheme。 |

## 公共 API

Window 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。

核心 public surface 按语义分组维护：

| 契约组 | 代表成员 | 维护含义 |
| --- | --- | --- |
| 内容与数据 | `ContentFrameBackground`、`ContentFrameLayer`、`ContentFrameLayerOpacity`、`ContentFrameLayerTemplate`、`IsTitleBarVisible`、`LogoTemplate`、`LeftAddOn`、`LeftAddOnTemplate`、`RightAddOn`、`RightAddOnTemplate`、`TitleBarFrameBackground`、`TitleBarFrameLayer`、`TitleBarFrameLayerOpacity`、`TitleBarFrameLayerTemplate` 等 | 定义控件展示内容、输入数据、模板或业务对象入口；`LeftAddOn` 和 `RightAddOn` 用于默认标题栏中的交互内容，`TitleBarFrameLayer` 仍只表示标题栏背景或装饰层。 |
| 选择与集合 | `ViewModel` | 维护选择、展开、过滤、分页、分组或集合状态。 |
| 交互与状态 | `IsMinimizeCaptionButtonVisible`、`IsMaximizeCaptionButtonVisible`、`IsCloseCaptionButtonVisible`、`IsFullScreenCaptionButtonVisible`、`IsPinCaptionButtonVisible`、`IsMoveEnabled` | 表达 managed caption button 呈现、窗口移动和用户可观察状态；visibility 不替代窗口 capability。 |
| 弹层与窗口 | `WindowFrameLayer`、`WindowFrameLayerOpacity` | 控制 popup、flyout、dialog、window 或 overlay 宿主协作。 |
| 其他稳定入口 | `Logo`、`LogoVisibility`、`IsTitleVisible`、`MediaBreakPoint`、`OsType`、`OsVersion`、`TitleAlignment` | 保留为 public surface，变更前需确认 Gallery 和用户 XAML 依赖。`IsTitleVisible` 只控制默认标题栏内的标题文字呈现（`WindowTitleBar` AddOwner facade），不影响系统级窗口标题；与 `IsTitleBarVisible`（隐藏整条标题栏）作用域不同。`Logo`/`LogoTemplate` 只承载显式值：未设置时标题栏渲染层回退到 `Icon`（再回退主窗口 Logo/Icon），运行时设为 `null` 回到默认，彻底隐藏用 `LogoVisibility=Never`。 |

当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。

主要 public 类型与枚举：

- 类型：`Window`、`ReactiveWindow<TViewModel>`、`MacStandardWindowButtons`。
- 枚举：无。

Window 模板还使用 `FullscreenPopoverLayer`、`WindowResizer`、`WindowVisualLayerClip` 等 internal
协作类型。它们会影响主题和可观察窗口行为，但不是用户可直接依赖的 public API。

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_CloseButton` | `?` | 承载用户触发入口、导航或关闭动作。 |
| `PART_ContentPresenter` | `?` | 展示用户内容、文本、图标或模板化数据。 |
| `PART_FullScreenButton` | `?` | 承载用户触发入口、导航或关闭动作。 |
| `PART_FullscreenPopoverLayer` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_OverlayWrapper` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_PopoverBorder` | `?` | 承载根视觉、边框、背景或尺寸基线。 |
| `PART_PopoverCloseButton` | `?` | 承载用户触发入口、导航或关闭动作。 |
| `PART_PopoverFullScreenButton` | `?` | 承载用户触发入口、导航或关闭动作。 |
| `PART_RootLayout` | `?` | 承载根视觉、边框、背景或尺寸基线。 |
| `PART_TitleBar` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_TitleBarPresenter` | `?` | 展示用户内容、文本、图标或模板化数据。 |
| `PART_TransparencyFallback` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_UnderlayWrapper` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_VisualLayerManager` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |
| `PART_WindowFrame` | `?` | 承载根视觉、边框、背景或尺寸基线。 |
| `PART_WindowResizer` | `?` | 稳定模板协作入口，重命名前必须同步主题和实现。 |

当前未抽取到控件专属伪类；主题主要依赖 Avalonia 标准伪类、模板绑定和内部 StyledProperty。

## 事件与命令

Window 的公共契约由 public/protected 类型成员、Avalonia 属性、事件、命令、template part、伪类、ControlTheme key 和资源 key 共同组成。维护时应先确认这些契约是否已经被源码、Gallery 示例或文档暴露。
当前没有抽取到控件专属 public 事件；交互通知主要来自继承事件、命令或 Gallery 可观察状态。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础窗口

来源：`controlgallery/AtomUIGallery/ShowCases/General/Window/Views/WindowShowCase.axaml:34`

Gallery key：`ExamplesContent` / item `0`

```axaml
<atom:Button ButtonType="Primary"
```

### 标题对齐

来源：`controlgallery/AtomUIGallery/ShowCases/General/Window/Views/WindowShowCase.axaml:47`

Gallery key：`ExamplesContent` / item `1`

```axaml
<atom:Button ButtonType="Primary"
```

### 隐藏标题文字

来源：`controlgallery/AtomUIGallery/ShowCases/General/Window/Views/WindowShowCase.axaml:61`

SourceKey：`window-title-visibility`

```axaml
<atom:Button ButtonType="Primary"
```

### Logo 显示策略

来源：`controlgallery/AtomUIGallery/ShowCases/General/Window/Views/WindowShowCase.axaml:74`

Gallery key：`ExamplesContent` / item `3`

```axaml
<atom:Button ButtonType="Primary"
```

## 状态模型

Window 的状态流按以下路径收敛：

```text
Public API / inherited command / item source / user input
  -> 控件实例状态
  -> effective state / pseudo-class / template property
  -> ControlTheme selector / presenter / renderer
  -> Gallery 可观察行为
```

状态维护规则：

- Disabled 或不可交互状态优先屏蔽 pointer、keyboard、motion 和提交类反馈。
- open/close 状态由控件实例或明确的数据 owner 推导，不能在 template part 之间双向竞争。
- 模板重套用时必须把 public API 对应状态回放到新的 part、伪类和主题变量。
- 集合、弹层、异步、动效或窗口相关状态必须能处理 reset、close、cancel、detach 和 owner 释放。
- 默认标题栏的交互内容通过 `LeftAddOn`、`RightAddOn` 及其模板属性承载；`TitleBarFrameLayer` 只表达标题栏背景、遮罩或装饰视觉，不保证内部控件获得 pointer、focus、keyboard 或 command 事件。

Caption button 的 requested visibility 与窗口 capability 分离：Minimize、Maximize 和 Close 默认请求显示，FullScreen 和 Pin 默认隐藏。设置 visibility 为 `false` 只隐藏 AtomUI managed button，不修改 `CanMinimize`、`CanMaximize`、`WindowState`、`Topmost` 或其他窗口操作入口；capability 为 `false` 时对应 managed button 保持隐藏。完整模型见 [WindowTitleBar Caption Button 配置设计](../window-title-bar/caption-button-configuration-design.md)。

Window 为逻辑树内每个 `WindowTitleBar` 定义相同的宿主上下文投影，包括 caption 配置、窗口能力、WindowState、active state、Topmost、平台/CSD 输入和窗口操作命令。投影由 Window 创建为可释放 lease，由各标题栏实例分别持有；Window 不以单例 binding 容器限制一个窗口只能接入一个标题栏。

## 主题与 Design Token

Window 的视觉模型由控件模板、ControlTheme、SharedToken 和必要的控件 Token 共同构成。

| 主题文件 | 职责 |
| --- | --- |
| `FullscreenPopoverLayerTheme.axaml` | 定义 macOS 全屏标题栏 popover 的固定模板、caption buttons 和标题展示。 |
| `WindowDrawnDecorationsTheme.axaml` | 定义 Avalonia drawn decorations overlay 下的标题栏、caption buttons、shadow 和 visible frame 裁剪结构；该 overlay 不承载 Dialog、Drawer 或其他业务 presentation。 |
| `WindowResizerTheme.axaml` | 定义 managed resize grip 的八向命中区域。 |
| `WindowTheme.axaml` | 定义普通 Window 模板、标题栏、内容 frame、visual layer、overlay host、fullscreen popover 和 managed resizer。 |

Window 使用 `WindowToken` 作为控件 Token scope。Token 只表达组件视觉语义，不承载 open/close 运行时状态。

主题维护规则：

- 不删除或重命名已经稳定的 ControlTheme key、template part、伪类和资源 key。
- 不把可由 AXAML 表达的模板状态迁移为 C# 动态创建视觉。
- 不把 hover、pressed、selected、expanded、loading、filter、popup open 等运行时状态写入 Token。
- Browser 或平台特化主题必须保持同一 API 的语义一致。

### 5.1 标题栏背景层与自定义 TitleBar 模型

Window 标题栏按职责拆分为背景/装饰层、默认标题栏层和自定义标题栏层三类稳定语义。该模型同时约束普通自绘模板和 Avalonia `WindowDrawnDecorations` CSD 模板：

| 语义层 | 代表入口 | 职责 | 命中语义 |
| --- | --- | --- | --- |
| 标题栏背景/装饰层 | `TitleBarFrameBackground` / `TitleBarFrameLayer` / `TitleBarFrameLayerTemplate` | 提供标题栏背景、遮罩、纹理、圆角、裁剪或装饰视觉。 | 不作为用户交互入口；CSD 下可处于标题栏拖拽 role 中。 |
| 默认标题栏层 | `WindowTitleBar` | 展示标题、Logo、`LeftAddOn`、`RightAddOn` 与 caption buttons，并在空白区域提供窗口拖拽语义。 | add-on 与 caption buttons 按普通 Avalonia client input 语义命中；空白区域保留标题栏交互。 |
| 自定义标题栏层 | `NotifyCreateTitleBar` / `NotifyConfigureTitleBar` 扩展点 | 承载需要替换默认标题栏组成或行为的派生窗口实现。 | 派生窗口负责其自定义标题栏的 client input 与空白区域拖拽策略。 |
| 内容区标题栏 | Window 内容逻辑树中的 public `WindowTitleBar` | 自动消费最近 Window 的 caption 状态和操作命令，并在空白区域提供窗口拖动和双击最大化/还原语义。 | 不提供标题栏高度提示，也不取得唯一 CSD chrome role。 |

维护标题栏模板时，不应把 `TitleBarFrameLayer` 提升为可交互覆盖层。需要向默认标题栏加入按钮、菜单或搜索框时，使用 `LeftAddOn` 或 `RightAddOn`；只有需要替换整个标题栏组成或行为时，才在派生 `Window` 中重写标题栏创建与配置扩展点。`Window.TitleBar` 是模板生命周期拥有的 internal 状态，不作为应用 API 公开。

Window 的 title-bar host projection 服务默认、派生和内容区标题栏，并由每个标题栏的 logical attach/detach 生命周期持有。该 lease 同时包含 caption 状态/命令投影和窗口拖动、双击请求的交互订阅。默认标题栏另行消费 Window facade：`Window.TitleAlignment` add-owner `WindowTitleBar.TitleAlignmentProperty`，`LeftAddOn`、`LeftAddOnTemplate`、`RightAddOn` 和 `RightAddOnTemplate` 同样 add-owner 对应标题栏属性，并以 `Template` 优先级单向投影。派生标题栏以 local value 提供的内置操作区优先于 Window facade。`NotifyConfigureTitleBar` 只扩展这组默认内容配置，不负责通用宿主发现或 caption command 接入。Window 提供内容、平台、CSD、WindowState 和原生 chrome 安全区，但不实现标题排列公式。

CSD 模式下，`IsTitleBarVisible=false` 只隐藏 AtomUI drawn title-bar frame、shadow 和 presenter，不把 `WindowDecorations` 从 `Full` 降级为 `BorderOnly`。内容区同时移除 Avalonia drawn title-bar 对顶部 decoration margin 的占位，但继续保留 frame 和 shadow margin，因此用户内容可以到达窗口顶部且不破坏可调整大小边框。窗口最小化、最大化和恢复继续通过 Avalonia `WindowState` 表达，由 Windows DWM、macOS AppKit 或 Linux 窗口管理器/合成器在平台支持范围内执行原生状态转换和动画；AtomUI 不伪造窗口缩放动画，也不为 caption button 建立平台专用状态旁路。macOS 非 CSD 且隐藏原生标题栏时仍可使用 `BorderOnly`，该分支不改变 CSD 契约。

默认标题栏的 add-on 可直接使用 AXAML 属性元素配置：

```xml
<atom:Window>
  <atom:Window.LeftAddOn>
    <Button Content="Back" />
  </atom:Window.LeftAddOn>
  <atom:Window.RightAddOn>
    <Button Content="Settings" />
  </atom:Window.RightAddOn>
</atom:Window>
```
完整协作模型见 [WindowTitleBar 实现原理](../window-title-bar/implementation.md)。

### 5.2 跨平台首帧主题表面模型

Windows、macOS 和 Linux 共用同一个首次显示主题契约：平台窗口进入可见状态前，`Window` 必须已经获得目标
`ThemeContext`、与该 context 一致的 `RequestedThemeVariant`，以及当前 Window Token scope 中的首帧背景。
该契约由 `Window` 的共享显示生命周期负责，不属于 Win32、AppKit、X11 或 Wayland chrome manager 的职责。

首次显示按以下所有权模型维护：

- `WindowTheme.axaml` 是窗口背景的唯一长期视觉所有者，`WindowToken.DefaultBackground` 是默认背景语义真源。
- `Show` 和 `ShowDialog` 的所有 AtomUI 入口必须先解析 owner 作用域并挂载可释放的 `ThemeContext` lease，再进入 Avalonia 的平台显示流程。
- 在正式 ControlTheme 接管前，`Window` 从已提交的当前作用域 Snapshot 同步读取一次默认背景，并以低于用户 local value 的优先级临时预热 `Background` 与 `TransparencyBackgroundFallback`。
- 首帧预热只覆盖同步显示临界区，不订阅资源变化；Avalonia 完成同步样式应用后立即释放临时值，由正式 ControlTheme 继续响应主题切换。
- 用户显式设置的 `Background` 或 `TransparencyBackgroundFallback` 始终优先，首帧预热不得改写或清除用户 local value。
- 平台 chrome manager 只处理窗口装饰、原生几何和平台能力投影，不得分别复制 ThemeContext、Token 查找或首帧背景算法。

如果上述 managed 状态在平台显示前已经正确，某个平台仍然暴露尚未提交内容的原生空白 surface，则该问题属于平台后端边界。此时应通过统一的平台能力接口提供最小后备实现，并分别验证对应后端；不得把平台消息、延时显示或透明度切换混入共享主题状态机。

Token 来源：

Window Token 只表达组件级视觉变量，例如尺寸、间距、颜色、圆角、阴影、图标尺寸和弹层边界。Token 不承载运行时选择、展开、加载、错误、上传任务、过滤条件或业务状态。

当前 Token scope：

- `WindowToken`，scope id 为 `Window`，源码位于 `src/AtomUI.Desktop.Controls/Window/WindowToken.cs`。

## AOT 与裁剪注意事项

资源和 AOT 约束：

- 不通过运行时反射扫描 public API、Token 或 Gallery 示例数据。
- 不把可静态声明的模板结构迁移到 C# 动态创建。
- 异步加载、上传、弹层和窗口生命周期必须能取消或释放。
- 缓存对象必须与控件、窗口、弹层或数据 owner 生命周期一致。
- Source generator 生成文件不手工编辑；需要修改时改输入源或 generator。

性能边界：

- 控件应优先复用 Avalonia 原生虚拟化、模板绑定和资源系统。
- 避免为每次状态变化创建不必要的视觉对象、订阅或动画对象。
- TitleBar 宿主发现只使用逻辑祖先和强类型 AvaloniaProperty binding，不使用全局 Window registry、反射或字符串 binding path；状态变化复用现有 lease，template reapply 不重复创建 lease。
- 首次显示主题表面使用一次性 Snapshot 读取，不为同步 `Show` 临界区创建资源 observable 或订阅。
- 大集合控件必须保证 container recycle 后不会泄漏旧 item 状态。

## 源码索引

主要源码文件：

- `src/AtomUI.Desktop.Controls/Window/Chrome/WindowChromeManager.cs`
- `src/AtomUI.Desktop.Controls/Window/Chrome/AbstractLinuxWindowChromeManager.cs`
- `src/AtomUI.Desktop.Controls/Window/Chrome/GenericLinuxWindowChromeManager.cs`
- `src/AtomUI.Desktop.Controls/Window/Chrome/X11WindowChromeManager.cs`
- `src/AtomUI.Desktop.Controls/Window/Chrome/WaylandWindowChromeManager.cs`
- `src/AtomUI.Desktop.Controls/Window/Chrome/WindowsWindowChromeManager.cs`
- `src/AtomUI.Desktop.Controls/Window/MacStandardWindowButtons.cs`
- `src/AtomUI.Desktop.Controls/Window/MediaBreakPointThemeBootstrapper.cs`
- `src/AtomUI.Desktop.Controls/Window/ReactiveWindow.cs`
- `src/AtomUI.Desktop.Controls/Window/Themes/FullscreenPopoverLayerTheme.axaml`
- `src/AtomUI.Desktop.Controls/Window/Themes/WindowDrawnDecorationsTheme.axaml`
- `src/AtomUI.Desktop.Controls/Window/Themes/WindowResizerTheme.axaml`
- `src/AtomUI.Desktop.Controls/Window/Themes/WindowTheme.axaml`
- `src/AtomUI.Desktop.Controls/Window/Themes/WindowTheme.cs`
- `src/AtomUI.Desktop.Controls/Window/Utils/FullscreenPopoverLayer.cs`
- `src/AtomUI.Desktop.Controls/Window/Utils/WindowDrawnDecorationsReflectionExtensions.cs`
- `src/AtomUI.Desktop.Controls/Window/Utils/WindowResizer.cs`
- `src/AtomUI.Desktop.Controls/Window/Utils/WindowTitleBarShadowBackground.cs`
- `src/AtomUI.Desktop.Controls/Window/Utils/WindowVisualLayerClip.cs`
- `src/AtomUI.Desktop.Controls/Window/Window.cs`
- `src/AtomUI.Desktop.Controls/Window/WindowToken.cs`
- `src/AtomUI.Native/WindowExtensions.cs`
- `src/AtomUI.Native/Linux/WaylandWindowReflectionExtensions.cs`
- `src/AtomUI.Native/Linux/WaylandWindowUtils.cs`
- `src/AtomUI.Native/Linux/WindowUtils.Linux.cs`

职责边界：

- 控件主文件保留 public/protected API、Avalonia 属性注册、事件和主要生命周期入口。
- `Window/Chrome` 只承载平台 chrome manager：选择后端、订阅 Window/PlatformImpl 事件、合并 frame geometry 更新，并把 X11、Wayland、Windows 的原生能力投影为 Window 内部状态。
- `Window/Utils` 承载 Window 模板内部视觉 helper 和 Desktop drawn decorations 反射边界，例如 visible frame clip、managed resize grip、macOS 全屏 popover 与 `DynamicDependency` 标注；这些类型是 internal 协作对象，不是用户 API。
- `AtomUI.Native` 只执行已经确定后端之后的底层平台调用，例如 XCB input region、Xlib geometry、Wayland `wl_surface.set_input_region`。X11 shadow 输入区订阅策略和 resize band 仍属于 `X11WindowChromeManager`，不下沉到 Native。
- Theme 文件负责静态视觉结构、template part、selector 和资源绑定。
- Token 文件只提供组件视觉变量，不保存实例状态。
- Gallery 文件只展示用法和示例，不作为运行时逻辑 owner。

## 相关文档

- 源设计文档：`docs/controls/desktop/window/window/overview.md`
- 实现文档：`docs/controls/desktop/window/window/implementation.md`
- Token 文档：`docs/controls/desktop/window/window/token.md`
- 变更记录：`docs/controls/desktop/window/window/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/window-title-bar/index-cn.md

# WindowTitleBar

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

`WindowTitleBar` 负责以下内容：

- 展示窗口 Logo、标题以及标题模板。
- 承载标题栏左右两侧的应用自定义内容。
- 为系统 caption buttons 提供统一交互表面；连接到 Window 后同时参与窗口拖动和双击最大化。
- 自动发现最近的 AtomUI `Window`，接收平台、激活状态、窗口状态和操作命令，并投影为稳定的模板状态。
- 在不同平台和窗口装饰模式下保持标题、原生按钮、managed buttons 与 add-on 互不覆盖。

`WindowTitleBar` 不是通用工具栏或导航栏。业务操作应放入 `LeftAddOn`、`RightAddOn` 或专用控件，并保留标题栏拖动区域和系统窗口操作的优先级。需要复用 AtomUI managed caption visual 的业务按钮使用 `WindowTitleBarButton` 或 `WindowTitleBarToggleButton`；这两个控件不承载系统窗口操作。

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/General/Window` |
| 状态 | Stable |

## 何时使用

标题栏由四类语义内容构成：

| 语义 | 内容 | 责任 |
| --- | --- | --- |
| Leading | Windows/Linux: `Logo + LeftAddOn`；macOS: `LeftAddOn` | 承载靠近起始侧的应用操作，并占用标题安全空间。Windows/Linux 中可见 Logo 位于物理最左侧，先于 `LeftAddOn`；两者同时有效时使用 `LogoAndLeftAddOnSpacing` 分隔。 |
| Title | Windows/Linux: `Title`；macOS: `Logo + Title` | 承载标题内容并执行对齐和裁剪。macOS 保留连续 Logo/Title 标题组以配合原生窗口按钮安全区。 |
| Trailing | `RightAddOn + CaptionButtonGroup` | 承载结束侧应用操作和 managed window operations。 |
| Native chrome | 平台原生窗口按钮或 overlay | 不进入 visual tree，通过窗口边缘安全区参与布局。 |

三块 managed 区域与 native chrome 的完整几何关系由本文第 8 节和 [WindowTitleBar 实现原理](implementation.md) 定义。Windows/Linux 的 Logo 属于 Leading，占用左侧操作安全空间；Logo 与 `LeftAddOn` 的条件间距同样属于 Leading 实测宽度。macOS 的 Logo 属于 Title，以保持平台标题行为。左右 add-on 属于操作区，不参与标题中心计算。

控件家族的职责边界：

| 类型 | 可见性 | 职责 |
| --- | --- | --- |
| `WindowTitleBar` | public | 公共内容契约、宿主状态投影、交互入口和主题入口。 |
| `WindowTitleBarButton` | public | 在 LeftAddOn/RightAddOn 中提供与 managed caption visual 一致的普通图标按钮。 |
| `WindowTitleBarToggleButton` | public | 在 LeftAddOn/RightAddOn 中提供与 managed caption visual 一致的 checked/unchecked 图标按钮。 |
| `WindowTitleBarLogoVisibility` | public | 定义 Logo 的显示策略。 |
| `WindowTitleBarTitleAlignment` | public | 定义标题组的跨平台对齐语义。 |
| `CaptionButtonGroup` | internal | 根据宿主投影的能力、requested visibility 和窗口状态推导 effective visibility，并把固定窗口操作转发给宿主命令。 |
| `CaptionButton` | internal | 承载系统 caption icon、checked icon 和按钮视觉状态。 |
| `WindowsCaptionButton` | internal | 提供 Windows 方形 caption button 尺寸和 hover 状态修正。 |

internal 类型服务于 AtomUI 内置主题，不属于应用可直接创建或继承的公共控件 API。

## 公共 API

### 3.1 标题内容

| API | 默认值 | 语义 |
| --- | --- | --- |
| `Logo` | `null` | Logo 内容对象；只承载开发者显式值，框架不写入默认值。未设置时标题栏渲染层回退到窗口 `Icon`（再回退主窗口 Logo/Icon），运行时设为 `null` 即回到该默认，彻底隐藏用 `LogoVisibility=Never`。 |
| `LogoTemplate` | `null` | Logo 数据模板；与 `Logo` 同为显式值，未设置时由渲染层的 effective 解析决定。 |
| `LogoVisibility` | `Auto` | Logo 显示策略。 |
| `Title` | `null` | 标题内容对象。 |
| `TitleTemplate` | `null` | 标题数据模板。 |
| `TitleAlignment` | `Auto` | 标题组对齐策略。 |
| `IsTitleVisible` | `true` | 是否渲染标题栏内的标题文字。设为 `false` 时仅隐藏标题栏呈现，系统级窗口标题（任务栏、窗口切换）不受影响；注意与 `IsTitleBarVisible`（隐藏整条标题栏，含 caption 按钮）区分；`LogoVisibility=Auto` 此时按无标题分支联动。 |

`Logo` 与 `Title` 都接受任意对象。字符串标题为空或仅包含空白时按“无标题内容”处理；非字符串对象按存在标题内容处理。标题文字的有效显示模型见第 4.2 节。

### 3.2 Add-on

| API | 默认值 | 语义 |
| --- | --- | --- |
| `LeftAddOn` | `null` | Leading 区域内容。 |
| `LeftAddOnTemplate` | `null` | Leading 内容模板。 |
| `RightAddOn` | `null` | Trailing 区域中位于 caption buttons 之前的内容。 |
| `RightAddOnTemplate` | `null` | Trailing 内容模板。 |

Add-on 可以包含可交互控件，也可以是 `null`、隐藏节点或当前没有孩子的容器。内置模板必须保留其命中测试能力，同时避免 Title 覆盖这些区域。Windows/Linux 仅在 `PART_Logo` 与 `PART_LeftAddOn` 两个 presenter 同时可见时产生 `LogoAndLeftAddOnSpacing`；Logo 隐藏、LeftAddOn 为 `null` 或 presenter 不可见时不保留该间距。该条件遵循 Avalonia sibling layout 的可见性语义，不根据子内容的实测宽度建立第二套状态。Leading 或 Trailing 的实测宽度为零时，该区域不占用标题安全空间，也不产生 `HeaderHorizontalSpacing`；内容出现、隐藏或动态替换后由正常 measure invalidation 重新计算。

需要与标题栏 managed caption visual 保持一致的应用操作，可以直接使用 `WindowTitleBarButton` 或
`WindowTitleBarToggleButton`。前者继承 `IconButton`，使用 `Icon`、`Command` 和标准按钮输入语义；后者继承
`ToggleIconButton`，使用 `CheckedIcon`、`UnCheckedIcon`、`IsChecked` 和标准 ToggleButton 输入语义。两个控件的
业务命令、启用状态和 checked 状态仍由应用所有，不映射到 Window 的系统操作。Windows 中两者默认占满标题栏可用高度并形成方形交互面，使用直角、透明常态背景和 Arrow 光标，业务图标继续使用 `CaptionButtonIconSize`；Linux/macOS 保持内容驱动的圆角 managed visual 与 Hand 光标。按钮容器间距由应用内容所有，Windows 中需要连续 hover 按钮带时应使用零 spacing。

### 3.3 宿主状态

| API | 语义 |
| --- | --- |
| `IsWindowActive` | 表示宿主窗口是否激活，并驱动标题和 caption buttons 的 active/inactive 视觉。 |
| `IsMotionEnabled` | 控制标题栏及 caption button 的状态过渡。 |
| `OsType` | 只读平台标识，用于选择平台主题和布局 Strategy。 |
| `OsVersion` | 只读平台版本，用于受版本约束的平台能力判断。 |

`OsType` 和 `OsVersion` 由 AtomUI 平台感知基础设施写入。应用不通过 CLR setter 修改这两个值。

### 3.4 交互事件

`MaximizeWindowRequested` 表示标题栏收到有效的主按钮双击请求。`WindowTitleBar` 不直接修改 `WindowState`；宿主 `Window` 根据 `CanResize`、`CanMaximize` 和当前状态决定最大化或还原。

事件在对应的 `PointerReleased` 阶段发出，避免窗口同步 resize 破坏当前 pointer capture。pointer capture 丢失或释放条件不匹配时，请求被取消。

每个连接到 AtomUI `Window` 的 `WindowTitleBar` 都是窗口交互表面：空白区域可按 `Window.IsMoveEnabled` 启动窗口拖动，主按钮双击可请求 Normal/Maximized 切换。该交互连接与 caption 状态投影由同一个 host lease 管理；标题栏离开逻辑树、切换宿主或 Window 关闭后不再操作旧宿主。标题栏高度提示和 CSD 几何仍只由 Window 模板正式接入的默认标题栏提供。

## 事件与命令

业务命令、启用状态和 checked 状态仍由应用所有，不映射到 Window 的系统操作。Windows 中两者默认占满标题栏可用高度并形成方形交互面，使用直角、透明常态背景和 Arrow 光标，业务图标继续使用 `CaptionButtonIconSize`；Linux/macOS 保持内容驱动的圆角 managed visual 与 Hand 光标。按钮容器间距由应用内容所有，Windows 中需要连续 hover 按钮带时应使用零 spacing。
### 3.4 交互事件
事件在对应的 `PointerReleased` 阶段发出，避免窗口同步 resize 破坏当前 pointer capture。pointer capture 丢失或释放条件不匹配时，请求被取消。

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础窗口

来源：`controlgallery/AtomUIGallery/ShowCases/General/Window/Views/WindowShowCase.axaml:34`

Gallery key：`ExamplesContent` / item `0`

```axaml
<atom:Button ButtonType="Primary"
```

### 标题对齐

来源：`controlgallery/AtomUIGallery/ShowCases/General/Window/Views/WindowShowCase.axaml:47`

Gallery key：`ExamplesContent` / item `1`

```axaml
<atom:Button ButtonType="Primary"
```

### 隐藏标题文字

来源：`controlgallery/AtomUIGallery/ShowCases/General/Window/Views/WindowShowCase.axaml:61`

SourceKey：`window-title-visibility`

```axaml
<atom:Button ButtonType="Primary"
```

### Logo 显示策略

来源：`controlgallery/AtomUIGallery/ShowCases/General/Window/Views/WindowShowCase.axaml:74`

Gallery key：`ExamplesContent` / item `3`

```axaml
<atom:Button ButtonType="Primary"
```

## 状态模型

### 4.1 Logo 显示模型

`LogoVisibility` 的三个值具有以下语义：

| 值 | 规则 |
| --- | --- |
| `Always` | 存在 `Logo` 或 `LogoTemplate` 时显示。 |
| `Never` | 始终隐藏。 |
| `Auto` | 根据标题内容、平台和窗口状态计算。 |

`Auto` 的计算矩阵：

| 条件 | 结果 |
| --- | --- |
| 不存在 Logo 内容和 Logo 模板 | 隐藏。 |
| 存在有效标题内容且标题未被 `IsTitleVisible=False` 隐藏 | 显示。 |
| 无标题内容且平台为 macOS | 隐藏。 |
| 无标题内容、平台不是 macOS、窗口非全屏 | 显示。 |
| 无标题内容、平台不是 macOS、窗口全屏 | 隐藏。 |

该模型只控制 Logo 的有效可见性，不修改 `Logo`、`Window.Icon` 或应用图标来源。

每个 `WindowTitleBar` 先解析自己的显式 `Logo` / `LogoTemplate`；两者都未设置时，才消费宿主 `Window` 解析出的有效 Logo。因而独立标题栏和窗口内的自定义标题栏都保留自身公共内容契约，宿主 `Icon` 与主窗口 Logo/Icon 仅作为回退，不覆盖标题栏的显式值。

### 4.2 标题显示模型

`IsTitleVisible` 控制标题栏内的标题文字呈现，与 `Window.IsTitleBarVisible`（隐藏整条标题栏）作用域不同：

| `IsTitleVisible` | `Title` | 有效可见性 |
| --- | --- | --- |
| `true`（默认） | `null` | 隐藏。 |
| `true`（默认） | 非 `null`（含空字符串） | 显示；空字符串渲染空内容，与历史行为逐位一致。 |
| `false` | 任意 | 隐藏；系统级窗口标题（任务栏、窗口切换）不受影响。 |

- 有效值计算为 `IsEffectiveTitleVisible = IsTitleVisible && Title is not null`；三平台模板的 `PART_ContentPresenter` 与全屏层 `FullscreenTitleText` 只绑定该 internal direct property（全屏层绑定 Window 侧 `IsEffectiveFullscreenTitleVisible`）。
- `IsTitleVisible=False` 时 `LogoVisibility=Auto` 按“无标题”分支联动：macOS 上 Logo 随标题一起隐藏，全屏层同步；非 macOS 非全屏时 Logo 仍显示。`Always`/`Never` 不受影响。
- `Window` 通过 `NotifyConfigureTitleBar` 投影 `IsTitleVisible`，与 `LogoVisibility` 同优先级。

### 4.3 窗口状态

`WindowTitleBar` 从宿主 `Window` 的单向属性投影接收状态并维护以下伪类：

| 伪类 | 条件 |
| --- | --- |
| `:active` | 宿主窗口处于激活状态。 |
| `:normal` | `WindowState.Normal`。 |
| `:minimized` | `WindowState.Minimized`。 |
| `:maximized` | `WindowState.Maximized`。 |
| `:fullscreen` | `WindowState.FullScreen`。 |

窗口激活状态同时写入 `IsWindowActive`，供模板中的内部协作控件使用。状态 owner 始终是宿主 `Window`；模板节点不反向维护第二份窗口状态。

标题栏在进入逻辑树时自动选择最近的 AtomUI `Window` 作为宿主，并为自身持有一个可释放的 host projection lease。同一 Window 可以包含多个 `WindowTitleBar`，每个实例都独立接收同一宿主状态；标题栏从逻辑树移除或转移到另一 Window 时，旧投影必须释放并由新宿主重新建立。

### 4.4 Caption buttons

caption button 的公共配置属于宿主 `Window`：

- `CanMinimize`、`CanMaximize` 和平台能力决定操作是否允许，不承担 managed button 的呈现配置。
- `IsMinimizeCaptionButtonVisible`、`IsMaximizeCaptionButtonVisible`、`IsCloseCaptionButtonVisible`、`IsFullScreenCaptionButtonVisible` 和 `IsPinCaptionButtonVisible` 分别表达 managed button 的 requested visibility。
- `Topmost`、`WindowState`、平台 backend、requested visibility 和 operation capability 共同决定 checked state 与 effective visibility。

Minimize、Maximize 和 Close 默认显示，FullScreen 和 Pin 默认隐藏。全屏时隐藏最小化和最大化按钮；最大化时隐藏进入全屏按钮；capability 为 `false` 时不显示不可执行的 managed button。Wayland backend 不提供置顶按钮。隐藏按钮不修改 `CanMinimize`、`CanMaximize`、`Topmost` 或其他窗口操作入口。完整状态矩阵、平台边界和单向命令流见 [WindowTitleBar Caption Button 配置设计](caption-button-configuration-design.md)。

### 4.5 拖动和双击

`Window` 通过每个标题栏的 host lease 监听 pointer 事件，并在移动距离超过拖动阈值后调用原生 `BeginMoveDrag`。拖动状态记录具体来源标题栏，同一 Window 中其他标题栏的移动、释放或 capture lost 不能推进该次交互。`IsMoveEnabled=False` 或全屏状态禁止拖动。双击最大化与拖动共用标题栏输入表面，但 caption buttons 和 add-on 的已处理输入不应触发窗口拖动。

应用直接放入 Window 内容区的 `WindowTitleBar` 自动获得 caption 状态、窗口操作命令、拖动和双击最大化语义。它不参与标题栏高度提示、CSD 最小高度或唯一 CSD geometry owner 计算；这些几何职责只属于 Window 模板正式接入的默认标题栏。

## 主题与 Design Token

`WindowTitleBarTheme` 是 `WindowTitleBar` 的 ControlTheme 入口。内置主题保留以下稳定 template part 与语义节点：

| 节点 | 类型 | 契约 |
| --- | --- | --- |
| `Frame` | `Border` | 绘制标题栏背景并提供完整可见 frame 的布局边界。 |
| `PART_Logo` | `ContentPresenter` | 展示有效 Logo；Windows/Linux 模板中位于 Leading 最左侧，macOS 模板中位于 Title 内容前。 |
| `PART_ContentPresenter` | `ContentPresenter` | 展示标题，可见性绑定 `IsEffectiveTitleVisible`；字符串标题在安全宽度不足时使用字符省略号，且不参与命中测试。 |
| `PART_LeftAddOn` | `ContentPresenter` | 展示 Leading 内容并投影 AddOn active、motion 和 platform 上下文；Windows/Linux 中由 Leading 容器负责它与有效 Logo 之间的条件间距。 |
| `PART_RightAddOn` | `ContentPresenter` | 展示 Trailing add-on 并投影 AddOn active、motion 和 platform 上下文。 |
| `PART_CaptionButtonGroup` | `CaptionButtonGroup` | 消费宿主投影，推导 managed button 状态并转发固定窗口操作。 |

`PART_CaptionButtonGroup` 是 `WindowTitleBar` 模板中的稳定协作 part，通过 `TemplateBinding` 接收能力、requested visibility、窗口状态和宿主命令。其内部 `PART_CloseButton`、`PART_MinimizeButton`、`PART_MaximizeButton`、`PART_FullScreenButton` 和 `PART_PinButton` 属于 `CaptionButtonGroup` 模板，不是 `WindowTitleBar` 的 public template part。

Windows/Linux 的 Leading 容器使用 `HorizontalSpacing` 消费 `LogoAndLeftAddOnSpacing`，不通过菜单、按钮或 `PART_LeftAddOn.Margin` 补偿相邻 Logo。该组合规则使间距跟随两个 presenter 的可见性，并允许任意 `LeftAddOn` 内容获得一致的视觉隔离。macOS 的 Leading 只有 `PART_LeftAddOn`，Logo/Title 间距继续由 Title role 的 `LogoAndTitleSpacing` 管理。

平台主题可以改变 caption button 外观和 native chrome 来源，但不得改变 Public API 语义、Title/Leading/Trailing 角色或窗口操作行为。应用替换完整 ControlTheme 时负责提供等价区域、裁剪和命中测试；internal caption 类型不作为定制 API。

视觉尺寸、间距、active/inactive 颜色和 caption button 状态颜色由 [WindowTitleBar Token 设计](token.md) 管理。标题对齐值、CSD 状态和窗口状态不是 Token。

Token 来源：

`WindowTitleBarToken` 是 scope id 为 `WindowTitleBar` 的 internal control token，源码位于 `src/AtomUI.Desktop.Controls/WindowTitleBar/WindowTitleBarToken.cs`。它从 `SharedToken` 计算标题栏和 caption button 的视觉变量，并通过生成的 `WindowTitleBarTokenResource` key 供 AXAML 使用。

Token 负责尺寸、间距、字体和状态颜色，不负责以下运行时语义：

- `TitleAlignment`、Leading/Title/Trailing 角色和布局公式。
- CSD、native chrome insets、WindowState 和 backend 能力。
- Logo、标题、add-on 或 caption button 的有效可见性。
- pointer capture、拖动、checked state 和窗口操作。

## AOT 与裁剪注意事项

- Window 定义强类型 host projection 字段集合并创建 lease；每个 WindowTitleBar 按 logical attach/detach 生命周期独立持有和释放 lease。effective Logo 通知属于同一 lease；宿主引用只用于解析 Logo 回退，不作为 caption 状态旁路，CaptionButtonGroup 不持有 Window relay binding 或宿主引用。
- 宿主发现只遍历当前逻辑祖先，不使用全局 Window registry、反射、字符串 binding path 或程序集扫描。
- 每个标题栏与每次宿主连接只创建一个固定大小 lease；状态更新复用现有 binding 和交互订阅，template reapply 不重建 lease。
- 标题布局 Strategy 使用静态无状态实例；measure/arrange 不创建 Context、Plan、binding 或临时 Visual。
- TemplateBinding 和 selector 承担静态视觉投影，不在状态变化时重建模板节点。
- Windows caption 类按钮共享无状态测量函数，不保存 Visual、不创建 binding，不引入反射或 AOT 注册。
- Logo 计算只在显式内容、Icon、宿主 effective 内容或显示策略相关状态变化时执行；子 Window 的主窗口回退订阅只在窗口已打开且依赖回退时存在，并在本地接管或 close 时释放。
- native chrome metrics 缓存属于 Window/platform manager，不能复制到 Panel 或 Strategy。
- 平台 Strategy 使用封闭 `OsType` switch，不使用反射、程序集扫描、字符串类型发现或运行时 DI。
- Token 通过生成的静态资源入口消费；不反射枚举 public API 或 Token 属性。

## 源码索引

```text
src/AtomUI.Desktop.Controls/
├── Window/
│   ├── Window.cs
│   ├── MacStandardWindowButtons.cs
│   ├── Chrome/
│   │   ├── WindowChromeManager.cs
│   │   ├── AbstractLinuxWindowChromeManager.cs
│   │   ├── GenericLinuxWindowChromeManager.cs
│   │   ├── X11WindowChromeManager.cs
│   │   ├── WaylandWindowChromeManager.cs
│   │   └── WindowsWindowChromeManager.cs
│   ├── Utils/
│   │   ├── FullscreenPopoverLayer.cs
│   │   └── WindowVisualLayerClip.cs
│   └── Themes/
│       ├── WindowTheme.axaml
│       ├── WindowDrawnDecorationsTheme.axaml
│       └── FullscreenPopoverLayerTheme.axaml
├── WindowTitleBar/
│   ├── WindowTitleBar.cs
│   ├── WindowTitleBarButton.cs
│   ├── WindowTitleBarToggleButton.cs
│   ├── WindowTitleBarHostContext.cs
│   ├── WindowTitleBarLogoVisibility.cs
│   ├── WindowTitleBarTitleAlignment.cs
│   ├── WindowTitleBarLayoutPanel.cs
│   ├── CaptionButtonGroup.cs
│   ├── CaptionButton.cs
│   ├── WindowsCaptionButton.cs
│   ├── WindowsCaptionButtonLayout.cs
│   ├── WindowTitleBarToken.cs
│   ├── Strategies/
│   │   ├── IWindowTitleBarLayoutStrategy.cs
│   │   ├── MacOSWindowTitleBarLayoutStrategy.cs
│   │   ├── WindowsWindowTitleBarLayoutStrategy.cs
│   │   └── LinuxWindowTitleBarLayoutStrategy.cs
│   └── Themes/
│       ├── WindowTitleBarTheme.axaml
│       ├── WindowTitleBarButtonTheme.axaml
│       ├── WindowTitleBarToggleButtonTheme.axaml
│       ├── CaptionButtonGroupTheme.axaml
│       ├── CaptionButtonTheme.axaml
│       └── WindowsCaptionButtonTheme.axaml
└── ImagePreviewer/
    ├── ImagePreviewerTitleBar.cs
    └── Themes/ImagePreviewerTitleBarTheme.axaml
```

标题布局输入和中间结果使用标量、`Thickness`、`Rect` 与命名 tuple 传递，不建立独立 Context、Plan 或布局结果类型。

## 相关文档

- 源设计文档：`docs/controls/desktop/window/window-title-bar/overview.md`
- 实现文档：`docs/controls/desktop/window/window-title-bar/implementation.md`
- Token 文档：`docs/controls/desktop/window/window-title-bar/token.md`
- 变更记录：`docs/controls/desktop/window/window-title-bar/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/border-beam/index-cn.md

# BorderBeam

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

BorderBeam 是 AtomUI 桌面其他类控件中的装饰性包装控件，用于在容器边界上绘制持续流动的高光效果。它强化某个容器的视觉关注度，但不表达焦点态、校验态、选中态、错误态、警告态或任何业务状态。

BorderBeam 的职责是围绕一个内容控件绘制一个或多个流光边界，并在不拦截内容交互的前提下提供数量、颜色、渐变、外扩、圆角、线宽和动效控制。它不拥有内容控件的布局语义，不改变内容控件的输入、焦点、命中测试、状态同步、数据绑定或模板结构。

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `controlgallery/AtomUIGallery/ShowCases/Other/BorderBeam` |
| 状态 | Stable |

## 何时使用

BorderBeam 的设计语言是“非业务状态的动态强调”。它通过沿容器边界运行的高光段形成持续注意力提示，但保持内容本身的控件语义不变。

| 维度 | 含义 | 典型表达 |
| --- | --- | --- |
| 装饰性 | 流光只是视觉强调，不参与业务状态判断。 | `IsHitTestVisible=false` 的渲染层。 |
| 容器边界 | 流光贴合容器有效边框和圆角。 | `IBorderBeamAwareControl` 或显式 `BorderThickness` / `CornerRadius`。 |
| 品牌强调 | 默认颜色来自主题主色。 | `ColorPrimary`、`ColorPrimaryHover`。 |
| 渐变尾迹 | 用户停靠点映射到可见段，尾部保留透明衰减。 | `ColorStops.Percent` 映射到可见段。 |
| 持续流动 | 默认流光不跟随全局动效开关关闭，保持装饰强调一致可见。 | `IsMotionEnabled` 实例开关。 |
| 均匀分布 | 多个光束共享一个周期，并沿同一边界保持等距。 | `Count` 与共享 `Progress`。 |

BorderBeam 不应绘制成一个新的实体边框，也不应让被装饰控件看起来拥有新的可交互状态。流光层应贴合容器边界，透明尾迹应保持连续，圆角转弯处不应出现断裂。

## 公共 API

BorderBeam 的公共 API 采用 Avalonia 属性模型。需要 XAML 设置、Style 设置、绑定、主题参与或动画参与的标量状态定义为 `StyledProperty`。可变集合类状态使用 `DirectProperty` 加实例级集合，避免共享默认集合和构造函数 local value 覆盖外部绑定。

BorderBeam 控件 API：

| API | 类型 | 语义 |
| --- | --- | --- |
| `Content` | `object?` | 被装饰内容，继承自 `ContentControl`。 |
| `Color` | `Color?` | 单色流光配置。 |
| `ColorStops` | `AvaloniaList<BorderBeamColorStop>` | 渐变流光停靠点集合。 |
| `Outset` | `Thickness?` | 流光层相对有效边界的渲染偏移；`null` 时贴合有效边界，不额外改变绘制范围。 |
| `BorderThickness` | `Thickness` | 未命中感知接口时使用的边框厚度，同时决定流光线宽；默认值来自共享边框厚度 Token。 |
| `CornerRadius` | `CornerRadius` | 未命中感知接口时使用的圆角。 |
| `IsMotionEnabled` | `bool` | 控制当前实例的流光动画是否启用；默认值不绑定全局 motion 设置。 |
| `Duration` | `TimeSpan` | 流光运行一周的时长；默认值为 `6s`。 |
| `BeamSize` | `double` | 流光高光段的设备无关像素基准尺寸；默认值为 `100`。 |
| `Count` | `int` | 沿同一边界均匀分布的光束数量；默认值为 `1`，小于 `1` 时按 `1` 渲染。 |

`ColorStops` 非空时优先于 `Color`。`ColorStops` 为空且 `Color` 不为空时，使用单色流光。两者均为空时，使用主题默认渐变。

边界感知接口：

```csharp
public interface IBorderBeamAwareControl
{
    BorderBeamGeometry GetBorderBeamGeometry();
    event EventHandler? BorderBeamGeometryChanged;
}

public readonly record struct BorderBeamGeometry(
    Thickness BorderThickness,
    CornerRadius CornerRadius);
```

`IBorderBeamAwareControl` 是被装饰控件向 BorderBeam 暴露有效边界几何的显式契约。它只描述可用于流光贴合的边框厚度和圆角，不暴露控件状态、不暴露模板节点、不要求 BorderBeam 理解被装饰控件的内部实现。

稳定 template part：

| Template Part | 类型 | 职责 |
| --- | --- | --- |
| `PART_ContentPresenter` | `ContentPresenter` | 承载被装饰内容。 |
| `PART_BeamPresenter` | internal `BorderBeamPresenter` | 绘制边框流光，不参与命中测试。 |

## 事件与命令

event EventHandler? BorderBeamGeometryChanged;

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

以下示例来自 Gallery 源码查看使用的 `ShowCaseItem` 片段，并已按中文资源规范化。

### 基础

来源：`controlgallery/AtomUIGallery/ShowCases/Other/BorderBeam/Views/BorderBeamShowCase.axaml:46`

SourceKey：`border-beam-basic`

```axaml
<atom:BorderBeam Width="360"
                 HorizontalAlignment="Left">
    <atom:Card Header="Workspace overview"
               BorderThickness="1"
               CornerRadius="8">
        <Grid ColumnDefinitions="*,*,*"
              RowDefinitions="Auto,Auto"
              MinHeight="96">
            <atom:TextBlock Grid.Column="0"
                            Text="Users"
                            Foreground="{atom:SharedTokenResource ColorTextTertiary}" />
            <atom:TextBlock Grid.Column="1"
                            Text="Projects"
                            Foreground="{atom:SharedTokenResource ColorTextTertiary}" />
            <atom:TextBlock Grid.Column="2"
                            Text="Tasks"
                            Foreground="{atom:SharedTokenResource ColorTextTertiary}" />
            <atom:TextBlock Grid.Row="1"
                            Grid.Column="0"
                            Text="128"
                            FontSize="22"
                            FontWeight="Bold" />
            <atom:TextBlock Grid.Row="1"
                            Grid.Column="1"
                            Text="24"
                            FontSize="22"
                            FontWeight="Bold" />
            <atom:TextBlock Grid.Row="1"
                            Grid.Column="2"
                            Text="86"
                            FontSize="22"
                            FontWeight="Bold" />
        </Grid>
    </atom:Card>
</atom:BorderBeam>
```

### 悬停显示

来源：`controlgallery/AtomUIGallery/ShowCases/Other/BorderBeam/Views/BorderBeamShowCase.axaml:93`

SourceKey：`border-beam-show-on-hover`

```axaml
<atom:BorderBeam Width="360"
                 Classes="show-on-hover"
                 HorizontalAlignment="Left">
    <atom:Card Header="将指针移到卡片上"
               BorderThickness="1"
               CornerRadius="8">
        <atom:TextBlock Text="当指针移到此卡片上时，边框流光会显示。"
                        TextWrapping="Wrap"
                        Foreground="{atom:SharedTokenResource ColorTextSecondary}" />
    </atom:Card>
</atom:BorderBeam>
```

### 多光束

来源：`controlgallery/AtomUIGallery/ShowCases/Other/BorderBeam/Views/BorderBeamShowCase.axaml:116`

SourceKey：`border-beam-multiple-beams`

```axaml
<StackPanel Orientation="Vertical"
            Spacing="12"
            HorizontalAlignment="Left">
    <atom:BorderBeam Width="360"
                     Count="3"
                     HorizontalAlignment="Left">
        <atom:Card Header="多光束"
                   BorderThickness="1"
                   CornerRadius="8">
            <atom:TextBlock Text="设置 Count 可沿容器边界均匀分布多个光束。"
                            TextWrapping="Wrap"
                            Foreground="{atom:SharedTokenResource ColorTextSecondary}" />
        </atom:Card>
    </atom:BorderBeam>
    <atom:BorderBeam Width="360"
                     Count="2"
                     HorizontalAlignment="Left">
        <atom:Card Header="多光束"
                   BorderThickness="1"
                   CornerRadius="8">
            <atom:TextBlock Text="设置 Count 可沿容器边界均匀分布多个光束。"
                            TextWrapping="Wrap"
                            Foreground="{atom:SharedTokenResource ColorTextSecondary}" />
        </atom:Card>
    </atom:BorderBeam>
</StackPanel>
```

### 自定义容器

来源：`controlgallery/AtomUIGallery/ShowCases/Other/BorderBeam/Views/BorderBeamShowCase.axaml:154`

SourceKey：`border-beam-custom-container`

```axaml
<atom:BorderBeam Width="420"
                 BorderThickness="1"
                 CornerRadius="8"
                 HorizontalAlignment="Left">
    <Border MinHeight="160"
            Padding="24"
            BorderThickness="1"
            CornerRadius="8"
            BorderBrush="{atom:SharedTokenResource ColorBorderSecondary}"
            Background="{atom:SharedTokenResource ColorBgContainer}">
        <atom:TextBlock Text="在一个自定义容器中查看任务状态、部署健康度和最近的自动化活动。"
                        TextWrapping="Wrap"
                        Foreground="{atom:SharedTokenResource ColorTextSecondary}" />
    </Border>
</atom:BorderBeam>
```

## 状态模型

BorderBeam 的行为优先级：

```text
IsVisible=false
> IsMotionEnabled=false
> Content geometry changed
> Progress animation
> Normal render
```

BorderBeam 不拦截鼠标、触控、键盘或焦点。流光 presenter 必须 `IsHitTestVisible=false`，内容控件继续承担自身交互。BorderBeam 不改变内容控件的 `IsEnabled`、`:pointerover`、`:pressed`、`:focus`、`:disabled` 或任何伪类。

effective state 由几何状态、颜色状态、数量状态和动效状态组成：

- 几何状态：优先读取 `IBorderBeamAwareControl`，未命中时使用 BorderBeam 自身 `BorderThickness` 与 `CornerRadius`。
- 颜色状态：`ColorStops` 优先，其次 `Color`，最后使用主题默认渐变。
- 数量状态：`effectiveCount = Math.Max(1, Count)`；`Count` 变化只触发 presenter 重绘，不替换模板或重启动画。
- 动效状态：实例级 `IsMotionEnabled`、可见性和有效尺寸共同决定动画是否运行；默认主题不从全局 `EnableMotion` 覆盖该属性。

`Progress` 是所有光束共享的 internal animation state。第 `i` 个光束使用
`NormalizeProgress(Progress + i / effectiveCount)` 计算相位。`Progress` 不形成公共 API，不参与样式选择器，
不允许外部绑定。

“仅悬停时显示”不是 BorderBeam 的内置状态或公共属性。调用方可以用 Style 在默认状态设置
`IsMotionEnabled=false`，并在 BorderBeam 根控件的 `:pointerover` 状态设置为 `true`；该组合会复用现有动画
启动与停止路径，内容控件仍保留完整交互。

## 主题与 Design Token

BorderBeam 的模板由内容层和装饰层组成，视觉树保持必要最小层级。

```text
BorderBeam
└─ Grid
   ├─ ContentPresenter#PART_ContentPresenter
   └─ BorderBeamPresenter#PART_BeamPresenter
```

视觉层级要求：

- `BorderBeamPresenter` 覆盖内容层边界，在一个 presenter 内绘制 `effectiveCount` 个等距光束，但不得改变内容层测量和排列结果。
- `BorderBeamPresenter` 必须 `IsHitTestVisible=false`。
- 不把 beam 层插入被装饰控件模板内部，不修改 Card、Button、GroupBox 等控件的模板结构。
- 不使用全局 `ScopeAwareAdornerLayer` 作为默认实现。
- 不在内容控件未实现感知接口时尝试读取其 internal 属性或模板 part。

BorderBeam Theme 只负责装配内容层和流光 presenter，并设置默认 token 绑定。几何解析、颜色归一和动画生命周期属于 C# 状态模型。

Token 来源：

BorderBeamToken 是 BorderBeam 的组件级设计变量层。它只承载流光装饰自身需要的默认动效、尺寸和渐变映射参数。颜色、线宽和圆角优先复用 SharedToken；motion 开关与光束数量保留为实例行为，不由 BorderBeamToken 或 `SharedToken.EnableMotion` 决定。

BorderBeamToken 服务以下主题和控件：

- `BorderBeamTheme.axaml`
- internal `BorderBeamPresenter`
- BorderBeam 渐变归一和动画默认值

BorderBeamToken 不承载 `Content`、`Color`、`ColorStops`、`Outset`、`Count`、`Progress`、`EffectiveBorderThickness`、`EffectiveCornerRadius` 等实例状态。这些状态由 BorderBeam 状态模型和边界感知接口处理。

## AOT 与裁剪注意事项

BorderBeam 不使用反射，不访问内容控件 internal 属性或 template part。集成通过 `IBorderBeamAwareControl` 完成。

实例级 motion 未启用时不应启动循环动画。隐藏祖先下的动画必须暂停；动画取消资源必须在 detached、模板替换、
实例 motion 关闭或动画重启时释放。Content 替换只更新 effective geometry，不重启动画。

ColorStops 使用实例级集合，避免共享默认集合。集合变更应触发渐变重建和 presenter 重绘，不应重建整个模板。

`Count=N` 的结构成本为：1 个 presenter、1 个 Animation、1 个 CancellationTokenSource、每帧 1 次路径指标构建和
N 次光束 draw call。额外持久内存保持 `O(1)`，单帧绘制成本为 `O(N)`；实现不创建 phase 集合、每光束 Control、
每光束 animation 或跨帧路径缓存。

## 源码索引

主要源码：

- `src/AtomUI.Desktop.Controls/BorderBeam/BorderBeam.cs`：公共 API、Content 几何感知、effective geometry 和
  `ColorStops` 集合变更通知。
- `src/AtomUI.Desktop.Controls/BorderBeam/BorderBeamPresenter.cs`：internal 渲染层，负责颜色归一、动画生命周期、
  单时钟多相位路径采样以及边框环内的流动高光绘制。
- `src/AtomUI.Desktop.Controls/BorderBeam/IBorderBeamAwareControl.cs`：被装饰控件边界感知接口。
- `src/AtomUI.Desktop.Controls/BorderBeam/BorderBeamGeometry.cs`：边框厚度和圆角几何值。
- `src/AtomUI.Desktop.Controls/BorderBeam/BorderBeamColorStop.cs`：单个渐变停靠点。
- `src/AtomUI.Desktop.Controls/BorderBeam/BorderBeamColorStops.cs`：颜色停靠点集合支持。
- `src/AtomUI.Desktop.Controls/BorderBeam/BorderBeamToken.cs`：BorderBeam 专属 Token。
- `src/AtomUI.Desktop.Controls/BorderBeam/Themes/BorderBeamTheme.axaml`：内容层和 presenter 层装配。

## 相关文档

- 源设计文档：`docs/controls/desktop/other/border-beam/overview.md`
- 实现文档：`docs/controls/desktop/other/border-beam/implementation.md`
- Token 文档：`docs/controls/desktop/other/border-beam/token.md`
- 变更记录：`docs/controls/desktop/other/border-beam/changelog.md`
- 语义结构：`./semantic-cn.md`

Source: ./controls/popup/index-cn.md

# Popup

> 生成产物：由源文档生成，不要手工编辑。修改内容请回到控件文档、源码 public surface、Token 类型或生成数据、Gallery ShowCase 或源码结构。

## 概述

Popup 是 AtomUI 桌面弹层体系的共享低层原语，继承 Avalonia `Popup` 的 anchor、placement、light-dismiss、native
window 与 overlay host 能力，并增加 AtomUI 的定位、阴影、动效、翻转通知和可选表面契约。Flyout、ToolTip、
ContextMenu、菜单、选择器和 Picker 等专用控件复用该原语，但继续拥有各自的 Presenter 和内容表面。

Popup 不提供默认 Padding、边框、箭头或内容布局。调用方负责 Child 内容结构；Popup 只在 Child bounds 内提供可选的
frame surface 与 host shadow。

Popup 同时拥有弹层家族的内部钉住打开物理契约。`internal IsPopupPinnedOpen` 只供测试和内部诊断使用：内容、锚点、attach、effective visible/enabled、TopLevel 和 placement 全部有效后保持 Popup 打开，普通关闭和关闭动效被拦截；PlacementTarget、logical owner、content、TopLevel 或窗口生命周期失效时仍必须立即清理。解除 pin 不关闭已打开 Popup，但必须取消尚未打开的 pending 请求。控件语义 owner、Flyout、ToolTip 和 ContextMenu 的接入规则见 [Popup 钉住打开设计](popup-pinned-open-design.md)。

## 包与命名空间

| 项 | 值 |
| --- | --- |
| NuGet 包 | `AtomUI.Desktop.Controls` |
| .NET 命名空间 | `AtomUI.Desktop.Controls` |
| AXAML 命名空间 | `https://atomui.net` |
| Gallery 页面 | `` |
| 状态 | Stable |

## 何时使用

Popup 的设计语言是“可定位的透明承载原语”。宿主保持透明并提供窗口/layer 能力，内容默认拥有自己的表面；调用方
显式提供 frame surface 时，该表面只覆盖实际内容 bounds。阴影可以延伸到透明区域，但不能把整个 host 变成不透明矩形。

该模型把 host、frame 和 content 分成稳定职责：host 决定 native/overlay、定位和输入；frame 决定可选表面与阴影；
content 决定 Padding、边框、箭头和业务视觉。

## 公共 API

| API | 类型 | 语义 |
| --- | --- | --- |
| `SurfaceBackground` | `IBrush?` | Popup frame 在 Child bounds 内绘制的可选表面；默认 `null`，表示内容拥有表面。只有显式非空 Brush 才启用 host-owned surface。 |
| `PopupRootShadow` | `BoxShadows` | native `PopupRoot` 路径的 frame shadow。 |
| `OverlayHostShadow` | `BoxShadows` | `OverlayPopupHost` 路径的 frame shadow。 |
| `RequestedPlacement` | `PlacementMode?` | AtomUI 自定义定位请求；有效值会投射到 Avalonia custom placement。 |
| `MarginToAnchor` | `double` | 内容 frame 与 anchor 之间的逻辑像素间距。 |
| `IsPointAtCenter` | `bool` | 箭头型内容是否按目标中心修正定位。 |
| `CustomPlacementCallback` | `CustomPlacementCallback?` | AtomUI 完成 placement 后的扩展回调。 |
| `IsHorizontalFlipped` / `IsVerticalFlipped` | `bool` | 当前定位相对请求方向的只读翻转结果。 |
| `PositionFlipped` | `EventHandler<PopupFlippedEventArgs>` | 翻转结果变化通知。 |
| `OpenMotion` / `CloseMotion` | `AbstractMotion?` | host 内容打开和关闭动效。 |
| `MotionDuration` / `IsMotionEnabled` | `TimeSpan` / `bool` | 当前 Popup 的动效时长和开关。 |

Avalonia `Popup` 继承属性继续负责 `Child`、`PlacementTarget`、`IsOpen`、`IsLightDismissEnabled`、offset、anchor、
gravity、overlay 选择和焦点行为。AtomUI 不复制这些契约。

## 事件与命令

| `PositionFlipped` | `EventHandler<PopupFlippedEventArgs>` | 翻转结果变化通知。 |

## 使用示例

稳定示例来源于 Gallery ShowCase 和源码查看片段。生成器只输出可从 `ShowCaseItem` 追溯的示例，不维护第二套手写示例。

未找到对应 Gallery 目录；生成器只链接源文档，不发明示例。

## 状态模型

Popup 使用显式 surface ownership，不根据 Child 类型、Child Background、透明度、Dialog ancestry 或 Theme 应用时序推断。

| 模式 | `SurfaceBackground` | 表面所有者 | 适用场景 |
| --- | --- | --- | --- |
| content-owned | `null`，默认 | Child / Presenter / `PopupFrame` | Direct Popup、Flyout、ToolTip、ContextMenu、菜单、选择器、Picker 等。 |
| host-owned | 显式非 `null` | Popup frame | 调用方明确要求 Popup frame 提供表面，Child 只声明内容和 Padding。 |

`SurfaceBackground` 只改变 frame fill，不增加 wrapper、Padding、border、arrow、DesiredSize、placement offset 或 shadow
thickness。Direct Popup 的 host frame 默认透明；可见弹层内容必须由 Child 自己绘制背景，只有确实需要 frame 拥有表面时，
调用方才显式提供 Brush。

AtomUI 自有的 content-owned 消费者继承 Popup 原语的 `null` 默认值，不在 AXAML 入口或共享 C# 构造路径重复赋值。
新增 Popup-bearing 控件只有在明确选择 host-owned 模式时才设置非空 Brush，并更新库存测试和控件家族回归。

关闭分为普通交互关闭和生命周期 teardown。普通关闭在实际 PlacementTarget 与打开时的 owning `TopLevel` 仍属于同一
有效会话时允许播放 `CloseMotion`；若 Popup 打开时存在 logical owner，该 owner 也必须继续有效。PlacementTarget detach、
已建立的 Popup logical owner detach、PlacementTarget 切换到其他 `TopLevel` 或目标无法转换到原 TopLevel 时，关闭不得被
动效延迟，必须立即释放 Avalonia PopupHost 和定位订阅。自身没有 logical owner、但具有有效显式 PlacementTarget 的 Direct
Popup 仍可使用普通关闭动效。

Pinned 状态不改变上述会话分类：外点、Escape、失焦、window deactivation、`Close`/`Hide` 和业务 open state 写 `false`
属于普通关闭并被拒绝；content removal、target/owner detach、effective visible/enabled 失效、跨 TopLevel、模板重建和窗口销毁
属于生命周期 teardown。无效的 pinned `IsOpen=true` 只保留请求，不发布 `Opened`；unpin 时已打开 Popup 保持打开，pending
请求则被清理且不能在锚点恢复后复活。

## 主题与 Design Token

```text
Popup
  -> PopupRoot or OverlayPopupHost       transparent host
     -> PopupMotionActor                 open/close motion
        -> VisualLayerManager
           -> ShadowsAwareContainer      frame surface + shadow
              -> Child                   direct content or specialized presenter
```

native 与 overlay 两条路径共享相同的 frame surface 实现：

| 路径 | 宿主 | frame shadow | layer 语义 |
| --- | --- | --- | --- |
| native | `PopupRoot` / OS popup window | `PopupRootShadow` | 独立窗口，不参与 owning Window 内部 Z-order。 |
| overlay | `OverlayPopupHost` / `PopupOverlayLayer` | `OverlayHostShadow` | owning `TopLevel` 的 popup layer，高于 Dialog `OverlayLayer`。 |

`PopupRoot.Background` 保持 `null`，`TransparencyLevelHint` 保持 `Transparent`。不透明表面只覆盖 Child bounds，不能填满
native popup 的透明 shadow buffer。overlay host 使用同一原则，避免 host 背景改变圆角、箭头或 shadow 几何。

Token 来源：

`PopupToken` 是 internal control token，服务 Popup frame 和多个 Popup-bearing 控件：

| Token | 派生来源 | 语义与消费者 |
| --- | --- | --- |
| `PopupRootShadow` | 两层固定 alpha shadow | native `PopupRoot` frame shadow；菜单、选择器和自动建议等家族复用。 |
| `OverlayHostShadow` | `BoxShadowsSecondary` | `OverlayPopupHost` frame shadow；Flyout/Menu/ContextMenu 等 overlay 路径复用。 |
| `PopupCornerRadius` | `BorderRadiusLG` | 专用 Presenter / `PopupFrame` 的默认圆角。 |
| `MarginToAnchor` | `UniformlyMarginXXS` | Popup 内容 frame 与 anchor 的默认间距。 |

`PopupCornerRadius` 不由 transparent host 绘制；它由 Child、Presenter 或 `PopupFrame` 提供给
`ShadowsAwareContainer`，使 frame shadow 与显式可选 surface 和内容圆角一致。

## AOT 与裁剪注意事项

可选 surface 不增加 host 或 wrapper；只扩展既有 renderer。renderer 因 shadow 或显式 surface 按需创建并复用，surface
更新只 invalidates render。默认路径没有 surface Theme resource，也不创建全局非 Visual resource host。

Pinned target/ancestor 订阅只在请求有效期内存在，并由 target replacement、unpin、logical detach 或 lifecycle teardown
释放。Pinned open 不使用 retry timer、程序集扫描、runtime type discovery、反射查找控件或字符串 binding；恢复只依赖
generation-checked Dispatcher callback 和 AvaloniaProperty relay。

`PopupReflectionExtensions` 集中反射 Avalonia Popup 的私有 closing event、parent setter、open-state flag 与定位刷新入口，
每个反射成员都通过 `DynamicDependency` 声明 NativeAOT 保留要求；Popup 不做程序集扫描或 runtime type discovery。
StyledProperty 和 ControlTheme 均为静态/AOT 可发现契约。源码库存测试使用正则扫描，但只存在于测试项目，不进入 runtime
或 NativeAOT 路径。

## 源码索引

- `src/AtomUI.Desktop.Controls/Popup/Popup.cs`：公共 API、自定义定位、翻转通知、frame shadow 选择、动效和 wheel guard。
- `src/AtomUI.Desktop.Controls/Popup/PopupReflectionExtensions.cs`：对 Avalonia Popup 私有 closing、parent 与定位入口的集中反射桥接。
- `src/AtomUI.Desktop.Controls/Popup/PopupUtils.cs`：placement 算法、popup scope 和 owning popup 查询。
- `src/AtomUI.Desktop.Controls/Popup/PopupToken.cs`：Popup 家族的阴影、圆角和 anchor margin Token。
- `src/AtomUI.Core/MotionScene/MotionExecutionState.cs`：MotionScene 共享的 internal 动效执行生命周期定义。
- `src/AtomUI.Desktop.Controls/Popup/Themes/PopupTheme.axaml`：Popup shadow 和 motion Theme 值；不覆盖 surface 默认值。
- `src/AtomUI.Desktop.Controls/Popup/Themes/PopupRootTheme.axaml`：native transparent host 组合。
- `src/AtomUI.Desktop.Controls/Popup/Themes/OverlayPopupHostTheme.axaml`：overlay host 组合。
- `src/AtomUI.Desktop.Controls/Primitives/ShadowsAwareContainer.cs`：两类 host 共享的 frame surface/shadow renderer 与几何适配。

## 相关文档

- 源设计文档：`docs/controls/desktop/other/popup/overview.md`
- 实现文档：`docs/controls/desktop/other/popup/implementation.md`
- Token 文档：`docs/controls/desktop/other/popup/token.md`
- 变更记录：`docs/controls/desktop/other/popup/changelog.md`
- 语义结构：`./semantic-cn.md`
