【Rust GUI开发入门】编写一个本地音乐播放器(4. 绘制按钮组件)

本系列教程对应的代码已开源在 Github zeedle

开始介绍播放器UI的构建部分,但是不会详细讲解Slint UI的设计基础,没有意义,因为官方文档介绍的已经十分详细了,一些基本用法需要借助参考文档熟悉。

Slint UI支持使用类SVG指令绘制矢量图标,为了保证UI的风格统一性,这里不使用网络上的图标,直接使用Path指令绘制,具体语法参考Path | Slint Docs

需要绘制的图标如下:

  • 播放/暂停
  • 上一曲
  • 下一曲
  • 播放模式

直接给出.slint代码:

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修饰符代表可读可写
  • 回调函数:指定用户点击/拖动...该组件时,要执行的任务
相关推荐
朝阳5818 小时前
使用过程宏实现自动化新增功能
后端·rust
JordanHaidee9 小时前
【Rust GUI开发入门】编写一个本地音乐播放器(8. 从文件中提取歌曲元信息)
rust
清心91512 小时前
Windows系统Rust安装与配置,解决安装慢问题
rust
清心91512 小时前
Windows系统Rust安装,自定义安装目录
rust
恒云客15 小时前
Rust开发环境配置
开发语言·后端·rust
吃饺子不吃馅1 天前
AntV X6图编辑器如何实现切换主题
前端·svg·图形学
红烧code1 天前
【Rust GUI开发入门】编写一个本地音乐播放器(1. 主要技术选型&架构设计)
rust·gui·slint·rodio·lofty
JordanHaidee1 天前
【Rust GUI开发入门】编写一个本地音乐播放器(3. UI与后台线程通信)
rust
Source.Liu2 天前
【mdBook】1 安装
笔记·rust·markdown