From 18e3f0199cbedf598146bd41d7796e2aba70354e Mon Sep 17 00:00:00 2001 From: Madeline Sparkles Date: Sun, 28 Apr 2024 10:27:28 +0800 Subject: [PATCH] update winit to 0.30 --- crates/bootstrap/Cargo.toml | 2 +- crates/bootstrap/src/lib.rs | 355 ++++++++++++++++++++-------------- crates/yakui-app/Cargo.toml | 2 +- crates/yakui-app/src/lib.rs | 6 +- crates/yakui-winit/Cargo.toml | 2 +- 5 files changed, 218 insertions(+), 149 deletions(-) diff --git a/crates/bootstrap/Cargo.toml b/crates/bootstrap/Cargo.toml index 3745b5cd..f9311194 100644 --- a/crates/bootstrap/Cargo.toml +++ b/crates/bootstrap/Cargo.toml @@ -21,4 +21,4 @@ pollster = "0.3.0" profiling = "1.0.6" tracy-client = { version = "0.15.1", optional = true } wgpu = "0.19.0" -winit = "0.29.2" +winit = "0.30.0" diff --git a/crates/bootstrap/src/lib.rs b/crates/bootstrap/src/lib.rs index 0b188c84..8c83ed36 100644 --- a/crates/bootstrap/src/lib.rs +++ b/crates/bootstrap/src/lib.rs @@ -3,14 +3,15 @@ mod custom_texture; use std::fmt::Write; use std::time::Instant; -use winit::dpi::LogicalSize; -use winit::event::{Event, WindowEvent}; -use winit::event_loop::{ControlFlow, EventLoop}; -use winit::window::WindowBuilder; +use winit::application::ApplicationHandler; +use winit::event::{DeviceEvent, DeviceId, Event, WindowEvent}; +use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop}; +use winit::window::{Window, WindowAttributes, WindowId}; use yakui::font::{Font, FontSettings, Fonts}; use yakui::paint::{Texture, TextureFilter, TextureFormat}; -use yakui::{ManagedTextureId, Rect, TextureId, UVec2, Vec2}; +use yakui::{ManagedTextureId, Rect, TextureId, UVec2, Vec2, Yakui}; +use yakui_app::Graphics; const MONKEY_PNG: &[u8] = include_bytes!("../assets/monkey.png"); const MONKEY_BLURRED_PNG: &[u8] = include_bytes!("../assets/monkey-blurred.png"); @@ -27,14 +28,201 @@ pub struct ExampleState { /// `ManagedTextureId` is a texture owned by yakui. You can create one by /// giving yakui some image data; it'll be uploaded by the renderer. - pub monkey: ManagedTextureId, - pub monkey_blurred: ManagedTextureId, - pub brown_inlay: ManagedTextureId, + pub monkey: Option, + pub monkey_blurred: Option, + pub brown_inlay: Option, /// `TextureId` represents either a managed texture or a texture owned by /// the renderer. This image is generated in `custom_texture.rs` and /// uploaded with wgpu directly. - pub custom: TextureId, + pub custom: Option, + + // Use an `Option` to allow the window to not be available until the + // application is properly running. + window: Option, + app: Option, + attributes: WindowAttributes, + + yak: Yakui, + start: Instant, + body: Option>, +} + +impl ApplicationHandler for ExampleState { + // This is a common indicator that you can create a window. + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + let window = event_loop.create_window(self.attributes.clone()).unwrap(); + window.set_ime_allowed(true); + + let sample_count = get_sample_count(); + + let mut app = pollster::block_on(yakui_app::Graphics::new(&window, sample_count)); + + // By default, yakui_winit will measure the system's scale factor and pass + // it to yakui. + // + // Sometimes, it might be desirable to scale the UI by a different factor, + // like if your game has a "UI scale" option, if you're writing tests, or + // you want to ensure your widgets work at a different scale. + // + // In these examples, setting the YAKUI_FORCE_SCALE environment variable to + // a number will override the automatic scaling. + if let Some(scale) = get_scale_override() { + app.window_mut().set_automatic_scale_factor(false); + self.yak.set_scale_factor(scale); + } + + // In these examples, set YAKUI_INSET to force the UI to be contained within + // a sub-viewport with the given edge inset on all sides. + let inset = get_inset_override(); + if inset.is_some() { + app.window_mut().set_automatic_viewport(false); + } + + // Preload some textures for the examples to use. + self.monkey = Some( + self.yak + .add_texture(load_texture(MONKEY_PNG, TextureFilter::Linear)), + ); + self.monkey_blurred = Some( + self.yak + .add_texture(load_texture(MONKEY_BLURRED_PNG, TextureFilter::Linear)), + ); + self.brown_inlay = Some( + self.yak + .add_texture(load_texture(BROWN_INLAY_PNG, TextureFilter::Nearest)), + ); + self.custom = Some(app.renderer.add_texture( + custom_texture::generate(&app.device, &app.queue), + wgpu::FilterMode::Nearest, + wgpu::FilterMode::Nearest, + wgpu::FilterMode::Nearest, + )); + + // Add a custom font for some of the examples. + let fonts = self.yak.dom().get_global_or_init(Fonts::default); + let font = Font::from_bytes( + include_bytes!("../assets/Hack-Regular.ttf").as_slice(), + FontSettings::default(), + ) + .unwrap(); + + fonts.add(font, Some("monospace")); + + self.app = Some(app); + self.window = Some(window); + } + + fn window_event( + &mut self, + event_loop: &ActiveEventLoop, + window_id: WindowId, + event: WindowEvent, + ) { + // `unwrap` is fine, the window will always be available when + // receiving a window event. + + if self.app.as_mut().unwrap().handle_event( + &mut self.yak, + &Event::<()>::WindowEvent { + window_id, + event: event.clone(), + }, + event_loop, + ) { + return; + } + + // Handle window event. + match event { + WindowEvent::RedrawRequested => { + self.time = (Instant::now() - self.start).as_secs_f32(); + + { + profiling::scope!("Build UI"); + + // Every frame, call yak.start() to begin building the UI for + // this frame. Any yakui widget calls that happen on this thread + // between start() and finish() will be applied to this yakui + // State. + self.yak.start(); + + // Call out to the body of the program, passing in a bit of + // shared state that all the examples can use. + let body = { + let body = self.body.take().unwrap(); + body.run(self); + + body + }; + + self.body = Some(body); + + // Finish building the UI and compute this frame's layout. + self.yak.finish(); + } + + // The example graphics abstraction calls yak.paint() to get + // access to the underlying PaintDom, which holds all the state + // about how to paint widgets. + self.app.as_mut().unwrap().paint(&mut self.yak, { + let bg = yakui::colors::BACKGROUND_1.to_linear(); + wgpu::Color { + r: bg.x.into(), + g: bg.y.into(), + b: bg.z.into(), + a: 1.0, + } + }); + + profiling::finish_frame!(); + } + + WindowEvent::MouseInput { state, button, .. } => { + // This print is a handy way to show which mouse events are + // handled by yakui, and which ones will make it to the + // underlying application. + if button == winit::event::MouseButton::Left { + println!("Left mouse button {state:?}"); + } + } + + WindowEvent::Resized(size) => { + let inset = get_inset_override(); + if let Some(inset) = inset { + let size = Vec2::new(size.width as f32, size.height as f32); + self.yak.set_unscaled_viewport(Rect::from_pos_size( + Vec2::splat(inset), + size - Vec2::splat(inset * 2.0), + )); + } + } + + _ => (), + } + } + + fn device_event( + &mut self, + event_loop: &ActiveEventLoop, + device_id: DeviceId, + event: DeviceEvent, + ) { + // Handle window event. + if self.app.as_mut().unwrap().handle_event( + &mut self.yak, + &Event::<()>::DeviceEvent { device_id, event }, + event_loop, + ) { + return; + } + } + + fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { + if let Some(window) = self.window.as_ref() { + window.request_redraw(); + } + } } pub trait ExampleBody: 'static { @@ -61,10 +249,10 @@ pub fn start(body: impl ExampleBody) { init_logging(); - pollster::block_on(run(body)); + run(body); } -async fn run(body: impl ExampleBody) { +fn run(body: impl ExampleBody) { let mut title = "yakui demo".to_owned(); if let Some(scale) = get_scale_override() { @@ -73,149 +261,30 @@ async fn run(body: impl ExampleBody) { // Normal winit setup for an EventLoop and Window. let event_loop = EventLoop::new().unwrap(); - let window = WindowBuilder::new() - .with_title(title) - .with_inner_size(LogicalSize::new(800.0, 600.0)) - .build(&event_loop) - .unwrap(); - - window.set_ime_allowed(true); - - let sample_count = get_sample_count(); - - // yakui_app has a helper for setting up winit and wgpu. - let mut app = yakui_app::Graphics::new(&window, sample_count).await; + let window_attribute = Window::default_attributes().with_title(title); // Create our yakui state. This is where our UI will be built, laid out, and // calculations for painting will happen. - let mut yak = yakui::Yakui::new(); - - // By default, yakui_winit will measure the system's scale factor and pass - // it to yakui. - // - // Sometimes, it might be desirable to scale the UI by a different factor, - // like if your game has a "UI scale" option, if you're writing tests, or - // you want to ensure your widgets work at a different scale. - // - // In these examples, setting the YAKUI_FORCE_SCALE environment variable to - // a number will override the automatic scaling. - if let Some(scale) = get_scale_override() { - app.window_mut().set_automatic_scale_factor(false); - yak.set_scale_factor(scale); - } - - // In these examples, set YAKUI_INSET to force the UI to be contained within - // a sub-viewport with the given edge inset on all sides. - let inset = get_inset_override(); - if inset.is_some() { - app.window_mut().set_automatic_viewport(false); - } - - // Preload some textures for the examples to use. - let monkey = yak.add_texture(load_texture(MONKEY_PNG, TextureFilter::Linear)); - let monkey_blurred = yak.add_texture(load_texture(MONKEY_BLURRED_PNG, TextureFilter::Linear)); - let brown_inlay = yak.add_texture(load_texture(BROWN_INLAY_PNG, TextureFilter::Nearest)); - let custom = app.renderer.add_texture( - custom_texture::generate(&app.device, &app.queue), - wgpu::FilterMode::Nearest, - wgpu::FilterMode::Nearest, - wgpu::FilterMode::Nearest, - ); - - // Add a custom font for some of the examples. - let fonts = yak.dom().get_global_or_init(Fonts::default); - let font = Font::from_bytes( - include_bytes!("../assets/Hack-Regular.ttf").as_slice(), - FontSettings::default(), - ) - .unwrap(); - - fonts.add(font, Some("monospace")); + let yak = yakui::Yakui::new(); // Set up some default state that we'll modify later. let mut state = ExampleState { time: 0.0, - monkey, - monkey_blurred, - brown_inlay, - custom, + monkey: None, + monkey_blurred: None, + brown_inlay: None, + custom: None, + window: None, + app: None, + attributes: window_attribute, + yak, + start: Instant::now(), + body: Some(Box::new(body)), }; - let start = Instant::now(); - event_loop.set_control_flow(ControlFlow::Poll); - event_loop - .run(move |event, elwt| { - if app.handle_event(&mut yak, &event, elwt) { - return; - } - - match event { - Event::AboutToWait => { - state.time = (Instant::now() - start).as_secs_f32(); - - { - profiling::scope!("Build UI"); - - // Every frame, call yak.start() to begin building the UI for - // this frame. Any yakui widget calls that happen on this thread - // between start() and finish() will be applied to this yakui - // State. - yak.start(); - // Call out to the body of the program, passing in a bit of - // shared state that all the examples can use. - body.run(&mut state); - - // Finish building the UI and compute this frame's layout. - yak.finish(); - } - - // The example graphics abstraction calls yak.paint() to get - // access to the underlying PaintDom, which holds all the state - // about how to paint widgets. - app.paint(&mut yak, { - let bg = yakui::colors::BACKGROUND_1.to_linear(); - wgpu::Color { - r: bg.x.into(), - g: bg.y.into(), - b: bg.z.into(), - a: 1.0, - } - }); - - profiling::finish_frame!(); - } - - Event::WindowEvent { - event: WindowEvent::MouseInput { state, button, .. }, - .. - } => { - // This print is a handy way to show which mouse events are - // handled by yakui, and which ones will make it to the - // underlying application. - if button == winit::event::MouseButton::Left { - println!("Left mouse button {state:?}"); - } - } - - Event::WindowEvent { - event: WindowEvent::Resized(size), - .. - } => { - if let Some(inset) = inset { - let size = Vec2::new(size.width as f32, size.height as f32); - yak.set_unscaled_viewport(Rect::from_pos_size( - Vec2::splat(inset), - size - Vec2::splat(inset * 2.0), - )); - } - } - - _ => (), - } - }) - .unwrap(); + event_loop.run_app(&mut state).unwrap(); } /// This function takes some bytes and turns it into a yakui `Texture` object so diff --git a/crates/yakui-app/Cargo.toml b/crates/yakui-app/Cargo.toml index 9ff1696d..80200ce3 100644 --- a/crates/yakui-app/Cargo.toml +++ b/crates/yakui-app/Cargo.toml @@ -18,4 +18,4 @@ yakui-wgpu = { path = "../yakui-wgpu" } profiling = { version = "1.0.6", optional = true } wgpu = "0.19.0" -winit = { version = "0.29.2", features = ["rwh_05"] } +winit = { version = "0.30.0" } diff --git a/crates/yakui-app/src/lib.rs b/crates/yakui-app/src/lib.rs index 24a68664..7ffda564 100644 --- a/crates/yakui-app/src/lib.rs +++ b/crates/yakui-app/src/lib.rs @@ -3,7 +3,7 @@ mod multisampling; use winit::{ dpi::PhysicalSize, event::{Event, StartCause, WindowEvent}, - event_loop::EventLoopWindowTarget, + event_loop::ActiveEventLoop, window::Window, }; @@ -188,7 +188,7 @@ impl Graphics { &mut self, yak: &mut yakui::Yakui, event: &Event, - elwt: &EventLoopWindowTarget, + event_loop: &ActiveEventLoop, ) -> bool { // yakui_winit will return whether it handled an event. This means that // yakui believes it should handle that event exclusively, like if a @@ -202,7 +202,7 @@ impl Graphics { event: WindowEvent::CloseRequested, .. } => { - elwt.exit(); + event_loop.exit(); } Event::NewEvents(cause) => { diff --git a/crates/yakui-winit/Cargo.toml b/crates/yakui-winit/Cargo.toml index 3103cc08..fc707131 100644 --- a/crates/yakui-winit/Cargo.toml +++ b/crates/yakui-winit/Cargo.toml @@ -12,4 +12,4 @@ edition = "2021" yakui-core = { path = "../yakui-core", version = "0.2.0" } # TODO: Disable all default features once supported (https://github.com/rust-windowing/winit/issues/3174) -winit = { version = "0.29.2", default-features = false, features = ["x11"] } +winit = { version = "0.30.0", default-features = false, features = ["x11"] }