【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修饰符代表可读可写
  • 回调函数:指定用户点击/拖动...该组件时,要执行的任务
相关推荐
干饭比赛第一名获得者12 小时前
🚀 终极指南:Mac M4 编译 Rust 至 Linux (AMD64)
后端·rust
未来之窗软件服务13 小时前
幽冥大陆(三十六)S18酒店门锁SDK rust语言——东方仙盟筑基期
开发语言·c++·rust·智能门锁·东方仙盟sdk·东方仙盟一体化
ALex_zry14 小时前
C语言底层编程与Rust的现代演进:内存管理、系统调用与零成本抽象
c语言·算法·rust
ALex_zry15 小时前
内核开发者的视角:C与Rust在系统编程中的哲学与实践
c语言·开发语言·rust
u***451615 小时前
Windows安装Rust环境(详细教程)
开发语言·windows·rust
星释16 小时前
Rust 练习册 101:字符串序列切片的艺术
开发语言·后端·rust
Source.Liu17 小时前
【Chrono库】Android和OpenHarmony系统绑定(src/offset/local/tz_data.rs)
rust·time
S***q1921 天前
Rust在系统工具中的内存安全给代码上了三道保险锁。但正是这种“编译期的严苛”,换来了运行时的安心。比如这段代码:
开发语言·后端·rust
T***u3331 天前
Rust在Web中的 Web框架
开发语言·后端·rust
Q***f6351 天前
Rust在嵌入式中的功耗优化
开发语言·后端·rust