diff --git a/.cargo/config b/.cargo/config.toml similarity index 100% rename from .cargo/config rename to .cargo/config.toml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b5109c40e..66ff752eb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,13 +4,12 @@ on: push: branches: - main - schedule: - - cron: "0 0 * * 1" jobs: rustfmt: name: Check rustfmt runs-on: ubuntu-latest + if: github.ref == 'refs/heads/main' steps: - name: Checkout uses: actions/checkout@v2 @@ -19,11 +18,7 @@ jobs: clippy: name: Run linter runs-on: ubuntu-latest - - strategy: - matrix: - rust: ["1.64"] - + if: github.ref == 'refs/heads/main' env: RUSTFLAGS: -D warnings RUSTDOCFLAGS: -D warnings @@ -33,7 +28,6 @@ jobs: - uses: hecrj/setup-rust-action@v1 with: components: clippy - rust-version: ${{ matrix.rust }} - name: Cache cargo directories uses: actions/cache@v2 with: @@ -80,8 +74,7 @@ jobs: fail-fast: false matrix: rust: - - stable - - "1.64" + - "1.70" os: - ubuntu-latest - macos-latest diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown index 10710b654..7f611458a 100644 --- a/CHANGELOG.markdown +++ b/CHANGELOG.markdown @@ -1,5 +1,17 @@ # Change Log +## [0.12.0] - WIP + +- Updated many dependencies - in particular the new winit release is a bit change. + - winit to 0.29 ([PR](https://github.com/imgui-rs/imgui-rs/pull/754) + - glium to 0.34 and glow to 0.13 ([PR](https://github.com/imgui-rs/imgui-rs/pull/774)) + - memset to 0.9 ([PR](https://github.com/imgui-rs/imgui-rs/pull/726)) +- Fix some unsaftey issue in the handling of text - [PR #746](https://github.com/imgui-rs/imgui-rs/pull/746) +- Added `Io::add_key_analog_event` for supporting analog key events such as gamepad sticks - [PR #728](https://github.com/imgui-rs/imgui-rs/pull/728) +- Added `begin_popup_context_*` methods - [PR #738](https://github.com/imgui-rs/imgui-rs/pull/738) +- Use correct alpha blending function ([problem described here](https://github.com/imgui-rs/imgui-rs/issues/733) - [PR #756](https://github.com/imgui-rs/imgui-rs/pull/756) +- Minimum supported version of Rust (MSRV) is now 1.70 - [PR #747](https://github.com/imgui-rs/imgui-rs/pull/747) + ## [0.11.0] - 2023-04-05 - Added API to add callbacks to draw list for advanced custom drawing - [PR#702](https://github.com/imgui-rs/imgui-rs/pull/702) diff --git a/Cargo.toml b/Cargo.toml index c0c9f5c09..f0438d14a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,3 +10,6 @@ members = [ "imgui-examples", "xtask", ] + +package.rust-version = "1.70" +resolver = "2" diff --git a/README.markdown b/README.markdown index 13a55ea5f..cd2ce9eef 100644 --- a/README.markdown +++ b/README.markdown @@ -60,7 +60,7 @@ be applicable to usage with any backend/renderer. ## Minimum Support Rust Version (MSRV) -The MSRV for `imgui-rs` and all of the backend crates is **1.64**. We update our MSRV periodically, and issue a minor bump for it. +The MSRV for `imgui-rs` and all of the backend crates is **1.70**. We update our MSRV periodically, and issue a minor bump for it. ## Choosing a backend platform and a renderer diff --git a/docs/development-process.md b/docs/development-process.md index eb7d4e02a..4801374b7 100644 --- a/docs/development-process.md +++ b/docs/development-process.md @@ -29,6 +29,7 @@ When it is time to make a new release, we create a `x.y-stable` branch (e.g `0.9 cargo publish -p imgui-glium-renderer cargo publish -p imgui-glow-renderer cargo publish -p imgui-sdl2-support + cargo publish -p imgui-winit-glow-renderer-viewports ``` 6. Create annotated tag `v0.9.0` and push to github 7. Create Release for this version on Github diff --git a/imgui-examples/Cargo.toml b/imgui-examples/Cargo.toml index ca92951dd..6accc2e73 100644 --- a/imgui-examples/Cargo.toml +++ b/imgui-examples/Cargo.toml @@ -1,16 +1,16 @@ [package] name = "imgui-examples" version = "0.1.0" -edition = "2018" +edition = "2021" description = "imgui crate examples using Glium backend" homepage = "https://github.com/imgui-rs/imgui-rs" repository = "https://github.com/imgui-rs/imgui-rs" -license = "MIT/Apache-2.0" +license = "MIT OR Apache-2.0" publish = false [dev-dependencies] copypasta = "0.8" -glium = { version = "0.32.1", default-features = true } +glium = { version = "0.34.0", default-features = true } image = "0.23" imgui = { path = "../imgui", features = ["tables-api"] } imgui-glium-renderer = { path = "../imgui-glium-renderer" } diff --git a/imgui-examples/examples/custom_textures.rs b/imgui-examples/examples/custom_textures.rs index 6b8cfc575..fabd8a802 100644 --- a/imgui-examples/examples/custom_textures.rs +++ b/imgui-examples/examples/custom_textures.rs @@ -189,8 +189,8 @@ impl Lenna { decoder.read_image(&mut image)?; let raw = RawImage2d { data: Cow::Owned(image), - width: width as u32, - height: height as u32, + width, + height, format: ClientFormat::U8U8U8, }; let gl_texture = Texture2d::new(gl_ctx, raw)?; diff --git a/imgui-examples/examples/support/mod.rs b/imgui-examples/examples/support/mod.rs index 44e88259a..65c4b361e 100644 --- a/imgui-examples/examples/support/mod.rs +++ b/imgui-examples/examples/support/mod.rs @@ -1,10 +1,11 @@ -use glium::glutin; -use glium::glutin::event::{Event, WindowEvent}; -use glium::glutin::event_loop::{ControlFlow, EventLoop}; -use glium::glutin::window::WindowBuilder; -use glium::{Display, Surface}; +use glium::glutin::surface::WindowSurface; +use glium::Surface; use imgui::{Context, FontConfig, FontGlyphRanges, FontSource, Ui}; use imgui_glium_renderer::Renderer; +use imgui_winit_support::winit::dpi::LogicalSize; +use imgui_winit_support::winit::event::{Event, WindowEvent}; +use imgui_winit_support::winit::event_loop::EventLoop; +use imgui_winit_support::winit::window::{Window, WindowBuilder}; use imgui_winit_support::{HiDpiMode, WinitPlatform}; use std::path::Path; use std::time::Instant; @@ -13,7 +14,8 @@ mod clipboard; pub struct System { pub event_loop: EventLoop<()>, - pub display: glium::Display, + pub window: Window, + pub display: glium::Display, pub imgui: Context, pub platform: WinitPlatform, pub renderer: Renderer, @@ -25,13 +27,14 @@ pub fn init(title: &str) -> System { Some(file_name) => file_name.to_str().unwrap(), None => title, }; - let event_loop = EventLoop::new(); - let context = glutin::ContextBuilder::new().with_vsync(true); + let event_loop = EventLoop::new().expect("Failed to create EventLoop"); + let builder = WindowBuilder::new() - .with_title(title.to_owned()) - .with_inner_size(glutin::dpi::LogicalSize::new(1024f64, 768f64)); - let display = - Display::new(builder, context, &event_loop).expect("Failed to initialize display"); + .with_title(title) + .with_inner_size(LogicalSize::new(1024, 768)); + let (window, display) = glium::backend::glutin::SimpleWindowBuilder::new() + .set_window_builder(builder) + .build(&event_loop); let mut imgui = Context::create(); imgui.set_ini_filename(None); @@ -44,9 +47,6 @@ pub fn init(title: &str) -> System { let mut platform = WinitPlatform::init(&mut imgui); { - let gl_window = display.gl_window(); - let window = gl_window.window(); - let dpi_mode = if let Ok(factor) = std::env::var("IMGUI_EXAMPLE_FORCE_DPI_FACTOR") { // Allow forcing of HiDPI factor for debugging purposes match factor.parse::() { @@ -57,7 +57,7 @@ pub fn init(title: &str) -> System { HiDpiMode::Default }; - platform.attach_window(imgui.io_mut(), window, dpi_mode); + platform.attach_window(imgui.io_mut(), &window, dpi_mode); } // Fixed font size. Note imgui_winit_support uses "logical @@ -103,6 +103,7 @@ pub fn init(title: &str) -> System { System { event_loop, + window, display, imgui, platform, @@ -115,6 +116,7 @@ impl System { pub fn main_loop(self, mut run_ui: F) { let System { event_loop, + window, display, mut imgui, mut platform, @@ -123,46 +125,57 @@ impl System { } = self; let mut last_frame = Instant::now(); - event_loop.run(move |event, _, control_flow| match event { - Event::NewEvents(_) => { - let now = Instant::now(); - imgui.io_mut().update_delta_time(now - last_frame); - last_frame = now; - } - Event::MainEventsCleared => { - let gl_window = display.gl_window(); - platform - .prepare_frame(imgui.io_mut(), gl_window.window()) - .expect("Failed to prepare frame"); - gl_window.window().request_redraw(); - } - Event::RedrawRequested(_) => { - let ui = imgui.frame(); - - let mut run = true; - run_ui(&mut run, ui); - if !run { - *control_flow = ControlFlow::Exit; + event_loop + .run(move |event, window_target| match event { + Event::NewEvents(_) => { + let now = Instant::now(); + imgui.io_mut().update_delta_time(now - last_frame); + last_frame = now; } - - let gl_window = display.gl_window(); - let mut target = display.draw(); - target.clear_color_srgb(1.0, 1.0, 1.0, 1.0); - platform.prepare_render(ui, gl_window.window()); - let draw_data = imgui.render(); - renderer - .render(&mut target, draw_data) - .expect("Rendering failed"); - target.finish().expect("Failed to swap buffers"); - } - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => *control_flow = ControlFlow::Exit, - event => { - let gl_window = display.gl_window(); - platform.handle_event(imgui.io_mut(), gl_window.window(), &event); - } - }) + Event::AboutToWait => { + platform + .prepare_frame(imgui.io_mut(), &window) + .expect("Failed to prepare frame"); + window.request_redraw(); + } + Event::WindowEvent { + event: WindowEvent::RedrawRequested, + .. + } => { + let ui = imgui.frame(); + + let mut run = true; + run_ui(&mut run, ui); + if !run { + window_target.exit(); + } + + let mut target = display.draw(); + target.clear_color_srgb(1.0, 1.0, 1.0, 1.0); + platform.prepare_render(ui, &window); + let draw_data = imgui.render(); + renderer + .render(&mut target, draw_data) + .expect("Rendering failed"); + target.finish().expect("Failed to swap buffers"); + } + Event::WindowEvent { + event: WindowEvent::Resized(new_size), + .. + } => { + if new_size.width > 0 && new_size.height > 0 { + display.resize((new_size.width, new_size.height)); + } + platform.handle_event(imgui.io_mut(), &window, &event); + } + Event::WindowEvent { + event: WindowEvent::CloseRequested, + .. + } => window_target.exit(), + event => { + platform.handle_event(imgui.io_mut(), &window, &event); + } + }) + .expect("EventLoop error"); } } diff --git a/imgui-examples/examples/test_window_impl.rs b/imgui-examples/examples/test_window_impl.rs index ee9e2c04e..60523c116 100644 --- a/imgui-examples/examples/test_window_impl.rs +++ b/imgui-examples/examples/test_window_impl.rs @@ -759,7 +759,7 @@ CTRL+click on individual component to input value.\n", ); state.filter.draw(); - let lines = vec!["aaa1.c", "bbb1.c", "ccc1.c", "aaa2.cpp", "bbb2.cpp", "ccc2.cpp", "abc.h", "hello, world!"]; + let lines = ["aaa1.c", "bbb1.c", "ccc1.c", "aaa2.cpp", "bbb2.cpp", "ccc2.cpp", "abc.h", "hello, world!"]; ui.same_line(); if ui.button("Clear##clear_filter") { @@ -1285,7 +1285,7 @@ fn show_app_log(ui: &Ui, app_log: &mut Vec) { } ui.same_line(); if ui.button("Copy") { - ui.set_clipboard_text(&ImString::from(app_log.join("\n"))); + ui.set_clipboard_text(ImString::from(app_log.join("\n"))); } ui.separator(); ui.child_window("logwindow") diff --git a/imgui-examples/examples/text_callbacks.rs b/imgui-examples/examples/text_callbacks.rs index dce2c7efc..c65d98780 100644 --- a/imgui-examples/examples/text_callbacks.rs +++ b/imgui-examples/examples/text_callbacks.rs @@ -4,7 +4,7 @@ mod support; fn main() { let system = support::init(file!()); - let mut buffers = vec![String::default(), String::default(), String::default()]; + let mut buffers = [String::default(), String::default(), String::default()]; system.main_loop(move |_, ui| { ui.window("Input text callbacks") @@ -70,7 +70,7 @@ fn main() { struct Wrapper<'a>(&'a mut String); impl<'a> InputTextCallbackHandler for Wrapper<'a> { fn on_always(&mut self, data: TextCallbackData) { - *self.0 = data.str().to_owned(); + data.str().clone_into(self.0); } } @@ -106,7 +106,7 @@ fn main() { if !data.str().is_empty() { data.remove_chars(0, 1); - if let Some((idx, _)) = data.str().char_indices().rev().next() { + if let Some((idx, _)) = data.str().char_indices().next_back() { data.remove_chars(idx, 1); } } @@ -118,7 +118,7 @@ fn main() { } // insert last char - if let Some((idx, _)) = self.1.char_indices().rev().next() { + if let Some((idx, _)) = self.1.char_indices().next_back() { data.push_str(&self.1[idx..]); } } diff --git a/imgui-glium-renderer/Cargo.toml b/imgui-glium-renderer/Cargo.toml index d3aac51e3..b1794e4c6 100644 --- a/imgui-glium-renderer/Cargo.toml +++ b/imgui-glium-renderer/Cargo.toml @@ -1,17 +1,22 @@ [package] name = "imgui-glium-renderer" -version = "0.11.0" -edition = "2018" +version = "0.12.0" +edition = "2021" description = "Glium renderer for the imgui crate" homepage = "https://github.com/imgui-rs/imgui-rs" repository = "https://github.com/imgui-rs/imgui-rs" documentation = "https://docs.rs/imgui-glium-renderer" -license = "MIT/Apache-2.0" +license = "MIT OR Apache-2.0" categories = ["gui", "rendering"] [dependencies] -glium = { version = "0.32.1", default-features = false } -imgui = { version = "0.11.0", path = "../imgui" } +glium = { version = "0.34.0", default-features = false } +imgui = { version = "0.12.0", path = "../imgui" } [dev-dependencies] +glium = { version = "0.34.0", default-features = false, features = ["glutin_backend"] } imgui-winit-support = {path = "../imgui-winit-support"} +glutin = "0.31.1" +glutin-winit = "0.4.2" +winit = { version = "0.29.3", features = ["rwh_05"] } +raw-window-handle = "0.5.0" diff --git a/imgui-glium-renderer/examples/glium_01_basic.rs b/imgui-glium-renderer/examples/glium_01_basic.rs index 9c1fdb446..b57c41936 100644 --- a/imgui-glium-renderer/examples/glium_01_basic.rs +++ b/imgui-glium-renderer/examples/glium_01_basic.rs @@ -1,6 +1,18 @@ -use glium::glutin::event::{Event, WindowEvent}; -use glium::glutin::event_loop::{ControlFlow, EventLoop}; +use std::num::NonZeroU32; + use glium::Surface; +use glutin::{ + config::ConfigTemplateBuilder, + context::{ContextAttributesBuilder, NotCurrentGlContext}, + display::{GetGlDisplay, GlDisplay}, + surface::{SurfaceAttributesBuilder, WindowSurface}, +}; +use imgui_winit_support::winit::{dpi::LogicalSize, event_loop::EventLoop, window::WindowBuilder}; +use raw_window_handle::HasRawWindowHandle; +use winit::{ + event::{Event, WindowEvent}, + window::Window, +}; const TITLE: &str = "Hello, imgui-rs!"; @@ -8,8 +20,8 @@ fn main() { // Common setup for creating a winit window and imgui context, not specifc // to this renderer at all except that glutin is used to create the window // since it will give us access to a GL context - let (event_loop, display) = create_window(); - let (mut winit_platform, mut imgui_context) = imgui_init(&display); + let (event_loop, window, display) = create_window(); + let (mut winit_platform, mut imgui_context) = imgui_init(&window); // Create renderer from this crate let mut renderer = imgui_glium_renderer::Renderer::init(&mut imgui_context, &display) @@ -19,73 +31,112 @@ fn main() { let mut last_frame = std::time::Instant::now(); // Standard winit event loop - event_loop.run(move |event, _, control_flow| match event { - Event::NewEvents(_) => { - let now = std::time::Instant::now(); - imgui_context.io_mut().update_delta_time(now - last_frame); - last_frame = now; - } - Event::MainEventsCleared => { - let gl_window = display.gl_window(); - winit_platform - .prepare_frame(imgui_context.io_mut(), gl_window.window()) - .expect("Failed to prepare frame"); - gl_window.window().request_redraw(); - } - Event::RedrawRequested(_) => { - // Create frame for the all important `&imgui::Ui` - let ui = imgui_context.frame(); - - // Draw our example content - ui.show_demo_window(&mut true); - - // Setup for drawing - let gl_window = display.gl_window(); - let mut target = display.draw(); - - // Renderer doesn't automatically clear window - target.clear_color_srgb(1.0, 1.0, 1.0, 1.0); - - // Perform rendering - winit_platform.prepare_render(ui, gl_window.window()); - let draw_data = imgui_context.render(); - renderer - .render(&mut target, draw_data) - .expect("Rendering failed"); - target.finish().expect("Failed to swap buffers"); - } - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => *control_flow = ControlFlow::Exit, - event => { - let gl_window = display.gl_window(); - winit_platform.handle_event(imgui_context.io_mut(), gl_window.window(), &event); - } - }); + event_loop + .run(move |event, window_target| match event { + Event::NewEvents(_) => { + let now = std::time::Instant::now(); + imgui_context.io_mut().update_delta_time(now - last_frame); + last_frame = now; + } + Event::AboutToWait => { + winit_platform + .prepare_frame(imgui_context.io_mut(), &window) + .expect("Failed to prepare frame"); + window.request_redraw(); + } + Event::WindowEvent { + event: WindowEvent::RedrawRequested, + .. + } => { + // Create frame for the all important `&imgui::Ui` + let ui = imgui_context.frame(); + + // Draw our example content + ui.show_demo_window(&mut true); + + // Setup for drawing + let mut target = display.draw(); + + // Renderer doesn't automatically clear window + target.clear_color_srgb(1.0, 1.0, 1.0, 1.0); + + // Perform rendering + winit_platform.prepare_render(ui, &window); + let draw_data = imgui_context.render(); + renderer + .render(&mut target, draw_data) + .expect("Rendering failed"); + target.finish().expect("Failed to swap buffers"); + } + Event::WindowEvent { + event: WindowEvent::CloseRequested, + .. + } => window_target.exit(), + winit::event::Event::WindowEvent { + event: winit::event::WindowEvent::Resized(new_size), + .. + } => { + if new_size.width > 0 && new_size.height > 0 { + display.resize((new_size.width, new_size.height)); + } + winit_platform.handle_event(imgui_context.io_mut(), &window, &event); + } + event => { + winit_platform.handle_event(imgui_context.io_mut(), &window, &event); + } + }) + .expect("EventLoop error"); } -fn create_window() -> (EventLoop<()>, glium::Display) { - let event_loop = EventLoop::new(); - let context = glium::glutin::ContextBuilder::new().with_vsync(true); - let builder = glium::glutin::window::WindowBuilder::new() - .with_title(TITLE.to_owned()) - .with_inner_size(glium::glutin::dpi::LogicalSize::new(1024f64, 768f64)); - let display = - glium::Display::new(builder, context, &event_loop).expect("Failed to initialize display"); - - (event_loop, display) +fn create_window() -> (EventLoop<()>, Window, glium::Display) { + let event_loop = EventLoop::new().expect("Failed to create EventLoop"); + + let window_builder = WindowBuilder::new() + .with_title(TITLE) + .with_inner_size(LogicalSize::new(1024, 768)); + + let (window, cfg) = glutin_winit::DisplayBuilder::new() + .with_window_builder(Some(window_builder)) + .build(&event_loop, ConfigTemplateBuilder::new(), |mut configs| { + configs.next().unwrap() + }) + .expect("Failed to create OpenGL window"); + let window = window.unwrap(); + + let context_attribs = ContextAttributesBuilder::new().build(Some(window.raw_window_handle())); + let context = unsafe { + cfg.display() + .create_context(&cfg, &context_attribs) + .expect("Failed to create OpenGL context") + }; + + let surface_attribs = SurfaceAttributesBuilder::::new().build( + window.raw_window_handle(), + NonZeroU32::new(1024).unwrap(), + NonZeroU32::new(768).unwrap(), + ); + let surface = unsafe { + cfg.display() + .create_window_surface(&cfg, &surface_attribs) + .expect("Failed to create OpenGL surface") + }; + + let context = context + .make_current(&surface) + .expect("Failed to make OpenGL context current"); + + let display = glium::Display::from_context_surface(context, surface) + .expect("Failed to create glium Display"); + + (event_loop, window, display) } -fn imgui_init(display: &glium::Display) -> (imgui_winit_support::WinitPlatform, imgui::Context) { +fn imgui_init(window: &Window) -> (imgui_winit_support::WinitPlatform, imgui::Context) { let mut imgui_context = imgui::Context::create(); imgui_context.set_ini_filename(None); let mut winit_platform = imgui_winit_support::WinitPlatform::init(&mut imgui_context); - let gl_window = display.gl_window(); - let window = gl_window.window(); - let dpi_mode = imgui_winit_support::HiDpiMode::Default; winit_platform.attach_window(imgui_context.io_mut(), window, dpi_mode); diff --git a/imgui-glium-renderer/src/lib.rs b/imgui-glium-renderer/src/lib.rs index 8cddd5541..82ea24cf6 100644 --- a/imgui-glium-renderer/src/lib.rs +++ b/imgui-glium-renderer/src/lib.rs @@ -9,8 +9,8 @@ use glium::uniforms::{ MagnifySamplerFilter, MinifySamplerFilter, Sampler, SamplerBehavior, SamplerWrapFunction, }; use glium::{ - program, uniform, vertex, Blend, DrawError, DrawParameters, IndexBuffer, Program, Rect, - Surface, Texture2d, VertexBuffer, + program, uniform, vertex, Blend, BlendingFunction, DrawError, DrawParameters, IndexBuffer, + LinearBlendingFactor, Program, Rect, Surface, Texture2d, VertexBuffer, }; use imgui::internal::RawWrapper; use imgui::{BackendFlags, DrawCmd, DrawCmdParams, DrawData, TextureId, Textures}; @@ -113,7 +113,7 @@ impl glium::vertex::Vertex for GliumDrawVert { #[inline] fn build_bindings() -> glium::vertex::VertexFormat { use std::borrow::Cow::*; - Borrowed(&[ + &[ ( Borrowed("pos"), 0, @@ -135,7 +135,7 @@ impl glium::vertex::Vertex for GliumDrawVert { glium::vertex::AttributeType::U8U8U8U8, false, ), - ]) + ] } } @@ -253,7 +253,13 @@ impl Renderer { tex: Sampler(texture.texture.as_ref(), texture.sampler) }, &DrawParameters { - blend: Blend::alpha_blending(), + blend: Blend { + alpha: BlendingFunction::Addition { + source: LinearBlendingFactor::One, + destination: LinearBlendingFactor::OneMinusSourceAlpha, + }, + ..Blend::alpha_blending() + }, scissor: Some(Rect { left: f32::max(0.0, clip_rect[0]).floor() as u32, bottom: f32::max(0.0, fb_height - clip_rect[3]).floor() diff --git a/imgui-glow-renderer/Cargo.toml b/imgui-glow-renderer/Cargo.toml index 4a88fab2d..175963e73 100644 --- a/imgui-glow-renderer/Cargo.toml +++ b/imgui-glow-renderer/Cargo.toml @@ -1,22 +1,25 @@ [package] name = "imgui-glow-renderer" -version = "0.11.0" -edition = "2018" +version = "0.12.0" +edition = "2021" description = "glow renderer for the imgui crate" homepage = "https://github.com/imgui-rs/imgui-rs" repository = "https://github.com/imgui-rs/imgui-rs" -license = "MIT/Apache-2.0" +license = "MIT OR Apache-2.0" categories = ["gui", "rendering"] [dependencies] -imgui = { version = "0.11.0", path = "../imgui" } -glow = "0.12.0" -memoffset = "0.6.4" +imgui = { version = "0.12.0", path = "../imgui" } +glow = "0.13.1" +memoffset = "0.9" [dev-dependencies] -glutin = "0.29.1" -imgui-winit-support = { version = "0.11.0", path = "../imgui-winit-support" } +glutin = "0.31.1" +glutin-winit = "0.4.2" +imgui-winit-support = { version = "0.12.0", path = "../imgui-winit-support" } image = "0.23" +raw-window-handle = "0.5.0" +winit = { version = "0.29.3", features = ["rwh_05"] } [features] # Features here are used to opt-out of compiling code that depends on certain diff --git a/imgui-glow-renderer/examples/glow_01_basic.rs b/imgui-glow-renderer/examples/glow_01_basic.rs index 1a34b0149..32b30ffc5 100644 --- a/imgui-glow-renderer/examples/glow_01_basic.rs +++ b/imgui-glow-renderer/examples/glow_01_basic.rs @@ -1,25 +1,36 @@ //! A basic self-contained example to get you from zero-to-demo-window as fast //! as possible. -use std::time::Instant; +use std::{num::NonZeroU32, time::Instant}; use glow::HasContext; -use glutin::{event_loop::EventLoop, WindowedContext}; -use imgui_winit_support::WinitPlatform; +use glutin::{ + config::ConfigTemplateBuilder, + context::{ContextAttributesBuilder, NotCurrentGlContext, PossiblyCurrentContext}, + display::{GetGlDisplay, GlDisplay}, + surface::{GlSurface, Surface, SurfaceAttributesBuilder, WindowSurface}, +}; +use imgui_winit_support::{ + winit::{ + dpi::LogicalSize, + event_loop::EventLoop, + window::{Window, WindowBuilder}, + }, + WinitPlatform, +}; +use raw_window_handle::HasRawWindowHandle; const TITLE: &str = "Hello, imgui-rs!"; -type Window = WindowedContext; - fn main() { // Common setup for creating a winit window and imgui context, not specifc // to this renderer at all except that glutin is used to create the window // since it will give us access to a GL context - let (event_loop, window) = create_window(); + let (event_loop, window, surface, context) = create_window(); let (mut winit_platform, mut imgui_context) = imgui_init(&window); // OpenGL context from glow - let gl = glow_context(&window); + let gl = glow_context(&context); // OpenGL renderer from this crate let mut ig_renderer = imgui_glow_renderer::AutoRenderer::initialize(gl, &mut imgui_context) @@ -28,70 +39,122 @@ fn main() { let mut last_frame = Instant::now(); // Standard winit event loop - event_loop.run(move |event, _, control_flow| { - match event { - glutin::event::Event::NewEvents(_) => { - let now = Instant::now(); - imgui_context - .io_mut() - .update_delta_time(now.duration_since(last_frame)); - last_frame = now; - } - glutin::event::Event::MainEventsCleared => { - winit_platform - .prepare_frame(imgui_context.io_mut(), window.window()) - .unwrap(); - window.window().request_redraw(); + event_loop + .run(move |event, window_target| { + match event { + winit::event::Event::NewEvents(_) => { + let now = Instant::now(); + imgui_context + .io_mut() + .update_delta_time(now.duration_since(last_frame)); + last_frame = now; + } + winit::event::Event::AboutToWait => { + winit_platform + .prepare_frame(imgui_context.io_mut(), &window) + .unwrap(); + window.request_redraw(); + } + winit::event::Event::WindowEvent { + event: winit::event::WindowEvent::RedrawRequested, + .. + } => { + // The renderer assumes you'll be clearing the buffer yourself + unsafe { ig_renderer.gl_context().clear(glow::COLOR_BUFFER_BIT) }; + + let ui = imgui_context.frame(); + ui.show_demo_window(&mut true); + + winit_platform.prepare_render(ui, &window); + let draw_data = imgui_context.render(); + + // This is the only extra render step to add + ig_renderer + .render(draw_data) + .expect("error rendering imgui"); + + surface + .swap_buffers(&context) + .expect("Failed to swap buffers"); + } + winit::event::Event::WindowEvent { + event: winit::event::WindowEvent::CloseRequested, + .. + } => { + window_target.exit(); + } + winit::event::Event::WindowEvent { + event: winit::event::WindowEvent::Resized(new_size), + .. + } => { + if new_size.width > 0 && new_size.height > 0 { + surface.resize( + &context, + NonZeroU32::new(new_size.width).unwrap(), + NonZeroU32::new(new_size.height).unwrap(), + ); + } + winit_platform.handle_event(imgui_context.io_mut(), &window, &event); + } + event => { + winit_platform.handle_event(imgui_context.io_mut(), &window, &event); + } } - glutin::event::Event::RedrawRequested(_) => { - // The renderer assumes you'll be clearing the buffer yourself - unsafe { ig_renderer.gl_context().clear(glow::COLOR_BUFFER_BIT) }; + }) + .expect("EventLoop error"); +} - let ui = imgui_context.frame(); - ui.show_demo_window(&mut true); +fn create_window() -> ( + EventLoop<()>, + Window, + Surface, + PossiblyCurrentContext, +) { + let event_loop = EventLoop::new().unwrap(); - winit_platform.prepare_render(ui, window.window()); - let draw_data = imgui_context.render(); + let window_builder = WindowBuilder::new() + .with_title(TITLE) + .with_inner_size(LogicalSize::new(1024, 768)); + let (window, cfg) = glutin_winit::DisplayBuilder::new() + .with_window_builder(Some(window_builder)) + .build(&event_loop, ConfigTemplateBuilder::new(), |mut configs| { + configs.next().unwrap() + }) + .expect("Failed to create OpenGL window"); + + let window = window.unwrap(); + + let context_attribs = ContextAttributesBuilder::new().build(Some(window.raw_window_handle())); + let context = unsafe { + cfg.display() + .create_context(&cfg, &context_attribs) + .expect("Failed to create OpenGL context") + }; - // This is the only extra render step to add - ig_renderer - .render(draw_data) - .expect("error rendering imgui"); + let surface_attribs = SurfaceAttributesBuilder::::new() + .with_srgb(Some(true)) + .build( + window.raw_window_handle(), + NonZeroU32::new(1024).unwrap(), + NonZeroU32::new(768).unwrap(), + ); + let surface = unsafe { + cfg.display() + .create_window_surface(&cfg, &surface_attribs) + .expect("Failed to create OpenGL surface") + }; - window.swap_buffers().unwrap(); - } - glutin::event::Event::WindowEvent { - event: glutin::event::WindowEvent::CloseRequested, - .. - } => { - *control_flow = glutin::event_loop::ControlFlow::Exit; - } - event => { - winit_platform.handle_event(imgui_context.io_mut(), window.window(), &event); - } - } - }); -} + let context = context + .make_current(&surface) + .expect("Failed to make OpenGL context current"); -fn create_window() -> (EventLoop<()>, Window) { - let event_loop = glutin::event_loop::EventLoop::new(); - let window = glutin::window::WindowBuilder::new() - .with_title(TITLE) - .with_inner_size(glutin::dpi::LogicalSize::new(1024, 768)); - let window = glutin::ContextBuilder::new() - .with_vsync(true) - .build_windowed(window, &event_loop) - .expect("could not create window"); - let window = unsafe { - window - .make_current() - .expect("could not make window context current") - }; - (event_loop, window) + (event_loop, window, surface, context) } -fn glow_context(window: &Window) -> glow::Context { - unsafe { glow::Context::from_loader_function(|s| window.get_proc_address(s).cast()) } +fn glow_context(context: &PossiblyCurrentContext) -> glow::Context { + unsafe { + glow::Context::from_loader_function_cstr(|s| context.display().get_proc_address(s).cast()) + } } fn imgui_init(window: &Window) -> (WinitPlatform, imgui::Context) { @@ -101,7 +164,7 @@ fn imgui_init(window: &Window) -> (WinitPlatform, imgui::Context) { let mut winit_platform = WinitPlatform::init(&mut imgui_context); winit_platform.attach_window( imgui_context.io_mut(), - window.window(), + window, imgui_winit_support::HiDpiMode::Rounded, ); diff --git a/imgui-glow-renderer/examples/glow_02_triangle.rs b/imgui-glow-renderer/examples/glow_02_triangle.rs index 512b84f0f..09e984b16 100644 --- a/imgui-glow-renderer/examples/glow_02_triangle.rs +++ b/imgui-glow-renderer/examples/glow_02_triangle.rs @@ -1,68 +1,89 @@ //! A basic example showing imgui rendering on top of a simple custom scene. -use std::time::Instant; +use std::{num::NonZeroU32, time::Instant}; mod utils; +use glutin::surface::GlSurface; use utils::Triangler; fn main() { - let (event_loop, window) = utils::create_window("Hello, triangle!", glutin::GlRequest::Latest); + let (event_loop, window, surface, context) = utils::create_window("Hello, triangle!", None); let (mut winit_platform, mut imgui_context) = utils::imgui_init(&window); - let gl = utils::glow_context(&window); + let gl = utils::glow_context(&context); let mut ig_renderer = imgui_glow_renderer::AutoRenderer::initialize(gl, &mut imgui_context) .expect("failed to create renderer"); let tri_renderer = Triangler::new(ig_renderer.gl_context(), "#version 330"); let mut last_frame = Instant::now(); - event_loop.run(move |event, _, control_flow| { - match event { - glutin::event::Event::NewEvents(_) => { - let now = Instant::now(); - imgui_context - .io_mut() - .update_delta_time(now.duration_since(last_frame)); - last_frame = now; - } - glutin::event::Event::MainEventsCleared => { - winit_platform - .prepare_frame(imgui_context.io_mut(), window.window()) - .unwrap(); + event_loop + .run(move |event, window_target| { + match event { + winit::event::Event::NewEvents(_) => { + let now = Instant::now(); + imgui_context + .io_mut() + .update_delta_time(now.duration_since(last_frame)); + last_frame = now; + } + winit::event::Event::AboutToWait => { + winit_platform + .prepare_frame(imgui_context.io_mut(), &window) + .unwrap(); - window.window().request_redraw(); - } - glutin::event::Event::RedrawRequested(_) => { - // Render your custom scene, note we need to borrow the OpenGL - // context from the `AutoRenderer`, which takes ownership of it. - tri_renderer.render(ig_renderer.gl_context()); + window.request_redraw(); + } + winit::event::Event::WindowEvent { + event: winit::event::WindowEvent::RedrawRequested, + .. + } => { + // Render your custom scene, note we need to borrow the OpenGL + // context from the `AutoRenderer`, which takes ownership of it. + tri_renderer.render(ig_renderer.gl_context()); - let ui = imgui_context.frame(); - ui.show_demo_window(&mut true); + let ui = imgui_context.frame(); + ui.show_demo_window(&mut true); - winit_platform.prepare_render(ui, window.window()); - let draw_data = imgui_context.render(); + winit_platform.prepare_render(ui, &window); + let draw_data = imgui_context.render(); - // Render imgui on top of it - ig_renderer - .render(draw_data) - .expect("error rendering imgui"); + // Render imgui on top of it + ig_renderer + .render(draw_data) + .expect("error rendering imgui"); - window.swap_buffers().unwrap(); - } - glutin::event::Event::WindowEvent { - event: glutin::event::WindowEvent::CloseRequested, - .. - } => { - *control_flow = glutin::event_loop::ControlFlow::Exit; - } - glutin::event::Event::LoopDestroyed => { - let gl = ig_renderer.gl_context(); - tri_renderer.destroy(gl); - } - event => { - winit_platform.handle_event(imgui_context.io_mut(), window.window(), &event); + surface + .swap_buffers(&context) + .expect("Failed to swap buffers"); + } + winit::event::Event::WindowEvent { + event: winit::event::WindowEvent::CloseRequested, + .. + } => { + window_target.exit(); + } + winit::event::Event::WindowEvent { + event: winit::event::WindowEvent::Resized(new_size), + .. + } => { + if new_size.width > 0 && new_size.height > 0 { + surface.resize( + &context, + NonZeroU32::new(new_size.width).unwrap(), + NonZeroU32::new(new_size.height).unwrap(), + ); + } + winit_platform.handle_event(imgui_context.io_mut(), &window, &event); + } + winit::event::Event::LoopExiting => { + let gl = ig_renderer.gl_context(); + tri_renderer.destroy(gl); + } + event => { + winit_platform.handle_event(imgui_context.io_mut(), &window, &event); + } } - } - }); + }) + .expect("EventLoop error"); } diff --git a/imgui-glow-renderer/examples/glow_03_triangle_gles.rs b/imgui-glow-renderer/examples/glow_03_triangle_gles.rs index a63f1ee86..dcc4db4f8 100644 --- a/imgui-glow-renderer/examples/glow_03_triangle_gles.rs +++ b/imgui-glow-renderer/examples/glow_03_triangle_gles.rs @@ -4,19 +4,23 @@ //! Note this example uses `Renderer` rather than `AutoRenderer` and //! therefore requries more lifetime-management of the OpenGL context. -use std::time::Instant; +use std::{num::NonZeroU32, time::Instant}; mod utils; +use glutin::{ + context::{ContextApi, Version}, + surface::GlSurface, +}; use utils::Triangler; fn main() { - let (event_loop, window) = utils::create_window( + let (event_loop, window, surface, context) = utils::create_window( "Hello, triangle! (GLES 3.0)", - glutin::GlRequest::Specific(glutin::Api::OpenGlEs, (3, 0)), + Some(ContextApi::Gles(Some(Version::new(3, 0)))), ); let (mut winit_platform, mut imgui_context) = utils::imgui_init(&window); - let gl = utils::glow_context(&window); + let gl = utils::glow_context(&context); // When using `Renderer`, we need to create a texture map let mut texture_map = imgui_glow_renderer::SimpleTextureMap::default(); @@ -31,54 +35,74 @@ fn main() { let tri_renderer = Triangler::new(&gl, "#version 300 es\nprecision mediump float;"); let mut last_frame = Instant::now(); - event_loop.run(move |event, _, control_flow| { - match event { - glutin::event::Event::NewEvents(_) => { - let now = Instant::now(); - imgui_context - .io_mut() - .update_delta_time(now.duration_since(last_frame)); - last_frame = now; - } - glutin::event::Event::MainEventsCleared => { - winit_platform - .prepare_frame(imgui_context.io_mut(), window.window()) - .unwrap(); + event_loop + .run(move |event, window_target| { + match event { + winit::event::Event::NewEvents(_) => { + let now = Instant::now(); + imgui_context + .io_mut() + .update_delta_time(now.duration_since(last_frame)); + last_frame = now; + } + winit::event::Event::AboutToWait => { + winit_platform + .prepare_frame(imgui_context.io_mut(), &window) + .unwrap(); - window.window().request_redraw(); - } - glutin::event::Event::RedrawRequested(_) => { - // Draw custom scene - tri_renderer.render(&gl); + window.request_redraw(); + } + winit::event::Event::WindowEvent { + event: winit::event::WindowEvent::RedrawRequested, + .. + } => { + // Draw custom scene + tri_renderer.render(&gl); - let ui = imgui_context.frame(); - ui.show_demo_window(&mut true); + let ui = imgui_context.frame(); + ui.show_demo_window(&mut true); - winit_platform.prepare_render(ui, window.window()); - let draw_data = imgui_context.render(); + winit_platform.prepare_render(ui, &window); + let draw_data = imgui_context.render(); - // Render imgui on top - ig_renderer - .render(&gl, &texture_map, draw_data) - .expect("error rendering imgui"); + // Render imgui on top + ig_renderer + .render(&gl, &texture_map, draw_data) + .expect("error rendering imgui"); - window.swap_buffers().unwrap(); - } - glutin::event::Event::WindowEvent { - event: glutin::event::WindowEvent::CloseRequested, - .. - } => { - *control_flow = glutin::event_loop::ControlFlow::Exit; - } - glutin::event::Event::LoopDestroyed => { - tri_renderer.destroy(&gl); - // Note, to be good citizens we should manually call destroy - // when the renderer does not own the GL context - ig_renderer.destroy(&gl); - } - event => { - winit_platform.handle_event(imgui_context.io_mut(), window.window(), &event); + surface + .swap_buffers(&context) + .expect("Failed to swap buffers"); + } + winit::event::Event::WindowEvent { + event: winit::event::WindowEvent::CloseRequested, + .. + } => { + window_target.exit(); + } + winit::event::Event::WindowEvent { + event: winit::event::WindowEvent::Resized(new_size), + .. + } => { + if new_size.width > 0 && new_size.height > 0 { + surface.resize( + &context, + NonZeroU32::new(new_size.width).unwrap(), + NonZeroU32::new(new_size.height).unwrap(), + ); + } + winit_platform.handle_event(imgui_context.io_mut(), &window, &event); + } + winit::event::Event::LoopExiting => { + tri_renderer.destroy(&gl); + // Note, to be good citizens we should manually call destroy + // when the renderer does not own the GL context + ig_renderer.destroy(&gl); + } + event => { + winit_platform.handle_event(imgui_context.io_mut(), &window, &event); + } } - } - }); + }) + .expect("EventLoop error"); } diff --git a/imgui-glow-renderer/examples/glow_04_custom_textures.rs b/imgui-glow-renderer/examples/glow_04_custom_textures.rs index cf77110dd..2128ae363 100644 --- a/imgui-glow-renderer/examples/glow_04_custom_textures.rs +++ b/imgui-glow-renderer/examples/glow_04_custom_textures.rs @@ -5,13 +5,15 @@ //! OpenGL automatically converts colors to linear space before the shaders. //! The renderer assumes you set this internal format correctly like this. -use std::{io::Cursor, time::Instant}; +use std::{io::Cursor, num::NonZeroU32, time::Instant}; use glow::HasContext; +use glutin::surface::GlSurface; use image::{jpeg::JpegDecoder, ImageDecoder}; use imgui::Condition; use imgui_glow_renderer::Renderer; +use winit::event_loop::ControlFlow; #[allow(dead_code)] mod utils; @@ -19,9 +21,9 @@ mod utils; const LENNA_JPEG: &[u8] = include_bytes!("../../resources/Lenna.jpg"); fn main() { - let (event_loop, window) = utils::create_window("Custom textures", glutin::GlRequest::Latest); + let (event_loop, window, surface, context) = utils::create_window("Custom textures", None); let (mut winit_platform, mut imgui_context) = utils::imgui_init(&window); - let gl = utils::glow_context(&window); + let gl = utils::glow_context(&context); // This time, we tell OpenGL this is an sRGB framebuffer and OpenGL will // do the conversion to sSGB space for us after the fragment shader. unsafe { gl.enable(glow::FRAMEBUFFER_SRGB) }; @@ -35,55 +37,75 @@ fn main() { let textures_ui = TexturesUi::new(&gl, &mut textures); let mut last_frame = Instant::now(); - event_loop.run(move |event, _, control_flow| { - // Note we can potentially make the loop more efficient by - // changing the `Poll` (default) value to `ControlFlow::Wait` - // but be careful to test on all target platforms! - *control_flow = glutin::event_loop::ControlFlow::Poll; - - match event { - glutin::event::Event::NewEvents(_) => { - let now = Instant::now(); - imgui_context - .io_mut() - .update_delta_time(now.duration_since(last_frame)); - last_frame = now; - } - glutin::event::Event::MainEventsCleared => { - winit_platform - .prepare_frame(imgui_context.io_mut(), window.window()) - .unwrap(); - - window.window().request_redraw(); - } - glutin::event::Event::RedrawRequested(_) => { - unsafe { gl.clear(glow::COLOR_BUFFER_BIT) }; - - let ui = imgui_context.frame(); - textures_ui.show(ui); - - winit_platform.prepare_render(ui, window.window()); - let draw_data = imgui_context.render(); - ig_renderer - .render(&gl, &textures, draw_data) - .expect("error rendering imgui"); + event_loop + .run(move |event, window_target| { + // Note we can potentially make the loop more efficient by + // changing the `Poll` (default) value to `ControlFlow::Wait` + // but be careful to test on all target platforms! + window_target.set_control_flow(ControlFlow::Poll); + + match event { + winit::event::Event::NewEvents(_) => { + let now = Instant::now(); + imgui_context + .io_mut() + .update_delta_time(now.duration_since(last_frame)); + last_frame = now; + } + winit::event::Event::AboutToWait => { + winit_platform + .prepare_frame(imgui_context.io_mut(), &window) + .unwrap(); - window.swap_buffers().unwrap(); - } - glutin::event::Event::WindowEvent { - event: glutin::event::WindowEvent::CloseRequested, - .. - } => { - *control_flow = glutin::event_loop::ControlFlow::Exit; - } - glutin::event::Event::LoopDestroyed => { - ig_renderer.destroy(&gl); - } - event => { - winit_platform.handle_event(imgui_context.io_mut(), window.window(), &event); + window.request_redraw(); + } + winit::event::Event::WindowEvent { + event: winit::event::WindowEvent::RedrawRequested, + .. + } => { + unsafe { gl.clear(glow::COLOR_BUFFER_BIT) }; + + let ui = imgui_context.frame(); + textures_ui.show(ui); + + winit_platform.prepare_render(ui, &window); + let draw_data = imgui_context.render(); + ig_renderer + .render(&gl, &textures, draw_data) + .expect("error rendering imgui"); + + surface + .swap_buffers(&context) + .expect("Failed to swap buffers"); + } + winit::event::Event::WindowEvent { + event: winit::event::WindowEvent::Resized(new_size), + .. + } => { + if new_size.width > 0 && new_size.height > 0 { + surface.resize( + &context, + NonZeroU32::new(new_size.width).unwrap(), + NonZeroU32::new(new_size.height).unwrap(), + ); + } + winit_platform.handle_event(imgui_context.io_mut(), &window, &event); + } + winit::event::Event::WindowEvent { + event: winit::event::WindowEvent::CloseRequested, + .. + } => { + window_target.exit(); + } + winit::event::Event::LoopExiting => { + ig_renderer.destroy(&gl); + } + event => { + winit_platform.handle_event(imgui_context.io_mut(), &window, &event); + } } - } - }); + }) + .expect("EventLoop error"); } struct TexturesUi { diff --git a/imgui-glow-renderer/examples/glow_05_framebufferobject.rs b/imgui-glow-renderer/examples/glow_05_framebufferobject.rs index f85174500..b1b9e49fc 100644 --- a/imgui-glow-renderer/examples/glow_05_framebufferobject.rs +++ b/imgui-glow-renderer/examples/glow_05_framebufferobject.rs @@ -1,10 +1,11 @@ //! A basic example showing imgui rendering on top of a simple custom scene. -use std::{cell::RefCell, rc::Rc, time::Instant}; +use std::{cell::RefCell, num::NonZeroU32, rc::Rc, time::Instant}; mod utils; use glow::HasContext; +use glutin::surface::GlSurface; use utils::Triangler; struct UserData { @@ -16,9 +17,9 @@ struct UserData { const FBO_SIZE: i32 = 128; fn main() { - let (event_loop, window) = utils::create_window("Hello, FBO!", glutin::GlRequest::Latest); + let (event_loop, window, surface, context) = utils::create_window("Hello, FBO!", None); let (mut winit_platform, mut imgui_context) = utils::imgui_init(&window); - let gl = utils::glow_context(&window); + let gl = utils::glow_context(&context); let mut ig_renderer = imgui_glow_renderer::AutoRenderer::initialize(gl, &mut imgui_context) .expect("failed to create renderer"); @@ -55,96 +56,124 @@ fn main() { })); let mut last_frame = Instant::now(); - event_loop.run(move |event, _, control_flow| { - match event { - glutin::event::Event::NewEvents(_) => { - let now = Instant::now(); - imgui_context - .io_mut() - .update_delta_time(now.duration_since(last_frame)); - last_frame = now; - } - glutin::event::Event::MainEventsCleared => { - winit_platform - .prepare_frame(imgui_context.io_mut(), window.window()) - .unwrap(); - - window.window().request_redraw(); - } - glutin::event::Event::RedrawRequested(_) => { - // Render your custom scene, note we need to borrow the OpenGL - // context from the `AutoRenderer`, which takes ownership of it. - unsafe { - ig_renderer.gl_context().clear(glow::COLOR_BUFFER_BIT); + event_loop + .run(move |event, window_target| { + match event { + winit::event::Event::NewEvents(_) => { + let now = Instant::now(); + imgui_context + .io_mut() + .update_delta_time(now.duration_since(last_frame)); + last_frame = now; } + winit::event::Event::AboutToWait => { + winit_platform + .prepare_frame(imgui_context.io_mut(), &window) + .unwrap(); - let ui = imgui_context.frame(); - ui.show_demo_window(&mut true); - ui.window("FBO").resizable(false).build(|| { - let pos = ui.cursor_screen_pos(); - ui.set_cursor_screen_pos([pos[0] + FBO_SIZE as f32, pos[1] + FBO_SIZE as f32]); - - let draws = ui.get_window_draw_list(); - let scale = ui.io().display_framebuffer_scale; - let dsp_size = ui.io().display_size; - draws - .add_callback({ - let data = Rc::clone(&data); - move || { - let data = data.borrow(); - let gl = &*data.gl; - unsafe { - let x = pos[0] * scale[0]; - let y = (dsp_size[1] - pos[1]) * scale[1]; - let dst_x0 = x as i32; - let dst_y0 = (y - FBO_SIZE as f32 * scale[1]) as i32; - let dst_x1 = (x + FBO_SIZE as f32 * scale[0]) as i32; - let dst_y1 = y as i32; - gl.scissor(dst_x0, dst_y0, dst_x1 - dst_x0, dst_y1 - dst_y0); - gl.enable(glow::SCISSOR_TEST); - gl.bind_framebuffer(glow::READ_FRAMEBUFFER, Some(data.fbo)); - gl.blit_framebuffer( - 0, - 0, - FBO_SIZE, - FBO_SIZE, - dst_x0, - dst_y0, - dst_x1, - dst_y1, - glow::COLOR_BUFFER_BIT, - glow::NEAREST, - ); - gl.bind_framebuffer(glow::READ_FRAMEBUFFER, None); + window.request_redraw(); + } + winit::event::Event::WindowEvent { + event: winit::event::WindowEvent::RedrawRequested, + .. + } => { + // Render your custom scene, note we need to borrow the OpenGL + // context from the `AutoRenderer`, which takes ownership of it. + unsafe { + ig_renderer.gl_context().clear(glow::COLOR_BUFFER_BIT); + } + + let ui = imgui_context.frame(); + ui.show_demo_window(&mut true); + ui.window("FBO").resizable(false).build(|| { + let pos = ui.cursor_screen_pos(); + ui.set_cursor_screen_pos([ + pos[0] + FBO_SIZE as f32, + pos[1] + FBO_SIZE as f32, + ]); + + let draws = ui.get_window_draw_list(); + let scale = ui.io().display_framebuffer_scale; + let dsp_size = ui.io().display_size; + draws + .add_callback({ + let data = Rc::clone(&data); + move || { + let data = data.borrow(); + let gl = &*data.gl; + unsafe { + let x = pos[0] * scale[0]; + let y = (dsp_size[1] - pos[1]) * scale[1]; + let dst_x0 = x as i32; + let dst_y0 = (y - FBO_SIZE as f32 * scale[1]) as i32; + let dst_x1 = (x + FBO_SIZE as f32 * scale[0]) as i32; + let dst_y1 = y as i32; + gl.scissor( + dst_x0, + dst_y0, + dst_x1 - dst_x0, + dst_y1 - dst_y0, + ); + gl.enable(glow::SCISSOR_TEST); + gl.bind_framebuffer(glow::READ_FRAMEBUFFER, Some(data.fbo)); + gl.blit_framebuffer( + 0, + 0, + FBO_SIZE, + FBO_SIZE, + dst_x0, + dst_y0, + dst_x1, + dst_y1, + glow::COLOR_BUFFER_BIT, + glow::NEAREST, + ); + gl.bind_framebuffer(glow::READ_FRAMEBUFFER, None); + } } - } - }) - .build(); - }); + }) + .build(); + }); - winit_platform.prepare_render(ui, window.window()); - let draw_data = imgui_context.render(); + winit_platform.prepare_render(ui, &window); + let draw_data = imgui_context.render(); - // Render imgui on top of it - ig_renderer - .render(draw_data) - .expect("error rendering imgui"); + // Render imgui on top of it + ig_renderer + .render(draw_data) + .expect("error rendering imgui"); - window.swap_buffers().unwrap(); - } - glutin::event::Event::WindowEvent { - event: glutin::event::WindowEvent::CloseRequested, - .. - } => { - *control_flow = glutin::event_loop::ControlFlow::Exit; - } - glutin::event::Event::LoopDestroyed => { - let gl = ig_renderer.gl_context(); - tri_renderer.destroy(gl); - } - event => { - winit_platform.handle_event(imgui_context.io_mut(), window.window(), &event); + surface + .swap_buffers(&context) + .expect("Failed to swap buffers"); + } + winit::event::Event::WindowEvent { + event: winit::event::WindowEvent::CloseRequested, + .. + } => { + window_target.exit(); + } + winit::event::Event::WindowEvent { + event: winit::event::WindowEvent::Resized(new_size), + .. + } => { + if new_size.width > 0 && new_size.height > 0 { + surface.resize( + &context, + NonZeroU32::new(new_size.width).unwrap(), + NonZeroU32::new(new_size.height).unwrap(), + ); + } + winit_platform.handle_event(imgui_context.io_mut(), &window, &event); + } + winit::event::Event::LoopExiting => { + let gl = ig_renderer.gl_context(); + tri_renderer.destroy(gl); + } + event => { + winit_platform.handle_event(imgui_context.io_mut(), &window, &event); + } } - } - }); + }) + .expect("EventLoop error"); } diff --git a/imgui-glow-renderer/examples/utils/mod.rs b/imgui-glow-renderer/examples/utils/mod.rs index 3f8399d56..b1a7b78bb 100644 --- a/imgui-glow-renderer/examples/utils/mod.rs +++ b/imgui-glow-renderer/examples/utils/mod.rs @@ -1,29 +1,82 @@ +use std::num::NonZeroU32; + use glow::HasContext; -use glutin::{event_loop::EventLoop, GlRequest}; +use glutin::{ + config::ConfigTemplateBuilder, + context::{ContextApi, ContextAttributesBuilder, NotCurrentGlContext, PossiblyCurrentContext}, + display::{GetGlDisplay, GlDisplay}, + surface::{GlSurface, Surface, SurfaceAttributesBuilder, SwapInterval, WindowSurface}, +}; use imgui_winit_support::WinitPlatform; - -pub type Window = glutin::WindowedContext; - -pub fn create_window(title: &str, gl_request: GlRequest) -> (EventLoop<()>, Window) { - let event_loop = glutin::event_loop::EventLoop::new(); - let window = glutin::window::WindowBuilder::new() +use raw_window_handle::HasRawWindowHandle; +use winit::{ + dpi::LogicalSize, + event_loop::EventLoop, + window::{Window, WindowBuilder}, +}; + +pub fn create_window( + title: &str, + context_api: Option, +) -> ( + EventLoop<()>, + Window, + Surface, + PossiblyCurrentContext, +) { + let event_loop = EventLoop::new().unwrap(); + + let window_builder = WindowBuilder::new() .with_title(title) - .with_inner_size(glutin::dpi::LogicalSize::new(1024, 768)); - let window = glutin::ContextBuilder::new() - .with_gl(gl_request) - .with_vsync(true) - .build_windowed(window, &event_loop) - .expect("could not create window"); - let window = unsafe { - window - .make_current() - .expect("could not make window context current") + .with_inner_size(LogicalSize::new(1024, 768)); + let (window, cfg) = glutin_winit::DisplayBuilder::new() + .with_window_builder(Some(window_builder)) + .build(&event_loop, ConfigTemplateBuilder::new(), |mut configs| { + configs.next().unwrap() + }) + .expect("Failed to create OpenGL window"); + + let window = window.unwrap(); + + let mut context_attribs = ContextAttributesBuilder::new(); + if let Some(context_api) = context_api { + context_attribs = context_attribs.with_context_api(context_api); + } + let context_attribs = context_attribs.build(Some(window.raw_window_handle())); + let context = unsafe { + cfg.display() + .create_context(&cfg, &context_attribs) + .expect("Failed to create OpenGL context") + }; + + let surface_attribs = SurfaceAttributesBuilder::::new() + .with_srgb(Some(true)) + .build( + window.raw_window_handle(), + NonZeroU32::new(1024).unwrap(), + NonZeroU32::new(768).unwrap(), + ); + let surface = unsafe { + cfg.display() + .create_window_surface(&cfg, &surface_attribs) + .expect("Failed to create OpenGL surface") }; - (event_loop, window) + + let context = context + .make_current(&surface) + .expect("Failed to make OpenGL context current"); + + surface + .set_swap_interval(&context, SwapInterval::Wait(NonZeroU32::new(1).unwrap())) + .expect("Failed to set swap interval"); + + (event_loop, window, surface, context) } -pub fn glow_context(window: &Window) -> glow::Context { - unsafe { glow::Context::from_loader_function(|s| window.get_proc_address(s).cast()) } +pub fn glow_context(context: &PossiblyCurrentContext) -> glow::Context { + unsafe { + glow::Context::from_loader_function_cstr(|s| context.display().get_proc_address(s).cast()) + } } pub fn imgui_init(window: &Window) -> (WinitPlatform, imgui::Context) { @@ -33,7 +86,7 @@ pub fn imgui_init(window: &Window) -> (WinitPlatform, imgui::Context) { let mut winit_platform = WinitPlatform::init(&mut imgui_context); winit_platform.attach_window( imgui_context.io_mut(), - window.window(), + window, imgui_winit_support::HiDpiMode::Rounded, ); diff --git a/imgui-glow-renderer/src/lib.rs b/imgui-glow-renderer/src/lib.rs index e178fa693..4b70cc662 100644 --- a/imgui-glow-renderer/src/lib.rs +++ b/imgui-glow-renderer/src/lib.rs @@ -1152,7 +1152,7 @@ fn calculate_matrix(draw_data: &DrawData, clip_origin_is_lower_left: bool) -> [f } unsafe fn to_byte_slice(slice: &[T]) -> &[u8] { - std::slice::from_raw_parts(slice.as_ptr().cast(), slice.len() * size_of::()) + std::slice::from_raw_parts(slice.as_ptr().cast(), std::mem::size_of_val(slice)) } const fn imgui_index_type_as_gl() -> u32 { diff --git a/imgui-sdl2-support/Cargo.toml b/imgui-sdl2-support/Cargo.toml index 2c860ab1d..f29dae5b9 100644 --- a/imgui-sdl2-support/Cargo.toml +++ b/imgui-sdl2-support/Cargo.toml @@ -1,20 +1,20 @@ [package] name = "imgui-sdl2-support" -version = "0.11.0" -edition = "2018" +version = "0.12.0" +edition = "2021" authors = ["The imgui-rs Developers"] description = "sdl2 support code for the imgui crate" homepage = "https://github.com/imgui-rs/imgui-rs" repository = "https://github.com/imgui-rs/imgui-rs" documentation = "https://docs.rs/imgui-sdl2-support" -license = "MIT/Apache-2.0" +license = "MIT OR Apache-2.0" categories = ["gui"] [dependencies] -imgui = { version = "0.11.0", path = "../imgui" } +imgui = { version = "0.12.0", path = "../imgui" } sdl2 = "0.34.5" [dev-dependencies] -glow = "0.12.0" -imgui-glow-renderer = { version = "0.11.0", path = "../imgui-glow-renderer" } +glow = "0.13.1" +imgui-glow-renderer = { version = "0.12.0", path = "../imgui-glow-renderer" } sdl2 = { version = "0.34.5", features = ["bundled", "static-link"] } diff --git a/imgui-sys/Cargo.toml b/imgui-sys/Cargo.toml index b4d014b1d..ace456924 100644 --- a/imgui-sys/Cargo.toml +++ b/imgui-sys/Cargo.toml @@ -1,12 +1,12 @@ [package] name = "imgui-sys" -version = "0.11.0" -edition = "2018" +version = "0.12.0" +edition = "2021" description = "Raw FFI bindings to dear imgui" homepage = "https://github.com/imgui-rs/imgui-rs" repository = "https://github.com/imgui-rs/imgui-rs" documentation = "https://docs.rs/imgui-sys" -license = "MIT/Apache-2.0" +license = "MIT OR Apache-2.0" categories = ["gui", "external-ffi-bindings"] build = "build.rs" links = "imgui" @@ -31,7 +31,7 @@ mint = "0.5.6" cfg-if = "1" [build-dependencies] -cc = "1.0" +cc = "1.0.2" pkg-config = { version="0.3", optional=true } vcpkg = { version="0.2.15", optional=true } diff --git a/imgui-winit-glow-renderer-viewports/Cargo.toml b/imgui-winit-glow-renderer-viewports/Cargo.toml index be6a32439..f20325263 100644 --- a/imgui-winit-glow-renderer-viewports/Cargo.toml +++ b/imgui-winit-glow-renderer-viewports/Cargo.toml @@ -1,20 +1,20 @@ [package] name = "imgui-winit-glow-renderer-viewports" -version = "0.11.0" +version = "0.12.0" edition = "2021" description = "combined platform + renderer using the docking branch viewport feature" homepage = "https://github.com/imgui-rs/imgui-rs" repository = "https://github.com/imgui-rs/imgui-rs" -license = "MIT/Apache-2.0" +license = "MIT OR Apache-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -imgui = { version = "0.11.0", path="../imgui", features=["docking"] } +imgui = { version = "0.12.0", path="../imgui", features=["docking"] } -glow = "0.12.0" -glutin = "0.30.3" +glow = "0.13.1" +glutin = "0.31.1" +glutin-winit = "0.4.2" raw-window-handle = "0.5.0" -winit = "0.27.5" +winit = "0.29.3" thiserror = "1.0.38" -glutin-winit = "0.2.1" diff --git a/imgui-winit-glow-renderer-viewports/examples/viewports_basic.rs b/imgui-winit-glow-renderer-viewports/examples/viewports_basic.rs index b49e70f8e..132834686 100644 --- a/imgui-winit-glow-renderer-viewports/examples/viewports_basic.rs +++ b/imgui-winit-glow-renderer-viewports/examples/viewports_basic.rs @@ -3,21 +3,24 @@ use std::{ffi::CString, num::NonZeroU32, time::Instant}; use glow::{Context, HasContext}; use glutin::{ config::ConfigTemplateBuilder, - context::ContextAttributesBuilder, + context::{ContextAttributesBuilder, NotCurrentGlContext, PossiblyCurrentGlContext}, display::GetGlDisplay, - prelude::{ - GlDisplay, NotCurrentGlContextSurfaceAccessor, PossiblyCurrentContextGlSurfaceAccessor, - }, + prelude::GlDisplay, surface::{GlSurface, SurfaceAttributesBuilder, WindowSurface}, }; use glutin_winit::DisplayBuilder; use imgui::ConfigFlags; use imgui_winit_glow_renderer_viewports::Renderer; use raw_window_handle::HasRawWindowHandle; -use winit::{dpi::LogicalSize, event::WindowEvent, event_loop::EventLoop, window::WindowBuilder}; +use winit::{ + dpi::LogicalSize, + event::WindowEvent, + event_loop::{ControlFlow, EventLoop}, + window::WindowBuilder, +}; fn main() { - let event_loop = EventLoop::new(); + let event_loop = EventLoop::new().expect("Failed to create EventLoop"); let window_builder = WindowBuilder::new() .with_inner_size(LogicalSize::new(800.0, 600.0)) @@ -82,81 +85,86 @@ fn main() { let mut last_frame = Instant::now(); - event_loop.run(move |event, window_target, control_flow| { - control_flow.set_poll(); + event_loop + .run(move |event, window_target| { + window_target.set_control_flow(ControlFlow::Poll); - renderer.handle_event(&mut imgui, &window, &event); + renderer.handle_event(&mut imgui, &window, &event); - match event { - winit::event::Event::NewEvents(_) => { - let now = Instant::now(); - imgui.io_mut().update_delta_time(now - last_frame); - last_frame = now; - } - winit::event::Event::WindowEvent { - window_id, - event: WindowEvent::CloseRequested, - } if window_id == window.id() => { - control_flow.set_exit(); - } - winit::event::Event::WindowEvent { - window_id, - event: WindowEvent::Resized(new_size), - } if window_id == window.id() => { - surface.resize( - &context, - NonZeroU32::new(new_size.width).unwrap(), - NonZeroU32::new(new_size.height).unwrap(), - ); - } - winit::event::Event::MainEventsCleared => { - window.request_redraw(); - } - winit::event::Event::RedrawRequested(_) => { - let ui = imgui.frame(); + match event { + winit::event::Event::NewEvents(_) => { + let now = Instant::now(); + imgui.io_mut().update_delta_time(now - last_frame); + last_frame = now; + } + winit::event::Event::WindowEvent { + window_id, + event: WindowEvent::CloseRequested, + } if window_id == window.id() => { + window_target.exit(); + } + winit::event::Event::WindowEvent { + window_id, + event: WindowEvent::Resized(new_size), + } if window_id == window.id() => { + surface.resize( + &context, + NonZeroU32::new(new_size.width).unwrap(), + NonZeroU32::new(new_size.height).unwrap(), + ); + } + winit::event::Event::AboutToWait => { + window.request_redraw(); + } + winit::event::Event::WindowEvent { + event: WindowEvent::RedrawRequested, + .. + } => { + let ui = imgui.frame(); - ui.dockspace_over_main_viewport(); + ui.dockspace_over_main_viewport(); - ui.show_demo_window(&mut true); - ui.window("Style Editor").build(|| { - ui.show_default_style_editor(); - }); + ui.show_demo_window(&mut true); + ui.window("Style Editor").build(|| { + ui.show_default_style_editor(); + }); - ui.end_frame_early(); + ui.end_frame_early(); - renderer.prepare_render(&mut imgui, &window); + renderer.prepare_render(&mut imgui, &window); - imgui.update_platform_windows(); - renderer - .update_viewports(&mut imgui, window_target, &glow) - .expect("Failed to update viewports"); + imgui.update_platform_windows(); + renderer + .update_viewports(&mut imgui, window_target, &glow) + .expect("Failed to update viewports"); - let draw_data = imgui.render(); + let draw_data = imgui.render(); - if let Err(e) = context.make_current(&surface) { - // For some reason make_current randomly throws errors on windows. - // Until the reason for this is found, we just print it out instead of panicing. - eprintln!("Failed to make current: {e}"); - } + if let Err(e) = context.make_current(&surface) { + // For some reason make_current randomly throws errors on windows. + // Until the reason for this is found, we just print it out instead of panicing. + eprintln!("Failed to make current: {e}"); + } - unsafe { - glow.disable(glow::SCISSOR_TEST); - glow.clear(glow::COLOR_BUFFER_BIT); - } + unsafe { + glow.disable(glow::SCISSOR_TEST); + glow.clear(glow::COLOR_BUFFER_BIT); + } - renderer - .render(&window, &glow, draw_data) - .expect("Failed to render main viewport"); + renderer + .render(&window, &glow, draw_data) + .expect("Failed to render main viewport"); - surface - .swap_buffers(&context) - .expect("Failed to swap buffers"); + surface + .swap_buffers(&context) + .expect("Failed to swap buffers"); - renderer - .render_viewports(&glow, &mut imgui) - .expect("Failed to render viewports"); + renderer + .render_viewports(&glow, &mut imgui) + .expect("Failed to render viewports"); + } + _ => {} } - _ => {} - } - }); + }) + .expect("EventLoop error"); } diff --git a/imgui-winit-glow-renderer-viewports/src/lib.rs b/imgui-winit-glow-renderer-viewports/src/lib.rs index 51ba1b7ec..22b2c5b81 100644 --- a/imgui-winit-glow-renderer-viewports/src/lib.rs +++ b/imgui-winit-glow-renderer-viewports/src/lib.rs @@ -10,19 +10,23 @@ use std::{ use glow::HasContext; use glutin::{ config::ConfigTemplateBuilder, - context::{ContextAttributesBuilder, NotCurrentContext}, + context::{ + ContextAttributesBuilder, NotCurrentContext, NotCurrentGlContext, PossiblyCurrentGlContext, + }, display::GetGlDisplay, - prelude::{GlDisplay, NotCurrentGlContextSurfaceAccessor, PossiblyCurrentGlContext}, + prelude::GlDisplay, surface::{GlSurface, Surface, SurfaceAttributesBuilder, WindowSurface}, }; use glutin_winit::DisplayBuilder; -use imgui::{BackendFlags, ConfigFlags, Id, Key, MouseButton, ViewportFlags}; +use imgui::{BackendFlags, ConfigFlags, Id, Io, Key, MouseButton, ViewportFlags}; use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; use thiserror::Error; use winit::{ dpi::{PhysicalPosition, PhysicalSize}, - event::{DeviceEvent, ElementState, KeyboardInput, TouchPhase, VirtualKeyCode}, + event::{ElementState, TouchPhase}, event_loop::EventLoopWindowTarget, + keyboard::{Key as WinitKey, KeyLocation, NamedKey}, + platform::modifier_supplement::KeyEventExtModifierSupplement, window::{CursorIcon, Window, WindowBuilder}, }; @@ -438,163 +442,148 @@ impl Renderer { main_window: &Window, event: &winit::event::Event, ) { - match *event { - winit::event::Event::WindowEvent { - window_id, - ref event, - } => { - let (window, viewport) = if window_id == main_window.id() { - (main_window, imgui.main_viewport_mut()) - } else if let Some((id, wnd)) = - self.extra_windows.iter().find_map(|(id, (_, _, _, wnd))| { - if wnd.id() == window_id { - Some((*id, wnd)) - } else { - None - } - }) - { - if let Some(viewport) = imgui.viewport_by_id_mut(id) { - (wnd, viewport) + if let winit::event::Event::WindowEvent { + window_id, + ref event, + } = *event + { + let (window, viewport) = if window_id == main_window.id() { + (main_window, imgui.main_viewport_mut()) + } else if let Some((id, wnd)) = + self.extra_windows.iter().find_map(|(id, (_, _, _, wnd))| { + if wnd.id() == window_id { + Some((*id, wnd)) } else { - return; + None } + }) + { + if let Some(viewport) = imgui.viewport_by_id_mut(id) { + (wnd, viewport) } else { return; - }; + } + } else { + return; + }; - match *event { - winit::event::WindowEvent::Resized(new_size) => { - unsafe { - (*(viewport.platform_user_data.cast::())).size = - [new_size.width as f32, new_size.height as f32]; - } + match *event { + winit::event::WindowEvent::Resized(new_size) => { + unsafe { + (*(viewport.platform_user_data.cast::())).size = + [new_size.width as f32, new_size.height as f32]; + } - viewport.platform_request_resize = true; + viewport.platform_request_resize = true; - if window_id == main_window.id() { - imgui.io_mut().display_size = - [new_size.width as f32, new_size.height as f32]; - } - } - winit::event::WindowEvent::Moved(_) => unsafe { - let new_pos = window.inner_position().unwrap().cast::(); - (*(viewport.platform_user_data.cast::())).pos = - [new_pos.x, new_pos.y]; - - viewport.platform_request_move = true; - }, - winit::event::WindowEvent::CloseRequested if window_id != main_window.id() => { - viewport.platform_request_close = true; + if window_id == main_window.id() { + imgui.io_mut().display_size = + [new_size.width as f32, new_size.height as f32]; } - winit::event::WindowEvent::ReceivedCharacter(c) => { - imgui.io_mut().add_input_character(c); - } - winit::event::WindowEvent::Focused(f) => unsafe { - (*(viewport.platform_user_data.cast::())).focus = f; - }, - winit::event::WindowEvent::KeyboardInput { - input: - KeyboardInput { - virtual_keycode: Some(key), - state, - .. - }, - .. - } => { - let pressed = state == ElementState::Pressed; - - // We map both left and right ctrl to `ModCtrl`, etc. - // imgui is told both "left control is pressed" and - // "consider the control key is pressed". Allows - // applications to use either general "ctrl" or a - // specific key. Same applies to other modifiers. - // https://github.com/ocornut/imgui/issues/5047 - handle_key_modifier(imgui.io_mut(), key, pressed); - - // Add main key event - if let Some(key) = to_imgui_key(key) { - imgui.io_mut().add_key_event(key, pressed); + } + winit::event::WindowEvent::Moved(_) => unsafe { + let new_pos = window.inner_position().unwrap().cast::(); + (*(viewport.platform_user_data.cast::())).pos = + [new_pos.x, new_pos.y]; + + viewport.platform_request_move = true; + }, + winit::event::WindowEvent::CloseRequested if window_id != main_window.id() => { + viewport.platform_request_close = true; + } + winit::event::WindowEvent::Focused(f) => unsafe { + (*(viewport.platform_user_data.cast::())).focus = f; + }, + winit::event::WindowEvent::KeyboardInput { ref event, .. } => { + if let Some(txt) = &event.text { + for ch in txt.chars() { + imgui.io_mut().add_input_character(ch); } } - winit::event::WindowEvent::ModifiersChanged(modifiers) => { - imgui - .io_mut() - .add_key_event(Key::ModShift, modifiers.shift()); - imgui.io_mut().add_key_event(Key::ModCtrl, modifiers.ctrl()); - imgui.io_mut().add_key_event(Key::ModAlt, modifiers.alt()); + + let key = event.key_without_modifiers(); + + let pressed = event.state == ElementState::Pressed; + + // We map both left and right ctrl to `ModCtrl`, etc. + // imgui is told both "left control is pressed" and + // "consider the control key is pressed". Allows + // applications to use either general "ctrl" or a + // specific key. Same applies to other modifiers. + // https://github.com/ocornut/imgui/issues/5047 + handle_key_modifier(imgui.io_mut(), &key, pressed); + + // Add main key event + if let Some(key) = to_imgui_key(key, event.location) { + imgui.io_mut().add_key_event(key, pressed); + } + } + winit::event::WindowEvent::ModifiersChanged(modifiers) => { + let state = modifiers.state(); + + imgui + .io_mut() + .add_key_event(Key::ModShift, state.shift_key()); + imgui + .io_mut() + .add_key_event(Key::ModCtrl, state.control_key()); + imgui.io_mut().add_key_event(Key::ModAlt, state.alt_key()); + imgui + .io_mut() + .add_key_event(Key::ModSuper, state.super_key()); + } + winit::event::WindowEvent::CursorMoved { position, .. } => { + if imgui + .io() + .config_flags + .contains(ConfigFlags::VIEWPORTS_ENABLE) + { + let window_pos = window.inner_position().unwrap_or_default().cast::(); + imgui.io_mut().add_mouse_pos_event([ + position.x as f32 + window_pos.x, + position.y as f32 + window_pos.y, + ]); + } else { imgui .io_mut() - .add_key_event(Key::ModSuper, modifiers.logo()); + .add_mouse_pos_event([position.x as f32, position.y as f32]); + } + } + winit::event::WindowEvent::MouseWheel { + delta, + phase: TouchPhase::Moved, + .. + } => match delta { + winit::event::MouseScrollDelta::LineDelta(h, v) => { + imgui.io_mut().add_mouse_wheel_event([h, v]); } - winit::event::WindowEvent::CursorMoved { position, .. } => { - if imgui - .io() - .config_flags - .contains(ConfigFlags::VIEWPORTS_ENABLE) - { - let window_pos = - window.inner_position().unwrap_or_default().cast::(); - imgui.io_mut().add_mouse_pos_event([ - position.x as f32 + window_pos.x, - position.y as f32 + window_pos.y, - ]); + winit::event::MouseScrollDelta::PixelDelta(pos) => { + let h = if pos.x > 0.0 { + 1.0 + } else if pos.x < 0.0 { + -1.0 } else { - imgui - .io_mut() - .add_mouse_pos_event([position.x as f32, position.y as f32]); - } + 0.0 + }; + let v = if pos.y > 0.0 { + 1.0 + } else if pos.y < 0.0 { + -1.0 + } else { + 0.0 + }; + imgui.io_mut().add_mouse_wheel_event([h, v]); } - winit::event::WindowEvent::MouseWheel { - delta, - phase: TouchPhase::Moved, - .. - } => match delta { - winit::event::MouseScrollDelta::LineDelta(h, v) => { - imgui.io_mut().add_mouse_wheel_event([h, v]); - } - winit::event::MouseScrollDelta::PixelDelta(pos) => { - let h = if pos.x > 0.0 { - 1.0 - } else if pos.x < 0.0 { - -1.0 - } else { - 0.0 - }; - let v = if pos.y > 0.0 { - 1.0 - } else if pos.y < 0.0 { - -1.0 - } else { - 0.0 - }; - imgui.io_mut().add_mouse_wheel_event([h, v]); - } - }, - winit::event::WindowEvent::MouseInput { state, button, .. } => { - let state = state == ElementState::Pressed; + }, + winit::event::WindowEvent::MouseInput { state, button, .. } => { + let state = state == ElementState::Pressed; - if let Some(button) = to_imgui_mouse_button(button) { - imgui.io_mut().add_mouse_button_event(button, state); - } + if let Some(button) = to_imgui_mouse_button(button) { + imgui.io_mut().add_mouse_button_event(button, state); } - _ => {} - } - } - winit::event::Event::DeviceEvent { - event: - DeviceEvent::Key(KeyboardInput { - virtual_keycode: Some(key), - state: ElementState::Released, - .. - }), - .. - } => { - if let Some(key) = to_imgui_key(key) { - imgui.io_mut().add_key_event(key, false); } + _ => {} } - _ => {} } } @@ -630,7 +619,7 @@ impl Renderer { } ViewportEvent::SetSize(id, size) => { if let Some((_, _, _, wnd)) = self.extra_windows.get(&id) { - wnd.set_inner_size(PhysicalSize::new(size[0], size[1])); + _ = wnd.request_inner_size(PhysicalSize::new(size[0], size[1])); } } ViewportEvent::SetVisible(id) => { @@ -663,7 +652,7 @@ impl Renderer { imgui::MouseCursor::ResizeEW => winit::window::CursorIcon::EwResize, imgui::MouseCursor::ResizeNESW => winit::window::CursorIcon::NeswResize, imgui::MouseCursor::ResizeNWSE => winit::window::CursorIcon::NwseResize, - imgui::MouseCursor::Hand => winit::window::CursorIcon::Hand, + imgui::MouseCursor::Hand => winit::window::CursorIcon::Grab, imgui::MouseCursor::NotAllowed => winit::window::CursorIcon::NotAllowed, } } @@ -911,15 +900,13 @@ struct PlatformBackend { event_queue: Rc>>, } -fn handle_key_modifier(io: &mut imgui::Io, key: VirtualKeyCode, down: bool) { - if key == VirtualKeyCode::LShift || key == VirtualKeyCode::RShift { - io.add_key_event(imgui::Key::ModShift, down); - } else if key == VirtualKeyCode::LControl || key == VirtualKeyCode::RControl { - io.add_key_event(imgui::Key::ModCtrl, down); - } else if key == VirtualKeyCode::LAlt || key == VirtualKeyCode::RAlt { - io.add_key_event(imgui::Key::ModAlt, down); - } else if key == VirtualKeyCode::LWin || key == VirtualKeyCode::RWin { - io.add_key_event(imgui::Key::ModSuper, down); +fn handle_key_modifier(io: &mut Io, key: &WinitKey, down: bool) { + match key { + WinitKey::Named(NamedKey::Shift) => io.add_key_event(imgui::Key::ModShift, down), + WinitKey::Named(NamedKey::Control) => io.add_key_event(imgui::Key::ModCtrl, down), + WinitKey::Named(NamedKey::Alt) => io.add_key_event(imgui::Key::ModAlt, down), + WinitKey::Named(NamedKey::Super) => io.add_key_event(imgui::Key::ModSuper, down), + _ => {} } } @@ -1028,113 +1015,113 @@ impl imgui::RendererViewportBackend for RendererBackend { fn swap_buffers(&mut self, _viewport: &mut imgui::Viewport) {} } -fn to_imgui_key(keycode: VirtualKeyCode) -> Option { - match keycode { - VirtualKeyCode::Tab => Some(Key::Tab), - VirtualKeyCode::Left => Some(Key::LeftArrow), - VirtualKeyCode::Right => Some(Key::RightArrow), - VirtualKeyCode::Up => Some(Key::UpArrow), - VirtualKeyCode::Down => Some(Key::DownArrow), - VirtualKeyCode::PageUp => Some(Key::PageUp), - VirtualKeyCode::PageDown => Some(Key::PageDown), - VirtualKeyCode::Home => Some(Key::Home), - VirtualKeyCode::End => Some(Key::End), - VirtualKeyCode::Insert => Some(Key::Insert), - VirtualKeyCode::Delete => Some(Key::Delete), - VirtualKeyCode::Back => Some(Key::Backspace), - VirtualKeyCode::Space => Some(Key::Space), - VirtualKeyCode::Return => Some(Key::Enter), - VirtualKeyCode::Escape => Some(Key::Escape), - VirtualKeyCode::LControl => Some(Key::LeftCtrl), - VirtualKeyCode::LShift => Some(Key::LeftShift), - VirtualKeyCode::LAlt => Some(Key::LeftAlt), - VirtualKeyCode::LWin => Some(Key::LeftSuper), - VirtualKeyCode::RControl => Some(Key::RightCtrl), - VirtualKeyCode::RShift => Some(Key::RightShift), - VirtualKeyCode::RAlt => Some(Key::RightAlt), - VirtualKeyCode::RWin => Some(Key::RightSuper), - //VirtualKeyCode::Menu => Some(Key::Menu), // TODO: find out if there is a Menu key in winit - VirtualKeyCode::Key0 => Some(Key::Alpha0), - VirtualKeyCode::Key1 => Some(Key::Alpha1), - VirtualKeyCode::Key2 => Some(Key::Alpha2), - VirtualKeyCode::Key3 => Some(Key::Alpha3), - VirtualKeyCode::Key4 => Some(Key::Alpha4), - VirtualKeyCode::Key5 => Some(Key::Alpha5), - VirtualKeyCode::Key6 => Some(Key::Alpha6), - VirtualKeyCode::Key7 => Some(Key::Alpha7), - VirtualKeyCode::Key8 => Some(Key::Alpha8), - VirtualKeyCode::Key9 => Some(Key::Alpha9), - VirtualKeyCode::A => Some(Key::A), - VirtualKeyCode::B => Some(Key::B), - VirtualKeyCode::C => Some(Key::C), - VirtualKeyCode::D => Some(Key::D), - VirtualKeyCode::E => Some(Key::E), - VirtualKeyCode::F => Some(Key::F), - VirtualKeyCode::G => Some(Key::G), - VirtualKeyCode::H => Some(Key::H), - VirtualKeyCode::I => Some(Key::I), - VirtualKeyCode::J => Some(Key::J), - VirtualKeyCode::K => Some(Key::K), - VirtualKeyCode::L => Some(Key::L), - VirtualKeyCode::M => Some(Key::M), - VirtualKeyCode::N => Some(Key::N), - VirtualKeyCode::O => Some(Key::O), - VirtualKeyCode::P => Some(Key::P), - VirtualKeyCode::Q => Some(Key::Q), - VirtualKeyCode::R => Some(Key::R), - VirtualKeyCode::S => Some(Key::S), - VirtualKeyCode::T => Some(Key::T), - VirtualKeyCode::U => Some(Key::U), - VirtualKeyCode::V => Some(Key::V), - VirtualKeyCode::W => Some(Key::W), - VirtualKeyCode::X => Some(Key::X), - VirtualKeyCode::Y => Some(Key::Y), - VirtualKeyCode::Z => Some(Key::Z), - VirtualKeyCode::F1 => Some(Key::F1), - VirtualKeyCode::F2 => Some(Key::F2), - VirtualKeyCode::F3 => Some(Key::F3), - VirtualKeyCode::F4 => Some(Key::F4), - VirtualKeyCode::F5 => Some(Key::F5), - VirtualKeyCode::F6 => Some(Key::F6), - VirtualKeyCode::F7 => Some(Key::F7), - VirtualKeyCode::F8 => Some(Key::F8), - VirtualKeyCode::F9 => Some(Key::F9), - VirtualKeyCode::F10 => Some(Key::F10), - VirtualKeyCode::F11 => Some(Key::F11), - VirtualKeyCode::F12 => Some(Key::F12), - VirtualKeyCode::Apostrophe => Some(Key::Apostrophe), - VirtualKeyCode::Comma => Some(Key::Comma), - VirtualKeyCode::Minus => Some(Key::Minus), - VirtualKeyCode::Period => Some(Key::Period), - VirtualKeyCode::Slash => Some(Key::Slash), - VirtualKeyCode::Semicolon => Some(Key::Semicolon), - VirtualKeyCode::Equals => Some(Key::Equal), - VirtualKeyCode::LBracket => Some(Key::LeftBracket), - VirtualKeyCode::Backslash => Some(Key::Backslash), - VirtualKeyCode::RBracket => Some(Key::RightBracket), - VirtualKeyCode::Grave => Some(Key::GraveAccent), - VirtualKeyCode::Capital => Some(Key::CapsLock), - VirtualKeyCode::Scroll => Some(Key::ScrollLock), - VirtualKeyCode::Numlock => Some(Key::NumLock), - VirtualKeyCode::Snapshot => Some(Key::PrintScreen), - VirtualKeyCode::Pause => Some(Key::Pause), - VirtualKeyCode::Numpad0 => Some(Key::Keypad0), - VirtualKeyCode::Numpad1 => Some(Key::Keypad1), - VirtualKeyCode::Numpad2 => Some(Key::Keypad2), - VirtualKeyCode::Numpad3 => Some(Key::Keypad3), - VirtualKeyCode::Numpad4 => Some(Key::Keypad4), - VirtualKeyCode::Numpad5 => Some(Key::Keypad5), - VirtualKeyCode::Numpad6 => Some(Key::Keypad6), - VirtualKeyCode::Numpad7 => Some(Key::Keypad7), - VirtualKeyCode::Numpad8 => Some(Key::Keypad8), - VirtualKeyCode::Numpad9 => Some(Key::Keypad9), - VirtualKeyCode::NumpadDecimal => Some(Key::KeypadDecimal), - VirtualKeyCode::NumpadDivide => Some(Key::KeypadDivide), - VirtualKeyCode::NumpadMultiply => Some(Key::KeypadMultiply), - VirtualKeyCode::NumpadSubtract => Some(Key::KeypadSubtract), - VirtualKeyCode::NumpadAdd => Some(Key::KeypadAdd), - VirtualKeyCode::NumpadEnter => Some(Key::KeypadEnter), - VirtualKeyCode::NumpadEquals => Some(Key::KeypadEqual), +fn to_imgui_key(key: winit::keyboard::Key, location: KeyLocation) -> Option { + match (key.as_ref(), location) { + (WinitKey::Named(NamedKey::Tab), _) => Some(Key::Tab), + (WinitKey::Named(NamedKey::ArrowLeft), _) => Some(Key::LeftArrow), + (WinitKey::Named(NamedKey::ArrowRight), _) => Some(Key::RightArrow), + (WinitKey::Named(NamedKey::ArrowUp), _) => Some(Key::UpArrow), + (WinitKey::Named(NamedKey::ArrowDown), _) => Some(Key::DownArrow), + (WinitKey::Named(NamedKey::PageUp), _) => Some(Key::PageUp), + (WinitKey::Named(NamedKey::PageDown), _) => Some(Key::PageDown), + (WinitKey::Named(NamedKey::Home), _) => Some(Key::Home), + (WinitKey::Named(NamedKey::End), _) => Some(Key::End), + (WinitKey::Named(NamedKey::Insert), _) => Some(Key::Insert), + (WinitKey::Named(NamedKey::Delete), _) => Some(Key::Delete), + (WinitKey::Named(NamedKey::Backspace), _) => Some(Key::Backspace), + (WinitKey::Named(NamedKey::Space), _) => Some(Key::Space), + (WinitKey::Named(NamedKey::Enter), KeyLocation::Standard) => Some(Key::Enter), + (WinitKey::Named(NamedKey::Enter), KeyLocation::Numpad) => Some(Key::KeypadEnter), + (WinitKey::Named(NamedKey::Escape), _) => Some(Key::Escape), + (WinitKey::Named(NamedKey::Control), KeyLocation::Left) => Some(Key::LeftCtrl), + (WinitKey::Named(NamedKey::Control), KeyLocation::Right) => Some(Key::RightCtrl), + (WinitKey::Named(NamedKey::Shift), KeyLocation::Left) => Some(Key::LeftShift), + (WinitKey::Named(NamedKey::Shift), KeyLocation::Right) => Some(Key::RightShift), + (WinitKey::Named(NamedKey::Alt), KeyLocation::Left) => Some(Key::LeftAlt), + (WinitKey::Named(NamedKey::Alt), KeyLocation::Right) => Some(Key::RightAlt), + (WinitKey::Named(NamedKey::Super), KeyLocation::Left) => Some(Key::LeftSuper), + (WinitKey::Named(NamedKey::Super), KeyLocation::Right) => Some(Key::RightSuper), + (WinitKey::Named(NamedKey::ContextMenu), _) => Some(Key::Menu), + (WinitKey::Named(NamedKey::F1), _) => Some(Key::F1), + (WinitKey::Named(NamedKey::F2), _) => Some(Key::F2), + (WinitKey::Named(NamedKey::F3), _) => Some(Key::F3), + (WinitKey::Named(NamedKey::F4), _) => Some(Key::F4), + (WinitKey::Named(NamedKey::F5), _) => Some(Key::F5), + (WinitKey::Named(NamedKey::F6), _) => Some(Key::F6), + (WinitKey::Named(NamedKey::F7), _) => Some(Key::F7), + (WinitKey::Named(NamedKey::F8), _) => Some(Key::F8), + (WinitKey::Named(NamedKey::F9), _) => Some(Key::F9), + (WinitKey::Named(NamedKey::F10), _) => Some(Key::F10), + (WinitKey::Named(NamedKey::F11), _) => Some(Key::F11), + (WinitKey::Named(NamedKey::F12), _) => Some(Key::F12), + (WinitKey::Named(NamedKey::CapsLock), _) => Some(Key::CapsLock), + (WinitKey::Named(NamedKey::ScrollLock), _) => Some(Key::ScrollLock), + (WinitKey::Named(NamedKey::NumLock), _) => Some(Key::NumLock), + (WinitKey::Named(NamedKey::PrintScreen), _) => Some(Key::PrintScreen), + (WinitKey::Named(NamedKey::Pause), _) => Some(Key::Pause), + (WinitKey::Character("0"), KeyLocation::Standard) => Some(Key::Alpha0), + (WinitKey::Character("1"), KeyLocation::Standard) => Some(Key::Alpha1), + (WinitKey::Character("2"), KeyLocation::Standard) => Some(Key::Alpha2), + (WinitKey::Character("3"), KeyLocation::Standard) => Some(Key::Alpha3), + (WinitKey::Character("4"), KeyLocation::Standard) => Some(Key::Alpha4), + (WinitKey::Character("5"), KeyLocation::Standard) => Some(Key::Alpha5), + (WinitKey::Character("6"), KeyLocation::Standard) => Some(Key::Alpha6), + (WinitKey::Character("7"), KeyLocation::Standard) => Some(Key::Alpha7), + (WinitKey::Character("8"), KeyLocation::Standard) => Some(Key::Alpha8), + (WinitKey::Character("9"), KeyLocation::Standard) => Some(Key::Alpha9), + (WinitKey::Character("0"), KeyLocation::Numpad) => Some(Key::Keypad0), + (WinitKey::Character("1"), KeyLocation::Numpad) => Some(Key::Keypad1), + (WinitKey::Character("2"), KeyLocation::Numpad) => Some(Key::Keypad2), + (WinitKey::Character("3"), KeyLocation::Numpad) => Some(Key::Keypad3), + (WinitKey::Character("4"), KeyLocation::Numpad) => Some(Key::Keypad4), + (WinitKey::Character("5"), KeyLocation::Numpad) => Some(Key::Keypad5), + (WinitKey::Character("6"), KeyLocation::Numpad) => Some(Key::Keypad6), + (WinitKey::Character("7"), KeyLocation::Numpad) => Some(Key::Keypad7), + (WinitKey::Character("8"), KeyLocation::Numpad) => Some(Key::Keypad8), + (WinitKey::Character("9"), KeyLocation::Numpad) => Some(Key::Keypad9), + (WinitKey::Character("a"), _) => Some(Key::A), + (WinitKey::Character("b"), _) => Some(Key::B), + (WinitKey::Character("c"), _) => Some(Key::C), + (WinitKey::Character("d"), _) => Some(Key::D), + (WinitKey::Character("e"), _) => Some(Key::E), + (WinitKey::Character("f"), _) => Some(Key::F), + (WinitKey::Character("g"), _) => Some(Key::G), + (WinitKey::Character("h"), _) => Some(Key::H), + (WinitKey::Character("i"), _) => Some(Key::I), + (WinitKey::Character("j"), _) => Some(Key::J), + (WinitKey::Character("k"), _) => Some(Key::K), + (WinitKey::Character("l"), _) => Some(Key::L), + (WinitKey::Character("m"), _) => Some(Key::M), + (WinitKey::Character("n"), _) => Some(Key::N), + (WinitKey::Character("o"), _) => Some(Key::O), + (WinitKey::Character("p"), _) => Some(Key::P), + (WinitKey::Character("q"), _) => Some(Key::Q), + (WinitKey::Character("r"), _) => Some(Key::R), + (WinitKey::Character("s"), _) => Some(Key::S), + (WinitKey::Character("t"), _) => Some(Key::T), + (WinitKey::Character("u"), _) => Some(Key::U), + (WinitKey::Character("v"), _) => Some(Key::V), + (WinitKey::Character("w"), _) => Some(Key::W), + (WinitKey::Character("x"), _) => Some(Key::X), + (WinitKey::Character("y"), _) => Some(Key::Y), + (WinitKey::Character("z"), _) => Some(Key::Z), + (WinitKey::Character("'"), _) => Some(Key::Apostrophe), + (WinitKey::Character(","), KeyLocation::Standard) => Some(Key::Comma), + (WinitKey::Character("-"), KeyLocation::Standard) => Some(Key::Minus), + (WinitKey::Character("-"), KeyLocation::Numpad) => Some(Key::KeypadSubtract), + (WinitKey::Character("."), KeyLocation::Standard) => Some(Key::Period), + (WinitKey::Character("."), KeyLocation::Numpad) => Some(Key::KeypadDecimal), + (WinitKey::Character("/"), KeyLocation::Standard) => Some(Key::Slash), + (WinitKey::Character("/"), KeyLocation::Numpad) => Some(Key::KeypadDivide), + (WinitKey::Character(";"), _) => Some(Key::Semicolon), + (WinitKey::Character("="), KeyLocation::Standard) => Some(Key::Equal), + (WinitKey::Character("="), KeyLocation::Numpad) => Some(Key::KeypadEqual), + (WinitKey::Character("["), _) => Some(Key::LeftBracket), + (WinitKey::Character("\\"), _) => Some(Key::Backslash), + (WinitKey::Character("]"), _) => Some(Key::RightBracket), + (WinitKey::Character("`"), _) => Some(Key::GraveAccent), + (WinitKey::Character("*"), KeyLocation::Numpad) => Some(Key::KeypadMultiply), + (WinitKey::Character("+"), KeyLocation::Numpad) => Some(Key::KeypadAdd), _ => None, } } diff --git a/imgui-winit-support/Cargo.toml b/imgui-winit-support/Cargo.toml index 39e04ee78..59ee88cfa 100644 --- a/imgui-winit-support/Cargo.toml +++ b/imgui-winit-support/Cargo.toml @@ -1,14 +1,14 @@ [package] name = "imgui-winit-support" -version = "0.11.0" -edition = "2018" +version = "0.12.0" +edition = "2021" description = "winit support code for the imgui crate" homepage = "https://github.com/imgui-rs/imgui-rs" repository = "https://github.com/imgui-rs/imgui-rs" documentation = "https://docs.rs/imgui-winit-support" -license = "MIT/Apache-2.0" +license = "MIT OR Apache-2.0" categories = ["gui"] [dependencies] -imgui = { version = "0.11.0", path = "../imgui" } -winit = { version = "0.27.2", default-features = false } +imgui = { version = "0.12.0", path = "../imgui" } +winit = { version = "0.29.3", default-features = false } diff --git a/imgui-winit-support/src/lib.rs b/imgui-winit-support/src/lib.rs index 47806ccee..ddc1c8e69 100644 --- a/imgui-winit-support/src/lib.rs +++ b/imgui-winit-support/src/lib.rs @@ -23,7 +23,7 @@ //! use winit::event_loop::{ControlFlow, EventLoop}; //! use winit::window::Window; //! -//! let mut event_loop = EventLoop::new(); +//! let mut event_loop = EventLoop::new().expect("Failed to create EventLoop"); //! let mut window = Window::new(&event_loop).unwrap(); //! //! let mut imgui = Context::create(); @@ -34,7 +34,7 @@ //! //! let mut last_frame = Instant::now(); //! let mut run = true; -//! event_loop.run(move |event, _, control_flow| { +//! event_loop.run(move |event, window_target| { //! match event { //! Event::NewEvents(_) => { //! // other application-specific logic @@ -42,13 +42,13 @@ //! imgui.io_mut().update_delta_time(now - last_frame); //! last_frame = now; //! }, -//! Event::MainEventsCleared => { +//! Event::AboutToWait => { //! // other application-specific logic //! platform.prepare_frame(imgui.io_mut(), &window) // step 4 //! .expect("Failed to prepare frame"); //! window.request_redraw(); //! } -//! Event::RedrawRequested(_) => { +//! Event::WindowEvent { event: WindowEvent::RedrawRequested, .. } => { //! let ui = imgui.frame(); //! // application-specific rendering *under the UI* //! @@ -62,7 +62,7 @@ //! // application-specific rendering *over the UI* //! }, //! Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => { -//! *control_flow = ControlFlow::Exit; +//! window_target.exit(); //! } //! // other application-specific event handling //! event => { @@ -70,7 +70,7 @@ //! // other application-specific event handling //! } //! } -//! }) +//! }).expect("EventLoop error"); //! ``` use imgui::{self, BackendFlags, ConfigFlags, Context, Io, Key, Ui}; @@ -78,14 +78,15 @@ use std::cmp::Ordering; // Re-export winit to make it easier for users to use the correct version. pub use winit; -use winit::dpi::{LogicalPosition, LogicalSize}; +use winit::{ + dpi::{LogicalPosition, LogicalSize}, + keyboard::{Key as WinitKey, KeyLocation, NamedKey}, + platform::modifier_supplement::KeyEventExtModifierSupplement, +}; use winit::{ error::ExternalError, - event::{ - DeviceEvent, ElementState, Event, KeyboardInput, MouseButton, MouseScrollDelta, TouchPhase, - VirtualKeyCode, WindowEvent, - }, + event::{ElementState, Event, MouseButton, MouseScrollDelta, TouchPhase, WindowEvent}, window::{CursorIcon as MouseCursor, Window}, }; @@ -112,7 +113,7 @@ fn to_winit_cursor(cursor: imgui::MouseCursor) -> MouseCursor { imgui::MouseCursor::ResizeEW => MouseCursor::EwResize, imgui::MouseCursor::ResizeNESW => MouseCursor::NeswResize, imgui::MouseCursor::ResizeNWSE => MouseCursor::NwseResize, - imgui::MouseCursor::Hand => MouseCursor::Hand, + imgui::MouseCursor::Hand => MouseCursor::Grab, imgui::MouseCursor::NotAllowed => MouseCursor::NotAllowed, } } @@ -180,126 +181,124 @@ fn to_imgui_mouse_button(button: MouseButton) -> Option { } } -fn to_imgui_key(keycode: VirtualKeyCode) -> Option { - match keycode { - VirtualKeyCode::Tab => Some(Key::Tab), - VirtualKeyCode::Left => Some(Key::LeftArrow), - VirtualKeyCode::Right => Some(Key::RightArrow), - VirtualKeyCode::Up => Some(Key::UpArrow), - VirtualKeyCode::Down => Some(Key::DownArrow), - VirtualKeyCode::PageUp => Some(Key::PageUp), - VirtualKeyCode::PageDown => Some(Key::PageDown), - VirtualKeyCode::Home => Some(Key::Home), - VirtualKeyCode::End => Some(Key::End), - VirtualKeyCode::Insert => Some(Key::Insert), - VirtualKeyCode::Delete => Some(Key::Delete), - VirtualKeyCode::Back => Some(Key::Backspace), - VirtualKeyCode::Space => Some(Key::Space), - VirtualKeyCode::Return => Some(Key::Enter), - VirtualKeyCode::Escape => Some(Key::Escape), - VirtualKeyCode::LControl => Some(Key::LeftCtrl), - VirtualKeyCode::LShift => Some(Key::LeftShift), - VirtualKeyCode::LAlt => Some(Key::LeftAlt), - VirtualKeyCode::LWin => Some(Key::LeftSuper), - VirtualKeyCode::RControl => Some(Key::RightCtrl), - VirtualKeyCode::RShift => Some(Key::RightShift), - VirtualKeyCode::RAlt => Some(Key::RightAlt), - VirtualKeyCode::RWin => Some(Key::RightSuper), - //VirtualKeyCode::Menu => Some(Key::Menu), // TODO: find out if there is a Menu key in winit - VirtualKeyCode::Key0 => Some(Key::Alpha0), - VirtualKeyCode::Key1 => Some(Key::Alpha1), - VirtualKeyCode::Key2 => Some(Key::Alpha2), - VirtualKeyCode::Key3 => Some(Key::Alpha3), - VirtualKeyCode::Key4 => Some(Key::Alpha4), - VirtualKeyCode::Key5 => Some(Key::Alpha5), - VirtualKeyCode::Key6 => Some(Key::Alpha6), - VirtualKeyCode::Key7 => Some(Key::Alpha7), - VirtualKeyCode::Key8 => Some(Key::Alpha8), - VirtualKeyCode::Key9 => Some(Key::Alpha9), - VirtualKeyCode::A => Some(Key::A), - VirtualKeyCode::B => Some(Key::B), - VirtualKeyCode::C => Some(Key::C), - VirtualKeyCode::D => Some(Key::D), - VirtualKeyCode::E => Some(Key::E), - VirtualKeyCode::F => Some(Key::F), - VirtualKeyCode::G => Some(Key::G), - VirtualKeyCode::H => Some(Key::H), - VirtualKeyCode::I => Some(Key::I), - VirtualKeyCode::J => Some(Key::J), - VirtualKeyCode::K => Some(Key::K), - VirtualKeyCode::L => Some(Key::L), - VirtualKeyCode::M => Some(Key::M), - VirtualKeyCode::N => Some(Key::N), - VirtualKeyCode::O => Some(Key::O), - VirtualKeyCode::P => Some(Key::P), - VirtualKeyCode::Q => Some(Key::Q), - VirtualKeyCode::R => Some(Key::R), - VirtualKeyCode::S => Some(Key::S), - VirtualKeyCode::T => Some(Key::T), - VirtualKeyCode::U => Some(Key::U), - VirtualKeyCode::V => Some(Key::V), - VirtualKeyCode::W => Some(Key::W), - VirtualKeyCode::X => Some(Key::X), - VirtualKeyCode::Y => Some(Key::Y), - VirtualKeyCode::Z => Some(Key::Z), - VirtualKeyCode::F1 => Some(Key::F1), - VirtualKeyCode::F2 => Some(Key::F2), - VirtualKeyCode::F3 => Some(Key::F3), - VirtualKeyCode::F4 => Some(Key::F4), - VirtualKeyCode::F5 => Some(Key::F5), - VirtualKeyCode::F6 => Some(Key::F6), - VirtualKeyCode::F7 => Some(Key::F7), - VirtualKeyCode::F8 => Some(Key::F8), - VirtualKeyCode::F9 => Some(Key::F9), - VirtualKeyCode::F10 => Some(Key::F10), - VirtualKeyCode::F11 => Some(Key::F11), - VirtualKeyCode::F12 => Some(Key::F12), - VirtualKeyCode::Apostrophe => Some(Key::Apostrophe), - VirtualKeyCode::Comma => Some(Key::Comma), - VirtualKeyCode::Minus => Some(Key::Minus), - VirtualKeyCode::Period => Some(Key::Period), - VirtualKeyCode::Slash => Some(Key::Slash), - VirtualKeyCode::Semicolon => Some(Key::Semicolon), - VirtualKeyCode::Equals => Some(Key::Equal), - VirtualKeyCode::LBracket => Some(Key::LeftBracket), - VirtualKeyCode::Backslash => Some(Key::Backslash), - VirtualKeyCode::RBracket => Some(Key::RightBracket), - VirtualKeyCode::Grave => Some(Key::GraveAccent), - VirtualKeyCode::Capital => Some(Key::CapsLock), - VirtualKeyCode::Scroll => Some(Key::ScrollLock), - VirtualKeyCode::Numlock => Some(Key::NumLock), - VirtualKeyCode::Snapshot => Some(Key::PrintScreen), - VirtualKeyCode::Pause => Some(Key::Pause), - VirtualKeyCode::Numpad0 => Some(Key::Keypad0), - VirtualKeyCode::Numpad1 => Some(Key::Keypad1), - VirtualKeyCode::Numpad2 => Some(Key::Keypad2), - VirtualKeyCode::Numpad3 => Some(Key::Keypad3), - VirtualKeyCode::Numpad4 => Some(Key::Keypad4), - VirtualKeyCode::Numpad5 => Some(Key::Keypad5), - VirtualKeyCode::Numpad6 => Some(Key::Keypad6), - VirtualKeyCode::Numpad7 => Some(Key::Keypad7), - VirtualKeyCode::Numpad8 => Some(Key::Keypad8), - VirtualKeyCode::Numpad9 => Some(Key::Keypad9), - VirtualKeyCode::NumpadDecimal => Some(Key::KeypadDecimal), - VirtualKeyCode::NumpadDivide => Some(Key::KeypadDivide), - VirtualKeyCode::NumpadMultiply => Some(Key::KeypadMultiply), - VirtualKeyCode::NumpadSubtract => Some(Key::KeypadSubtract), - VirtualKeyCode::NumpadAdd => Some(Key::KeypadAdd), - VirtualKeyCode::NumpadEnter => Some(Key::KeypadEnter), - VirtualKeyCode::NumpadEquals => Some(Key::KeypadEqual), +fn to_imgui_key(key: winit::keyboard::Key, location: KeyLocation) -> Option { + match (key.as_ref(), location) { + (WinitKey::Named(NamedKey::Tab), _) => Some(Key::Tab), + (WinitKey::Named(NamedKey::ArrowLeft), _) => Some(Key::LeftArrow), + (WinitKey::Named(NamedKey::ArrowRight), _) => Some(Key::RightArrow), + (WinitKey::Named(NamedKey::ArrowUp), _) => Some(Key::UpArrow), + (WinitKey::Named(NamedKey::ArrowDown), _) => Some(Key::DownArrow), + (WinitKey::Named(NamedKey::PageUp), _) => Some(Key::PageUp), + (WinitKey::Named(NamedKey::PageDown), _) => Some(Key::PageDown), + (WinitKey::Named(NamedKey::Home), _) => Some(Key::Home), + (WinitKey::Named(NamedKey::End), _) => Some(Key::End), + (WinitKey::Named(NamedKey::Insert), _) => Some(Key::Insert), + (WinitKey::Named(NamedKey::Delete), _) => Some(Key::Delete), + (WinitKey::Named(NamedKey::Backspace), _) => Some(Key::Backspace), + (WinitKey::Named(NamedKey::Space), _) => Some(Key::Space), + (WinitKey::Named(NamedKey::Enter), KeyLocation::Standard) => Some(Key::Enter), + (WinitKey::Named(NamedKey::Enter), KeyLocation::Numpad) => Some(Key::KeypadEnter), + (WinitKey::Named(NamedKey::Escape), _) => Some(Key::Escape), + (WinitKey::Named(NamedKey::Control), KeyLocation::Left) => Some(Key::LeftCtrl), + (WinitKey::Named(NamedKey::Control), KeyLocation::Right) => Some(Key::RightCtrl), + (WinitKey::Named(NamedKey::Shift), KeyLocation::Left) => Some(Key::LeftShift), + (WinitKey::Named(NamedKey::Shift), KeyLocation::Right) => Some(Key::RightShift), + (WinitKey::Named(NamedKey::Alt), KeyLocation::Left) => Some(Key::LeftAlt), + (WinitKey::Named(NamedKey::Alt), KeyLocation::Right) => Some(Key::RightAlt), + (WinitKey::Named(NamedKey::Super), KeyLocation::Left) => Some(Key::LeftSuper), + (WinitKey::Named(NamedKey::Super), KeyLocation::Right) => Some(Key::RightSuper), + (WinitKey::Named(NamedKey::ContextMenu), _) => Some(Key::Menu), + (WinitKey::Named(NamedKey::F1), _) => Some(Key::F1), + (WinitKey::Named(NamedKey::F2), _) => Some(Key::F2), + (WinitKey::Named(NamedKey::F3), _) => Some(Key::F3), + (WinitKey::Named(NamedKey::F4), _) => Some(Key::F4), + (WinitKey::Named(NamedKey::F5), _) => Some(Key::F5), + (WinitKey::Named(NamedKey::F6), _) => Some(Key::F6), + (WinitKey::Named(NamedKey::F7), _) => Some(Key::F7), + (WinitKey::Named(NamedKey::F8), _) => Some(Key::F8), + (WinitKey::Named(NamedKey::F9), _) => Some(Key::F9), + (WinitKey::Named(NamedKey::F10), _) => Some(Key::F10), + (WinitKey::Named(NamedKey::F11), _) => Some(Key::F11), + (WinitKey::Named(NamedKey::F12), _) => Some(Key::F12), + (WinitKey::Named(NamedKey::CapsLock), _) => Some(Key::CapsLock), + (WinitKey::Named(NamedKey::ScrollLock), _) => Some(Key::ScrollLock), + (WinitKey::Named(NamedKey::NumLock), _) => Some(Key::NumLock), + (WinitKey::Named(NamedKey::PrintScreen), _) => Some(Key::PrintScreen), + (WinitKey::Named(NamedKey::Pause), _) => Some(Key::Pause), + (WinitKey::Character("0"), KeyLocation::Standard) => Some(Key::Alpha0), + (WinitKey::Character("1"), KeyLocation::Standard) => Some(Key::Alpha1), + (WinitKey::Character("2"), KeyLocation::Standard) => Some(Key::Alpha2), + (WinitKey::Character("3"), KeyLocation::Standard) => Some(Key::Alpha3), + (WinitKey::Character("4"), KeyLocation::Standard) => Some(Key::Alpha4), + (WinitKey::Character("5"), KeyLocation::Standard) => Some(Key::Alpha5), + (WinitKey::Character("6"), KeyLocation::Standard) => Some(Key::Alpha6), + (WinitKey::Character("7"), KeyLocation::Standard) => Some(Key::Alpha7), + (WinitKey::Character("8"), KeyLocation::Standard) => Some(Key::Alpha8), + (WinitKey::Character("9"), KeyLocation::Standard) => Some(Key::Alpha9), + (WinitKey::Character("0"), KeyLocation::Numpad) => Some(Key::Keypad0), + (WinitKey::Character("1"), KeyLocation::Numpad) => Some(Key::Keypad1), + (WinitKey::Character("2"), KeyLocation::Numpad) => Some(Key::Keypad2), + (WinitKey::Character("3"), KeyLocation::Numpad) => Some(Key::Keypad3), + (WinitKey::Character("4"), KeyLocation::Numpad) => Some(Key::Keypad4), + (WinitKey::Character("5"), KeyLocation::Numpad) => Some(Key::Keypad5), + (WinitKey::Character("6"), KeyLocation::Numpad) => Some(Key::Keypad6), + (WinitKey::Character("7"), KeyLocation::Numpad) => Some(Key::Keypad7), + (WinitKey::Character("8"), KeyLocation::Numpad) => Some(Key::Keypad8), + (WinitKey::Character("9"), KeyLocation::Numpad) => Some(Key::Keypad9), + (WinitKey::Character("a"), _) => Some(Key::A), + (WinitKey::Character("b"), _) => Some(Key::B), + (WinitKey::Character("c"), _) => Some(Key::C), + (WinitKey::Character("d"), _) => Some(Key::D), + (WinitKey::Character("e"), _) => Some(Key::E), + (WinitKey::Character("f"), _) => Some(Key::F), + (WinitKey::Character("g"), _) => Some(Key::G), + (WinitKey::Character("h"), _) => Some(Key::H), + (WinitKey::Character("i"), _) => Some(Key::I), + (WinitKey::Character("j"), _) => Some(Key::J), + (WinitKey::Character("k"), _) => Some(Key::K), + (WinitKey::Character("l"), _) => Some(Key::L), + (WinitKey::Character("m"), _) => Some(Key::M), + (WinitKey::Character("n"), _) => Some(Key::N), + (WinitKey::Character("o"), _) => Some(Key::O), + (WinitKey::Character("p"), _) => Some(Key::P), + (WinitKey::Character("q"), _) => Some(Key::Q), + (WinitKey::Character("r"), _) => Some(Key::R), + (WinitKey::Character("s"), _) => Some(Key::S), + (WinitKey::Character("t"), _) => Some(Key::T), + (WinitKey::Character("u"), _) => Some(Key::U), + (WinitKey::Character("v"), _) => Some(Key::V), + (WinitKey::Character("w"), _) => Some(Key::W), + (WinitKey::Character("x"), _) => Some(Key::X), + (WinitKey::Character("y"), _) => Some(Key::Y), + (WinitKey::Character("z"), _) => Some(Key::Z), + (WinitKey::Character("'"), _) => Some(Key::Apostrophe), + (WinitKey::Character(","), KeyLocation::Standard) => Some(Key::Comma), + (WinitKey::Character("-"), KeyLocation::Standard) => Some(Key::Minus), + (WinitKey::Character("-"), KeyLocation::Numpad) => Some(Key::KeypadSubtract), + (WinitKey::Character("."), KeyLocation::Standard) => Some(Key::Period), + (WinitKey::Character("."), KeyLocation::Numpad) => Some(Key::KeypadDecimal), + (WinitKey::Character("/"), KeyLocation::Standard) => Some(Key::Slash), + (WinitKey::Character("/"), KeyLocation::Numpad) => Some(Key::KeypadDivide), + (WinitKey::Character(";"), _) => Some(Key::Semicolon), + (WinitKey::Character("="), KeyLocation::Standard) => Some(Key::Equal), + (WinitKey::Character("="), KeyLocation::Numpad) => Some(Key::KeypadEqual), + (WinitKey::Character("["), _) => Some(Key::LeftBracket), + (WinitKey::Character("\\"), _) => Some(Key::Backslash), + (WinitKey::Character("]"), _) => Some(Key::RightBracket), + (WinitKey::Character("`"), _) => Some(Key::GraveAccent), + (WinitKey::Character("*"), KeyLocation::Numpad) => Some(Key::KeypadMultiply), + (WinitKey::Character("+"), KeyLocation::Numpad) => Some(Key::KeypadAdd), _ => None, } } -fn handle_key_modifier(io: &mut Io, key: VirtualKeyCode, down: bool) { - if key == VirtualKeyCode::LShift || key == VirtualKeyCode::RShift { - io.add_key_event(imgui::Key::ModShift, down); - } else if key == VirtualKeyCode::LControl || key == VirtualKeyCode::RControl { - io.add_key_event(imgui::Key::ModCtrl, down); - } else if key == VirtualKeyCode::LAlt || key == VirtualKeyCode::RAlt { - io.add_key_event(imgui::Key::ModAlt, down); - } else if key == VirtualKeyCode::LWin || key == VirtualKeyCode::RWin { - io.add_key_event(imgui::Key::ModSuper, down); +fn handle_key_modifier(io: &mut Io, key: &WinitKey, down: bool) { + match key { + WinitKey::Named(NamedKey::Shift) => io.add_key_event(imgui::Key::ModShift, down), + WinitKey::Named(NamedKey::Control) => io.add_key_event(imgui::Key::ModCtrl, down), + WinitKey::Named(NamedKey::Alt) => io.add_key_event(imgui::Key::ModAlt, down), + WinitKey::Named(NamedKey::Super) => io.add_key_event(imgui::Key::ModSuper, down), + _ => {} } } @@ -411,19 +410,18 @@ impl WinitPlatform { } // Track key release events outside our window. If we don't do this, // we might never see the release event if some other window gets focus. - Event::DeviceEvent { - event: - DeviceEvent::Key(KeyboardInput { - state: ElementState::Released, - virtual_keycode: Some(key), - .. - }), - .. - } => { - if let Some(key) = to_imgui_key(key) { - io.add_key_event(key, false); - } - } + // Event::DeviceEvent { + // event: + // DeviceEvent::Key(RawKeyEvent { + // physical_key, + // state: ElementState::Released, + // }), + // .. + // } => { + // if let Some(key) = to_imgui_key(key) { + // io.add_key_event(key, false); + // } + // } _ => (), } } @@ -456,24 +454,26 @@ impl WinitPlatform { io.display_size = [logical_size.width as f32, logical_size.height as f32]; } WindowEvent::ModifiersChanged(modifiers) => { + let state = modifiers.state(); // We need to track modifiers separately because some system like macOS, will // not reliably send modifier states during certain events like ScreenCapture. // Gotta let the people show off their pretty imgui widgets! - io.add_key_event(Key::ModShift, modifiers.shift()); - io.add_key_event(Key::ModCtrl, modifiers.ctrl()); - io.add_key_event(Key::ModAlt, modifiers.alt()); - io.add_key_event(Key::ModSuper, modifiers.logo()); + io.add_key_event(Key::ModShift, state.shift_key()); + io.add_key_event(Key::ModCtrl, state.control_key()); + io.add_key_event(Key::ModAlt, state.alt_key()); + io.add_key_event(Key::ModSuper, state.super_key()); } - WindowEvent::KeyboardInput { - input: - KeyboardInput { - virtual_keycode: Some(key), - state, - .. - }, - .. - } => { - let pressed = state == ElementState::Pressed; + WindowEvent::KeyboardInput { ref event, .. } => { + if let Some(txt) = &event.text { + for ch in txt.chars() { + if ch != '\u{7f}' { + io.add_input_character(ch) + } + } + } + + let key = event.key_without_modifiers(); + let pressed = event.state == ElementState::Pressed; // We map both left and right ctrl to `ModCtrl`, etc. // imgui is told both "left control is pressed" and @@ -481,20 +481,13 @@ impl WinitPlatform { // applications to use either general "ctrl" or a // specific key. Same applies to other modifiers. // https://github.com/ocornut/imgui/issues/5047 - handle_key_modifier(io, key, pressed); + handle_key_modifier(io, &key, pressed); // Add main key event - if let Some(key) = to_imgui_key(key) { + if let Some(key) = to_imgui_key(key, event.location) { io.add_key_event(key, pressed); } } - WindowEvent::ReceivedCharacter(ch) => { - // Exclude the backspace key ('\u{7f}'). Otherwise we will insert this char and then - // delete it. - if ch != '\u{7f}' { - io.add_input_character(ch) - } - } WindowEvent::CursorMoved { position, .. } => { let position = position.to_logical(window.scale_factor()); let position = self.scale_pos_from_winit(window, position); diff --git a/imgui/Cargo.toml b/imgui/Cargo.toml index 0e1586d45..870647423 100644 --- a/imgui/Cargo.toml +++ b/imgui/Cargo.toml @@ -1,12 +1,12 @@ [package] name = "imgui" -version = "0.11.0" -edition = "2018" +version = "0.12.0" +edition = "2021" description = "High-level Rust bindings to dear imgui" homepage = "https://github.com/imgui-rs/imgui-rs" repository = "https://github.com/imgui-rs/imgui-rs" documentation = "https://docs.rs/imgui" -license = "MIT/Apache-2.0" +license = "MIT OR Apache-2.0" categories = ["gui", "api-bindings"] readme = "../README.markdown" @@ -17,7 +17,7 @@ features = ["freetype", "docking", "tables-api"] [dependencies] bitflags = "1" -imgui-sys = { version = "0.11.0", path = "../imgui-sys" } +imgui-sys = { version = "0.12.0", path = "../imgui-sys" } mint = "0.5.6" parking_lot = "0.12" cfg-if = "1" @@ -31,4 +31,4 @@ docking = ["imgui-sys/docking"] tables-api = [] [dev-dependencies] -memoffset = "0.6" +memoffset = "0.9" diff --git a/imgui/src/context.rs b/imgui/src/context.rs index 67bb7866b..e30835048 100644 --- a/imgui/src/context.rs +++ b/imgui/src/context.rs @@ -523,7 +523,7 @@ impl Context { // we take this with an `&mut Self` here, which means // that we can't get the sharedfontatlas through safe code // otherwise - unsafe { &mut *(self.io_mut().fonts as *mut FontAtlas) } + unsafe { &mut *self.io_mut().fonts } } /// Attempts to clone the interior shared font atlas **if it exists**. diff --git a/imgui/src/draw_list.rs b/imgui/src/draw_list.rs index 3ca6eeebd..863471345 100644 --- a/imgui/src/draw_list.rs +++ b/imgui/src/draw_list.rs @@ -27,7 +27,6 @@ bitflags!( /// Options for some DrawList operations. #[repr(C)] pub struct DrawFlags: u32 { - const NONE = sys::ImDrawFlags_None; const CLOSED = sys::ImDrawFlags_Closed; const ROUND_CORNERS_TOP_LEFT = sys::ImDrawFlags_RoundCornersTopLeft; const ROUND_CORNERS_TOP_RIGHT = sys::ImDrawFlags_RoundCornersTopRight; @@ -46,7 +45,6 @@ bitflags!( /// Draw list flags #[repr(C)] pub struct DrawListFlags: u32 { - const NONE = sys::ImDrawListFlags_None; /// Enable anti-aliased lines/borders (*2 the number of triangles for 1.0f wide line or lines /// thin enough to be drawn using textures, otherwise *3 the number of triangles) const ANTI_ALIASED_LINES = sys::ImDrawListFlags_AntiAliasedLines; diff --git a/imgui/src/fonts/glyph_ranges.rs b/imgui/src/fonts/glyph_ranges.rs index b22ae5067..29ae59ac8 100644 --- a/imgui/src/fonts/glyph_ranges.rs +++ b/imgui/src/fonts/glyph_ranges.rs @@ -17,10 +17,6 @@ enum FontGlyphRangeData { #[derive(Clone, Eq, PartialEq, Debug)] pub struct FontGlyphRanges(FontGlyphRangeData); impl FontGlyphRanges { - /// The default set of glyph ranges used by imgui. - pub fn default() -> FontGlyphRanges { - FontGlyphRanges(FontGlyphRangeData::Default) - } /// A set of glyph ranges appropriate for use with simplified common Chinese text. pub fn chinese_simplified_common() -> FontGlyphRanges { FontGlyphRanges(FontGlyphRangeData::ChineseSimplifiedCommon) @@ -162,3 +158,10 @@ impl FontGlyphRanges { } } } + +impl Default for FontGlyphRanges { + /// The default set of glyph ranges used by imgui. + fn default() -> Self { + FontGlyphRanges(FontGlyphRangeData::Default) + } +} diff --git a/imgui/src/internal.rs b/imgui/src/internal.rs index d6a38a739..ad68764e8 100644 --- a/imgui/src/internal.rs +++ b/imgui/src/internal.rs @@ -1,6 +1,6 @@ //! Internal raw utilities (don't use unless you know what you're doing!) -use std::{mem::size_of, slice}; +use std::slice; /// A generic version of the raw imgui-sys ImVector struct types #[repr(C)] @@ -25,7 +25,7 @@ impl ImVector { unsafe { sys::igMemFree(self.data as *mut _); - let buffer_ptr = sys::igMemAlloc(size_of::() * data.len()) as *mut T; + let buffer_ptr = sys::igMemAlloc(std::mem::size_of_val(data)) as *mut T; buffer_ptr.copy_from_nonoverlapping(data.as_ptr(), data.len()); self.size = data.len() as i32; diff --git a/imgui/src/io.rs b/imgui/src/io.rs index ada2ecff2..cf30d891a 100644 --- a/imgui/src/io.rs +++ b/imgui/src/io.rs @@ -423,7 +423,7 @@ impl Io { /// `RandomAccessIterator`, or we'd use that). pub fn input_queue_characters( &self, - ) -> impl Iterator + DoubleEndedIterator + ExactSizeIterator + Clone + '_ { + ) -> impl DoubleEndedIterator + ExactSizeIterator + Clone + '_ { self.input_queue_characters .as_slice() .iter() @@ -459,6 +459,12 @@ impl Io { sys::ImGuiIO_AddKeyEvent(self.raw_mut(), key as u32, down); } } + + pub fn add_key_analog_event(&mut self, key: Key, down: bool, value: f32) { + unsafe { + sys::ImGuiIO_AddKeyAnalogEvent(self.raw_mut(), key as u32, down, value); + } + } } impl Index for Io { diff --git a/imgui/src/lib.rs b/imgui/src/lib.rs index f250f706f..8a180a120 100644 --- a/imgui/src/lib.rs +++ b/imgui/src/lib.rs @@ -10,8 +10,6 @@ use std::os::raw::c_char; pub use self::clipboard::*; pub use self::color::ImColor32; pub use self::context::*; -#[cfg(feature = "docking")] -pub use self::dock_space::*; pub use self::drag_drop::{DragDropFlags, DragDropSource, DragDropTarget}; pub use self::draw_list::{ChannelsSplit, DrawListMut}; pub use self::fonts::atlas::*; @@ -721,9 +719,16 @@ impl Ui { let handle = &mut *self.scratch_buffer().get(); handle.refresh_buffer(); - let label_ptr = handle.push(label); + let label_start = handle.push(label); + + // we do this in two allocations + let items_inner: Vec = items.iter().map(|&v| handle.push(v)).collect(); + let items_inner: Vec<*const _> = items_inner + .into_iter() + .map(|v| handle.buffer.as_ptr().add(v) as *const _) + .collect(); - let items_inner: Vec<_> = items.iter().map(|&v| handle.push(v)).collect(); + let label_ptr = handle.buffer.as_ptr().add(label_start) as *const _; (label_ptr, items_inner) }; diff --git a/imgui/src/popups.rs b/imgui/src/popups.rs index b22cd7a28..a94bcec94 100644 --- a/imgui/src/popups.rs +++ b/imgui/src/popups.rs @@ -247,6 +247,138 @@ impl Ui { pub fn close_current_popup(&self) { unsafe { sys::igCloseCurrentPopup() }; } + + /// Open and begin popup when clicked with the right mouse button on last item. + /// + /// This does not take a label, which means that multiple calls **in a row** will use the same label, which + /// is based on the last node which had a label. Text and other non-interactive elements generally don't have + /// ids, so you'll need to use [begin_popup_context_with_label](Self::begin_popup_context_with_label) for them. + #[doc(alias = "BeginPopupContextItem")] + pub fn begin_popup_context_item(&self) -> Option> { + let render = unsafe { + sys::igBeginPopupContextItem( + std::ptr::null(), + imgui_sys::ImGuiPopupFlags_MouseButtonRight as i32, + ) + }; + + if render { + Some(PopupToken::new(self)) + } else { + None + } + } + + /// Open and begin popup when clicked with the right mouse button on the given item with a dedicated label. + /// + /// If you want to use the label of the previous popup (outside of `Text` and other non-interactive cases, that + /// is the more normal case), use [begin_popup_context_item](Self::begin_popup_context_item). + #[doc(alias = "BeginPopupContextItem")] + pub fn begin_popup_context_with_label>( + &self, + str_id: Label, + ) -> Option> { + let render = unsafe { + sys::igBeginPopupContextItem( + self.scratch_txt(str_id), + imgui_sys::ImGuiPopupFlags_MouseButtonRight as i32, + ) + }; + + if render { + Some(PopupToken::new(self)) + } else { + None + } + } + + /// Open and begin popup when clicked on current window. + /// + /// This does not take a label, which means that multiple calls will use the same provided label. + /// If you want an explicit label, such as having two different kinds of windows popups in your program, + /// use [begin_popup_context_window_with_label](Self::begin_popup_context_window_with_label). + #[doc(alias = "BeginPopupContextWindow")] + pub fn begin_popup_context_window(&self) -> Option> { + let render = unsafe { + sys::igBeginPopupContextWindow( + std::ptr::null(), + imgui_sys::ImGuiPopupFlags_MouseButtonRight as i32, + ) + }; + + if render { + Some(PopupToken::new(self)) + } else { + None + } + } + + /// Open and begin popup when clicked on current window. + /// + /// This takes a label explicitly. This is useful when multiple code + /// locations may want to manipulate/open the same popup, given an explicit id. + #[doc(alias = "BeginPopupContextWindow")] + pub fn begin_popup_context_window_with_label>( + &self, + str_id: Label, + ) -> Option> { + let render = unsafe { + sys::igBeginPopupContextWindow( + self.scratch_txt(str_id), + imgui_sys::ImGuiPopupFlags_MouseButtonRight as i32, + ) + }; + + if render { + Some(PopupToken::new(self)) + } else { + None + } + } + + /// Open and begin popup when right clicked in void (where there are no windows). + /// + /// This does not take a label, which means that multiple calls will use the same provided label. + /// If you want an explicit label, such as having two different kinds of void popups in your program, + /// use [begin_popup_context_void_with_label](Self::begin_popup_context_void_with_label). + #[doc(alias = "BeginPopupContextWindow")] + pub fn begin_popup_context_void(&self) -> Option> { + let render = unsafe { + sys::igBeginPopupContextVoid( + std::ptr::null(), + imgui_sys::ImGuiPopupFlags_MouseButtonRight as i32, + ) + }; + + if render { + Some(PopupToken::new(self)) + } else { + None + } + } + + /// Open and begin popup when right clicked in void (where there are no windows). + /// + /// This takes a label explicitly. This is useful when multiple code + /// locations may want to manipulate/open the same popup, given an explicit id. + #[doc(alias = "BeginPopupContextVoid")] + pub fn begin_popup_context_void_with_label>( + &self, + str_id: Label, + ) -> Option> { + let render = unsafe { + sys::igBeginPopupContextVoid( + self.scratch_txt(str_id), + imgui_sys::ImGuiPopupFlags_MouseButtonRight as i32, + ) + }; + + if render { + Some(PopupToken::new(self)) + } else { + None + } + } } create_token!( diff --git a/imgui/src/render/draw_data.rs b/imgui/src/render/draw_data.rs index 14fc78977..55fa5d257 100644 --- a/imgui/src/render/draw_data.rs +++ b/imgui/src/render/draw_data.rs @@ -52,7 +52,6 @@ impl DrawData { /// Returns the number of draw lists included in the draw data. #[inline] pub fn draw_lists_count(&self) -> usize { - use std::convert::TryInto; self.cmd_lists_count.try_into().unwrap() } #[inline] @@ -334,7 +333,7 @@ impl From<&DrawData> for OwnedDrawData { OwnedDrawData { draw_data: unsafe { let other_ptr = value.raw(); - let mut result = sys::ImDrawData_ImDrawData(); + let result = sys::ImDrawData_ImDrawData(); (*result).Valid = other_ptr.Valid; (*result).TotalIdxCount = other_ptr.TotalIdxCount; (*result).TotalVtxCount = other_ptr.TotalVtxCount; @@ -404,7 +403,7 @@ fn test_owneddrawdata_from_drawdata() { DisplaySize: sys::ImVec2 { x: 789.0, y: 012.0 }, FramebufferScale: sys::ImVec2 { x: 3.0, y: 7.0 }, #[cfg(feature = "docking")] - OwnerViewport: unsafe { (std::ptr::null_mut() as *mut sys::ImGuiViewport).offset(123) }, + OwnerViewport: unsafe { std::ptr::null_mut::().offset(123) }, }; let draw_data = unsafe { DrawData::from_raw(&draw_data_raw) }; diff --git a/imgui/src/string.rs b/imgui/src/string.rs index 601682a8e..f9a08f828 100644 --- a/imgui/src/string.rs +++ b/imgui/src/string.rs @@ -24,7 +24,9 @@ impl UiBuffer { /// Internal method to push a single text to our scratch buffer. pub fn scratch_txt(&mut self, txt: impl AsRef) -> *const sys::cty::c_char { self.refresh_buffer(); - self.push(txt) + + let start_of_substr = self.push(txt); + unsafe { self.offset(start_of_substr) } } /// Internal method to push an option text to our scratch buffer. @@ -42,7 +44,11 @@ impl UiBuffer { txt_1: impl AsRef, ) -> (*const sys::cty::c_char, *const sys::cty::c_char) { self.refresh_buffer(); - (self.push(txt_0), self.push(txt_1)) + + let first_offset = self.push(txt_0); + let second_offset = self.push(txt_1); + + unsafe { (self.offset(first_offset), self.offset(second_offset)) } } /// Helper method, same as [`Self::scratch_txt`] but with one optional value @@ -58,21 +64,30 @@ impl UiBuffer { } /// Attempts to clear the buffer if it's over the maximum length allowed. + /// This is to prevent us from making a giant vec over time. pub fn refresh_buffer(&mut self) { if self.buffer.len() > self.max_len { self.buffer.clear(); } } - /// Pushes a new scratch sheet text, which means it's not handling any clearing at all. - pub fn push(&mut self, txt: impl AsRef) -> *const sys::cty::c_char { - unsafe { - let len = self.buffer.len(); - self.buffer.extend(txt.as_ref().as_bytes()); - self.buffer.push(b'\0'); + /// Given a position, gives an offset from the start of the scatch buffer. + /// + /// # Safety + /// This can return a pointer to undefined data if given a `pos >= self.buffer.len()`. + /// This is marked as unsafe to reflect that. + pub unsafe fn offset(&self, pos: usize) -> *const sys::cty::c_char { + self.buffer.as_ptr().add(pos) as *const _ + } - self.buffer.as_ptr().add(len) as *const _ - } + /// Pushes a new scratch sheet text and return the byte index where the sub-string + /// starts. + pub fn push(&mut self, txt: impl AsRef) -> usize { + let len = self.buffer.len(); + self.buffer.extend(txt.as_ref().as_bytes()); + self.buffer.push(b'\0'); + + len } } diff --git a/imgui/src/widget/drag.rs b/imgui/src/widget/drag.rs index 1c550b87e..bbccebe5b 100644 --- a/imgui/src/widget/drag.rs +++ b/imgui/src/widget/drag.rs @@ -209,22 +209,22 @@ where /// Returns true if the slider value was changed. #[doc(alias = "DragFloatRange2")] pub fn build(self, ui: &Ui, min: &mut f32, max: &mut f32) -> bool { - let label; - let mut display_format = std::ptr::null(); - let mut max_display_format = std::ptr::null(); - // we do this ourselves the long way... unsafe { let buffer = &mut *ui.scratch_buffer().get(); buffer.refresh_buffer(); - label = buffer.push(self.label); - if let Some(v) = self.display_format { - display_format = buffer.push(v); - } - if let Some(v) = self.max_display_format { - max_display_format = buffer.push(v); - } + let label_start = buffer.push(self.label); + let display_format = self.display_format.as_ref().map(|v| buffer.push(v)); + let max_display_format = self.max_display_format.as_ref().map(|v| buffer.push(v)); + + let label = buffer.offset(label_start); + let display_format = display_format + .map(|v| buffer.offset(v)) + .unwrap_or_else(std::ptr::null); + let max_display_format = max_display_format + .map(|v| buffer.offset(v)) + .unwrap_or_else(std::ptr::null); sys::igDragFloatRange2( label, @@ -253,20 +253,21 @@ where #[doc(alias = "DragIntRange2")] pub fn build(self, ui: &Ui, min: &mut i32, max: &mut i32) -> bool { unsafe { - let mut display_format = std::ptr::null(); - let mut max_display_format = std::ptr::null(); - // we do this ourselves the long way... let buffer = &mut *ui.scratch_buffer().get(); buffer.refresh_buffer(); - let label = buffer.push(self.label); - if let Some(v) = self.display_format { - display_format = buffer.push(v); - } - if let Some(v) = self.max_display_format { - max_display_format = buffer.push(v); - } + let label_start = buffer.push(self.label); + let display_format = self.display_format.as_ref().map(|v| buffer.push(v)); + let max_display_format = self.max_display_format.as_ref().map(|v| buffer.push(v)); + + let label = buffer.offset(label_start); + let display_format = display_format + .map(|v| buffer.offset(v)) + .unwrap_or_else(std::ptr::null); + let max_display_format = max_display_format + .map(|v| buffer.offset(v)) + .unwrap_or_else(std::ptr::null); sys::igDragIntRange2( label,