slint ui学习笔记

slint学习笔记

slint是一个类似qml的标记语言(xml/css之类方便界面设计的语言),经过编译器slint compile(类似QT的moc/uic/rcc工具)可完整的转换成Cpp或者Rust。
其开发方式类似qml+cpp,这得益于slint ui的两位初始创建人来自QT团队。

与qt的qml相比,slint有几个优点:

  1. 全slint代码转换成本地语言
  2. 原生多本地语言支持(Rust/Cpp/NodeJs)
  3. 无历史包袱
  4. 开源协议(手机和嵌入式收费)

slint 优点

  • 流畅:平滑,触摸友好的用户接口
  • 跨平台:目标平台包括嵌入式设备和桌面应用软件(手机和网页)
  • 多语言:可以使用自己擅长的语言API(C++,Rust,JavaScript)

许可协议

GPLv3 + 商业

1.1版本以后:
许可证变更:新增更宽松的免版税许可证
除了 GPLv3 和专有商业许可,此版本添加了新的免版税许可作为第三个选项,该许可证可免费用于构建桌面或 Web 应用程序,并消除了 Copyleft 许可证的限制。
所有示例、教程中的示例代码等现在都可以在宽松的 MIT 许可证下使用。这可以在应用程序中自由复制、修改和使用代码,而不受任何 Copyleft 条款的限制。
简化了 CLA 协议,所有贡献现在均在 MIT 无署名许可证下实现,没有版权限制。

https://github.com/slint-ui/slint/blob/release/1.3/LICENSES/LicenseRef-Slint-Royalty-free-1.1.md

个人结论

思路很好,做UI感受很不错(IDE+预览)。
有种LVGL Pro的感觉,对于简单UI的程序,完全够用。对于复杂UI缺失内容太多。
支持多种前端和后端,支持很广泛,点赞。
支持嵌入式设备,点赞!(收费问题很坑)

值得入手学习一下,作为小工具语言。(替代flutter)
如果做商业,用在嵌入式领域rust+slint还可以。(如果不用rust,可以完全使用LVGL替代)

所以,以语言为导向,rust系,推荐。c++系,不推荐。

基础用法

// 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;
        }
    }
    // alignment 和 horizontal-stretch
    HorizontalLayout {
        alignment: start;
        Rectangle { background: blue; min-width: 20px; }
        Rectangle { background: yellow; min-width: 30px; }
        Rectangle { background: navy; horizontal-stretch: 2;}
    }
    // border
    Rectangle {
        border-color: orange; border-width: 2px;
        HorizontalLayout {
            Rectangle { border-color: black; border-width: 2px; }
            Rectangle { border-color: pink; border-width: 2px; }
        }
    }
    // 支持for循环
    HorizontalLayout {
        spacing: 5px;
        Rectangle { background: green; }
        for t in [ "Hello", "World", "!" ] : Text {
            text: t;
        }
        Rectangle { background: blue; }
    }
    // grid
    GridLayout {
        spacing: 0px;
        Rectangle { background: red; }
        Rectangle { background: blue; }
        Rectangle { background: yellow; row: 1; }
        Rectangle { background: green; }
        Rectangle { background: black; col: 2; row: 0; }
    }
}

常规属性:

  • px像素, phx物理像素(考虑缩放)
  • preferred-width/preferred-height: 优先的size,不指定时由子元素决定
    • 如果制定了 width、height 那么大小就不可变了
    • horizontal-stretch、vertical-stretch 非零时,表示拉伸
  • VerticalLayout / HorizontalLayout / GridLayout:布局,默认子元素拉伸
  • padding/padding-left/padding-right/padding-top/bottom: 子元素与layout之间的空白
  • spacing: layout内子元素之间的空白
  • alignment: stretch/start/end/center/space-between/space-around

容器(可重用组件)

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; }
    }
}

高阶用法

  • 多国语言参考 https://slint.dev/releases/1.3.0/docs/slint/src/language/concepts/translations
import { Button } from "std-widgets.slint";
import "./NotoSans-Regular.ttf";  // 引用字体

component LabeledInput inherits GridLayout {
    forward-focus: input;
    Row {
        Text {
            text: @tr("Input Label:");  // @tr() 用来做多国语言
            font-family: "xxx";
            font-size: 20px;
            font-weight: bold;
        }
        input := TextInput {}
    }
}

export component App inherits Window {
    default-font-family: "Noto Sans";  // 默认字体
    
    GridLayout {
        Button {
            text: "press me";
            clicked => { label.focus(); }  // 组件的事件
        }
        // 为组件起名
        label := LabeledInput {
        }
    }
}

完整语法

1. comments
// 或者 /* .. */

2. 命名
a-zA-Z 0-9 _ -
注意: _会被统一改为-,所以 foo_bar 等价于 foo-bar

3. 数据类型
参考 https://slint.dev/releases/1.3.0/docs/slint/src/language/syntax/types

angle:0deg/1.2rad/0.25turn
bool: true/false
brush: Colors.red... // 通常是color或者渐变色linear-gradient/radial-gradient
color: #RRGGBBAA / #RGB / transparent
duration: 1ms/2s  // the duration of animations
easing:  linear/ease/... // animation allow specifying an easing curve
float: 0.21/42%
image: @image-url("...")  // image reference
int: 10/222/..
length: 1px / 1pt / 1in / 1mm / 1cm  // used for x, y, width and height coordinates
percent: 0% // 32-bit floating point number that is interpreted as percentage
physical-length: 1phx // an amount of physical pixels
relative-font-size: 0rem  // font size factor that is multiplied with the Window
string: "abc"  // UTF-8 encoded, 支持 \n \u{x} \\ \{ex} 转义符

4. 数据结构
// 结构体
export struct Player  {
    name: string,
    score: int,
}

anonymous structures using 
{ identifier1: type2, identifier1: type2 }

// 枚举
export enum CardSuit { clubs, diamonds, hearts, spade }

// 数组
[int] l1 : [1,12,35];
l1.length
l1[index]

5. 类型转换
int,float可以直接转string
physical-length,length 可以直接转
struct直接如果有相同属性名可以直接转
array不能直接互相转
string转float: "3.14".to-float()/is-float()

6. Properties
每个element都有built-in的属性,比如width、color等。
属性的值可以是值,也可以是表达式`{42px}`,支持各种运算符.

//自定义属性
export component Example {
    // declare a property of type int with the name `my-property`
    property my-property;
    // declare a property with a default value
    property my-second-property: 42;
    
    // This is meant to be set by the user of the component.
    in property  text;
    // This property is meant to be read by the user of the component.
    out property  pressed;
    // This property is meant to both be changed by the user and the component itself.
    in-out property  checked;

    // This property is internal to this component.
    private property  has-mouse;
}

// 默认的property是private, 只在component内部使用
// in 表示input, 可以被外部user修改/binding, assignment by callback。 内部理论上也可以。
// out 表示output, 可以被component内部修改,但是外部user只有read-only.
// in-out both

7. Binding
// 普通绑定
import { Button } from "std-widgets.slint";
export component Example inherits Window {
    preferred-width: 50px;
    preferred-height: 50px;
    Button {
        property  counter: 3;
        clicked => { self.counter += 3 }
        text: self.counter * 2;  // 根据counter动态计算 binding
    }
}

// 双向绑定
export component Example  {
    in property rect-color <=> r.background;
    // It's allowed to omit the type to have it automatically inferred
    in property rect-color2 <=> r.background;
    r:= Rectangle {
        width: parent.width * 50%;  // 单向 Relative Lengths
        height: parent.height * 50%; // 单向 Relative Lengths
        background: blue;  // 双向同步
    }
}

8. Function

// function 关键字定义函数,默认 private
export component Example {
    in-out property  min;
    in-out property  max;
    protected function set-bounds(min: int, max: int) {
        root.min = min;
        root.max = max
    }
    public pure function inbound(x: int) -> int {
        return Math.min(root.max, Math.max(root.min, x));
    }
}

9. Callback
communicate changes of state to the outside

export component Example inherits Rectangle {
    // declare a callback
    callback hello(int, string);
    
    callback clicked <=> area.clicked;  // 双向绑定两个 callback
    
    hello(aa, bb) => { aa + bb }  // callback的实现,可以在其他地方通过 => 绑定一个callback
    
    area := TouchArea {
        // sets a handler with `=>`
        clicked => {
            // emit the callback
            root.hello(1, "abc")
        }
    }
}

10. Repetition and condition
// 通过for循环创建重复component
export component Example inherits Window {
    preferred-width: 300px;
    preferred-height: 100px;
    for my-color[index] in [ #e11, #1a2, #23d ]: Rectangle {
        height: 100px;
        width: 60px;
        x: self.width * index;
        background: my-color;
    }
}

// if 条件执行不同渲染
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 {}
}


11. Animation
// 属性动画,具体效果参考 https://slint.dev/releases/1.3.0/docs/slint/src/language/syntax/animations
export component Example inherits Window {
    preferred-width: 100px;
    preferred-height: 100px;

    background: area.pressed ? blue : red;
    animate background {
        duration: 250ms;
    }

    area := TouchArea {}
}

12. States
13. Global Singletons
14. Modules
// button.slint
component Button inherits Rectangle {
    // ...
}

export { Button as ColorButton } // option1
export { Button }  // Option2

// main.slint
import { Button } from "./button.slint";
import { Button as CoolButton } from "../other_theme/button.slint";

export component App inherits Rectangle {
    // ...
    Button {
        // ...
    }
}


// 相对路径引入
import { MySwitch } from "@mylibrary";

内建功能

  1. init()

component MyButton inherits Rectangle {
    in-out property  text: "Initial";
    init => {
        // If `text` is queried here, it will have the value "Hello".
        debug("first");
    }
}

component MyCheckBox inherits Rectangle {
    init => { debug("second"); }
}

export component MyWindow inherits Window {
    MyButton {
        text: "Hello";
        init => { debug("third"); }
    }
    MyCheckBox {
    }
}
// print “first”, then “second”, and then “third”
  1. Common properties
    Geometry
  • width/height: length
  • x/y: length
  • z: float
  • absolute-position: point

Layout

  • col/row/colspan/rowspan: int
  • horizontal-stretch/vertical-stretch: float
  • max-width/max-height/min-width/min-height: length
  • preferred-width/preferred-height: length

Miscellaneous

  • cache-rendering-hint: bool
  • dialog-button-role: enum DialogButtonRole
  • opacity: float, 0-1, 0 is fully transparent (invisible), and 1 is fully opaque
  • visible: bool

Drop Shadows(support Rectangle)

  • drop-shadow-blur: length // The radius of the shadow
  • drop-shadow-color: color // The base color of the shadow
  • drop-shadow-offset-x/y: length // The horizontal and vertical distance of the shadow from the element’s frame

Accessibility

  • accessible-role: enum AccessibleRole
  • accessible-checkable: bool
  • accessible-checked: bool
  • accessible-description: string
  • accessible-has-focus: bool
  • accessible-label: string
  • accessible-value-maximum/minimum/step: float
  • accessible-value: string

Image

  • colorize: brush // is used as an alpha mask and is drawn in the given color
  • image-fit: enum ImageFit // default contain, fill parent
  • image-rendering: enum ImageRendering //image scaled, default smooth
  • rotation-angle: angle
  • rotation-origin-x/y: length // Rotates the image by the given origin point.
  • source: image // @image-url(“…”)
  • source-clip-x/y/width/height: int // define the region of the source image that is rendered
  • width/height: length

Path(SVG or slint)

  • fill: brush // The color for filling the shape of the path
  • fill-rule: enum FillRule // nonzero
  • stroke: brush // the outline of the path.
  • stroke-width: length
  • width/height: length // If non-zero, the path will be scaled to fit into the specified height
  • viewbox-x/viewbox-y/viewbox-width/viewbox-height: float // the position and size of the viewport of the path
  • clip: bool // default false. if true, rendering will be clipped at the boundaries of the view box
  • commands: string // SVG Path, such as: “M 0 0 L 0 100 A 1 1 0 0 0 100 100 L 100 0 Z”

custom the Path in slint:

// more command: https://slint.dev/releases/1.3.0/docs/slint/src/language/builtins/elements#path-using-svg-path-elements
export component Example inherits Path {
    width: 100px;
    height: 100px;
    stroke: blue;
    stroke-width: 1px;

    MoveTo {
        x: 0;
        y: 0;
    }
    LineTo {
        x: 0;
        y: 100;
    }
    ArcTo {
        radius-x: 1;
        radius-y: 1;
        x: 100;
        y: 100;
    }
    LineTo {
        x: 100;
        y: 0;
    }
    Close {
    }
}

PopupWindow

  • close-on-click : in bool
  • show()
  • close()

Rectangle

  • background : in brush
  • border-color : in brush
  • border-radius : in length
  • border-width : in length
  • clip : in bool //default false, 超出parent border时,子元素是显示还是裁剪掉

TextInput

  • color: brush
  • font-family: string
  • font-size: length
  • font-weight: int // 100-900
  • font-italic: bool
  • has-foucs: out bool
  • vertical/horizontal-alignment: enum TextVerticalAlignment/TextHorizontalAlignment
  • input-type: enum InputType // default text, password/number/decimal
  • letter-spacing: length
  • read-only: bool
  • selection-background/foreground-color: color // 选中文本颜色
  • single-line: bool // default true
  • text-cursor-width: length
  • wrap: enum TextWrap //default no-wrap, Only makes sense when single-line is false
  • focus()
  • select-all()
  • clear-selection()
  • copy() // 剪切板
  • cut() // 剪切板
  • paste() //剪切板
  • accepted() : callback // when enter key is pressed
  • cursor-position-changed(Point) : callback // cursor was moved to the new (x, y) position
  • edited() : callback // the text has changed because the user modified it

Text

  • color: brush
  • font-family: string
  • font-size: length
  • font-weight: int // 100-900
  • font-italic: bool
  • vertical/horizontal-alignment: enum TextVerticalAlignment/TextHorizontalAlignment
  • letter-spacing: length
  • wrap: enum TextWrap //default no-wrap, Only makes sense when single-line is false
  • overflow: enum TextOverflow // default clip. What happens when the text overflows
  • text: string

TouchArea

  • has-hover : out bool
  • mouse-cursor: enum MouseCursor
  • mouse-x, mouse-y: length //
  • pressed-x, pressed-y: length // the position of the mouse at the moment it was last pressed
  • pressed : bool
  • clicked() : callback
  • moved() : callback // his will only be called if the mouse is also pressed
  • pointer-event(PointerEvent) : callback // when a button was pressed or released.
  • scroll-event(PointerScrollEvent) -> EventResult: callback // when the mouse wheel was rotated or another scroll gesture was made

Window

  • preferred-width/preferred-height: length // initial window size
  • always-on-top: bool
  • background: brush // default depends on window style
  • default-font-family: string
  • default-font-size: length
  • default-font-weight: int // 100-900, 400 normal
  • icon: image // window icon shown in the title bar or the task bar
  • no-frame: bool // window should be borderless/frameless or not
  • title: string // window title that is shown in the title bar

functions/global

  • animation-tick()
  • debug(…)
  • TextInputInterface.text-input-focused:bool //全局输入状态,做屏幕键盘用
  • rgb(int, int, int) -> color, rgba(int, int, int, float) -> color
  • abs(float) -> float / ceil(float) -> int / floor(float) -> int / mod(T, T) -> T
  • Colors.aquamarine/Colors.red
  • Key.F1/Key.Home
  1. Widgets
    All widgets support all properties common to builtin elements.
  • AboutSlint
  • Button // icon, checkable, text, primary, clicked()
  • CheckBox // checked, text, toggled()
  • Switch // checked, text, toggled()
  • ComboBox // current-index, model [], selected(string)
  • GroupBox // title
  • LineEdit // input-type, placeholder-text, text, accepted(string), edited(string)
  • TextEdit // text, wrap, edited(string)
  • ListView // like ScrollView, 虚拟的list容器,需要自定义item
  • ScrollView // viewport-width/height, visible-width/height
  • StandardListView // 标准list. current-item, model, set-current-item(int), current-item-changed(int)
  • ProgressIndicator //indeterminate, progress
  • Spinner // indeterminate, progress
  • Slider // value, maximum, minimum, orientation, changed(float)
  • SpinBox // value, maximum, minimum, edited(int)
  • StandardButton // kind, pressed, clicked()
  • StandardTableView // 标准table, current-sort-column, columns, rows, current-row, set-current-row(int), current-row-changed(int)
  • TabWidget // current-index, content-width/height. For Tab: icon, num-tabs, tab-index, title, current-focused
  • VerticalBox/HorizontalBox/GridBox // as with VerticalLayout/HorizontalLayout/GridLayout

Widget Style

The widget style is determined at your project’s compile time.
If no style is selected, native is the default.

Renderer

Slint可以选择多种渲染后端,各有利弊。
https://slint.dev/releases/1.3.0/docs/slint/src/advanced/backends_and_renderers

Qt Renderer
Software Renderer
FemtoVG Renderer
Skia Renderer

你可能感兴趣的:(Linux,嵌入式,界面库,ui,slint)