mirror of
https://github.com/eliasstepanik/imgui-rs.git
synced 2026-01-15 07:28:28 +00:00
Merge pull request #517 from sanbox-irl/string
Remove ImStr and ImString
This commit is contained in:
commit
dd5110b57d
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
@ -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
|
||||
|
||||
@ -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)!
|
||||
|
||||
|
||||
@ -10,12 +10,12 @@
|
||||

|
||||
|
||||
```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
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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);
|
||||
});
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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],
|
||||
);
|
||||
});
|
||||
|
||||
@ -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!(
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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!");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -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);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -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";
|
||||
}
|
||||
});
|
||||
|
||||
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -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<ClipboardSupport> {
|
||||
ClipboardContext::new().ok().map(ClipboardSupport)
|
||||
}
|
||||
|
||||
impl ClipboardBackend for ClipboardSupport {
|
||||
fn get(&mut self) -> Option<ImString> {
|
||||
self.0.get_contents().ok().map(|text| text.into())
|
||||
fn get(&mut self) -> Option<String> {
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
@ -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");
|
||||
}
|
||||
|
||||
@ -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>"),
|
||||
None => "<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<String>) {
|
||||
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<String>) {
|
||||
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();
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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!(
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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<Renderer, RendererError> {
|
||||
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);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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<ImString>;
|
||||
fn get(&mut self) -> Option<String>;
|
||||
/// 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<dyn ClipboardBackend>,
|
||||
// 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<dyn ClipboardBackend>) -> 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<T: ClipboardBackend>(backend: T) -> ClipboardContext {
|
||||
ClipboardContext {
|
||||
backend,
|
||||
last_value: ImString::default(),
|
||||
backend: Box::new(backend) as Box<dyn ClipboardBackend>,
|
||||
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<String> {
|
||||
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<ImString> {
|
||||
pub fn clipboard_text(&self) -> Option<String> {
|
||||
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<str>) {
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<str>, border: bool) {
|
||||
unsafe { sys::igColumns(count, self.scratch_txt(id), border) }
|
||||
}
|
||||
/// Switches to the next column.
|
||||
///
|
||||
|
||||
@ -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<Rc<RefCell<SharedFontAtlas>>>,
|
||||
ini_filename: Option<ImString>,
|
||||
log_filename: Option<ImString>,
|
||||
platform_name: Option<ImString>,
|
||||
renderer_name: Option<ImString>,
|
||||
clipboard_ctx: Option<Box<ClipboardContext>>,
|
||||
ini_filename: Option<CString>,
|
||||
log_filename: Option<CString>,
|
||||
platform_name: Option<CString>,
|
||||
renderer_name: Option<CString>,
|
||||
// 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<UnsafeCell<ClipboardContext>>,
|
||||
}
|
||||
|
||||
// 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<T: Into<Option<PathBuf>>>(&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<PathBuf> = 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<PathBuf>` instead of an `Option<&Path>`?
|
||||
pub fn log_filename(&self) -> Option<PathBuf> {
|
||||
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<T: Into<Option<PathBuf>>>(&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<T: Into<Option<ImString>>>(&mut self, platform_name: T) {
|
||||
let platform_name = platform_name.into();
|
||||
pub fn set_platform_name<T: Into<Option<String>>>(&mut self, platform_name: T) {
|
||||
let platform_name: Option<CString> =
|
||||
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<T: Into<Option<ImString>>>(&mut self, renderer_name: T) {
|
||||
let renderer_name = renderer_name.into();
|
||||
pub fn set_renderer_name<T: Into<Option<String>>>(&mut self, renderer_name: T) {
|
||||
let renderer_name: Option<CString> =
|
||||
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<dyn ClipboardBackend>) {
|
||||
use std::borrow::BorrowMut;
|
||||
let mut clipboard_ctx = Box::new(ClipboardContext::new(backend));
|
||||
pub fn set_clipboard_backend<T: ClipboardBackend>(&mut self, backend: T) {
|
||||
let clipboard_ctx: Box<UnsafeCell<_>> = 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<Rc<RefCell<SharedFontAtlas>>>) -> 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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<T> {
|
||||
name: T,
|
||||
flags: DragDropFlags,
|
||||
cond: Condition,
|
||||
}
|
||||
|
||||
impl<'a> DragDropSource<'a> {
|
||||
impl<T: AsRef<str>> DragDropSource<T> {
|
||||
/// 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<String>) {
|
||||
/// 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<DragDropSourceToolTip<'ui>> {
|
||||
unsafe {
|
||||
let payload = TypedPayload::new(payload);
|
||||
self.begin_payload_unchecked(
|
||||
ui,
|
||||
&payload as *const _ as *const ffi::c_void,
|
||||
std::mem::size_of::<TypedPayload<T>>(),
|
||||
std::mem::size_of::<TypedPayload<P>>(),
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -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<DragDropSourceToolTip<'ui>> {
|
||||
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::<usize>(im_str!("BUTTON_ID"), DragDropFlags::empty()) {
|
||||
/// if let Some(Ok(payload_data)) = target.accept_payload::<usize, _>("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<Ui<'ui>>);
|
||||
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<Self> {
|
||||
pub fn new(ui: &'ui Ui<'ui>) -> Option<Self> {
|
||||
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<str>,
|
||||
flags: DragDropFlags,
|
||||
) -> Option<DragDropPayloadEmpty> {
|
||||
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<T: 'static + Copy>(
|
||||
pub fn accept_payload<T: 'static + Copy, Name: AsRef<str>>(
|
||||
&self,
|
||||
name: &ImStr,
|
||||
name: Name,
|
||||
flags: DragDropFlags,
|
||||
) -> Option<Result<DragDropPayloadPod<T>, 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<str>,
|
||||
flags: DragDropFlags,
|
||||
) -> Option<DragDropPayload> {
|
||||
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 {
|
||||
|
||||
@ -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<H>,
|
||||
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<str>> 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<str>,
|
||||
H: AsRef<str>,
|
||||
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<H2: AsRef<str>>(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::<T>),
|
||||
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::<T>),
|
||||
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<str>> 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<str>> 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::<T>),
|
||||
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<str>> 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<str>> 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<str>> $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<str>> $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<T: InputTextCallbackHandler>(
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
181
imgui/src/lib.rs
181
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<cell::RefMut<'ui, SharedFontAtlas>>,
|
||||
// imgui isn't mutli-threaded -- so no one will ever access twice.
|
||||
buffer: cell::UnsafeCell<string::UiBuffer>,
|
||||
}
|
||||
|
||||
impl<'ui> Ui<'ui> {
|
||||
/// Internal method to push a single text to our scratch buffer.
|
||||
fn scratch_txt(&self, txt: impl AsRef<str>) -> *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<impl AsRef<str>>) -> *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<str>,
|
||||
txt_1: impl AsRef<str>,
|
||||
) -> (*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<str>,
|
||||
txt_1: Option<impl AsRef<str>>,
|
||||
) -> (*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<T> 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<str>>(
|
||||
&'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<str>>(
|
||||
&'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<str>>(
|
||||
&'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<str>>(
|
||||
&'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<str>>(
|
||||
&'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<str>>(
|
||||
&'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<str>>(
|
||||
&'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<str>>(
|
||||
&'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<str>>(
|
||||
&'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<str>>(
|
||||
&'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<ImStr> + ?Sized>(
|
||||
pub fn list_box<'p, StringType: AsRef<str> + ?Sized>(
|
||||
&self,
|
||||
label: &'p ImStr,
|
||||
label: impl AsRef<str>,
|
||||
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<str> + ?Sized, const N: usize>(
|
||||
// &self,
|
||||
// label: impl AsRef<str>,
|
||||
// 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<str>>(
|
||||
&'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<str>>(
|
||||
&'ui self,
|
||||
label: Label,
|
||||
values: &'p [f32],
|
||||
) -> PlotHistogram<'ui, 'p> {
|
||||
) -> PlotHistogram<'ui, 'p, Label> {
|
||||
PlotHistogram::new(self, label, values)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<Overlay>,
|
||||
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<str>> 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<str>, Overlay: AsRef<str>> 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<NewOverlay: AsRef<str>>(
|
||||
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(),
|
||||
|
||||
@ -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<Overlay>,
|
||||
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<str>> 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<str>, Overlay: AsRef<str>> 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<Overlay2: AsRef<str>>(
|
||||
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(),
|
||||
|
||||
@ -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<str>> 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<PopupToken<'ui>> {
|
||||
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<str>) {
|
||||
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<PopupToken<'_>> {
|
||||
let render =
|
||||
unsafe { sys::igBeginPopup(str_id.as_ptr(), WindowFlags::empty().bits() as i32) };
|
||||
pub fn begin_popup(&self, str_id: impl AsRef<str>) -> Option<PopupToken<'_>> {
|
||||
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<F>(&self, str_id: &ImStr, f: F)
|
||||
pub fn popup<F>(&self, str_id: impl AsRef<str>, 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<str>>(&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() }
|
||||
);
|
||||
|
||||
@ -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<u8>,
|
||||
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<str>) -> *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<impl AsRef<str>>) -> *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<str>,
|
||||
txt_1: impl AsRef<str>,
|
||||
) -> (*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<str>,
|
||||
txt_1: Option<impl AsRef<str>>,
|
||||
) -> (*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<str>) -> *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<str> now -- use inline strings or `format` instead"]
|
||||
macro_rules! im_str {
|
||||
($e:literal $(,)?) => {{
|
||||
const __INPUT: &str = concat!($e, "\0");
|
||||
|
||||
@ -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<str> + 'a> {
|
||||
label: T,
|
||||
value: EditableColor<'a>,
|
||||
flags: ColorEditFlags,
|
||||
}
|
||||
|
||||
impl<'a> ColorEdit<'a> {
|
||||
impl<'a, T: AsRef<str> + 'a> ColorEdit<'a, T> {
|
||||
/// Constructs a new color editor builder.
|
||||
#[doc(alias = "ColorEdit3", alias = "ColorEdit4")]
|
||||
pub fn new<T: Into<EditableColor<'a>>>(label: &'a ImStr, value: T) -> ColorEdit<'a> {
|
||||
pub fn new(label: T, value: impl Into<EditableColor<'a>>) -> 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<str> + 'a> {
|
||||
label: T,
|
||||
value: EditableColor<'a>,
|
||||
flags: ColorEditFlags,
|
||||
ref_color: Option<&'a [f32; 4]>,
|
||||
}
|
||||
|
||||
impl<'a> ColorPicker<'a> {
|
||||
impl<'a, T: AsRef<str>> ColorPicker<'a, T> {
|
||||
/// Constructs a new color picker builder.
|
||||
#[doc(alias = "ColorButton")]
|
||||
pub fn new<T: Into<EditableColor<'a>>>(label: &'a ImStr, value: T) -> ColorPicker<'a> {
|
||||
pub fn new(label: T, value: impl Into<EditableColor<'a>>) -> 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<T> {
|
||||
desc_id: T,
|
||||
color: [f32; 4],
|
||||
flags: ColorEditFlags,
|
||||
size: [f32; 2],
|
||||
}
|
||||
|
||||
impl<'a> ColorButton<'a> {
|
||||
impl<T: AsRef<str>> ColorButton<T> {
|
||||
/// 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(),
|
||||
|
||||
@ -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, Preview = &'static str> {
|
||||
label: Label,
|
||||
preview_value: Option<Preview>,
|
||||
flags: ComboBoxFlags,
|
||||
}
|
||||
|
||||
impl<'a> ComboBox<'a> {
|
||||
impl<Label: AsRef<str>> ComboBox<Label> {
|
||||
/// Constructs a new combo box builder.
|
||||
#[doc(alias = "BeginCombo")]
|
||||
pub const fn new(label: &'a ImStr) -> ComboBox<'a> {
|
||||
pub fn new(label: Label) -> Self {
|
||||
ComboBox {
|
||||
label,
|
||||
preview_value: None,
|
||||
flags: ComboBoxFlags::empty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the preview value displayed in the preview box (if visible).
|
||||
#[inline]
|
||||
pub const fn preview_value(mut self, preview_value: &'a ImStr) -> Self {
|
||||
self.preview_value = Some(preview_value);
|
||||
self
|
||||
impl<T: AsRef<str>, Preview: AsRef<str>> ComboBox<T, Preview> {
|
||||
pub fn preview_value<Preview2: AsRef<str>>(
|
||||
self,
|
||||
preview_value: Preview2,
|
||||
) -> ComboBox<T, Preview2> {
|
||||
ComboBox {
|
||||
label: self.label,
|
||||
preview_value: Some(preview_value),
|
||||
flags: self.flags,
|
||||
}
|
||||
}
|
||||
|
||||
/// Replaces all current settings with the given flags.
|
||||
#[inline]
|
||||
pub const fn flags(mut self, flags: ComboBoxFlags) -> Self {
|
||||
pub fn flags(mut self, flags: ComboBoxFlags) -> Self {
|
||||
self.flags = flags;
|
||||
self
|
||||
}
|
||||
@ -90,7 +93,6 @@ impl<'a> ComboBox<'a> {
|
||||
/// Enables/disables aligning the combo box popup toward the left.
|
||||
///
|
||||
/// Disabled by default.
|
||||
#[inline]
|
||||
pub fn popup_align_left(mut self, popup_align_left: bool) -> Self {
|
||||
self.flags
|
||||
.set(ComboBoxFlags::POPUP_ALIGN_LEFT, popup_align_left);
|
||||
@ -142,11 +144,13 @@ impl<'a> ComboBox<'a> {
|
||||
#[must_use]
|
||||
pub fn begin<'ui>(self, ui: &Ui<'ui>) -> Option<ComboBoxToken<'ui>> {
|
||||
let should_render = unsafe {
|
||||
sys::igBeginCombo(
|
||||
self.label.as_ptr(),
|
||||
self.preview_value.map(ImStr::as_ptr).unwrap_or(ptr::null()),
|
||||
self.flags.bits() as i32,
|
||||
)
|
||||
if let Some(preview_value) = self.preview_value {
|
||||
let (ptr_one, ptr_two) = ui.scratch_txt_two(self.label, preview_value);
|
||||
sys::igBeginCombo(ptr_one, ptr_two, self.flags.bits() as i32)
|
||||
} else {
|
||||
let ptr_one = ui.scratch_txt(self.label);
|
||||
sys::igBeginCombo(ptr_one, std::ptr::null(), self.flags.bits() as i32)
|
||||
}
|
||||
};
|
||||
if should_render {
|
||||
Some(ComboBoxToken::new(ui))
|
||||
@ -158,7 +162,7 @@ impl<'a> ComboBox<'a> {
|
||||
/// Returns the result of the closure, if it is called.
|
||||
///
|
||||
/// Note: the closure is not called if the combo box is not open.
|
||||
pub fn build<T, F: FnOnce() -> T>(self, ui: &Ui, f: F) -> Option<T> {
|
||||
pub fn build<R, F: FnOnce() -> R>(self, ui: &Ui, f: F) -> Option<R> {
|
||||
self.begin(ui).map(|_combo| f())
|
||||
}
|
||||
}
|
||||
@ -173,49 +177,141 @@ create_token!(
|
||||
);
|
||||
|
||||
/// # Convenience functions
|
||||
impl<'a> ComboBox<'a> {
|
||||
/// Builds a simple combo box for choosing from a slice of values
|
||||
impl<'ui> Ui<'ui> {
|
||||
/// Creates a combo box which can be appended to with `Selectable::new`.
|
||||
///
|
||||
/// If you do not want to provide a preview, use [`begin_combo_no_preview`]. If you want
|
||||
/// to pass flags, use [`begin_combo_with_flags`].
|
||||
///
|
||||
/// Returns `None` if the combo box is not open and no content should be rendered.
|
||||
///
|
||||
/// [`begin_combo_no_preview`]: Ui::begin_combo_no_preview
|
||||
/// [`begin_combo_with_flags`]: Ui::begin_combo_with_flags
|
||||
#[must_use]
|
||||
#[doc(alias = "BeginCombo")]
|
||||
pub fn build_simple<T, L>(
|
||||
self,
|
||||
ui: &Ui,
|
||||
pub fn begin_combo(
|
||||
&self,
|
||||
label: impl AsRef<str>,
|
||||
preview_value: impl AsRef<str>,
|
||||
) -> Option<ComboBoxToken<'ui>> {
|
||||
self.begin_combo_with_flags(label, preview_value, ComboBoxFlags::empty())
|
||||
}
|
||||
|
||||
/// Creates a combo box which can be appended to with `Selectable::new`.
|
||||
///
|
||||
/// If you do not want to provide a preview, use [begin_combo_no_preview].
|
||||
/// Returns `Some(ComboBoxToken)` if the combo box is open. After content has been
|
||||
/// rendered, the token must be ended by calling `.end()`.
|
||||
///
|
||||
/// Returns `None` if the combo box is not open and no content should be rendered.
|
||||
///
|
||||
/// [begin_combo_no_preview]: Ui::begin_combo_no_preview
|
||||
#[must_use]
|
||||
#[doc(alias = "BeginCombo")]
|
||||
pub fn begin_combo_with_flags(
|
||||
&self,
|
||||
label: impl AsRef<str>,
|
||||
preview_value: impl AsRef<str>,
|
||||
flags: ComboBoxFlags,
|
||||
) -> Option<ComboBoxToken<'ui>> {
|
||||
self._begin_combo(label, Some(preview_value), flags)
|
||||
}
|
||||
|
||||
/// Creates a combo box which can be appended to with `Selectable::new`.
|
||||
///
|
||||
/// If you want to provide a preview, use [begin_combo]. If you want
|
||||
/// to pass flags, use [begin_combo_no_preview_with_flags].
|
||||
///
|
||||
/// Returns `Some(ComboBoxToken)` if the combo box is open. After content has been
|
||||
/// rendered, the token must be ended by calling `.end()`.
|
||||
///
|
||||
/// Returns `None` if the combo box is not open and no content should be rendered.
|
||||
///
|
||||
/// [begin_combo]: Ui::begin_combo
|
||||
/// [begin_combo_no_preview_with_flags]: Ui::begin_combo_no_preview_with_flags
|
||||
#[must_use]
|
||||
#[doc(alias = "BeginCombo")]
|
||||
pub fn begin_combo_no_preview(&self, label: impl AsRef<str>) -> Option<ComboBoxToken<'ui>> {
|
||||
self.begin_combo_no_preview_with_flags(label, ComboBoxFlags::empty())
|
||||
}
|
||||
|
||||
/// Creates a combo box which can be appended to with `Selectable::new`.
|
||||
///
|
||||
/// If you do not want to provide a preview, use [begin_combo_no_preview].
|
||||
/// Returns `Some(ComboBoxToken)` if the combo box is open. After content has been
|
||||
/// rendered, the token must be ended by calling `.end()`.
|
||||
///
|
||||
/// Returns `None` if the combo box is not open and no content should be rendered.
|
||||
///
|
||||
/// [begin_combo_no_preview]: Ui::begin_combo_no_preview
|
||||
#[must_use]
|
||||
#[doc(alias = "BeginCombo")]
|
||||
pub fn begin_combo_no_preview_with_flags(
|
||||
&self,
|
||||
label: impl AsRef<str>,
|
||||
flags: ComboBoxFlags,
|
||||
) -> Option<ComboBoxToken<'ui>> {
|
||||
self._begin_combo(label, Option::<&'static str>::None, flags)
|
||||
}
|
||||
|
||||
/// This is the internal begin combo method that they all...eventually call.
|
||||
fn _begin_combo(
|
||||
&self,
|
||||
label: impl AsRef<str>,
|
||||
preview_value: Option<impl AsRef<str>>,
|
||||
flags: ComboBoxFlags,
|
||||
) -> Option<ComboBoxToken<'ui>> {
|
||||
let should_render = unsafe {
|
||||
let (ptr_one, ptr_two) = self.scratch_txt_with_opt(label, preview_value);
|
||||
sys::igBeginCombo(ptr_one, ptr_two, flags.bits() as i32)
|
||||
};
|
||||
if should_render {
|
||||
Some(ComboBoxToken::new(self))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
/// Builds a simple combo box for choosing from a slice of values
|
||||
#[doc(alias = "Combo")]
|
||||
pub fn combo<V, L>(
|
||||
&self,
|
||||
label: impl AsRef<str>,
|
||||
current_item: &mut usize,
|
||||
items: &[T],
|
||||
label_fn: &L,
|
||||
items: &[V],
|
||||
label_fn: L,
|
||||
) -> bool
|
||||
where
|
||||
for<'b> L: Fn(&'b T) -> Cow<'b, ImStr>,
|
||||
for<'b> L: Fn(&'b V) -> Cow<'b, str>,
|
||||
{
|
||||
use crate::widget::selectable::Selectable;
|
||||
let label_fn = &label_fn;
|
||||
let mut result = false;
|
||||
let mut cb = self;
|
||||
let preview_value = items.get(*current_item).map(label_fn);
|
||||
if cb.preview_value.is_none() {
|
||||
if let Some(preview_value) = preview_value.as_ref() {
|
||||
cb = cb.preview_value(preview_value);
|
||||
}
|
||||
}
|
||||
if let Some(_cb) = cb.begin(ui) {
|
||||
|
||||
if let Some(_cb) = self._begin_combo(label, preview_value, ComboBoxFlags::empty()) {
|
||||
for (idx, item) in items.iter().enumerate() {
|
||||
let text = label_fn(item);
|
||||
let selected = idx == *current_item;
|
||||
if Selectable::new(&text).selected(selected).build(ui) {
|
||||
if Selectable::new(&text).selected(selected).build(self) {
|
||||
*current_item = idx;
|
||||
result = true;
|
||||
}
|
||||
if selected {
|
||||
ui.set_item_default_focus();
|
||||
self.set_item_default_focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
/// Builds a simple combo box for choosing from a slice of strings
|
||||
#[doc(alias = "BeginCombo")]
|
||||
pub fn build_simple_string<S>(self, ui: &Ui, current_item: &mut usize, items: &[&S]) -> bool
|
||||
where
|
||||
S: AsRef<ImStr> + ?Sized,
|
||||
{
|
||||
self.build_simple(ui, current_item, items, &|&s| s.as_ref().into())
|
||||
|
||||
/// Builds a simple combo box for choosing from a slice of values
|
||||
#[doc(alias = "Combo")]
|
||||
pub fn combo_simple_string(
|
||||
&self,
|
||||
label: impl AsRef<str>,
|
||||
current_item: &mut usize,
|
||||
items: &[impl AsRef<str>],
|
||||
) -> bool {
|
||||
self.combo(label, current_item, items, |s| Cow::Borrowed(s.as_ref()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,7 +2,6 @@ use std::os::raw::c_void;
|
||||
use std::ptr;
|
||||
|
||||
use crate::internal::DataTypeKind;
|
||||
use crate::string::ImStr;
|
||||
use crate::sys;
|
||||
use crate::widget::slider::SliderFlags;
|
||||
use crate::Ui;
|
||||
@ -10,19 +9,19 @@ use crate::Ui;
|
||||
/// Builder for a drag slider widget.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[must_use]
|
||||
pub struct Drag<'a, T: DataTypeKind> {
|
||||
label: &'a ImStr,
|
||||
pub struct Drag<T, L, F = &'static str> {
|
||||
label: L,
|
||||
speed: f32,
|
||||
min: Option<T>,
|
||||
max: Option<T>,
|
||||
display_format: Option<&'a ImStr>,
|
||||
display_format: Option<F>,
|
||||
flags: SliderFlags,
|
||||
}
|
||||
|
||||
impl<'a, T: DataTypeKind> Drag<'a, T> {
|
||||
impl<L: AsRef<str>, T: DataTypeKind> Drag<T, L> {
|
||||
/// Constructs a new drag slider builder.
|
||||
#[doc(alias = "DragScalar", alias = "DragScalarN")]
|
||||
pub fn new(label: &ImStr) -> Drag<T> {
|
||||
pub fn new(label: L) -> Self {
|
||||
Drag {
|
||||
label,
|
||||
speed: 1.0,
|
||||
@ -32,8 +31,10 @@ impl<'a, T: DataTypeKind> Drag<'a, T> {
|
||||
flags: SliderFlags::empty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<L: AsRef<str>, T: DataTypeKind, F: AsRef<str>> Drag<T, L, F> {
|
||||
/// Sets the range (inclusive)
|
||||
#[inline]
|
||||
pub fn range(mut self, min: T, max: T) -> Self {
|
||||
self.min = Some(min);
|
||||
self.max = Some(max);
|
||||
@ -42,19 +43,22 @@ impl<'a, T: DataTypeKind> Drag<'a, T> {
|
||||
/// Sets the value increment for a movement of one pixel.
|
||||
///
|
||||
/// Example: speed=0.2 means mouse needs to move 5 pixels to increase the slider value by 1
|
||||
#[inline]
|
||||
pub fn speed(mut self, speed: f32) -> Self {
|
||||
self.speed = speed;
|
||||
self
|
||||
}
|
||||
/// Sets the display format using *a C-style printf string*
|
||||
#[inline]
|
||||
pub fn display_format(mut self, display_format: &'a ImStr) -> Self {
|
||||
self.display_format = Some(display_format);
|
||||
self
|
||||
pub fn display_format<F2: AsRef<str>>(self, display_format: F2) -> Drag<T, L, F2> {
|
||||
Drag {
|
||||
label: self.label,
|
||||
speed: self.speed,
|
||||
min: self.min,
|
||||
max: self.max,
|
||||
display_format: Some(display_format),
|
||||
flags: self.flags,
|
||||
}
|
||||
}
|
||||
/// Replaces all current settings with the given flags
|
||||
#[inline]
|
||||
pub fn flags(mut self, flags: SliderFlags) -> Self {
|
||||
self.flags = flags;
|
||||
self
|
||||
@ -62,10 +66,12 @@ impl<'a, T: DataTypeKind> Drag<'a, T> {
|
||||
/// Builds a drag slider that is bound to the given value.
|
||||
///
|
||||
/// Returns true if the slider value was changed.
|
||||
pub fn build(self, _: &Ui, value: &mut T) -> bool {
|
||||
pub fn build(self, ui: &Ui, value: &mut T) -> bool {
|
||||
unsafe {
|
||||
let (one, two) = ui.scratch_txt_with_opt(self.label, self.display_format);
|
||||
|
||||
sys::igDragScalar(
|
||||
self.label.as_ptr(),
|
||||
one,
|
||||
T::KIND as i32,
|
||||
value as *mut T as *mut c_void,
|
||||
self.speed,
|
||||
@ -77,9 +83,7 @@ impl<'a, T: DataTypeKind> Drag<'a, T> {
|
||||
.as_ref()
|
||||
.map(|max| max as *const T)
|
||||
.unwrap_or(ptr::null()) as *const c_void,
|
||||
self.display_format
|
||||
.map(ImStr::as_ptr)
|
||||
.unwrap_or(ptr::null()),
|
||||
two,
|
||||
self.flags.bits() as i32,
|
||||
)
|
||||
}
|
||||
@ -87,10 +91,12 @@ impl<'a, T: DataTypeKind> Drag<'a, T> {
|
||||
/// Builds a horizontal array of multiple drag sliders attached to the given slice.
|
||||
///
|
||||
/// Returns true if any slider value was changed.
|
||||
pub fn build_array(self, _: &Ui, values: &mut [T]) -> bool {
|
||||
pub fn build_array(self, ui: &Ui, values: &mut [T]) -> bool {
|
||||
unsafe {
|
||||
let (one, two) = ui.scratch_txt_with_opt(self.label, self.display_format);
|
||||
|
||||
sys::igDragScalarN(
|
||||
self.label.as_ptr(),
|
||||
one,
|
||||
T::KIND as i32,
|
||||
values.as_mut_ptr() as *mut c_void,
|
||||
values.len() as i32,
|
||||
@ -103,9 +109,7 @@ impl<'a, T: DataTypeKind> Drag<'a, T> {
|
||||
.as_ref()
|
||||
.map(|max| max as *const T)
|
||||
.unwrap_or(ptr::null()) as *const c_void,
|
||||
self.display_format
|
||||
.map(ImStr::as_ptr)
|
||||
.unwrap_or(ptr::null()),
|
||||
two,
|
||||
self.flags.bits() as i32,
|
||||
)
|
||||
}
|
||||
@ -115,20 +119,20 @@ impl<'a, T: DataTypeKind> Drag<'a, T> {
|
||||
/// Builder for a drag slider widget.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[must_use]
|
||||
pub struct DragRange<'a, T: DataTypeKind> {
|
||||
label: &'a ImStr,
|
||||
pub struct DragRange<T, L, F = &'static str, M = &'static str> {
|
||||
label: L,
|
||||
speed: f32,
|
||||
min: Option<T>,
|
||||
max: Option<T>,
|
||||
display_format: Option<&'a ImStr>,
|
||||
max_display_format: Option<&'a ImStr>,
|
||||
display_format: Option<F>,
|
||||
max_display_format: Option<M>,
|
||||
flags: SliderFlags,
|
||||
}
|
||||
|
||||
impl<'a, T: DataTypeKind> DragRange<'a, T> {
|
||||
impl<T: DataTypeKind, L: AsRef<str>> DragRange<T, L> {
|
||||
/// Constructs a new drag slider builder.
|
||||
#[doc(alias = "DragIntRange2", alias = "DragFloatRange2")]
|
||||
pub fn new(label: &ImStr) -> DragRange<T> {
|
||||
pub fn new(label: L) -> DragRange<T, L> {
|
||||
DragRange {
|
||||
label,
|
||||
speed: 1.0,
|
||||
@ -139,7 +143,15 @@ impl<'a, T: DataTypeKind> DragRange<'a, T> {
|
||||
flags: SliderFlags::empty(),
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
}
|
||||
|
||||
impl<T, L, F, M> DragRange<T, L, F, M>
|
||||
where
|
||||
T: DataTypeKind,
|
||||
L: AsRef<str>,
|
||||
F: AsRef<str>,
|
||||
M: AsRef<str>,
|
||||
{
|
||||
pub fn range(mut self, min: T, max: T) -> Self {
|
||||
self.min = Some(min);
|
||||
self.max = Some(max);
|
||||
@ -148,77 +160,124 @@ impl<'a, T: DataTypeKind> DragRange<'a, T> {
|
||||
/// Sets the value increment for a movement of one pixel.
|
||||
///
|
||||
/// Example: speed=0.2 means mouse needs to move 5 pixels to increase the slider value by 1
|
||||
#[inline]
|
||||
pub fn speed(mut self, speed: f32) -> Self {
|
||||
self.speed = speed;
|
||||
self
|
||||
}
|
||||
/// Sets the display format using *a C-style printf string*
|
||||
#[inline]
|
||||
pub fn display_format(mut self, display_format: &'a ImStr) -> Self {
|
||||
self.display_format = Some(display_format);
|
||||
self
|
||||
pub fn display_format<F2: AsRef<str>>(self, display_format: F2) -> DragRange<T, L, F2, M> {
|
||||
DragRange {
|
||||
label: self.label,
|
||||
speed: self.speed,
|
||||
min: self.min,
|
||||
max: self.max,
|
||||
display_format: Some(display_format),
|
||||
max_display_format: self.max_display_format,
|
||||
flags: self.flags,
|
||||
}
|
||||
}
|
||||
/// Sets the display format for the max value using *a C-style printf string*
|
||||
#[inline]
|
||||
pub fn max_display_format(mut self, max_display_format: &'a ImStr) -> Self {
|
||||
self.max_display_format = Some(max_display_format);
|
||||
self
|
||||
pub fn max_display_format<M2: AsRef<str>>(
|
||||
self,
|
||||
max_display_format: M2,
|
||||
) -> DragRange<T, L, F, M2> {
|
||||
DragRange {
|
||||
label: self.label,
|
||||
speed: self.speed,
|
||||
min: self.min,
|
||||
max: self.max,
|
||||
display_format: self.display_format,
|
||||
max_display_format: Some(max_display_format),
|
||||
flags: self.flags,
|
||||
}
|
||||
}
|
||||
/// Replaces all current settings with the given flags
|
||||
#[inline]
|
||||
pub fn flags(mut self, flags: SliderFlags) -> Self {
|
||||
self.flags = flags;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DragRange<'a, f32> {
|
||||
impl<L, F, M> DragRange<f32, L, F, M>
|
||||
where
|
||||
L: AsRef<str>,
|
||||
F: AsRef<str>,
|
||||
M: AsRef<str>,
|
||||
{
|
||||
/// Builds a drag range slider that is bound to the given min/max values.
|
||||
///
|
||||
/// Returns true if the slider value was changed.
|
||||
#[doc(alias = "DragFloatRange2")]
|
||||
pub fn build(self, _: &Ui, min: &mut f32, max: &mut f32) -> bool {
|
||||
pub fn build(self, ui: &Ui, min: &mut f32, max: &mut f32) -> bool {
|
||||
let label;
|
||||
let mut display_format = std::ptr::null();
|
||||
let mut max_display_format = std::ptr::null();
|
||||
|
||||
// we do this ourselves the long way...
|
||||
unsafe {
|
||||
let buffer = &mut *ui.buffer.get();
|
||||
buffer.refresh_buffer();
|
||||
|
||||
label = buffer.push(self.label);
|
||||
if let Some(v) = self.display_format {
|
||||
display_format = buffer.push(v);
|
||||
}
|
||||
if let Some(v) = self.max_display_format {
|
||||
max_display_format = buffer.push(v);
|
||||
}
|
||||
|
||||
sys::igDragFloatRange2(
|
||||
self.label.as_ptr(),
|
||||
label,
|
||||
min as *mut f32,
|
||||
max as *mut f32,
|
||||
self.speed,
|
||||
self.min.unwrap_or(0.0),
|
||||
self.max.unwrap_or(0.0),
|
||||
self.display_format
|
||||
.map(ImStr::as_ptr)
|
||||
.unwrap_or(ptr::null()),
|
||||
self.max_display_format
|
||||
.map(ImStr::as_ptr)
|
||||
.unwrap_or(ptr::null()),
|
||||
display_format,
|
||||
max_display_format,
|
||||
self.flags.bits() as i32,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DragRange<'a, i32> {
|
||||
impl<L, F, M> DragRange<i32, L, F, M>
|
||||
where
|
||||
L: AsRef<str>,
|
||||
F: AsRef<str>,
|
||||
M: AsRef<str>,
|
||||
{
|
||||
/// Builds a drag range slider that is bound to the given min/max values.
|
||||
///
|
||||
/// Returns true if the slider value was changed.
|
||||
#[doc(alias = "DragIntRange2")]
|
||||
pub fn build(self, _: &Ui, min: &mut i32, max: &mut i32) -> bool {
|
||||
pub fn build(self, ui: &Ui, min: &mut i32, max: &mut i32) -> bool {
|
||||
unsafe {
|
||||
let label;
|
||||
let mut display_format = std::ptr::null();
|
||||
let mut max_display_format = std::ptr::null();
|
||||
|
||||
// we do this ourselves the long way...
|
||||
let buffer = &mut *ui.buffer.get();
|
||||
buffer.refresh_buffer();
|
||||
|
||||
label = buffer.push(self.label);
|
||||
if let Some(v) = self.display_format {
|
||||
display_format = buffer.push(v);
|
||||
}
|
||||
if let Some(v) = self.max_display_format {
|
||||
max_display_format = buffer.push(v);
|
||||
}
|
||||
|
||||
sys::igDragIntRange2(
|
||||
self.label.as_ptr(),
|
||||
label,
|
||||
min as *mut i32,
|
||||
max as *mut i32,
|
||||
self.speed,
|
||||
self.min.unwrap_or(0),
|
||||
self.max.unwrap_or(0),
|
||||
self.display_format
|
||||
.map(ImStr::as_ptr)
|
||||
.unwrap_or(ptr::null()),
|
||||
self.max_display_format
|
||||
.map(ImStr::as_ptr)
|
||||
.unwrap_or(ptr::null()),
|
||||
display_format,
|
||||
max_display_format,
|
||||
self.flags.bits() as i32,
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,21 +1,20 @@
|
||||
use std::borrow::Cow;
|
||||
|
||||
use crate::string::ImStr;
|
||||
use crate::sys;
|
||||
use crate::Ui;
|
||||
|
||||
/// Builder for a list box widget
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[must_use]
|
||||
pub struct ListBox<'a> {
|
||||
label: &'a ImStr,
|
||||
pub struct ListBox<T> {
|
||||
label: T,
|
||||
size: sys::ImVec2,
|
||||
}
|
||||
|
||||
impl<'a> ListBox<'a> {
|
||||
impl<T: AsRef<str>> ListBox<T> {
|
||||
/// Constructs a new list box builder.
|
||||
#[doc(alias = "ListBoxHeaderVec2", alias = "ListBoxHeaderInt")]
|
||||
pub const fn new(label: &'a ImStr) -> ListBox<'a> {
|
||||
pub fn new(label: T) -> ListBox<T> {
|
||||
ListBox {
|
||||
label,
|
||||
size: sys::ImVec2::zero(),
|
||||
@ -29,7 +28,7 @@ impl<'a> ListBox<'a> {
|
||||
///
|
||||
/// Default: [0.0, 0.0], in which case the combobox calculates a sensible width and height
|
||||
#[inline]
|
||||
pub const fn size(mut self, size: [f32; 2]) -> Self {
|
||||
pub fn size(mut self, size: [f32; 2]) -> Self {
|
||||
self.size = sys::ImVec2::new(size[0], size[1]);
|
||||
self
|
||||
}
|
||||
@ -41,7 +40,7 @@ impl<'a> ListBox<'a> {
|
||||
/// Returns `None` if the list box is not open and no content should be rendered.
|
||||
#[must_use]
|
||||
pub fn begin<'ui>(self, ui: &Ui<'ui>) -> Option<ListBoxToken<'ui>> {
|
||||
let should_render = unsafe { sys::igBeginListBox(self.label.as_ptr(), self.size) };
|
||||
let should_render = unsafe { sys::igBeginListBox(ui.scratch_txt(self.label), self.size) };
|
||||
if should_render {
|
||||
Some(ListBoxToken::new(ui))
|
||||
} else {
|
||||
@ -52,7 +51,7 @@ impl<'a> ListBox<'a> {
|
||||
/// Returns the result of the closure, if it is called.
|
||||
///
|
||||
/// Note: the closure is not called if the list box is not open.
|
||||
pub fn build<T, F: FnOnce() -> T>(self, ui: &Ui<'_>, f: F) -> Option<T> {
|
||||
pub fn build<R, F: FnOnce() -> R>(self, ui: &Ui<'_>, f: F) -> Option<R> {
|
||||
self.begin(ui).map(|_list| f())
|
||||
}
|
||||
}
|
||||
@ -67,17 +66,17 @@ create_token!(
|
||||
);
|
||||
|
||||
/// # Convenience functions
|
||||
impl<'a> ListBox<'a> {
|
||||
impl<T: AsRef<str>> ListBox<T> {
|
||||
/// Builds a simple list box for choosing from a slice of values
|
||||
pub fn build_simple<T, L>(
|
||||
pub fn build_simple<V, L>(
|
||||
self,
|
||||
ui: &Ui,
|
||||
current_item: &mut usize,
|
||||
items: &[T],
|
||||
items: &[V],
|
||||
label_fn: &L,
|
||||
) -> bool
|
||||
where
|
||||
for<'b> L: Fn(&'b T) -> Cow<'b, ImStr>,
|
||||
for<'b> L: Fn(&'b V) -> Cow<'b, str>,
|
||||
{
|
||||
use crate::widget::selectable::Selectable;
|
||||
let mut result = false;
|
||||
|
||||
@ -1,6 +1,4 @@
|
||||
use std::ptr;
|
||||
|
||||
use crate::string::ImStr;
|
||||
// use crate::string::ImStr;
|
||||
use crate::sys;
|
||||
use crate::Ui;
|
||||
|
||||
@ -66,7 +64,7 @@ impl<'ui> Ui<'ui> {
|
||||
/// with `enabled` set to `true`.
|
||||
#[must_use]
|
||||
#[doc(alias = "BeginMenu")]
|
||||
pub fn begin_menu(&self, label: &ImStr) -> Option<MenuToken<'_>> {
|
||||
pub fn begin_menu(&self, label: impl AsRef<str>) -> Option<MenuToken<'_>> {
|
||||
self.begin_menu_with_enabled(label, true)
|
||||
}
|
||||
|
||||
@ -78,8 +76,12 @@ impl<'ui> Ui<'ui> {
|
||||
/// Returns `None` if the menu is not visible and no content should be rendered.
|
||||
#[must_use]
|
||||
#[doc(alias = "BeginMenu")]
|
||||
pub fn begin_menu_with_enabled(&self, label: &ImStr, enabled: bool) -> Option<MenuToken<'_>> {
|
||||
if unsafe { sys::igBeginMenu(label.as_ptr(), enabled) } {
|
||||
pub fn begin_menu_with_enabled(
|
||||
&self,
|
||||
label: impl AsRef<str>,
|
||||
enabled: bool,
|
||||
) -> Option<MenuToken<'_>> {
|
||||
if unsafe { sys::igBeginMenu(self.scratch_txt(label), enabled) } {
|
||||
Some(MenuToken::new(self))
|
||||
} else {
|
||||
None
|
||||
@ -92,7 +94,7 @@ impl<'ui> Ui<'ui> {
|
||||
/// This is the equivalent of [menu_with_enabled](Self::menu_with_enabled)
|
||||
/// with `enabled` set to `true`.
|
||||
#[doc(alias = "BeginMenu")]
|
||||
pub fn menu<F: FnOnce()>(&self, label: &ImStr, f: F) {
|
||||
pub fn menu<F: FnOnce()>(&self, label: impl AsRef<str>, f: F) {
|
||||
self.menu_with_enabled(label, true, f);
|
||||
}
|
||||
|
||||
@ -100,7 +102,7 @@ impl<'ui> Ui<'ui> {
|
||||
///
|
||||
/// Note: the closure is not called if the menu is not visible.
|
||||
#[doc(alias = "BeginMenu")]
|
||||
pub fn menu_with_enabled<F: FnOnce()>(&self, label: &ImStr, enabled: bool, f: F) {
|
||||
pub fn menu_with_enabled<F: FnOnce()>(&self, label: impl AsRef<str>, enabled: bool, f: F) {
|
||||
if let Some(_menu) = self.begin_menu_with_enabled(label, enabled) {
|
||||
f();
|
||||
}
|
||||
@ -110,16 +112,16 @@ impl<'ui> Ui<'ui> {
|
||||
/// Builder for a menu item.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[must_use]
|
||||
pub struct MenuItem<'a> {
|
||||
label: &'a ImStr,
|
||||
shortcut: Option<&'a ImStr>,
|
||||
pub struct MenuItem<Label, Shortcut = &'static str> {
|
||||
label: Label,
|
||||
shortcut: Option<Shortcut>,
|
||||
selected: bool,
|
||||
enabled: bool,
|
||||
}
|
||||
|
||||
impl<'a> MenuItem<'a> {
|
||||
impl<Label: AsRef<str>> MenuItem<Label> {
|
||||
/// Construct a new menu item builder.
|
||||
pub fn new(label: &ImStr) -> MenuItem {
|
||||
pub fn new(label: Label) -> Self {
|
||||
MenuItem {
|
||||
label,
|
||||
shortcut: None,
|
||||
@ -127,13 +129,23 @@ impl<'a> MenuItem<'a> {
|
||||
enabled: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Label: AsRef<str>, Shortcut: AsRef<str>> MenuItem<Label, Shortcut> {
|
||||
/// Sets the menu item shortcut.
|
||||
///
|
||||
/// Shortcuts are displayed for convenience only and are not automatically handled.
|
||||
#[inline]
|
||||
pub fn shortcut(mut self, shortcut: &'a ImStr) -> Self {
|
||||
self.shortcut = Some(shortcut);
|
||||
self
|
||||
pub fn shortcut<Shortcut2: AsRef<str>>(
|
||||
self,
|
||||
shortcut: Shortcut2,
|
||||
) -> MenuItem<Label, Shortcut2> {
|
||||
MenuItem {
|
||||
label: self.label,
|
||||
shortcut: Some(shortcut),
|
||||
selected: self.selected,
|
||||
enabled: self.enabled,
|
||||
}
|
||||
}
|
||||
/// Sets the selected state of the menu item.
|
||||
///
|
||||
@ -155,20 +167,14 @@ impl<'a> MenuItem<'a> {
|
||||
///
|
||||
/// Returns true if the menu item is activated.
|
||||
#[doc(alias = "MenuItemBool")]
|
||||
pub fn build(self, _: &Ui) -> bool {
|
||||
pub fn build(self, ui: &Ui) -> bool {
|
||||
unsafe {
|
||||
sys::igMenuItemBool(
|
||||
self.label.as_ptr(),
|
||||
self.shortcut.map(ImStr::as_ptr).unwrap_or(ptr::null()),
|
||||
self.selected,
|
||||
self.enabled,
|
||||
)
|
||||
let (label, shortcut) = ui.scratch_txt_with_opt(self.label, self.shortcut);
|
||||
sys::igMenuItemBool(label, shortcut, self.selected, self.enabled)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// # Convenience functions
|
||||
impl<'a> MenuItem<'a> {
|
||||
#[doc(alias = "MenuItemBool")]
|
||||
/// Builds the menu item using a mutable reference to selected state.
|
||||
pub fn build_with_ref(self, ui: &Ui, selected: &mut bool) -> bool {
|
||||
if self.selected(*selected).build(ui) {
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
use bitflags::bitflags;
|
||||
use std::ops::{BitAnd, BitAndAssign, BitOrAssign, Not};
|
||||
|
||||
use crate::string::ImStr;
|
||||
use crate::sys;
|
||||
use crate::{Direction, Ui};
|
||||
|
||||
@ -29,7 +28,7 @@ impl<'ui> Ui<'ui> {
|
||||
/// label's width in the current style.
|
||||
/// the current style.
|
||||
#[doc(alias = "Button")]
|
||||
pub fn button(&self, label: &ImStr) -> bool {
|
||||
pub fn button(&self, label: impl AsRef<str>) -> bool {
|
||||
self.button_with_size(label, [0.0, 0.0])
|
||||
}
|
||||
|
||||
@ -40,48 +39,53 @@ impl<'ui> Ui<'ui> {
|
||||
/// Setting `size` as `[0.0, 0.0]` will size the button to the label's width in
|
||||
/// the current style.
|
||||
#[doc(alias = "Button")]
|
||||
pub fn button_with_size(&self, label: &ImStr, size: [f32; 2]) -> bool {
|
||||
unsafe { sys::igButton(label.as_ptr(), size.into()) }
|
||||
pub fn button_with_size(&self, label: impl AsRef<str>, size: [f32; 2]) -> bool {
|
||||
unsafe { sys::igButton(self.scratch_txt(label), size.into()) }
|
||||
}
|
||||
/// Renders a small clickable button that is easy to embed in text.
|
||||
///
|
||||
/// Returns true if this button was clicked.
|
||||
#[doc(alias = "SmallButton")]
|
||||
pub fn small_button(&self, label: &ImStr) -> bool {
|
||||
unsafe { sys::igSmallButton(label.as_ptr()) }
|
||||
pub fn small_button(&self, label: impl AsRef<str>) -> bool {
|
||||
unsafe { sys::igSmallButton(self.scratch_txt(label)) }
|
||||
}
|
||||
/// Renders a widget with button behaviour without the visual look.
|
||||
///
|
||||
/// Returns true if this button was clicked.
|
||||
#[doc(alias = "InvisibleButton")]
|
||||
pub fn invisible_button(&self, id: &ImStr, size: [f32; 2]) -> bool {
|
||||
unsafe { sys::igInvisibleButton(id.as_ptr(), size.into(), 0) }
|
||||
pub fn invisible_button(&self, id: impl AsRef<str>, size: [f32; 2]) -> bool {
|
||||
unsafe { sys::igInvisibleButton(self.scratch_txt(id), size.into(), 0) }
|
||||
}
|
||||
/// Renders a widget with button behaviour without the visual look.
|
||||
///
|
||||
/// Returns true if this button was clicked.
|
||||
#[doc(alias = "InvisibleButton")]
|
||||
pub fn invisible_button_flags(&self, id: &ImStr, size: [f32; 2], flags: ButtonFlags) -> bool {
|
||||
unsafe { sys::igInvisibleButton(id.as_ptr(), size.into(), flags.bits() as i32) }
|
||||
pub fn invisible_button_flags(
|
||||
&self,
|
||||
id: impl AsRef<str>,
|
||||
size: [f32; 2],
|
||||
flags: ButtonFlags,
|
||||
) -> bool {
|
||||
unsafe { sys::igInvisibleButton(self.scratch_txt(id), size.into(), flags.bits() as i32) }
|
||||
}
|
||||
/// Renders a square button with an arrow shape.
|
||||
///
|
||||
/// Returns true if this button was clicked.
|
||||
#[doc(alias = "ArrowButton")]
|
||||
pub fn arrow_button(&self, id: &ImStr, direction: Direction) -> bool {
|
||||
unsafe { sys::igArrowButton(id.as_ptr(), direction as i32) }
|
||||
pub fn arrow_button(&self, id: impl AsRef<str>, direction: Direction) -> bool {
|
||||
unsafe { sys::igArrowButton(self.scratch_txt(id), direction as i32) }
|
||||
}
|
||||
/// Renders a simple checkbox.
|
||||
///
|
||||
/// Returns true if this checkbox was clicked.
|
||||
#[doc(alias = "Checkbox")]
|
||||
pub fn checkbox(&self, label: &ImStr, value: &mut bool) -> bool {
|
||||
unsafe { sys::igCheckbox(label.as_ptr(), value as *mut bool) }
|
||||
pub fn checkbox(&self, label: impl AsRef<str>, value: &mut bool) -> bool {
|
||||
unsafe { sys::igCheckbox(self.scratch_txt(label), value as *mut bool) }
|
||||
}
|
||||
/// Renders a checkbox suitable for toggling bit flags using a mask.
|
||||
///
|
||||
/// Returns true if this checkbox was clicked.
|
||||
pub fn checkbox_flags<T>(&self, label: &ImStr, flags: &mut T, mask: T) -> bool
|
||||
pub fn checkbox_flags<T>(&self, label: impl AsRef<str>, flags: &mut T, mask: T) -> bool
|
||||
where
|
||||
T: Copy + PartialEq + BitOrAssign + BitAndAssign + BitAnd<Output = T> + Not<Output = T>,
|
||||
{
|
||||
@ -100,14 +104,14 @@ impl<'ui> Ui<'ui> {
|
||||
///
|
||||
/// Returns true if this radio button was clicked.
|
||||
#[doc(alias = "RadioButtonBool")]
|
||||
pub fn radio_button_bool(&self, label: &ImStr, active: bool) -> bool {
|
||||
unsafe { sys::igRadioButtonBool(label.as_ptr(), active) }
|
||||
pub fn radio_button_bool(&self, label: impl AsRef<str>, active: bool) -> bool {
|
||||
unsafe { sys::igRadioButtonBool(self.scratch_txt(label), active) }
|
||||
}
|
||||
/// Renders a radio button suitable for choosing an arbitrary value.
|
||||
///
|
||||
/// Returns true if this radio button was clicked.
|
||||
#[doc(alias = "RadioButtonBool")]
|
||||
pub fn radio_button<T>(&self, label: &ImStr, value: &mut T, button_value: T) -> bool
|
||||
pub fn radio_button<T>(&self, label: impl AsRef<str>, value: &mut T, button_value: T) -> bool
|
||||
where
|
||||
T: Copy + PartialEq,
|
||||
{
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
use std::ptr;
|
||||
|
||||
use crate::string::ImStr;
|
||||
use crate::sys;
|
||||
// use crate::ImStr;
|
||||
use crate::Ui;
|
||||
|
||||
/// Builder for a progress bar widget.
|
||||
@ -14,18 +12,18 @@ use crate::Ui;
|
||||
/// # let ui = imgui.frame();
|
||||
/// ProgressBar::new(0.6)
|
||||
/// .size([100.0, 12.0])
|
||||
/// .overlay_text(im_str!("Progress!"))
|
||||
/// .overlay_text("Progress!")
|
||||
/// .build(&ui);
|
||||
/// ```
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[must_use]
|
||||
pub struct ProgressBar<'a> {
|
||||
pub struct ProgressBar<T = &'static str> {
|
||||
fraction: f32,
|
||||
size: [f32; 2],
|
||||
overlay_text: Option<&'a ImStr>,
|
||||
overlay_text: Option<T>,
|
||||
}
|
||||
|
||||
impl<'a> ProgressBar<'a> {
|
||||
impl ProgressBar {
|
||||
/// Creates a progress bar with a given fraction showing
|
||||
/// the progress (0.0 = 0%, 1.0 = 100%).
|
||||
///
|
||||
@ -33,19 +31,23 @@ impl<'a> ProgressBar<'a> {
|
||||
/// custom size is specified.
|
||||
#[inline]
|
||||
#[doc(alias = "ProgressBar")]
|
||||
pub const fn new(fraction: f32) -> ProgressBar<'a> {
|
||||
pub fn new(fraction: f32) -> Self {
|
||||
ProgressBar {
|
||||
fraction,
|
||||
size: [-1.0, 0.0],
|
||||
overlay_text: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsRef<str>> ProgressBar<T> {
|
||||
/// Sets an optional text that will be drawn over the progress bar.
|
||||
#[inline]
|
||||
pub const fn overlay_text(mut self, overlay_text: &'a ImStr) -> ProgressBar {
|
||||
self.overlay_text = Some(overlay_text);
|
||||
self
|
||||
pub fn overlay_text<T2: AsRef<str>>(self, overlay_text: T2) -> ProgressBar<T2> {
|
||||
ProgressBar {
|
||||
fraction: self.fraction,
|
||||
size: self.size,
|
||||
overlay_text: Some(overlay_text),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the size of the progress bar.
|
||||
@ -53,18 +55,18 @@ impl<'a> ProgressBar<'a> {
|
||||
/// Negative values will automatically align to the end of the axis, zero will let the progress
|
||||
/// bar choose a size, and positive values will use the given size.
|
||||
#[inline]
|
||||
pub const fn size(mut self, size: [f32; 2]) -> Self {
|
||||
pub fn size(mut self, size: [f32; 2]) -> Self {
|
||||
self.size = size;
|
||||
self
|
||||
}
|
||||
|
||||
/// Builds the progress bar
|
||||
pub fn build(self, _: &Ui) {
|
||||
pub fn build(self, ui: &Ui) {
|
||||
unsafe {
|
||||
sys::igProgressBar(
|
||||
self.fraction,
|
||||
self.size.into(),
|
||||
self.overlay_text.map(|x| x.as_ptr()).unwrap_or(ptr::null()),
|
||||
ui.scratch_txt_opt(self.overlay_text),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
use bitflags::bitflags;
|
||||
|
||||
use crate::string::ImStr;
|
||||
use crate::sys;
|
||||
use crate::Ui;
|
||||
|
||||
@ -24,18 +23,18 @@ bitflags!(
|
||||
/// Builder for a selectable widget.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[must_use]
|
||||
pub struct Selectable<'a> {
|
||||
label: &'a ImStr,
|
||||
pub struct Selectable<T> {
|
||||
label: T,
|
||||
selected: bool,
|
||||
flags: SelectableFlags,
|
||||
size: [f32; 2],
|
||||
}
|
||||
|
||||
impl<'a> Selectable<'a> {
|
||||
impl<T: AsRef<str>> Selectable<T> {
|
||||
/// Constructs a new selectable builder.
|
||||
#[inline]
|
||||
#[doc(alias = "Selectable")]
|
||||
pub const fn new(label: &ImStr) -> Selectable {
|
||||
pub fn new(label: T) -> Self {
|
||||
Selectable {
|
||||
label,
|
||||
selected: false,
|
||||
@ -108,20 +107,17 @@ impl<'a> Selectable<'a> {
|
||||
/// Builds the selectable.
|
||||
///
|
||||
/// Returns true if the selectable was clicked.
|
||||
pub fn build(self, _: &Ui) -> bool {
|
||||
pub fn build(self, ui: &Ui) -> bool {
|
||||
unsafe {
|
||||
sys::igSelectableBool(
|
||||
self.label.as_ptr(),
|
||||
ui.scratch_txt(self.label),
|
||||
self.selected,
|
||||
self.flags.bits() as i32,
|
||||
self.size.into(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// # Convenience functions
|
||||
impl<'a> Selectable<'a> {
|
||||
/// Builds the selectable using a mutable reference to selected state.
|
||||
pub fn build_with_ref(self, ui: &Ui, selected: &mut bool) -> bool {
|
||||
if self.selected(*selected).build(ui) {
|
||||
|
||||
@ -1,9 +1,7 @@
|
||||
use bitflags::bitflags;
|
||||
use std::os::raw::c_void;
|
||||
use std::ptr;
|
||||
|
||||
use crate::internal::DataTypeKind;
|
||||
use crate::string::ImStr;
|
||||
use crate::sys;
|
||||
use crate::Ui;
|
||||
|
||||
@ -27,18 +25,18 @@ bitflags!(
|
||||
/// Builder for a slider widget.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[must_use]
|
||||
pub struct Slider<'a, T: DataTypeKind> {
|
||||
label: &'a ImStr,
|
||||
min: T,
|
||||
max: T,
|
||||
display_format: Option<&'a ImStr>,
|
||||
pub struct Slider<Label, Data, Format = &'static str> {
|
||||
label: Label,
|
||||
min: Data,
|
||||
max: Data,
|
||||
display_format: Option<Format>,
|
||||
flags: SliderFlags,
|
||||
}
|
||||
|
||||
impl<'a, T: DataTypeKind> Slider<'a, T> {
|
||||
impl<T: AsRef<str>, K: DataTypeKind> Slider<T, K> {
|
||||
/// Constructs a new slider builder with the given range.
|
||||
#[doc(alias = "SliderScalar", alias = "SliderScalarN")]
|
||||
pub fn new(label: &ImStr, min: T, max: T) -> Slider<T> {
|
||||
pub fn new(label: T, min: K, max: K) -> Self {
|
||||
Slider {
|
||||
label,
|
||||
min,
|
||||
@ -47,6 +45,14 @@ impl<'a, T: DataTypeKind> Slider<'a, T> {
|
||||
flags: SliderFlags::empty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Label, Data, Format> Slider<Label, Data, Format>
|
||||
where
|
||||
Label: AsRef<str>,
|
||||
Data: DataTypeKind,
|
||||
Format: AsRef<str>,
|
||||
{
|
||||
/// Sets the range inclusively, such that both values given
|
||||
/// are valid values which the slider can be dragged to.
|
||||
///
|
||||
@ -61,16 +67,24 @@ impl<'a, T: DataTypeKind> Slider<'a, T> {
|
||||
/// It is safe, though up to C++ Dear ImGui, on how to handle when
|
||||
/// `min > max`.
|
||||
#[inline]
|
||||
pub fn range(mut self, min: T, max: T) -> Self {
|
||||
pub fn range(mut self, min: Data, max: Data) -> Self {
|
||||
self.min = min;
|
||||
self.max = max;
|
||||
self
|
||||
}
|
||||
/// Sets the display format using *a C-style printf string*
|
||||
#[inline]
|
||||
pub fn display_format(mut self, display_format: &'a ImStr) -> Self {
|
||||
self.display_format = Some(display_format);
|
||||
self
|
||||
pub fn display_format<Format2: AsRef<str>>(
|
||||
self,
|
||||
display_format: Format2,
|
||||
) -> Slider<Label, Data, Format2> {
|
||||
Slider {
|
||||
label: self.label,
|
||||
min: self.min,
|
||||
max: self.max,
|
||||
display_format: Some(display_format),
|
||||
flags: self.flags,
|
||||
}
|
||||
}
|
||||
/// Replaces all current settings with the given flags
|
||||
#[inline]
|
||||
@ -81,17 +95,17 @@ impl<'a, T: DataTypeKind> Slider<'a, T> {
|
||||
/// Builds a slider that is bound to the given value.
|
||||
///
|
||||
/// Returns true if the slider value was changed.
|
||||
pub fn build(self, _: &Ui, value: &mut T) -> bool {
|
||||
pub fn build(self, ui: &Ui, value: &mut Data) -> bool {
|
||||
unsafe {
|
||||
let (label, display_format) = ui.scratch_txt_with_opt(self.label, self.display_format);
|
||||
|
||||
sys::igSliderScalar(
|
||||
self.label.as_ptr(),
|
||||
T::KIND as i32,
|
||||
value as *mut T as *mut c_void,
|
||||
&self.min as *const T as *const c_void,
|
||||
&self.max as *const T as *const c_void,
|
||||
self.display_format
|
||||
.map(ImStr::as_ptr)
|
||||
.unwrap_or(ptr::null()),
|
||||
label,
|
||||
Data::KIND as i32,
|
||||
value as *mut Data as *mut c_void,
|
||||
&self.min as *const Data as *const c_void,
|
||||
&self.max as *const Data as *const c_void,
|
||||
display_format,
|
||||
self.flags.bits() as i32,
|
||||
)
|
||||
}
|
||||
@ -99,18 +113,18 @@ impl<'a, T: DataTypeKind> Slider<'a, T> {
|
||||
/// Builds a horizontal array of multiple sliders attached to the given slice.
|
||||
///
|
||||
/// Returns true if any slider value was changed.
|
||||
pub fn build_array(self, _: &Ui, values: &mut [T]) -> bool {
|
||||
pub fn build_array(self, ui: &Ui, values: &mut [Data]) -> bool {
|
||||
unsafe {
|
||||
let (label, display_format) = ui.scratch_txt_with_opt(self.label, self.display_format);
|
||||
|
||||
sys::igSliderScalarN(
|
||||
self.label.as_ptr(),
|
||||
T::KIND as i32,
|
||||
label,
|
||||
Data::KIND as i32,
|
||||
values.as_mut_ptr() as *mut c_void,
|
||||
values.len() as i32,
|
||||
&self.min as *const T as *const c_void,
|
||||
&self.max as *const T as *const c_void,
|
||||
self.display_format
|
||||
.map(ImStr::as_ptr)
|
||||
.unwrap_or(ptr::null()),
|
||||
&self.min as *const Data as *const c_void,
|
||||
&self.max as *const Data as *const c_void,
|
||||
display_format,
|
||||
self.flags.bits() as i32,
|
||||
)
|
||||
}
|
||||
@ -120,16 +134,20 @@ impl<'a, T: DataTypeKind> Slider<'a, T> {
|
||||
/// Builder for a vertical slider widget.
|
||||
#[derive(Clone, Debug)]
|
||||
#[must_use]
|
||||
pub struct VerticalSlider<'a, T: DataTypeKind + Copy> {
|
||||
label: &'a ImStr,
|
||||
pub struct VerticalSlider<Label, Data, Format = &'static str> {
|
||||
label: Label,
|
||||
size: [f32; 2],
|
||||
min: T,
|
||||
max: T,
|
||||
display_format: Option<&'a ImStr>,
|
||||
min: Data,
|
||||
max: Data,
|
||||
display_format: Option<Format>,
|
||||
flags: SliderFlags,
|
||||
}
|
||||
|
||||
impl<'a, T: DataTypeKind> VerticalSlider<'a, T> {
|
||||
impl<Label, Data> VerticalSlider<Label, Data>
|
||||
where
|
||||
Label: AsRef<str>,
|
||||
Data: DataTypeKind,
|
||||
{
|
||||
/// Constructs a new vertical slider builder with the given size and range.
|
||||
///
|
||||
/// ```rust
|
||||
@ -143,7 +161,7 @@ impl<'a, T: DataTypeKind> VerticalSlider<'a, T> {
|
||||
/// It is safe, though up to C++ Dear ImGui, on how to handle when
|
||||
/// `min > max`.
|
||||
#[doc(alias = "VSliderScalar")]
|
||||
pub fn new(label: &ImStr, size: [f32; 2], min: T, max: T) -> VerticalSlider<T> {
|
||||
pub fn new(label: Label, size: [f32; 2], min: Data, max: Data) -> Self {
|
||||
VerticalSlider {
|
||||
label,
|
||||
size,
|
||||
@ -153,7 +171,14 @@ impl<'a, T: DataTypeKind> VerticalSlider<'a, T> {
|
||||
flags: SliderFlags::empty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Label, Data, Format> VerticalSlider<Label, Data, Format>
|
||||
where
|
||||
Label: AsRef<str>,
|
||||
Data: DataTypeKind,
|
||||
Format: AsRef<str>,
|
||||
{
|
||||
/// Sets the range for the vertical slider.
|
||||
///
|
||||
/// ```rust
|
||||
@ -167,16 +192,25 @@ impl<'a, T: DataTypeKind> VerticalSlider<'a, T> {
|
||||
/// It is safe, though up to C++ Dear ImGui, on how to handle when
|
||||
/// `min > max`.
|
||||
#[inline]
|
||||
pub fn range(mut self, min: T, max: T) -> Self {
|
||||
pub fn range(mut self, min: Data, max: Data) -> Self {
|
||||
self.min = min;
|
||||
self.max = max;
|
||||
self
|
||||
}
|
||||
/// Sets the display format using *a C-style printf string*
|
||||
#[inline]
|
||||
pub fn display_format(mut self, display_format: &'a ImStr) -> Self {
|
||||
self.display_format = Some(display_format);
|
||||
self
|
||||
pub fn display_format<Format2: AsRef<str>>(
|
||||
self,
|
||||
display_format: Format2,
|
||||
) -> VerticalSlider<Label, Data, Format2> {
|
||||
VerticalSlider {
|
||||
label: self.label,
|
||||
size: self.size,
|
||||
min: self.min,
|
||||
max: self.max,
|
||||
display_format: Some(display_format),
|
||||
flags: self.flags,
|
||||
}
|
||||
}
|
||||
/// Replaces all current settings with the given flags
|
||||
#[inline]
|
||||
@ -187,18 +221,18 @@ impl<'a, T: DataTypeKind> VerticalSlider<'a, T> {
|
||||
/// Builds a vertical slider that is bound to the given value.
|
||||
///
|
||||
/// Returns true if the slider value was changed.
|
||||
pub fn build(self, _: &Ui, value: &mut T) -> bool {
|
||||
pub fn build(self, ui: &Ui, value: &mut Data) -> bool {
|
||||
unsafe {
|
||||
let (label, display_format) = ui.scratch_txt_with_opt(self.label, self.display_format);
|
||||
|
||||
sys::igVSliderScalar(
|
||||
self.label.as_ptr(),
|
||||
label,
|
||||
self.size.into(),
|
||||
T::KIND as i32,
|
||||
value as *mut T as *mut c_void,
|
||||
&self.min as *const T as *const c_void,
|
||||
&self.max as *const T as *const c_void,
|
||||
self.display_format
|
||||
.map(ImStr::as_ptr)
|
||||
.unwrap_or(ptr::null()),
|
||||
Data::KIND as i32,
|
||||
value as *mut Data as *mut c_void,
|
||||
&self.min as *const Data as *const c_void,
|
||||
&self.max as *const Data as *const c_void,
|
||||
display_format,
|
||||
self.flags.bits() as i32,
|
||||
)
|
||||
}
|
||||
@ -208,27 +242,37 @@ impl<'a, T: DataTypeKind> VerticalSlider<'a, T> {
|
||||
/// Builder for an angle slider widget.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[must_use]
|
||||
pub struct AngleSlider<'a> {
|
||||
label: &'a ImStr,
|
||||
pub struct AngleSlider<Label, Format = &'static str> {
|
||||
label: Label,
|
||||
min_degrees: f32,
|
||||
max_degrees: f32,
|
||||
display_format: &'a ImStr,
|
||||
display_format: Format,
|
||||
flags: SliderFlags,
|
||||
}
|
||||
|
||||
impl<'a> AngleSlider<'a> {
|
||||
impl<Label> AngleSlider<Label>
|
||||
where
|
||||
Label: AsRef<str>,
|
||||
{
|
||||
/// Constructs a new angle slider builder, where its minimum defaults to -360.0 and
|
||||
/// maximum defaults to 360.0
|
||||
#[doc(alias = "SliderAngle")]
|
||||
pub fn new(label: &ImStr) -> AngleSlider {
|
||||
pub fn new(label: Label) -> Self {
|
||||
AngleSlider {
|
||||
label,
|
||||
min_degrees: -360.0,
|
||||
max_degrees: 360.0,
|
||||
display_format: im_str!("%.0f deg"),
|
||||
display_format: "%.0f deg",
|
||||
flags: SliderFlags::empty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Label, Format> AngleSlider<Label, Format>
|
||||
where
|
||||
Label: AsRef<str>,
|
||||
Format: AsRef<str>,
|
||||
{
|
||||
/// Sets the range in degrees (inclusive)
|
||||
/// ```rust
|
||||
/// # use imgui::im_str;
|
||||
@ -260,9 +304,17 @@ impl<'a> AngleSlider<'a> {
|
||||
}
|
||||
/// Sets the display format using *a C-style printf string*
|
||||
#[inline]
|
||||
pub fn display_format(mut self, display_format: &'a ImStr) -> Self {
|
||||
self.display_format = display_format;
|
||||
self
|
||||
pub fn display_format<Format2: AsRef<str>>(
|
||||
self,
|
||||
display_format: Format2,
|
||||
) -> AngleSlider<Label, Format2> {
|
||||
AngleSlider {
|
||||
label: self.label,
|
||||
min_degrees: self.min_degrees,
|
||||
max_degrees: self.max_degrees,
|
||||
display_format,
|
||||
flags: self.flags,
|
||||
}
|
||||
}
|
||||
/// Replaces all current settings with the given flags
|
||||
#[inline]
|
||||
@ -273,14 +325,16 @@ impl<'a> AngleSlider<'a> {
|
||||
/// Builds an angle slider that is bound to the given value (in radians).
|
||||
///
|
||||
/// Returns true if the slider value was changed.
|
||||
pub fn build(self, _: &Ui, value_rad: &mut f32) -> bool {
|
||||
pub fn build(self, ui: &Ui, value_rad: &mut f32) -> bool {
|
||||
unsafe {
|
||||
let (label, display_format) = ui.scratch_txt_two(self.label, self.display_format);
|
||||
|
||||
sys::igSliderAngle(
|
||||
self.label.as_ptr(),
|
||||
label,
|
||||
value_rad as *mut _,
|
||||
self.min_degrees,
|
||||
self.max_degrees,
|
||||
self.display_format.as_ptr(),
|
||||
display_format,
|
||||
self.flags.bits() as i32,
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
//! Safe wrapper around imgui-sys for tab menu.
|
||||
//!
|
||||
//! # Examples
|
||||
//
|
||||
//! ```no_run
|
||||
@ -19,7 +17,6 @@
|
||||
//! ```
|
||||
//!
|
||||
//! See `test_window_impl.rs` for a more complicated example.
|
||||
use crate::string::ImStr;
|
||||
use crate::sys;
|
||||
use crate::Ui;
|
||||
use bitflags::bitflags;
|
||||
@ -56,15 +53,15 @@ bitflags! {
|
||||
}
|
||||
|
||||
/// Builder for a tab bar.
|
||||
pub struct TabBar<'a> {
|
||||
id: &'a ImStr,
|
||||
pub struct TabBar<T> {
|
||||
id: T,
|
||||
flags: TabBarFlags,
|
||||
}
|
||||
|
||||
impl<'a> TabBar<'a> {
|
||||
impl<T: AsRef<str>> TabBar<T> {
|
||||
#[inline]
|
||||
#[doc(alias = "BeginTabBar")]
|
||||
pub const fn new(id: &'a ImStr) -> Self {
|
||||
pub fn new(id: T) -> Self {
|
||||
Self {
|
||||
id,
|
||||
flags: TabBarFlags::empty(),
|
||||
@ -90,23 +87,15 @@ impl<'a> TabBar<'a> {
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn begin<'ui>(self, ui: &Ui<'ui>) -> Option<TabBarToken<'ui>> {
|
||||
let should_render =
|
||||
unsafe { sys::igBeginTabBar(self.id.as_ptr(), self.flags.bits() as i32) };
|
||||
|
||||
if should_render {
|
||||
Some(TabBarToken::new(ui))
|
||||
} else {
|
||||
unsafe { sys::igEndTabBar() };
|
||||
None
|
||||
}
|
||||
pub fn begin<'ui>(self, ui: &'ui Ui<'_>) -> Option<TabBarToken<'ui>> {
|
||||
ui.tab_bar_with_flags(self.id, self.flags)
|
||||
}
|
||||
|
||||
/// Creates a tab bar and runs a closure to construct the contents.
|
||||
/// Returns the result of the closure, if it is called.
|
||||
///
|
||||
/// Note: the closure is not called if no tabbar content is visible
|
||||
pub fn build<T, F: FnOnce() -> T>(self, ui: &Ui<'_>, f: F) -> Option<T> {
|
||||
pub fn build<R, F: FnOnce() -> R>(self, ui: &Ui<'_>, f: F) -> Option<R> {
|
||||
self.begin(ui).map(|_tab| f())
|
||||
}
|
||||
}
|
||||
@ -120,23 +109,23 @@ create_token!(
|
||||
drop { sys::igEndTabBar() }
|
||||
);
|
||||
|
||||
pub struct TabItem<'a> {
|
||||
name: &'a ImStr,
|
||||
pub struct TabItem<'a, T> {
|
||||
label: T,
|
||||
opened: Option<&'a mut bool>,
|
||||
flags: TabItemFlags,
|
||||
}
|
||||
|
||||
impl<'a> TabItem<'a> {
|
||||
impl<'a, T: AsRef<str>> TabItem<'a, T> {
|
||||
#[doc(alias = "BeginTabItem")]
|
||||
pub fn new(name: &'a ImStr) -> Self {
|
||||
pub fn new(name: T) -> Self {
|
||||
Self {
|
||||
name,
|
||||
label: name,
|
||||
opened: None,
|
||||
flags: TabItemFlags::empty(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Will open or close the tab.\
|
||||
/// Will open or close the tab.
|
||||
///
|
||||
/// True to display the tab. Tab item is visible by default.
|
||||
#[inline]
|
||||
@ -155,29 +144,15 @@ impl<'a> TabItem<'a> {
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn begin<'ui>(self, ui: &Ui<'ui>) -> Option<TabItemToken<'ui>> {
|
||||
let should_render = unsafe {
|
||||
sys::igBeginTabItem(
|
||||
self.name.as_ptr(),
|
||||
self.opened
|
||||
.map(|x| x as *mut bool)
|
||||
.unwrap_or(ptr::null_mut()),
|
||||
self.flags.bits() as i32,
|
||||
)
|
||||
};
|
||||
|
||||
if should_render {
|
||||
Some(TabItemToken::new(ui))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
pub fn begin<'ui>(self, ui: &'ui Ui<'_>) -> Option<TabItemToken<'ui>> {
|
||||
ui.tab_item_with_flags(self.label, self.opened, self.flags)
|
||||
}
|
||||
|
||||
/// Creates a tab item and runs a closure to construct the contents.
|
||||
/// Returns the result of the closure, if it is called.
|
||||
///
|
||||
/// Note: the closure is not called if the tab item is not selected
|
||||
pub fn build<T, F: FnOnce() -> T>(self, ui: &Ui<'_>, f: F) -> Option<T> {
|
||||
pub fn build<R, F: FnOnce() -> R>(self, ui: &Ui<'_>, f: F) -> Option<R> {
|
||||
self.begin(ui).map(|_tab| f())
|
||||
}
|
||||
}
|
||||
@ -190,3 +165,74 @@ create_token!(
|
||||
/// Ends a tab bar item.
|
||||
drop { sys::igEndTabItem() }
|
||||
);
|
||||
|
||||
impl Ui<'_> {
|
||||
/// Creates a tab bar and returns a tab bar token, allowing you to append
|
||||
/// Tab items afterwards. This passes no flags. To pass flags explicitly,
|
||||
/// use [tab_bar_with_flags](Self::tab_bar_with_flags).
|
||||
pub fn tab_bar(&self, id: impl AsRef<str>) -> Option<TabBarToken<'_>> {
|
||||
self.tab_bar_with_flags(id, TabBarFlags::empty())
|
||||
}
|
||||
//
|
||||
/// Creates a tab bar and returns a tab bar token, allowing you to append
|
||||
/// Tab items afterwards.
|
||||
pub fn tab_bar_with_flags(
|
||||
&self,
|
||||
id: impl AsRef<str>,
|
||||
flags: TabBarFlags,
|
||||
) -> Option<TabBarToken<'_>> {
|
||||
let should_render =
|
||||
unsafe { sys::igBeginTabBar(self.scratch_txt(id), flags.bits() as i32) };
|
||||
|
||||
if should_render {
|
||||
Some(TabBarToken::new(self))
|
||||
} else {
|
||||
unsafe { sys::igEndTabBar() };
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new tab item and returns a token if its contents are visible.
|
||||
///
|
||||
/// By default, this doesn't pass an opened bool nor any flags. See [tab_item_with_opened]
|
||||
/// and [tab_item_with_flags] for more.
|
||||
///
|
||||
/// [tab_item_with_opened]: Self::tab_item_with_opened
|
||||
/// [tab_item_with_flags]: Self::tab_item_with_flags
|
||||
pub fn tab_item(&self, label: impl AsRef<str>) -> Option<TabItemToken<'_>> {
|
||||
self.tab_item_with_flags(label, None, TabItemFlags::empty())
|
||||
}
|
||||
|
||||
/// Creates a new tab item and returns a token if its contents are visible.
|
||||
///
|
||||
/// By default, this doesn't pass any flags. See `[tab_item_with_flags]` for more.
|
||||
pub fn tab_item_with_opened(
|
||||
&self,
|
||||
label: impl AsRef<str>,
|
||||
opened: &mut bool,
|
||||
) -> Option<TabItemToken<'_>> {
|
||||
self.tab_item_with_flags(label, Some(opened), TabItemFlags::empty())
|
||||
}
|
||||
|
||||
/// Creates a new tab item and returns a token if its contents are visible.
|
||||
pub fn tab_item_with_flags(
|
||||
&self,
|
||||
label: impl AsRef<str>,
|
||||
opened: Option<&mut bool>,
|
||||
flags: TabItemFlags,
|
||||
) -> Option<TabItemToken<'_>> {
|
||||
let should_render = unsafe {
|
||||
sys::igBeginTabItem(
|
||||
self.scratch_txt(label),
|
||||
opened.map(|x| x as *mut bool).unwrap_or(ptr::null_mut()),
|
||||
flags.bits() as i32,
|
||||
)
|
||||
};
|
||||
|
||||
if should_render {
|
||||
Some(TabItemToken::new(self))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use std::os::raw::c_char;
|
||||
|
||||
use crate::string::ImStr;
|
||||
// use crate::string::ImStr;
|
||||
use crate::style::StyleColor;
|
||||
use crate::Ui;
|
||||
|
||||
@ -38,17 +38,18 @@ impl<'ui> Ui<'ui> {
|
||||
}
|
||||
/// Renders text wrapped to the end of window (or column)
|
||||
#[doc(alias = "TextWrapperd")]
|
||||
pub fn text_wrapped(&self, text: &ImStr) {
|
||||
unsafe { sys::igTextWrapped(fmt_ptr(), text.as_ptr()) }
|
||||
pub fn text_wrapped(&self, text: impl AsRef<str>) {
|
||||
unsafe { sys::igTextWrapped(fmt_ptr(), self.scratch_txt(text)) }
|
||||
}
|
||||
/// Render a text + label combination aligned the same way as value+label widgets
|
||||
#[doc(alias = "LabelText")]
|
||||
pub fn label_text(&self, label: &ImStr, text: &ImStr) {
|
||||
unsafe { sys::igLabelText(label.as_ptr(), fmt_ptr(), text.as_ptr()) }
|
||||
pub fn label_text(&self, label: impl AsRef<str>, text: impl AsRef<str>) {
|
||||
let (ptr_one, ptr_two) = self.scratch_txt_two(label, text);
|
||||
unsafe { sys::igLabelText(ptr_one, fmt_ptr(), ptr_two) }
|
||||
}
|
||||
/// Renders text with a little bullet aligned to the typical tree node
|
||||
#[doc(alias = "BulletText")]
|
||||
pub fn bullet_text(&self, text: &ImStr) {
|
||||
unsafe { sys::igBulletText(fmt_ptr(), text.as_ptr()) }
|
||||
pub fn bullet_text(&self, text: impl AsRef<str>) {
|
||||
unsafe { sys::igBulletText(fmt_ptr(), self.scratch_txt(text)) }
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
use bitflags::bitflags;
|
||||
use std::os::raw::{c_char, c_void};
|
||||
|
||||
use crate::string::ImStr;
|
||||
// use crate::string::ImStr;
|
||||
use crate::sys;
|
||||
use crate::{Condition, Ui};
|
||||
|
||||
@ -61,27 +61,26 @@ fn fmt_ptr() -> *const c_char {
|
||||
|
||||
/// Unique ID used by tree nodes
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum TreeNodeId<'a> {
|
||||
Str(&'a ImStr),
|
||||
pub enum TreeNodeId<T> {
|
||||
Str(T),
|
||||
Ptr(*const c_void),
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized + AsRef<ImStr>> From<&'a T> for TreeNodeId<'a> {
|
||||
#[inline]
|
||||
fn from(s: &'a T) -> Self {
|
||||
TreeNodeId::Str(s.as_ref())
|
||||
impl<T: AsRef<str>> From<T> for TreeNodeId<T> {
|
||||
fn from(s: T) -> Self {
|
||||
TreeNodeId::Str(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<*const T> for TreeNodeId<'static> {
|
||||
#[inline]
|
||||
// this is a bit wonky here using the T param...
|
||||
impl<T> From<*const T> for TreeNodeId<T> {
|
||||
fn from(p: *const T) -> Self {
|
||||
TreeNodeId::Ptr(p as *const c_void)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<*mut T> for TreeNodeId<'static> {
|
||||
#[inline]
|
||||
// this is a bit wonky here using the T param...
|
||||
impl<T> From<*mut T> for TreeNodeId<T> {
|
||||
fn from(p: *mut T) -> Self {
|
||||
TreeNodeId::Ptr(p as *const T as *const c_void)
|
||||
}
|
||||
@ -90,17 +89,17 @@ impl<T> From<*mut T> for TreeNodeId<'static> {
|
||||
/// Builder for a tree node widget
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[must_use]
|
||||
pub struct TreeNode<'a> {
|
||||
id: TreeNodeId<'a>,
|
||||
label: Option<&'a ImStr>,
|
||||
pub struct TreeNode<T, L = &'static str> {
|
||||
id: TreeNodeId<T>,
|
||||
label: Option<L>,
|
||||
opened: bool,
|
||||
opened_cond: Condition,
|
||||
flags: TreeNodeFlags,
|
||||
}
|
||||
|
||||
impl<'a> TreeNode<'a> {
|
||||
impl<T: AsRef<str>> TreeNode<T, &'static str> {
|
||||
/// Constructs a new tree node builder
|
||||
pub fn new<I: Into<TreeNodeId<'a>>>(id: I) -> TreeNode<'a> {
|
||||
pub fn new<I: Into<TreeNodeId<T>>>(id: I) -> TreeNode<T, &'static str> {
|
||||
TreeNode {
|
||||
id: id.into(),
|
||||
label: None,
|
||||
@ -109,125 +108,133 @@ impl<'a> TreeNode<'a> {
|
||||
flags: TreeNodeFlags::empty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsRef<str>, L: AsRef<str>> TreeNode<T, L> {
|
||||
/// Sets the tree node label
|
||||
#[inline]
|
||||
pub fn label(mut self, label: &'a ImStr) -> Self {
|
||||
self.label = Some(label);
|
||||
self
|
||||
pub fn label<I: Into<TreeNodeId<L2>>, L2: AsRef<str>>(self, label: L2) -> TreeNode<T, L2> {
|
||||
TreeNode {
|
||||
label: Some(label),
|
||||
id: self.id,
|
||||
opened: self.opened,
|
||||
opened_cond: self.opened_cond,
|
||||
flags: self.flags,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the opened state of the tree node, which is applied based on the given condition value
|
||||
#[inline]
|
||||
pub fn opened(mut self, opened: bool, cond: Condition) -> Self {
|
||||
self.opened = opened;
|
||||
self.opened_cond = cond;
|
||||
self
|
||||
}
|
||||
|
||||
/// Replaces all current settings with the given flags.
|
||||
#[inline]
|
||||
pub fn flags(mut self, flags: TreeNodeFlags) -> Self {
|
||||
self.flags = flags;
|
||||
self
|
||||
}
|
||||
|
||||
/// Enables/disables drawing the tree node in selected state.
|
||||
///
|
||||
/// Disabled by default.
|
||||
#[inline]
|
||||
pub fn selected(mut self, value: bool) -> Self {
|
||||
self.flags.set(TreeNodeFlags::SELECTED, value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Enables/disables full-colored frame.
|
||||
///
|
||||
/// Disabled by default.
|
||||
#[inline]
|
||||
pub fn framed(mut self, value: bool) -> Self {
|
||||
self.flags.set(TreeNodeFlags::FRAMED, value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Enables/disables allowing the tree node to overlap subsequent widgets.
|
||||
///
|
||||
/// Disabled by default.
|
||||
#[inline]
|
||||
pub fn allow_item_overlap(mut self, value: bool) -> Self {
|
||||
self.flags.set(TreeNodeFlags::ALLOW_ITEM_OVERLAP, value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Enables/disables automatic tree push when the tree node is open (= adds extra indentation
|
||||
/// and pushes to the ID stack).
|
||||
///
|
||||
/// Enabled by default.
|
||||
#[inline]
|
||||
pub fn tree_push_on_open(mut self, value: bool) -> Self {
|
||||
self.flags.set(TreeNodeFlags::NO_TREE_PUSH_ON_OPEN, !value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Enables/disables automatic opening of the tree node when logging is active.
|
||||
///
|
||||
/// By default, logging will automatically open all tree nodes.
|
||||
///
|
||||
/// Enabled by default.
|
||||
#[inline]
|
||||
pub fn auto_open_on_log(mut self, value: bool) -> Self {
|
||||
self.flags.set(TreeNodeFlags::NO_AUTO_OPEN_ON_LOG, !value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the default open state for the tree node.
|
||||
///
|
||||
/// Tree nodes are closed by default.
|
||||
#[inline]
|
||||
pub fn default_open(mut self, value: bool) -> Self {
|
||||
self.flags.set(TreeNodeFlags::DEFAULT_OPEN, value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Only open when the tree node is double-clicked.
|
||||
///
|
||||
/// Disabled by default.
|
||||
#[inline]
|
||||
pub fn open_on_double_click(mut self, value: bool) -> Self {
|
||||
self.flags.set(TreeNodeFlags::OPEN_ON_DOUBLE_CLICK, value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Only open when clicking the arrow part of the tree node.
|
||||
///
|
||||
/// Disabled by default.
|
||||
#[inline]
|
||||
pub fn open_on_arrow(mut self, value: bool) -> Self {
|
||||
self.flags.set(TreeNodeFlags::OPEN_ON_ARROW, value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Enable/disables leaf mode (no collapsing, no arrow).
|
||||
///
|
||||
/// Disabled by default.
|
||||
#[inline]
|
||||
pub fn leaf(mut self, value: bool) -> Self {
|
||||
self.flags.set(TreeNodeFlags::LEAF, value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Display a bullet instead of arrow.
|
||||
///
|
||||
/// Disabled by default.
|
||||
#[inline]
|
||||
pub fn bullet(mut self, value: bool) -> Self {
|
||||
self.flags.set(TreeNodeFlags::BULLET, value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Use `frame_padding` to vertically align text baseline to regular widget height.
|
||||
///
|
||||
/// Disabled by default.
|
||||
#[inline]
|
||||
pub fn frame_padding(mut self, value: bool) -> Self {
|
||||
self.flags.set(TreeNodeFlags::FRAME_PADDING, value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Left direction may move to this tree node from any of its child.
|
||||
///
|
||||
/// Disabled by default.
|
||||
#[inline]
|
||||
pub fn nav_left_jumps_back_here(mut self, value: bool) -> Self {
|
||||
self.flags
|
||||
.set(TreeNodeFlags::NAV_LEFT_JUMPS_BACK_HERE, value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Pushes a tree node and starts appending to it.
|
||||
///
|
||||
/// Returns `Some(TreeNodeToken)` if the tree node is open. After content has been
|
||||
@ -240,17 +247,25 @@ impl<'a> TreeNode<'a> {
|
||||
sys::igSetNextItemOpen(self.opened, self.opened_cond as i32);
|
||||
}
|
||||
match self.id {
|
||||
TreeNodeId::Str(id) => sys::igTreeNodeExStrStr(
|
||||
id.as_ptr(),
|
||||
self.flags.bits() as i32,
|
||||
fmt_ptr(),
|
||||
self.label.unwrap_or(id).as_ptr(),
|
||||
),
|
||||
TreeNodeId::Str(id) => {
|
||||
let (id, label) = match self.label {
|
||||
Some(label) => ui.scratch_txt_two(id, label),
|
||||
None => {
|
||||
let v = ui.scratch_txt(id);
|
||||
(v, v)
|
||||
}
|
||||
};
|
||||
|
||||
sys::igTreeNodeExStrStr(id, self.flags.bits() as i32, fmt_ptr(), label)
|
||||
}
|
||||
TreeNodeId::Ptr(id) => sys::igTreeNodeExPtr(
|
||||
id,
|
||||
self.flags.bits() as i32,
|
||||
fmt_ptr(),
|
||||
self.label.unwrap_or_default().as_ptr(),
|
||||
match self.label {
|
||||
Some(v) => ui.scratch_txt(v),
|
||||
None => ui.scratch_txt(""),
|
||||
},
|
||||
),
|
||||
}
|
||||
};
|
||||
@ -267,7 +282,7 @@ impl<'a> TreeNode<'a> {
|
||||
/// Returns the result of the closure, if it is called.
|
||||
///
|
||||
/// Note: the closure is not called if the tree node is not open.
|
||||
pub fn build<T, F: FnOnce() -> T>(self, ui: &Ui<'_>, f: F) -> Option<T> {
|
||||
pub fn build<R, F: FnOnce() -> R>(self, ui: &Ui<'_>, f: F) -> Option<R> {
|
||||
self.push(ui).map(|_node| f())
|
||||
}
|
||||
}
|
||||
@ -310,15 +325,15 @@ impl Drop for TreeNodeToken<'_> {
|
||||
/// Builder for a collapsing header widget
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[must_use]
|
||||
pub struct CollapsingHeader<'a> {
|
||||
label: &'a ImStr,
|
||||
pub struct CollapsingHeader<T> {
|
||||
label: T,
|
||||
flags: TreeNodeFlags,
|
||||
}
|
||||
|
||||
impl<'a> CollapsingHeader<'a> {
|
||||
impl<T: AsRef<str>> CollapsingHeader<T> {
|
||||
/// Constructs a new collapsing header builder
|
||||
#[doc(alias = "CollapsingHeader")]
|
||||
pub fn new(label: &ImStr) -> CollapsingHeader {
|
||||
pub fn new(label: T) -> CollapsingHeader<T> {
|
||||
CollapsingHeader {
|
||||
label,
|
||||
flags: TreeNodeFlags::empty(),
|
||||
@ -413,9 +428,12 @@ impl<'a> CollapsingHeader<'a> {
|
||||
/// Returns true if the collapsing header is open and content should be rendered.
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub fn build(self, _: &Ui) -> bool {
|
||||
pub fn build(self, ui: &Ui) -> bool {
|
||||
unsafe {
|
||||
sys::igCollapsingHeaderTreeNodeFlags(self.label.as_ptr(), self.flags.bits() as i32)
|
||||
sys::igCollapsingHeaderTreeNodeFlags(
|
||||
ui.scratch_txt(self.label),
|
||||
self.flags.bits() as i32,
|
||||
)
|
||||
}
|
||||
}
|
||||
/// Builds the collapsing header, and adds an additional close button that changes the value of
|
||||
@ -424,10 +442,10 @@ impl<'a> CollapsingHeader<'a> {
|
||||
/// Returns true if the collapsing header is open and content should be rendered.
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub fn build_with_close_button(self, _: &Ui, opened: &mut bool) -> bool {
|
||||
pub fn build_with_close_button(self, ui: &Ui, opened: &mut bool) -> bool {
|
||||
unsafe {
|
||||
sys::igCollapsingHeaderBoolPtr(
|
||||
self.label.as_ptr(),
|
||||
ui.scratch_txt(self.label),
|
||||
opened as *mut bool,
|
||||
self.flags.bits() as i32,
|
||||
)
|
||||
@ -438,7 +456,7 @@ impl<'a> CollapsingHeader<'a> {
|
||||
impl Ui<'_> {
|
||||
/// Constructs a new collapsing header
|
||||
#[doc(alias = "CollapsingHeader")]
|
||||
pub fn collapsing_header(&self, label: &ImStr, flags: TreeNodeFlags) -> bool {
|
||||
pub fn collapsing_header(&self, label: impl AsRef<str>, flags: TreeNodeFlags) -> bool {
|
||||
CollapsingHeader::new(label).flags(flags).build(self)
|
||||
}
|
||||
|
||||
@ -446,7 +464,7 @@ impl Ui<'_> {
|
||||
#[doc(alias = "CollapsingHeader")]
|
||||
pub fn collapsing_header_with_close_button(
|
||||
&self,
|
||||
label: &ImStr,
|
||||
label: impl AsRef<str>,
|
||||
flags: TreeNodeFlags,
|
||||
opened: &mut bool,
|
||||
) -> bool {
|
||||
|
||||
@ -2,7 +2,6 @@ use bitflags::bitflags;
|
||||
use std::f32;
|
||||
use std::ptr;
|
||||
|
||||
use crate::string::ImStr;
|
||||
use crate::sys;
|
||||
use crate::{Condition, Ui};
|
||||
|
||||
@ -161,8 +160,8 @@ impl<'ui> Ui<'ui> {
|
||||
/// Builder for a window
|
||||
#[derive(Debug)]
|
||||
#[must_use]
|
||||
pub struct Window<'a> {
|
||||
name: &'a ImStr,
|
||||
pub struct Window<'a, T> {
|
||||
name: T,
|
||||
opened: Option<&'a mut bool>,
|
||||
flags: WindowFlags,
|
||||
pos: [f32; 2],
|
||||
@ -178,9 +177,9 @@ pub struct Window<'a> {
|
||||
bg_alpha: f32,
|
||||
}
|
||||
|
||||
impl<'a> Window<'a> {
|
||||
impl<'a, T: AsRef<str>> Window<'a, T> {
|
||||
/// Creates a new window builder with the given name
|
||||
pub fn new(name: &ImStr) -> Window {
|
||||
pub fn new(name: T) -> Self {
|
||||
Window {
|
||||
name,
|
||||
opened: None,
|
||||
@ -526,7 +525,7 @@ impl<'a> Window<'a> {
|
||||
}
|
||||
let should_render = unsafe {
|
||||
sys::igBegin(
|
||||
self.name.as_ptr(),
|
||||
ui.scratch_txt(self.name),
|
||||
self.opened
|
||||
.map(|x| x as *mut bool)
|
||||
.unwrap_or(ptr::null_mut()),
|
||||
@ -545,7 +544,7 @@ impl<'a> Window<'a> {
|
||||
///
|
||||
/// Note: the closure is not called if no window content is visible (e.g. window is collapsed
|
||||
/// or fully clipped).
|
||||
pub fn build<T, F: FnOnce() -> T>(self, ui: &Ui<'_>, f: F) -> Option<T> {
|
||||
pub fn build<R, F: FnOnce() -> R>(self, ui: &Ui<'_>, f: F) -> Option<R> {
|
||||
self.begin(ui).map(|_window| f())
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user