🚧 基于
rik
分支
Makepad Framework
主要包含两大部分:
Makepad Platform 是平台抽象层。这意味着,它对跨平台的各种操作做了抽象和封装。
主要包含以下部分:
platform 的代码组织结构分为如下部分:
源码地址:https://github.com/makepad/makepad/tree/rik/platform
derive_live / live_compiler / live_tokenizer
属于 Live 系统,我们随后介绍。shader_compiler
是着色器编译器。在 Makepad 的 platform/src
目录下,我们找到了多个 Rust 源文件(.rs 文件),以及一些子目录,大概分类如下。
cx.rs
): 该文件包含了 Makepad 平台层的核心数据结构和函数(例如,上下文、配置参数等)。draw_list.rs
, draw_matrix.rs
, draw_shader.rs
, draw_vars.rs
): 这些文件涉及到图形渲染的逻辑,包括着色器管理、渲染队列和变换矩阵的处理。event
目录): 事件处理对于 UI 框架至关重要。这个目录包含处理各种用户输入和系统事件的代码。os
目录): UI 框架通常需要处理不同操作系统的特定情况。这个目录包含用于不同平台的特定实现代码。audio.rs
, audio_stream.rs
, media_api.rs
, midi.rs
): 这些文件包含音频播放、流媒体处理和 MIDI 设备交互等功能的实现。web_socket.rs
): WebSocket 实现文件,用于网络通信和数据交换。window.rs
, cursor.rs
): 窗口创建和管理,以及光标处理等功能的实现。看得出来,src
中主要还是封装了与 事件、UI 渲染、音视频、网络等相关跨平台的底层接口。我们暂且不去深入细节,接下来了解下什么是 Live 系统。
Live 系统是 makepad 提供的一种实时系统。因为 Rust 语言是静态编译语言,不像动态语言那样能实现运行时实时编码,所以 makepad 提供这套 Live 系统来实现实时编码的效果。正如第一章中对 makepad 愿景展示视频里所见,对代码的实时修改,无需重新编译代码,即可更新 UI。
目前,使用 makepad studio 和 vscode,可以看到实时重载(live reload)所见即所得的效果,后续支持更多 IDE 和 编辑器。
Live 系统是依赖于 Rust 的宏来实现的。这里有一个 Live 语法示例:
上面这段代码的作用是定义了一个 可设置颜色的 按钮(Button)。
其中的 live_design!
、#[derive(Live)]
和 #[live]
这些过程宏就属于 Live 系统,它们共同作用于 Button
结构体,就创建了这个「按钮」小部件(Widget)。
#[derive(Live)]
是 Rust 中的派生宏,它会自动为 Button
结构体实现 Live trait
,这是 「按钮」控件能与 Live 系统的交互需要的所有必要的机制。
#[live]
属性定义了 Button
结构体的那个字段要参与实时更新,这里是颜色(color)字段。
live_design!
宏则像是 CSS
样式那样,为「按钮」小部件定义了基本样式,比如设置了默认的颜色为 #f00
。
整套代码的工作机制如下图所示。
我们暂且不去深入 Live 系统的工作原理,我们只需要理解这套系统的作用和其语法即可。
具体更多细节将在后面Live DSL章节介绍。
Live 系统还包含了 Makepad 自定义的着色器语言 MPSL (Makepad’s custom shader language),它是一种统一的语言,可以转换为图形 API (openGL/ DirectX / Metal)的着色器语言。
下面是一个例子:
MPSL 代码是可以嵌入到 Live DSL 代码中的。其中 DrawColor
的字段 draw_super
类型 DrawQuad ,代表该结构体继承了 DrawQuad
的行为 (为这个字段实现了 deref
trait)。这算是一个 Rust/C 中的技巧,算是一种行为代理。DrawQuad
是 Makepad 框架中 draw crate 提供的功能,代表了绘制人意四边形的基本着色器。
利用 Deref trait 来实现继承,被认为是一种反模式。
因为 Deref 是隐式行为,这和 Rust 的显式哲学是相反的,滥用它容易出错。 但是这里的例子是 OK 的,因为这种方法可以方便地利用框架本身的能力,不属于滥用。
示例代码中 #[[repr(C)]
也是 MPSL 着色器语言的硬性要求,这是为结构体指定了兼容 C-ABI 的布局,目的是为了阻止 Rust 编译器对字段重新排序。
同样,本章不会去深入着色器语法和其渲染机制的更多细节,您看查看着色语言章节介绍。
你可能会对 Live System 产生这样的疑问:为什么不用 javascript/ lua ,亦或像 slint 那样自己开发一套脚本语言呢,为什么一定要使用现在这种 Rust 宏的写法?
用脚本语言编写的代码可以在运行中进行解释或重新编译,从而让开发周期变得更短。遗憾的是,脚本语言缺乏编译语言的性能优势,这又与 Makepad 力求支撑的重渲染应用产生冲突。将脚本语言和 Rust 结合使用时,代码的归属又极难平衡,因为最佳方案总是因应用程序而异的。
为了解决这一困境才选择使用现在的混合方法的方案:
Makepad 应用程序使用 Rust 编写,但负责应用程序外观(比如样式)的部分则用 Live DSL 编写。
Live 语言的主要作用类似于 CSS:它描述了应用程序的用户界面应该如何呈现。特别是与 CSS 相似,Live 语言可以方便地覆盖样式。
相较于 Rust 代码,Live 代码是在运行时编译和执行的。
另外,将来为 Makepad 构建的可视化设计器(visual designer,比如 Makepad studio 之类)能感知代码中哪些部分是 Live 代码。当使用可视化设计器来修改一段 Live 代码时,可视化设计器不会重新编译应用程序,而是将更新后的 Live 代码发送给运行中的应用程序。这样,应用程序就可以重新编译并执行新的 Live 代码,从而立刻反映出代码中的变化而无需重新编译 Rust 代码或者重启应用程序。
Makepad 的 2D 和 3D 绘图层(drawing layer)也包含在 Makepad platform 中。
绘图层包含了下面内容:
绘图层的更多细节留待后面特定章节来介绍。
与 Makepad Platform
并行存在的另一部分是 Makepad Widget
。
Makepad Widget
构建在 makepad draw
之上。它包含以下内容:
makepad-widget 的 src 组织结构中包含了很多文件,代表了不同的 UI 控件大概可以作如下分类:
button.rs
, check_box.rs
, slider.rs
, text_input.rs
等,这些文件可能包含各种 UI 控件的实现。image_cache.rs
, keyboard_view.rs
, scroll_bars.rs
,这些可能提供支持控件显示和交互的附加功能。color_picker.rs
, popup_menu.rs
, tab_bar.rs
等,代表更复杂或特定用途的控件。dock.rs
, splitter.rs
, view.rs
等,这些可能用于组织和管理 UI 控件的布局。widget_match_event.rs
可能与事件处理机制相关。更多细节我们在后面 Makepad Widget 章节中介绍。