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

【Rust GUI开发入门】编写一个本地音乐播放器(6. 制作播放控制面板)

原创
作者头像
用户11855011
发布2025-09-30 17:09:02
发布2025-09-30 17:09:02
950
举报

目的是要制作一个这样的音乐播放控制面板:

control-panel.png
control-panel.png

水平布局显示:

  • 专辑封面
  • 当前播放歌曲-歌手
  • 顺序播放模式按钮
  • 上一曲按钮
  • 播放/暂停按钮
  • 下一曲按钮
  • 随机播放模式按钮
  • 播放进度/总进度

几个按钮前几篇文章已经绘制过了,直接给出该组件代码:

代码语言:slint
复制
export component ControlPanel inherits Window {
    in-out property <float> progress;
    in property <float> duration;
    in property <bool> paused;
    in property <PlayMode> play_mode;
    in-out property <bool> dragging;
    in property <SongInfo> current_song;
    in property <image> album_image;
    callback change-progress(float);
    callback toggle-play();
    callback play-next();
    callback play-prev();
    callback switch-mode(PlayMode);
    callback double-clicked();
    pure callback format-duration(float) -> string;
    VerticalLayout {
        width: 100%;
        height: 100%;
        Slider {
            minimum: 0;
            maximum: root.duration;
            // 单向绑定被用户强制改变状态后,绑定失效,不再自动更新, 所以双向绑定
            value <=> root.progress;
            released(v) => {
                root.dragging = false;
                change_progress(v);
            }
            changed(_) => {
                root.dragging = true;
            }
        }

        HorizontalLayout {
            height: 80%;
            alignment: center;
            TouchArea {
                width: 35%;
                height: 100%;
                double-clicked => {
                    root.double-clicked()
                };
                HorizontalLayout {
                    alignment: start;
                    spacing: 3px;
                    Rectangle {
                        width: 20%;
                        height: 100%;
                        Rectangle {
                            width: 40px;
                            height: 40px;
                            border-radius: 4px;
                            clip: true;
                            drop-shadow-blur: 6px;
                            Image {
                                y: parent.height / 2 - self.height / 2;
                                width: 100%;
                                height: 100%;
                                source: root.album_image;
                                image-fit: contain;
                            }
                        }
                    }

                    Rectangle {
                        width: 80%;
                        height: 100%;
                        Text {
                            width: 100%;
                            x: parent.width / 2 - self.width / 2;
                            text: root.current_song.song_name + " - " + root.current_song.singer;
                            vertical-alignment: center;
                            horizontal-alignment: center;
                            overflow: TextOverflow.elide;
                        }
                    }
                }
            }

            Rectangle {
                width: 6%;
                OverlapButton {
                    x: parent.width / 2 - self.width / 2;
                    y: parent.height / 2 - self.height / 2;
                    width: 20px;
                    height: 20px;
                    mode: root.play_mode;
                    clicked => {
                        if (root.play_mode == PlayMode.InOrder) {
                            root.switch_mode(PlayMode.Recursive);
                        } else {
                            root.switch_mode(PlayMode.InOrder);
                        }
                    }
                }
            }

            Rectangle {
                width: 6%;
                PrevSongButton {
                    x: parent.width / 2 - self.width / 2;
                    y: parent.height / 2 - self.height / 2;
                    width: 20px;
                    height: 20px;
                    clicked => {
                        root.play_prev();
                    }
                }
            }

            Rectangle {
                width: 6%;
                PlayPauseButton {
                    x: parent.width / 2 - self.width / 2;
                    y: parent.height / 2 - self.height / 2;
                    width: 20px;
                    height: 20px;
                    // 单向绑定被用户强制改变状态后,绑定失效,不再自动更新, 所以双向绑定
                    paused: root.paused;
                    toggled => {
                        root.toggle_play();
                    }
                }
            }

            Rectangle {
                width: 6%;
                NextSongButton {
                    x: parent.width / 2 - self.width / 2;
                    y: parent.height / 2 - self.height / 2;
                    width: 20px;
                    height: 20px;
                    clicked => {
                        root.play_next();
                    }
                }
            }

            Rectangle {
                width: 6%;
                border-color: transparent;
                RandomButton {
                    x: parent.width / 2 - self.width / 2;
                    y: parent.height / 2 - self.height / 2;
                    selected: root.play_mode == PlayMode.Random;
                    width: 20px;
                    height: 20px;
                    clicked => {
                        root.switch_mode(PlayMode.Random);
                    }
                }
            }

            Rectangle {
                width: 35%;
                TouchArea {
                    double-clicked => {
                        root.double-clicked()
                    }
                }

                Text {
                    x: parent.width / 2 - self.width / 2;
                    text: root.format-duration(root.progress) + " / " + root.format-duration(root.duration);
                    vertical-alignment: center;
                    horizontal-alignment: center;
                }
            }
        }
    }
}

代码解释

实现思路很直接,点击每个按钮会触发一个回调函数,对应一个功能。

其中,进度条拖动来改变播放进度的实现比较特殊,因为进度条一方面是根据音乐播放进度从后台用定时器定时更新的,另一方面又要支持用户手动拖动来改变播放进度,为了防止用户拖动时被后台定时更新覆盖,这里使用一个额外变量dragging来记录用户是否在拖动,设置当后台检测到draggingtrue时,不再更新进度条。

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

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 代码解释
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档