diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f24874e..8b6f951 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: steps: - name: Checkout uses: actions/checkout@v2 - with: {submodules: true} + with: { submodules: true } - uses: hecrj/setup-rust-action@v1 with: components: clippy @@ -65,7 +65,7 @@ jobs: steps: - name: Checkout uses: actions/checkout@v2 - with: {submodules: true} + with: { submodules: true } - uses: hecrj/setup-rust-action@v1 with: components: rustfmt @@ -134,6 +134,8 @@ jobs: ${{ runner.os }}-target-test-${{ matrix.rust }}- - run: cargo test --workspace --all-targets - run: cargo test --workspace --doc + # run to check for lint problems + - run: cargo doc # - run: cargo test --manifest-path imgui-gfx-examples/Cargo.toml --no-default-features --features directx # if: runner.os == 'Windows' - run: cargo test --manifest-path imgui-winit-support/Cargo.toml --no-default-features --features winit-19 diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown index ec23e4e..bb3564a 100644 --- a/CHANGELOG.markdown +++ b/CHANGELOG.markdown @@ -4,6 +4,11 @@ - BREAKING: MSRV is now **1.54**. This is gives us access to min-const-generics, which we use in a few places, but will gradually use more. Because this is the first time we've bumped MSRV intentionally, we have added a new feature `min-const-generics`, which is _enabled by default_. If you are pre-1.54, you can hang onto this update by disabling that feature. In our next update, this feature will be removed and we will commit to our MSRVs going forward. +- **Removed ImStr and ImString from the API.** Currently `im_str!` is deprecated and **will be removed in 0.9.0**. To change your code: + + - If you were just wrapping a string literal, like `im_str!("button")`, just use `"button"`. + - If you were formatting, like `&im_str!("My age is {}", 100)`, you can now just use format like `format!("My age is {}, 100)`. Notice that due to the trait bounds, you can pass the string in directly too. + - Removed automatically adding default features for `imgui-winit-support` with the exception of the current default winit feature/dep version. Additionally, that version was updated to 0.25. If you want to not have the default features of winit with 0.25, set `default-features = false` and add `winit-25` as a normal feature. Thank you to @dzil123 for the work [implementing this here](https://github.com/imgui-rs/imgui-rs/pull/477)! diff --git a/README.markdown b/README.markdown index 8a198fc..52de4e3 100644 --- a/README.markdown +++ b/README.markdown @@ -10,12 +10,12 @@ ![Hello world](hello_world.png) ```rust -Window::new(im_str!("Hello world")) +Window::new("Hello world") .size([300.0, 100.0], Condition::FirstUseEver) .build(&ui, || { - ui.text(im_str!("Hello world!")); - ui.text(im_str!("こんにちは世界!")); - ui.text(im_str!("This...is...imgui-rs!")); + ui.text("Hello world!"); + ui.text("こんにちは世界!"); + ui.text("This...is...imgui-rs!"); ui.separator(); let mouse_pos = ui.io().mouse_pos; ui.text(format!( @@ -41,10 +41,6 @@ Window::new(im_str!("Hello world")) is not 100%, but will keep improving over time. - Builder structs for use cases where the original C++ library uses optional function parameters -- `&ImStr` / `ImString` types and `im_str!` macro for defining and passing - null-terminated UTF-8 to Dear ImGui, which doesn't accept Rust `&str` / - `String` values. See [issue #7](https://github.com/Gekkio/imgui-rs/issues/7) - for more information and justification for this design. - Easy integration with Glium / pre-ll gfx (renderer) - Easy integration with winit (backend platform) - Optional support for the freetype font rasterizer diff --git a/imgui-examples/examples/collapsing_header.rs b/imgui-examples/examples/collapsing_header.rs index 8c4e7c4..74873d7 100644 --- a/imgui-examples/examples/collapsing_header.rs +++ b/imgui-examples/examples/collapsing_header.rs @@ -8,19 +8,19 @@ fn main() { }; let system = support::init(file!()); system.main_loop(move |run, ui| { - let w = Window::new(im_str!("Collapsing header")) + let w = Window::new("Collapsing header") .opened(run) .position([20.0, 20.0], Condition::Appearing) .size([700.0, 500.0], Condition::Appearing); w.build(ui, || { - if CollapsingHeader::new(im_str!("I'm a collapsing header. Click me!")).build(ui) { + if CollapsingHeader::new("I'm a collapsing header. Click me!").build(ui) { ui.text( "A collapsing header can be used to toggle rendering of a group of widgets", ); } ui.spacing(); - if CollapsingHeader::new(im_str!("I'm open by default")) + if CollapsingHeader::new("I'm open by default") .default_open(true) .build(ui) { @@ -28,7 +28,7 @@ fn main() { } ui.spacing(); - if CollapsingHeader::new(im_str!("I only open with double-click")) + if CollapsingHeader::new("I only open with double-click") .open_on_double_click(true) .build(ui) { @@ -36,7 +36,7 @@ fn main() { } ui.spacing(); - if CollapsingHeader::new(im_str!("I don't have an arrow")) + if CollapsingHeader::new("I don't have an arrow") .bullet(true) .build(ui) { @@ -44,7 +44,7 @@ fn main() { } ui.spacing(); - if CollapsingHeader::new(im_str!("I only open if you click the arrow")) + if CollapsingHeader::new("I only open if you click the arrow") .open_on_arrow(true) .build(ui) { @@ -53,10 +53,10 @@ fn main() { ui.spacing(); ui.checkbox( - im_str!("Toggle rendering of the next example"), + "Toggle rendering of the next example", &mut state.render_closable, ); - if CollapsingHeader::new(im_str!("I've got a separate close button")) + if CollapsingHeader::new("I've got a separate close button") .build_with_close_button(ui, &mut state.render_closable) { ui.text("I've got contents just like any other collapsing header"); diff --git a/imgui-examples/examples/color_button.rs b/imgui-examples/examples/color_button.rs index 390b89d..5c19746 100644 --- a/imgui-examples/examples/color_button.rs +++ b/imgui-examples/examples/color_button.rs @@ -17,15 +17,15 @@ fn main() { } fn example_selector(run: &mut bool, ui: &mut Ui, state: &mut State) { - let w = Window::new(im_str!("Color button examples")) + let w = Window::new("Color button examples") .opened(run) .position([20.0, 20.0], Condition::Appearing) .size([700.0, 100.0], Condition::Appearing) .resizable(false); w.build(ui, || { - let ex1 = ui.radio_button(im_str!("Example 1: Basics"), &mut state.example, 1); - let ex2 = ui.radio_button(im_str!("Example 2: Alpha component"), &mut state.example, 2); - let ex3 = ui.radio_button(im_str!("Example 3: Input format"), &mut state.example, 3); + let ex1 = ui.radio_button("Example 1: Basics", &mut state.example, 1); + let ex2 = ui.radio_button("Example 2: Alpha component", &mut state.example, 2); + let ex3 = ui.radio_button("Example 3: Input format", &mut state.example, 3); if ex1 || ex2 || ex3 { state.reset(); } @@ -33,29 +33,29 @@ fn example_selector(run: &mut bool, ui: &mut Ui, state: &mut State) { } fn example_1(ui: &Ui, state: &mut State) { - let w = Window::new(im_str!("Example 1: Basics")) + let w = Window::new("Example 1: Basics") .size([700.0, 300.0], Condition::Appearing) .position([20.0, 140.0], Condition::Appearing); w.build(ui, || { - ui.text_wrapped(im_str!( + ui.text_wrapped( "Color button is a widget that displays a color value as a clickable rectangle. \ It also supports a tooltip with detailed information about the color value. \ - Try hovering over and clicking these buttons!" - )); + Try hovering over and clicking these buttons!", + ); ui.text(state.notify_text); ui.text("This button is black:"); - if ColorButton::new(im_str!("Black color"), [0.0, 0.0, 0.0, 1.0]).build(ui) { + if ColorButton::new("Black color", [0.0, 0.0, 0.0, 1.0]).build(ui) { state.notify_text = "*** Black button was clicked"; } ui.text("This button is red:"); - if ColorButton::new(im_str!("Red color"), [1.0, 0.0, 0.0, 1.0]).build(ui) { + if ColorButton::new("Red color", [1.0, 0.0, 0.0, 1.0]).build(ui) { state.notify_text = "*** Red button was clicked"; } ui.text("This button is BIG because it has a custom size:"); - if ColorButton::new(im_str!("Green color"), [0.0, 1.0, 0.0, 1.0]) + if ColorButton::new("Green color", [0.0, 1.0, 0.0, 1.0]) .size([100.0, 50.0]) .build(ui) { @@ -63,7 +63,7 @@ fn example_1(ui: &Ui, state: &mut State) { } ui.text("This button doesn't use the tooltip at all:"); - if ColorButton::new(im_str!("No tooltip"), [0.0, 0.0, 1.0, 1.0]) + if ColorButton::new("No tooltip", [0.0, 0.0, 1.0, 1.0]) .tooltip(false) .build(ui) { @@ -73,71 +73,66 @@ fn example_1(ui: &Ui, state: &mut State) { } fn example_2(ui: &Ui) { - let w = Window::new(im_str!("Example 2: Alpha component")) + let w = Window::new("Example 2: Alpha component") .size([700.0, 320.0], Condition::Appearing) .position([20.0, 140.0], Condition::Appearing); w.build(ui, || { - ui.text_wrapped(im_str!( + ui.text_wrapped( "The displayed color is passed to the button as four float values between \ 0.0 - 1.0 (RGBA). If you don't care about the alpha component, it can be \ - disabled and it won't show up in the tooltip" - )); + disabled and it won't show up in the tooltip", + ); ui.text("This button ignores the alpha component:"); - ColorButton::new(im_str!("Red color"), [1.0, 0.0, 0.0, 0.5]) + ColorButton::new("Red color", [1.0, 0.0, 0.0, 0.5]) .alpha(false) .build(ui); ui.spacing(); ui.spacing(); ui.spacing(); - ui.text_wrapped(im_str!( + ui.text_wrapped( "If you *do* care about the alpha component, you can choose how it's \ - displayed in the button and the tooltip" - )); + displayed in the button and the tooltip", + ); ui.separator(); - ui.text_wrapped(im_str!( - "ColorPreview::Opaque (default) doesn't show the alpha component at all" - )); - ColorButton::new(im_str!("Red + ColorPreview::Opaque"), [1.0, 0.0, 0.0, 0.5]) + ui.text_wrapped("ColorPreview::Opaque (default) doesn't show the alpha component at all"); + ColorButton::new("Red + ColorPreview::Opaque", [1.0, 0.0, 0.0, 0.5]) .preview(ColorPreview::Opaque) .build(ui); ui.separator(); - ui.text_wrapped(im_str!( + ui.text_wrapped( "ColorPreview::HalfAlpha divides the color area into two halves and uses a \ - checkerboard pattern in one half to illustrate the alpha component" - )); - ColorButton::new( - im_str!("Red + ColorPreview::HalfAlpha"), - [1.0, 0.0, 0.0, 0.5], - ) - .preview(ColorPreview::HalfAlpha) - .build(ui); + checkerboard pattern in one half to illustrate the alpha component", + ); + ColorButton::new("Red + ColorPreview::HalfAlpha", [1.0, 0.0, 0.0, 0.5]) + .preview(ColorPreview::HalfAlpha) + .build(ui); ui.separator(); - ui.text_wrapped(im_str!( + ui.text_wrapped( "ColorPreview::Alpha uses a checkerboard pattern in the entire color area to \ - illustrate the alpha component" - )); - ColorButton::new(im_str!("Red + ColorPreview::Alpha"), [1.0, 0.0, 0.0, 0.5]) + illustrate the alpha component", + ); + ColorButton::new("Red + ColorPreview::Alpha", [1.0, 0.0, 0.0, 0.5]) .preview(ColorPreview::Alpha) .build(ui); }); } fn example_3(ui: &Ui) { - let w = Window::new(im_str!("Example 3: Input format")) + let w = Window::new("Example 3: Input format") .size([700.0, 320.0], Condition::Appearing) .position([20.0, 140.0], Condition::Appearing); w.build(ui, || { ui.text("This button interprets the input value [1.0, 0.0, 0.0, 1.0] as RGB(A) (default):"); - ColorButton::new(im_str!("RGBA red"), [1.0, 0.0, 0.0, 1.0]).build(ui); + ColorButton::new("RGBA red", [1.0, 0.0, 0.0, 1.0]).build(ui); ui.separator(); ui.text("This button interprets the input value [1.0, 0.0, 0.0, 1.0] as HSV(A):"); - ColorButton::new(im_str!("HSVA black"), [1.0, 0.0, 0.0, 1.0]) + ColorButton::new("HSVA black", [1.0, 0.0, 0.0, 1.0]) .input_mode(ColorEditInputMode::HSV) .build(ui); }); diff --git a/imgui-examples/examples/custom_textures.rs b/imgui-examples/examples/custom_textures.rs index c1cce95..a76b03f 100644 --- a/imgui-examples/examples/custom_textures.rs +++ b/imgui-examples/examples/custom_textures.rs @@ -78,10 +78,10 @@ impl CustomTexturesApp { } fn show_textures(&self, ui: &Ui) { - Window::new(im_str!("Hello textures")) + Window::new("Hello textures") .size([400.0, 400.0], Condition::FirstUseEver) .build(ui, || { - ui.text(im_str!("Hello textures!")); + ui.text("Hello textures!"); if let Some(my_texture_id) = self.my_texture_id { ui.text("Some generated texture"); Image::new(my_texture_id, [100.0, 100.0]).build(ui); @@ -97,7 +97,7 @@ impl CustomTexturesApp { ui.text("The Lenna buttons"); { - ui.invisible_button(im_str!("Boring Button"), [100.0, 100.0]); + ui.invisible_button("Boring Button", [100.0, 100.0]); // See also `imgui::Ui::style_color` let tint_none = [1.0, 1.0, 1.0, 1.0]; let tint_green = [0.5, 1.0, 0.5, 1.0]; @@ -124,7 +124,7 @@ impl CustomTexturesApp { ui.same_line(); // Button using quad positioned image - ui.invisible_button(im_str!("Exciting Button"), [100.0, 100.0]); + ui.invisible_button("Exciting Button", [100.0, 100.0]); // Button bounds let min = ui.item_rect_min(); @@ -151,7 +151,7 @@ impl CustomTexturesApp { // Rounded image { ui.same_line(); - ui.invisible_button(im_str!("Smooth Button"), [100.0, 100.0]); + ui.invisible_button("Smooth Button", [100.0, 100.0]); let draw_list = ui.get_window_draw_list(); draw_list diff --git a/imgui-examples/examples/draw_list.rs b/imgui-examples/examples/draw_list.rs index 9d1a5b2..dfd9125 100644 --- a/imgui-examples/examples/draw_list.rs +++ b/imgui-examples/examples/draw_list.rs @@ -7,7 +7,7 @@ fn draw_text_centered( ui: &Ui, draw_list: &DrawListMut, rect: [f32; 4], - text: &ImStr, + text: &str, color: [f32; 3], ) { let text_size = ui.calc_text_size(text); @@ -37,7 +37,7 @@ fn main() { ui, &bg_draw_list, [0.0, 0.0, 300.0, 300.0], - im_str!("background draw list"), + "background draw list", [0.0, 0.0, 0.0], ); } @@ -52,16 +52,16 @@ fn main() { ui, &fg_draw_list, [w - 300.0, h - 300.0, 300.0, 300.0], - im_str!("foreground draw list"), + "foreground draw list", [1.0, 0.0, 0.0], ); } - Window::new(im_str!("Draw list")) + Window::new("Draw list") .size([300.0, 110.0], Condition::FirstUseEver) .scroll_bar(false) .build(ui, || { - ui.button(im_str!("random button")); + ui.button("random button"); let draw_list = ui.get_window_draw_list(); let o = ui.cursor_screen_pos(); let ws = ui.content_region_avail(); @@ -89,7 +89,7 @@ fn main() { ui, &draw_list, [o[0], o[1], ws[0], ws[1]], - im_str!("window draw list"), + "window draw list", [1.0, 1.0, 1.0], ); }); diff --git a/imgui-examples/examples/hello_world.rs b/imgui-examples/examples/hello_world.rs index 3e423a4..56eb774 100644 --- a/imgui-examples/examples/hello_world.rs +++ b/imgui-examples/examples/hello_world.rs @@ -4,13 +4,20 @@ mod support; fn main() { let system = support::init(file!()); + let mut value = 0; + let choices = ["test test this is 1", "test test this is 2"]; system.main_loop(move |_, ui| { - Window::new(im_str!("Hello world")) + Window::new("Hello world") .size([300.0, 110.0], Condition::FirstUseEver) .build(ui, || { - ui.text(im_str!("Hello world!")); - ui.text(im_str!("こんにちは世界!")); - ui.text(im_str!("This...is...imgui-rs!")); + ui.text_wrapped("Hello world!"); + ui.text_wrapped("こんにちは世界!"); + if ui.button(choices[value]) { + value += 1; + value %= 2; + } + + ui.button("This...is...imgui-rs!"); ui.separator(); let mouse_pos = ui.io().mouse_pos; ui.text(format!( diff --git a/imgui-examples/examples/keyboard.rs b/imgui-examples/examples/keyboard.rs index bf09785..d2d9c3f 100644 --- a/imgui-examples/examples/keyboard.rs +++ b/imgui-examples/examples/keyboard.rs @@ -11,10 +11,10 @@ fn main() { let mut uncaptured_counter = 0u32; let mut home_counter = 0u32; let mut f1_release_count = 0u32; - let mut text_buffer = ImString::new(""); + let mut text_buffer = String::new(); system.main_loop(move |_, ui| { - Window::new(im_str!("Means of accessing key state")) + Window::new("Means of accessing key state") .size([500.0, 300.0], Condition::FirstUseEver) .build(ui, || { // You can check if a key is currently held down @@ -81,12 +81,12 @@ fn main() { // example, if you try to type into this input, the // above interaction still counts the key presses. ui.input_text_multiline( - im_str!("##Dummy text input widget"), + "##Dummy text input widget", &mut text_buffer, [100.0, 100.0], ) // .do_not_resize() if you pass this, then this won't resize! - // .hint(im_str!("Example text input")) + // .hint("Example text input") .build(); // If you want to check if a widget is capturing diff --git a/imgui-examples/examples/long_list.rs b/imgui-examples/examples/long_list.rs index 79278af..8a745e6 100644 --- a/imgui-examples/examples/long_list.rs +++ b/imgui-examples/examples/long_list.rs @@ -15,7 +15,7 @@ fn main() { let system = support::init(file!()); system.main_loop(move |_, ui| { - Window::new(im_str!("Hello long world")) + Window::new("Hello long world") .size([300.0, 110.0], Condition::FirstUseEver) .build(ui, || { let mut clipper = imgui::ListClipper::new(lots_of_words.len() as i32) diff --git a/imgui-examples/examples/multiple_fonts.rs b/imgui-examples/examples/multiple_fonts.rs index e78a17b..5e0e927 100644 --- a/imgui-examples/examples/multiple_fonts.rs +++ b/imgui-examples/examples/multiple_fonts.rs @@ -19,18 +19,16 @@ fn main() { .reload_font_texture(&mut system.imgui) .expect("Failed to reload fonts"); system.main_loop(move |run, ui| { - Window::new(im_str!("Hello world")) - .opened(run) - .build(ui, || { - ui.text("Hello, I'm the default font!"); - let _roboto = ui.push_font(roboto); - ui.text("Hello, I'm Roboto Regular!"); - let _dokdo = ui.push_font(dokdo); - ui.text("Hello, I'm Dokdo Regular!"); - _dokdo.pop(); - ui.text("Hello, I'm Roboto Regular again!"); - _roboto.pop(); - ui.text("Hello, I'm the default font again!"); - }); + Window::new("Hello world").opened(run).build(ui, || { + ui.text("Hello, I'm the default font!"); + let _roboto = ui.push_font(roboto); + ui.text("Hello, I'm Roboto Regular!"); + let _dokdo = ui.push_font(dokdo); + ui.text("Hello, I'm Dokdo Regular!"); + _dokdo.pop(); + ui.text("Hello, I'm Roboto Regular again!"); + _roboto.pop(); + ui.text("Hello, I'm the default font again!"); + }); }); } diff --git a/imgui-examples/examples/progress_bar.rs b/imgui-examples/examples/progress_bar.rs index b5bd9db..9d11746 100644 --- a/imgui-examples/examples/progress_bar.rs +++ b/imgui-examples/examples/progress_bar.rs @@ -5,7 +5,7 @@ mod support; fn main() { let system = support::init(file!()); system.main_loop(move |run, ui| { - let w = Window::new(im_str!("Progress bar")) + let w = Window::new("Progress bar") .opened(run) .position([20.0, 20.0], Condition::Appearing) .size([700.0, 200.0], Condition::Appearing); @@ -19,9 +19,7 @@ fn main() { ui.separator(); ui.text("This progress bar uses overlay text:"); - ProgressBar::new(0.8) - .overlay_text(im_str!("Lorem ipsum")) - .build(ui); + ProgressBar::new(0.8).overlay_text("Lorem ipsum").build(ui); }); }); } diff --git a/imgui-examples/examples/radio_button.rs b/imgui-examples/examples/radio_button.rs index d968c1d..cb86a82 100644 --- a/imgui-examples/examples/radio_button.rs +++ b/imgui-examples/examples/radio_button.rs @@ -16,19 +16,15 @@ fn main() { } fn example_selector(run: &mut bool, ui: &mut Ui, state: &mut State) { - let w = Window::new(im_str!("Radio button examples")) + let w = Window::new("Radio button examples") .opened(run) .position([20.0, 20.0], Condition::Appearing) .size([700.0, 80.0], Condition::Appearing) .resizable(false); w.build(ui, || { let mut clicked = false; - clicked |= ui.radio_button( - im_str!("Example 1: Boolean radio buttons"), - &mut state.example, - 1, - ); - clicked |= ui.radio_button(im_str!("Example 2: Radio buttons"), &mut state.example, 2); + clicked |= ui.radio_button("Example 1: Boolean radio buttons", &mut state.example, 1); + clicked |= ui.radio_button("Example 2: Radio buttons", &mut state.example, 2); if clicked { state.reset(); } @@ -36,25 +32,25 @@ fn example_selector(run: &mut bool, ui: &mut Ui, state: &mut State) { } fn example_1(ui: &Ui, state: &mut State) { - let w = Window::new(im_str!("Example 1: Boolean radio buttons")) + let w = Window::new("Example 1: Boolean radio buttons") .size([700.0, 200.0], Condition::Appearing) .position([20.0, 120.0], Condition::Appearing); w.build(ui, || { - ui.text_wrapped(im_str!( + ui.text_wrapped( "Boolean radio buttons accept a boolean active state, which is passed as a value and \ not as a mutable reference. This means that it's not updated automatically, so you \ can implement any click behaviour you want. The return value is true if the button \ - was clicked." - )); + was clicked.", + ); ui.text(state.notify_text); - if ui.radio_button_bool(im_str!("I'm permanently active"), true) { + if ui.radio_button_bool("I'm permanently active", true) { state.notify_text = "*** Permanently active radio button was clicked"; } - if ui.radio_button_bool(im_str!("I'm permanently inactive"), false) { + if ui.radio_button_bool("I'm permanently inactive", false) { state.notify_text = "*** Permanently inactive radio button was clicked"; } - if ui.radio_button_bool(im_str!("I toggle my state on click"), state.simple_bool) { + if ui.radio_button_bool("I toggle my state on click", state.simple_bool) { state.simple_bool = !state.simple_bool; // flip state on click state.notify_text = "*** Toggling radio button was clicked"; } @@ -62,36 +58,36 @@ fn example_1(ui: &Ui, state: &mut State) { } fn example_2(ui: &Ui, state: &mut State) { - let w = Window::new(im_str!("Example 2: Radio buttons")) + let w = Window::new("Example 2: Radio buttons") .size([700.0, 300.0], Condition::Appearing) .position([20.0, 120.0], Condition::Appearing); w.build(ui, || { - ui.text_wrapped(im_str!( + ui.text_wrapped( "Normal radio buttons accept a mutable reference to state, and the value \ corresponding to this button. They are very flexible, because the value can be any \ - type that is both Copy and PartialEq. This is especially useful with Rust enums" - )); + type that is both Copy and PartialEq. This is especially useful with Rust enums", + ); ui.text(state.notify_text); ui.separator(); - if ui.radio_button(im_str!("I'm number 1"), &mut state.number, 1) { + if ui.radio_button("I'm number 1", &mut state.number, 1) { state.notify_text = "*** Number 1 was clicked"; } - if ui.radio_button(im_str!("I'm number 2"), &mut state.number, 2) { + if ui.radio_button("I'm number 2", &mut state.number, 2) { state.notify_text = "*** Number 2 was clicked"; } - if ui.radio_button(im_str!("I'm number 3"), &mut state.number, 3) { + if ui.radio_button("I'm number 3", &mut state.number, 3) { state.notify_text = "*** Number 3 was clicked"; } ui.separator(); - if ui.radio_button(im_str!("I'm choice A"), &mut state.choice, Some(Choice::A)) { + if ui.radio_button("I'm choice A", &mut state.choice, Some(Choice::A)) { state.notify_text = "*** Choice A was clicked"; } - if ui.radio_button(im_str!("I'm choice B"), &mut state.choice, Some(Choice::B)) { + if ui.radio_button("I'm choice B", &mut state.choice, Some(Choice::B)) { state.notify_text = "*** Choice B was clicked"; } - if ui.radio_button(im_str!("I'm choice C"), &mut state.choice, Some(Choice::C)) { + if ui.radio_button("I'm choice C", &mut state.choice, Some(Choice::C)) { state.notify_text = "*** Choice C was clicked"; } }); diff --git a/imgui-examples/examples/slider.rs b/imgui-examples/examples/slider.rs index 2b3698e..e5e5bcc 100644 --- a/imgui-examples/examples/slider.rs +++ b/imgui-examples/examples/slider.rs @@ -16,15 +16,15 @@ fn main() { } fn example_selector(run: &mut bool, ui: &mut Ui, state: &mut State) { - let w = Window::new(im_str!("Slider examples")) + let w = Window::new("Slider examples") .opened(run) .position([20.0, 20.0], Condition::Appearing) .size([700.0, 80.0], Condition::Appearing) .resizable(false); w.build(ui, || { let mut clicked = false; - clicked |= ui.radio_button(im_str!("Example 1: Basic sliders"), &mut state.example, 1); - clicked |= ui.radio_button(im_str!("Example 2: Slider arrays"), &mut state.example, 2); + clicked |= ui.radio_button("Example 1: Basic sliders", &mut state.example, 1); + clicked |= ui.radio_button("Example 2: Slider arrays", &mut state.example, 2); if clicked { state.reset(); } @@ -32,7 +32,7 @@ fn example_selector(run: &mut bool, ui: &mut Ui, state: &mut State) { } fn example_1(ui: &Ui, state: &mut State) { - let w = Window::new(im_str!("Example 1: Basic sliders")) + let w = Window::new("Example 1: Basic sliders") .size([700.0, 340.0], Condition::Appearing) .position([20.0, 120.0], Condition::Appearing); w.build(ui, || { @@ -41,42 +41,42 @@ fn example_1(ui: &Ui, state: &mut State) { ui.text("Unsigned: u8 u16 u32 u64"); ui.text("Floats: f32 f64"); - Slider::new(im_str!("u8 value"), 0, 255) + Slider::new("u8 value", 0, 255) .build(ui, &mut state.u8_value); - Slider::new(im_str!("f32 value"), -f32::MIN, f32::MAX) + Slider::new("f32 value", -f32::MIN, f32::MAX) .build(ui, &mut state.f32_value); ui.separator(); ui.text("Slider range can be limited:"); - Slider::new(im_str!("i32 value with range"), -999, 999) + Slider::new("i32 value with range", -999, 999) .build(ui, &mut state.i32_value); ui.text("Note that for 32-bit/64-bit types, sliders are always limited to half of the natural type range!"); ui.separator(); ui.text("Value formatting can be customized with a C-style printf string:"); - Slider::new(im_str!("f64 value with custom formatting"), -999_999_999.0, 999_999_999.0) - .display_format(im_str!("%09.0f")) + Slider::new("f64 value with custom formatting", -999_999_999.0, 999_999_999.0) + .display_format("%09.0f") .build(ui, &mut state.f64_formatted); ui.separator(); ui.text("Vertical sliders require a size parameter but otherwise work in a similar way:"); - VerticalSlider::new(im_str!("vertical\nu8 value"), [50.0, 50.0], u8::MIN, u8::MAX) + VerticalSlider::new("vertical\nu8 value", [50.0, 50.0], u8::MIN, u8::MAX) .build(ui, &mut state.u8_value); }); } fn example_2(ui: &Ui, state: &mut State) { - let w = Window::new(im_str!("Example 2: Slider arrays")) + let w = Window::new("Example 2: Slider arrays") .size([700.0, 260.0], Condition::Appearing) .position([20.0, 120.0], Condition::Appearing); w.build(ui, || { ui.text("You can easily build a slider group from an array of values:"); - Slider::new(im_str!("[u8; 4]"), 0, u8::MAX).build_array(ui, &mut state.array); + Slider::new("[u8; 4]", 0, u8::MAX).build_array(ui, &mut state.array); ui.text("You don't need to use arrays with known length; arbitrary slices can be used:"); let slice: &mut [u8] = &mut state.array[1..=2]; - Slider::new(im_str!("subslice"), 0, u8::MAX).build_array(ui, slice); + Slider::new("subslice", 0, u8::MAX).build_array(ui, slice); }); } diff --git a/imgui-examples/examples/support/clipboard.rs b/imgui-examples/examples/support/clipboard.rs index d970b29..613dbb5 100644 --- a/imgui-examples/examples/support/clipboard.rs +++ b/imgui-examples/examples/support/clipboard.rs @@ -1,17 +1,18 @@ use clipboard::{ClipboardContext, ClipboardProvider}; -use imgui::{ClipboardBackend, ImStr, ImString}; +use imgui::ClipboardBackend; -pub struct ClipboardSupport(ClipboardContext); +pub struct ClipboardSupport(pub ClipboardContext); pub fn init() -> Option { ClipboardContext::new().ok().map(ClipboardSupport) } impl ClipboardBackend for ClipboardSupport { - fn get(&mut self) -> Option { - self.0.get_contents().ok().map(|text| text.into()) + fn get(&mut self) -> Option { + self.0.get_contents().ok() } - fn set(&mut self, text: &ImStr) { - let _ = self.0.set_contents(text.to_str().to_owned()); + fn set(&mut self, text: &str) { + // ignore errors? + let _ = self.0.set_contents(text.to_owned()); } } diff --git a/imgui-examples/examples/support/mod.rs b/imgui-examples/examples/support/mod.rs index 0a2ea36..2a5c9d5 100644 --- a/imgui-examples/examples/support/mod.rs +++ b/imgui-examples/examples/support/mod.rs @@ -37,7 +37,7 @@ pub fn init(title: &str) -> System { imgui.set_ini_filename(None); if let Some(backend) = clipboard::init() { - imgui.set_clipboard_backend(Box::new(backend)); + imgui.set_clipboard_backend(backend); } else { eprintln!("Failed to initialize clipboard"); } diff --git a/imgui-examples/examples/test_window_impl.rs b/imgui-examples/examples/test_window_impl.rs index 867b848..f47c4a2 100644 --- a/imgui-examples/examples/test_window_impl.rs +++ b/imgui-examples/examples/test_window_impl.rs @@ -25,13 +25,13 @@ struct State { no_menu: bool, no_close: bool, wrap_width: f32, - buf: ImString, + buf: String, item: usize, item2: usize, item3: i32, - text: ImString, - text_with_hint: ImString, - text_multiline: ImString, + text: String, + text_with_hint: String, + text_multiline: String, i0: i32, f0: f32, vec2f: [f32; 2], @@ -57,12 +57,12 @@ struct State { impl Default for State { fn default() -> Self { - let mut buf = ImString::with_capacity(32); + let mut buf = String::with_capacity(32); buf.push_str("日本語"); - let mut text = ImString::with_capacity(128); + let mut text = String::with_capacity(128); text.push_str("Hello, world!"); - let text_with_hint = ImString::with_capacity(128); - let mut text_multiline = ImString::with_capacity(128); + let text_with_hint = String::with_capacity(128); + let mut text_multiline = String::with_capacity(128); text_multiline.push_str("Hello, world!\nMultiline"); State { show_app_main_menu_bar: false, @@ -236,7 +236,7 @@ fn main() { } fn show_help_marker(ui: &Ui, desc: &str) { - ui.text_disabled(im_str!("(?)")); + ui.text_disabled("(?)"); if ui.is_item_hovered() { ui.tooltip(|| { ui.text(desc); @@ -245,18 +245,14 @@ fn show_help_marker(ui: &Ui, desc: &str) { } fn show_user_guide(ui: &Ui) { - ui.bullet_text(im_str!("Double-click on title bar to collapse window.")); - ui.bullet_text(im_str!( - "Click and drag on lower right corner to resize window." - )); - ui.bullet_text(im_str!("Click and drag on any empty space to move window.")); - ui.bullet_text(im_str!("Mouse Wheel to scroll.")); + ui.bullet_text("Double-click on title bar to collapse window."); + ui.bullet_text("Click and drag on lower right corner to resize window."); + ui.bullet_text("Click and drag on any empty space to move window."); + ui.bullet_text("Mouse Wheel to scroll."); // TODO: check font_allow_user_scaling - ui.bullet_text(im_str!( - "TAB/SHIFT+TAB to cycle through keyboard editable fields." - )); - ui.bullet_text(im_str!("CTRL+Click on a slider or drag box to input text.")); - ui.bullet_text(im_str!( + ui.bullet_text("TAB/SHIFT+TAB to cycle through keyboard editable fields."); + ui.bullet_text("CTRL+Click on a slider or drag box to input text."); + ui.bullet_text( "While editing text: - Hold SHIFT or use mouse to select text - CTRL+Left/Right to word jump @@ -265,8 +261,8 @@ fn show_user_guide(ui: &Ui) { - CTRL+Z,CTRL+Y undo/redo - ESCAPE to revert - You can apply arithmetic operators +,*,/ on numerical values. - Use +- to subtract.\n" - )); + Use +- to subtract.\n", + ); } fn show_test_window(ui: &Ui, state: &mut State, opened: &mut bool) { @@ -290,12 +286,12 @@ fn show_test_window(ui: &Ui, state: &mut State, opened: &mut bool) { ui.show_metrics_window(&mut state.show_app_metrics); } if state.show_app_style_editor { - Window::new(im_str!("Style Editor")) + Window::new("Style Editor") .opened(&mut state.show_app_style_editor) .build(ui, || ui.show_default_style_editor()); } if state.show_app_about { - Window::new(im_str!("About ImGui")) + Window::new("About ImGui") .always_auto_resize(true) .opened(&mut state.show_app_about) .build(ui, || { @@ -321,7 +317,7 @@ fn show_test_window(ui: &Ui, state: &mut State, opened: &mut bool) { show_app_log(ui, &mut state.app_log); } - let mut window = Window::new(im_str!("ImGui Demo")) + let mut window = Window::new("ImGui Demo") .title_bar(!state.no_titlebar) .resizable(!state.no_resize) .movable(!state.no_move) @@ -336,186 +332,185 @@ fn show_test_window(ui: &Ui, state: &mut State, opened: &mut bool) { ui.push_item_width(-140.0); ui.text(format!("dear imgui says hello. ({})", imgui::dear_imgui_version())); if let Some(menu_bar) = ui.begin_menu_bar() { - if let Some(menu) = ui.begin_menu(im_str!("Menu")) { + if let Some(menu) = ui.begin_menu("Menu") { show_example_menu_file(ui, &mut state.file_menu); menu.end(); } - if let Some(menu) = ui.begin_menu(im_str!("Examples")) { - MenuItem::new(im_str!("Main menu bar")) + if let Some(menu) = ui.begin_menu("Examples") { + MenuItem::new("Main menu bar") .build_with_ref(ui, &mut state.show_app_main_menu_bar); - MenuItem::new(im_str!("Console")) + MenuItem::new("Console") .build_with_ref(ui, &mut state.show_app_console); - MenuItem::new(im_str!("Log")) + MenuItem::new("Log") .build_with_ref(ui, &mut state.show_app_log); - MenuItem::new(im_str!("Simple layout")) + MenuItem::new("Simple layout") .build_with_ref(ui, &mut state.show_app_layout); - MenuItem::new(im_str!("Property editor")) + MenuItem::new("Property editor") .build_with_ref(ui, &mut state.show_app_property_editor); - MenuItem::new(im_str!("Long text display")) + MenuItem::new("Long text display") .build_with_ref(ui, &mut state.show_app_long_text); - MenuItem::new(im_str!("Auto-resizing window")) + MenuItem::new("Auto-resizing window") .build_with_ref(ui, &mut state.show_app_auto_resize); - MenuItem::new(im_str!("Constrained-resizing window")) + MenuItem::new("Constrained-resizing window") .build_with_ref(ui, &mut state.show_app_constrained_resize); - MenuItem::new(im_str!("Simple overlay")) + MenuItem::new("Simple overlay") .build_with_ref(ui, &mut state.show_app_fixed_overlay); - MenuItem::new(im_str!("Manipulating window title")) + MenuItem::new("Manipulating window title") .build_with_ref(ui, &mut state.show_app_manipulating_window_title); - MenuItem::new(im_str!("Custom rendering")) + MenuItem::new("Custom rendering") .build_with_ref(ui, &mut state.show_app_custom_rendering); menu.end(); } - if let Some(menu) = ui.begin_menu(im_str!("Help")) { - MenuItem::new(im_str!("Metrics")) + if let Some(menu) = ui.begin_menu("Help") { + MenuItem::new("Metrics") .build_with_ref(ui, &mut state.show_app_metrics); - MenuItem::new(im_str!("Style Editor")) + MenuItem::new("Style Editor") .build_with_ref(ui, &mut state.show_app_style_editor); - MenuItem::new(im_str!("About ImGui")) + MenuItem::new("About ImGui") .build_with_ref(ui, &mut state.show_app_about); menu.end(); } menu_bar.end(); } ui.spacing(); - if CollapsingHeader::new(im_str!("Help")).build(ui) { - ui.text_wrapped(im_str!( + if CollapsingHeader::new("Help").build(ui) { + ui.text_wrapped( "This window is being created by the show_test_window() \ function. Please refer to the code for programming \ reference.\n\nUser Guide:" - )); + ); show_user_guide(ui); } - if CollapsingHeader::new(im_str!("Window options")).build(ui) { - ui.checkbox(im_str!("No titlebar"), &mut state.no_titlebar); + if CollapsingHeader::new("Window options").build(ui) { + ui.checkbox("No titlebar", &mut state.no_titlebar); ui.same_line_with_pos(150.0); - ui.checkbox(im_str!("No scrollbar"), &mut state.no_scrollbar); + ui.checkbox("No scrollbar", &mut state.no_scrollbar); ui.same_line_with_pos(300.0); - ui.checkbox(im_str!("No menu"), &mut state.no_menu); - ui.checkbox(im_str!("No move"), &mut state.no_move); + ui.checkbox("No menu", &mut state.no_menu); + ui.checkbox("No move", &mut state.no_move); ui.same_line_with_pos(150.0); - ui.checkbox(im_str!("No resize"), &mut state.no_resize); + ui.checkbox("No resize", &mut state.no_resize); ui.same_line_with_pos(300.0); - ui.checkbox(im_str!("No collapse"), &mut state.no_collapse); - ui.checkbox(im_str!("No close"), &mut state.no_close); + ui.checkbox("No collapse", &mut state.no_collapse); + ui.checkbox("No close", &mut state.no_close); - TreeNode::new(im_str!("Style")).build(ui, || { + TreeNode::new("Style").build(ui, || { ui.show_default_style_editor(); }); } - if CollapsingHeader::new(im_str!("Widgets")).build(ui) { - TreeNode::new(im_str!("Tree")).build(ui, || { + if CollapsingHeader::new("Widgets").build(ui) { + TreeNode::new("Tree").build(ui, || { for i in 0..5 { - TreeNode::new(&im_str!("Child {}", i)).build(ui, || { - ui.text(im_str!("blah blah")); + TreeNode::new(format!("Child {}", i)).build(ui, || { + ui.text("blah blah"); ui.same_line(); - if ui.small_button(im_str!("print")) { + if ui.small_button("print") { println!("Child {} pressed", i); } }); } }); - TreeNode::new(im_str!("Bullets")).build(ui, || { - ui.bullet_text(im_str!("Bullet point 1")); - ui.bullet_text(im_str!("Bullet point 2\nOn multiple lines")); + TreeNode::new("Bullets").build(ui, || { + ui.bullet_text("Bullet point 1"); + ui.bullet_text("Bullet point 2\nOn multiple lines"); ui.bullet(); - ui.text(im_str!("Bullet point 3 (two calls)")); + ui.text("Bullet point 3 (two calls)"); ui.bullet(); - ui.small_button(im_str!("Button")); + ui.small_button("Button"); }); - TreeNode::new(im_str!("Colored text")).build(ui, || { - ui.text_colored([1.0, 0.0, 1.0, 1.0], im_str!("Pink")); - ui.text_colored([1.0, 1.0, 0.0, 1.0], im_str!("Yellow")); - ui.text_disabled(im_str!("Disabled")); + TreeNode::new("Colored text").build(ui, || { + ui.text_colored([1.0, 0.0, 1.0, 1.0], "Pink"); + ui.text_colored([1.0, 1.0, 0.0, 1.0], "Yellow"); + ui.text_disabled("Disabled"); }); - TreeNode::new(im_str!("Multi-line text")).build(ui, || { + TreeNode::new("Multi-line text").build(ui, || { ui.input_text_multiline( - im_str!("multiline"), + "multiline", &mut state.text_multiline, [300., 100.], ).build(); }); - TreeNode::new(im_str!("Word wrapping")).build(ui, || { - ui.text_wrapped(im_str!( + TreeNode::new("Word wrapping").build(ui, || { + ui.text_wrapped( "This text should automatically wrap on the edge of \ the window.The current implementation for text \ wrapping follows simple rulessuitable for English \ and possibly other languages." - )); + ); ui.spacing(); - Slider::new(im_str!("Wrap width"), -20.0, 600.0) - .display_format(im_str!("%.0f")) + Slider::new("Wrap width", -20.0, 600.0) + .display_format("%.0f") .build(ui, &mut state.wrap_width); - ui.text(im_str!("Test paragraph 1:")); + ui.text("Test paragraph 1:"); // TODO - ui.text(im_str!("Test paragraph 2:")); + ui.text("Test paragraph 2:"); // TODO }); - TreeNode::new(im_str!("UTF-8 Text")).build(ui, || { - ui.text_wrapped(im_str!( + TreeNode::new("UTF-8 Text").build(ui, || { + ui.text_wrapped( "CJK text will only appear if the font was loaded \ with theappropriate CJK character ranges. Call \ io.Font->LoadFromFileTTF()manually to load extra \ character ranges." - )); + ); - ui.text(im_str!("Hiragana: かきくけこ (kakikukeko)")); - ui.text(im_str!("Kanjis: 日本語 (nihongo)")); - ui.input_text(im_str!("UTF-8 input"), &mut state.buf) + ui.text("Hiragana: かきくけこ (kakikukeko)"); + ui.text("Kanjis: 日本語 (nihongo)"); + ui.input_text("UTF-8 input", &mut state.buf) .build(); }); - ui.radio_button(im_str!("radio a"), &mut state.radio_button, 0); + ui.radio_button("radio a", &mut state.radio_button, 0); ui.same_line(); - ui.radio_button(im_str!("radio b"), &mut state.radio_button, 1); + ui.radio_button("radio b", &mut state.radio_button, 1); ui.same_line(); - ui.radio_button(im_str!("radio c"), &mut state.radio_button, 2); + ui.radio_button("radio c", &mut state.radio_button, 2); ui.separator(); - ui.label_text(im_str!("label"), im_str!("Value")); - ComboBox::new(im_str!("combo")).build_simple_string(ui, + ui.label_text("label", "Value"); + ui.combo_simple_string("combo", &mut state.item, &[ - im_str!("aaaa"), - im_str!("bbbb"), - im_str!("cccc"), - im_str!("dddd"), - im_str!("eeee"), + "aaaa", + "bbbb", + "cccc", + "dddd", + "eeee", ]); let items = [ - im_str!("AAAA"), - im_str!("BBBB"), - im_str!("CCCC"), - im_str!("DDDD"), - im_str!("EEEE"), - im_str!("FFFF"), - im_str!("GGGG"), - im_str!("HHHH"), - im_str!("IIII"), - im_str!("JJJJ"), - im_str!("KKKK"), + "AAAA", + "BBBB", + "CCCC", + "DDDD", + "EEEE", + "FFFF", + "GGGG", + "HHHH", + "IIII", + "JJJJ", + "KKKK", ]; - ComboBox::new(im_str!("combo scroll")).build_simple_string(ui, &mut state.item2, &items); - - ui.list_box(im_str!("list"), &mut state.item3, &items, 8); + ui.combo_simple_string("combo scroll", &mut state.item2, &items); + ui.list_box("list", &mut state.item3, &items, 8); let names = [ - im_str!("Bream"), - im_str!("Haddock"), - im_str!("Mackerel"), - im_str!("Pollock"), - im_str!("Tilefish"), + "Bream", + "Haddock", + "Mackerel", + "Pollock", + "Tilefish", ]; - ListBox::new(im_str!("selectables list")).build(ui, || { + ListBox::new("selectables list").build(ui, || { for (index, name) in names.iter().enumerate() { let selected = matches!(state.selected_fish2, Some(i) if i == index ); if Selectable::new(name).selected(selected).build(ui) { @@ -525,7 +520,7 @@ fn show_test_window(ui: &Ui, state: &mut State, opened: &mut bool) { }); let last_size = ui.item_rect_size(); - ListBox::new(im_str!("selectable list 2")).size([0.0, last_size[1] * 0.66]).build(ui, || { + ListBox::new("selectable list 2").size([0.0, last_size[1] * 0.66]).build(ui, || { for (index, name) in names.iter().enumerate() { let selected = matches!(state.selected_fish2, Some(i) if i == index ); if Selectable::new(name).selected(selected).build(ui) { @@ -534,40 +529,40 @@ fn show_test_window(ui: &Ui, state: &mut State, opened: &mut bool) { } }); - ui.input_text(im_str!("input text"), &mut state.text) + ui.input_text("input text", &mut state.text) .build(); - ui.input_text(im_str!("input text with hint"), &mut state.text_with_hint) - .hint(im_str!("enter text here")) + ui.input_text("input text with hint", &mut state.text_with_hint) + .hint("enter text here") .build(); - ui.input_int(im_str!("input int"), &mut state.i0).build(); - Drag::new(im_str!("drag int")).build(ui, &mut state.i0); - ui.input_float(im_str!("input float"), &mut state.f0) + ui.input_int("input int", &mut state.i0).build(); + // Drag::new("drag int").build(ui, &mut state.i0); + ui.input_float("input float", &mut state.f0) .step(0.01) .step_fast(1.0) .build(); - Drag::new(im_str!("drag float")).range(-1.0, 1.0).speed(0.001).build(ui, &mut state.f0); - ui.input_float3(im_str!("input float3"), &mut state.vec3f) + Drag::new("drag float").range(-1.0, 1.0).speed(0.001).build(ui, &mut state.f0); + ui.input_float3("input float3", &mut state.vec3f) .build(); - ColorEdit::new(im_str!("color 1"), &mut state.col1).build(ui); - ColorEdit::new(im_str!("color 2"), &mut state.col2).build(ui); + ColorEdit::new("color 1", &mut state.col1).build(ui); + ColorEdit::new("color 2", &mut state.col2).build(ui); - TreeNode::new(im_str!("Multi-component Widgets")).build(ui, || { - ui.input_float2(im_str!("input float2"), &mut state.vec2f) + TreeNode::new("Multi-component Widgets").build(ui, || { + ui.input_float2("input float2", &mut state.vec2f) .build(); - ui.input_int2(im_str!("input int2"), &mut state.vec2i) + ui.input_int2("input int2", &mut state.vec2i) .build(); ui.spacing(); - ui.input_float3(im_str!("input float3"), &mut state.vec3f) + ui.input_float3("input float3", &mut state.vec3f) .build(); - ui.input_int3(im_str!("input int3"), &mut state.vec3i) + ui.input_int3("input int3", &mut state.vec3i) .build(); ui.spacing(); }); - TreeNode::new(im_str!("Color/Picker Widgets")).build(ui, || { + TreeNode::new("Color/Picker Widgets").build(ui, || { let s = &mut state.color_edit; - ui.checkbox(im_str!("With HDR"), &mut s.hdr); + ui.checkbox("With HDR", &mut s.hdr); ui.same_line(); show_help_marker( ui, @@ -575,12 +570,12 @@ fn show_test_window(ui: &Ui, state: &mut State, opened: &mut bool) { limits on dragging widgets.", ); - ui.checkbox(im_str!("With Alpha Preview"), &mut s.alpha_preview); + ui.checkbox("With Alpha Preview", &mut s.alpha_preview); ui.checkbox( - im_str!("With Half Alpha Preview"), + "With Half Alpha Preview", &mut s.alpha_half_preview, ); - ui.checkbox(im_str!("With Options Menu"), &mut s.options_menu); + ui.checkbox("With Options Menu", &mut s.options_menu); ui.same_line(); show_help_marker( ui, @@ -598,31 +593,31 @@ fn show_test_window(ui: &Ui, state: &mut State, opened: &mut bool) { f }; - ui.text(im_str!("Color widget:")); + ui.text("Color widget:"); ui.same_line(); show_help_marker( ui, "Click on the colored square to open a color picker. CTRL+click on individual component to input value.\n", ); - ColorEdit::new(im_str!("MyColor##1"), &mut s.color) + ColorEdit::new("MyColor##1", &mut s.color) .flags(misc_flags) .alpha(false) .build(ui); - ui.text(im_str!("Color widget HSV with Alpha:")); - ColorEdit::new(im_str!("MyColor##2"), &mut s.color) + ui.text("Color widget HSV with Alpha:"); + ColorEdit::new("MyColor##2", &mut s.color) .flags(misc_flags) .input_mode(ColorEditInputMode::HSV) .build(ui); - ui.text(im_str!("Color widget with Float Display:")); - ColorEdit::new(im_str!("MyColor##2f"), &mut s.color) + ui.text("Color widget with Float Display:"); + ColorEdit::new("MyColor##2f", &mut s.color) .flags(misc_flags) .format(ColorFormat::Float) .build(ui); - ui.text(im_str!("Color button with Picker:")); + ui.text("Color button with Picker:"); ui.same_line(); show_help_marker( ui, @@ -631,29 +626,29 @@ CTRL+click on individual component to input value.\n", With the label(false) function you can pass a non-empty label which \ will only be used for the tooltip and picker popup.", ); - ColorEdit::new(im_str!("MyColor##3"), &mut s.color) + ColorEdit::new("MyColor##3", &mut s.color) .flags(misc_flags) .inputs(false) .label(false) .build(ui); - ui.text(im_str!("Color picker:")); - ui.checkbox(im_str!("With Alpha"), &mut s.alpha); - ui.checkbox(im_str!("With Alpha Bar"), &mut s.alpha_bar); - ui.checkbox(im_str!("With Side Preview"), &mut s.side_preview); + ui.text("Color picker:"); + ui.checkbox("With Alpha", &mut s.alpha); + ui.checkbox("With Alpha Bar", &mut s.alpha_bar); + ui.checkbox("With Side Preview", &mut s.side_preview); if s.side_preview { ui.same_line(); - ui.checkbox(im_str!("With Ref Color"), &mut s.ref_color); + ui.checkbox("With Ref Color", &mut s.ref_color); if s.ref_color { ui.same_line(); - ColorEdit::new(im_str!("##RefColor"), &mut s.ref_color_v) + ColorEdit::new("##RefColor", &mut s.ref_color_v) .flags(misc_flags) .inputs(false) .build(ui); } } let mut b = ColorPicker::new - (im_str!("MyColor##4"), &mut s.color) + ("MyColor##4", &mut s.color) .flags(misc_flags) .alpha(s.alpha) .alpha_bar(s.alpha_bar) @@ -667,48 +662,48 @@ CTRL+click on individual component to input value.\n", }); } - if CollapsingHeader::new(im_str!("Layout")).build(ui) { - TreeNode::new(im_str!("Tabs")).build(ui, || { - TreeNode::new(im_str!("Basic")).build(ui, || { - TabBar::new(im_str!("basictabbar")).build(ui, || { - TabItem::new(im_str!("Avocado")).build(ui, || { - ui.text(im_str!("This is the Avocado tab!")); - ui.text(im_str!("blah blah blah blah blah")); + if CollapsingHeader::new("Layout").build(ui) { + TreeNode::new("Tabs").build(ui, || { + TreeNode::new("Basic").build(ui, || { + TabBar::new("basictabbar").build(ui, || { + TabItem::new("Avocado").build(ui, || { + ui.text("This is the Avocado tab!"); + ui.text("blah blah blah blah blah"); }); - TabItem::new(im_str!("Broccoli")).build(ui, || { - ui.text(im_str!("This is the Broccoli tab!")); - ui.text(im_str!("blah blah blah blah blah")); + TabItem::new("Broccoli").build(ui, || { + ui.text("This is the Broccoli tab!"); + ui.text("blah blah blah blah blah"); }); - TabItem::new(im_str!("Cucumber")).build(ui, || { - ui.text(im_str!("This is the Cucumber tab!")); - ui.text(im_str!("blah blah blah blah blah")); + TabItem::new("Cucumber").build(ui, || { + ui.text("This is the Cucumber tab!"); + ui.text("blah blah blah blah blah"); }); }); }); - TreeNode::new(im_str!("Advanced & Close button")).build(ui, || { + TreeNode::new("Advanced & Close button").build(ui, || { ui.separator(); let s = &mut state.tabs; - ui.checkbox(im_str!("ImGuiTabBarFlags_Reorderable"), &mut s.reorderable); - ui.checkbox(im_str!("ImGuiTabBarFlags_AutoSelectNewTabs"), &mut s.autoselect); - ui.checkbox(im_str!("ImGuiTabBarFlags_TabListPopupButton"), &mut s.listbutton); - ui.checkbox(im_str!("ImGuiTabBarFlags_NoCloseWithMiddleMouseButton"), &mut s.noclose_middlebutton); - if ui.checkbox(im_str!("ImGuiTabBarFlags_FittingPolicyResizeDown"), &mut s.fitting_resizedown) { + ui.checkbox("ImGuiTabBarFlags_Reorderable", &mut s.reorderable); + ui.checkbox("ImGuiTabBarFlags_AutoSelectNewTabs", &mut s.autoselect); + ui.checkbox("ImGuiTabBarFlags_TabListPopupButton", &mut s.listbutton); + ui.checkbox("ImGuiTabBarFlags_NoCloseWithMiddleMouseButton", &mut s.noclose_middlebutton); + if ui.checkbox("ImGuiTabBarFlags_FittingPolicyResizeDown", &mut s.fitting_resizedown) { s.fitting_scroll = !s.fitting_resizedown; } - if ui.checkbox(im_str!("ImGuiTabBarFlags_FittingPolicyScroll"), &mut s.fitting_scroll) { + if ui.checkbox("ImGuiTabBarFlags_FittingPolicyScroll", &mut s.fitting_scroll) { s.fitting_resizedown = !s.fitting_scroll; } let style = ui.push_style_var(StyleVar::FramePadding([0.0, 0.0])); - ui.checkbox(im_str!("Artichoke"), &mut s.artichoke_tab); + ui.checkbox("Artichoke", &mut s.artichoke_tab); ui.same_line(); - ui.checkbox(im_str!("Beetroot"), &mut s.beetroot_tab); + ui.checkbox("Beetroot", &mut s.beetroot_tab); ui.same_line(); - ui.checkbox(im_str!("Celery"), &mut s.celery_tab); + ui.checkbox("Celery", &mut s.celery_tab); ui.same_line(); - ui.checkbox(im_str!("Daikon"), &mut s.daikon_tab); + ui.checkbox("Daikon", &mut s.daikon_tab); style.pop(); let flags = { @@ -722,48 +717,48 @@ CTRL+click on individual component to input value.\n", f }; - TabBar::new(im_str!("tabbar")).flags(flags).build(ui, || { - TabItem::new(im_str!("Artichoke")).opened(&mut s.artichoke_tab).build(ui, || { - ui.text(im_str!("This is the Artichoke tab!")); + TabBar::new("tabbar").flags(flags).build(ui, || { + TabItem::new("Artichoke").opened(&mut s.artichoke_tab).build(ui, || { + ui.text("This is the Artichoke tab!"); }); - TabItem::new(im_str!("Beetroot")).opened(&mut s.beetroot_tab).build(ui, || { - ui.text(im_str!("This is the Beetroot tab!")); + TabItem::new("Beetroot").opened(&mut s.beetroot_tab).build(ui, || { + ui.text("This is the Beetroot tab!"); }); - TabItem::new(im_str!("Celery")).opened(&mut s.celery_tab).build(ui, || { - ui.text(im_str!("This is the Celery tab!")); + TabItem::new("Celery").opened(&mut s.celery_tab).build(ui, || { + ui.text("This is the Celery tab!"); }); - TabItem::new(im_str!("Daikon")).opened(&mut s.daikon_tab).build(ui, || { - ui.text(im_str!("This is the Daikon tab!")); + TabItem::new("Daikon").opened(&mut s.daikon_tab).build(ui, || { + ui.text("This is the Daikon tab!"); }); }); }); }); } - if CollapsingHeader::new(im_str!("Popups & Modal windows")).build(ui) { - TreeNode::new(im_str!("Popups")).build(ui, || { - ui.text_wrapped(im_str!( + if CollapsingHeader::new("Popups & Modal windows").build(ui) { + TreeNode::new("Popups").build(ui, || { + ui.text_wrapped( "When a popup is active, it inhibits interacting \ with windows that are behind the popup. Clicking \ outside the popup closes it." - )); + ); let names = [ - im_str!("Bream"), - im_str!("Haddock"), - im_str!("Mackerel"), - im_str!("Pollock"), - im_str!("Tilefish"), + "Bream", + "Haddock", + "Mackerel", + "Pollock", + "Tilefish", ]; - if ui.small_button(im_str!("Select..")) { - ui.open_popup(im_str!("select")); + if ui.small_button("Select..") { + ui.open_popup("select"); } ui.same_line(); ui.text(match state.selected_fish { Some(index) => names[index], - None => im_str!(""), + None => "", }); - ui.popup(im_str!("select"), || { - ui.text(im_str!("Aquarium")); + ui.popup("select", || { + ui.text("Aquarium"); ui.separator(); for (index, name) in names.iter().enumerate() { if Selectable::new(name).build(ui) { @@ -773,56 +768,56 @@ CTRL+click on individual component to input value.\n", }); }); - TreeNode::new(im_str!("Modals")).build(ui, || { - ui.text_wrapped(im_str!( + TreeNode::new("Modals").build(ui, || { + ui.text_wrapped( "Modal windows are like popups but the user cannot close \ them by clicking outside the window." - )); + ); - if ui.button(im_str!("Delete..")) { - ui.open_popup(im_str!("Delete?")); + if ui.button("Delete..") { + ui.open_popup("Delete?"); } - PopupModal::new(im_str!("Delete?")).always_auto_resize(true).build(ui, || { + PopupModal::new("Delete?").always_auto_resize(true).build(ui, || { ui.text("All those beautiful files will be deleted.\nThis operation cannot be undone!\n\n"); ui.separator(); let style = ui.push_style_var(StyleVar::FramePadding([0.0, 0.0])); - ui.checkbox(im_str!("Don't ask me next time"), &mut state.dont_ask_me_next_time); + ui.checkbox("Don't ask me next time", &mut state.dont_ask_me_next_time); - if ui.button_with_size(im_str!("OK"), [120.0, 0.0]) { + if ui.button_with_size("OK", [120.0, 0.0]) { ui.close_current_popup(); } ui.same_line(); - if ui.button_with_size(im_str!("Cancel"), [120.0, 0.0]) { + if ui.button_with_size("Cancel", [120.0, 0.0]) { ui.close_current_popup(); } style.pop(); }); - if ui.button(im_str!("Stacked modals..")) { - ui.open_popup(im_str!("Stacked 1")); + if ui.button("Stacked modals..") { + ui.open_popup("Stacked 1"); } - PopupModal::new(im_str!("Stacked 1")).build(ui, || { + PopupModal::new("Stacked 1").build(ui, || { ui.text( "Hello from Stacked The First\n\ Using style[StyleColor::ModalWindowDarkening] for darkening." ); - let items = &[im_str!("aaaa"), im_str!("bbbb"), im_str!("cccc"), im_str!("dddd"), im_str!("eeee")]; - ComboBox::new(im_str!("Combo")).build_simple_string(ui, &mut state.stacked_modals_item, items); + let items = &["aaaa", "bbbb", "cccc", "dddd", "eeee"]; + ui.combo_simple_string("Combo", &mut state.stacked_modals_item, items); - ColorEdit::new(im_str!("color"), &mut state.stacked_modals_color).build(ui); + ColorEdit::new("color", &mut state.stacked_modals_color).build(ui); - if ui.button(im_str!("Add another modal..")) { - ui.open_popup(im_str!("Stacked 2")) ; + if ui.button("Add another modal..") { + ui.open_popup("Stacked 2") ; } - PopupModal::new(im_str!("Stacked 2")).build(ui, || { + PopupModal::new("Stacked 2").build(ui, || { ui.text("Hello from Stacked The Second"); - if ui.button(im_str!("Close")) { + if ui.button("Close") { ui.close_current_popup(); } }); - if ui.button(im_str!("Close")) { + if ui.button("Close") { ui.close_current_popup(); } }); @@ -833,28 +828,20 @@ CTRL+click on individual component to input value.\n", fn show_example_app_main_menu_bar<'a>(ui: &Ui<'a>, state: &mut State) { if let Some(menu_bar) = ui.begin_main_menu_bar() { - if let Some(menu) = ui.begin_menu(im_str!("File")) { + if let Some(menu) = ui.begin_menu("File") { show_example_menu_file(ui, &mut state.file_menu); menu.end(); } - if let Some(menu) = ui.begin_menu(im_str!("Edit")) { - MenuItem::new(im_str!("Undo")) - .shortcut(im_str!("CTRL+Z")) - .build(ui); - MenuItem::new(im_str!("Redo")) - .shortcut(im_str!("CTRL+Y")) + if let Some(menu) = ui.begin_menu("Edit") { + MenuItem::new("Undo").shortcut("CTRL+Z").build(ui); + MenuItem::new("Redo") + .shortcut("CTRL+Y") .enabled(false) .build(ui); ui.separator(); - MenuItem::new(im_str!("Cut")) - .shortcut(im_str!("CTRL+X")) - .build(ui); - MenuItem::new(im_str!("Copy")) - .shortcut(im_str!("CTRL+C")) - .build(ui); - MenuItem::new(im_str!("Paste")) - .shortcut(im_str!("CTRL+V")) - .build(ui); + MenuItem::new("Cut").shortcut("CTRL+X").build(ui); + MenuItem::new("Copy").shortcut("CTRL+C").build(ui); + MenuItem::new("Paste").shortcut("CTRL+V").build(ui); menu.end(); } menu_bar.end(); @@ -862,21 +849,17 @@ fn show_example_app_main_menu_bar<'a>(ui: &Ui<'a>, state: &mut State) { } fn show_example_menu_file<'a>(ui: &Ui<'a>, state: &mut FileMenuState) { - MenuItem::new(im_str!("(dummy menu)")) - .enabled(false) - .build(ui); - MenuItem::new(im_str!("New")).build(ui); - MenuItem::new(im_str!("Open")) - .shortcut(im_str!("Ctrl+O")) - .build(ui); - if let Some(menu) = ui.begin_menu(im_str!("Open Recent")) { - MenuItem::new(im_str!("fish_hat.c")).build(ui); - MenuItem::new(im_str!("fish_hat.inl")).build(ui); - MenuItem::new(im_str!("fish_hat.h")).build(ui); - if let Some(menu) = ui.begin_menu(im_str!("More..")) { - MenuItem::new(im_str!("Hello")).build(ui); - MenuItem::new(im_str!("Sailor")).build(ui); - if let Some(menu) = ui.begin_menu(im_str!("Recurse..")) { + MenuItem::new("(dummy menu)").enabled(false).build(ui); + MenuItem::new("New").build(ui); + MenuItem::new("Open").shortcut("Ctrl+O").build(ui); + if let Some(menu) = ui.begin_menu("Open Recent") { + MenuItem::new("fish_hat.c").build(ui); + MenuItem::new("fish_hat.inl").build(ui); + MenuItem::new("fish_hat.h").build(ui); + if let Some(menu) = ui.begin_menu("More..") { + MenuItem::new("Hello").build(ui); + MenuItem::new("Sailor").build(ui); + if let Some(menu) = ui.begin_menu("Recurse..") { show_example_menu_file(ui, state); menu.end(); } @@ -884,13 +867,11 @@ fn show_example_menu_file<'a>(ui: &Ui<'a>, state: &mut FileMenuState) { } menu.end(); } - MenuItem::new(im_str!("Save")) - .shortcut(im_str!("Ctrl+S")) - .build(ui); - MenuItem::new(im_str!("Save As..")).build(ui); + MenuItem::new("Save").shortcut("Ctrl+S").build(ui); + MenuItem::new("Save As..").build(ui); ui.separator(); - if let Some(menu) = ui.begin_menu(im_str!("Options")) { - MenuItem::new(im_str!("Enabled")).build_with_ref(ui, &mut state.enabled); + if let Some(menu) = ui.begin_menu("Options") { + MenuItem::new("Enabled").build_with_ref(ui, &mut state.enabled); ChildWindow::new("child") .size([0.0, 60.0]) .border(true) @@ -899,33 +880,27 @@ fn show_example_menu_file<'a>(ui: &Ui<'a>, state: &mut FileMenuState) { ui.text(format!("Scrolling Text {}", i)); } }); - Slider::new(im_str!("Value"), 0.0, 1.0).build(ui, &mut state.f); + Slider::new("Value", 0.0, 1.0).build(ui, &mut state.f); - ui.input_float(im_str!("Input"), &mut state.f) - .step(0.1) - .build(); - let items = [im_str!("Yes"), im_str!("No"), im_str!("Maybe")]; - ComboBox::new(im_str!("Combo")).build_simple_string(ui, &mut state.n, &items); - ui.checkbox(im_str!("Check"), &mut state.b); + ui.input_float("Input", &mut state.f).step(0.1).build(); + let items = ["Yes", "No", "Maybe"]; + ui.combo_simple_string("Combo", &mut state.n, &items); + ui.checkbox("Check", &mut state.b); menu.end(); } - if let Some(menu) = ui.begin_menu(im_str!("Colors")) { + if let Some(menu) = ui.begin_menu("Colors") { for &col in StyleColor::VARIANTS.iter() { - MenuItem::new(&im_str!("{:?}", col)).build(ui); + MenuItem::new(format!("{:?}", col)).build(ui); } menu.end(); } - assert!(ui - .begin_menu_with_enabled(im_str!("Disabled"), false) - .is_none()); - MenuItem::new(im_str!("Checked")).selected(true).build(ui); - MenuItem::new(im_str!("Quit")) - .shortcut(im_str!("Alt+F4")) - .build(ui); + assert!(ui.begin_menu_with_enabled("Disabled", false).is_none()); + MenuItem::new("Checked").selected(true).build(ui); + MenuItem::new("Quit").shortcut("Alt+F4").build(ui); } fn show_example_app_auto_resize(ui: &Ui, state: &mut AutoResizeState, opened: &mut bool) { - Window::new(im_str!("Example: Auto-resizing window")) + Window::new("Example: Auto-resizing window") .opened(opened) .always_auto_resize(true) .build(ui, || { @@ -934,7 +909,7 @@ fn show_example_app_auto_resize(ui: &Ui, state: &mut AutoResizeState, opened: &m Note that you probably don't want to query the window size to output your content because that would create a feedback loop.", ); - Slider::new(im_str!("Number of lines"), 1, 20).build(ui, &mut state.lines); + Slider::new("Number of lines", 1, 20).build(ui, &mut state.lines); for i in 0..state.lines { ui.text(format!("{:2$}This is line {}", "", i, i as usize * 4)); } @@ -945,7 +920,7 @@ fn show_example_app_fixed_overlay(ui: &Ui, opened: &mut bool) { const DISTANCE: f32 = 10.0; let window_pos = [DISTANCE, DISTANCE]; let style = ui.push_style_color(StyleColor::WindowBg, [0.0, 0.0, 0.0, 0.3]); - Window::new(im_str!("Example: Fixed Overlay")) + Window::new("Example: Fixed Overlay") .opened(opened) .position(window_pos, Condition::Always) .title_bar(false) @@ -968,7 +943,7 @@ fn show_example_app_fixed_overlay(ui: &Ui, opened: &mut bool) { } fn show_example_app_manipulating_window_title(ui: &Ui) { - Window::new(im_str!("Same title as another window##1")) + Window::new("Same title as another window##1") .position([100.0, 100.0], Condition::FirstUseEver) .build(ui, || { ui.text( @@ -976,7 +951,7 @@ fn show_example_app_manipulating_window_title(ui: &Ui) { My title is the same as window 2, but my identifier is unique.", ); }); - Window::new(im_str!("Same title as another window##2")) + Window::new("Same title as another window##2") .position([100.0, 200.0], Condition::FirstUseEver) .build(ui, || { ui.text( @@ -987,20 +962,20 @@ My title is the same as window 1, but my identifier is unique.", let chars = ['|', '/', '-', '\\']; let ch_idx = (ui.time() / 0.25) as usize & 3; let num = ui.frame_count(); // The C++ version uses rand() here - let title = im_str!("Animated title {} {}###AnimatedTitle", chars[ch_idx], num); - Window::new(&title) + let title = format!("Animated title {} {}###AnimatedTitle", chars[ch_idx], num); + Window::new(title) .position([100.0, 300.0], Condition::FirstUseEver) .build(ui, || ui.text("This window has a changing title")); } fn show_example_app_custom_rendering(ui: &Ui, state: &mut CustomRenderingState, opened: &mut bool) { - Window::new(im_str!("Example: Custom rendering")) + Window::new("Example: Custom rendering") .size([350.0, 560.0], Condition::FirstUseEver) .opened(opened) .build(ui, || { ui.text("Primitives"); // TODO: Add DragFloat to change value of sz - ColorEdit::new(im_str!("Color"), &mut state.col).build(ui); + ColorEdit::new("Color", &mut state.col).build(ui); let draw_list = ui.get_window_draw_list(); let p = ui.cursor_screen_pos(); let spacing = 8.0; @@ -1129,20 +1104,18 @@ fn show_example_app_custom_rendering(ui: &Ui, state: &mut CustomRenderingState, ui.dummy([(state.sz + spacing) * 8.0, (state.sz + spacing) * 3.0]); ui.separator(); - ui.text(im_str!("Canvas example")); - if ui.button(im_str!("Clear")) { + ui.text("Canvas example"); + if ui.button("Clear") { state.points.clear(); } if state.points.len() >= 2 { ui.same_line(); - if ui.button(im_str!("Undo")) { + if ui.button("Undo") { state.points.pop(); state.points.pop(); } } - ui.text(im_str!( - "Left-click and drag to add lines,\nRight-click to undo" - )); + ui.text("Left-click and drag to add lines,\nRight-click to undo"); // Here we are using InvisibleButton() as a convenience to // 1) advance the cursor, and // 2) allows us to use IsItemHovered() @@ -1190,7 +1163,7 @@ fn show_example_app_custom_rendering(ui: &Ui, state: &mut CustomRenderingState, .build(); let mut adding_preview = false; - ui.invisible_button(im_str!("canvas"), canvas_size); + ui.invisible_button("canvas", canvas_size); let mouse_pos = ui.io().mouse_pos; let mouse_pos_in_canvas = [mouse_pos[0] - canvas_pos[0], mouse_pos[1] - canvas_pos[1]]; if state.adding_line { @@ -1244,10 +1217,10 @@ fn show_example_app_custom_rendering(ui: &Ui, state: &mut CustomRenderingState, } fn show_app_log(ui: &Ui, app_log: &mut Vec) { - Window::new(im_str!("Example: Log")) + Window::new("Example: Log") .size([500.0, 400.0], Condition::FirstUseEver) .build(ui, || { - if ui.small_button(im_str!("[Debug] Add 5 entries")) { + if ui.small_button("[Debug] Add 5 entries") { let categories = ["info", "warn", "error"]; let words = [ "Bumfuzzled", @@ -1270,11 +1243,11 @@ fn show_app_log(ui: &Ui, app_log: &mut Vec) { app_log.push(text); } } - if ui.button(im_str!("Clear")) { + if ui.button("Clear") { app_log.clear(); } ui.same_line(); - if ui.button(im_str!("Copy")) { + if ui.button("Copy") { ui.set_clipboard_text(&ImString::from(app_log.join("\n"))); } ui.separator(); diff --git a/imgui-examples/examples/text_callbacks.rs b/imgui-examples/examples/text_callbacks.rs index 43c4ec3..4adcb4a 100644 --- a/imgui-examples/examples/text_callbacks.rs +++ b/imgui-examples/examples/text_callbacks.rs @@ -4,14 +4,10 @@ mod support; fn main() { let system = support::init(file!()); - let mut buffers = vec![ - ImString::default(), - ImString::default(), - ImString::default(), - ]; + let mut buffers = vec![String::default(), String::default(), String::default()]; system.main_loop(move |_, ui| { - Window::new(im_str!("Input text callbacks")) + Window::new("Input text callbacks") .size([500.0, 300.0], Condition::FirstUseEver) .build(ui, || { ui.text("You can make a variety of buffer callbacks on an Input Text"); @@ -28,9 +24,10 @@ fn main() { ui.separator(); ui.text("No callbacks:"); - ui.input_text(im_str!("buf0"), &mut buffers[0]).build(); - ui.input_text(im_str!("buf0"), &mut buffers[1]).build(); - ui.input_text(im_str!("buf0"), &mut buffers[2]).build(); + + ui.input_text("buf0", &mut buffers[0]).build(); + ui.input_text("buf1", &mut buffers[1]).build(); + ui.input_text("buf2", &mut buffers[2]).build(); ui.separator(); @@ -60,30 +57,27 @@ fn main() { } } - ui.input_text( - im_str!("All Callbacks logging"), - buffers.get_mut(0).unwrap(), - ) - .callback(InputTextCallback::all(), AllCallback) - .build(); + ui.input_text("All Callbacks logging", buffers.get_mut(0).unwrap()) + .callback(InputTextCallback::all(), AllCallback) + .build(); ui.separator(); ui.text("You can also define a callback on structs with data."); - ui.text("Here we implement the callback handler on a wrapper around &mut ImString"); + ui.text("Here we implement the callback handler on a wrapper around &mut String"); ui.text("to duplicate edits to buf0 on buf1"); - struct Wrapper<'a>(&'a mut ImString); + struct Wrapper<'a>(&'a mut String); impl<'a> InputTextCallbackHandler for Wrapper<'a> { fn on_always(&mut self, data: TextCallbackData<'_>) { - *self.0 = im_str!("{}", data.str()); + *self.0 = data.str().to_owned(); } } let (buf0, brwchk_dance) = buffers.split_first_mut().unwrap(); let buf1 = Wrapper(&mut brwchk_dance[0]); - ui.input_text(im_str!("Edits copied to buf1"), buf0) + ui.input_text("Edits copied to buf1", buf0) .callback(InputTextCallback::ALWAYS, buf1) .build(); @@ -136,11 +130,8 @@ fn main() { } } - ui.input_text(im_str!("Wild buf2 editor"), buf2) - .callback( - InputTextCallback::HISTORY, - Wrapper2(buf0.to_str(), buf1.to_str()), - ) + ui.input_text("Wild buf2 editor", buf2) + .callback(InputTextCallback::HISTORY, Wrapper2(buf0, buf1)) .build(); ui.text( diff --git a/imgui-gfx-examples/examples/gfx_custom_textures.rs b/imgui-gfx-examples/examples/gfx_custom_textures.rs index 8446b8e..4c92217 100644 --- a/imgui-gfx-examples/examples/gfx_custom_textures.rs +++ b/imgui-gfx-examples/examples/gfx_custom_textures.rs @@ -62,10 +62,10 @@ impl CustomTexturesApp { } fn show_textures(&self, ui: &Ui) { - Window::new(im_str!("Hello textures")) + Window::new("Hello textures") .size([400.0, 600.0], Condition::FirstUseEver) .build(ui, || { - ui.text(im_str!("Hello textures!")); + ui.text("Hello textures!"); if let Some(my_texture_id) = self.my_texture_id { ui.text("Some generated texture"); Image::new(my_texture_id, [100.0, 100.0]).build(ui); diff --git a/imgui-gfx-examples/examples/gfx_hello_world.rs b/imgui-gfx-examples/examples/gfx_hello_world.rs index 42a6b75..6c96b76 100644 --- a/imgui-gfx-examples/examples/gfx_hello_world.rs +++ b/imgui-gfx-examples/examples/gfx_hello_world.rs @@ -6,18 +6,18 @@ fn main() { let system = support::init(file!()); let window_title = if cfg!(all(feature = "directx", windows)) { - im_str!("Hello world (OpenGL)") + "Hello world (OpenGL)" } else { - im_str!("Hello world (DirectX)") + "Hello world (DirectX)" }; system.main_loop(|_, ui| { Window::new(window_title) .size([300.0, 100.0], Condition::FirstUseEver) .build(ui, || { - ui.text(im_str!("Hello world!")); - ui.text(im_str!("こんにちは世界!")); - ui.text(im_str!("This...is...imgui-rs!")); + ui.text("Hello world!"); + ui.text("こんにちは世界!"); + ui.text("This...is...imgui-rs!"); ui.separator(); let mouse_pos = ui.io().mouse_pos; ui.text(format!( diff --git a/imgui-gfx-renderer/src/lib.rs b/imgui-gfx-renderer/src/lib.rs index 7e9345e..e479ae1 100644 --- a/imgui-gfx-renderer/src/lib.rs +++ b/imgui-gfx-renderer/src/lib.rs @@ -10,9 +10,7 @@ use gfx::texture::{FilterMethod, SamplerInfo, WrapMode}; use gfx::traits::FactoryExt; use gfx::{CommandBuffer, Encoder, Factory, IntoIndexBuffer, Rect, Resources, Slice}; use imgui::internal::RawWrapper; -use imgui::{ - BackendFlags, DrawCmd, DrawCmdParams, DrawData, DrawIdx, ImString, TextureId, Textures, -}; +use imgui::{BackendFlags, DrawCmd, DrawCmdParams, DrawData, DrawIdx, TextureId, Textures}; use std::error::Error; use std::fmt; use std::usize; @@ -178,10 +176,10 @@ where instances: None, buffer: index_buffer.clone().into_index_buffer(factory), }; - ctx.set_renderer_name(Some(ImString::from(format!( + ctx.set_renderer_name(Some(format!( "imgui-gfx-renderer {}", env!("CARGO_PKG_VERSION") - )))); + ))); ctx.io_mut() .backend_flags .insert(BackendFlags::RENDERER_HAS_VTX_OFFSET); diff --git a/imgui-glium-renderer/src/lib.rs b/imgui-glium-renderer/src/lib.rs index 72d4aa6..723fae9 100644 --- a/imgui-glium-renderer/src/lib.rs +++ b/imgui-glium-renderer/src/lib.rs @@ -13,7 +13,7 @@ use glium::{ Surface, Texture2d, VertexBuffer, }; use imgui::internal::RawWrapper; -use imgui::{BackendFlags, DrawCmd, DrawCmdParams, DrawData, ImString, TextureId, Textures}; +use imgui::{BackendFlags, DrawCmd, DrawCmdParams, DrawData, TextureId, Textures}; use std::borrow::Cow; use std::error::Error; use std::fmt; @@ -143,10 +143,10 @@ impl Renderer { ) -> Result { let program = compile_default_program(facade)?; let font_texture = upload_font_texture(ctx.fonts(), facade.get_context())?; - ctx.set_renderer_name(Some(ImString::from(format!( + ctx.set_renderer_name(Some(format!( "imgui-glium-renderer {}", env!("CARGO_PKG_VERSION") - )))); + ))); ctx.io_mut() .backend_flags .insert(BackendFlags::RENDERER_HAS_VTX_OFFSET); diff --git a/imgui-glow-renderer/examples/04_custom_textures.rs b/imgui-glow-renderer/examples/04_custom_textures.rs index 2741203..efca79c 100644 --- a/imgui-glow-renderer/examples/04_custom_textures.rs +++ b/imgui-glow-renderer/examples/04_custom_textures.rs @@ -9,7 +9,7 @@ use std::{io::Cursor, time::Instant}; use glow::HasContext; use image::{jpeg::JpegDecoder, ImageDecoder}; -use imgui::{im_str, Condition}; +use imgui::Condition; use imgui_glow_renderer::Renderer; @@ -144,10 +144,10 @@ impl TexturesUi { } fn show(&self, ui: &imgui::Ui) { - imgui::Window::new(im_str!("Hello textures")) + imgui::Window::new("Hello textures") .size([400.0, 400.0], Condition::FirstUseEver) .build(ui, || { - ui.text(im_str!("Hello textures!")); + ui.text("Hello textures!"); ui.text("Some generated texture"); imgui::Image::new(self.generated_texture, [100.0, 100.0]).build(ui); @@ -157,7 +157,7 @@ impl TexturesUi { // Example of using custom textures on a button ui.text("The Lenna buttons"); { - ui.invisible_button(im_str!("Boring Button"), [100.0, 100.0]); + ui.invisible_button("Boring Button", [100.0, 100.0]); // See also `imgui::Ui::style_color` let tint_none = [1.0, 1.0, 1.0, 1.0]; let tint_green = [0.5, 1.0, 0.5, 1.0]; @@ -187,7 +187,7 @@ impl TexturesUi { ui.same_line(); // Button using quad positioned image - ui.invisible_button(im_str!("Exciting Button"), [100.0, 100.0]); + ui.invisible_button("Exciting Button", [100.0, 100.0]); // Button bounds let min = ui.item_rect_min(); @@ -214,7 +214,7 @@ impl TexturesUi { // Rounded image { ui.same_line(); - ui.invisible_button(im_str!("Smooth Button"), [100.0, 100.0]); + ui.invisible_button("Smooth Button", [100.0, 100.0]); let draw_list = ui.get_window_draw_list(); draw_list diff --git a/imgui-glow-renderer/src/lib.rs b/imgui-glow-renderer/src/lib.rs index 4379d16..f6d1814 100644 --- a/imgui-glow-renderer/src/lib.rs +++ b/imgui-glow-renderer/src/lib.rs @@ -513,9 +513,10 @@ impl Renderer { } fn configure_imgui_context(&self, imgui_context: &mut imgui::Context) { - imgui_context.set_renderer_name(Some( - format!("imgui-rs-glow-render {}", env!("CARGO_PKG_VERSION")).into(), - )); + imgui_context.set_renderer_name(Some(format!( + "imgui-rs-glow-render {}", + env!("CARGO_PKG_VERSION") + ))); #[cfg(feature = "vertex_offset_support")] if self.gl_version.vertex_offset_support() { diff --git a/imgui-sys/src/lib.rs b/imgui-sys/src/lib.rs index 3913d39..d7afc7f 100644 --- a/imgui-sys/src/lib.rs +++ b/imgui-sys/src/lib.rs @@ -18,7 +18,7 @@ // // ¹ The exception to this is that `std::os::raw` isn't there for `no_std`, and // `libc` has potentially undesirable linking impacts on windows. -extern crate chlorine as cty; +pub extern crate chlorine as cty; #[cfg(feature = "wasm")] mod wasm_bindings; diff --git a/imgui-winit-support/src/lib.rs b/imgui-winit-support/src/lib.rs index cc6d44f..126c2c0 100644 --- a/imgui-winit-support/src/lib.rs +++ b/imgui-winit-support/src/lib.rs @@ -174,7 +174,7 @@ use winit_20 as winit; ))] use winit_19 as winit; -use imgui::{self, BackendFlags, ConfigFlags, Context, ImString, Io, Key, Ui}; +use imgui::{self, BackendFlags, ConfigFlags, Context, Io, Key, Ui}; use std::cell::Cell; use std::cmp::Ordering; use winit::dpi::{LogicalPosition, LogicalSize}; @@ -459,10 +459,10 @@ impl WinitPlatform { io[Key::X] = VirtualKeyCode::X as _; io[Key::Y] = VirtualKeyCode::Y as _; io[Key::Z] = VirtualKeyCode::Z as _; - imgui.set_platform_name(Some(ImString::from(format!( + imgui.set_platform_name(Some(format!( "imgui-winit-support {}", env!("CARGO_PKG_VERSION") - )))); + ))); WinitPlatform { hidpi_mode: ActiveHiDpiMode::Default, hidpi_factor: 1.0, diff --git a/imgui/src/clipboard.rs b/imgui/src/clipboard.rs index e6ff182..7be4043 100644 --- a/imgui/src/clipboard.rs +++ b/imgui/src/clipboard.rs @@ -1,54 +1,78 @@ +use std::ffi::{CStr, CString}; use std::fmt; use std::os::raw::{c_char, c_void}; use std::panic::catch_unwind; use std::process; use std::ptr; -use crate::string::{ImStr, ImString}; +// use crate::string::{ImStr, ImString}; use crate::Ui; /// Trait for clipboard backends -pub trait ClipboardBackend { +pub trait ClipboardBackend: 'static { /// Returns the current clipboard contents as an owned imgui-rs string, or None if the /// clipboard is empty or inaccessible - fn get(&mut self) -> Option; + fn get(&mut self) -> Option; /// Sets the clipboard contents to the given imgui-rs string slice. - fn set(&mut self, value: &ImStr); + fn set(&mut self, value: &str); } pub(crate) struct ClipboardContext { backend: Box, // this is needed to keep ownership of the value when the raw C callback is called - last_value: ImString, + last_value: CString, } impl ClipboardContext { - #[inline] - pub fn new(backend: Box) -> ClipboardContext { + /// Creates a new [ClipboardContext]. This function previously took a `Box`, but now + /// is generic over the T it takes and boxes itself (which should be less strange). + pub fn new(backend: T) -> ClipboardContext { ClipboardContext { - backend, - last_value: ImString::default(), + backend: Box::new(backend) as Box, + last_value: CString::default(), } } + + pub fn dummy() -> ClipboardContext { + Self { + backend: Box::new(DummyClipboardContext), + last_value: CString::default(), + } + } +} + +pub struct DummyClipboardContext; +impl ClipboardBackend for DummyClipboardContext { + fn get(&mut self) -> Option { + None + } + + fn set(&mut self, _: &str) { + // empty + } } impl fmt::Debug for ClipboardContext { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "ClipboardContext({:?}, {:?})", - &(*self.backend) as *const _, - self.last_value - ) + f.debug_struct("ClipboardContext") + // beautiful code, no? + .field("backend", &(&(*self.backend) as *const _)) + .field("last_value", &self.last_value) + .finish() } } pub(crate) unsafe extern "C" fn get_clipboard_text(user_data: *mut c_void) -> *const c_char { let result = catch_unwind(|| { + println!("gettin!"); let ctx = &mut *(user_data as *mut ClipboardContext); + println!("gettin!"); + match ctx.backend.get() { Some(text) => { - ctx.last_value = text; + println!("gettin!"); + + ctx.last_value = CString::new(text).unwrap(); ctx.last_value.as_ptr() } None => ptr::null(), @@ -63,8 +87,8 @@ pub(crate) unsafe extern "C" fn get_clipboard_text(user_data: *mut c_void) -> *c pub(crate) unsafe extern "C" fn set_clipboard_text(user_data: *mut c_void, text: *const c_char) { let result = catch_unwind(|| { let ctx = &mut *(user_data as *mut ClipboardContext); - let text = ImStr::from_ptr_unchecked(text); - ctx.backend.set(text); + let text = CStr::from_ptr(text).to_owned(); + ctx.backend.set(text.to_str().unwrap()); }); result.unwrap_or_else(|_| { eprintln!("Clipboard setter panicked"); @@ -77,7 +101,7 @@ pub(crate) unsafe extern "C" fn set_clipboard_text(user_data: *mut c_void, text: impl<'ui> Ui<'ui> { /// Returns the current clipboard contents as text, or None if the clipboard is empty or cannot /// be accessed - pub fn clipboard_text(&self) -> Option { + pub fn clipboard_text(&self) -> Option { let io = self.io(); io.get_clipboard_text_fn.and_then(|get_clipboard_text_fn| { // Bypass FFI if we end up calling our own function anyway @@ -90,25 +114,32 @@ impl<'ui> Ui<'ui> { if text_ptr.is_null() || *text_ptr == b'\0' as c_char { None } else { - Some(ImStr::from_ptr_unchecked(text_ptr).to_owned()) + Some( + CStr::from_ptr(text_ptr) + .to_owned() + .to_str() + .ok()? + .to_owned(), + ) } } } }) } + /// Sets the clipboard contents. /// /// Does nothing if the clipboard cannot be accessed. - pub fn set_clipboard_text(&self, text: &ImStr) { + pub fn set_clipboard_text(&self, text: impl AsRef) { let io = self.io(); if let Some(set_clipboard_text_fn) = io.set_clipboard_text_fn { // Bypass FFI if we end up calling our own function anyway if set_clipboard_text_fn == set_clipboard_text { let ctx = unsafe { &mut *(io.clipboard_user_data as *mut ClipboardContext) }; - ctx.backend.set(text); + ctx.backend.set(text.as_ref()); } else { unsafe { - set_clipboard_text_fn(io.clipboard_user_data, text.as_ptr()); + set_clipboard_text_fn(io.clipboard_user_data, self.scratch_txt(text)); } } } diff --git a/imgui/src/columns.rs b/imgui/src/columns.rs index 2a631d5..b282e91 100644 --- a/imgui/src/columns.rs +++ b/imgui/src/columns.rs @@ -1,12 +1,11 @@ -use crate::string::ImStr; use crate::sys; use crate::Ui; /// # Columns impl<'ui> Ui<'ui> { #[doc(alias = "Columns")] - pub fn columns(&self, count: i32, id: &ImStr, border: bool) { - unsafe { sys::igColumns(count, id.as_ptr(), border) } + pub fn columns(&self, count: i32, id: impl AsRef, border: bool) { + unsafe { sys::igColumns(count, self.scratch_txt(id), border) } } /// Switches to the next column. /// diff --git a/imgui/src/context.rs b/imgui/src/context.rs index 433dc41..8139b3c 100644 --- a/imgui/src/context.rs +++ b/imgui/src/context.rs @@ -1,6 +1,6 @@ use parking_lot::ReentrantMutex; -use std::cell::RefCell; -use std::ffi::CStr; +use std::cell::{RefCell, UnsafeCell}; +use std::ffi::{CStr, CString}; use std::ops::Drop; use std::path::PathBuf; use std::ptr; @@ -9,7 +9,6 @@ use std::rc::Rc; use crate::clipboard::{ClipboardBackend, ClipboardContext}; use crate::fonts::atlas::{FontAtlas, FontAtlasRefMut, FontId, SharedFontAtlas}; use crate::io::Io; -use crate::string::{ImStr, ImString}; use crate::style::Style; use crate::sys; use crate::Ui; @@ -52,11 +51,15 @@ use crate::Ui; pub struct Context { raw: *mut sys::ImGuiContext, shared_font_atlas: Option>>, - ini_filename: Option, - log_filename: Option, - platform_name: Option, - renderer_name: Option, - clipboard_ctx: Option>, + ini_filename: Option, + log_filename: Option, + platform_name: Option, + renderer_name: Option, + // we need to box this because we hand imgui a pointer to it, + // and we don't want to deal with finding `clipboard_ctx`. + // we also put it in an unsafecell since we're going to give + // imgui a mutable pointer to it. + clipboard_ctx: Box>, } // This mutex needs to be used to guard all public functions that can affect the underlying @@ -109,18 +112,17 @@ impl Context { if io.ini_filename.is_null() { None } else { - let s = unsafe { ImStr::from_ptr_unchecked(io.ini_filename) }; - Some(PathBuf::from(s.to_str().to_owned())) + let s = unsafe { CStr::from_ptr(io.ini_filename) }; + Some(PathBuf::from(s.to_str().ok()?)) } } /// Sets the path to the ini file (default is "imgui.ini") /// /// Pass None to disable automatic .Ini saving. pub fn set_ini_filename>>(&mut self, ini_filename: T) { - let ini_filename = ini_filename - .into() - .and_then(|path| path.to_str().map(str::to_owned)) - .map(ImString::from); + let ini_filename: Option = ini_filename.into(); + let ini_filename = ini_filename.and_then(|v| CString::new(v.to_str()?).ok()); + self.io_mut().ini_filename = ini_filename .as_ref() .map(|x| x.as_ptr()) @@ -128,21 +130,22 @@ impl Context { self.ini_filename = ini_filename; } /// Returns the path to the log file, or None if not set + // TODO: why do we return an `Option` instead of an `Option<&Path>`? pub fn log_filename(&self) -> Option { let io = self.io(); if io.log_filename.is_null() { None } else { - let s = unsafe { ImStr::from_ptr_unchecked(io.log_filename) }; - Some(PathBuf::from(s.to_str().to_owned())) + let cstr = unsafe { CStr::from_ptr(io.log_filename) }; + Some(PathBuf::from(cstr.to_str().ok()?)) } } /// Sets the log filename (default is "imgui_log.txt"). pub fn set_log_filename>>(&mut self, log_filename: T) { let log_filename = log_filename .into() - .and_then(|path| path.to_str().map(str::to_owned)) - .map(ImString::from); + .and_then(|v| CString::new(v.to_str()?).ok()); + self.io_mut().log_filename = log_filename .as_ref() .map(|x| x.as_ptr()) @@ -150,17 +153,19 @@ impl Context { self.log_filename = log_filename; } /// Returns the backend platform name, or None if not set - pub fn platform_name(&self) -> Option<&ImStr> { + pub fn platform_name(&self) -> Option<&str> { let io = self.io(); if io.backend_platform_name.is_null() { None } else { - unsafe { Some(ImStr::from_ptr_unchecked(io.backend_platform_name)) } + let cstr = unsafe { CStr::from_ptr(io.backend_platform_name) }; + cstr.to_str().ok() } } /// Sets the backend platform name - pub fn set_platform_name>>(&mut self, platform_name: T) { - let platform_name = platform_name.into(); + pub fn set_platform_name>>(&mut self, platform_name: T) { + let platform_name: Option = + platform_name.into().and_then(|v| CString::new(v).ok()); self.io_mut().backend_platform_name = platform_name .as_ref() .map(|x| x.as_ptr()) @@ -168,21 +173,25 @@ impl Context { self.platform_name = platform_name; } /// Returns the backend renderer name, or None if not set - pub fn renderer_name(&self) -> Option<&ImStr> { + pub fn renderer_name(&self) -> Option<&str> { let io = self.io(); if io.backend_renderer_name.is_null() { None } else { - unsafe { Some(ImStr::from_ptr_unchecked(io.backend_renderer_name)) } + let cstr = unsafe { CStr::from_ptr(io.backend_renderer_name) }; + cstr.to_str().ok() } } /// Sets the backend renderer name - pub fn set_renderer_name>>(&mut self, renderer_name: T) { - let renderer_name = renderer_name.into(); + pub fn set_renderer_name>>(&mut self, renderer_name: T) { + let renderer_name: Option = + renderer_name.into().and_then(|v| CString::new(v).ok()); + self.io_mut().backend_renderer_name = renderer_name .as_ref() .map(|x| x.as_ptr()) .unwrap_or(ptr::null()); + self.renderer_name = renderer_name; } /// Loads settings from a string slice containing settings in .Ini file format @@ -197,14 +206,14 @@ impl Context { buf.push_str(&data.to_string_lossy()); } /// Sets the clipboard backend used for clipboard operations - pub fn set_clipboard_backend(&mut self, backend: Box) { - use std::borrow::BorrowMut; - let mut clipboard_ctx = Box::new(ClipboardContext::new(backend)); + pub fn set_clipboard_backend(&mut self, backend: T) { + let clipboard_ctx: Box> = Box::new(ClipboardContext::new(backend).into()); let io = self.io_mut(); io.set_clipboard_text_fn = Some(crate::clipboard::set_clipboard_text); io.get_clipboard_text_fn = Some(crate::clipboard::get_clipboard_text); - io.clipboard_user_data = clipboard_ctx.borrow_mut() as *mut ClipboardContext as *mut _; - self.clipboard_ctx.replace(clipboard_ctx); + + io.clipboard_user_data = clipboard_ctx.get() as *mut _; + self.clipboard_ctx = clipboard_ctx; } fn create_internal(shared_font_atlas: Option>>) -> Self { let _guard = CTX_MUTEX.lock(); @@ -231,7 +240,7 @@ impl Context { log_filename: None, platform_name: None, renderer_name: None, - clipboard_ctx: None, + clipboard_ctx: Box::new(ClipboardContext::dummy().into()), } } fn is_current_context(&self) -> bool { @@ -312,7 +321,7 @@ impl SuspendedContext { log_filename: None, platform_name: None, renderer_name: None, - clipboard_ctx: None, + clipboard_ctx: Box::new(ClipboardContext::dummy().into()), }; if ctx.is_current_context() { // Oops, the context was activated -> deactivate @@ -527,6 +536,7 @@ impl Context { Ui { ctx: self, font_atlas, + buffer: crate::UiBuffer::new(1024).into(), } } } diff --git a/imgui/src/drag_drop.rs b/imgui/src/drag_drop.rs index a4ca0d9..2fbf0b5 100644 --- a/imgui/src/drag_drop.rs +++ b/imgui/src/drag_drop.rs @@ -28,7 +28,7 @@ //! For examples of each payload type, see [DragDropSource]. use std::{any, ffi, marker::PhantomData}; -use crate::{sys, Condition, ImStr, Ui}; +use crate::{sys, Condition, Ui}; use bitflags::bitflags; bitflags!( @@ -75,10 +75,10 @@ bitflags!( /// ```no_run /// # use imgui::*; /// fn show_ui(ui: &Ui<'_>) { -/// ui.button(im_str!("Hello, I am a drag source!")); +/// ui.button("Hello, I am a drag source!"); /// /// // Creates an empty DragSource with no tooltip -/// DragDropSource::new(im_str!("BUTTON_DRAG")).begin(ui); +/// DragDropSource::new("BUTTON_DRAG").begin(ui); /// } /// ``` /// @@ -92,18 +92,17 @@ bitflags!( /// will manage, and then give to a [DragDropTarget], which will received the payload. The /// simplest and safest Payload is the empty payload, created with [begin](Self::begin). #[derive(Debug)] -pub struct DragDropSource<'a> { - name: &'a ImStr, +pub struct DragDropSource { + name: T, flags: DragDropFlags, cond: Condition, } -impl<'a> DragDropSource<'a> { +impl> DragDropSource { /// Creates a new [DragDropSource] with no flags and the `Condition::Always` with the given name. /// ImGui refers to this `name` field as a `type`, but really it's just an identifier to match up /// Source/Target for DragDrop. - #[inline] - pub const fn new(name: &'a ImStr) -> Self { + pub fn new(name: T) -> Self { Self { name, flags: DragDropFlags::empty(), @@ -116,14 +115,14 @@ impl<'a> DragDropSource<'a> { /// `SOURCE_EXTERN`, `SOURCE_AUTO_EXPIRE_PAYLOAD` make semantic sense, but any other flags will /// be accepted without panic. #[inline] - pub const fn flags(mut self, flags: DragDropFlags) -> Self { + pub fn flags(mut self, flags: DragDropFlags) -> Self { self.flags = flags; self } /// Sets the condition on the [DragDropSource]. Defaults to [Always](Condition::Always). #[inline] - pub const fn condition(mut self, cond: Condition) -> Self { + pub fn condition(mut self, cond: Condition) -> Self { self.cond = cond; self } @@ -143,9 +142,9 @@ impl<'a> DragDropSource<'a> { /// ```no_run /// # use imgui::*; /// fn show_ui(ui: &Ui<'_>, drop_message: &mut Option) { - /// ui.button(im_str!("Drag me!")); + /// ui.button("Drag me!"); /// - /// let drag_drop_name = im_str!("Test Drag"); + /// let drag_drop_name = "Test Drag"; /// /// // drag drop SOURCE /// if DragDropSource::new(drag_drop_name).begin(ui).is_some() { @@ -155,7 +154,7 @@ impl<'a> DragDropSource<'a> { /// *drop_message = Some("Test Payload".to_string()); /// } /// - /// ui.button(im_str!("Target me!")); + /// ui.button("Target me!"); /// /// // drag drop TARGET /// if let Some(target) = imgui::DragDropTarget::new(ui) { @@ -196,9 +195,9 @@ impl<'a> DragDropSource<'a> { /// ```no_run /// # use imgui::*; /// fn show_ui(ui: &Ui<'_>) { - /// ui.button(im_str!("Drag me!")); + /// ui.button("Drag me!"); /// - /// let drag_drop_name = im_str!("Test Drag"); + /// let drag_drop_name = "Test Drag"; /// let msg_to_send = "hello there sailor"; /// /// // drag drop SOURCE @@ -207,12 +206,12 @@ impl<'a> DragDropSource<'a> { /// tooltip.end(); /// } /// - /// ui.button(im_str!("Target me!")); + /// ui.button("Target me!"); /// /// // drag drop TARGET /// if let Some(target) = imgui::DragDropTarget::new(ui) { /// if let Some(Ok(payload_data)) = target - /// .accept_payload::<&'static str>(drag_drop_name, DragDropFlags::empty()) + /// .accept_payload::<&'static str, _>(drag_drop_name, DragDropFlags::empty()) /// { /// let msg = payload_data.data; /// assert_eq!(msg, msg_to_send); @@ -223,17 +222,17 @@ impl<'a> DragDropSource<'a> { /// } /// ``` #[inline] - pub fn begin_payload<'ui, T: Copy + 'static>( + pub fn begin_payload<'ui, P: Copy + 'static>( self, ui: &Ui<'ui>, - payload: T, + payload: P, ) -> Option> { unsafe { let payload = TypedPayload::new(payload); self.begin_payload_unchecked( ui, &payload as *const _ as *const ffi::c_void, - std::mem::size_of::>(), + std::mem::size_of::>(), ) } } @@ -267,14 +266,14 @@ impl<'a> DragDropSource<'a> { #[inline] pub unsafe fn begin_payload_unchecked<'ui>( &self, - _ui: &Ui<'ui>, + ui: &Ui<'ui>, ptr: *const ffi::c_void, size: usize, ) -> Option> { let should_begin = sys::igBeginDragDropSource(self.flags.bits() as i32); if should_begin { - sys::igSetDragDropPayload(self.name.as_ptr(), ptr, size, self.cond as i32); + sys::igSetDragDropPayload(ui.scratch_txt(&self.name), ptr, size, self.cond as i32); Some(DragDropSourceToolTip::push()) } else { @@ -313,18 +312,18 @@ impl Drop for DragDropSourceToolTip<'_> { /// # use imgui::*; /// fn show_ui(ui: &Ui<'_>) { /// // Drop something on this button please! -/// ui.button(im_str!("Hello, I am a drag Target!")); +/// ui.button("Hello, I am a drag Target!"); /// /// if let Some(target) = DragDropTarget::new(ui) { /// // accepting an empty payload (which is really just raising an event) -/// if let Some(_payload_data) = target.accept_payload_empty(im_str!("BUTTON_DRAG"), DragDropFlags::empty()) { +/// if let Some(_payload_data) = target.accept_payload_empty("BUTTON_DRAG", DragDropFlags::empty()) { /// println!("Nice job getting on the payload!"); /// } /// /// // and we can accept multiple, different types of payloads with one drop target. /// // this is a good pattern for handling different kinds of drag/drop situations with /// // different kinds of data in them. -/// if let Some(Ok(payload_data)) = target.accept_payload::(im_str!("BUTTON_ID"), DragDropFlags::empty()) { +/// if let Some(Ok(payload_data)) = target.accept_payload::("BUTTON_ID", DragDropFlags::empty()) { /// println!("Our payload's data was {}", payload_data.data); /// } /// } @@ -340,17 +339,17 @@ impl Drop for DragDropSourceToolTip<'_> { /// on this struct. Each of these methods will spit out a _Payload struct with an increasing /// amount of information on the Payload. The absolute safest solution is [accept_payload_empty](Self::accept_payload_empty). #[derive(Debug)] -pub struct DragDropTarget<'ui>(PhantomData>); +pub struct DragDropTarget<'ui>(&'ui Ui<'ui>); impl<'ui> DragDropTarget<'ui> { /// Creates a new DragDropTarget, holding the [Ui]'s lifetime for the duration /// of its existence. This is required since this struct runs some code on its Drop /// to end the DragDropTarget code. #[doc(alias = "BeginDragDropTarget")] - pub fn new(_ui: &Ui<'_>) -> Option { + pub fn new(ui: &'ui Ui<'ui>) -> Option { let should_begin = unsafe { sys::igBeginDragDropTarget() }; if should_begin { - Some(Self(PhantomData)) + Some(Self(ui)) } else { None } @@ -364,12 +363,12 @@ impl<'ui> DragDropTarget<'ui> { /// to use this function. Use `accept_payload_unchecked` instead pub fn accept_payload_empty( &self, - name: &ImStr, + name: impl AsRef, flags: DragDropFlags, ) -> Option { - self.accept_payload::<()>(name, flags)? + self.accept_payload(name, flags)? .ok() - .map(|payload_pod| DragDropPayloadEmpty { + .map(|payload_pod: DragDropPayloadPod<()>| DragDropPayloadEmpty { preview: payload_pod.preview, delivery: payload_pod.delivery, }) @@ -380,9 +379,9 @@ impl<'ui> DragDropTarget<'ui> { /// /// Note: If you began this operation with `begin_payload_unchecked` it always incorrect /// to use this function. Use `accept_payload_unchecked` instead - pub fn accept_payload( + pub fn accept_payload>( &self, - name: &ImStr, + name: Name, flags: DragDropFlags, ) -> Option, PayloadIsWrongType>> { let output = unsafe { self.accept_payload_unchecked(name, flags) }; @@ -435,10 +434,10 @@ impl<'ui> DragDropTarget<'ui> { /// of the various edge cases. pub unsafe fn accept_payload_unchecked( &self, - name: &ImStr, + name: impl AsRef, flags: DragDropFlags, ) -> Option { - let inner = sys::igAcceptDragDropPayload(name.as_ptr(), flags.bits() as i32); + let inner = sys::igAcceptDragDropPayload(self.0.scratch_txt(name), flags.bits() as i32); if inner.is_null() { None } else { diff --git a/imgui/src/input_widget.rs b/imgui/src/input_widget.rs index 5dd6290..9c99169 100644 --- a/imgui/src/input_widget.rs +++ b/imgui/src/input_widget.rs @@ -1,10 +1,9 @@ use bitflags::bitflags; -use std::marker::PhantomData; use std::ops::Range; use std::os::raw::{c_char, c_int, c_void}; use crate::sys; -use crate::{ImStr, ImString, Ui}; +use crate::Ui; bitflags!( /// Flags for text inputs @@ -160,17 +159,17 @@ macro_rules! impl_step_params { } #[must_use] -pub struct InputText<'ui, 'p, T = PassthroughCallback> { - label: &'p ImStr, - hint: Option<&'p ImStr>, - buf: &'p mut ImString, +pub struct InputText<'ui, 'p, L, H = &'static str, T = PassthroughCallback> { + label: L, + hint: Option, + buf: &'p mut String, callback_handler: T, flags: InputTextFlags, - _phantom: PhantomData<&'ui Ui<'ui>>, + ui: &'ui Ui<'ui>, } -impl<'ui, 'p> InputText<'ui, 'p, PassthroughCallback> { - pub fn new(_: &Ui<'ui>, label: &'p ImStr, buf: &'p mut ImString) -> Self { +impl<'ui, 'p, L: AsRef> InputText<'ui, 'p, L> { + pub fn new(ui: &'ui Ui<'ui>, label: L, buf: &'p mut String) -> Self { InputText { label, hint: None, @@ -178,17 +177,28 @@ impl<'ui, 'p> InputText<'ui, 'p, PassthroughCallback> { callback_handler: PassthroughCallback, buf, flags: InputTextFlags::CALLBACK_RESIZE, - _phantom: PhantomData, + ui, } } } -impl<'ui, 'p, T: InputTextCallbackHandler> InputText<'ui, 'p, T> { +impl<'ui, 'p, T, L, H> InputText<'ui, 'p, L, H, T> +where + L: AsRef, + H: AsRef, + T: InputTextCallbackHandler, +{ /// Sets the hint displayed in the input text background. #[inline] - pub fn hint(mut self, hint: &'p ImStr) -> Self { - self.hint = Some(hint); - self + pub fn hint>(self, hint: H2) -> InputText<'ui, 'p, L, H2, T> { + InputText { + label: self.label, + hint: Some(hint), + buf: self.buf, + callback_handler: self.callback_handler, + flags: self.flags, + ui: self.ui, + } } impl_text_flags!(InputText); @@ -198,8 +208,14 @@ impl<'ui, 'p, T: InputTextCallbackHandler> InputText<'ui, 'p, T> { /// /// If, for some reason, you don't want this, you can run this function to prevent this. /// In that case, edits which would cause a resize will not occur. + /// + /// # Safety + /// Importantly, we silently push and pop a `\0` to the string given here. + /// If you do not want mutable access (ie, do not want that string to resize), + /// you **must** make sure to null-terminate it yourself. This is janky, but ImGui + /// expects a null termination, and we didn't want to re-allocate an entire string per call. #[inline] - pub fn do_not_resize(mut self) -> Self { + pub unsafe fn do_not_resize(mut self) -> Self { self.flags.remove(InputTextFlags::CALLBACK_RESIZE); self } @@ -209,7 +225,7 @@ impl<'ui, 'p, T: InputTextCallbackHandler> InputText<'ui, 'p, T> { mut self, callbacks: InputTextCallback, callback: T2, - ) -> InputText<'ui, 'p, T2> { + ) -> InputText<'ui, 'p, L, H, T2> { if callbacks.contains(InputTextCallback::COMPLETION) { self.flags.insert(InputTextFlags::CALLBACK_COMPLETION); } @@ -231,12 +247,14 @@ impl<'ui, 'p, T: InputTextCallbackHandler> InputText<'ui, 'p, T> { hint: self.hint, buf: self.buf, flags: self.flags, - _phantom: self._phantom, + ui: self.ui, } } pub fn build(self) -> bool { - let (ptr, capacity) = (self.buf.as_mut_ptr(), self.buf.capacity_with_nul()); + // needs to be null-terminated! + self.buf.push('\0'); + let (ptr, capacity) = (self.buf.as_mut_ptr(), self.buf.capacity()); let mut data = UserData { container: self.buf, @@ -244,58 +262,65 @@ impl<'ui, 'p, T: InputTextCallbackHandler> InputText<'ui, 'p, T> { }; let data = &mut data as *mut _ as *mut c_void; - unsafe { - let result = if let Some(hint) = self.hint { + let o = unsafe { + if let Some(hint) = self.hint { + let (label, hint) = self.ui.scratch_txt_two(self.label, hint); sys::igInputTextWithHint( - self.label.as_ptr(), - hint.as_ptr(), - ptr, + label, + hint, + ptr as *mut sys::cty::c_char, capacity, self.flags.bits() as i32, Some(callback::), data, ) } else { + let label = self.ui.scratch_txt(self.label); + sys::igInputText( - self.label.as_ptr(), - ptr, + label, + ptr as *mut sys::cty::c_char, capacity, self.flags.bits() as i32, Some(callback::), data, ) - }; - self.buf.refresh_len(); - result + } + }; + + // it should always end with this \0. + if self.buf.ends_with('\0') { + self.buf.pop(); } + + o } } #[must_use] -pub struct InputTextMultiline<'ui, 'p, T = PassthroughCallback> { - label: &'p ImStr, - buf: &'p mut ImString, +pub struct InputTextMultiline<'ui, 'p, L, T = PassthroughCallback> { + label: L, + buf: &'p mut String, flags: InputTextFlags, size: [f32; 2], callback_handler: T, - _phantom: PhantomData<&'ui Ui<'ui>>, + ui: &'ui Ui<'ui>, } -impl<'ui, 'p> InputTextMultiline<'ui, 'p, PassthroughCallback> { - pub fn new(_: &Ui<'ui>, label: &'p ImStr, buf: &'p mut ImString, size: [f32; 2]) -> Self { +impl<'ui, 'p, L: AsRef> InputTextMultiline<'ui, 'p, L, PassthroughCallback> { + pub fn new(ui: &'ui Ui<'ui>, label: L, buf: &'p mut String, size: [f32; 2]) -> Self { InputTextMultiline { label, buf, flags: InputTextFlags::CALLBACK_RESIZE, size, - // this is safe because callback_handler: PassthroughCallback, - _phantom: PhantomData, + ui, } } } -impl<'ui, 'p, T: InputTextCallbackHandler> InputTextMultiline<'ui, 'p, T> { +impl<'ui, 'p, T: InputTextCallbackHandler, L: AsRef> InputTextMultiline<'ui, 'p, L, T> { impl_text_flags!(InputText); /// By default (as of 0.8.0), imgui-rs will automatically handle string resizes @@ -314,7 +339,7 @@ impl<'ui, 'p, T: InputTextCallbackHandler> InputTextMultiline<'ui, 'p, T> { mut self, callbacks: InputTextMultilineCallback, callback_handler: T2, - ) -> InputTextMultiline<'ui, 'p, T2> { + ) -> InputTextMultiline<'ui, 'p, L, T2> { if callbacks.contains(InputTextMultilineCallback::COMPLETION) { self.flags.insert(InputTextFlags::CALLBACK_COMPLETION); } @@ -334,12 +359,14 @@ impl<'ui, 'p, T: InputTextCallbackHandler> InputTextMultiline<'ui, 'p, T> { flags: self.flags, size: self.size, callback_handler, - _phantom: self._phantom, + ui: self.ui, } } pub fn build(self) -> bool { - let (ptr, capacity) = (self.buf.as_mut_ptr(), self.buf.capacity_with_nul()); + // needs to be null-terminated! + self.buf.push('\0'); + let (ptr, capacity) = (self.buf.as_mut_ptr(), self.buf.capacity()); let mut data = UserData { container: self.buf, @@ -347,48 +374,53 @@ impl<'ui, 'p, T: InputTextCallbackHandler> InputTextMultiline<'ui, 'p, T> { }; let data = &mut data as *mut _ as *mut c_void; - unsafe { - let result = sys::igInputTextMultiline( - self.label.as_ptr(), - ptr, + let o = unsafe { + sys::igInputTextMultiline( + self.ui.scratch_txt(self.label), + ptr as *mut sys::cty::c_char, capacity, self.size.into(), self.flags.bits() as i32, Some(callback::), data, - ); - self.buf.refresh_len(); - result + ) + }; + + // it should always end with this \0. + if self.buf.ends_with('\0') { + self.buf.pop(); } + + o } } #[must_use] -pub struct InputInt<'ui, 'p> { - label: &'p ImStr, +pub struct InputInt<'ui, 'p, L> { + label: L, value: &'p mut i32, step: i32, step_fast: i32, flags: InputTextFlags, - _phantom: PhantomData<&'ui Ui<'ui>>, + ui: &'ui Ui<'ui>, } -impl<'ui, 'p> InputInt<'ui, 'p> { - pub fn new(_: &Ui<'ui>, label: &'p ImStr, value: &'p mut i32) -> Self { +impl<'ui, 'p, L: AsRef> InputInt<'ui, 'p, L> { + pub fn new(ui: &'ui Ui<'ui>, label: L, value: &'p mut i32) -> Self { InputInt { label, value, step: 1, step_fast: 100, flags: InputTextFlags::empty(), - _phantom: PhantomData, + ui, } } pub fn build(self) -> bool { unsafe { sys::igInputInt( - self.label.as_ptr(), + self.ui.scratch_txt(self.label), self.value as *mut i32, self.step, self.step_fast, @@ -402,31 +434,31 @@ impl<'ui, 'p> InputInt<'ui, 'p> { } #[must_use] -pub struct InputFloat<'ui, 'p> { - label: &'p ImStr, +pub struct InputFloat<'ui, 'p, L> { + label: L, value: &'p mut f32, step: f32, step_fast: f32, flags: InputTextFlags, - _phantom: PhantomData<&'ui Ui<'ui>>, + ui: &'ui Ui<'ui>, } -impl<'ui, 'p> InputFloat<'ui, 'p> { - pub fn new(_: &Ui<'ui>, label: &'p ImStr, value: &'p mut f32) -> Self { +impl<'ui, 'p, L: AsRef> InputFloat<'ui, 'p, L> { + pub fn new(ui: &'ui Ui<'ui>, label: L, value: &'p mut f32) -> Self { InputFloat { label, value, step: 0.0, step_fast: 0.0, flags: InputTextFlags::empty(), - _phantom: PhantomData, + ui, } } pub fn build(self) -> bool { unsafe { sys::igInputFloat( - self.label.as_ptr(), + self.ui.scratch_txt(self.label), self.value as *mut f32, self.step, self.step_fast, @@ -443,27 +475,27 @@ impl<'ui, 'p> InputFloat<'ui, 'p> { macro_rules! impl_input_floatn { ($InputFloatN:ident, $N:expr, $igInputFloatN:ident) => { #[must_use] - pub struct $InputFloatN<'ui, 'p> { - label: &'p ImStr, + pub struct $InputFloatN<'ui, 'p, L> { + label: L, value: &'p mut [f32; $N], flags: InputTextFlags, - _phantom: PhantomData<&'ui Ui<'ui>>, + ui: &'ui Ui<'ui>, } - impl<'ui, 'p> $InputFloatN<'ui, 'p> { - pub fn new(_: &Ui<'ui>, label: &'p ImStr, value: &'p mut [f32; $N]) -> Self { + impl<'ui, 'p, L: AsRef> $InputFloatN<'ui, 'p, L> { + pub fn new(ui: &'ui Ui<'ui>, label: L, value: &'p mut [f32; $N]) -> Self { $InputFloatN { label, value, flags: InputTextFlags::empty(), - _phantom: PhantomData, + ui, } } pub fn build(self) -> bool { unsafe { sys::$igInputFloatN( - self.label.as_ptr(), + self.ui.scratch_txt(self.label), self.value.as_mut_ptr(), b"%.3f\0".as_ptr() as *const _, self.flags.bits() as i32, @@ -483,27 +515,27 @@ impl_input_floatn!(InputFloat4, 4, igInputFloat4); macro_rules! impl_input_intn { ($InputIntN:ident, $N:expr, $igInputIntN:ident) => { #[must_use] - pub struct $InputIntN<'ui, 'p> { - label: &'p ImStr, + pub struct $InputIntN<'ui, 'p, L> { + label: L, value: &'p mut [i32; $N], flags: InputTextFlags, - _phantom: PhantomData<&'ui Ui<'ui>>, + ui: &'ui Ui<'ui>, } - impl<'ui, 'p> $InputIntN<'ui, 'p> { - pub fn new(_: &Ui<'ui>, label: &'p ImStr, value: &'p mut [i32; $N]) -> Self { + impl<'ui, 'p, L: AsRef> $InputIntN<'ui, 'p, L> { + pub fn new(ui: &'ui Ui<'ui>, label: L, value: &'p mut [i32; $N]) -> Self { $InputIntN { label, value, flags: InputTextFlags::empty(), - _phantom: PhantomData, + ui, } } pub fn build(self) -> bool { unsafe { sys::$igInputIntN( - self.label.as_ptr(), + self.ui.scratch_txt(self.label), self.value.as_mut_ptr(), self.flags.bits() as i32, ) @@ -809,7 +841,7 @@ impl<'a> TextCallbackData<'a> { #[repr(C)] struct UserData<'a, T> { - container: &'a mut ImString, + container: &'a mut String, cback_handler: T, } @@ -849,14 +881,22 @@ extern "C" fn callback( } InputTextFlags::CALLBACK_RESIZE => { unsafe { - let requested_size = (*data).BufSize as usize; - let buffer = &mut callback_data.user_data.container; - if requested_size > buffer.capacity_with_nul() { - // Refresh the buffer's length to take into account changes made by dear imgui. - buffer.refresh_len(); - buffer.reserve(requested_size - buffer.0.len()); - debug_assert!(buffer.capacity_with_nul() >= requested_size); - (*data).Buf = buffer.as_mut_ptr(); + let requested_size = (*data).BufTextLen as usize; + + // just confirm that we ARE working with our string. + debug_assert_eq!( + callback_data.user_data.container.as_ptr() as *const _, + (*data).Buf + ); + + if requested_size > callback_data.user_data.container.capacity() { + // reserve more data... + callback_data + .user_data + .container + .reserve(requested_size - callback_data.user_data.container.capacity()); + + (*data).Buf = callback_data.user_data.container.as_mut_ptr() as *mut _; (*data).BufDirty = true; } } diff --git a/imgui/src/lib.rs b/imgui/src/lib.rs index 8bfe9bf..26fee3d 100644 --- a/imgui/src/lib.rs +++ b/imgui/src/lib.rs @@ -1,4 +1,4 @@ -#![allow(clippy::float_cmp)] +#![cfg_attr(debug_assertions, allow(clippy::float_cmp))] pub extern crate imgui_sys as sys; use std::cell; @@ -117,12 +117,53 @@ impl Context { } /// A temporary reference for building the user interface for one frame +#[derive(Debug)] pub struct Ui<'ui> { ctx: &'ui Context, font_atlas: Option>, + // imgui isn't mutli-threaded -- so no one will ever access twice. + buffer: cell::UnsafeCell, } impl<'ui> Ui<'ui> { + /// Internal method to push a single text to our scratch buffer. + fn scratch_txt(&self, txt: impl AsRef) -> *const sys::cty::c_char { + unsafe { + let handle = &mut *self.buffer.get(); + handle.scratch_txt(txt) + } + } + + /// Internal method to push an option text to our scratch buffer. + fn scratch_txt_opt(&self, txt: Option>) -> *const sys::cty::c_char { + unsafe { + let handle = &mut *self.buffer.get(); + handle.scratch_txt_opt(txt) + } + } + + fn scratch_txt_two( + &self, + txt_0: impl AsRef, + txt_1: impl AsRef, + ) -> (*const sys::cty::c_char, *const sys::cty::c_char) { + unsafe { + let handle = &mut *self.buffer.get(); + handle.scratch_txt_two(txt_0, txt_1) + } + } + + fn scratch_txt_with_opt( + &self, + txt_0: impl AsRef, + txt_1: Option>, + ) -> (*const sys::cty::c_char, *const sys::cty::c_char) { + unsafe { + let handle = &mut *self.buffer.get(); + handle.scratch_txt_with_opt(txt_0, txt_1) + } + } + /// Returns an immutable reference to the inputs/outputs object #[doc(alias = "GetIO")] pub fn io(&self) -> &Io { @@ -250,59 +291,83 @@ impl From<*mut T> for Id<'static> { // Widgets: Input impl<'ui> Ui<'ui> { #[doc(alias = "InputText", alias = "InputTextWithHint")] - pub fn input_text<'p>(&self, label: &'p ImStr, buf: &'p mut ImString) -> InputText<'ui, 'p> { + pub fn input_text<'p, L: AsRef>( + &'ui self, + label: L, + buf: &'p mut String, + ) -> InputText<'ui, 'p, L> { InputText::new(self, label, buf) } #[doc(alias = "InputText", alias = "InputTextMultiline")] - pub fn input_text_multiline<'p>( - &self, - label: &'p ImStr, - buf: &'p mut ImString, + pub fn input_text_multiline<'p, L: AsRef>( + &'ui self, + label: L, + buf: &'p mut String, size: [f32; 2], - ) -> InputTextMultiline<'ui, 'p> { + ) -> InputTextMultiline<'ui, 'p, L> { InputTextMultiline::new(self, label, buf, size) } #[doc(alias = "InputFloat2")] - pub fn input_float<'p>(&self, label: &'p ImStr, value: &'p mut f32) -> InputFloat<'ui, 'p> { + pub fn input_float<'p, L: AsRef>( + &'ui self, + label: L, + value: &'p mut f32, + ) -> InputFloat<'ui, 'p, L> { InputFloat::new(self, label, value) } - pub fn input_float2<'p>( - &self, - label: &'p ImStr, + pub fn input_float2<'p, L: AsRef>( + &'ui self, + label: L, value: &'p mut [f32; 2], - ) -> InputFloat2<'ui, 'p> { + ) -> InputFloat2<'ui, 'p, L> { InputFloat2::new(self, label, value) } #[doc(alias = "InputFloat3")] - pub fn input_float3<'p>( - &self, - label: &'p ImStr, + pub fn input_float3<'p, L: AsRef>( + &'ui self, + label: L, value: &'p mut [f32; 3], - ) -> InputFloat3<'ui, 'p> { + ) -> InputFloat3<'ui, 'p, L> { InputFloat3::new(self, label, value) } #[doc(alias = "InputFloat4")] - pub fn input_float4<'p>( - &self, - label: &'p ImStr, + pub fn input_float4<'p, L: AsRef>( + &'ui self, + label: L, value: &'p mut [f32; 4], - ) -> InputFloat4<'ui, 'p> { + ) -> InputFloat4<'ui, 'p, L> { InputFloat4::new(self, label, value) } #[doc(alias = "InputInt")] - pub fn input_int<'p>(&self, label: &'p ImStr, value: &'p mut i32) -> InputInt<'ui, 'p> { + pub fn input_int<'p, L: AsRef>( + &'ui self, + label: L, + value: &'p mut i32, + ) -> InputInt<'ui, 'p, L> { InputInt::new(self, label, value) } #[doc(alias = "InputInt2")] - pub fn input_int2<'p>(&self, label: &'p ImStr, value: &'p mut [i32; 2]) -> InputInt2<'ui, 'p> { + pub fn input_int2<'p, L: AsRef>( + &'ui self, + label: L, + value: &'p mut [i32; 2], + ) -> InputInt2<'ui, 'p, L> { InputInt2::new(self, label, value) } #[doc(alias = "InputInt3")] - pub fn input_int3<'p>(&self, label: &'p ImStr, value: &'p mut [i32; 3]) -> InputInt3<'ui, 'p> { + pub fn input_int3<'p, L: AsRef>( + &'ui self, + label: L, + value: &'p mut [i32; 3], + ) -> InputInt3<'ui, 'p, L> { InputInt3::new(self, label, value) } #[doc(alias = "InputInt4")] - pub fn input_int4<'p>(&self, label: &'p ImStr, value: &'p mut [i32; 4]) -> InputInt4<'ui, 'p> { + pub fn input_int4<'p, L: AsRef>( + &'ui self, + label: L, + value: &'p mut [i32; 4], + ) -> InputInt4<'ui, 'p, L> { InputInt4::new(self, label, value) } } @@ -373,18 +438,27 @@ impl<'ui> Ui<'ui> { // Widgets: ListBox impl<'ui> Ui<'ui> { #[doc(alias = "ListBox")] - pub fn list_box<'p, StringType: AsRef + ?Sized>( + pub fn list_box<'p, StringType: AsRef + ?Sized>( &self, - label: &'p ImStr, + label: impl AsRef, current_item: &mut i32, items: &'p [&'p StringType], height_in_items: i32, ) -> bool { - let items_inner: Vec<*const c_char> = - items.iter().map(|item| item.as_ref().as_ptr()).collect(); + let (label_ptr, items_inner) = unsafe { + let handle = &mut *self.buffer.get(); + + handle.refresh_buffer(); + let label_ptr = handle.push(label); + + let items_inner: Vec<_> = items.iter().map(|&v| handle.push(v)).collect(); + + (label_ptr, items_inner) + }; + unsafe { sys::igListBoxStr_arr( - label.as_ptr(), + label_ptr, current_item, items_inner.as_ptr() as *mut *const c_char, items_inner.len() as i32, @@ -392,22 +466,61 @@ impl<'ui> Ui<'ui> { ) } } + + // written out for the future times... + // #[doc(alias = "ListBox")] + // pub fn list_box_const<'p, StringType: AsRef + ?Sized, const N: usize>( + // &self, + // label: impl AsRef, + // current_item: &mut i32, + // items: [&'p StringType; N], + // height_in_items: i32, + // ) -> bool { + // let (label_ptr, items_inner) = unsafe { + // let handle = &mut *self.buffer.get(); + + // handle.refresh_buffer(); + // let label_ptr = handle.push(label); + + // let mut items_inner: [*const i8; N] = [std::ptr::null(); N]; + + // for (i, item) in items.iter().enumerate() { + // items_inner[i] = handle.push(item); + // } + + // (label_ptr, items_inner) + // }; + + // unsafe { + // sys::igListBoxStr_arr( + // label_ptr, + // current_item, + // items_inner.as_ptr() as *mut *const c_char, + // items_inner.len() as i32, + // height_in_items, + // ) + // } + // } } impl<'ui> Ui<'ui> { #[doc(alias = "PlotLines")] - pub fn plot_lines<'p>(&self, label: &'p ImStr, values: &'p [f32]) -> PlotLines<'ui, 'p> { + pub fn plot_lines<'p, Label: AsRef>( + &'ui self, + label: Label, + values: &'p [f32], + ) -> PlotLines<'ui, 'p, Label> { PlotLines::new(self, label, values) } } impl<'ui> Ui<'ui> { #[doc(alias = "PlotHistogram")] - pub fn plot_histogram<'p>( - &self, - label: &'p ImStr, + pub fn plot_histogram<'p, Label: AsRef>( + &'ui self, + label: Label, values: &'p [f32], - ) -> PlotHistogram<'ui, 'p> { + ) -> PlotHistogram<'ui, 'p, Label> { PlotHistogram::new(self, label, values) } } diff --git a/imgui/src/plothistogram.rs b/imgui/src/plothistogram.rs index e58c472..a88aabf 100644 --- a/imgui/src/plothistogram.rs +++ b/imgui/src/plothistogram.rs @@ -1,23 +1,22 @@ -use std::marker::PhantomData; use std::os::raw::c_float; -use std::{f32, mem, ptr}; +use std::{f32, mem}; -use super::{ImStr, Ui}; +use super::Ui; #[must_use] -pub struct PlotHistogram<'ui, 'p> { - label: &'p ImStr, +pub struct PlotHistogram<'ui, 'p, Label, Overlay = &'static str> { + label: Label, values: &'p [f32], values_offset: usize, - overlay_text: Option<&'p ImStr>, + overlay_text: Option, scale_min: f32, scale_max: f32, graph_size: [f32; 2], - _phantom: PhantomData<&'ui Ui<'ui>>, + ui: &'ui Ui<'ui>, } -impl<'ui, 'p> PlotHistogram<'ui, 'p> { - pub const fn new(_: &Ui<'ui>, label: &'p ImStr, values: &'p [f32]) -> Self { +impl<'ui, 'p, Label: AsRef> PlotHistogram<'ui, 'p, Label> { + pub fn new(ui: &'ui Ui<'ui>, label: Label, values: &'p [f32]) -> Self { PlotHistogram { label, values, @@ -26,48 +25,58 @@ impl<'ui, 'p> PlotHistogram<'ui, 'p> { scale_min: f32::MAX, scale_max: f32::MAX, graph_size: [0.0, 0.0], - _phantom: PhantomData, + ui, } } +} - #[inline] - pub const fn values_offset(mut self, values_offset: usize) -> Self { +impl<'ui, 'p, Label: AsRef, Overlay: AsRef> PlotHistogram<'ui, 'p, Label, Overlay> { + pub fn values_offset(mut self, values_offset: usize) -> Self { self.values_offset = values_offset; self } - #[inline] - pub const fn overlay_text(mut self, overlay_text: &'p ImStr) -> Self { - self.overlay_text = Some(overlay_text); - self + pub fn overlay_text>( + self, + overlay_text: NewOverlay, + ) -> PlotHistogram<'ui, 'p, Label, NewOverlay> { + PlotHistogram { + label: self.label, + values: self.values, + values_offset: self.values_offset, + overlay_text: Some(overlay_text), + scale_min: self.scale_min, + scale_max: self.scale_max, + graph_size: self.graph_size, + ui: self.ui, + } } - #[inline] - pub const fn scale_min(mut self, scale_min: f32) -> Self { + pub fn scale_min(mut self, scale_min: f32) -> Self { self.scale_min = scale_min; self } - #[inline] - pub const fn scale_max(mut self, scale_max: f32) -> Self { + pub fn scale_max(mut self, scale_max: f32) -> Self { self.scale_max = scale_max; self } - #[inline] - pub const fn graph_size(mut self, graph_size: [f32; 2]) -> Self { + pub fn graph_size(mut self, graph_size: [f32; 2]) -> Self { self.graph_size = graph_size; self } pub fn build(self) { unsafe { + let (label, overlay_text) = self.ui.scratch_txt_with_opt(self.label, self.overlay_text); + sys::igPlotHistogramFloatPtr( - self.label.as_ptr(), + label, self.values.as_ptr() as *const c_float, self.values.len() as i32, self.values_offset as i32, - self.overlay_text.map(|x| x.as_ptr()).unwrap_or(ptr::null()), + overlay_text, self.scale_min, self.scale_max, self.graph_size.into(), diff --git a/imgui/src/plotlines.rs b/imgui/src/plotlines.rs index 9f7f4c1..2390b3e 100644 --- a/imgui/src/plotlines.rs +++ b/imgui/src/plotlines.rs @@ -1,23 +1,22 @@ -use std::marker::PhantomData; use std::os::raw::c_float; -use std::{f32, mem, ptr}; +use std::{f32, mem}; -use super::{ImStr, Ui}; +use super::Ui; #[must_use] -pub struct PlotLines<'ui, 'p> { - label: &'p ImStr, +pub struct PlotLines<'ui, 'p, Label, Overlay = &'static str> { + label: Label, values: &'p [f32], values_offset: usize, - overlay_text: Option<&'p ImStr>, + overlay_text: Option, scale_min: f32, scale_max: f32, graph_size: [f32; 2], - _phantom: PhantomData<&'ui Ui<'ui>>, + ui: &'ui Ui<'ui>, } -impl<'ui, 'p> PlotLines<'ui, 'p> { - pub const fn new(_: &Ui<'ui>, label: &'p ImStr, values: &'p [f32]) -> Self { +impl<'ui, 'p, Label: AsRef> PlotLines<'ui, 'p, Label> { + pub fn new(ui: &'ui Ui<'ui>, label: Label, values: &'p [f32]) -> Self { PlotLines { label, values, @@ -26,48 +25,58 @@ impl<'ui, 'p> PlotLines<'ui, 'p> { scale_min: f32::MAX, scale_max: f32::MAX, graph_size: [0.0, 0.0], - _phantom: PhantomData, + ui, } } +} - #[inline] - pub const fn values_offset(mut self, values_offset: usize) -> Self { +impl<'ui, 'p, Label: AsRef, Overlay: AsRef> PlotLines<'ui, 'p, Label, Overlay> { + pub fn values_offset(mut self, values_offset: usize) -> Self { self.values_offset = values_offset; self } - #[inline] - pub const fn overlay_text(mut self, overlay_text: &'p ImStr) -> Self { - self.overlay_text = Some(overlay_text); - self + pub fn overlay_text>( + self, + overlay_text: Overlay2, + ) -> PlotLines<'ui, 'p, Label, Overlay2> { + PlotLines { + label: self.label, + values: self.values, + values_offset: self.values_offset, + overlay_text: Some(overlay_text), + scale_min: self.scale_min, + scale_max: self.scale_max, + graph_size: self.graph_size, + ui: self.ui, + } } - #[inline] - pub const fn scale_min(mut self, scale_min: f32) -> Self { + pub fn scale_min(mut self, scale_min: f32) -> Self { self.scale_min = scale_min; self } - #[inline] - pub const fn scale_max(mut self, scale_max: f32) -> Self { + pub fn scale_max(mut self, scale_max: f32) -> Self { self.scale_max = scale_max; self } - #[inline] - pub const fn graph_size(mut self, graph_size: [f32; 2]) -> Self { + pub fn graph_size(mut self, graph_size: [f32; 2]) -> Self { self.graph_size = graph_size; self } pub fn build(self) { unsafe { + let (label, overlay) = self.ui.scratch_txt_with_opt(self.label, self.overlay_text); + sys::igPlotLinesFloatPtr( - self.label.as_ptr(), + label, self.values.as_ptr() as *const c_float, self.values.len() as i32, self.values_offset as i32, - self.overlay_text.map(|x| x.as_ptr()).unwrap_or(ptr::null()), + overlay, self.scale_min, self.scale_max, self.graph_size.into(), diff --git a/imgui/src/popups.rs b/imgui/src/popups.rs index ca30b10..dc6f9aa 100644 --- a/imgui/src/popups.rs +++ b/imgui/src/popups.rs @@ -2,16 +2,7 @@ use std::ptr; use crate::sys; use crate::window::WindowFlags; -use crate::{ImStr, Ui}; - -create_token!( - /// Tracks a popup token that can be ended with `end` or by dropping. - pub struct PopupToken<'ui>; - - /// Drops the popup token manually. You can also just allow this token - /// to drop on its own. - drop { sys::igEndPopup() } -); +use crate::Ui; /// Create a modal pop-up. /// @@ -31,14 +22,14 @@ create_token!( /// }; /// ``` #[must_use] -pub struct PopupModal<'p> { - label: &'p ImStr, +pub struct PopupModal<'p, Label> { + label: Label, opened: Option<&'p mut bool>, flags: WindowFlags, } -impl<'p> PopupModal<'p> { - pub fn new(label: &'p ImStr) -> Self { +impl<'p, Label: AsRef> PopupModal<'p, Label> { + pub fn new(label: Label) -> Self { PopupModal { label, opened: None, @@ -140,7 +131,7 @@ impl<'p> PopupModal<'p> { pub fn begin_popup<'ui>(self, ui: &Ui<'ui>) -> Option> { let render = unsafe { sys::igBeginPopupModal( - self.label.as_ptr(), + ui.scratch_txt(self.label), self.opened .map(|x| x as *mut bool) .unwrap_or(ptr::null_mut()), @@ -165,8 +156,8 @@ impl<'ui> Ui<'ui> { /// can also force close a popup when a user clicks outside a popup. If you do not want users to be /// able to close a popup without selected an option, use [`PopupModal`]. #[doc(alias = "OpenPopup")] - pub fn open_popup(&self, str_id: &ImStr) { - unsafe { sys::igOpenPopup(str_id.as_ptr(), 0) }; + pub fn open_popup(&self, str_id: impl AsRef) { + unsafe { sys::igOpenPopup(self.scratch_txt(str_id), 0) }; } /// Construct a popup that can have any kind of content. @@ -174,9 +165,10 @@ impl<'ui> Ui<'ui> { /// This should be called *per frame*, whereas [`open_popup`](Self::open_popup) should be called *once* /// when you want to actual create the popup. #[doc(alias = "BeginPopup")] - pub fn begin_popup(&self, str_id: &ImStr) -> Option> { - let render = - unsafe { sys::igBeginPopup(str_id.as_ptr(), WindowFlags::empty().bits() as i32) }; + pub fn begin_popup(&self, str_id: impl AsRef) -> Option> { + let render = unsafe { + sys::igBeginPopup(self.scratch_txt(str_id), WindowFlags::empty().bits() as i32) + }; if render { Some(PopupToken::new(self)) @@ -190,21 +182,17 @@ impl<'ui> Ui<'ui> { /// This should be called *per frame*, whereas [`open_popup`](Self::open_popup) should be called *once* /// when you want to actual create the popup. #[doc(alias = "BeginPopup")] - pub fn popup(&self, str_id: &ImStr, f: F) + pub fn popup(&self, str_id: impl AsRef, f: F) where F: FnOnce(), { - let render = - unsafe { sys::igBeginPopup(str_id.as_ptr(), WindowFlags::empty().bits() as i32) }; - if render { + if let Some(_t) = self.begin_popup(str_id) { f(); - unsafe { sys::igEndPopup() }; } } /// Creates a PopupModal directly. - #[deprecated = "Please use PopupModal to create a modal popup."] - pub fn popup_modal<'p>(&self, str_id: &'p ImStr) -> PopupModal<'p> { + pub fn popup_modal<'p, Label: AsRef>(&self, str_id: Label) -> PopupModal<'p, Label> { PopupModal::new(str_id) } @@ -215,3 +203,12 @@ impl<'ui> Ui<'ui> { unsafe { sys::igCloseCurrentPopup() }; } } + +create_token!( + /// Tracks a popup token that can be ended with `end` or by dropping. + pub struct PopupToken<'ui>; + + /// Drops the popup token manually. You can also just allow this token + /// to drop on its own. + drop { sys::igEndPopup() } +); diff --git a/imgui/src/string.rs b/imgui/src/string.rs index 9b7d91a..1ba181b 100644 --- a/imgui/src/string.rs +++ b/imgui/src/string.rs @@ -1,11 +1,81 @@ use std::borrow::{Borrow, Cow}; use std::ffi::CStr; -use std::fmt; use std::ops::{Deref, Index, RangeFull}; use std::os::raw::c_char; use std::str; +use std::{fmt, ptr}; + +/// this is the unsafe cell upon which we build our abstraction. +#[derive(Debug)] +pub(crate) struct UiBuffer { + buffer: Vec, + max_len: usize, +} + +impl UiBuffer { + /// Creates a new max buffer with the given length. + pub fn new(max_len: usize) -> Self { + Self { + buffer: Vec::with_capacity(max_len), + max_len, + } + } + + /// 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) + } + + /// Internal method to push an option text to our scratch buffer. + pub fn scratch_txt_opt(&mut self, txt: Option>) -> *const sys::cty::c_char { + match txt { + Some(v) => self.scratch_txt(v), + None => ptr::null(), + } + } + + pub fn scratch_txt_two( + &mut self, + txt_0: impl AsRef, + 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)) + } + + pub fn scratch_txt_with_opt( + &mut self, + txt_0: impl AsRef, + txt_1: Option>, + ) -> (*const sys::cty::c_char, *const sys::cty::c_char) { + match txt_1 { + Some(value) => self.scratch_txt_two(txt_0, value), + None => (self.scratch_txt(txt_0), ptr::null()), + } + } + + /// Attempts to clear the buffer if it's over the maximum length allowed. + 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'); + + self.buffer.as_ptr().add(len) as *const _ + } + } +} #[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"); diff --git a/imgui/src/widget/color_editors.rs b/imgui/src/widget/color_editors.rs index daa2101..1d8949a 100644 --- a/imgui/src/widget/color_editors.rs +++ b/imgui/src/widget/color_editors.rs @@ -1,7 +1,6 @@ use bitflags::bitflags; use std::ptr; -use crate::string::ImStr; use crate::sys; use crate::Ui; @@ -184,16 +183,16 @@ bitflags! { /// ``` #[derive(Debug)] #[must_use] -pub struct ColorEdit<'a> { - label: &'a ImStr, +pub struct ColorEdit<'a, T: AsRef + 'a> { + label: T, value: EditableColor<'a>, flags: ColorEditFlags, } -impl<'a> ColorEdit<'a> { +impl<'a, T: AsRef + 'a> ColorEdit<'a, T> { /// Constructs a new color editor builder. #[doc(alias = "ColorEdit3", alias = "ColorEdit4")] - pub fn new>>(label: &'a ImStr, value: T) -> ColorEdit<'a> { + pub fn new(label: T, value: impl Into>) -> ColorEdit<'a, T> { ColorEdit { label, value: value.into(), @@ -319,21 +318,21 @@ impl<'a> ColorEdit<'a> { /// Builds the color editor. /// /// Returns true if the color value was changed. - pub fn build(mut self, _: &Ui) -> bool { + pub fn build(mut self, ui: &Ui) -> bool { if let EditableColor::Float3(_) = self.value { self.flags.insert(ColorEditFlags::NO_ALPHA); } match self.value { EditableColor::Float3(value) => unsafe { sys::igColorEdit3( - self.label.as_ptr(), + ui.scratch_txt(self.label), value.as_mut_ptr(), self.flags.bits() as _, ) }, EditableColor::Float4(value) => unsafe { sys::igColorEdit4( - self.label.as_ptr(), + ui.scratch_txt(self.label), value.as_mut_ptr(), self.flags.bits() as _, ) @@ -358,17 +357,17 @@ impl<'a> ColorEdit<'a> { /// ``` #[derive(Debug)] #[must_use] -pub struct ColorPicker<'a> { - label: &'a ImStr, +pub struct ColorPicker<'a, T: AsRef + 'a> { + label: T, value: EditableColor<'a>, flags: ColorEditFlags, ref_color: Option<&'a [f32; 4]>, } -impl<'a> ColorPicker<'a> { +impl<'a, T: AsRef> ColorPicker<'a, T> { /// Constructs a new color picker builder. #[doc(alias = "ColorButton")] - pub fn new>>(label: &'a ImStr, value: T) -> ColorPicker<'a> { + pub fn new(label: T, value: impl Into>) -> Self { ColorPicker { label, value: value.into(), @@ -507,14 +506,14 @@ impl<'a> ColorPicker<'a> { /// Builds the color picker. /// /// Returns true if the color value was changed. - pub fn build(mut self, _: &Ui) -> bool { + pub fn build(mut self, ui: &Ui) -> bool { if let EditableColor::Float3(_) = self.value { self.flags.insert(ColorEditFlags::NO_ALPHA); } let ref_color = self.ref_color.map(|c| c.as_ptr()).unwrap_or(ptr::null()); unsafe { sys::igColorPicker4( - self.label.as_ptr(), + ui.scratch_txt(self.label), self.value.as_mut_ptr(), self.flags.bits() as _, ref_color, @@ -536,16 +535,16 @@ impl<'a> ColorPicker<'a> { /// ``` #[derive(Copy, Clone, Debug)] #[must_use] -pub struct ColorButton<'a> { - desc_id: &'a ImStr, +pub struct ColorButton { + desc_id: T, color: [f32; 4], flags: ColorEditFlags, size: [f32; 2], } -impl<'a> ColorButton<'a> { +impl> ColorButton { /// Constructs a new color button builder. - pub fn new(desc_id: &ImStr, color: [f32; 4]) -> ColorButton { + pub fn new(desc_id: T, color: [f32; 4]) -> Self { ColorButton { desc_id, color, @@ -622,10 +621,10 @@ impl<'a> ColorButton<'a> { /// Builds the color button. /// /// Returns true if this color button was clicked. - pub fn build(self, _: &Ui) -> bool { + pub fn build(self, ui: &Ui) -> bool { unsafe { sys::igColorButton( - self.desc_id.as_ptr(), + ui.scratch_txt(self.desc_id), self.color.into(), self.flags.bits() as _, self.size.into(), diff --git a/imgui/src/widget/combo_box.rs b/imgui/src/widget/combo_box.rs index 5859c84..557ccf3 100644 --- a/imgui/src/widget/combo_box.rs +++ b/imgui/src/widget/combo_box.rs @@ -1,8 +1,6 @@ use bitflags::bitflags; use std::borrow::Cow; -use std::ptr; -use crate::string::ImStr; use crate::sys; use crate::Ui; @@ -56,33 +54,38 @@ pub struct ComboBoxFlags: u32 { /// Builder for a combo box widget #[derive(Copy, Clone, Debug)] #[must_use] -pub struct ComboBox<'a> { - label: &'a ImStr, - preview_value: Option<&'a ImStr>, +pub struct ComboBox { + label: Label, + preview_value: Option, flags: ComboBoxFlags, } -impl<'a> ComboBox<'a> { +impl> ComboBox