使用 Rust 代码运行时刷新 Slint UI 内的组件属性
过程
首先, 我们通过全局单例 global singleton 定义一些用于列表刷新的属性:
export global TestShared {
callback update-list-items([ListItem]);
in-out property <[ListItem]> test-list-items: [
{ text: "AboutSlint", label: "aboutslint"},
{ text: "Button", label: "button"},
{ text: "CheckBox", label: "checkbox"},
{ text: "ComboBox", label: "combobox"},
{ text: "GridBox", label: "gridbox"},
{ text: "GroupBox", label: "groupbox"},
{ text: "HorizontalBox", label: "horizontalbox"},
{ text: "LineEdit", label: "lineedit"},
{ text: "ListView", label: "listview"},
{ text: "ProgressIndicator", label: "progressindicator"},
{ text: "ScrollView", label: "scrollview"},
{ text: "Slider", label: "slider"},
{ text: "SpinBox", label: "spinbox"},
{ text: "Spinner", label: "spinner"},
{ text: "StandardButton", label: "standardbutton"},
{ text: "StandardListView", label: "standardlistview"},
{ text: "StandardTableView", label: "standardtableview"},
{ text: "Switch", label: "switch"},
{ text: "TabWidget", label: "tabwidget"},
{ text: "TextEdit", label: "textedit"},
{ text: "VerticalBox", label: "verticalbox"},
];
update-list-items(items) => {
test-list-items = items;
}
}
[!info] 说明
update-list-items
是一个全局回调, 我们在后面通过给它提供默认实现, 表明这是一个可以在外部调用的回调. 其目的是允许我们在 Rust 的 native code 中调用invoke_update_list_items
这个自动生成的回调来传入新的items
, 并间接更新全局单例TestShared
的test-list-items
属性.
此时, 我们运行程序会看到这样的样式 (具体布局实现不是本文重点, 略去不表):
此时回到 Rust native code 部分, 稍作更改:
let items: Rc<VecModel<ListItem>> = Rc::new(VecModel::from(vec![ListItem {
text: "HHH".into(),
label: "hhh".into(),
}]));
app.global::<TestShared>()
.invoke_update_list_items(ModelRc::from(items.clone()));
[!info] 说明
这里使用了std::rc::Rc
, 是因为目的就是在主线程中直接更新. 如果要跨线程则需要用允许在多线程中 move 的相应 wrapper 容器. 另外, Slint 中不能直接传递字符串和数组, 所以我们要将字符串 (String
或字符串切片&str
) 转变为SharedString
类型, 并使用VecModel
作为数组的封装构成一个对属性值数组的引用. 最后再通过ModelRc
封装成 Slint 能够识别的结构体数组ModelRc<ListItem>
.此外, 在 Rust 中需要使用
app.global::<TestShared>().invoke_update_list_items
的方式来触发回调函数. 其中的TestShared
就是早前定义的全局单例,invoke_xxx
是 Slint 生成 Rust 代码时映射的结构体方法, 所有的-
都被替换成了_
.
这个时候再次运行, 可以发现程序中的列表已经被替换了:
启发
这个简单的例子只是在进行属性的更新, 但是往远处想, 我们可以通过这种机制实现将 Slint UI 中难以实现的复杂逻辑 (比如状态切换, 导航, 路由) 等统统抽象成全局实例的接口. 一方面 Slint UI 中只需要进行回调函数的映射和绑定, 另一方面 Rust 可以异步进行运算和处理. 这样一来, 很多场景的代码都可以得到优化解决了. 笔者目前进行了路由的改进尝试, 效果还不错, 或许哪天有空了可以摘要记录一下.
References
- Slint Rust documentation slint::ModelRc
- Slint UI documentation Callbacks