首页 > 其他分享 >Rust winit 0.30.0版本简介

Rust winit 0.30.0版本简介

时间:2024-05-14 19:53:30浏览次数:24  
标签:App 0.30 window winit 窗体 event Rust

不久前,Rust著名的跨平台窗体管理库winit发布了它的0.30.0版本,较之前的0.2x.x版本,新增了19个的模块API,改动大约19个模块API,移除了大约8个模块API。可见本次升级改动之大,主要是对事件循环、窗口管理的重构。鉴于目前网上较多的文章都是基于0.2x版本的winit的代码,存在时效性问题,所以我决定写一篇文章,对winit的0.30.0版本做一个简单的介绍,同时也为后面的Rust Wgpu系列文章做铺垫。

关于0.2x版本winit

为了呈现清晰的对比,我们先给一关于0.2x版本的winit编写一个应用程序,运行并展示一个窗口:

010-v0_2x_winit

0.2x版本的winit的运行模型主要基于过程式:

  1. 创建事件循环
  2. 创建该事件循环关联的窗体
  3. 启动事件循环

尽管使用起来比较简单,但是实际的应用场景会比较复杂,考虑到多窗体情况,这块的代码会愈发的复杂,需要用户做出适当的封装,才能让代码更加的清晰。

关于0.30.0版winit

关于0.30.0版本的winit,则新增ApplicationHandler,来对整个应用程序进行抽象,并把窗体创建、事件处理,收敛到了应用程序这个抽象中,提供更加直观的API。具体是怎样呢?话不多说,让我们通过代码实践来理解。首先初始化一个项目(这里不再赘述,请读者自行创建基础空项目),添加0.30.0版本winit依赖:

[dependencies]
winit = { version = "0.30.0" }

接着,为了后续项目结构的划分,我们在main.rs同级目录下创建一个名为app.rs,内容如下:

use winit::application::ApplicationHandler;
use winit::event::WindowEvent;
use winit::event_loop::ActiveEventLoop;
use winit::window::WindowId;

pub struct App {}

impl ApplicationHandler for App {
    fn resumed(&mut self, event_loop: &ActiveEventLoop) {}

    fn window_event(&mut self, event_loop: &ActiveEventLoop, window_id: WindowId, event: WindowEvent) {}
}

在新版的winit中,我们首先定义一个自定义结构体:App,它代表了我们运行的应用程序;接着,我们为App实现来自winit 0.30.0中的新trait:ApplicationHandler。该trait有两个必须实现的方法:resumedwindow_event方法。

先看window_event方法。该在窗口事件发生时被调用,这块其实就是0.2x版本中事件循环中的触发事件的封装。但值得注意的是,在该方法的2个入参:

  1. event_loop: &ActiveEventLoop
  2. window_id: WindowId

这两个参数从含义上讲,代表了当前正激活的事件循环以及与之匹配的窗口。这里就不难理解,winit的0.30.0的新模型,主要是为了以友好的接口方式来支持多窗体、多事件循环。我们可以通过该事件回调,来得到当前是哪个窗体触发,在哪个激活的事件循环中触发的窗体事件。

再看resumed方法。官方文档:ApplicationHandler#resumed,笔者简单总结下:

  1. 所有的平台(桌面端、Android、iOS以及Web)都会 Resumed 事件,各个平台触发该事件是对应了相关平台应用生命周期的某个阶段(例如,iOS中对应applicationDidBecomeActive)。
  2. 考虑多平台可以移植性,推荐建议应用程序在收到第一个 Resumed 事件后仅初始化其图形上下文并创建窗口。由于系统平台的事件驱动具体实现的差异,可能会调用多次,要做“幂等”处理,确保在收到 Resumed 事件后仅初始化一次图形上下文和窗口(比如,iOS上只要激活了就会触发一次,如果没做幂等处理,就会在每次激活时都初始化一次图形上下文和窗口)。

鉴于上述说明,我们在App结构体中增加一个字段:window: Option<winit::window::Window>,稍后我们会在resumed方法中创建窗口,并把它存储在这个字段中,同时给App加上Default特性以便于快速创建App实例:

+ // 添加 Default 以便App::default()来快速创建App实例
+ #[derive(Default)]
pub struct App {
+   window: Option<winit::window::Window>,
}

接着,我们在resumed方法中创建窗口,并把它存储在window字段中:

impl ApplicationHandler for App {
    fn resumed(&mut self, event_loop: &ActiveEventLoop) {
        // 如果窗口为创建,我们就创建一个窗口
        if self.window.is_none() {
            let win_attr = Window::default_attributes().with_title("demo");
            let window = event_loop.create_window(win_attr).unwrap();
            self.window = Some(window);
        }
    }
    fn window_event(&mut self, event_loop: &ActiveEventLoop, window_id: WindowId, event: WindowEvent) {}
}

上述的代码,通过判断self.window.is_none(),我们可以避免重复创建窗口。

至此,我们的app.rs中的代码就编写完毕了。接下来,我们需要在main.rs中增加创建App以及运行该应用的代码:

use winit::event_loop::EventLoop;
use crate::app::App;

mod app;

fn main() {
    let event_loop = EventLoop::new().unwrap();
    let mut app = App::default();
    event_loop.run_app(&mut app).expect("run app error.");
}

其实,读者可以感受到,新版本的winit下的应用程序运行模型,更加好进行模块的划分了。通过ApplicationHandler,我们将整个应用程序的生命周期抽象出来,并通过事件回调的方式,来处理窗体事件。

上述代码运行以后,会在桌面出现一个窗体,不过此时你还无法点击窗体关闭按钮关闭它。因为我们没有实现对应的窗体退出逻辑,让我们在前面的ApplicationHandlerwindow_event方法中,处理下退出事件:

impl ApplicationHandler for App {
    fn resumed(&mut self, event_loop: &ActiveEventLoop) {
        // ...
    }

    fn window_event(&mut self, event_loop: &ActiveEventLoop, window_id: WindowId, event: WindowEvent) {
        match event {
            // 处理退出
            WindowEvent::CloseRequested => {
                event_loop.exit();
            }
            _ => ()
        }
    }
}

至此,我们就能显示一个空白的窗体,并能通过点击关闭按钮关闭它。当然,有读者在macOS关闭窗体时,会出现如下panic:

a delegate was not configured on the application
stack backtrace:
   0: rust_begin_unwind
   ... ...

这是0.30.0的BUG,具体可以参考issue,该问题会在0.30.1版本修复。

写在最后

在本文中,笔者对winit的0.30.0版本的主要变动进行简单的介绍,更多的内容还需要读者自行阅读官方文档以及examples。当然,相信通过本篇文章,不难看出,新版的winit,对其运行模型架构进行了重构,使得其更加易于使用,更符合现代GUI框架的运行模型思路。

但是,由于其架构升级,导致一些现阶段网络上一些经典的文章,可能无法在新版的winit下正确运行,例如《学习 Wgpu》就还是使用的0.29版本。笔者后续会开启关于Rust Wgpu系列文章,会使用新版winit来进行项目的搭建,并且讲解其中一些在新版winit下的Wgpu构建的注意点,敬请期待。

本文完整代码就不单独放库了,主要是概念讲解。读者可以直接参考官方文档的简单例子

PS:笔者虽然还没有编写Rust Wgpu系列文章,但其基于winit 0.30.0版本的example已经在开发编写中了,笔者可以在这个仓库中checkout代码:w4ngzhen/wgpu_winit_example,也欢迎读者给个star,十分感谢。

标签:App,0.30,window,winit,窗体,event,Rust
From: https://www.cnblogs.com/w4ngzhen/p/18192117

相关文章

  • Rust数据驱动-参数化测试
    需求假如有以下测试用例,同样的逻辑,我们需要测试多组数据。modtests{#[test]fntest_add(){leta=1;letb=2;assert_eq!(a+b,3);}}一般情况下我们不在用例中使用for循环(即subtests子测试模式)来验证多组数据。modtest......
  • OSS_PIPE:Rust编写的大规模文件迁移工具| 京东云技术团队
    文盘rust好久没有更新了。这段时间笔者用rust写了个小东西,跟各位分享一下背景随着业务的发展,文件数量和文件大小会急剧增加,文件迁移的数量和难度不断攀升。oss_pipe是rust编写的文件迁移工具,旨在支撑大规模的文件迁移场景。编写oss_pipe的初衷•同类产品面临的问题•rust......
  • 2024年,Rust和Go学习哪个更有优势
    在编程语言的世界里,技术的更新迭代速度一直都是非常快的。而在2024年这个特殊的年份,Rust和Go这两门编程语言备受关注,成为了许多程序员学习的焦点。那么,在这两者之间,到底该选择学习哪个,才能更具优势呢?本文将从各个方面分析比较Rust和Go,帮助读者更好地做出决策。1.语言特性比较Ru......
  • RustDesk 自建服务器部署和使用教程
    RustDesk是一个强大的开源远程桌面软件,是中国开发者的作品,它使用Rust编程语言构建,提供安全、高效、跨平台的远程访问体验。可以说是目前全球最火的开源远程桌面软件了,GitHub星星数量达到了惊人的64k!与TeamViewer、ToDesk等专有远程访问解决方案相比,RustDesk作为一个开源......
  • Rust工作空间(workspace)实践
    本文将介绍如何使用cargoworkspace来管理多个package,并通过实践介绍workspace的一些基础场景下的使用、配置方式。在rust中编写某些中小型项目时,我们通常不会将一个工程拆分为多个package,而是通过一个package下不同的目录模块来实现模块拆分,尽管大部分场景下这种开发方式已经足......
  • Rust 错误处理
    rust处理错误,不使用trycatch,而是使用Result<T,E>。简单的处理rust错误在各种关于rust错误处理的文档中,为了解释清楚其背后的机制,看着内容很多,不好理解。比如我们写一个方法,读取文件内容:fnread_file_to_string(file_path:String)->Result<String,io::Error>{le......
  • Rust | 实现 API 限速操作 Example
    在这篇文章中,我们将讨论如何在Rust中实现API限速。当涉及到生产中的服务时,是为了确保不良行为者不会滥用API——这就是API限速的作用所在。我们将实现“滑动窗口”算法,通过动态周期来检查请求历史,并使用基本的内存hashmap来存储用户IP及其请求时间。我们还将研究如......
  • rust搭建交叉编译环境
    最近尝试了一下rust交叉编译,简单记录一下。原理1、使用rust的编译器将rust源码编译到汇编或者.o的状态(具体是汇编还是.o没有考证过)。2、使用目标平台的toolchain将rust生成的汇编或者.o链接成ELF等可执行的格式。基于上述原理,需要解决两个问题:首先,怎么让rust将rust代......
  • rust trait 关联类型和泛型的区别
    关联类型和泛型虽然在某些方面看起来相似,但它们在Rust中扮演着不同的角色,有着本质的区别。下面我会详细解释关联类型、泛型以及它们在Iteratortrait上的应用,以帮助理解为什么Iteratortrait使用关联类型而非泛型参数来定义。关联类型关联类型是trait的一部分,它允许trait......
  • Trusted Types API
    TrustedTypesAPI:锁定DOMAPI的不安全部分,以防止客户端跨站脚本(XSS)攻击untrusted<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"/><metaname="viewport"content="width=device-width......