Rust Slint库达成桌面萌宠源码分享(包含拖动、右键菜单效果)
Rust Slint库实现桌面萌宠源码分享(包含拖动、右键菜单功能)
一、效果展示
1、效果展示

2、源码分享
2.1、工程结构

2.2、main.slint
import { AboutSlint, VerticalBox, LineEdit, HorizontalBox, Button, GroupBox, GridBox,
ComboBox, Spinner, Slider, ListView, Palette, ProgressIndicator, CheckBox, Switch } from "std-widgets.slint";
import { DataAdapter,Theme } from "models.slint";
export { DataAdapter ,Theme}
export component MainWindow inherits Window {
width: 100px;
height: 150px;
always-on-top: true;
no-frame: true;
background: transparent;
private property <Point> pressed_point;
private property <bool> is_pressed: false;
private property <int> image_cnt:0;
private property <[image]> image : [
@image-url("./image/0.png"),
@image-url("./image/1.png"),
@image-url("./image/2.png"),
@image-url("./image/3.png"),
@image-url("./image/4.png"),
@image-url("./image/5.png"),
@image-url("./image/6.png"),
@image-url("./image/7.png"),
@image-url("./image/8.png"),
@image-url("./image/9.png"),
@image-url("./image/10.png"),
@image-url("./image/11.png"),
@image-url("./image/12.png"),
@image-url("./image/13.png"),
@image-url("./image/14.png"),
@image-url("./image/15.png"),
@image-url("./image/16.png"),
@image-url("./image/17.png"),
@image-url("./image/18.png"),
@image-url("./image/19.png"),
@image-url("./image/20.png"),
@image-url("./image/21.png"),
@image-url("./image/22.png"),
@image-url("./image/23.png"),
@image-url("./image/24.png"),
@image-url("./image/25.png"),
@image-url("./image/26.png"),
@image-url("./image/27.png"),
@image-url("./image/28.png"),
@image-url("./image/29.png"),
@image-url("./image/30.png"),
@image-url("./image/31.png"),
@image-url("./image/32.png"),
@image-url("./image/33.png"),
@image-url("./image/34.png"),
@image-url("./image/35.png"),
@image-url("./image/36.png"),
@image-url("./image/37.png"),
@image-url("./image/38.png"),
@image-url("./image/39.png"),
@image-url("./image/40.png"),
@image-url("./image/41.png"),
@image-url("./image/42.png"),
@image-url("./image/43.png"),
@image-url("./image/44.png"),
@image-url("./image/45.png"),
@image-url("./image/46.png"),
@image-url("./image/47.png"),
@image-url("./image/48.png"),
@image-url("./image/49.png"),
];
image := Image {
width: parent.width;
height: parent.height;
source: @image-url("./image/0.png");
image-fit: fill;
image-rendering: smooth;
}
timer := Timer {
interval: 30ms;
running: false;
triggered => {
image.source = root.image[image_cnt];
image_cnt +=1;
if image_cnt >= 49 {
image_cnt = 0;
timer.stop();
}
}
}
TouchArea {
enabled: true;
pointer-event(event) => {
if event.kind == PointerEventKind.move{
if !timer.running {
timer.start();
}
}
if event.kind == PointerEventKind.down && event.button == PointerEventButton.left {
root.is_pressed = true;
root.pressed_point.x = self.mouse-x;
root.pressed_point.y = self.mouse-y
} else if event.kind == PointerEventKind.up && event.button == PointerEventButton.left {
root.is_pressed = false;
}
}
moved => {
if(root.is_pressed){
DataAdapter.position_changed((self.mouse-x - root.pressed_point.x)/1px,(self.mouse-y - root.pressed_point.y)/1px);
}
}
}
ContextMenuArea {
Menu {
MenuItem {
title: @tr("剪切");
activated => { debug("Cut"); }
}
MenuItem {
title: @tr("复制");
activated => { debug("Copy"); }
}
MenuItem {
title: @tr("粘贴");
activated => { debug("Paste"); }
}
MenuSeparator {}
Menu {
title: @tr("查找");
MenuItem {
title: @tr("查找下一个");
}
MenuItem {
title: @tr("查找上一个");
}
}
MenuSeparator {}
MenuItem {
title: @tr("退出");
activated => { debug("quit");
DataAdapter.quit_clicked();
}
}
}
}
}
2.3、models.slint
export enum Theme {
Light,
Dark,
System
}
export global DataAdapter {
in-out property <string> textResValue:0;
callback btn_clicked(string);
callback position_changed(int, int);
callback quit_clicked();
}
2.4、main.rs
use slint::{PlatformError, WindowPosition};
slint::include_modules!();
use slint::{Color,Brush};
use slint::Timer;
fn main() ->Result<(), PlatformError>{
let app: MainWindow = MainWindow::new()?;
let weak: slint::Weak<MainWindow> = app.as_weak();
app.global::<DataAdapter>().on_btn_clicked({
let weak = weak.clone();
move |text|{
if let Some(strong) = weak.upgrade(){
let adapter = strong.global::<DataAdapter>();
}
}
});
app.global::<DataAdapter>().on_position_changed({
let app = app.clone_strong();
move |x:i32,y:i32|{
let win = app.window();
let mut pos = win.position();
pos.x += x;
pos.y += y;
win.set_position(pos);
}
});
app.global::<DataAdapter>().on_quit_clicked({
let app = app.clone_strong();
move ||{
let _ = app.window().hide();
}
});
let _ = app.run();
Ok(())
}
2.5、Cargo.toml
[dependencies]
slint = "1.13.1"
[build-dependencies]
slint-build = "1.13.1"
二、工程搭建及资源文件
1、工程搭建
参考我这篇文章:工程搭建详细教程
2、资源文件
在文章顶部下载
三、实现原理
主要通过Image、Timer、ContextMenuArea三个控件实现。
1、Image控件介绍
Image控件用于在界面中显示图片,支持多种图片格式和加载方式。
1.1、Image控件的基本用法
在Slint中,Image控件可以通过声明式语法或编程方式创建。以下是一个简单的Rust示例:
slint::slint! {
import { VerticalBox, Image } from "std-widgets.slint";
export component MainWindow inherits Window {
VerticalBox {
Image {
source: @image-url("path/to/image.png");
width: 200px;
height: 200px;
}
}
}
}
1.2、支持的图片格式
Slint的Image控件支持常见的图片格式,包括PNG、JPEG、GIF和BMP。图片可以通过文件路径、内存数据或URL加载。
1.3、动态更新图片
Image控件支持动态更新图片源。可以通过绑定到变量或回调函数实现:
slint::slint! {
export component MainWindow inherits Window {
in-out property <string> image-path: "default.png";
Image {
source: @image-url(image-path);
}
}
}
1.4、图片缩放和裁剪
Image控件提供多种缩放和裁剪选项:
fit: 保持宽高比适应控件大小fill: 拉伸填满整个控件stretch: 不保持宽高比拉伸tile: 平铺图片
slint::slint! {
Image {
source: @image-url("image.jpg");
image-fit: fill;
}
}
1.5、性能优化
对于频繁更新的图片,建议:
- 使用适当大小的图片资源
- 考虑缓存机制
- 避免在热路径中频繁加载图片
2、Timer介绍
Timer控件是Slint中用于处理定时任务的核心组件,通常用于执行周期性任务或延迟操作。
2.1、基本用法
在Slint中,Timer控件通常通过Timer结构体或相关接口实现。以下是一个简单的示例代码:
import slint;
export component MainWindow {
in property counter: 0;
callback tick;
Timer {
interval: 1000ms;
running: true;
triggered => {
tick();
counter += 1;
}
}
}
2.2、主要属性
interval
定义定时器触发的时间间隔,支持毫秒(ms)和秒(s)单位。例如1000ms或1s。
running
布尔值属性,控制定时器是否处于活动状态。设置为true时定时器开始运行,false时停止。
2.3、信号处理
定时器触发时会发送triggered信号,可以通过回调函数处理:
triggered() => {
// 处理定时器触发时的逻辑
}
2.4、注意事项
- 定时器的精度取决于底层系统实现,不可用于需要高精度计时的场景
- 在界面不可见时,某些平台可能会限制或暂停定时器的执行
- 过度使用定时器可能影响应用性能
Timer控件是Slint中处理时间相关任务的简单有效方式,适用于UI动画、定期更新等场景。
3、ContextMenuArea介绍
ContextMenuArea 是 slint 库中用于处理上下文菜单(右键菜单)的组件。它允许开发者为 UI 元素绑定自定义的右键菜单逻辑,提供更丰富的交互体验。
3.1、核心功能
- 右键菜单触发:监听鼠标右键点击事件,触发自定义菜单。
- 动态菜单内容:支持根据上下文动态生成菜单项。
- 跨平台兼容:在支持
slint的平台上(如 Windows、macOS、Linux)均可使用。
3.2、基本用法
export component MyComponent {
in-out property text: "Right-click me!";
ContextMenuArea {
Menu {
MenuItem {
title: "Copy";
activated => { }
}
MenuItem {
title: "Paste";
activated => { }
}
}
}
}
3.3、注意事项
- 移动端平台可能需要特殊处理,因为通常没有右键操作
- 菜单层级过深时需考虑用户体验
- 快捷键绑定需与系统快捷键避免冲突


浙公网安备 33010602011771号