前三期聊了选型、对比、跑 demo。如果你跟着上一期把 counter 跑起来了,窗口弹出来那一瞬间应该还挺爽的。
然后你打开 src/main.rs,往下翻了几行。
不是熟悉的 Rust 结构体,不是 fn main(),而是一堆看起来像 CSS 又像 JSON、但两边都不像的东西。
我当时是半夜看的,盯着屏幕愣了大概两分钟。
然后去搜教程。更懵了。有的文章在讲 live_design!,有的在讲 script_mod!,还有在讲 Splash 的。我花了一整个晚上才搞明白它们之间的关系。
这一期就是想帮你省掉那个晚上。
先交底:这篇不是 Splash 语法大全,也不是官方文档翻译。它就是一份"第一次打开 Makepad 代码时,先看懂哪几块就够了"的导航。
1. 先别读代码,先搞清楚一个时间线
很多人卡在这一步,问题不在代码本身,而在于看了不同时代的资料。
Makepad 的 UI 描述方式经历过一次比较大的转向:
- 老版本 :用
live_design!宏写 UI,语法混在 Rust 宏里 - 当前版本(Makepad 2.0 / dev 分支) :用
script_mod!+ Splash,UI 描述变成了一套独立的 DSL
这不是"旧语法换了几个关键字"那种小改。是整个 UI 描述层的组织思路变了。
所以你搜到 2023 年的教程全是 live_design! { ... },而官方 dev 分支打开全是 script_mod! 和 Splash。你没看错,它们就是同一个框架在两个时间点的样子。
这个时间差不说清楚,新手大概率会越搜越乱。
截至 2026 年 6 月 ,官方 dev 分支的示例已经全面偏向 script_mod! / Splash。这一篇也按新写法来。
2. 第一层:UI 声明 ------ 界面长什么样
先解决最让人困惑的问题:在 Makepad 里,一个界面到底是怎么被"描述"出来的。
2.1 一个最小示例
以官方 counter 为参考,简化后大概长这样:
rust
script_mod!(
mod my_app {
main_window := Window {
window.inner_size: vec2(420, 220)
body +: {
View {
width: Fill,
height: Fill,
flow: Down,
align: Center
Label {
text: "Count: 0"
}
Button {
text: "Increment"
}
}
}
}
}
);
我第一次看到这段代码,脑子里有两个问号:
script_mod!是啥?这算 Rust 代码吗?main_window := Window { ... },这:=又是什么语法?
2.2 先别管 script_mod!
script_mod! 是个宏,编译时 Makepad 会把里面的 DSL 展开成框架能理解的东西。
第一次读,别追宏展开。追了就出不来了。
你只需要先抓住一件事:这段代码定义了一个窗口,窗口里有一个垂直排列的视图,视图里放了一个标签和一个按钮。够了。
2.3 := 和 +: 记两个词就行
这两个符号是 Makepad DSL 里出现频率最高的,也是新手最容易懵的:
main_window := Window { ... }:声明一个叫main_window的东西,类型是Windowbody +: { ... }:往body里面追加内容
粗暴理解:
:=→ "我声明一个"+:→ "往里面塞"
不严谨。但够用。第一遍读不卡住就是胜利。
2.4 先认控件名,别的往后放
读 Makepad UI 代码最省力的办法:
先找你认识的单词,忽略不认识的语法。
上面那段代码,你盯住这四个词就行:
Window--- 窗口View--- 容器Label--- 文本Button--- 按钮
这四个认出来,界面的骨架已经在你脑子里了。vec2(420, 220)、Fill、Down 第二轮再看,别急。
3. 第二层:布局和样式 ------ 东西怎么摆
知道"有什么"了,下一步:这些东西为什么摆在那个位置。
3.1 flow 管方向
flow 是 Makepad 里用得最多的布局属性。没有之一。
text
View {
flow: Down, // 从上往下排
// flow: Right, // 从左往右排
}
Down 是垂直,Right 是水平。和 CSS 的 flex-direction: column / row 一个意思,只是名字更短。
3.2 align 管对齐
text
View {
flow: Down,
align: Center,
}
align 管的是子元素在排列方向上的对齐。Center、Start、End,三个值够用了。
3.3 width / height 管尺寸
text
View {
width: Fill, // 撑满
height: 200, // 固定 200
}
Button {
width: Fit, // 自适应内容
}
Fill 撑满,Fit 自适应,具体数字就是固定尺寸。flow + align + width/height,三个属性组合起来,常见布局基本都能出来。
3.4 样式直接写在控件上
这一点和 CSS 差别很大。Makepad 没有单独的样式表,颜色、字体、间距全写在控件上:
text
Label {
text: "Hello"
draw_text: {
color: #333333,
font_size: 16.0
}
}
我第一次看到这种写法,第一反应是"这也太不分离了吧"。但用了两天之后发现,看一个控件就能看到它的全部外观,不用去另一个文件翻 class。说实话,小项目里挺爽的。
4. 第三层:事件和逻辑 ------ 点了为什么有反应
界面能看了,布局能摆了。最后一步:点了按钮,为什么会有反应。
4.1 UI 描述和 Rust 逻辑在同一个 script_mod! 里,但角色分开
rust
script_mod!(
mod my_app {
// 第一部分:UI 描述(DSL)
main_window := Window { ... }
// 第二部分:Rust 逻辑
#[run]
fn main() -> Result<(), String> {
// 初始化
}
#[event]
fn handle_button_click(&mut self, cx: &mut Cx, actions: &Actions) {
// 事件处理
}
}
);
UI 描述只管"长什么样",Rust 逻辑管"点完发生什么"。它们写在一个宏里,但分工很明确。
4.2 按钮点击是怎么接上的
以 counter 的点击为例,简化后大概是这样:
rust
// UI 侧:给按钮一个 id
Button {
id: increment_button,
text: "Increment"
}
// Rust 侧:通过 id 找到按钮,判断是否被点击
if self.ui.button(cx, ids!(increment_button)).clicked(actions) {
self.count += 1;
self.ui.label(cx, ids!(count_label)).set_text(cx, &format!("Count: {}", self.count));
self.ui.redraw(cx);
}
整个连接靠的就是 id。
UI 里给控件起个名字(id: increment_button),Rust 里通过同一个名字找到它(ids!(increment_button)),然后判断事件、更新状态、触发重绘。
所以我读 Makepad 代码有个习惯:先扫一遍 UI 描述,把所有 id 圈出来。这些 id 就是 UI 和逻辑之间的接头暗号,找到它们,后面的关系网就清楚了。
4.3 状态放哪
Makepad 里状态直接挂在 script_mod! 模块里:
rust
script_mod!(
mod my_app {
count: i32 = 0,
name: String = String::new(),
main_window := Window { ... }
#[event]
fn handle_click(&mut self, cx: &mut Cx, actions: &Actions) {
// self.count, self.name 直接用
}
}
);
不需要单独的 store,不需要 context。字段就挂在模块上,self.xxx 直接访问。中小型应用里这个模型用起来很快。
5. 一条偷懒的阅读顺序
如果现在你打开一个 Makepad 示例,不知道从哪开始看,按这个顺序来:
- 先找
main_window--- 界面入口,往下追 - 认控件名 ---
Window、View、Label、Button......先在脑子里画个树 - 看
flow和align--- 搞清楚东西为什么在这个位置 - 找所有
id--- 有id的控件,说明 Rust 那边会用到它 - 顺着
id跳到 Rust --- 看事件处理、状态更新
这个顺序走一遍,大部分示例就不会再给人"一堆符号看不懂"的感觉了。
6. 我踩过的两个坑
6.1 拿 CSS 心智硬套
Makepad 的 DSL 和 CSS 有几个词长得像(width、align),但布局模型完全不是 Flexbox。
- 没有
display: flex,flow就是排列方式 - 没有
margin/padding的完整对应 - 样式不级联,父元素不影响子元素
我当时卡在这上面至少两个小时,一直在找"Makepad 的 padding 怎么写"。后来发现有些东西它就是没有。接受"这是一套新东西"比纠结"为什么和 CSS 不一样"快得多。
6.2 一上来就想搞懂宏展开
script_mod! 展开之后代码量很大,充满框架内部细节。
第一次读,把它当黑盒。先理解里面的 DSL 结构和 Rust 逻辑怎么组织。等你整个框架有感觉了,回头看宏展开,那个时机才对。
7. 和上一期的关系
从第三期直接过来的话:
- 第三期:把窗口跑起来,按钮能点
- 这一期:看懂这些按钮和文字是怎么被定义、怎么被摆放、怎么连上逻辑的
两期合在一起,你就能从"能跑"走到"能读"。
总结
这一期其实就一件事:帮你建立 Makepad UI 代码的阅读框架。
认控件名 → 看 flow/align → 顺 id 找逻辑。这个顺序抓住,大部分代码就不会再让你愣在那了。
下一期拆事件处理、状态管理和组件通信。从"能看的界面"到"能交互的应用"。
你第一次打开 Makepad 代码,卡在哪一步?评论区聊聊。我猜很多人的答案都差不多。