Slint 的原则就是,用户界面 UI 使用 Slint 语言编写,逻辑部分交给其他语言处理,比如 Rust,Javascript,C++等,做到 UI 和逻辑分离,有利于编写逻辑清晰的代码。
用户界面 UI 由.slint
文件或者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”分配给 firstMyButton
的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;
x: 50px;
text: "world";
}
}
名称必须是有效的标识符。
一些元素也可以在预定义的名称下访问:
root
指组件的最外层元素。self
引用当前元素。parent
引用当前元素的父元素。
这些名称是唯一保留的,不能重新定义它们。
元素的定位和布局
Slint 是单窗口模式,也就是所有视觉元素都显示在一个窗口(Window)中。每一个组件都有定位和布局属性,比如:
属性x
、y
:表示元素相对于其父元素的位置。
属性width``height
:表示可视元素的大小。
component Empty {
in property <length> x;
in property <length> y;
in property <length> width;
in property <length> height;
//-is_internal
}
可以通过以两种方式来定位和布局已创建的图形用户界面:
- 显式 - 通过设置
x
、y
、width
和height
属性。 - 自动 - 通过使用布局元素。
显式放置非常适合元素很少的静态场景,自动布局适用于复杂的用户界面,有助于创建可扩展的用户界面。
使用显式放置
下面的示例将两个矩形放入一个窗口中,一个是蓝色的,一个是绿色的。绿色矩形是蓝色的子矩形:
// Explicit positioning
export component Example inherits Window {
width: 200px;
height: 200px;
Rectangle {
x: 100px;
y: 70px;
width: parent.width - self.x;
height: parent.height - self.y;
background: blue;
Rectangle {
x: 10px;
y: 5px;
width: 50px;
height: 30px;
background: green;
}
}
}
两个矩形的位置和内部绿色矩形的大小都是固定的。外部蓝色矩形的大小是使用width
和height
属性的绑定表达式自动计算的。计算结果是左下角与窗口的角对齐——只要窗口大小发生变化,它就会更新。
为任何几何属性指定显式值时,Slint 要求将单位附加到数字。可以选择两种不同的单位:
- 逻辑像素,使用
px
单位后缀。这是推荐单位。 - 物理像素,使用
phx
单位后缀
逻辑像素会根据系统配置的设备像素比自动缩放。例如,在现代高 DPI 显示器上,设备像素比可以为 2,因此每个逻辑像素占用 2 个物理像素。在旧屏幕上,用户界面无需任何调整即可缩放。
此外,width
和height
属性也可以指定为%
百分比单位,它相对于父元素的百分比。例如width: 50%
表示父组件width
的50%,x
和y
属性的默认值使得元素在其父元素中居中。
width
和height
的默认值取决于元素的类型。某些元素会根据其内容自动调整大小,例如Image
、Text
和大多数小部件。以下元素没有内容,当它们没有子元素时默认填充它们的父元素的:
Rectangle
TouchArea
FocusScope
Flickable
布局也默认填充父组件的大小,无论它们自己的首选大小如何。
其他元素(包括没有基础的自定义元素)默认使用它们的首选大小。
首选大小
可以使用preferred-width
和preferred-height
属性指定元素的首选大小。
如果未明确设置,则首选大小取决于子项,并且是具有较大首选大小的子项的首选大小,其x
和y
属性未设置。因此,首选大小是从子项到父项计算的,就像其他约束(最大和最小大小)一样,除非明确覆盖。
一种特殊情况是将首选大小设置为使用父级的大小,即100%
。例如,此组件将默认使用父级的大小:
component MyComponent {
preferred-width: 100%;
preferred-height: 100%;
// ...
}
使用自动布局放置
Slint 有些布局组件,可以自动计算其子元素的位置和大小:
VerticalLayout
/HorizontalLayout
:子项沿垂直或水平轴放置。GridLayout
:子元素被放置在一个由列和行组成的网格中。
您还可以嵌套布局以创建复杂的用户界面。
您可以使用不同的约束来调整自动放置,以适应用户界面设计。每个元素都有最小大小、最大大小和首选大小。使用以下属性显式设置这些:
min-width
min-height
max-width
max-height
preferred-width
preferred-height
在布局中具有指定width
和固定大小的任何元素。height
当布局中有额外空间时,元素可以沿布局轴伸展拉伸。可以使用以下属性控制元素及其兄弟元素之间的拉伸因子:
horizontal-stretch
:水平方向拉伸因子vertical-stretch
:垂直方向拉伸因子
值0
表示该元素根本不会拉伸。如果所有元素的拉伸因子都为0
,则所有元素均等拉伸0
。
这些约束属性的默认值可能取决于元素的内容。如果元素的x
或y
未设置,这些约束也会自动应用于父元素。
布局元素的通用属性
所有布局元素都具有以下共同属性:
spacing
:这控制子项之间的间距(即间隔)padding
:这指定布局内的填充(即内边距)
对于更细粒度的控制,padding
可以将属性拆分为布局每一侧的属性:
padding-left
padding-right
padding-top
padding-bottom
VerticalLayout
和HorizontalLayout
和VerticalLayout
元素HorizontalLayout
将它们的子元素放在一列或一行中。默认情况下,它们会拉伸或收缩以占据整个空间。您可以根据需要调整元素的对齐方式。
以下示例将蓝色和黄色矩形放置在一行中,并均匀拉伸到 的 200 个逻辑像素width
:
// Stretch by default
export component Example inherits Window {
width: 200px;
height: 200px;
HorizontalLayout {
Rectangle { background: blue; min-width: 20px; }
Rectangle { background: yellow; min-width: 30px; }
}
}
另一方面,下面的示例指定矩形应与布局的开始对齐(视觉左侧)。这不会导致拉伸,而是矩形会保留其指定的最小宽度:
// Unless an alignment is specified
export component Example inherits Window {
width: 200px;
height: 200px;
HorizontalLayout {
alignment: start; // 指定对其方式:左侧对齐
Rectangle { background: blue; min-width: 20px; }
Rectangle { background: yellow; min-width: 30px; }
}
}
下面的示例为更复杂的场景嵌套了两个布局:
export component Example inherits Window {
width: 200px;
height: 200px;
HorizontalLayout {
// Side panel
Rectangle { background: green; width: 10px; }
VerticalLayout {
padding: 0px;
//toolbar
Rectangle { background: blue; height: 7px; }
Rectangle {
border-color: red; border-width: 2px;
HorizontalLayout {
Rectangle { border-color: blue; border-width: 2px; }
Rectangle { border-color: green; border-width: 2px; }
}
}
Rectangle {
border-color: orange; border-width: 2px;
HorizontalLayout {
Rectangle { border-color: black; border-width: 2px; }
Rectangle { border-color: pink; border-width: 2px; }
}
}
}
}
}
对齐
每个元素都根据它们的大小width
或height
指定大小,否则将其设置为使用 min-width 或 min-height 属性设置的最小大小,或者内部布局的最小大小,以较大者为准。
元素根据对齐方式放置alignment
仅当布局属性为LayoutAlignment.stretch
(默认)时,元素的大小才大于最小大小
此示例显示了不同的对齐可能性
export component Example inherits Window {
width: 300px;
height: 200px;
VerticalLayout {
HorizontalLayout {
alignment: stretch;
Text { text: "stretch (default)"; }
Rectangle { background: blue; min-width: 20px; }
Rectangle { background: yellow; min-width: 30px; }
}
HorizontalLayout {
alignment: start;
Text { text: "start"; }
Rectangle { background: blue; min-width: 20px; }
Rectangle { background: yellow; min-width: 30px; }
}
HorizontalLayout {
alignment: end;
Text { text: "end"; }
Rectangle { background: blue; min-width: 20px; }
Rectangle { background: yellow; min-width: 30px; }
}
HorizontalLayout {
alignment: start;
Text { text: "start"; }
Rectangle { background: blue; min-width: 20px; }
Rectangle { background: yellow; min-width: 30px; }
}
HorizontalLayout {
alignment: center;
Text { text: "center"; }
Rectangle { background: blue; min-width: 20px; }
Rectangle { background: yellow; min-width: 30px; }
}
HorizontalLayout {
alignment: space-between;
Text { text: "space-between"; }
Rectangle { background: blue; min-width: 20px; }
Rectangle { background: yellow; min-width: 30px; }
}
HorizontalLayout {
alignment: space-around;
Text { text: "space-around"; }
Rectangle { background: blue; min-width: 20px; }
Rectangle { background: yellow; min-width: 30px; }
}
}
}
拉伸算法
当alignment
设置为拉伸(默认)时,元素的大小将调整为它们的最小尺寸,然后在元素之间共享额外的空间,剩下空间大小根据拉伸因子 horizontal-stretch
或vertical-stretch
的值按比例计算分派。拉伸尺寸不会超过最大尺寸。拉伸因子是一个浮点数。具有默认内容大小的元素通常默认为 0,而默认为其父元素大小的元素默认为 1。拉伸因子为 0 的元素将保持其最小大小,除非所有其他元素也具有拉伸因子0 或达到其最大尺寸。
例子:
export component Example inherits Window {
width: 300px;
height: 200px;
VerticalLayout {
// Same stretch factor (1 by default): the size is divided equally
HorizontalLayout {
Rectangle { background: blue; }
Rectangle { background: yellow;}
Rectangle { background: green;}
}
// Elements with a bigger min-width are given a bigger size before they expand
HorizontalLayout {
Rectangle { background: cyan; min-width: 100px;}
Rectangle { background: magenta; min-width: 50px;}
Rectangle { background: gold;}
}
// Stretch factor twice as big: grows twice as much
HorizontalLayout {
Rectangle { background: navy; horizontal-stretch: 2;}
Rectangle { background: gray; }
}
// All elements not having a maximum width have a stretch factor of 0 so they grow
HorizontalLayout {
Rectangle { background: red; max-width: 20px; }
Rectangle { background: orange; horizontal-stretch: 0; }
Rectangle { background: pink; horizontal-stretch: 0; }
}
}
}
for
表达式
VerticalLayout 和 Horizontal 布局也可以包含for
orif
表达式:
export component Example inherits Window {
width: 200px;
height: 50px;
HorizontalLayout {
Rectangle { background: green; }
for t in [ "Hello", "World", "!" ] : Text {
text: t;
}
Rectangle { background: blue; }
}
}
网格布局
GridLayout 将元素放置在网格中。每个元素都获得属性row
、col
、rowspan
和colspan
。可以使用子Row
元素,也可以row
显式设置属性。这些属性必须在编译时静态已知,因此不可能使用算术或依赖属性。在网格布局 GridLayout 中不允许使用for
or if
表达式。
- 使用
Row
元素的例子:
export component Foo inherits Window {
width: 200px;
height: 200px;
GridLayout {
spacing: 5px;
Row {
Rectangle { background: red; }
Rectangle { background: blue; }
}
Row {
Rectangle { background: yellow; }
Rectangle { background: green; }
}
}
}
- 使用
col
androw
属性的例子
export component Foo inherits Window {
width: 200px;
height: 150px;
GridLayout {
spacing: 0px;
Rectangle { background: red; }
Rectangle { background: blue; }
Rectangle { background: yellow; row: 1; }
Rectangle { background: green; }
Rectangle { background: black; col: 2; row: 0; }
}
}
容器组件
创建组件时,有时影响子元素在使用时的放置位置很有用。例如,假设一个组件在用户放置在其中的任何元素上方绘制一个标签:这时可以使用BoxWithLabel
布局来实现这样的功能。先定义一个Text
元素成为默认直接子元素,然后通过@children
定义其他子元素在组件层次结构中的位置:
component BoxWithLabel inherits GridLayout {
Row {
Text { text: "label text here"; }
}
Row {
@children
}
}
export component MyApp inherits Window {
preferred-height: 100px;
BoxWithLabel {
Rectangle { background: blue; }
Rectangle { background: yellow; }
}
}
字体处理
诸如Text
和TextInput
之类的元素可以呈现文本并允许通过不同的属性自定义文本的外观。font-family
会影响用于呈现到屏幕font-size
的font-weight
字体的选择。
为渲染选择的字体会自动从系统中获取。也可以使用导入的自定义字体。自定义字体必须是 TrueType 字体 ( .ttf
) 或 TrueType 字体集 ( .ttc
)。可以使用import
:导入自定义字体在 .slint 文件中,表示使用导入字体,并全局可用于font-family
属性。import "./my_custom_font.ttf"
例如:
import "./NotoSans-Regular.ttf";
export component Example inherits Window {
// default-font-size: 10px; // 设置窗口内默认的文字大小
// default-font-weight: 900; // 设置窗口默认的字体粗细 100~900
default-font-family: "Noto Sans"; // 设置窗口使用的字体系列
Text {
text: "Hello World";
}
}
焦点处理
某些元素例如TextInput
不仅接受来自鼠标/手指的输入,还接受来自(虚拟)键盘的键事件。为了让项目接收这些事件,它必须有焦点。这可以通过has-focus
(out) 属性判断。
- 通过调用手动激活元素上的焦点
focus()
:
import { Button } from "std-widgets.slint";
export component App inherits Window {
VerticalLayout {
alignment: start;
Button {
text: "press me";
clicked => { input.focus(); // 点击激活TextInput焦点}
}
// 某些元素例如TextInput不仅接受来自鼠标/手指的输入,还接受来自(虚拟)键盘的键事件。
// 为了让项目接收这些事件,它必须有焦点。这可以通过has-focus(out) 属性看到。
input := TextInput {
text: "I am a text input field";
}
}
}
- 如果
TextInput
包装在一个组件中,可以使用forward-focus
属性转发焦点激活:
import { Button } from "std-widgets.slint";
component LabeledInput inherits GridLayout {
forward-focus: input; // 转发input的焦点
Row {
Text {
text: "Input Label:";
}
// 申明一个input
input := TextInput {}
}
}
export component App inherits Window {
GridLayout {
Button {
text: "press me";
clicked => { label.focus(); // 点击激活LabeledInput焦点}
}
// 申明一个label,内部包装一个input
label := LabeledInput {
}
}
}
如果您forward-focus
在Window
上使用该属性,则指定的元素将在窗口第一次接收到焦点时接收到焦点 - 它成为初始焦点元素。