Files

12 KiB
Raw Permalink Blame History

Timeline 桌面版实现原理

本文档描述 Timeline 桌面版的内部实现范围、源码职责、状态流、生命周期、资源边界和维护规则。公共设计与 API 契约见 Timeline 桌面版架构设计,方向布局矩阵和算法边界见 Timeline 方向与布局设计,变化记录见 Timeline Changelog。涉及控件 Token 的实现应同时阅读 Timeline Token 设计。

1. 实现定位

本文档覆盖 Timeline 的控件实现、主题接入、状态同步和 Gallery 可见维护边界。具体属性注册、默认值、绘制细节和 AXAML selector 仍应直接阅读源码;本文只记录维护者必须理解的稳定结构和不变量。

2. 源码文件结构

主要源码文件:

  • 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。

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

状态归一顺序为:

  1. 从已生成容器中过滤不可见项。
  2. 根据 IsReverse 得到视觉顺序。
  3. 按视觉索引投影奇偶和首尾状态。
  4. 按视觉邻接关系投影 NextIsPending。
  5. 检测任一可见 Item 是否声明 Label,并统一投影 IsLabelLayout。
  6. 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。