16 KiB
Segmented 桌面版架构设计
本文档定义 AtomUI.Desktop.Controls.Segmented 桌面版的最新设计定位、公共契约、状态模型、视觉主题关系和兼容边界。通用控件研发约束见 控件研发标准,内部实现原理见 Segmented 桌面版实现原理,Segmented Token 的专项设计见 Segmented Token 设计,设计和契约变化记录见 Segmented Changelog。
1. 控件定位
选中滑块首次出现时直接使用当前选中容器的完整矩形,后续有效选择变化保留飞行动画。就绪状态、首次有效几何 与主题过渡的共享契约见 状态动效初始化设计, 源码接入差异与验证边界见 状态动效初始化验证。
| 项 | 值 |
|---|---|
| NuGet 包 | AtomUI.Desktop.Controls |
| .NET 命名空间 | AtomUI.Desktop.Controls |
| AXAML 命名空间 | https://atomui.net |
| Gallery 页面 | controlgallery/AtomUIGallery/ShowCases/DataDisplay/Segmented |
| 控件状态 | Stable |
Segmented 是桌面端数据展示类的紧凑单选控件,用于在少量互斥选项之间切换当前视图、展示模式、时间粒度或状态筛选。它通过分段轨道、选项 item、选中滑块、图标和文本表达“当前只有一个有效选择”的语义。
Segmented 的职责是承载一组有限选项并维护单选状态。它不是完整导航系统、Tab 容器、列表选择器、弹层选择器、表单编辑器或大数据集合控件;复杂页面导航、动态搜索、虚拟化、多选和异步加载应由专用控件或业务层承担。
2. 设计语言
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。 |
3. 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) 是受保护扩展点,用于派生控件补充容器准备逻辑。
4. 行为与状态模型
Segmented 的核心状态流:
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。
5. 视觉与主题模型
Segmented 的视觉由根主题、item 主题、专属 Token 和 SharedToken 共同决定。
| 主题文件 | 职责 |
|---|---|
SegmentedTheme.axaml |
根模板、轨道 padding/background、选中滑块资源、SizeType/Shape 圆角分支、方向/展开对齐和滑块动画。 |
SegmentedItemTheme.axaml |
item 模板、图标/内容布局、hover/pressed/selected/disabled 状态、SizeType/Shape 分支和图标尺寸。 |
视觉关系:
SharedToken
↓
SegmentedToken
↓
SegmentedTheme / SegmentedItemTheme
↓
track + selected thumb + item states
根控件在 Render() 中绘制轨道背景和选中滑块。item 模板绘制每个选项自身的 hover/pressed 背景遮罩、图标、内容和状态颜色。选中背景由选中滑块独占承载:item 的 :selected 不绘制背景,选中滑块飞行途中目标项也不会提前点亮;滑块飞行期间非选中 item 的 hover/pressed 背景遮罩被屏蔽,对齐上游 thumb ~ item::after 透明规则。无选中项时滑块不绘制。选中滑块位置来自当前选中容器相对根控件的坐标,尺寸来自当前选中容器最终排列后的 Bounds.Size。
Shape=Default 时,根、item 和滑块圆角继续由 SizeType 对应的 SharedToken 决定。Shape=Round 时,Shape 分支在 SizeType 分支之后统一覆盖根、item 和滑块圆角为胶囊几何;该覆盖不新增 Design Token,也不改变模板结构。
6. 控件家族或集成关系
Segmented 的共享实现位于 AtomUI.Controls,桌面实现位于 AtomUI.Desktop.Controls。
集成关系:
AbstractSegmented:共享单选、motion、form、布局和滑块渲染逻辑。Segmented:桌面公开控件,注册SegmentedToken.ScopeProvider,创建SegmentedItem容器。AbstractSegmentedItem:共享 item 选择、图标伪类和 pointer release 选择逻辑。SegmentedItem:桌面公开 item,注册SegmentedToken.ScopeProvider。SegmentedStackPanel:内部方向感知 items panel,负责横向/纵向排列、expanding 和可导航容器语义。- Form:通过
IFormItemAware把SelectedItem暴露为表单值。 - Gallery:通过 Segmented ShowCase 展示基础、块级、禁用、动态数据、尺寸、垂直布局、胶囊形状、纯图标和图标文本示例。
7. 兼容性不变量
维护 Segmented 时必须保持以下不变量:
SelectionMode保持单选。- 未提供选择且存在 item 时,模板应用后默认选择第一个 item。
- 已绑定或显式设置的
SelectedIndex/SelectedItem不能被默认选择覆盖。 - 鼠标左键释放触发 item 选择的语义不能擅自改为按下触发。
Segmented必须为非 visual item 创建SegmentedItem容器。Orientation默认值必须保持Horizontal,Shape默认值必须保持Default。- 容器准备时必须把根
SizeType、Shape和IsMotionEnabled同步给 item。 - 水平
IsExpanding=true时只按可见SegmentedItem等分宽度;垂直模式不能因此扩展父容器高度。 - 选中滑块必须跟随当前选中容器的最终排列位置和
Bounds.Size。 - 四个方向键必须按前后顺序循环选择,并跳过 disabled 和 hidden item。
Frame、PART_ItemsPresenter、IconPresenter、Content等模板节点名称和职责不能在未授权情况下改变。:selected、:pressed、:has-icon伪类语义不能改变。SegmentedToken的名称、语义和资源使用点不能擅自删除或重命名。
如果实现某项能力时无法保持这些不变量,应先停止实现,说明原因、影响范围、替代方案和迁移方式,并获得授权。
8. 专项模型
8.1 选中滑块模型
选中滑块是根控件 render 层的视觉,不是单独的 visual child。它的 SelectedThumbPos 来自选中容器相对根控件的坐标,SelectedThumbSize 来自选中容器最终排列后的 Bounds.Size。
滑块矩形同步分两条路径:选择变化时(SelectionChanged)允许动画飞行,滑块位移与尺寸同时插值,时长取
SharedToken MotionDurationSlow(300ms),缓动为 cubic-bezier(0.645, 0.045, 0.355, 1)(对齐上游
motionEaseInOut);布局完成后(ArrangeOverride)按最终 Bounds 校准,一律瞬移动画。滑块首次出现、无选中来源
容器、过渡系统尚未就绪或目标矩形无变化时也一律瞬移。无选中项(如 Form ClearFormValue)时滑块停止绘制。
item 的文字和图标颜色切换不受滑块飞行约束,立即生效并由 MotionDurationMid 柔化过渡。
该模型要求 item 布局完成后再同步滑块尺寸。修改布局、容器创建或选择时序时,必须验证滑块和 item 边界一致。
8.2 方向与 Expanding 布局模型
SegmentedStackPanel 是 Segmented 专用方向感知 items panel。水平模式沿 X 轴累计宽度,expanding 时按可见 item 数量等分可用宽度;垂直模式沿 Y 轴累计自然高度,并把每个 item 排列为轨道全宽。垂直 IsExpanding 只控制父容器宽度适配,不把可用高度分配给 item。
8.3 Shape 模型
SegmentedShape.Default 保留 SizeType 的圆角映射。SegmentedShape.Round 使用足够大的固定圆角形成几何胶囊,并在所有 SizeType 分支之后覆盖根、item 和滑块圆角。Round 是实例形状状态,不是 Token,也不要求新增 Visual、template part 或伪类。
8.4 Custom SizeType 模型
SizeType=Custom 是 ICustomizableSizeTypeAware 的自定义尺寸入口。默认主题下,Custom 使用 Middle 分支作为初始视觉基线;实例上的显式属性值覆盖主题 setter。控件不新增 Segmented 专属 CustomHeight、CustomPadding 等公开 API。
9. 文档导航与验证策略
关联文档:
验证策略:
| 层次 | 验证内容 |
|---|---|
| Public API | SizeType、Orientation、Shape、IsExpanding、IsMotionEnabled、SelectedIndex、SelectedItem、SelectionChanged、SegmentedItem.Icon、SegmentedItem.IsSelected。 |
| 状态行为 | 默认选择、显式选择保留、绑定选择保留、pointer release 选择、四方向键循环选择、Form value、disabled item、hidden item。 |
| 布局与滑块 | 横向/纵向自然布局、水平 expanding、垂直宽度适配、动态方向切换、最终 Bounds 滑块矩形;滑块过渡时长/缓动契约(300ms + cubic-bezier(0.645, 0.045, 0.355, 1))、飞行期间 item 背景遮罩抑制、无选中时滑块隐藏、detach 清理。 |
| AXAML / Template | 根 Frame、PART_ItemsPresenter、SegmentedStackPanel、item Frame、IconPresenter、Content 和 SizeType/Shape 样式分支。 |
| Semantic Part | descriptor 只含 root/item/icon/label;semantic-item marker 随容器创建/prepare 幂等就位,semantic-icon/semantic-label marker 位于 item 模板节点;集合重置、图文/纯图标/纯文本选项与选择变化不增删 marker;owner-scoped Semantic Style 命中最低 public 类型;选中滑块不属于任何 Part。 |
| Token | 轨道 padding/background、item 文本/背景状态色、item 最小高度、图标和图文间距。 |
| Gallery | Basic、Block、Disabled、Dynamic、Sizes、Vertical、Round Shape、Icon Only、With Icon 示例,以及 Token 语义和 ShowCase 示例。 |
| 文档 | 运行 git diff --check,检查相对链接存在。 |