首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【Rust GUI开发入门】编写一个本地音乐播放器(5. 制作音乐列表组件)

【Rust GUI开发入门】编写一个本地音乐播放器(5. 制作音乐列表组件)

原创
作者头像
用户11855011
发布2025-09-30 16:28:47
发布2025-09-30 16:28:47
590
举报

目的是要做一个这样的音乐列表组件:

songlist-view.png
songlist-view.png

包含:

  • 一个标题栏
  • 多个列表项

制作标题栏

需要在标题栏上显示排序图标,实现点击排序功能,因此额外需要定义一个枚举类型SortKey指示排序依据。这里的排序图标也手动绘制,不使用网络上的图标:

代码语言:slint
复制
@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回调函数:

代码语言:slint
复制
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

代码语言:slint
复制
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);
                }
            }
        }
    }
}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 制作标题栏
  • 制作列表项
  • 把二者组合起来
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档