• 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; }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    常规属性:

    • 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; }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    高阶用法

    • 多国语言参考 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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    完整语法

    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199

    内建功能

    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    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 {
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    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
    
    • 1
    • 2
    • 3
    • 4
  • 相关阅读:
    计算机毕设(附源码)JAVA-SSM基于框架的毕业生就业管理系统
    编程基础 —— 链表
    从零开始完成一副西南地区全图的地图版面设计
    rust学习——智能指针
    什么是Mirai僵尸网络
    FreeSWITCH在session上执行定时挂机与取消
    数据库管理-第116期 Oracle Exadata 06-ESS-下(202301114)
    React使用forward和useImperativeHandle实现父组件调用子组件
    Linux配置java,maven,marshalsec环境
    【第五部分 | JS WebAPI】2:DOM 元素操作
  • 原文地址:https://blog.csdn.net/bbdxf/article/details/134422814