Rust winit 0.30.0版本简介

news/2024/9/22 1:12:08

不久前,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,十分感谢。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/31545.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

4-常用标签

标题标签<h1> - <h6>为了使网页更具有语义化,我们经常会在页面中用到标题标签。HTML提供了6个等级的网页标题加了标题的文字会变的加粗,字号也会一次变大。 一个标题独占一行段落和换行标签在网页中,要把文字有条理地显示出来,就需要将这些文字分段显示。在HTM…

智慧安防系统:构建更安全的社区环境

随着科技的不断进步,人们的生活质量得到了显著提高。然而,与此同时,社会治安问题也日益凸显。为了维护社会的和谐稳定,提高人们的生活安全感,智慧安防系统应运而生。本文将为您详细介绍智慧安防系统的项目背景、需求分析、建设目标、建设内容、技术方案、安全设计以及结语…

桌面图标间距Bug:Win10/Win11桌面图标占用空间变成长方形怎么办?

在使用Windows 10或Windows 11操作系统时,桌面图标的间距突然变得很大,变成了长方形。阅读全文:https://itxiaozhang.com/win10-win11-desktop-icon-bug-rectangular-fix/ 此教程配合视频学习效果最佳,视频教程在文章末尾。问题描述 在使用Windows 10或Windows 11操作系统时…

量化交易:财务选股RSRS择时的策略

哈喽,大家好,我是木头左!引言 本文将介绍一种结合财务指标选股和RSRS(Risk-Adjusted Return to Strength Ratio)择时的策略,旨在帮助投资者在复杂的市场环境中做出更明智的决策。 感兴趣的朋友,可以在下方公号内回复:001,即可获取源码,共同交流! 策略原理 财务指标选…

流畅的python--第三章

字典 示例3-1-1字典推导式示例3-1-2映射拆包 在调用函数时,不止一个参数可以使用**。但是所有键都要是字符串,而且在所有参数中,是唯一的(因为关键字参数不可重复)。示例3-1-3使用|合并映射 合并映射遵循重复键情况下后键覆盖前键的规律。可哈希 如果一个对象的哈希码在整…

CodeGeeX 智能编程助手 6 项功能升级,在Visual Studio插件市场霸榜2周!

CodeGeeX是支持适配Visual Studio 2019的唯一一款国产智能辅助编程工具,已经在Visual Studio趋势上霸榜2周! CodeGeeX v1.0.4版本上线Visual Studio插件市场,带来了多项新功能和性能优化,提升编程体验。 新功能亮点速览: 一、侧边栏工具箱功能 v1.0.4版本中,CodeGeeX新增…

俄罗斯方块

原题链接 题解从小正方形到大正方形,有四个变化方向,分别是左上、右上、右下、左上。 分类讨论模拟即可 code #include<bits/stdc++.h> using namespace std; int main() {int n,x,y;cin>>n>>x>>y;puts("Yes");int flag=1;if(x==1){if(y==…