12 KiB
Timeline 桌面版实现原理
本文档描述 Timeline 桌面版的内部实现范围、源码职责、状态流、生命周期、资源边界和维护规则。公共设计与 API 契约见 Timeline 桌面版架构设计,方向布局矩阵和算法边界见 Timeline 方向与布局设计,变化记录见 Timeline Changelog。涉及控件 Token 的实现应同时阅读 Timeline Token 设计。
1. 实现定位
本文档覆盖 Timeline 的控件实现、主题接入、状态同步和 Gallery 可见维护边界。具体属性注册、默认值、绘制细节和 AXAML selector 仍应直接阅读源码;本文只记录维护者必须理解的稳定结构和不变量。
2. 源码文件结构
主要源码文件:
src/AtomUI.Controls/Timeline/AbstractTimeline.cssrc/AtomUI.Controls/Timeline/AbstractTimelineItem.cssrc/AtomUI.Controls/Timeline/TimeLineEnums.cssrc/AtomUI.Controls/Timeline/TimelineIndicator.cssrc/AtomUI.Controls/Timeline/TimelineItemPanel.cssrc/AtomUI.Controls/Timeline/TimelineStackPanel.cssrc/AtomUI.Desktop.Controls/Timeline/Themes/TimelineIndicatorTheme.axamlsrc/AtomUI.Desktop.Controls/Timeline/Themes/TimelineItemTheme.axamlsrc/AtomUI.Desktop.Controls/Timeline/Themes/TimelineTheme.axamlsrc/AtomUI.Desktop.Controls/Timeline/Timeline.cssrc/AtomUI.Desktop.Controls/Timeline/TimelineItem.cssrc/AtomUI.Desktop.Controls/Timeline/TimelineToken.cs
职责边界:
- 控件主文件保留 public/protected API、Avalonia 属性注册、事件和主要生命周期入口。
- Theme 文件负责静态视觉结构、template part、selector 和资源绑定。
- Token 文件只提供组件视觉变量,不保存实例状态。
- Gallery 文件只展示用法和示例,不作为运行时逻辑 owner。
3. 核心类职责
AbstractTimeline:Orientation、Mode、IsReverse、Items 和 Pending 的状态 owner;负责容器准备、可见项视觉顺序和内部状态投影。AbstractTimelineItem:承载 Label、Content、Indicator API,并接收 owner 投影的 Orientation、Mode、奇偶、首尾、Reverse、Pending 和 Label 布局状态。Timeline:桌面公开控件,负责创建普通 TimelineItem 和 Pending TimelineItem,不复制共享布局逻辑。TimelineIndicator:内部 renderer,根据 Orientation、首尾、节点尺寸和自定义图标 Bounds 绘制节点与连接线。TimelineItem:桌面公开 Item 容器,复用 AbstractTimelineItem 的内容和内部状态契约。TimelineItemPanel:负责 Label、Indicator 和 Content 的方向化 Measure/Arrange,并把 Alternate 解析为 Start 或 End。TimelineStackPanel:负责主轴排列、Reverse、可见项过滤、Spacing 和水平等宽槽位。TimelineToken:控件 Token scope,负责从全局 token 派生控件语义变量。
核心协作规则:
- AbstractTimeline 是集合级方向、模式、视觉顺序和 Pending 邻接状态的唯一 owner。
- AbstractTimelineItem 不根据 Parent Items 索引自行推导位置,只保存 owner 的单向投影。
- TimelineStackPanel 只负责 Item 主轴槽位,不计算 Alternate、首尾或 Pending 状态。
- TimelineItemPanel 只负责单项内部布局,不修改集合级状态。
- TimelineIndicator 只消费布局和视觉属性进行绘制,不参与容器顺序计算。
- Template part 是视觉协作对象,生命周期受 ControlTemplate 和
OnApplyTemplate管理。
4. 状态与数据流
Timeline 的状态流遵循下面路径:
Orientation / Mode / IsReverse / Items / item visibility / Label / Pending
-> AbstractTimeline builds visible visual order
-> IsOdd / IsFirst / IsLast / NextIsPending / IsLabelLayout
-> AbstractTimelineItem internal properties and pseudo-classes
-> TimelineStackPanel / TimelineItemPanel / TimelineIndicator
-> ControlTheme selector and rendered Timeline
状态归一顺序为:
- 从已生成容器中过滤不可见项。
- 根据 IsReverse 得到视觉顺序。
- 按视觉索引投影奇偶和首尾状态。
- 按视觉邻接关系投影 NextIsPending。
- 检测任一可见 Item 是否声明 Label,并统一投影 IsLabelLayout。
- Item 模板把集合状态传递给 Panel 和 Indicator。
Items 增删、Reset、容器索引变化、Item 可见性、Label、IsReverse 和 Pending 变化必须重放相关派生状态。Mode 与 Orientation 不改变源集合顺序,通过 Avalonia 属性失效机制触发重新测量、排列或绘制。
5. 组合结构模型
5.1 控件角色图
Timeline (public)
-> Border#Frame (TimelineTheme.axaml, template-stable)
-> ScrollViewer#ScrollViewer (template-stable)
-> ItemsPresenter#ItemsPresenter (template-stable)
-> TimelineStackPanel (internal-observable)
-> TimelineItem (public item container)
-> TimelineItemPanel#RootLayout (internal-observable)
-> TextBlock#Label (template-stable)
-> TimelineIndicator#Indicator (internal-observable)
-> IconPresenter#PART_IconPresenter (template-stable)
-> ContentPresenter#ContentPresenter (template-stable)
5.2 协作节点
| 节点 | 类型 | 来源 | 生命周期 owner | 影响的 public API | 稳定性 | Agent 使用边界 |
|---|---|---|---|---|---|---|
Timeline |
public control | Timeline.cs |
应用/视觉树 | Orientation、Mode、IsReverse、Pending、Items | public | 用户直接创建和配置。 |
TimelineItem |
public item container | TimelineItem.cs / AbstractTimelineItem.cs |
Timeline ItemsControl | Label、Content、IndicatorIcon、IndicatorColor | public | 用户可显式声明,也可由 Timeline 生成。 |
TimelineStackPanel |
internal Panel | TimelineTheme.axaml |
ItemsPresenter | Orientation、IsReverse、Items | internal-observable | 只用于理解主轴布局,不应由用户直接依赖。 |
RootLayout |
internal TimelineItemPanel | TimelineItemTheme.axaml |
TimelineItem template | Orientation、Mode、Label、Content | internal-observable | 主题维护入口,不是 public API。 |
Indicator |
internal TimelineIndicator | TimelineItemTheme.axaml |
TimelineItem template | IndicatorIcon、IndicatorColor、首尾状态 | internal-observable | 负责轴线渲染,不作为用户控件暴露。 |
PART_IconPresenter |
IconPresenter template part | TimelineIndicatorTheme.axaml |
TimelineIndicator template | IndicatorIcon | template-stable | 模板替换时必须保留图标承载职责。 |
Label |
TextBlock theme node | TimelineItemTheme.axaml |
TimelineItem template | Label、Orientation、Mode | template-stable | 主题可以定制视觉,不能改变状态 owner。 |
ContentPresenter |
ContentPresenter theme node | TimelineItemTheme.axaml |
TimelineItem template | Content、ContentTemplate、Orientation、Mode | template-stable | 负责内容呈现和受限宽度换行。 |
6. 生命周期与模板接入
生命周期规则:
- 构造阶段只注册集合变化入口,不访问 template part。
PrepareContainerForItemOverride将 Orientation、Mode 和 IsReverse 从 Timeline 单向绑定到 TimelineItem。- 容器准备、索引变化和集合 Reset 后重算视觉顺序派生状态。
- Item 的 IsVisible 或 Label 变化通过 owner 回调请求重算,不建立无释放路径的长期订阅。
- TimelineIndicator 在
OnApplyTemplate获取新的PART_IconPresenter;绘制只使用当前模板实例的 Bounds。 - 模板绑定由 AXAML 和 AvaloniaProperty 管理,不为固定 template part 关系创建 C# relay binding。
- Browser 和 Desktop 宿主下的主题加载顺序不得改变 Orientation、Mode 或视觉顺序语义。
稳定 template part 接入点:
PART_IconPresenter:展示用户内容、文本、图标或模板化数据。
7. 交互与事件处理
Timeline 没有控件专属 pointer、keyboard、focus、command 或 popup 状态机。交互语义主要来自 ItemsControl 基础行为和 public 属性变化。Mode、Orientation 和 IsReverse 可以在运行时通过 binding 更新,更新必须依赖属性失效和状态重放完成,不能使用延迟刷新或强制同步。
8. 内部算法与关键流程
方向布局的完整公式和模式矩阵见 Timeline 方向与布局设计。实现层必须维持以下算法边界:
- 视觉顺序只在 AbstractTimeline 中归一,不能使用包含隐藏项的 ItemCount 直接计算 Reverse 索引。
- Alternate 使用视觉索引偶数映射 Start、奇数映射 End,第一可见项始终为 Start。
- TimelineStackPanel 的有限水平宽度按
(width - spacing * (visibleCount - 1)) / visibleCount分配;零项和无限宽必须显式退化。 - TimelineItemPanel 的 Measure、Arrange 和 alignment 使用同一个 Effective Mode 解析结果。
- 无 Label 且非 Alternate 的水平布局使用单侧紧凑模型;存在可见 Label 或 Alternate 时使用上区、Indicator、下区双侧模型。
- 双侧水平 Item 使用对称 side extent,TimelineStackPanel 使用所有 Item 的最大 DesiredHeight 排列,保证节点同轴。
- TimelineIndicator 按 Orientation 绘制主轴前后线段,首尾状态阻止产生悬空线段,自定义图标 Bounds 决定线段终点。
9. 资源、性能与 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 状态。
10. 维护不变量
维护 Timeline 时不得破坏:
TimelineMode精确包含 Start、End、Alternate,不保留 Left/Right 或重复值别名。- Orientation 默认 Vertical,Mode 默认 Start。
- 第一可见 Alternate Item 为 Start,Reverse 后仍按最终视觉顺序重新从 Start 计算。
- 隐藏项不占水平槽位,不参与奇偶、首尾、Label 布局或 Pending 邻接计算。
- 水平可见 Item 等宽、节点同轴、长文本项内换行且不创建内部水平滚动。
- Vertical Start/End 遵循 FlowDirection;Horizontal Start/End 的下方/上方语义不受 RTL 交换。
- Template part 名称、ControlTheme key、伪类和资源 key。
- Item、Panel 和 Indicator 只能消费 AbstractTimeline 投影的状态,不能成为第二状态 owner。
- Light/Dark、Browser/Desktop 和运行时方向切换下的主题一致性。
- 控件文档、源码 public surface、Token 类型或生成数据与源码契约的一致性。
11. 测试与验证
推荐验证:
- API 测试覆盖 TimelineMode 精确值、Orientation/Mode 默认值和运行时切换。
- 状态测试覆盖 Reverse、隐藏项、Alternate 首项、首尾、Label 布局和 Pending 邻接。
- Panel 测试覆盖 Vertical 既有布局、Horizontal 等宽、Spacing、零项、无限宽和 final width 重算。
- Item 测试覆盖 Orientation x Start/End/Alternate x Label 布局矩阵和窄宽度文本换行。
- Renderer 测试覆盖垂直/水平首尾线段、内置圆点和自定义图标边界。
- Theme 与 Gallery 测试覆盖 Orientation 传递、方向 selector、三种水平示例和 approved snapshot。
- 人工走查覆盖 Light/Dark、Desktop/Browser、LTR/RTL、动态切换和有限宽度。
- Gallery NativeAOT publish 验证 AXAML 属性绑定、枚举引用和 trimming 边界。
- 所有文档和源码改动收尾运行
git diff --check;LLMS 产物由生成器生成并执行 verify。