目的是要做一个这样的音乐列表组件:
包含:
- 一个标题栏
- 多个列表项
制作标题栏
需要在标题栏上显示排序图标,实现点击排序功能,因此额外需要定义一个枚举类型SortKey
指示排序依据。这里的排序图标也手动绘制,不使用网络上的图标:
@rust-attr(derive(serde::Serialize, serde::Deserialize))
export enum SortKey {BySongName,BySinger,ByDuration,
}component SortIcon inherits Window {in-out property <bool> ascending:true;in-out property <bool> display:true;height: 15px;width: 15px;background: transparent;if !ascending && display: Path {MoveTo {x: 5;y: 1;}LineTo {x: 5;y: 9;}LineTo {x: 7;y: 7;}stroke: gray;stroke-width: 1px;}if ascending && display:Path {MoveTo {x: 5;y: 9;}LineTo {x: 5;y: 1;}LineTo {x: 3;y: 3;}stroke: gray;stroke-width: 1px;}
}component TitleBarItem inherits Window {in property <string> name:"name";in-out property <bool> display-sort-icon <=> arrow.display;in-out property <bool> ascending-sort <=> arrow.ascending;Rectangle {width: 100%;height: 100%;text := Text {text: name;color: dimgray;x: parent.width * 0.4;y: parent.height / 2 - self.height / 2;}arrow := SortIcon {ascending: true;width: 12px;height: 12px;x: text.x + text.width + 5px;y: parent.height / 2 - self.height / 2;}}
}export component TitleBar inherits Window {height: 30px;in-out property <SortKey> key;in-out property <bool> ascending;callback sort-items(SortKey, bool);VerticalLayout {HorizontalLayout {alignment: space-between;area1 := TouchArea {width: 33%;clicked => {sort-items(SortKey.BySongName, ascending);}TitleBarItem {width: 100%;height: 100%;name: @tr("Title");background: area1.has-hover ? Palette.control-background : transparent;display-sort-icon: key == SortKey.BySongName;ascending-sort: ascending;}}area2 := TouchArea {width: 33%;clicked => {sort-items(SortKey.BySinger, ascending);}TitleBarItem {name: @tr("Artist");height: 100%;width: 100%;background: area2.has-hover ? Palette.control-background : transparent;display-sort-icon: key == SortKey.BySinger;ascending-sort: ascending;}}area3 := TouchArea {width: 33%;clicked => {sort-items(SortKey.ByDuration, ascending);}TitleBarItem {name: @tr("Duration");height: 100%;width: 100%;background: area3.has-hover ? Palette.control-background : transparent;display-sort-icon: key == SortKey.ByDuration;ascending-sort: ascending;}}}Path {width: 100%;height: 5px;MoveTo {x: 0;y: 0;}LineTo {x: 100%;y: 0;}stroke: Palette.foreground;stroke-width: 0.3px;}}
}
制作列表项
每个列表项关联一首歌曲,显示其相关信息:
- 歌名
- 歌手
- 时长
因此额外定义了一个SongInfo
结构体。此外,双击该列表项会触发音乐播放,所以对外暴露一个double-clicked
回调函数:
export struct SongInfo {id:int,song_name:string,singer:string,duration:string,song_path:string,
}export component SongItem inherits Window {height: 30px;in property <SongInfo> info:{ id:0, song_name:"xxx", singer:"xxx", duration:"xxx", song_path:"xxx" };callback double_clicked();background: area.has-hover ? Palette.control-background : transparent;VerticalLayout {area := TouchArea {double-clicked => {double_clicked();}HorizontalLayout {alignment: space-between;Rectangle {width: 33%;Text {width: 100%;x: parent.width * 0.4;text: info.song-name;overflow: elide;}}Rectangle {width: 33%;Text {width: 100%;x: parent.width * 0.4;text: info.singer;overflow: elide;}}Rectangle {width: 33%;Text {x: parent.width * 0.4;text: info.duration;}}}}Path {width: 100%;height: 1px;MoveTo {x: 0;y: 0;}LineTo {x: 100%;y: 0;}stroke: Palette.foreground;stroke-width: 0.15px;}}
}
把二者组合起来
使用Slint UI的内置ListView
组件来渲染多个音乐项,然后竖直堆叠标题栏和这个ListView
:
export component SongListView inherits Window {in-out property <bool> ascending;in-out property <SortKey> sort-key;in-out property <SortKey> last-sort-key;in-out property <[SongInfo]> song-list;callback sort-songs(SortKey, bool);callback play-song(SongInfo, TriggerSource);VerticalLayout {width: 100%;height: 100%;TitleBar {ascending: root.ascending;key: root.sort-key;sort-items(key, ascending) => {if (root.last-sort-key == key) {root.sort-songs(key, !ascending);} else {root.sort-songs(key, true)}}}ListView {for item in root.song-list: SongItem {info: item;double_clicked => {root.play-song(item, TriggerSource.ClickItem);}}}}
}