当前位置: 首页 > news >正文

Rust Slint库达成桌面萌宠源码分享(包含拖动、右键菜单效果)

Rust Slint库实现桌面萌宠源码分享(包含拖动、右键菜单功能)

  • 一、效果展示
    • 1、效果展示
    • 2、源码分享
      • 2.1、工程结构
      • 2.2、main.slint
      • 2.3、models.slint
      • 2.4、main.rs
      • 2.5、Cargo.toml
  • 二、工程搭建及资源文件
    • 1、工程搭建
    • 2、资源文件
  • 三、实现原理
    • 1、Image控件介绍
      • 1.1、Image控件的基本用法
      • 1.2、支持的图片格式
      • 1.3、动态更新图片
      • 1.4、图片缩放和裁剪
      • 1.5、性能优化
    • 2、Timer介绍
      • 2.1、基本用法
      • 2.2、主要属性
      • 2.3、信号处理
      • 2.4、注意事项
    • 3、ContextMenuArea介绍
      • 3.1、核心功能
      • 3.2、基本用法
      • 3.3、注意事项

一、效果展示

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、资源文件

在文章顶部下载

三、实现原理

主要通过ImageTimerContextMenuArea三个控件实现。

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)单位。例如1000ms1s

running
布尔值属性,控制定时器是否处于活动状态。设置为true时定时器开始运行,false时停止。

2.3、信号处理

定时器触发时会发送triggered信号,可以通过回调函数处理:

triggered()  => {// 处理定时器触发时的逻辑
}

2.4、注意事项

  1. 定时器的精度取决于底层系统实现,不可用于需要高精度计时的场景
  2. 在界面不可见时,某些平台可能会限制或暂停定时器的执行
  3. 过度使用定时器可能影响应用性能

Timer控件是Slint中处理时间相关任务的简单有效方式,适用于UI动画、定期更新等场景。

3、ContextMenuArea介绍

ContextMenuAreaslint 库中用于处理上下文菜单(右键菜单)的组件。它允许开发者为 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、注意事项

  1. 移动端平台可能需要特殊处理,因为通常没有右键操作
  2. 菜单层级过深时需考虑用户体验
  3. 快捷键绑定需与系统快捷键避免冲突

在这里插入图片描述

http://www.jsqmd.com/news/7528/

相关文章:

  • Redis 持久化机制 - 教程
  • 2025染井吉野樱公司 TOP 种植服务推荐排行榜,染井吉野樱花苗,五公分染井吉野樱,十公分染井吉野樱,染井吉野樱批发,染井吉野樱基地,染井吉野樱花树公司推荐
  • glazewm_windows平铺窗口管理器使用方法
  • 详细介绍:LeetCode热题100(1-7)
  • 树莓派搭建NAS之三:使用OpenList挂载网盘
  • sg-ss 逆向分析
  • PySide6 之登录界面设计
  • Jupyter notebook 虚拟环境(服务)EnvironmentLocationNotFound: Not a conda environment
  • 本地部署overleaf服务帮助latex论文编写 —— 操作笔记
  • 【Groovy】Array、List、Set、Map简介
  • 深入解析:【Python高级语法与正则表达式】
  • 20届-测试面经-华为OD - 指南
  • 【大信息技术】ClickHouse配置详细解读
  • 做题记录4
  • lucene 8.7.0 版本中的倒排索引、数字、DocValues三种类型的查询性能对比 - 教程
  • 数哈多应用授权系统如何为Go语言编程开发者给予知识产权保护?
  • 好听的音乐地址
  • display ip routing-table故障判断及题目 - 详解
  • 完整教程:华为eNSP环境安装和命令使用教程
  • 分布式架构初识:为什么需要分布式 - 教程
  • [IOI 1998 / USACO2.2] 派对灯 Party Lamps 题解 + bitset浅谈
  • 解题报告-小 A 的树
  • 【React 状态管理深度解析:Object.is()、Hook 机制与 Vue 对比实践指南】 - 教程
  • 2025 --【J+S 二十连测】-- 第一套 总结
  • 【实验报告】华东理工大学随机信号处理实验报告 - 详解
  • 页面置换算法
  • Docker部署配置全流程(超详细——Windows和Linux) - 指南
  • AT_abc309_g [ABC309G] Ban Permutation
  • 在Mac上运行Windows 365的完整指南
  • 完整教程:华为海思正式进入Wi-Fi FEM赛道?