你使用 Slint 语言编写用户界面,并保存为扩展名为 .slint 的文件。
每个 .slint 文件定义一个或多个组件。这些组件声明了一个元素树。组件构成了 Slint 中组合的基础。利用它们,你可以构建自己可重用的 UI 控件集。你可以在另一个组件中以元素的形式使用每个已声明的组件(按其名称)。
下面是组件与元素的示例:
component MyButton inherits Text { color: black; // ...}
export component MyApp inherits Window { preferred-width: 200px; preferred-height: 100px; Rectangle { width: 200px; height: 100px; background: green; } MyButton { x:0;y:0; text: "hello"; } MyButton { y:0; x: 50px; text: "world"; }}MyButton 和 MyApp 都是组件。Window 和 Rectangle 是 MyApp 使用的内置元素。MyApp 还以两个独立元素的形式重用了 MyButton 组件。
元素具有属性,你可以为它们赋值。上面的示例将字符串常量 "hello" 赋值给第一个 MyButton 的 text 属性。你也可以赋值整个表达式。当表达式所依赖的任何属性发生变化时,Slint 会重新计算该表达式,这使得用户界面具有响应式特性。
你可以使用 := 语法为元素命名:
component MyButton inherits Text { // ...}
export component MyApp inherits Window { preferred-width: 200px; preferred-height: 100px;
hello := MyButton { x:0;y:0; text: "hello"; } world := MyButton { y:0; text: "world"; x: 50px; }}Note
名称必须是有效的 标识符。
一些元素也可以通过预定义的名称访问:
root指代组件的最外层元素。self指代当前元素。parent指代当前元素的父元素。
这些名称是保留的,你不能重新定义它们。
注释
注释是 Slint 编译器忽略的代码行。它们用于解释代码或临时禁用代码。
单行注释
单行注释以 // 开头,以换行符结束。
// Amazing text! This is a comment多行注释
多行注释以 /* 开头、*/ 结束,并以换行符结束。
/* This is a multi line comment. It can span multiple lines.*/元素与组件
Slint 语言的核心部分是元素和组件。从技术上讲它们是相同的,因此一旦你知道如何声明和使用其中之一,你也就知道了另一个。元素是构成 Slint 语言的基本构件,而组件(也称为控件)是由多个元素和属性组合而成的更大单元。
如果你来自其他语言(例如 HTML 或 React),你可能习惯于使用开闭标签以及自闭合标签。
<!-- opening and closing tag --><Button>Hello World</Button> <!-- self closing tag --><img/>Slint 简单地提供了一种声明项的方式:使用 element-name 后跟一对花括号 {},其中包含该元素的属性。
// validText {}
Text {}// Valid, but considered bad Slint practiceText{}
// Not valid due to terminating semicolonText {};Note
Slint 工具链提供了一个代码格式化器,用于强制实施被认为是良好实践的格式。
如果你是编程新手,可以与其他开发者讨论你不喜欢的一些代码格式方面的话题,由此结交朋友。这是开发者们喜爱并欣赏的一种闲聊方式。
根元素
component MyApp { Text { text: "Hello World"; font-size: 24px; }}要成为有效的 Slint 文件,根元素必须是一个组件。然后在组件内部你可以声明任意数量的元素。这将在后面更详细地解释,此时理解它并不重要。
属性
属性是赋给元素的值。它们使用 property-name: value; 语法进行设置。
标识符
标识符可以由字母(a-zA-Z)、数字(0-9)、下划线(_)或短横线(-)组成。 它们不能以数字或短横线开头(但可以以下划线开头)。 下划线会被标准化为短横线。这意味着下面两个标识符是相同的:foo_bar 和 foo-bar。
条件元素
if 构造仅在给定条件为真时实例化元素。 语法为 if condition : id := Element { ... }
export component Example inherits Window { preferred-width: 50px; preferred-height: 50px; if area.pressed : foo := Rectangle { background: blue; } if !area.pressed : Rectangle { background: red; } area := TouchArea {}}模块
在 .slint 文件中声明的组件可以通过导出和导入的方式在其他 .slint 文件中作为元素使用。
默认情况下,在 .slint 文件中声明的每个类型都是私有的。export 关键字会改变这一点。
component ButtonHelper inherits Rectangle { // ...}
component Button inherits Rectangle { // ... ButtonHelper { // ... }}
export { Button }在上面的示例中,Button 可被其他 .slint 文件访问,但 ButtonHelper 不行。
也可以仅为了导出的目的更改名称,而不影响其内部使用:
component Button inherits Rectangle { // ...}
export { Button as ColorButton }在上面的示例中,Button 不能从外部访问,而是以 ColorButton 这个名称提供。
为方便起见,导出组件的第三种方式是直接将其声明为已导出:
export component Button inherits Rectangle { // ...}类似地,可以导入从其他文件导出的组件:
import { Button } from "./button.slint";
export component App inherits Rectangle { // ... Button { // ... }}如果两个文件以相同名称导出一个类型,你可以在导入时为其分配一个不同的名称:
import { Button } from "./button.slint";import { Button as CoolButton } from "../other_theme/button.slint";
export component App inherits Rectangle { // ... CoolButton {} // from other_theme/button.slint Button {} // from button.slint}元素、全局变量和结构体都可以被导出和导入。
也可以导出从其他文件导入的全局变量(参见 全局单例):
import { Logic as MathLogic } from "math.slint";export { MathLogic } // known as "MathLogic" when using native APIs to access globals模块语法
支持以下导入类型的语法:
import { MyButton } from "module.slint";import { MyButton, MySwitch } from "module.slint";import { MyButton as OtherButton } from "module.slint";import { MyButton, /* ... */, MySwitch as OtherSwitch,} from "module.slint";支持以下导出类型的语法:
// Export declarationsexport component MyButton inherits Rectangle { /* ... */ }
// Export listscomponent MySwitch inherits Rectangle { /* ... */ }export { MySwitch }export { MySwitch as Alias1, MyButton as Alias2 }
// Re-export types from other moduleexport { MyCheckBox, MyButton as OtherButton } from "other_module.slint";
// Re-export all types from other module (only possible once per file)export * from "other_module.slint";组件库
将代码库拆分为单独的模块文件可以促进重用,并通过允许你隐藏辅助组件来改善封装。这在你项目的自有目录结构中可以很好地工作。要在项目之间共享组件库而不硬编码其相对路径,请使用组件库语法:
import { MySwitch } from "@mylibrary/switch.slint";import { MyButton } from "@otherlibrary";在上面的示例中,MySwitch 组件将从名为 mylibrary 的组件库导入,Slint 会在其中查找 switch.slint 文件。因此 mylibrary 必须声明为指向一个目录,以便后续能够成功查找到 switch.slint。MyButton 将从 otherlibrary 导入。因此 otherlibrary 必须声明为指向一个导出 MyButton 的 .slint 文件。
每个库(文件或目录)的路径必须在编译时单独定义。 使用以下方法之一来帮助 Slint 编译器将库解析为磁盘上的正确路径:
使用
slint_target_sources指定LIBRARY_PATHS。例如:
slint_target_sources(my_application ui/main.slint LIBRARY_PATHS material=${CMAKE_CURRENT_SOURCE_DIR}/material-1.0/material.slint)cmake
- 在
build.rs中,调用with_library_paths以提供从库名到路径的映射。例如:
// build.rsfn main() { let manifest_dir = std::path::PathBuf::from(std::env::var_os("CARGO_MANIFEST_DIR").unwrap()); let library_paths = std::collections::HashMap::from([( "example".to_string(), manifest_dir.join("third_party/example/ui/lib.slint"), )]); let config = slint_build::CompilerConfiguration::new().with_library_paths(library_paths); slint_build::compile_with_config("ui/main.slint", config).unwrap();}- 使用
LoadFileOptions中的loadFile提供libraryPaths映射。例如:
let ui = slint.loadFile("/path/to/main.slint", { libraryPaths: { "material": "/path/to/material-1.0/material.slint" }});- 使用
load_file提供library_paths字典。例如:
ui = slint.load_file( "/path/to/main.slint", library_paths={ "material": "/path/to/material-1.0/material.slint" },)- 通过命令行调用
slint-viewer时,为每个组件库传递-Lmylibrary=/path/to/my/library。 - 使用 VS Code 扩展时,使用
Slint: Library Paths设置来配置 Slint 扩展的库路径。示例如下:
"slint.libraryPaths": { "mylibrary": "/path/to/my/library", "otherlibrary": "/path/to/otherlib/index.slint",},这也可以在提交到你仓库中的 .vscode/settings.json 文件中进行编辑。 相对路径是相对于工作区根目录解析的。
- 对于其他编辑器,可以将它们配置为像
slint-viewer那样向slint-lsp传递-L参数。