diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 670e0d0..90c5d16 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,7 +22,7 @@ jobs: strategy: matrix: - rust: ["1.54"] + rust: ["1.57"] env: RUSTFLAGS: -D warnings @@ -67,15 +67,8 @@ jobs: - name: freetype and docking run: cargo clippy --workspace --all-targets --features freetype,docking - # supported winit versions (with otherwise default features) - - run: cargo doc - - run: cargo clippy --manifest-path imgui-winit-support/Cargo.toml --no-default-features --features winit-19 --all-targets - - run: cargo clippy --manifest-path imgui-winit-support/Cargo.toml --no-default-features --features winit-20 --all-targets - - run: cargo clippy --manifest-path imgui-winit-support/Cargo.toml --no-default-features --features winit-22 --all-targets - - run: cargo clippy --manifest-path imgui-winit-support/Cargo.toml --no-default-features --features winit-23/default --all-targets - - run: cargo clippy --manifest-path imgui-winit-support/Cargo.toml --no-default-features --features winit-24/default --all-targets - - run: cargo clippy --manifest-path imgui-winit-support/Cargo.toml --no-default-features --features winit-25/default --all-targets - - run: cargo clippy --manifest-path imgui-winit-support/Cargo.toml --no-default-features --features winit-26/default --all-targets + - name: all features + run: cargo clippy --workspace --all-targets --features docking,freetype test: name: Run tests @@ -89,7 +82,7 @@ jobs: rust: - stable - beta - - "1.54" + - "1.57" os: - ubuntu-latest - macos-latest @@ -147,20 +140,17 @@ jobs: if: matrix.os != 'windows-latest' run: cargo test --workspace --all-targets --features freetype - - name: freetype and docking + - name: all features if: matrix.os != 'windows-latest' - run: cargo test --workspace --all-targets --features freetype,docking + run: cargo test --workspace --all-targets --features docking,freetype + + - name: doc tests + run: cargo test --workspace --doc - - run: cargo test --workspace --doc # run to check for lint problems - - run: cargo doc - - run: cargo test --manifest-path imgui-winit-support/Cargo.toml --no-default-features --features winit-19 - - run: cargo test --manifest-path imgui-winit-support/Cargo.toml --no-default-features --features winit-20 - - run: cargo test --manifest-path imgui-winit-support/Cargo.toml --no-default-features --features winit-22 - - run: cargo test --manifest-path imgui-winit-support/Cargo.toml --no-default-features --features winit-23/default - - run: cargo test --manifest-path imgui-winit-support/Cargo.toml --no-default-features --features winit-24/default - - run: cargo test --manifest-path imgui-winit-support/Cargo.toml --no-default-features --features winit-25/default - - run: cargo test --manifest-path imgui-winit-support/Cargo.toml --no-default-features --features winit-26/default + - name: build documentation + run: cargo doc + # Run unreasonably slow tests under release, but only the crates that have # them, and don't bother doing this on most platforms. - run: cargo test -p imgui --release -- --ignored diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown index c6784be..6f1464c 100644 --- a/CHANGELOG.markdown +++ b/CHANGELOG.markdown @@ -1,11 +1,20 @@ # Change Log -## Unreleased +## [unreleased] +- Removed `im_str!` macro - deprecated since v0.8. -- MSRV is now **1.54**. We soft-updated to this in 0.8.0 with a feature `min-const-generics`, which has now been removed (and as such, we resume having no default features). + `ui.button(im_str!("Example"))` just becomes `ui.button("Example")` and `ui.button(&im_str!("My age is {}", 100))` becomes `ui.button!(format!("My age is {}", 100))` + +## [0.9.0] - 2022-11-30 + +- MSRV is now **1.57**. We soft-updated to this to Rust 1.54 in the v0.8.0 release (with a feature `min-const-generics`), which has now been removed (and as such, we resume having no default features). Rust 1.56 is required for the Rust 2021 edition, and 1.57 is required by some dependencies - Upgraded from Dear ImGui 1.84.2 to 1.86. See [the 1.85](https://github.com/ocornut/imgui/releases/tag/v1.85) and [the 1.86](https://github.com/ocornut/imgui/releases/tag/v1.86) release notes +- Upgraded winit version to `v0.27` for `imgu-winit-support` + +- The `imgui-winit-support` and `imgui-glow-renderer` re-export `winit` and `glow` respectively to make setup easier for simple projects. [PR #676](https://github.com/imgui-rs/imgui-rs/pull/676) + - BREAKING: Removed `push_style_colors` and `push_style_vars`. Instead, use `push_style_color` in a loop. This was deprecated in `0.7.0` and should have been removed in `0.8.0`. This also removes their associated tokens. - BREAKING: Ui now does not have a lifetime associated with it, but is only ever given to users in the form of `&mut Ui`. Additionally, the `render` function has been moved to the `Context` instead of `Ui`. @@ -36,6 +45,10 @@ - Fixed dpi related issues when not in `HiDpiMode::Default` mode. The wrong scale factor was used when converting winit physical size to logical size, causing the imgui display size to be incorrect. +- Fixed creation of `.crate` (published to crates.io) so required files for freetype feature are included + +- Added binding to TextFilter API. [PR #658](https://github.com/imgui-rs/imgui-rs/pull/658) + ## [0.8.0] - 2021-09-17 Welcome to the `0.8.0` update. This is one of the largest updates imgui-rs has ever seen; it will generate errors in a `0.7` project, but hopefully it should be both quick to fix, and enjoyable to update. See our [release page](https://github.com/imgui-rs/imgui-rs/releases/tag/v0.8.0) for more information and a list of contributors to this cycle. Thank you to everyone who uses `imgui-rs`, files issues, and spend their time and effort to PR new changes into the codebase. Because of all that effort, this is by far the best `imgui-rs` has looked! @@ -771,7 +784,8 @@ As mentioned, the 0.6.1 release of `imgui-winit-support` has been yanked. - Initial release with cimgui/imgui 1.44, glium 0.9 -[unreleased]: https://github.com/Gekkio/imgui-rs/compare/v0.8.0...HEAD +[unreleased]: https://github.com/Gekkio/imgui-rs/compare/v0.9.0...HEAD +[0.9.0]: https://github.com/Gekkio/imgui-rs/compare/v0.8.0...v0.9.0 [0.8.0]: https://github.com/Gekkio/imgui-rs/compare/v0.7.0...v0.8.0 [0.7.0]: https://github.com/Gekkio/imgui-rs/compare/v0.6.1...v0.7.0 [0.6.1]: https://github.com/Gekkio/imgui-rs/compare/v0.6.0...v0.6.1 diff --git a/README.markdown b/README.markdown index 0960ea1..0ba81ac 100644 --- a/README.markdown +++ b/README.markdown @@ -3,9 +3,7 @@ [![Build Status](https://github.com/imgui-rs/imgui-rs/workflows/ci/badge.svg)](https://github.com/imgui-rs/imgui-rs/actions) [![Latest release on crates.io](https://img.shields.io/crates/v/imgui.svg)](https://crates.io/crates/imgui) [![Documentation on docs.rs](https://docs.rs/imgui/badge.svg)](https://docs.rs/imgui) -[![Wrapped Dear ImGui Version](https://img.shields.io/badge/Dear%20ImGui%20Version-1.84.2-blue.svg)](https://github.com/ocornut/imgui) - -(Recently under new maintenance, things subject to change) +[![Wrapped Dear ImGui Version](https://img.shields.io/badge/Dear%20ImGui%20Version-1.86.0-blue.svg)](https://github.com/ocornut/imgui) ![Hello world](hello_world.png) @@ -34,11 +32,6 @@ ui.window("Hello world") - imgui-glium-renderer: Renderer implementation that uses the `glium` crate - imgui-sys: Low-level unsafe API (automatically generated) -Additionally, the following are no longer maintained, but might work still: - -- imgui-gfx-renderer: Renderer implementation that uses the `gfx` crate (_not - the new gfx-hal crate_). This can be found at [imgui-rs/imgui-gfx-renderer](https://github.com/imgui-rs/imgui-gfx-renderer) - ## Features - Bindings for Dear ImGui that can be used with safe Rust. Note: API coverage @@ -51,7 +44,7 @@ Additionally, the following are no longer maintained, but might work still: ## Minimum Support Rust Version (MSRV) -The MSRV for `imgui-rs` and all of the backend crates is **1.54**. 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.57**. We update our MSRV periodically, and issue a minor bump for it. ## Choosing a backend platform and a renderer @@ -90,6 +83,8 @@ Additionally, there are other libraries which provide other kinds of renderers, 1. [`imgui-wgpu`](https://github.com/Yatekii/imgui-wgpu-rs) 2. [`imgui-d3d12-renderer`](https://github.com/curldivergence/imgui-d3d12-renderer) 3. [`imgui-dx11-renderer`](https://github.com/veykril/imgui-dx11-renderer) + 4. [`imgui-gfx-renderer`](https://github.com/imgui-rs/imgui-gfx-renderer): Deprecated (no longer maintained beyond imgui-rs v0.8). Renderer implementation that uses the `gfx` crate (_not the new gfx-hal crate_) + You can also write your own support code if you have a more advanced use case, because **imgui-rs is not tied to any specific graphics / OS API**. diff --git a/docs/development-process.md b/docs/development-process.md new file mode 100644 index 0000000..3677469 --- /dev/null +++ b/docs/development-process.md @@ -0,0 +1,37 @@ +# imgui-rs development process + +As summarised in [issue #665](https://github.com/imgui-rs/imgui-rs/issues/665) + +In summary: + +1. There is a `main` branch +2. For each semver compatible release there is a stable branch (e.g `0.9-stable`) +3. Each patch release becomes a tagged commit on this stable branch (e.g `v0.9.5` would come from a tagged commit on the `0.9-stable` branch) + +## General process + +Day to day development + +1. Work on `main` branch +2. General PR's are submitted against the `main` branch + +When it is time to make a new release, we create a `x.y-stable` branch (e.g `0.9-stable`) from `main` + +1. Ensure `CHANGELOG` is up to date +2. Ensure README is up-to-date (including the Dear ImGui Version in badge URL, MSRV) +3. Bump `version` in the various `Cargo.toml` +4. A stable branch is created, e.g `git switch -c 0.9-stable` and pushed to Github +5. Publish various crates (noting it has to be done starting with `imgui-sys`, then `imgui`, then the others) +6. Create annotated tag `v0.9.0` and push to github +7. Create Release for this version on Github +8. Update and close any relevant tickets + +All further PR's are still done to `main` + +1. If they are applicable to previous release (e.g bugfixes or non-breaking changes), they are cherry-picked back to the applicable `stable` branch(es) + +## When to bump versions in Cargo.toml + +Only before publishing to crates.io. + +This makes users able use `[patch.crates-io]` without hand-editing versions throughout their dependency tree (typically impossible without forking/editing transitive dependencies, even if there are no breaking code changes otherwise). diff --git a/imgui-examples/Cargo.toml b/imgui-examples/Cargo.toml index 0961a77..f158f8d 100644 --- a/imgui-examples/Cargo.toml +++ b/imgui-examples/Cargo.toml @@ -2,7 +2,6 @@ name = "imgui-examples" version = "0.0.0" edition = "2018" -authors = ["The imgui-rs Developers"] description = "imgui crate examples using Glium backend" homepage = "https://github.com/imgui-rs/imgui-rs" repository = "https://github.com/imgui-rs/imgui-rs" @@ -11,11 +10,16 @@ publish = false [dev-dependencies] clipboard = "0.5" -glium = { version = "0.31", default-features = true } +glium = { version = "0.32.1", default-features = true } image = "0.23" imgui = { path = "../imgui", features = ["tables-api"] } imgui-glium-renderer = { path = "../imgui-glium-renderer" } imgui-winit-support = { path = "../imgui-winit-support", features=["viewports"] } -glow = "0.11.2" -glutin = "0.28.0" +# Pin indirect dependency scoped-tls to 1.0.0 +# as 1.0.1 bumped MSRV to 1.59 +# Used only in +# imgui-examples -> glium -> glutin -> wayland-client -> scoped-tls +# so not worth bumping MSRV for this basically to keep CI happy +# FIXME: Remove this for imgui-rs v0.10 and bump MSRV +scoped-tls = "=1.0.0" diff --git a/imgui-examples/examples/long_list.rs b/imgui-examples/examples/long_list.rs index 2204c53..e4b019a 100644 --- a/imgui-examples/examples/long_list.rs +++ b/imgui-examples/examples/long_list.rs @@ -15,8 +15,10 @@ fn main() { let system = support::init(file!()); system.main_loop(move |_, ui| { + // Show the C++ style API ui.window("Hello long world") - .size([300.0, 110.0], Condition::FirstUseEver) + .size([100.0, 500.0], Condition::FirstUseEver) + .position([10.0, 10.0], crate::Condition::Always) .build(|| { let mut clipper = imgui::ListClipper::new(lots_of_words.len() as i32) .items_height(ui.current_font_size()) @@ -27,5 +29,18 @@ fn main() { } } }); + + // Show the more Rust'y iterator + ui.window("Hello long world (iterator API)") + .size([100.0, 500.0], Condition::FirstUseEver) + .position([150.0, 10.0], crate::Condition::Always) + .build(|| { + let clipper = imgui::ListClipper::new(lots_of_words.len() as i32) + .items_height(ui.current_font_size()) + .begin(ui); + for row_num in clipper.iter() { + ui.text(&lots_of_words[row_num as usize]); + } + }); }); } diff --git a/imgui-examples/examples/long_table.rs b/imgui-examples/examples/long_table.rs new file mode 100644 index 0000000..c610ef1 --- /dev/null +++ b/imgui-examples/examples/long_table.rs @@ -0,0 +1,52 @@ +use imgui::*; + +mod support; + +fn main() { + let system = support::init(file!()); + + system.main_loop(move |_, ui| { + ui.show_demo_window(&mut true); + + ui.window("Table with list clipper") + .size([800.0, 700.0], Condition::FirstUseEver) + .build(|| { + let num_cols = 3; + let num_rows = 1000; + + let flags = imgui::TableFlags::ROW_BG + | imgui::TableFlags::RESIZABLE + | imgui::TableFlags::BORDERS_H + | imgui::TableFlags::BORDERS_V; //| imgui::TableFlags::SCROLL_Y; + + if let Some(_t) = ui.begin_table_with_sizing( + "longtable", + num_cols, + flags, + [300.0, 100.0], + /*inner width=*/ 0.0, + ) { + ui.table_setup_column("A"); + ui.table_setup_column("B"); + ui.table_setup_column("C"); + + // Freeze first row so headers are visible even + // when scrolling + ui.table_setup_scroll_freeze(num_cols, 1); + + // Done with headers row + ui.table_headers_row(); + + // Create clipper with st + let clip = imgui::ListClipper::new(num_rows).begin(ui); + for row_num in clip.iter() { + ui.table_next_row(); + for col_num in 0..num_cols { + ui.table_set_column_index(col_num); + ui.text(format!("Hello {},{}", col_num, row_num)); + } + } + } + }); + }); +} diff --git a/imgui-examples/examples/test_window_impl.rs b/imgui-examples/examples/test_window_impl.rs index c89f068..ee9e2c0 100644 --- a/imgui-examples/examples/test_window_impl.rs +++ b/imgui-examples/examples/test_window_impl.rs @@ -53,6 +53,7 @@ struct State { stacked_modals_item: usize, stacked_modals_color: [f32; 4], app_log: Vec, + filter: imgui::TextFilter, tabs: TabState, } @@ -117,6 +118,7 @@ impl Default for State { stacked_modals_item: 0, stacked_modals_color: [0.4, 0.7, 0.0, 0.5], app_log: Vec::new(), + filter: TextFilter::new(String::from("Test")), tabs: TabState::default(), } } @@ -389,7 +391,8 @@ fn show_test_window(ui: &Ui, state: &mut State, opened: &mut bool) { } if CollapsingHeader::new("Widgets").build(ui) { if let Some(_t) = ui.tree_node("Tree") { - for i in 0..5 { + let num_child = 4; + for i in 0..num_child { if let Some(_t) = ui.tree_node(format!("Child {}", i)) { ui.text("blah blah"); ui.same_line(); @@ -398,6 +401,20 @@ fn show_test_window(ui: &Ui, state: &mut State, opened: &mut bool) { } } } + + { + let tree_node_stack = ui.tree_node_config("##HideTreeNodeLabel") + .allow_item_overlap(true) + .push(); + ui.same_line(); + if ui.small_button(format!("Child {} is a button", num_child)) { + println!("TreeNode Button pressed."); + } + + if tree_node_stack.is_some() { + ui.text("blah blah") + } + } } if let Some(_t) = ui.tree_node("Bullets") { @@ -732,6 +749,30 @@ CTRL+click on individual component to input value.\n", } } + if CollapsingHeader::new("Filtering").build(ui) { + ui.text_wrapped( + "Filter usage:\n\ + \"\" display all lines\n\ + \"xxx\" display lines containing \"xxx\"\n\ + \"xxx,yyy\" display lines containing \"xxx\" or \"yyy\"\n\ + \"-xxx\" hide lines containing \"xxx\"" + ); + + state.filter.draw(); + let lines = vec!["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") { + state.filter.clear(); + } + + for i in lines.iter() { + if state.filter.pass_filter(i) { + ui.bullet_text(i); + } + } + } + if CollapsingHeader::new("Popups & Modal windows").build(ui) { if let Some(_t) = ui.tree_node("Popups") { ui.text_wrapped( diff --git a/imgui-glium-renderer/Cargo.toml b/imgui-glium-renderer/Cargo.toml index 574b93a..6338966 100644 --- a/imgui-glium-renderer/Cargo.toml +++ b/imgui-glium-renderer/Cargo.toml @@ -1,14 +1,14 @@ [package] name = "imgui-glium-renderer" -version = "0.8.1-alpha.0" +version = "0.9.0" edition = "2018" -authors = ["The imgui-rs Developers"] 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" categories = ["gui", "rendering"] [dependencies] -glium = { version = "0.31", default-features = false } -imgui = { version = "0.8.1-alpha.0", path = "../imgui" } +glium = { version = "0.32.1", default-features = false } +imgui = { version = "0.9.0", path = "../imgui" } diff --git a/imgui-glium-renderer/src/lib.rs b/imgui-glium-renderer/src/lib.rs index eddc659..8cddd55 100644 --- a/imgui-glium-renderer/src/lib.rs +++ b/imgui-glium-renderer/src/lib.rs @@ -117,18 +117,21 @@ impl glium::vertex::Vertex for GliumDrawVert { ( Borrowed("pos"), 0, + -1, glium::vertex::AttributeType::F32F32, false, ), ( Borrowed("uv"), 8, + -1, glium::vertex::AttributeType::F32F32, false, ), ( Borrowed("col"), 16, + -1, glium::vertex::AttributeType::U8U8U8U8, false, ), diff --git a/imgui-glow-renderer/Cargo.toml b/imgui-glow-renderer/Cargo.toml index a0df2bd..297ecad 100644 --- a/imgui-glow-renderer/Cargo.toml +++ b/imgui-glow-renderer/Cargo.toml @@ -1,8 +1,7 @@ [package] name = "imgui-glow-renderer" -version = "0.8.1-alpha.0" +version = "0.9.0" edition = "2018" -authors = ["jmaargh and the imgui-rs Developers"] description = "glow renderer for the imgui crate" homepage = "https://github.com/imgui-rs/imgui-rs" repository = "https://github.com/imgui-rs/imgui-rs" @@ -10,13 +9,13 @@ license = "MIT/Apache-2.0" categories = ["gui", "rendering"] [dependencies] -imgui = { version = "0.8.1-alpha.0", path = "../imgui" } +imgui = { version = "0.9.0", path = "../imgui" } glow = "0.10.0" memoffset = "0.6.4" [dev-dependencies] -glutin = "0.28.0" -imgui-winit-support = { version = "0.8.1-alpha.0", path = "../imgui-winit-support" } +glutin = "0.29.1" +imgui-winit-support = { version = "0.9.0", path = "../imgui-winit-support" } image = "0.23" [features] diff --git a/imgui-glow-renderer/src/lib.rs b/imgui-glow-renderer/src/lib.rs index a6be20d..1f3f3cb 100644 --- a/imgui-glow-renderer/src/lib.rs +++ b/imgui-glow-renderer/src/lib.rs @@ -26,7 +26,10 @@ //! Consider this an example renderer. It is intended to be sufficent for simple //! applications running imgui-rs as the final rendering step. If your application //! has more specific needs, it's probably best to write your own renderer, in -//! which case this can be a useful starting point. +//! which case this can be a useful starting point. This renderer is also not +//! foolproof (largely due to the global nature of the OpenGL state). For example, +//! a few "internal" functions are marked `pub` to allow the user more +//! fine-grained control at the cost of allowing potential rendering errors. //! //! # sRGB //! @@ -47,6 +50,9 @@ use std::{borrow::Cow, error::Error, fmt::Display, mem::size_of}; use imgui::{internal::RawWrapper, DrawCmd, DrawData, DrawVert}; use crate::versions::{GlVersion, GlslVersion}; + +// Re-export glow to make it easier for users to use the correct version. +pub use glow; use glow::{Context, HasContext}; pub mod versions; @@ -276,6 +282,16 @@ impl Renderer { gl_debug_message(gl, "imgui-rs-glow: start render"); self.state_backup.pre_render(gl, self.gl_version); + #[cfg(feature = "bind_vertex_array_support")] + if self.gl_version.bind_vertex_array_support() { + unsafe { + self.vertex_array_object = gl + .create_vertex_array() + .map_err(|err| format!("Error creating vertex array object: {}", err))?; + gl.bind_vertex_array(Some(self.vertex_array_object)); + } + } + self.set_up_render_state(gl, draw_data, fb_width, fb_height)?; gl_debug_message(gl, "start loop over draw lists"); @@ -396,16 +412,6 @@ impl Renderer { unsafe { gl.bind_sampler(0, None) }; } - #[cfg(feature = "bind_vertex_array_support")] - if self.gl_version.bind_vertex_array_support() { - unsafe { - self.vertex_array_object = gl - .create_vertex_array() - .map_err(|err| format!("Error creating vertex array object: {}", err))?; - gl.bind_vertex_array(Some(self.vertex_array_object)); - } - } - // TODO: soon it should be possible for these to be `const` functions let position_field_offset = memoffset::offset_of!(DrawVert, pos) as _; let uv_field_offset = memoffset::offset_of!(DrawVert, uv) as _; diff --git a/imgui-sdl2-support/Cargo.toml b/imgui-sdl2-support/Cargo.toml index dfbc76a..c760612 100644 --- a/imgui-sdl2-support/Cargo.toml +++ b/imgui-sdl2-support/Cargo.toml @@ -1,19 +1,20 @@ [package] name = "imgui-sdl2-support" -version = "0.8.1-alpha.0" +version = "0.9.0" edition = "2018" 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" categories = ["gui"] [dependencies] -imgui = { version = "0.8.1-alpha.0", path = "../imgui" } +imgui = { version = "0.9.0", path = "../imgui" } sdl2 = "0.34.5" [dev-dependencies] glow = "0.10.0" -imgui-glow-renderer = { version = "0.8.1-alpha.0", path = "../imgui-glow-renderer" } +imgui-glow-renderer = { version = "0.9.0", path = "../imgui-glow-renderer" } sdl2 = { version = "0.34.5", features = ["bundled", "static-link"] } diff --git a/imgui-sdl2-support/src/lib.rs b/imgui-sdl2-support/src/lib.rs index edf8c6b..7baf8f4 100644 --- a/imgui-sdl2-support/src/lib.rs +++ b/imgui-sdl2-support/src/lib.rs @@ -167,25 +167,29 @@ impl SdlPlatform { /// /// * keyboard state is updated /// * mouse state is updated - pub fn handle_event(&mut self, context: &mut Context, event: &Event) { + pub fn handle_event(&mut self, context: &mut Context, event: &Event) -> bool { let io = context.io_mut(); match *event { Event::MouseWheel { x, y, .. } => { io.mouse_wheel = y as f32; io.mouse_wheel_h = x as f32; + true } Event::MouseButtonDown { mouse_btn, .. } => { self.handle_mouse_button(&mouse_btn, true); + true } Event::MouseButtonUp { mouse_btn, .. } => { self.handle_mouse_button(&mouse_btn, false); + true } Event::TextInput { ref text, .. } => { text.chars().for_each(|c| io.add_input_character(c)); + true } Event::KeyDown { @@ -195,6 +199,7 @@ impl SdlPlatform { } => { io.keys_down[key as usize] = true; handle_key_modifier(io, &keymod); + true } Event::KeyUp { @@ -204,9 +209,10 @@ impl SdlPlatform { } => { io.keys_down[key as usize] = false; handle_key_modifier(io, &keymod); + true } - _ => {} + _ => false, } } diff --git a/imgui-sys/Cargo.toml b/imgui-sys/Cargo.toml index 1a7605f..6923859 100644 --- a/imgui-sys/Cargo.toml +++ b/imgui-sys/Cargo.toml @@ -1,17 +1,29 @@ [package] name = "imgui-sys" -version = "0.8.1-alpha.0" +version = "0.9.0" edition = "2018" -authors = ["The imgui-rs Developers"] 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" categories = ["gui", "external-ffi-bindings"] build = "build.rs" links = "imgui" -# exclude .json, .lua from imgui dirs - they are intermediate artifacts from cimgui generator -exclude = ["third-party/imgui-*/*.json", "third-party/imgui-*/*.lua"] + +# exclude json, lua, and the imgui subdirs (imgui/examples, imgui/docs, etc) +# ..but we need imgui/misc/freetype/ for the freetype feature +exclude = [ + "third-party/*.json", + "third-party/*.lua", + "third-party/imgui/backends/", + "third-party/imgui/docs/", + "third-party/imgui/examples/", + "third-party/imgui/misc/cpp/", + "third-party/imgui/misc/debuggers/", + "third-party/imgui/misc/fonts/", + "third-party/imgui/misc/single_file/", +] [dependencies] chlorine = "1.0.7" diff --git a/imgui-winit-support/Cargo.toml b/imgui-winit-support/Cargo.toml index cae6bd3..7e62be1 100644 --- a/imgui-winit-support/Cargo.toml +++ b/imgui-winit-support/Cargo.toml @@ -1,32 +1,17 @@ [package] name = "imgui-winit-support" -version = "0.8.1-alpha.0" +version = "0.9.0" edition = "2018" -authors = ["The imgui-rs Developers"] 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" categories = ["gui"] [dependencies] -imgui = { version = "0.8.1-alpha.0", path = "../imgui" } -winit-19 = { version = ">= 0.16, < 0.20", package = "winit", optional = true } -winit-20 = { version = ">= 0.20, < 0.22", package = "winit", optional = true } -winit-22 = { version = "0.22", package = "winit", optional = true } -winit-23 = { version = "0.23", package = "winit", default-features = false, optional = true } -winit-24 = { version = "0.24", package = "winit", default-features = false, optional = true } -winit-25 = { version = "0.25", package = "winit", default-features = false, optional = true } -winit-26 = { version = "0.26", package = "winit", default-features = false, optional = true } +imgui = { version = "0.9.0", path = "../imgui" } +winit = { version = "0.27.2", default-features = false } [features] -default = ["winit-26/default"] -test = ["winit-23/default", "winit-24/default", "winit-25/default", "winit-26/default"] - viewports = [ "imgui/docking" ] - -# This is phrased as a negative (unlike most features) so that it needs to be -# explicitly disabled (and `default-features = false` won't do it). To avoid -# problems from this we don't expose this in the public API in any way, keeping -# things additive. -no-warn-on-multiple = [] diff --git a/imgui-winit-support/UPDATING_WINIT.md b/imgui-winit-support/UPDATING_WINIT.md deleted file mode 100644 index 9ff786d..0000000 --- a/imgui-winit-support/UPDATING_WINIT.md +++ /dev/null @@ -1,8 +0,0 @@ -# Updating Winit - -Updating the default version of Winit is very annoying and error prone. We should make some automated way to do it so we don't have any issues in the future, but here is what needs to be done: - -1. Make sure that glutin is on the new version of Winit that you want to use. It's easier if our default winit version is on that new default. -2. Update the default in the Cargo.toml by simply changing the default guard. -3. At the top of lib.rs, edit the CFG guards which handle `use winit_x as winit;` such that the new default only relies on a positive feature guard (just copy the form used for the old default). If you don't do this, you'll get some particularly strange errors about two crates being used, since somehow the dependency resolver will pick a winit that the user didn't choose (and presumably won't use if it actually made it to compilation). -4. Profit?? diff --git a/imgui-winit-support/src/lib.rs b/imgui-winit-support/src/lib.rs index 3e484e5..e63cb56 100644 --- a/imgui-winit-support/src/lib.rs +++ b/imgui-winit-support/src/lib.rs @@ -13,16 +13,15 @@ //! 4. Call frame preparation callback (every frame) //! 5. Call render preparation callback (every frame) //! -//! ## Complete example for winit 0.20+ (without a renderer) +//! ## Complete example (without a renderer) //! -//! ```rust,no_run,ignore -//! # // TODO: Remove ignore when only one winit version is used +//! ```no_run //! use imgui::Context; //! use imgui_winit_support::{HiDpiMode, WinitPlatform}; //! use std::time::Instant; //! use winit::event::{Event, WindowEvent}; //! use winit::event_loop::{ControlFlow, EventLoop}; -//! use winit::window::{Window}; +//! use winit::window::Window; //! //! let mut event_loop = EventLoop::new(); //! let mut window = Window::new(&event_loop).unwrap(); @@ -39,7 +38,9 @@ //! match event { //! Event::NewEvents(_) => { //! // other application-specific logic -//! last_frame = imgui.io_mut().update_delta_time(last_frame); +//! let now = Instant::now(); +//! imgui.io_mut().update_delta_time(now - last_frame); +//! last_frame = now; //! }, //! Event::MainEventsCleared => { //! // other application-specific logic @@ -55,7 +56,7 @@ //! //! platform.prepare_render(&ui, &window); // step 5 //! // render the UI with a renderer -//! let draw_data = ui.render(); +//! let draw_data = imgui.render(); //! // renderer.render(..., draw_data).expect("UI rendering failed"); //! //! // application-specific rendering *over the UI* @@ -71,152 +72,15 @@ //! } //! }) //! ``` -//! -//! ## `winit` versions and features. -//! -//! This crate has several features which control the version of winit which is -//! used. -//! -//! The following versions are supported, controlled by the listed feature. -//! -//! - The `winit-26` feature uses winit versions compatible with `0.26`. This is -//! on by default, so to use any other version you need to disable this crates -//! default features. -//! - The `winit-25` feature supports winit versions `0.25`. -//! - The `winit-24` feature supports winit versions `0.24`. -//! - The `winit-23` feature uses winit versions compatible with `0.23`. -//! - The `winit-22` feature uses winit versions compatible with `0.22`. -//! - The `winit-20` feature should support winit either `0.20` or winit `0.21`. -//! - The `winit-19` feature should support winits older than `0.19` (possibly -//! back to winit 0.16.*, but this isn't regularly tested and may not work). -//! -//! If multiple `winit-*` features are enabled, and it is a debug build (as -//! determined by `debug_assertions`), we will log a warning to stderr during -//! init. This can be disabled by either turning on the `no-warn-on-multiple` -//! feature, fixing the configuration, or disabling `debug_assertions`. -//! -//! Conversely, if no `winit-*` features are enabled, we will fail to compile. -//! This is not an issue generally, as by default we turn on `winit-26`. -//! -//! All of this is in attempt to preserve the additive nature of features (while -//! still helping users notice project configuration issues), however it's done -//! fairly weakly as our this crate's API isn't 100% identical across winit -//! versions. -//! -//! ### Using an older `winit` version -//! -//! To use an older version, you must configure `default-features = false` in -//! your `Cargo.toml`: -//! -//! ```toml -//! [dependencies.imgui-winit-support] -//! version = "0.6" -//! features = ["winit-$YOUR_VERSION_HERE"] -//! default-features = false -//! ``` -//! -//! ### Old `winit` compatibility -//! -//! No guarantee is made on how long this crate will support legacy versions of -//! `winit`, but we'll try to follow these rules: -//! -//! - Versions which are still in widespread use in the ecosystem will be -//! supported while that is true (for example, 0.19 at the time of writing is -//! quite old, but used by the most recent version of several popular crates). -//! -//! - Versions which are not a significant maintenance burden will be supported -//! (for example, supporting versions older than winit 0.19 given that we -//! support 0.19). -//! -//! - Explicitly removing support for a feature-indicated version will be -//! considered a breaking change. -//! -//! - Changing the default feature to the new latest `winit` version is *not* a -//! breaking change. - -#[cfg(feature = "winit-26")] -use winit_26 as winit; - -#[cfg(all(not(any(feature = "winit-26")), feature = "winit-25"))] -use winit_25 as winit; - -#[cfg(all( - not(any(feature = "winit-26", feature = "winit-25")), - feature = "winit-24" -))] -use winit_24 as winit; - -#[cfg(all( - not(any(feature = "winit-26", feature = "winit-25", feature = "winit-24")), - feature = "winit-23" -))] -use winit_23 as winit; - -#[cfg(all( - not(any( - feature = "winit-26", - feature = "winit-25", - feature = "winit-24", - feature = "winit-23" - )), - feature = "winit-22", -))] -use winit_22 as winit; - -#[cfg(all( - not(any( - feature = "winit-26", - feature = "winit-25", - feature = "winit-24", - feature = "winit-23", - feature = "winit-22" - )), - feature = "winit-20", -))] -use winit_20 as winit; - -#[cfg(all( - not(any( - feature = "winit-26", - feature = "winit-25", - feature = "winit-24", - feature = "winit-23", - feature = "winit-22", - feature = "winit-20" - )), - feature = "winit-19", -))] -use winit_19 as winit; use imgui::{self, BackendFlags, ConfigFlags, Context, Io, Key, Ui}; use std::cell::Cell; 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}; -#[cfg(all( - not(any( - feature = "winit-26", - feature = "winit-25", - feature = "winit-24", - feature = "winit-23", - feature = "winit-22", - feature = "winit-20" - )), - feature = "winit-19", -))] -use winit::{ - DeviceEvent, ElementState, Event, KeyboardInput, MouseButton, MouseCursor, MouseScrollDelta, - TouchPhase, VirtualKeyCode, Window, WindowEvent, -}; - -#[cfg(any( - feature = "winit-26", - feature = "winit-25", - feature = "winit-24", - feature = "winit-23", - feature = "winit-22", - feature = "winit-20" -))] use winit::{ error::ExternalError, event::{ @@ -226,83 +90,6 @@ use winit::{ window::{CursorIcon as MouseCursor, Window}, }; -// Ensure at least one is enabled -#[cfg(not(any( - feature = "winit-19", - feature = "winit-20", - feature = "winit-22", - feature = "winit-23", - feature = "winit-24", - feature = "winit-25", - feature = "winit-26", -)))] -compile_error!("Please enable at least one version of `winit` (see documentation for details)."); - -// FIXME(thom): make updading winit and adding a new feature less of a hassle here. -fn check_multiple_winits() { - use std::io::Write as _; - use std::sync::atomic::{AtomicBool, Ordering}; - // bail out for release builds or if we've been explicitly disabled. - if cfg!(any(not(debug_assertions), feature = "no-warn-on-multiple")) { - return; - } - let winits_enabled = cfg!(feature = "winit-26") as usize - + cfg!(feature = "winit-25") as usize - + cfg!(feature = "winit-24") as usize - + cfg!(feature = "winit-23") as usize - + cfg!(feature = "winit-22") as usize - + cfg!(feature = "winit-20") as usize - + cfg!(feature = "winit-19") as usize; - - // Only complain once even if we're called multiple times. - static COMPLAINED: AtomicBool = AtomicBool::new(false); - // Note that the `Ordering` basically doesn't matter here, but even if it - // did, `Relaxed` is still correct because we're only interested in the - // effects on a single atomic variable. - if winits_enabled <= 1 - || COMPLAINED - .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed) - .is_err() - { - return; - } - let mut err = Vec::with_capacity(512); - - // Log the complaint into a buffer first — in practice this is enough to - // ensure atomicity. - let _ = writeln!( - err, - "Warning (imgui-winit-support): More than one `winit-*` version feature is enabled \ - (this likely indicates misconfiguration, see documentation for details)." - ); - let feats = [ - ("winit-26", cfg!(feature = "winit-26"), " (default)"), - ("winit-25", cfg!(feature = "winit-25"), ""), - ("winit-24", cfg!(feature = "winit-24"), ""), - ("winit-23", cfg!(feature = "winit-23"), ""), - ("winit-22", cfg!(feature = "winit-22"), ""), - ("winit-20", cfg!(feature = "winit-20"), ""), - ("winit-19", cfg!(feature = "winit-19"), ""), - ]; - for &(name, enabled, extra) in &feats { - if enabled { - let _ = writeln!(err, " `feature = {:?}` is enabled{}", name, extra); - } - } - if cfg!(feature = "winit-25") && winits_enabled == 2 { - let _ = writeln!( - err, - " Perhaps you are missing a `default-features = false`?", - ); - } - let _ = writeln!( - err, - " (Note: This warning is only present in debug builds, and \ - can be disabled using the \"no-warn-on-multiple\" feature)" - ); - let _ = std::io::stderr().write_all(&err); -} - /// State of a single mouse button. Used so that we can detect cases where mouse /// press and release occur on the same frame (seems surprisingly frequent on /// macOS now...) @@ -365,34 +152,6 @@ fn to_winit_cursor(cursor: imgui::MouseCursor) -> MouseCursor { } impl CursorSettings { - #[cfg(all( - not(any( - feature = "winit-26", - feature = "winit-25", - feature = "winit-24", - feature = "winit-23", - feature = "winit-22", - feature = "winit-20" - )), - feature = "winit-19", - ))] - fn apply(&self, window: &Window) { - match self.cursor { - Some(mouse_cursor) if !self.draw_cursor => { - window.hide_cursor(false); - window.set_cursor(to_winit_cursor(mouse_cursor)); - } - _ => window.hide_cursor(true), - } - } - #[cfg(any( - feature = "winit-20", - feature = "winit-22", - feature = "winit-23", - feature = "winit-24", - feature = "winit-25", - feature = "winit-26" - ))] fn apply(&self, window: &Window) { match self.cursor { Some(mouse_cursor) if !self.draw_cursor => { @@ -571,8 +330,6 @@ impl WinitPlatform { /// * keys are configured /// * platform name is set pub fn init(imgui: &mut Context) -> WinitPlatform { - // noop in non-debug builds, if disabled, or if called a second time. - check_multiple_winits(); let io = imgui.io_mut(); io.backend_flags.insert(BackendFlags::HAS_MOUSE_CURSORS); io.backend_flags.insert(BackendFlags::HAS_SET_MOUSE_POS); @@ -718,41 +475,6 @@ impl WinitPlatform { /// /// * framebuffer scale (= DPI factor) is set /// * display size is set - #[cfg(all( - not(any( - feature = "winit-26", - feature = "winit-25", - feature = "winit-24", - feature = "winit-23", - feature = "winit-22", - feature = "winit-20" - )), - feature = "winit-19", - ))] - pub fn attach_window(&mut self, io: &mut Io, window: &Window, hidpi_mode: HiDpiMode) { - let (hidpi_mode, hidpi_factor) = hidpi_mode.apply(window.get_hidpi_factor()); - self.hidpi_mode = hidpi_mode; - self.hidpi_factor = hidpi_factor; - io.display_framebuffer_scale = [hidpi_factor as f32, hidpi_factor as f32]; - if let Some(logical_size) = window.get_inner_size() { - let logical_size = self.scale_size_from_winit(window, logical_size); - io.display_size = [logical_size.width as f32, logical_size.height as f32]; - } - } - /// Attaches the platform instance to a winit window. - /// - /// This function configures imgui-rs in the following ways: - /// - /// * framebuffer scale (= DPI factor) is set - /// * display size is set - #[cfg(any( - feature = "winit-20", - feature = "winit-22", - feature = "winit-23", - feature = "winit-24", - feature = "winit-25", - feature = "winit-26", - ))] pub fn attach_window(&mut self, io: &mut Io, window: &Window, hidpi_mode: HiDpiMode) { let (hidpi_mode, hidpi_factor) = hidpi_mode.apply(window.scale_factor()); self.hidpi_mode = hidpi_mode; @@ -772,37 +494,6 @@ impl WinitPlatform { /// /// This utility function is useful if you are using a DPI mode other than default, and want /// your application to use the same logical coordinates as imgui-rs. - #[cfg(all( - not(any( - feature = "winit-26", - feature = "winit-25", - feature = "winit-24", - feature = "winit-23", - feature = "winit-22", - feature = "winit-20" - )), - feature = "winit-19", - ))] - pub fn scale_size_from_winit(&self, window: &Window, logical_size: LogicalSize) -> LogicalSize { - match self.hidpi_mode { - ActiveHiDpiMode::Default => logical_size, - _ => logical_size - .to_physical(window.get_hidpi_factor()) - .to_logical(self.hidpi_factor), - } - } - /// Scales a logical size coming from winit using the current DPI mode. - /// - /// This utility function is useful if you are using a DPI mode other than default, and want - /// your application to use the same logical coordinates as imgui-rs. - #[cfg(any( - feature = "winit-20", - feature = "winit-22", - feature = "winit-23", - feature = "winit-24", - feature = "winit-25", - feature = "winit-26" - ))] pub fn scale_size_from_winit( &self, window: &Window, @@ -819,41 +510,6 @@ impl WinitPlatform { /// /// This utility function is useful if you are using a DPI mode other than default, and want /// your application to use the same logical coordinates as imgui-rs. - #[cfg(all( - not(any( - feature = "winit-26", - feature = "winit-25", - feature = "winit-24", - feature = "winit-23", - feature = "winit-22", - feature = "winit-20" - )), - feature = "winit-19", - ))] - pub fn scale_pos_from_winit( - &self, - window: &Window, - logical_pos: LogicalPosition, - ) -> LogicalPosition { - match self.hidpi_mode { - ActiveHiDpiMode::Default => logical_pos, - _ => logical_pos - .to_physical(window.get_hidpi_factor()) - .to_logical(self.hidpi_factor), - } - } - /// Scales a logical position coming from winit using the current DPI mode. - /// - /// This utility function is useful if you are using a DPI mode other than default, and want - /// your application to use the same logical coordinates as imgui-rs. - #[cfg(any( - feature = "winit-20", - feature = "winit-22", - feature = "winit-23", - feature = "winit-24", - feature = "winit-25", - feature = "winit-26" - ))] pub fn scale_pos_from_winit( &self, window: &Window, @@ -870,41 +526,6 @@ impl WinitPlatform { /// /// This utility function is useful if you are using a DPI mode other than default, and want /// your application to use the same logical coordinates as imgui-rs. - #[cfg(all( - not(any( - feature = "winit-26", - feature = "winit-25", - feature = "winit-24", - feature = "winit-23", - feature = "winit-22", - feature = "winit-20" - )), - feature = "winit-19", - ))] - pub fn scale_pos_for_winit( - &self, - window: &Window, - logical_pos: LogicalPosition, - ) -> LogicalPosition { - match self.hidpi_mode { - ActiveHiDpiMode::Default => logical_pos, - _ => logical_pos - .to_physical(self.hidpi_factor) - .to_logical(window.get_hidpi_factor()), - } - } - /// Scales a logical position for winit using the current DPI mode. - /// - /// This utility function is useful if you are using a DPI mode other than default, and want - /// your application to use the same logical coordinates as imgui-rs. - #[cfg(any( - feature = "winit-20", - feature = "winit-22", - feature = "winit-23", - feature = "winit-24", - feature = "winit-25", - feature = "winit-26" - ))] pub fn scale_pos_for_winit( &self, window: &Window, @@ -924,116 +545,6 @@ impl WinitPlatform { /// * window size / dpi factor changes are applied /// * keyboard state is updated /// * mouse state is updated - #[cfg(all( - not(any( - feature = "winit-26", - feature = "winit-25", - feature = "winit-24", - feature = "winit-23", - feature = "winit-22", - feature = "winit-20" - )), - feature = "winit-19", - ))] - pub fn handle_event(&mut self, io: &mut Io, window: &Window, event: &Event) { - match *event { - Event::WindowEvent { - window_id, - ref event, - } if window_id == window.id() => { - self.handle_window_event(io, window, event); - } - // 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), - .. - }), - .. - } => { - io.keys_down[key as usize] = false; - match key { - VirtualKeyCode::LShift | VirtualKeyCode::RShift => io.key_shift = false, - VirtualKeyCode::LControl | VirtualKeyCode::RControl => io.key_ctrl = false, - VirtualKeyCode::LAlt | VirtualKeyCode::RAlt => io.key_alt = false, - VirtualKeyCode::LWin | VirtualKeyCode::RWin => io.key_super = false, - _ => (), - } - } - _ => (), - } - } - /// Handles a winit event. - /// - /// This function performs the following actions (depends on the event): - /// - /// * window size / dpi factor changes are applied - /// * keyboard state is updated - /// * mouse state is updated - #[cfg(all( - not(any( - feature = "winit-26", - feature = "winit-25", - feature = "winit-24", - feature = "winit-23", - feature = "winit-22" - )), - feature = "winit-20", - ))] - pub fn handle_event(&mut self, io: &mut Io, window: &Window, event: &Event) { - match *event { - Event::WindowEvent { - window_id, - ref event, - } if window_id == window.id() => { - self.handle_window_event(io, window, event); - } - // 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), - .. - }), - .. - } => { - io.keys_down[key as usize] = false; - } - - // 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! - Event::DeviceEvent { - event: DeviceEvent::ModifiersChanged(modifiers), - .. - } => { - io.key_shift = modifiers.shift(); - io.key_ctrl = modifiers.ctrl(); - io.key_alt = modifiers.alt(); - io.key_super = modifiers.logo(); - } - _ => (), - } - } - /// Handles a winit event. - /// - /// This function performs the following actions (depends on the event): - /// - /// * window size / dpi factor changes are applied - /// * keyboard state is updated - /// * mouse state is updated - #[cfg(any( - feature = "winit-22", - feature = "winit-23", - feature = "winit-24", - feature = "winit-25", - feature = "winit-26", - ))] pub fn handle_event(&mut self, io: &mut Io, window: &Window, event: &Event) { match *event { Event::WindowEvent { @@ -1068,6 +579,7 @@ impl WinitPlatform { _ => (), } } + #[cfg(feature = "viewports")] pub fn handle_viewport_event( &mut self, @@ -1213,221 +725,6 @@ impl WinitPlatform { } } - #[cfg(all( - not(any( - feature = "winit-26", - feature = "winit-25", - feature = "winit-24", - feature = "winit-23", - feature = "winit-22", - feature = "winit-20" - )), - feature = "winit-19", - ))] - fn handle_window_event(&mut self, io: &mut Io, window: &Window, event: &WindowEvent) { - match *event { - WindowEvent::Resized(logical_size) => { - let logical_size = self.scale_size_from_winit(window, logical_size); - io.display_size = [logical_size.width as f32, logical_size.height as f32]; - } - WindowEvent::HiDpiFactorChanged(scale) => { - let hidpi_factor = match self.hidpi_mode { - ActiveHiDpiMode::Default => scale, - ActiveHiDpiMode::Rounded => scale.round(), - _ => return, - }; - // Mouse position needs to be changed while we still have both the old and the new - // values - if io.mouse_pos[0].is_finite() && io.mouse_pos[1].is_finite() { - io.mouse_pos = [ - io.mouse_pos[0] * (hidpi_factor / self.hidpi_factor) as f32, - io.mouse_pos[1] * (hidpi_factor / self.hidpi_factor) as f32, - ]; - } - self.hidpi_factor = hidpi_factor; - io.display_framebuffer_scale = [hidpi_factor as f32, hidpi_factor as f32]; - // Window size might change too if we are using DPI rounding - if let Some(logical_size) = window.get_inner_size() { - let logical_size = self.scale_size_from_winit(window, logical_size); - io.display_size = [logical_size.width as f32, logical_size.height as f32]; - } - } - WindowEvent::KeyboardInput { - input: - KeyboardInput { - virtual_keycode: Some(key), - state, - .. - }, - .. - } => { - io.keys_down[key as usize] = state == ElementState::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 = self.scale_pos_from_winit(window, position); - io.mouse_pos = [position.x as f32, position.y as f32]; - } - WindowEvent::MouseWheel { - delta, - phase: TouchPhase::Moved, - .. - } => match delta { - MouseScrollDelta::LineDelta(h, v) => { - io.mouse_wheel_h = h; - io.mouse_wheel = v; - } - MouseScrollDelta::PixelDelta(pos) => { - match pos.x.partial_cmp(&0.0) { - Some(Ordering::Greater) => io.mouse_wheel_h += 1.0, - Some(Ordering::Less) => io.mouse_wheel_h -= 1.0, - _ => (), - } - match pos.y.partial_cmp(&0.0) { - Some(Ordering::Greater) => io.mouse_wheel += 1.0, - Some(Ordering::Less) => io.mouse_wheel -= 1.0, - _ => (), - } - } - }, - WindowEvent::MouseInput { state, button, .. } => { - let pressed = state == ElementState::Pressed; - match button { - MouseButton::Left => self.mouse_buttons[0].set(pressed), - MouseButton::Right => self.mouse_buttons[1].set(pressed), - MouseButton::Middle => self.mouse_buttons[2].set(pressed), - MouseButton::Other(idx @ 0..=4) => { - self.mouse_buttons[idx as usize].set(pressed) - } - _ => (), - } - } - _ => (), - } - } - #[cfg(all( - not(any( - feature = "winit-23", - feature = "winit-24", - feature = "winit-25", - feature = "winit-26" - )), - any(feature = "winit-20", feature = "winit-22") - ))] - fn handle_window_event(&mut self, io: &mut Io, window: &Window, event: &WindowEvent) { - match *event { - WindowEvent::Resized(physical_size) => { - let logical_size = physical_size.to_logical(window.scale_factor()); - let logical_size = self.scale_size_from_winit(window, logical_size); - io.display_size = [logical_size.width as f32, logical_size.height as f32]; - } - WindowEvent::ScaleFactorChanged { scale_factor, .. } => { - let hidpi_factor = match self.hidpi_mode { - ActiveHiDpiMode::Default => scale_factor, - ActiveHiDpiMode::Rounded => scale_factor.round(), - _ => return, - }; - // Mouse position needs to be changed while we still have both the old and the new - // values - if io.mouse_pos[0].is_finite() && io.mouse_pos[1].is_finite() { - io.mouse_pos = [ - io.mouse_pos[0] * (hidpi_factor / self.hidpi_factor) as f32, - io.mouse_pos[1] * (hidpi_factor / self.hidpi_factor) as f32, - ]; - } - self.hidpi_factor = hidpi_factor; - io.display_framebuffer_scale = [hidpi_factor as f32, hidpi_factor as f32]; - // Window size might change too if we are using DPI rounding - let logical_size = window.inner_size().to_logical(scale_factor); - let logical_size = self.scale_size_from_winit(window, logical_size); - io.display_size = [logical_size.width as f32, logical_size.height as f32]; - } - WindowEvent::KeyboardInput { - input: - KeyboardInput { - virtual_keycode: Some(key), - state, - .. - }, - .. - } => { - let pressed = state == ElementState::Pressed; - io.keys_down[key as usize] = pressed; - - // This is a bit redundant here, but we'll leave it in. The OS occasionally - // fails to send modifiers keys, but it doesn't seem to send false-positives, - // so double checking isn't terrible in case some system *doesn't* send - // device events sometimes. - match key { - VirtualKeyCode::LShift | VirtualKeyCode::RShift => io.key_shift = pressed, - VirtualKeyCode::LControl | VirtualKeyCode::RControl => io.key_ctrl = pressed, - VirtualKeyCode::LAlt | VirtualKeyCode::RAlt => io.key_alt = pressed, - VirtualKeyCode::LWin | VirtualKeyCode::RWin => io.key_super = 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); - io.mouse_pos = [position.x as f32, position.y as f32]; - } - WindowEvent::MouseWheel { - delta, - phase: TouchPhase::Moved, - .. - } => match delta { - MouseScrollDelta::LineDelta(h, v) => { - io.mouse_wheel_h = h; - io.mouse_wheel = v; - } - MouseScrollDelta::PixelDelta(pos) => { - match pos.x.partial_cmp(&0.0) { - Some(Ordering::Greater) => io.mouse_wheel_h += 1.0, - Some(Ordering::Less) => io.mouse_wheel_h -= 1.0, - _ => (), - } - match pos.y.partial_cmp(&0.0) { - Some(Ordering::Greater) => io.mouse_wheel += 1.0, - Some(Ordering::Less) => io.mouse_wheel -= 1.0, - _ => (), - } - } - }, - WindowEvent::MouseInput { state, button, .. } => { - let pressed = state == ElementState::Pressed; - match button { - MouseButton::Left => self.mouse_buttons[0].set(pressed), - MouseButton::Right => self.mouse_buttons[1].set(pressed), - MouseButton::Middle => self.mouse_buttons[2].set(pressed), - MouseButton::Other(idx @ 0..=4) => { - self.mouse_buttons[idx as usize].set(pressed) - } - _ => (), - } - } - _ => (), - } - } - - #[cfg(any( - feature = "winit-23", - feature = "winit-24", - feature = "winit-25", - feature = "winit-26" - ))] fn handle_window_event(&mut self, io: &mut Io, window: &Window, event: &WindowEvent) { match *event { WindowEvent::Resized(physical_size) => { @@ -1527,6 +824,13 @@ impl WinitPlatform { _ => (), } } + WindowEvent::Focused(newly_focused) => { + if !newly_focused { + // Set focus-lost to avoid stuck keys (like 'alt' + // when alt-tabbing) + io.app_focus_lost = true; + } + } _ => (), } } @@ -1536,43 +840,6 @@ impl WinitPlatform { /// This function performs the following actions: /// /// * mouse cursor is repositioned (if requested by imgui-rs) - #[cfg(all( - not(any( - feature = "winit-26", - feature = "winit-25", - feature = "winit-24", - feature = "winit-23", - feature = "winit-22", - feature = "winit-20" - )), - feature = "winit-19", - ))] - pub fn prepare_frame(&self, io: &mut Io, window: &Window) -> Result<(), String> { - self.copy_mouse_to_io(&mut io.mouse_down); - if io.want_set_mouse_pos { - let logical_pos = self.scale_pos_for_winit( - window, - LogicalPosition::new(f64::from(io.mouse_pos[0]), f64::from(io.mouse_pos[1])), - ); - window.set_cursor_position(logical_pos) - } else { - Ok(()) - } - } - /// Frame preparation callback. - /// - /// Call this before calling the imgui-rs context `frame` function. - /// This function performs the following actions: - /// - /// * mouse cursor is repositioned (if requested by imgui-rs) - #[cfg(any( - feature = "winit-20", - feature = "winit-22", - feature = "winit-23", - feature = "winit-24", - feature = "winit-25", - feature = "winit-26", - ))] pub fn prepare_frame(&self, io: &mut Io, window: &Window) -> Result<(), ExternalError> { self.copy_mouse_to_io(&mut io.mouse_down); if io.want_set_mouse_pos { diff --git a/imgui/Cargo.toml b/imgui/Cargo.toml index 33fcead..7bbe1a3 100644 --- a/imgui/Cargo.toml +++ b/imgui/Cargo.toml @@ -1,11 +1,11 @@ [package] name = "imgui" -version = "0.8.1-alpha.0" +version = "0.9.0" edition = "2018" -authors = ["The imgui-rs Developers"] 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" categories = ["gui", "api-bindings"] readme = "../README.markdown" @@ -17,9 +17,9 @@ features = ["freetype", "docking", "tables-api"] [dependencies] bitflags = "1" -imgui-sys = { version = "0.8.1-alpha.0", path = "../imgui-sys" } +imgui-sys = { version = "0.9.0", path = "../imgui-sys" } mint = "0.5.6" -parking_lot = "0.11" +parking_lot = "0.12" cfg-if = "1" [features] diff --git a/imgui/src/draw_list.rs b/imgui/src/draw_list.rs index 4da1248..0180061 100644 --- a/imgui/src/draw_list.rs +++ b/imgui/src/draw_list.rs @@ -426,7 +426,7 @@ impl<'ui> DrawListMut<'ui> { /// # use imgui::*; /// fn custom_button(ui: &Ui, img_id: TextureId) { /// // Invisible button is good widget to customise with image - /// ui.invisible_button(im_str!("custom_button"), [100.0, 20.0]); + /// ui.invisible_button("custom_button", [100.0, 20.0]); /// /// // Get draw list and draw image over invisible button /// let draw_list = ui.get_window_draw_list(); diff --git a/imgui/src/input_widget.rs b/imgui/src/input_widget.rs index 59e8c29..043e5ef 100644 --- a/imgui/src/input_widget.rs +++ b/imgui/src/input_widget.rs @@ -1051,7 +1051,20 @@ impl TextCallbackData { /// This Range is given in `usize` so that it might be used in indexing /// operations more easily. To quickly grab the selected text, use [selected](Self::selected). pub fn selection(&self) -> Range { - unsafe { (*(self.0)).SelectionStart as usize..(*(self.0)).SelectionEnd as usize } + let (start, end) = unsafe { + ( + (*(self.0)).SelectionStart as usize, + (*(self.0)).SelectionEnd as usize, + ) + }; + // Avoid returning a range with start > end, which would be problematic. For example, it + // would cause panics when used to index the string buffer and would also cause Self::has_selection + // to return a false negative. + if start < end { + start..end + } else { + end..start + } } /// Returns the selected text directly. Note that if no text is selected, diff --git a/imgui/src/io.rs b/imgui/src/io.rs index 9f5bece..b6ce023 100644 --- a/imgui/src/io.rs +++ b/imgui/src/io.rs @@ -369,7 +369,11 @@ pub struct Io { nav_inputs_down_duration: [f32; NavInput::COUNT + NavInput::INTERNAL_COUNT], nav_inputs_down_duration_prev: [f32; NavInput::COUNT + NavInput::INTERNAL_COUNT], pen_pressure: f32, - app_focus_lost: bool, + + /// Clear buttons state when focus is lost (this is useful so + /// e.g. releasing Alt after focus loss on Alt-Tab doesn't trigger + /// the Alt menu toggle) + pub app_focus_lost: bool, input_queue_surrogate: sys::ImWchar16, input_queue_characters: ImVector, } diff --git a/imgui/src/lib.rs b/imgui/src/lib.rs index 14bd06e..a353efe 100644 --- a/imgui/src/lib.rs +++ b/imgui/src/lib.rs @@ -37,6 +37,7 @@ pub use self::style::*; #[cfg(feature = "tables-api")] pub use self::tables::*; +pub use self::text_filter::*; pub use self::utils::*; pub use self::widget::color_editors::*; pub use self::widget::combo_box::*; @@ -89,6 +90,7 @@ mod style; mod tables; #[cfg(test)] mod test; +pub mod text_filter; mod utils; mod widget; mod window; @@ -465,6 +467,7 @@ impl Ui { // Widgets: Input impl<'ui> Ui { + /// Edits text in a single line input widget #[doc(alias = "InputText", alias = "InputTextWithHint")] pub fn input_text<'p, L: AsRef>( &'ui self, @@ -473,6 +476,8 @@ impl<'ui> Ui { ) -> InputText<'ui, 'p, L> { InputText::new(self, label, buf) } + + /// Edits text in a multi line widget #[doc(alias = "InputText", alias = "InputTextMultiline")] pub fn input_text_multiline<'p, L: AsRef>( &'ui self, @@ -482,6 +487,8 @@ impl<'ui> Ui { ) -> InputTextMultiline<'ui, 'p, L> { InputTextMultiline::new(self, label, buf, size) } + + /// Simple floating point number widget #[doc(alias = "InputFloat2")] pub fn input_float<'p, L: AsRef>( &'ui self, @@ -618,7 +625,7 @@ impl Ui { /// ui.text("Hover over me"); /// if ui.is_item_hovered() { /// ui.tooltip(|| { - /// ui.text_colored([1.0, 0.0, 0.0, 1.0], im_str!("I'm red!")); + /// ui.text_colored([1.0, 0.0, 0.0, 1.0], "I'm red!"); /// }); /// } /// } @@ -685,7 +692,7 @@ impl Ui { /// fn user_interface(ui: &Ui) { /// let disable_buttons = true; /// let _d = ui.begin_disabled(disable_buttons); - /// ui.button(im_str!("Dangerous button")); + /// ui.button("Dangerous button"); /// } /// ``` @@ -712,7 +719,7 @@ impl Ui { /// fn user_interface(ui: &Ui) { /// let safe_mode = true; /// ui.disabled(safe_mode, || { - /// ui.button(im_str!("Dangerous button")); + /// ui.button("Dangerous button"); /// }); /// } /// ``` @@ -727,9 +734,7 @@ impl Ui { /// [`Ui::begin_enabled`]. #[doc(alias = "BeginDisabled", alias = "EndDisabled")] pub fn enabled(&self, enabled: bool, f: F) { - unsafe { sys::igBeginDisabled(!enabled) }; - f(); - unsafe { sys::igEndDisabled() }; + self.disabled(!enabled, f) } } @@ -802,6 +807,7 @@ impl Ui { } impl<'ui> Ui { + /// Plot a list of floats as a "sparkline" style plot #[doc(alias = "PlotLines")] pub fn plot_lines<'p, Label: AsRef>( &'ui self, @@ -811,6 +817,7 @@ impl<'ui> Ui { PlotLines::new(self, label, values) } + /// Plot a list of floats as a histogram #[doc(alias = "PlotHistogram")] pub fn plot_histogram<'p, Label: AsRef>( &'ui self, @@ -863,7 +870,11 @@ impl<'ui> Ui { /// # Draw list for custom drawing impl Ui { - /// Get access to drawing API + /// Get access to drawing API. + /// + /// The window draw list draws within the current + /// window. Coordinates are within the current window coordinates, + /// so `[0.0, 0.0]` would be at beginning of window /// /// # Examples /// @@ -898,12 +909,24 @@ impl Ui { DrawListMut::window(self) } + /// Get draw list to draw behind all windows + /// + /// Coordinates are in window coordinates, so `[0.0, 0.0]` is at + /// top left of the Dear ImGui window + /// + /// See [`Self::get_window_draw_list`] for more details #[must_use] #[doc(alias = "GetBackgroundDrawList")] pub fn get_background_draw_list(&self) -> DrawListMut<'_> { DrawListMut::background(self) } + /// Get draw list instance to draw above all window content + /// + /// Coordinates are in window coordinates, so `[0.0, 0.0]` is at + /// top left of the Dear ImGui window + /// + /// See [`Self::get_window_draw_list`] for more details #[must_use] #[doc(alias = "GetForegroundDrawList")] pub fn get_foreground_draw_list(&self) -> DrawListMut<'_> { diff --git a/imgui/src/list_clipper.rs b/imgui/src/list_clipper.rs index cfe4ff5..a7c000c 100644 --- a/imgui/src/list_clipper.rs +++ b/imgui/src/list_clipper.rs @@ -1,5 +1,4 @@ use std::marker::PhantomData; -use std::thread; use crate::sys; use crate::Ui; @@ -22,6 +21,7 @@ pub struct ListClipper { } impl ListClipper { + /// Begins configuring a list clipper. pub const fn new(items_count: i32) -> Self { ListClipper { items_count, @@ -45,9 +45,23 @@ impl ListClipper { } } +/// List clipper is a mechanism to efficiently implement scrolling of +/// large lists with random access. +/// +/// For example you have a list of 1 million buttons, and the list +/// clipper will help you only draw the ones which are visible. pub struct ListClipperToken<'ui> { list_clipper: *mut sys::ImGuiListClipper, _phantom: PhantomData<&'ui Ui>, + + /// In upstream imgui < 1.87, calling step too many times will + /// cause a segfault due to null pointer. So we keep track of this + /// and panic instead. + /// + /// Fixed in https://github.com/ocornut/imgui/commit/dca527b which + /// will likely be part of imgui 1.88 - at which point this can be + /// removed. + consumed_workaround: bool, } impl<'ui> ListClipperToken<'ui> { @@ -55,40 +69,202 @@ impl<'ui> ListClipperToken<'ui> { Self { list_clipper, _phantom: PhantomData, + consumed_workaround: false, } } + /// Progress the list clipper. + /// + /// If this returns returns `true` then the you can loop between + /// between `clipper.display_start() .. clipper.display_end()`. + /// If this returns false, you must stop calling this method. + /// + /// Calling step again after it returns `false` will cause imgui + /// to abort. This mirrors the C++ interface. + /// + /// It is recommended to use the iterator interface! pub fn step(&mut self) -> bool { - unsafe { sys::ImGuiListClipper_Step(self.list_clipper) } + let is_imgui_1_88_or_higher = false; + if is_imgui_1_88_or_higher { + unsafe { sys::ImGuiListClipper_Step(self.list_clipper) } + } else { + if self.consumed_workaround { + panic!("ListClipperToken::step called after it has previously returned false"); + } + let ret = unsafe { sys::ImGuiListClipper_Step(self.list_clipper) }; + if !ret { + self.consumed_workaround = true; + } + ret + } } + /// This is automatically called back the final call to + /// `step`. You can call it sooner but typically not needed. pub fn end(&mut self) { unsafe { sys::ImGuiListClipper_End(self.list_clipper); } } + /// First item to call, updated each call to `step` pub fn display_start(&self) -> i32 { unsafe { (*self.list_clipper).DisplayStart } } + /// End of items to call (exclusive), updated each call to `step` pub fn display_end(&self) -> i32 { unsafe { (*self.list_clipper).DisplayEnd } } + + /// Get an iterator which outputs all visible indexes. This is the + /// recommended way of using the clipper. + pub fn iter(self) -> ListClipperIterator<'ui> { + ListClipperIterator::new(self) + } } impl<'ui> Drop for ListClipperToken<'ui> { fn drop(&mut self) { - if !self.step() { - unsafe { - sys::ImGuiListClipper_destroy(self.list_clipper); - }; - } else if !thread::panicking() { - panic!( - "Forgot to call End(), or to Step() until false? \ - This is the only token in the repository which users must call `.end()` or `.step()` \ - with. See https://github.com/imgui-rs/imgui-rs/issues/438" - ); + unsafe { + sys::ImGuiListClipper_destroy(self.list_clipper); + }; + } +} + +pub struct ListClipperIterator<'ui> { + list_clipper: ListClipperToken<'ui>, + exhausted: bool, + last_value: Option, +} + +impl<'ui> ListClipperIterator<'ui> { + fn new(list_clipper: ListClipperToken<'ui>) -> Self { + Self { + list_clipper, + exhausted: false, + last_value: None, } } } + +impl Iterator for ListClipperIterator<'_> { + type Item = i32; + + fn next(&mut self) -> Option { + if let Some(lv) = self.last_value { + // Currently iterating a chunk (returning all values + // between display_start and display_end) + let next_value = lv + 1; + + if lv >= self.list_clipper.display_end() - 1 { + // If we reach the end of the current chunk, clear + // last_value so we call step below + self.last_value = None; + } else { + // Otherwise just increment it + self.last_value = Some(next_value); + } + } + + if let Some(lv) = self.last_value { + // Next item within current step's chunk + Some(lv) + } else { + // Start iterating a new chunk + + if self.exhausted { + // If the clipper is exhausted, don't call step again! + None + } else { + // Advance the clipper + let ret = self.list_clipper.step(); + if !ret { + self.exhausted = true; + None + } else { + // Setup iteration for this step's chunk + let start = self.list_clipper.display_start(); + let end = self.list_clipper.display_end(); + + if start == end { + // Somewhat special case: if a single item, we + // don't store the last_value so we call + // step() again next iteration + self.last_value = None; + } else { + self.last_value = Some(start); + } + Some(start) + } + } + } + } +} + +#[test] +fn cpp_style_usage() { + // Setup + let (_guard, mut ctx) = crate::test::test_ctx_initialized(); + let ui = ctx.frame(); + + let _window = ui + .window("Example") + .position([0.0, 0.0], crate::Condition::Always) + .size([100.0, 800.0], crate::Condition::Always) + .begin(); + + // Create clipper + let clip = ListClipper::new(1000); + let mut tok = clip.begin(ui); + + let mut ticks = 0; + + while dbg!(tok.step()) { + for row_num in dbg!(tok.display_start())..dbg!(tok.display_end()) { + dbg!(row_num); + ui.text("..."); + ticks += 1; + } + } + + // Check it's called an expected amount of time (only the ones + // visible in given sized window) + assert_eq!(ticks, 44); + + // Calling end multiple times is fine albeit redundant + tok.end(); + tok.end(); + tok.end(); + tok.end(); + tok.end(); + tok.end(); +} + +#[test] +fn iterator_usage() { + // Setup + let (_guard, mut ctx) = crate::test::test_ctx_initialized(); + let ui = ctx.frame(); + + let _window = ui + .window("Example") + .position([0.0, 0.0], crate::Condition::Always) + .size([100.0, 800.0], crate::Condition::Always) + .begin(); + + // Create clipper + let clip = ListClipper::new(1000); + + let mut ticks = 0; + + let tok = clip.begin(ui); + for row_num in tok.iter() { + dbg!(row_num); + ui.text("..."); + ticks += 1; + } + + // Should be consistent with size in `cpp_style_usage` + assert_eq!(ticks, 44); +} diff --git a/imgui/src/stacks.rs b/imgui/src/stacks.rs index a256c59..81a0bb1 100644 --- a/imgui/src/stacks.rs +++ b/imgui/src/stacks.rs @@ -179,7 +179,8 @@ unsafe fn push_style_var(style_var: StyleVar) { impl Ui { /// Changes the item width by pushing a change to the item width stack. /// - /// Returns an `ItemWidthStackToken` that may be popped by calling `.pop()` + /// Returns an `ItemWidthStackToken`. The pushed width item is popped when either + /// `ItemWidthStackToken` goes out of scope, or `.end()` is called. /// /// - `> 0.0`: width is `item_width` pixels /// - `= 0.0`: default to ~2/3 of window width @@ -208,13 +209,14 @@ impl Ui { unsafe { sys::igCalcItemWidth() } } - /// Changes the text wrapping position to the end of window (or column), which - /// is generally the default. + /// Makes the text wrap at the end of window/column (which is generally the default), by + /// pushing a change to the text wrapping position stack. /// /// This is the same as calling [push_text_wrap_pos_with_pos](Self::push_text_wrap_pos_with_pos) /// with `wrap_pos_x` set to 0.0. /// - /// Returns a `TextWrapPosStackToken` that may be popped by calling `.pop()` + /// Returns a `TextWrapPosStackToken`. The pushed position item is popped when either + /// `TextWrapPosStackToken` goes out of scope, or `.end()` is called. #[doc(alias = "PushTextWrapPos")] pub fn push_text_wrap_pos(&self) -> TextWrapPosStackToken<'_> { self.push_text_wrap_pos_with_pos(0.0) @@ -222,7 +224,8 @@ impl Ui { /// Changes the text wrapping position by pushing a change to the text wrapping position stack. /// - /// Returns a `TextWrapPosStackToken` that may be popped by calling `.pop()` + /// Returns a `TextWrapPosStackToken`. The pushed position item is popped when either + /// `TextWrapPosStackToken` goes out of scope, or `.end()` is called. /// /// - `> 0.0`: wrap at `wrap_pos_x` position in window local space /// - `= 0.0`: wrap to end of window (or column) diff --git a/imgui/src/string.rs b/imgui/src/string.rs index af6557b..fdd0858 100644 --- a/imgui/src/string.rs +++ b/imgui/src/string.rs @@ -74,36 +74,6 @@ impl UiBuffer { } } -#[macro_export] -#[deprecated = "all functions take AsRef now -- use inline strings or `format` instead"] -macro_rules! im_str { - ($e:literal $(,)?) => {{ - const __INPUT: &str = concat!($e, "\0"); - { - // Trigger a compile error if there's an interior NUL character. - const _CHECK_NUL: [(); 0] = [(); { - let bytes = __INPUT.as_bytes(); - let mut i = 0; - let mut found_nul = 0; - while i < bytes.len() - 1 && found_nul == 0 { - if bytes[i] == 0 { - found_nul = 1; - } - i += 1; - } - found_nul - }]; - const RESULT: &'static $crate::ImStr = unsafe { - $crate::__core::mem::transmute::<&'static [u8], &'static $crate::ImStr>(__INPUT.as_bytes()) - }; - RESULT - } - }}; - ($e:literal, $($arg:tt)+) => ({ - $crate::ImString::new(format!($e, $($arg)*)) - }); -} - /// A UTF-8 encoded, growable, implicitly nul-terminated string. #[derive(Clone, Hash, Ord, Eq, PartialOrd, PartialEq)] pub struct ImString(pub(crate) Vec); diff --git a/imgui/src/text_filter.rs b/imgui/src/text_filter.rs new file mode 100644 index 0000000..b64ebf9 --- /dev/null +++ b/imgui/src/text_filter.rs @@ -0,0 +1,106 @@ +use crate::sys; +use crate::Ui; +use std::ptr; + +/// Helper to parse and apply text filters +pub struct TextFilter { + id: String, + raw: *mut sys::ImGuiTextFilter, +} + +impl TextFilter { + /// Creates a new TextFilter with an empty filter. + /// + /// This is equivalent of [new_with_filter](Self::new_with_filter) with `filter` set to `""`. + pub fn new(label: String) -> Self { + Self::new_with_filter(label, String::new()) + } + + /// Creates a new TextFilter with a custom filter. + pub fn new_with_filter(label: String, mut filter: String) -> Self { + filter.push('\0'); + let ptr = filter.as_mut_ptr(); + Self { + id: label, + raw: unsafe { sys::ImGuiTextFilter_ImGuiTextFilter(ptr as *mut sys::cty::c_char) }, + } + } + + /// Builds the TextFilter with its filter attribute. You can use + /// [`pass_filter()`](Self::pass_filter) after it. + /// + /// If you want control the filter with an InputText, check [`draw()`](Self::draw). + pub fn build(&self) { + unsafe { + sys::ImGuiTextFilter_Build(self.raw); + } + } + + /// Draws an [InputText](crate::input_widget::InputText) to control the filter of the TextFilter. + /// + /// This is equivalent of [draw_with_size](Self::draw_with_size) with `size` set to `0.0`. + pub fn draw(&self) { + self.draw_with_size(0.0); + } + + /// Draws an [InputText](crate::input_widget::InputText) to control the filter of the TextFilter. + /// + /// The InputText has the size passed in parameters. + pub fn draw_with_size(&self, size: f32) { + unsafe { + let mut id = self.id.clone(); + id.push('\0'); + let ptr = id.as_mut_ptr(); + sys::ImGuiTextFilter_Draw(self.raw, ptr as *mut sys::cty::c_char, size); + } + } + + /// Returns true if the filter is not empty (`""`). + pub fn is_active(&self) -> bool { + unsafe { sys::ImGuiTextFilter_IsActive(self.raw) } + } + + /// Returns true if the buffer matches the filter. + /// + /// [`draw()`](Self::draw) or [`build()`](Self::build) mut be called **before** this function. + pub fn pass_filter(&self, buf: &str) -> bool { + let mut buf = String::from(buf); + buf.push('\0'); + let ptr = buf.as_mut_ptr(); + unsafe { + sys::ImGuiTextFilter_PassFilter(self.raw, ptr as *mut sys::cty::c_char, ptr::null()) + } + } + + pub fn pass_filter_with_end(&self, start: &str, end: &str) -> bool { + let (mut start, mut end) = (String::from(start), String::from(end)); + start.push('\0'); + end.push('\0'); + let b_ptr = start.as_mut_ptr(); + let e_ptr = end.as_mut_ptr(); + unsafe { + sys::ImGuiTextFilter_PassFilter( + self.raw, + b_ptr as *mut sys::cty::c_char, + e_ptr as *mut sys::cty::c_char, + ) + } + } + + /// Clears the filter. + pub fn clear(&self) { + unsafe { + sys::ImGuiTextFilter_Clear(self.raw); + } + } +} + +impl Ui { + pub fn text_filter(label: String) -> TextFilter { + TextFilter::new(label) + } + + pub fn text_filter_with_filter(label: String, filter: String) -> TextFilter { + TextFilter::new_with_filter(label, filter) + } +} diff --git a/imgui/src/widget/drag.rs b/imgui/src/widget/drag.rs index af25ca1..1c550b8 100644 --- a/imgui/src/widget/drag.rs +++ b/imgui/src/widget/drag.rs @@ -253,7 +253,6 @@ where #[doc(alias = "DragIntRange2")] pub fn build(self, ui: &Ui, min: &mut i32, max: &mut i32) -> bool { unsafe { - let label; let mut display_format = std::ptr::null(); let mut max_display_format = std::ptr::null(); @@ -261,7 +260,7 @@ where let buffer = &mut *ui.scratch_buffer().get(); buffer.refresh_buffer(); - label = buffer.push(self.label); + let label = buffer.push(self.label); if let Some(v) = self.display_format { display_format = buffer.push(v); } diff --git a/imgui/src/widget/slider.rs b/imgui/src/widget/slider.rs index 9da91d3..6ddec0d 100644 --- a/imgui/src/widget/slider.rs +++ b/imgui/src/widget/slider.rs @@ -196,8 +196,7 @@ where /// Constructs a new vertical slider builder with the given size and range. /// /// ```rust - /// # use imgui::im_str; - /// imgui::VerticalSlider::new(im_str!("Example"), [20.0, 20.0], i8::MIN, i8::MAX) + /// imgui::VerticalSlider::new("Example", [20.0, 20.0], i8::MIN, i8::MAX) /// .range(4, 8) /// // Remember to call .build(&ui) /// ; @@ -227,8 +226,7 @@ where /// Sets the range for the vertical slider. /// /// ```rust - /// # use imgui::im_str; - /// imgui::VerticalSlider::new(im_str!("Example"), [20.0, 20.0], i8::MIN, i8::MAX) + /// imgui::VerticalSlider::new("Example", [20.0, 20.0], i8::MIN, i8::MAX) /// .range(4, 8) /// // Remember to call .build(&ui) /// ; @@ -320,8 +318,7 @@ where { /// Sets the range in degrees (inclusive) /// ```rust - /// # use imgui::im_str; - /// imgui::AngleSlider::new(im_str!("Example")) + /// imgui::AngleSlider::new("Example") /// .range_degrees(-20.0, 20.0) /// // Remember to call .build(&ui) /// ; diff --git a/imgui/src/widget/tab.rs b/imgui/src/widget/tab.rs index 54b43a0..9bdf48d 100644 --- a/imgui/src/widget/tab.rs +++ b/imgui/src/widget/tab.rs @@ -6,12 +6,12 @@ //! # let ui = ctx.frame(); //! //! // During UI construction -//! TabBar::new(im_str!("tabbar")).build(&ui, || { -//! TabItem::new(im_str!("a tab")).build(&ui, || { -//! ui.text(im_str!("tab content 1")); +//! TabBar::new("tabbar").build(&ui, || { +//! TabItem::new("a tab").build(&ui, || { +//! ui.text("tab content 1"); //! }); -//! TabItem::new(im_str!("2tab")).build(&ui, || { -//! ui.text(im_str!("tab content 2")); +//! TabItem::new("2tab").build(&ui, || { +//! ui.text("tab content 2"); //! }); //! }); //! ``` diff --git a/imgui/src/widget/tree.rs b/imgui/src/widget/tree.rs index 5a707c8..118c93f 100644 --- a/imgui/src/widget/tree.rs +++ b/imgui/src/widget/tree.rs @@ -287,17 +287,16 @@ impl<'a, T: AsRef, L: AsRef> TreeNode<'a, T, L> { sys::igSetNextItemOpen(self.opened, self.opened_cond as i32); } match self.id { - TreeNodeId::Str(id) => { - let (id, label) = match self.label { - Some(label) => self.ui.scratch_txt_two(id, label), - None => { - let v = self.ui.scratch_txt(id); - (v, v) - } - }; - - sys::igTreeNodeExStrStr(id, self.flags.bits() as i32, fmt_ptr(), label) - } + TreeNodeId::Str(id) => match self.label { + Some(label) => { + let (id, label) = self.ui.scratch_txt_two(id, label); + sys::igTreeNodeExStrStr(id, self.flags.bits() as i32, fmt_ptr(), label) + } + None => { + let id = self.ui.scratch_txt(id); + sys::igTreeNodeExStr(id, self.flags.bits() as i32) + } + }, TreeNodeId::Ptr(id) => sys::igTreeNodeExPtr( id, self.flags.bits() as i32, diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index 355e524..df9a5c7 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -2,7 +2,6 @@ [package] name = "xtask" version = "0.1.0" -authors = ["Thom Chiovoloni "] edition = "2018" publish = false diff --git a/xtask/src/bindgen.rs b/xtask/src/bindgen.rs index 1beb900..1272f25 100644 --- a/xtask/src/bindgen.rs +++ b/xtask/src/bindgen.rs @@ -1,5 +1,5 @@ use crate::flags::Bindgen; -use anyhow::{anyhow, Context, Result}; +use anyhow::{anyhow, bail, Context, Result}; use std::path::{Path, PathBuf}; impl Bindgen { @@ -140,7 +140,7 @@ fn generate_binding_file( eprintln!("Executing bindgen [output = {}]", output.display()); let status = cmd.status().context("Failed to execute bindgen")?; if !status.success() { - anyhow!( + bail!( "Failed to execute bindgen: {}, see output for details", status ); diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 1445554..38beb16 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -32,30 +32,11 @@ fn try_main() -> Result<()> { } fn lint_all() -> Result<()> { - // Lint with only default, only docking, and only freetype + // Lint with only default, only docking, and only freetype, and everything xshell::cmd!("cargo clippy --workspace --all-targets").run()?; xshell::cmd!("cargo clippy --workspace --all-targets --features docking").run()?; xshell::cmd!("cargo clippy --workspace --all-targets --features freetype").run()?; - - // Lint winit with all features - xshell::cmd!( - "cargo clippy --manifest-path imgui-winit-support/Cargo.toml --all-features --all-targets" - ) - .run()?; - - // Lint with various winit versions - let winits = &[ - "winit-19", - "winit-20", - "winit-22", - "winit-23/default", - "winit-24/default", - "winit-25/default", - "winit-26/default", - ]; - for &winit in winits { - xshell::cmd!("cargo clippy --manifest-path imgui-winit-support/Cargo.toml --no-default-features --features {winit} --all-targets").run()?; - } + xshell::cmd!("cargo clippy --workspace --all-targets --all-features").run()?; // Check formatting xshell::cmd!("cargo fmt --all -- --check").run()?; @@ -71,18 +52,6 @@ fn test_all() -> Result<()> { // Test doc examples xshell::cmd!("cargo test --workspace --doc").run()?; - // Test with various winit versions - let winits = &[ - "winit-19", - "winit-20", - "winit-22", - "winit-23/default", - "winit-24/default", - "winit-25/default", - ]; - for &winit in winits { - xshell::cmd!("cargo test --manifest-path imgui-winit-support/Cargo.toml --no-default-features --features {winit} --all-targets").run()?; - } // Run heavy tests in release mode xshell::cmd!("cargo test -p imgui --release -- --ignored").run()?; Ok(())