使用 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, 并间接更新全局单例 TestSharedtest-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

  1. Slint Rust documentation slint::ModelRc
  2. Slint UI documentation Callbacks
posted @ 2024-01-15 00:14  那阵东风  阅读(766)  评论(0编辑  收藏  举报