【Rust GUI开发入门】编写一个本地音乐播放器(4. 绘制按钮组件)
本系列教程对应的代码已开源在 Github zeedle
开始介绍播放器UI的构建部分,但是不会详细讲解Slint UI的设计基础,没有意义,因为官方文档介绍的已经十分详细了,一些基本用法需要借助参考文档熟悉。
Slint UI支持使用类SVG指令绘制矢量图标,为了保证UI的风格统一性,这里不使用网络上的图标,直接使用Path指令绘制,具体语法参考Path | Slint Docs。
需要绘制的图标如下:
- 播放/暂停
- 上一曲
- 下一曲
- 播放模式
直接给出.slint
代码:
import { Palette } from "std-widgets.slint";
export component NextSongButton inherits Window {callback clicked();TouchArea {clicked => {root.clicked();}Path {width: 100%;height: 100%;MoveTo {x: 0;y: 0;}LineTo {x: 0;y: 100;}LineTo {x: 70;y: 50;}Close { }MoveTo {x: 80;y: 0;}LineTo {x: 80;y: 100;}stroke: Palette.control-foreground;stroke-width: root.width * 0.1;}}
}export component PrevSongButton inherits Window {callback clicked();TouchArea {clicked => {root.clicked();};Path {width: 100%;height: 100%;MoveTo {x: 80;y: 0;}LineTo {x: 80;y: 100;}LineTo {x: 10;y: 50;}Close { }MoveTo {x: 0;y: 0;}LineTo {x: 0;y: 100;}stroke: Palette.control-foreground;stroke-width: root.width * 0.1;}}
}export component PlayPauseButton inherits Window {in-out property <bool> paused:true;callback toggled();TouchArea {clicked => {root.toggled();}if !root.paused:Path {width: 100%;height: 100%;MoveTo {x: 0;y: 0;}LineTo {x: 0;y: 100;}LineTo {x: 30;y: 100;}LineTo {x: 30;y: 0;}Close { }MoveTo {x: 50;y: 0;}LineTo {x: 50;y: 100;}LineTo {x: 80;y: 100;}LineTo {x: 80;y: 0;}Close { }stroke: Palette.control-foreground;stroke-width: root.width * 0.1;}if root.paused:Path {width: 100%;height: 100%;MoveTo {x: 0;y: 0;}LineTo {x: 0;y: 100;}LineTo {x: 80;y: 50;}Close { }stroke: Palette.control-foreground;stroke-width: root.width * 0.1;}}
}export component InOrderButton inherits Window {in-out property <bool> selected;Path {MoveTo {x: 90;y: 50;}ArcTo {x: 50;y: 10;radius-x: 40;radius-y: 40;x-rotation: 0;large-arc: true;sweep: true;}LineTo {x: 40;y: 4;}MoveTo {x: 50;y: 10;}LineTo {x: 42;y: 18;}stroke-width: 1px;stroke: selected ? Palette.accent-background : Palette.foreground;}
}export component RecursiveButton inherits Window {in-out property <bool> selected;Path {MoveTo {x: 90;y: 50;}ArcTo {x: 50;y: 10;radius-x: 40;radius-y: 40;x-rotation: 0;large-arc: true;sweep: true;}LineTo {x: 40;y: 4;}MoveTo {x: 50;y: 10;}LineTo {x: 42;y: 18;}MoveTo {x: 50;y: 40;}LineTo {x: 50;y: 60;}MoveTo {x: 50;y: 40;}LineTo {x: 44;y: 44;}stroke-width: 1px;stroke: selected ? Palette.accent-background : Palette.foreground;}
}@rust-attr(derive(serde::Serialize, serde::Deserialize))
export enum PlayMode { InOrder, Recursive, Random}export component OverlapButton inherits Window {in-out property <PlayMode> mode;callback clicked();TouchArea {width: 100%;height: 100%;clicked => {root.clicked();}if mode == PlayMode.Recursive: RecursiveButton {width: 100%;height: 100%;selected: mode == PlayMode.Recursive || mode == PlayMode.InOrder;}if mode != PlayMode.Recursive: InOrderButton {width: 100%;height: 100%;selected: mode == PlayMode.Recursive || mode == PlayMode.InOrder;}}
}export component RandomButton inherits Window {callback clicked();in-out property <bool> selected;TouchArea {clicked => {root.clicked();};Path {MoveTo {x: 0;y: 20;}LineTo {x: 30;y: 20;}LineTo {x: 70;y: 80;}LineTo {x: 100;y: 80;}LineTo {x: 90;y: 70;}MoveTo {x: 100;y: 80;}LineTo {x: 90;y: 90;}// 第二段MoveTo {x: 0;y: 80;}LineTo {x: 30;y: 80;}LineTo {x: 70;y: 20;}LineTo {x: 100;y: 20;}LineTo {x: 90;y: 30;}MoveTo {x: 100;y: 20;}LineTo {x: 90;y: 10;}stroke: selected ? Palette.accent-background : Palette.foreground;stroke-width: 1px;}}
}
代码解释
从上述指令可以看出,这里使用的绘图指令还是比较简单的,基本就是画直线,画圆弧:
- MoveTo:移动到某个点,并按下画笔
- LineTo:从当前位置绘制直线到目标位置,并松开画笔
- ArcTo:绘制圆弧,参数比较多,可以参考文档
- radius-x:X轴半径
- radius-y:Y轴半径
- sweep:是否为顺时针方向
- x, y:目标位置
- x-rotation:椭圆的X轴旋转角
上面.slint代码中,有一些关于属性<property>
和组件回调函数<callback>
相关的东西:
- 属性:可以是组件的外观参数(长,宽等),或者指示组件的内部状态,
in
修饰符代表只能由外部传入(只写),out
修饰符代表只能从内部传出(只读),in-out
修饰符代表可读可写 - 回调函数:指定用户点击/拖动…该组件时,要执行的任务