12 KiB
Tooltip 桌面版架构设计
本文档定义 Tooltip 桌面版的最新设计定位、公共契约、状态模型、视觉主题关系和兼容边界。通用控件研发约束见 控件研发标准,内部实现原理见 Tooltip 桌面版实现原理,语义结构契约见 Tooltip Semantic Part 契约,Tooltip Token 的专项设计见 Tooltip Token 设计,设计和契约变化记录见 Tooltip Changelog。
该控件的 Popup 钉住打开属于共享弹层契约,详见 Popup 钉住打开设计。本控件的语义 owner 为 ToolTip,其 internal IsPopupPinnedOpen 只供测试和内部诊断使用;设置为 true 时保持 tooltip open state 并 relay 到 manually created Popup,设置为 false 时只解除关闭拦截。控件卸载、锚点失效、TopLevel 改变和模板重建仍按共享生命周期规则清理。
1. 控件定位
| 项 | 值 |
|---|---|
| NuGet 包 | AtomUI.Desktop.Controls |
| .NET 命名空间 | AtomUI.Desktop.Controls |
| AXAML 命名空间 | https://atomui.net |
| Gallery 页面 | controlgallery/AtomUIGallery/ShowCases/DataDisplay/Tooltip |
| 控件状态 | Stable |
Tooltip 是 AtomUI 桌面控件体系中的工具提示控件,用于在目标元素附近展示短文本或轻量说明。
Tooltip 不负责复杂弹层、菜单或模态对话框。这些职责应由业务层、组合控件或更专用的 AtomUI 控件承担。
主要源码入口:
src/AtomUI.Desktop.Controls/Tooltip
2. 设计语言
Tooltip 的设计语言围绕控件职责、可观察状态和主题契约组织,而不是围绕模板节点组织。
| 维度 | 含义 | Tooltip 中的表达 |
|---|---|---|
| 产品语义 | 控件在界面中承担的稳定职责。 | Tooltip 是 AtomUI 桌面控件体系中的工具提示控件,用于在目标元素附近展示短文本或轻量说明。 |
| 内容承载 | 用户数据、展示内容、集合项或操作入口如何进入控件。 | 继承 ToolTip 的 Content 与目标控件 tooltip 绑定。 |
| 状态反馈 | public API、内部状态和伪类如何形成用户可感知反馈。 | motion。 |
| 主题语义 | ControlTheme、SharedToken、控件 Token 和模板绑定如何表达视觉。 | Tooltip Token + ControlTheme。 |
3. 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 的情况下重命名或删除。
4. 行为与状态模型
Tooltip 的状态流按以下路径收敛:
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 和调和出口,不存在第二套开关路径。
5. 视觉与主题模型
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 的语义一致。
6. 控件家族或集成关系
Tooltip 与同分类控件共享尺寸、状态、Token、Gallery 展示和验证规则。组合或派生控件应显式说明哪些 API 被继承、覆盖或不支持。
主要协作类型:
ToolTip:控件核心或内部协作类型,维护 public surface 与主题可观察行为。ToolTipPseudoClass:控件核心或内部协作类型,维护 public surface 与主题可观察行为。ToolTipToken:控件 Token scope,负责从全局 token 派生控件语义变量。
集成关系:
- 与 ThemeManager、SharedToken、ControlTheme、控件文档和 Gallery ShowCase 示例保持一致。
- 涉及 ItemsSource、Popup、Flyout、Window、Form 或 CompactSpace 的路径必须保持生命周期释放和数据状态同步。
- 源码目录中的共享基类和内部协作类型形成维护边界,不能只修改桌面包装类而忽略共享状态 owner。
7. 兼容性不变量
维护 Tooltip 时必须保持以下不变量:
- 不擅自新增、删除、重命名或改变 public/protected API、Avalonia 属性、事件和默认值。
- 不破坏 template part、伪类、ControlTheme key、Token 名称和资源 key。
- 不破坏 Semantic Part 契约:
root/container/arrow的 selector class、route、ContractType与 cardinality 以 Tooltip Semantic Part 契约 为准。 - 不改变 Gallery 已展示的 XAML 用法、默认外观、交互顺序和状态优先级。
- Template part 重新应用、集合替换、弹层关闭、窗口失活和控件 detach 时必须释放旧订阅和资源宿主。
- 不通过隐藏延迟、强制刷新或吞异常掩盖状态同步问题。
IsOpen的声明式语义保持稳定:赋值时序无关、ToolTipOpening否决回写、弹层外部关闭回写、宿主 detach/reattach 自动重开。- Tip 文本的默认布局语义保持稳定:内容受
ToolTipMaxWidth约束,超出时在约束内换行(TextWrapping.Wrap)而不是被裁剪或截断。 - 不引入运行时反射扫描作为 API、Token 或数据路径发现机制。
- 文档只描述当前稳定设计;历史变化记录在
changelog.md。
8. 专项模型
8.1 动效模型
Tooltip 的动效只表达状态变化反馈,不应改变 public API 语义。初始加载、禁用态和卸载路径应能抑制或取消动效,避免保留旧控件实例。
8.2 Tip 实例定制模型
Tip 附加属性接受任意内容;当内容本身是 ToolTip 实例时,调和流程直接使用该实例作为弹层内容控件,实例成为完整定制面:
- 实例自身显式设置(
IsSet)的呈现类附加属性优先于宿主的值;未设置的回落到宿主。参与实例级覆盖的属性:Placement、HorizontalOffset、VerticalOffset、MarginToAnchor、IsPointAtCenter、IsArrowVisible、PresetColor、Color、TipHostWidth、TextWrapping、TextTrimming。 - 实例的实例属性(如
Background、ContentTemplate、IsMotionEnabled)按普通控件语义直接生效。 - 生命周期类附加属性(
ShowDelay、BetweenShowDelay、ServiceEnabled、ShowOnDisabled、IsUseOverlayHost)不参与覆盖,始终归宿主与悬停服务。 - 实例级取值来源在弹层打开时确定;打开期间对实例属性的修改经活绑定生效,但"是否覆盖宿主"的归属判定不随打开状态切换。
宿主够不到的场景(如 NavMenu 折叠项的内部 header)通过该模型获得完整 tooltip 定制能力,宿主侧不需要新增任何配置语言。
9. 文档导航与验证策略
关联文档:
语义结构(Semantic Part):
Tooltip 公开 root、container、arrow 三个 Semantic Part。root 是 ToolTip owner 本体;container 与
arrow 声明 CrossNestedOwners=true,marker 由共享 ArrowDecoratedBox 模板携带。完整 Part 表、Selector、
数量语义与定制边界见 Tooltip Semantic Part 契约,此处只保留 owner 与职责摘要:
| Part | 职责 |
|---|---|
root |
工具提示根语义区域,承载内容、打开状态与主题入口。 |
container |
内容盒,承载内边距、背景、圆角与文本样式。 |
arrow |
指向锚定控件的箭头指示器。 |
验证策略:
| 改动类型 | 验证要求 |
|---|---|
| 文档改动 | 运行 git diff --check,检查相对链接存在。 |
| Public API | 覆盖属性默认值、事件触发、命令和继承语义。 |
| 状态模型 | 覆盖 motion、disabled、hover、pressed、focus 以及控件特有状态。 |
| AXAML/Theme | 检查 template part、伪类、资源 key、Light/Dark 主题和 Browser 主题。 |
| Semantic Part | 检查 descriptor 字段、marker 与内置模板一致性、生成 Style 编译、跨嵌套 owner 命中(见 Tooltip Semantic Part 契约)。 |
| Token | 检查 TokenKind、AXAML token resource、Token 类型、生成数据和 token.md和文档同步。 |
| Gallery | 走查对应 ShowCase 示例和源码片段入口。 |