- 前端
- 桌面应用
【免费下载链接】xilem
An experimental Rust native UI framework
本文围绕 masonry_core/README.md 展开,系统介绍 Masonry Core 在 Xilem 仓库中的角色:一个不依赖任何窗口系统与渲染后端的无头(headless)GUI 基础引擎。你将了解到它提供哪些底层能力(Widget 特质、事件冒泡、布局通信、合成、无障碍树、WidgetMut、Action 机制),理解 Pass System 的整体运作,掌握何时应直接依赖masonry_core而非masonry、tracy特性标志的用法,以及 MSRV 等工程细节。
Masonry Core 是什么:无头基础引擎
Masonry Core 为 Masonry 提供基础 GUI 引擎(base GUI engine),其 crate 描述为 "Traits and types of the Masonry toolkit"(见 masonry_core/Cargo.toml)。与包含具体控件、主题和测试工具的masonrycrate 不同,Masonry Core 专注于平台无关的引擎层:它不创建窗口、不启动事件循环、不直接进行渲染,而是负责维护 widget 树、分发事件、计算布局、组织合成场景与无障碍信息——这些能力由 masonry_core/src/lib.rs 中app、core、layout、properties等模块提供。
"无头"的含义可以从 masonry_core/src/app/render_root.rs 中RenderRoot的定义看出端倪:RenderRoot是 Masonry 的"合成根"(composition root),它是所有用户事件的入口、所有待发往事件循环(如 winit)信号的来源,也是 widget 树的持有者,但本身并不绑定任何具体的窗口后端。
在 Xilem 仓库中的分层位置
Masonry Core 位于整个生态的最底层,其上还有多个 crate(workspace 成员见根 Cargo.toml):
masonry_core:无头基础引擎,本文主角;masonry:完整 GUI 框架,实现具体控件(Button、Flex、Label、Portal 等)、主题与属性系统,并将masonry_core重新导出为masonry::core(见 masonry/src/lib.rs);masonry_winit:基于 winit 的窗口后端驱动;masonry_testing:TestHarness等无头测试基础设施;masonry_imaging:渲染后端适配(vello、skia、wgpu 等);xilem、xilem_masonry:构建在 Masonry 之上的响应式 UI 层。
从源码结构看,这种拆分的目的在于隔离关注点:引擎逻辑与平台细节解耦,渲染后端可插拔,测试不依赖真实窗口。
Masonry Core 提供的能力清单
README 给出了 Masonry Core 提供的核心能力列表,下面逐项结合源码展开。
Widget特质:一切控件的抽象
Widget是所有 Masonry 控件的统一特质,定义在 masonry_core/src/core/widget.rs。它通过关联类型与方法约定控件的完整生命周期:
type Action:该控件会提交的 Action 类型,由EventCtx::submit_action提交,并被 Masonry 校验;- 事件处理:
on_pointer_event、on_text_event、on_access_event、on_anim_frame、on_action; - 状态更新:
register_children、update(接收Update枚举事件)、property_changed; - 布局:
measure(计算期望长度,Masonry 默认缓存测量结果,缓存键由axis、len_req、cross_length决定)与layout(容器控件必须为每个子控件调用run_layout与place_child); - 呈现:
compose(仅更新 transform 的廉价重排)、pre_paint、paint、post_paint; - 无障碍:
accessibility_role与accessibility(获取预初始化的accesskit::Node并填充控件专属信息,每次调用都会新建节点); - 结构:
children_ids()返回子控件 id 列表(内部为SmallVec<[WidgetId; 16]>,见 masonry_core/src/core/widget.rs); - 交互策略:
accepts_pointer_interaction、propagates_pointer_interaction、accepts_focus、accepts_text_input(注意:这些返回值在控件创建时缓存,不可动态修改); - 调试辅助:
get_debug_text、make_trace_span(为每次遍历建立 tracing span)。
WidgetId是每个WidgetPod自动生成的唯一标识,用于事件路由与测试中定位控件(masonry_core/src/core/widget.rs)。WidgetId无法预分配,若需要提前引用某控件,应使用NewWidget::with_tag或WidgetTag。
事件处理与冒泡(基于ui-events)
事件类型来自ui-eventscrate(workspace 依赖ui-events = "0.3.0",见根 Cargo.toml),保证跨框架互操作。Masonry Core 在其上封装了TextEvent、AccessEvent、WindowEvent等类型(见 masonry_core/src/core/events.rs),并在core模块重新导出PointerEvent系类型(masonry_core/src/core/mod.rs)。
事件采用类似浏览器的"冒泡"模型:事件先送达目标控件,再沿父链逐级上溯直至根。目标选择规则为:指针事件指向指针下方的控件(或持有指针捕获的控件);文本与无障碍事件指向当前持有焦点的控件。实际分发逻辑见 masonry_core/src/passes/event.rs 中的run_event_pass:它从目标开始逐级冒泡,遇到禁用控件时跳过(指针Cancel事件除外),并在每级处理后将状态合并到父级(merge_state_up)。
父子控件间的布局通信
布局信息通过MeasureCtx、LayoutCtx等上下文类型在父子控件之间传递。measure是"期望尺寸"的协商过程(子控件可自由选择如何测量自己,即使无视父级的提示也完全合法),layout则是父控件最终决定子控件大小的过程。LayoutCtx::run_layout与LayoutCtx::place_child必须由容器控件为每个直接子控件显式调用,遗漏属于逻辑错误,可能触发调试断言。Widget::layout运行后还会自动请求compose。
内容合成与渲染解耦(Imaging)
控件不直接画到屏幕上,而是通过Painter产出可复用的合成场景(Scene)。paint系列方法接收masonry_core::imaging::Painter(见 masonry_core/src/lib.rs 的pub use imaging),场景随后交由具体的渲染后端(vello、skia、wgpu 等,见 masonry_imaging)绘制。这种设计让 Masonry Core 与渲染技术栈彻底解耦。
无障碍树(AccessKit)
Masonry Core 通过 AccessKit(workspace 依赖accesskit = "0.24.0")生成无障碍树:每个控件在 accessibility pass 中填充一个accesskit::Node,所有节点共同构成无障碍树;WidgetId可直接转换为accesskit::NodeId(见 masonry_core/src/core/widget.rs)。操作系统无障碍 API 触发的交互则通过AccessEvent回传。
WidgetMut:受控的可变引用
在 Masonry 中,控件不能被随意可变借用,所有修改都经过WidgetMut包装(masonry_core/src/core/widget_mut.rs)。其结构包含两个字段:widget: &'a mut W(目标控件)与ctx: MutateCtx<'a>(指向控件状态和相关数据的句柄)。修改完成后,Drop实现会把控件内部元数据"合并上溯"到父级(masonry_core/src/core/widget_mut.rs),从而保证每次修改后 Masonry 的元数据一致。
WidgetMut可从RenderRoot、EventCtx、UpdateCtx或父级WidgetMut(经MutateCtx)创建,支持insert_prop/remove_prop(会自动触发property_changed)、set_transform、downcast/try_downcast(向下转型为具体控件类型)等操作。这也是测试框架中修改控件状态的统一入口。
Action机制:控件向应用发消息
控件通过EventCtx::submit_action提交ErasedAction(即Box<dyn AnyDebug + Send>,见 masonry_core/src/core/mod.rs),Action 随后沿树向上传播,最终到达应用驱动层(如AppDriver::on_action)。不做任何动作的控件可用NoAction作为关联类型。测试中可用TestHarness的pop_action取出这些 Action 进行断言。
深入 Pass System:每一帧如何运转
README 明确指出,上述能力的实现细节集中在 Masonry Pass System 文章(仓库中对应 masonry_core/src/doc/pass_system.md)。该文章(以及术语表 masonry_core/src/doc/masonry_concepts.md)通过include_str!嵌入 rustdoc 模块,入口见 masonry_core/src/doc/mod.rs。Pass System 的核心思想是:每一帧对 widget 树子集执行一组"pass"(遍历计算),pass 分为事件、重写、渲染三大类。除非另有说明,所有 pass 均以深度优先前序(depth-first preorder)遍历,子级顺序由children_ids()数组决定。
事件 pass(Event passes)
由用户交互触发,共三类:
on_pointer_event:鼠标、触控笔、触摸板等指针设备的位置事件;on_text_event:键盘、IME、剪贴板粘贴等文本输入;on_access_event:操作系统无障碍 API 事件。
事件发生时,应用先选择目标控件(指针事件为指针下方或持有捕获的控件;文本/无障碍事件为聚焦控件),调用其处理回调后沿父链逐级冒泡到根。另有update_anim动画 pass:当树中包含动画控件时按固定间隔触发,可视为一种"特殊事件 pass"——不由用户交互触发、不冒泡,但同样会启动重写 pass。
重写 pass(Rewrite passes)
每次事件 pass 后,若某些标志被修改、某些值失效需要重算,Masonry 按固定顺序运行一组重写 pass:
- mutate:以可变访问执行排队回调;
- update_widget_tree:控件增删时更新树;
- update_disabled:传播禁用(disabled)状态;
- update_stashed:传播隐藏(stashed)状态;
- update_focusable:内部更新"是否有可聚焦后代"标志;
- update_focus:更新聚焦状态;
- layout:计算整树布局;
- update_scrolls:更新滚动位置;
- compose:为控件分配 transform;
- update_pointer:更新悬停状态与当前光标图标;
- update_props:类(class)变化时应用计算属性。
默认情况下每个 pass 不做任何工作,除非设置了相关失效标志。每个 pass 通常可以为后续pass 请求工作(例如 mutate pass 使某控件布局失效,则 layout pass 会覆盖该控件及其子级与父级);若某 pass 为先前pass 请求工作,则所有重写 pass 会从头再跑一遍。为防止无限循环,重跑次数设有静态上限,超限部分会推迟到下一帧。
值得单独说明的是mutate pass:它是"逃生舱口"(escape hatch)。通过各类上下文类型的mutate_later()方法可排队回调,回调会获得WidgetMut——这是除RenderRoot全局持有者之外唯一获取可变访问的途径。若回调运行前控件已被删除,回调会被静默丢弃。文档建议控件逻辑尽量融入其他 pass,谨慎使用mutate_later()。
action pass则将控件的 Action 沿树向上传播,任意祖先控件都可对后代 Action 做出反应,或标记为已处理(handled)以中止传播。
渲染 pass(Render passes)
当事件或重写 pass 使呈现失效时,环境会请求重绘,随后按需运行:
- paint:每个控件产出 Vello Scene 描述,按前序拼接(父级、其首个子级、该子级的首个子级……);
- accessibility:每个控件产出 AccessKit 节点,共同构成无障碍树。
这两个 pass 的方法实现必须假设自己可能被跳过或多次调用,因此它们对 widget 树的影响能力被刻意限制。
外部修改
持有RenderRoot可变访问的代码(如 Xilem 的 app runner)可通过edit_root_widget()传入回调,获得根控件的WidgetMut——这等价于一次只处理单个回调的 mutate pass。Xilem 正是借此把响应式步骤产生的树改动应用到 widget 树;edit_root_widget()返回前会触发完整的一套重写 pass。
各 pass 上下文类型的能力差异(masonry_core/src/doc/pass_system.md)也值得注意:渲染 pass 的PaintCtx/AccessCtx不能设置失效标志;MeasureCtx/LayoutCtx/ComposeCtx不能访问自身尺寸位置或创建WidgetRef;MutateCtx/EventCtx/UpdateCtx可增删子级;RegisterCtx只能注册子级;QueryCtx只提供只读查询。
何时应直接依赖masonry_core而不是masonry
README 给出的核心建议是:大多数想用 Masonry 开发应用或 UI 库的用户应直接依赖masonry;但如果你是 Masonry 生态中的库作者,应尽可能直接依赖masonry_core,这样应用使用你的库时能获得更大的编译并行度。典型适用场景包括:
- 编写 Masonry 的替代驱动:类似 masonry_winit 那样的窗口后端驱动,只需引擎能力而无需控件集;
- 编写包含自定义控件的库:例如一个 2D 地图控件库,只关心基础引擎契约;
- 完全不用 Masonry 自带控件集的应用:自行实现全部控件以获得最大控制力,例如希望精确匹配某个既有库的外观、或强制遵循特定设计规范——这些场景下 Masonry 自带控件可能无法满足要求。Masonry Core 提供了实现替代控件库所需的共享功能集。
README 也坦率说明:Masonry Core 目前主要聚焦于masonry自身,尚未发现有项目按上述方式独立使用它。这一说明同样保留在 masonry_core/src/lib.rs 的文档注释中,写作时应注意这一现状前提。
从编译并行性的角度理解:masonry及其控件集、主题、测试工具链较重,若库作者直接依赖masonry_core,下游应用无需为你的库编译完整的控件栈,因而整体编译时间更短。
Feature flags:tracy性能剖析
Masonry Core 目前只提供一个 crate feature(见 masonry_core/Cargo.toml):
[features] default = [] tracy = ["dep:tracing-tracy", "tracing-tracy/enable"]default:无默认特性;tracy:通过tracing-tracy为 Tracy 剖析器 生成输出。启用方式为安装 Tracy 客户端,并以该 feature 编译运行 Masonry 应用后连接。
启用后,Masonry Core 的默认 tracing 后端会在注册 subscriber 时附带TracyLayer(见 masonry_core/src/app/tracing_backend.rs)。此外,通过MASONRY_TRACE_PASSES环境变量可控制各 pass 的详细 tracing(支持all或逗号分隔的update_tree、anim、layout、compose、paint、access,见 masonry_core/src/passes/mod.rs)。在masonry层也存在同名tracyfeature 透传(masonry/src/lib.rs)。
依赖、版本与 MSRV
- MSRV:本版本经验证可在Rust 1.96 及更高版本编译(workspace 的
rust-version = "1.96",见根 Cargo.toml)。未来版本可能提高 Rust 版本要求,且不被视为破坏性变更,小版本补丁也可能提升。 - 版本:
masonry_core与 workspace 中多个 crate 同步采用 0.4.0 版本(根 Cargo.toml)。 - 核心依赖(masonry_core/Cargo.toml):
accesskit(无障碍)、imaging(渲染抽象)、kurbo(几何)、parley(文本布局)、peniko(颜色/画刷)、ui-events(事件类型)、tree_arena(树存储)、smallvec、tracing系。平台适配方面:非 wasm32 目标使用time;wasm32 目标使用console_error_panic_hook、tracing-wasm、web-time;Android 目标附带tracing_android_trace。 - 文档同步机制:README 的正文由 masonry_core/src/lib.rs 的 crate 文档经
cargo rdme --workspace-project=masonry_core自动生成,README 顶部注释说明了这一点——因此修改文档时需保持两处一致。
配套生态速览:app 层、tracing 与测试
虽然 Masonry Core 本身无头,但它提供的配套模块让上层驱动与测试成为可能:
- RenderRoot 与选项:
RenderRoot::new接收根控件、信号回调与RenderRootOptions(默认属性、是否使用系统字体、窗口尺寸策略WindowSizePolicy(Content/User)、缩放因子、测试字体等),见 masonry_core/src/app/render_root.rs。RenderRootSignal定义了向事件循环发送的信号(重绘、IME、光标、窗口尺寸、新建/移除层等)。 - 默认 tracing:
try_init_tracing/try_init_test_tracing会注册一个面向 GUI 的默认 tracing subscriber——debug 构建默认 DEBUG 级别、release 默认 INFO 级别,可用RUST_LOG覆盖;debug 模式还会把完整日志写入临时文件(masonry-<时间戳>-dense.log),且不会覆盖已设置的 subscriber(masonry_core/src/app/tracing_backend.rs)。 - 无头测试:
masonry_testing的TestHarness模拟RenderRoot,在无窗口环境中运行与真实应用相同的 pass 序列,支持模拟点击、按键与animate_ms动画,并通过WidgetRef(实现Debug,可配insta做快照)、assert_render_snapshot宏等进行断言(masonry_testing/src/harness.rs)。 - 调试功能:
masonry层附带控件检查器(F11 切换)与布局矩形绘制(F12 切换)等调试特性(masonry/src/lib.rs)。
许可证与社区
Masonry Core 采用Apache License 2.0(许可证全文见仓库根目录 LICENSE,workspace 亦在根 Cargo.toml 中统一声明)。开发讨论在 Linebender 社区进行,贡献者需遵守 Rust 行为准则,欢迎以 Pull Request 形式贡献代码。
综合来看,Masonry Core 的价值在于把"GUI 引擎"与"控件库/渲染后端/窗口系统"彻底分层:它定义的是控件契约(Widget)、事件与布局的流转规则(Pass System)以及树的可变访问模型(WidgetMut)。理解它的边界——什么能做、什么刻意不做(如窗口创建、具体控件实现、渲染细节)——是正确使用整个 Masonry 生态,乃至在其上构建 Xilem 响应式层的前提。
- 前端
- 桌面应用
【免费下载链接】xilem
An experimental Rust native UI framework
相关推荐
Xilem 仓库架构全解析:从 Xilem Core 反应式核心到 Masonry Pass 系统
Xilem 仓库架构全解析:从 Xilem Core 反应式核心到 Masonry Pass 系统 本文以仓库根目录的 ARCHITECTURE.md http
前端桌面应用Masonry基础使用与语法详解
Masonry基础使用与语法详解 Masonry是iOS开发中广泛使用的Auto Layout框架,通过简洁的链式语法大幅简化了Auto Layout的使用复杂
UI组件Ant引擎基础库:Foundation核心工具集
Ant引擎基础库:Foundation核心工具集 引言 在游戏引擎开发中,高效的内存管理和数据结构操作是性能优化的关键。Ant引擎的Foundation核心工具
游戏开发图形学3D渲染
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考