simplified repository down

This commit is contained in:
Jonathan Spira 2024-09-27 02:02:00 -04:00
parent 4a8bf5cd9b
commit 0caa9bd5fb
32 changed files with 26 additions and 3318 deletions

View File

@ -1,10 +1,5 @@
[workspace]
members = [
"imgui",
"imgui-sys",
"imgui-examples",
"xtask",
]
members = ["imgui", "imgui-sys", "xtask"]
package.rust-version = "1.70"
resolver = "2"
resolver = "2"

View File

@ -1,20 +0,0 @@
[package]
name = "imgui-examples"
version = "0.1.0"
edition = "2021"
description = "imgui crate examples using Glium backend"
homepage = "https://github.com/imgui-rs/imgui-rs"
repository = "https://github.com/imgui-rs/imgui-rs"
license = "MIT OR Apache-2.0"
publish = false
[dev-dependencies]
copypasta = "0.8"
glium = { version = "0.34.0", default-features = true }
image = "0.23"
imgui = { path = "../imgui", features = ["tables-api"] }
[dependencies]
imgui-glium-renderer = "0.12.0"
# imgui-glium-renderer = { path = "../imgui-glium-renderer" }
# imgui-winit-support = { path = "../imgui-winit-support" }

View File

@ -1,70 +0,0 @@
use imgui::*;
mod support;
fn main() {
let mut state = State {
render_closable: true,
};
support::simple_init(file!(), move |run, ui| {
let w = ui
.window("Collapsing header")
.opened(run)
.position([20.0, 20.0], Condition::Appearing)
.size([700.0, 500.0], Condition::Appearing);
w.build(|| {
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("I'm open by default")
.default_open(true)
.build(ui)
{
ui.text("You can still close me with a click!");
}
ui.spacing();
if CollapsingHeader::new("I only open with double-click")
.open_on_double_click(true)
.build(ui)
{
ui.text("Double the clicks, double the fun!");
}
ui.spacing();
if CollapsingHeader::new("I don't have an arrow")
.bullet(true)
.build(ui)
{
ui.text("Collapsing headers can use a bullet instead of an arrow");
}
ui.spacing();
if CollapsingHeader::new("I only open if you click the arrow")
.open_on_arrow(true)
.build(ui)
{
ui.text("You clicked the arrow");
}
ui.spacing();
ui.checkbox(
"Toggle rendering of the next example",
&mut state.render_closable,
);
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");
}
});
});
}
struct State {
render_closable: bool,
}

View File

@ -1,156 +0,0 @@
use imgui::*;
mod support;
fn main() {
let mut state = State::default();
support::simple_init(file!(), move |run, ui| {
example_selector(run, ui, &mut state);
match state.example {
1 => example_1(ui, &mut state),
2 => example_2(ui),
3 => example_3(ui),
_ => (),
}
});
}
fn example_selector(run: &mut bool, ui: &mut Ui, state: &mut State) {
let w = ui
.window("Color button examples")
.opened(run)
.position([20.0, 20.0], Condition::Appearing)
.size([700.0, 100.0], Condition::Appearing)
.resizable(false);
w.build(|| {
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();
}
});
}
fn example_1(ui: &Ui, state: &mut State) {
let w = ui
.window("Example 1: Basics")
.size([700.0, 300.0], Condition::Appearing)
.position([20.0, 140.0], Condition::Appearing);
w.build(|| {
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!",
);
ui.text(state.notify_text);
ui.text("This button is black:");
if ui.color_button("Black color", [0.0, 0.0, 0.0, 1.0]) {
state.notify_text = "*** Black button was clicked";
}
ui.text("This button is red:");
if ui.color_button("Red color", [1.0, 0.0, 0.0, 1.0]) {
state.notify_text = "*** Red button was clicked";
}
ui.text("This button is BIG because it has a custom size:");
if ui
.color_button_config("Green color", [0.0, 1.0, 0.0, 1.0])
.size([100.0, 50.0])
.build()
{
state.notify_text = "*** BIG button was clicked";
}
ui.text("This button doesn't use the tooltip at all:");
if ui
.color_button_config("No tooltip", [0.0, 0.0, 1.0, 1.0])
.tooltip(false)
.build()
{
state.notify_text = "*** No tooltip button was clicked";
}
});
}
fn example_2(ui: &Ui) {
let w = ui
.window("Example 2: Alpha component")
.size([700.0, 320.0], Condition::Appearing)
.position([20.0, 140.0], Condition::Appearing);
w.build(|| {
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",
);
ui.text("This button ignores the alpha component:");
ui.color_button_config("Red color", [1.0, 0.0, 0.0, 0.5])
.alpha(false)
.build();
ui.spacing();
ui.spacing();
ui.spacing();
ui.text_wrapped(
"If you *do* care about the alpha component, you can choose how it's \
displayed in the button and the tooltip",
);
ui.separator();
ui.text_wrapped("ColorPreview::Opaque (default) doesn't show the alpha component at all");
ui.color_button_config("Red + ColorPreview::Opaque", [1.0, 0.0, 0.0, 0.5])
.preview(ColorPreview::Opaque)
.build();
ui.separator();
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",
);
ui.color_button_config("Red + ColorPreview::HalfAlpha", [1.0, 0.0, 0.0, 0.5])
.preview(ColorPreview::HalfAlpha)
.build();
ui.separator();
ui.text_wrapped(
"ColorPreview::Alpha uses a checkerboard pattern in the entire color area to \
illustrate the alpha component",
);
ui.color_button_config("Red + ColorPreview::Alpha", [1.0, 0.0, 0.0, 0.5])
.preview(ColorPreview::Alpha)
.build();
});
}
fn example_3(ui: &Ui) {
let w = ui
.window("Example 3: Input format")
.size([700.0, 320.0], Condition::Appearing)
.position([20.0, 140.0], Condition::Appearing);
w.build(|| {
ui.text("This button interprets the input value [1.0, 0.0, 0.0, 1.0] as RGB(A) (default):");
ui.color_button("RGBA red", [1.0, 0.0, 0.0, 1.0]);
ui.separator();
ui.text("This button interprets the input value [1.0, 0.0, 0.0, 1.0] as HSV(A):");
ui.color_button_config("HSVA black", [1.0, 0.0, 0.0, 1.0])
.input_mode(ColorEditInputMode::HSV)
.build();
});
}
#[derive(Default)]
struct State {
example: u32,
notify_text: &'static str,
}
impl State {
fn reset(&mut self) {
self.notify_text = "";
}
}

View File

@ -1,65 +0,0 @@
mod support;
fn main() {
support::simple_init(file!(), move |_, ui| {
// If we don't explicitly create a window before creating some kind of widget, then Dear Imgui will automatically create one
ui.text("This text will appear in a default window titled 'Debug'");
// However, in almost all cases it's best to make a window, so it has a useful title etc
// imgui-rs has two main methods of creating windows (and these same approaches
// apply to many other widgets). First, callback based:
ui.window("My window via callback").build(|| {
ui.text("This content appears in a window");
// Everything in this callback appears in the window, like this button:
ui.button("This button");
});
// Often the callback approach is most convenient, however occasionally the callbacks can be hard to use.
// In this case, there is the "token based" approach. You call a method and get a "window token",
// everything that happens until the token is dropped is included in the window this is more-or-less how
// the Dear ImGui C++ API works)
// Here we (maybe) get a window token:
let window_token = ui.window("Token based window").begin();
if let Some(_t) = window_token {
// If the token is Some(...) then the window contents are visible, so we need to draw them!
ui.text("Window contents!")
}
// Here we create a window with a specific size, and force it to always have a vertical scrollbar visible
ui.window("Big complex window")
.size([200.0, 100.0], imgui::Condition::FirstUseEver)
.always_vertical_scrollbar(true)
.build(|| {
ui.text("Imagine something complicated here..");
// Note you can create windows inside other windows, however, they both appear as separate windows.
// For example, somewhere deep inside a complex window, we can quickly create a widget to display a
// variable, like a graphical "debug print"
ui.window("Confusion")
.build(|| ui.text(format!("Some variable: {:?}", ui.io().mouse_pos)))
});
// If you want to nest windows inside other windows, you can a "child window".
// This is essentially a scrollable area, with all the same properties as a regular window
ui.window("Parent window").build(|| {
ui.child_window("Child window")
.size([100.0, 100.0])
.build(|| {
for _ in 0..10 {
ui.text("Lines and");
}
});
ui.child_window("Second child window")
.size([100.0, 100.0])
.build(|| {
for _ in 0..10 {
ui.text("More and");
}
});
});
});
}

View File

@ -1,233 +0,0 @@
use std::borrow::Cow;
use std::error::Error;
use std::io::Cursor;
use std::rc::Rc;
use glium::{
backend::Facade,
texture::{ClientFormat, RawImage2d},
uniforms::{MagnifySamplerFilter, MinifySamplerFilter, SamplerBehavior},
Texture2d,
};
use image::{jpeg::JpegDecoder, ImageDecoder};
use imgui::*;
use imgui_glium_renderer::Texture;
mod support;
#[derive(Default)]
struct CustomTexturesApp {
my_texture_id: Option<TextureId>,
lenna: Option<Lenna>,
}
struct Lenna {
texture_id: TextureId,
size: [f32; 2],
}
impl CustomTexturesApp {
fn register_textures<F>(
&mut self,
gl_ctx: &F,
textures: &mut Textures<Texture>,
) -> Result<(), Box<dyn Error>>
where
F: Facade,
{
const WIDTH: usize = 100;
const HEIGHT: usize = 100;
if self.my_texture_id.is_none() {
// Generate dummy texture
let mut data = Vec::with_capacity(WIDTH * HEIGHT);
for i in 0..WIDTH {
for j in 0..HEIGHT {
// Insert RGB values
data.push(i as u8);
data.push(j as u8);
data.push((i + j) as u8);
}
}
let raw = RawImage2d {
data: Cow::Owned(data),
width: WIDTH as u32,
height: HEIGHT as u32,
format: ClientFormat::U8U8U8,
};
let gl_texture = Texture2d::new(gl_ctx, raw)?;
let texture = Texture {
texture: Rc::new(gl_texture),
sampler: SamplerBehavior {
magnify_filter: MagnifySamplerFilter::Linear,
minify_filter: MinifySamplerFilter::Linear,
..Default::default()
},
};
let texture_id = textures.insert(texture);
self.my_texture_id = Some(texture_id);
}
if self.lenna.is_none() {
self.lenna = Some(Lenna::new(gl_ctx, textures)?);
}
Ok(())
}
fn show_textures(&self, ui: &Ui) {
ui.window("Hello textures")
.size([400.0, 400.0], Condition::FirstUseEver)
.build(|| {
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);
}
if let Some(lenna) = &self.lenna {
ui.text("Say hello to Lenna.jpg");
lenna.show(ui);
}
// Example of using custom textures on a button
if let Some(lenna) = &self.lenna {
ui.text("The Lenna buttons");
{
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];
let tint_red = [1.0, 0.5, 0.5, 1.0];
let tint = match (
ui.is_item_hovered(),
ui.is_mouse_down(imgui::MouseButton::Left),
) {
(false, false) => tint_none,
(false, true) => tint_none,
(true, false) => tint_green,
(true, true) => tint_red,
};
let draw_list = ui.get_window_draw_list();
draw_list
.add_image(lenna.texture_id, ui.item_rect_min(), ui.item_rect_max())
.col(tint)
.build();
}
{
ui.same_line();
// Button using quad positioned image
ui.invisible_button("Exciting Button", [100.0, 100.0]);
// Button bounds
let min = ui.item_rect_min();
let max = ui.item_rect_max();
// get corner coordinates
let tl = [
min[0],
min[1] + (ui.frame_count() as f32 / 10.0).cos() * 10.0,
];
let tr = [
max[0],
min[1] + (ui.frame_count() as f32 / 10.0).sin() * 10.0,
];
let bl = [min[0], max[1]];
let br = max;
let draw_list = ui.get_window_draw_list();
draw_list
.add_image_quad(lenna.texture_id, tl, tr, br, bl)
.build();
}
// Rounded image
{
ui.same_line();
ui.invisible_button("Smooth Button", [100.0, 100.0]);
let draw_list = ui.get_window_draw_list();
draw_list
.add_image_rounded(
lenna.texture_id,
ui.item_rect_min(),
ui.item_rect_max(),
16.0,
)
// Tint brighter for visiblity of corners
.col([2.0, 0.5, 0.5, 1.0])
// Rounding on each corner can be changed separately
.round_top_left(ui.frame_count() / 60 % 4 == 0)
.round_top_right((ui.frame_count() + 1) / 60 % 4 == 1)
.round_bot_right((ui.frame_count() + 3) / 60 % 4 == 2)
.round_bot_left((ui.frame_count() + 2) / 60 % 4 == 3)
.build();
}
}
});
}
}
impl Lenna {
fn new<F>(gl_ctx: &F, textures: &mut Textures<Texture>) -> Result<Self, Box<dyn Error>>
where
F: Facade,
{
let lenna_bytes = include_bytes!("../../resources/Lenna.jpg");
let byte_stream = Cursor::new(lenna_bytes.as_ref());
let decoder = JpegDecoder::new(byte_stream)?;
let (width, height) = decoder.dimensions();
let mut image = vec![0; decoder.total_bytes() as usize];
decoder.read_image(&mut image)?;
let raw = RawImage2d {
data: Cow::Owned(image),
width,
height,
format: ClientFormat::U8U8U8,
};
let gl_texture = Texture2d::new(gl_ctx, raw)?;
let texture = Texture {
texture: Rc::new(gl_texture),
sampler: SamplerBehavior {
magnify_filter: MagnifySamplerFilter::Linear,
minify_filter: MinifySamplerFilter::Linear,
..Default::default()
},
};
let texture_id = textures.insert(texture);
Ok(Lenna {
texture_id,
size: [width as f32, height as f32],
})
}
fn show(&self, ui: &Ui) {
Image::new(self.texture_id, self.size).build(ui);
}
}
fn main() {
let my_app = std::rc::Rc::new(std::cell::RefCell::new(CustomTexturesApp::default()));
let app_clone = my_app.clone();
support::init_with_startup(
file!(),
move |_ctx, renderer, display| {
app_clone
.borrow_mut()
.register_textures(display.get_context(), renderer.textures())
.expect("Failed to register textures");
},
move |_, ui| {
my_app.borrow_mut().show_textures(ui);
},
);
}

View File

@ -1,58 +0,0 @@
//! Demonstrates disabling widgets. Prevents mouse interaction and greys out widgets
use imgui::*;
mod support;
fn main() {
let mut edit_mode = true;
let mut safe_mode = true;
let mut click_count = 0;
support::simple_init(file!(), move |_, ui| {
ui.window("Disabling widgets")
.size([300.0, 200.0], Condition::FirstUseEver)
.build(|| {
ui.checkbox("Edit mode", &mut edit_mode);
ui.checkbox("Safe mode", &mut safe_mode);
ui.separator();
// Disable entire rest of widget unless in edit mode
let _d = ui.begin_enabled(edit_mode);
if ui.button("Button 1") {
click_count += 1;
}
if ui.button("Button 2") {
click_count += 1;
}
// Disable dangerous buttons when in safe mode
ui.disabled(safe_mode, || {
let _red = ui.push_style_color(StyleColor::Button, [1.0, 0.0, 0.0, 1.0]);
if ui.button("Dangerous button!") {
click_count -= 1;
}
});
// Can also create a token in a specific scope
{
let _danger_token = ui.begin_disabled(safe_mode);
if ui.button("Button 3") {
click_count += 1;
}
// _danger_token implicitly dropped here
}
// Or manually drop the token
let danger_token2 = ui.begin_disabled(safe_mode);
if ui.button("Button 4") {
click_count += 1;
}
danger_token2.end();
// Note the `_d` token is dropped here automatically
});
});
}

View File

@ -1,136 +0,0 @@
use imgui::*;
mod support;
// rect is [x, y, w, h]
fn draw_text_centered(
ui: &Ui,
draw_list: &DrawListMut,
rect: [f32; 4],
text: &str,
color: [f32; 3],
) {
let text_size = ui.calc_text_size(text);
let cx = (rect[2] - text_size[0]) / 2.0;
let cy = (rect[3] - text_size[1]) / 2.0;
draw_list.add_text([rect[0] + cx, rect[1] + cy], color, text);
}
fn main() {
support::simple_init(file!(), move |_, ui| {
// Get access to draw FG and BG draw lists.
let bg_draw_list = ui.get_background_draw_list();
let fg_draw_list = ui.get_foreground_draw_list();
// Note we cannot access two instances of the same draw list
// at once. That is to say, the following line would panic if
// uncommented:
//let bg_draw_list_2 = ui.get_background_draw_list(); // panic!
{
bg_draw_list
.add_circle([150.0, 150.0], 150.0, [1.0, 0.0, 0.0])
.thickness(4.0)
.build();
draw_text_centered(
ui,
&bg_draw_list,
[0.0, 0.0, 300.0, 300.0],
"background draw list",
[0.0, 0.0, 0.0],
);
}
{
let [w, h] = ui.io().display_size;
fg_draw_list
.add_circle([w - 150.0, h - 150.0], 150.0, [1.0, 0.0, 0.0])
.thickness(4.0)
.build();
draw_text_centered(
ui,
&fg_draw_list,
[w - 300.0, h - 300.0, 300.0, 300.0],
"foreground draw list",
[1.0, 0.0, 0.0],
);
}
ui.window("Draw list")
.size([300.0, 110.0], Condition::FirstUseEver)
.scroll_bar(false)
.build(|| {
ui.button("random button");
let draw_list = ui.get_window_draw_list();
let o = ui.cursor_screen_pos();
let ws = ui.content_region_avail();
draw_list
.add_circle([o[0] + 10.0, o[1] + 10.0], 5.0, [1.0, 0.0, 0.0])
.thickness(4.0)
.build();
draw_list
.add_circle([o[0] + ws[0] - 10.0, o[1] + 10.0], 5.0, [0.0, 1.0, 0.0])
.thickness(4.0)
.build();
draw_list
.add_circle(
[o[0] + ws[0] - 10.0, o[1] + ws[1] - 10.0],
5.0,
[0.0, 0.0, 1.0],
)
.thickness(4.0)
.build();
draw_list
.add_circle([o[0] + 10.0, o[1] + ws[1] - 10.0], 5.0, [1.0, 1.0, 0.0])
.thickness(4.0)
.build();
draw_text_centered(
ui,
&draw_list,
[o[0], o[1], ws[0], ws[1]],
"window draw list",
[1.0, 1.0, 1.0],
);
});
ui.window("Polygons")
.size([300.0, 150.0], Condition::FirstUseEver)
.position([400.0, 110.0], Condition::FirstUseEver)
.scroll_bar(false)
.build(|| {
let draw_list = ui.get_window_draw_list();
// Origin
let o = ui.cursor_screen_pos();
draw_list
.add_polyline(
vec![
[o[0] + 0.0, o[1] + 0.0],
[o[0] + 100.0, o[1] + 25.0],
[o[0] + 50.0, o[1] + 50.0],
[o[0] + 100.0, o[1] + 75.0],
[o[0] + 0.0, o[1] + 100.0],
[o[0] + 0.0, o[1] + 0.0],
],
[1.0, 0.0, 1.0],
)
.build();
draw_list
.add_polyline(
vec![
[o[0] + 120.0 + 0.0, o[1] + 0.0],
[o[0] + 120.0 + 100.0, o[1] + 25.0],
[o[0] + 120.0 + 50.0, o[1] + 50.0],
[o[0] + 120.0 + 100.0, o[1] + 75.0],
[o[0] + 120.0 + 0.0, o[1] + 100.0],
[o[0] + 120.0 + 0.0, o[1] + 0.0],
],
[0.0, 1.0, 1.0],
)
.filled(true)
.build();
});
});
}

View File

@ -1,7 +0,0 @@
mod support;
fn main() {
support::simple_init(file!(), move |_, _ui| {
// nothing! don't actually do any imgui funtimes
});
}

View File

@ -1,28 +0,0 @@
use imgui::*;
mod support;
fn main() {
let mut value = 0;
let choices = ["test test this is 1", "test test this is 2"];
support::simple_init(file!(), move |_, ui| {
ui.window("Hello world")
.size([300.0, 110.0], Condition::FirstUseEver)
.build(|| {
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!(
"Mouse Position: ({:.1},{:.1})",
mouse_pos[0], mouse_pos[1]
));
});
});
}

View File

@ -1,44 +0,0 @@
mod support;
fn main() {
support::simple_init(file!(), move |_, ui| {
let items = vec!["a", "b", "c", "d"];
ui.window("Broken Example")
.position([0.0, 0.0], imgui::Condition::FirstUseEver)
.size([390.0, 200.0], imgui::Condition::FirstUseEver)
.build(|| {
ui.text("Broken! Only first button responds to clicks");
// Because all our buttons have the same label (and thus ID),
// only the first button responds to clicks!
for it in &items {
ui.text(it);
for num in 0..5 {
ui.same_line();
if ui.button("Example") {
println!("{}: {}", it, num);
}
}
}
});
ui.window("Good Example")
.position([400.0, 0.0], imgui::Condition::FirstUseEver)
.size([390.0, 200.0], imgui::Condition::FirstUseEver)
.build(|| {
ui.text("Works!");
for it in &items {
let _label_id = ui.push_id(it);
ui.text(it);
for num in 0..5 {
let _num_id = ui.push_id_usize(num);
ui.same_line();
if ui.button("Example") {
println!("{}: {}", it, num);
}
}
}
});
});
}

View File

@ -1,143 +0,0 @@
use imgui::*;
mod support;
fn main() {
let mut press_counter = 0u32;
let mut press_no_repeat_counter = 0u32;
let mut release_counter = 0u32;
let mut ctrl_a_counter = 0u32;
let mut uncaptured_counter = 0u32;
let mut home_counter = 0u32;
let mut f1_release_count = 0u32;
let mut text_buffer = String::new();
support::simple_init(file!(), move |_, ui| {
ui.window("Means of accessing key state")
.size([500.0, 300.0], Condition::FirstUseEver)
.build(|| {
// You can check if a key is currently held down
if ui.is_key_down(Key::A) {
ui.text("The A key is down!");
} else {
ui.text("The A key is not down");
}
// You can also check if the key has been pressed
// down, which is true for one frame. This has "key
// repeat" so will be true again based on the repeat
// delay and rate.
if ui.is_key_pressed(Key::A) {
press_counter += 1;
}
ui.text(format!(
"The A key has been pressed {} times",
press_counter
));
// You can also check if the key has been pressed
// down, which is true for one frame. This has "key
// repeat" so will be true again based on the repeat
// delay and rate.
if ui.is_key_pressed_no_repeat(Key::A) {
press_no_repeat_counter += 1;
}
ui.text(format!(
"The A key has been pressed {} times (ignoring key repeat)",
press_no_repeat_counter
));
// Note due to the key-repeat behaviour that the key
// may be pressed more often than it is released.
if ui.is_key_released(Key::A) {
release_counter += 1;
}
ui.text(format!(
"The A key has been released {} times",
release_counter
));
// Modifiers are accessed via bools on the `imgui::Io`
// struct,
if ui.io().key_ctrl {
ui.text("Ctrl is down!");
} else {
ui.text("Ctrl is up!");
}
// Using modifiers in conjunction with key press
// events is simple:
if ui.io().key_ctrl && ui.is_key_released(Key::A) {
ctrl_a_counter += 1;
}
ui.text(format!(
"The Ctrl+A key has been released {} times",
ctrl_a_counter
));
// Note that `is_key_released` gives the state of the
// key regardless of what widget has focus, for
// example, if you try to type into this input, the
// above interaction still counts the key presses.
ui.input_text_multiline(
"##Dummy text input widget",
&mut text_buffer,
[100.0, 100.0],
)
// .do_not_resize() if you pass this, then this won't resize!
// .hint("Example text input")
.build();
// If you want to check if a widget is capturing
// keyboard input, you can check
// `Io::want_capture_keyboard`
if !ui.io().want_capture_keyboard && ui.is_key_pressed(Key::A) {
uncaptured_counter += 1;
}
ui.text(format!(
"There has been {} uncaptured A key presses",
uncaptured_counter
));
// These examples all use `Key::A`. The `imgui::Key`
// enum only contains the few keys which imgui
// internally uses (such as for the `Ctrl + A` select
// all shortcut). This may expand in future versions
// of imgui, but will likely never contain every
// possible key
//
// Instead we can use keys using an index, the meaning
// of which is defined by the implementation of "IO"
// backend.
//
// For example, in the `WinitPlatform` backend each
// key is indexed by it's integer value of
// `winit::VirtualKeyCode`. So we can query if a key
// is down based on it's virtual key code,
if ui.is_key_down(Key::Home) {
home_counter += 1;
}
ui.text(format!("Home has been pressed for {} frames", home_counter));
// It is important to remember that unlike using
// `imgui::Key`, there is nothing enforcing the index
// is the key you expect. For example if you hardcode
// your key ID's and switch backends, you may be
// querying different keys!
if ui.io().keys_down[123] {
// A mystery key is down!
}
// It is also possible to use the `is_key_...` methods
// with arbitrary key indexes. For example, to check
// if the F1 key is been pressed
if ui.is_key_released(Key::F1) {
// Index is hardcoded for imgui-examples only, instead do this:
//if ui.is_key_index_released(winit::event::VirtualKeyCode::F1 as i32) {
f1_release_count += 1;
}
ui.text(format!("F1 has been released {} times", f1_release_count));
});
});
}

View File

@ -1,45 +0,0 @@
/// Demonstrates using the "list clipper" to efficiently display long
/// lists in a scrolling area.
///
/// You specify the height per item, and the `ListClipper` API will
/// provide which item index are visible. This avoids having to create
/// thousands of items only for them to not be made visible.
///
/// Note this requires a fixed (or easily computable) height per item.
use imgui::*;
mod support;
fn main() {
let lots_of_words: Vec<String> = (0..10000).map(|x| format!("Line {}", x)).collect();
support::simple_init(file!(), move |_, ui| {
// Show the C++ style API
ui.window("Hello long world")
.size([100.0, 500.0], Condition::FirstUseEver)
.position([10.0, 10.0], crate::Condition::Always)
.build(|| {
let mut clipper = imgui::ListClipper::new(lots_of_words.len() as i32)
.items_height(ui.current_font_size())
.begin(ui);
while clipper.step() {
for row_num in clipper.display_start()..clipper.display_end() {
ui.text(&lots_of_words[row_num as usize]);
}
}
});
// Show the more Rust'y iterator
ui.window("Hello long world (iterator API)")
.size([100.0, 500.0], Condition::FirstUseEver)
.position([150.0, 10.0], crate::Condition::Always)
.build(|| {
let clipper = imgui::ListClipper::new(lots_of_words.len() as i32)
.items_height(ui.current_font_size())
.begin(ui);
for row_num in clipper.iter() {
ui.text(&lots_of_words[row_num as usize]);
}
});
});
}

View File

@ -1,50 +0,0 @@
use imgui::*;
mod support;
fn main() {
support::simple_init(file!(), move |_, ui| {
ui.show_demo_window(&mut true);
ui.window("Table with list clipper")
.size([800.0, 700.0], Condition::FirstUseEver)
.build(|| {
let num_cols = 3;
let num_rows = 1000;
let flags = imgui::TableFlags::ROW_BG
| imgui::TableFlags::RESIZABLE
| imgui::TableFlags::BORDERS_H
| imgui::TableFlags::BORDERS_V; //| imgui::TableFlags::SCROLL_Y;
if let Some(_t) = ui.begin_table_with_sizing(
"longtable",
num_cols,
flags,
[300.0, 100.0],
/*inner width=*/ 0.0,
) {
ui.table_setup_column("A");
ui.table_setup_column("B");
ui.table_setup_column("C");
// Freeze first row so headers are visible even
// when scrolling
ui.table_setup_scroll_freeze(num_cols, 1);
// Done with headers row
ui.table_headers_row();
// Create clipper with st
let clip = imgui::ListClipper::new(num_rows).begin(ui);
for row_num in clip.iter() {
ui.table_next_row();
for col_num in 0..num_cols {
ui.table_set_column_index(col_num);
ui.text(format!("Hello {},{}", col_num, row_num));
}
}
}
});
});
}

View File

@ -1,58 +0,0 @@
use imgui::*;
mod support;
/// NOTE on this example:
/// Most of this complexity is because of how we initialize our support
/// (which primarily is a result of `winit`'s requirements for making a window).
/// In reality, most of these two functions can be made directly after each other --
/// to run the initialization code (which loads a font), all you need is imgui + a renderer.
fn main() {
let dokdo = std::rc::Rc::new(std::cell::RefCell::new(None));
let roboto = std::rc::Rc::new(std::cell::RefCell::new(None));
let dokdo_init = dokdo.clone();
let roboto_init = roboto.clone();
support::init_with_startup(
file!(),
move |ctx, renderer, _| {
let mut dokdo_handle = dokdo_init.borrow_mut();
let mut roboto_handle = roboto_init.borrow_mut();
// this function runs right after the window is created.
// In your own code, this can be done whenever you have a renderer
// and a context.
*dokdo_handle = Some(ctx.fonts().add_font(&[FontSource::TtfData {
data: include_bytes!("../../resources/Dokdo-Regular.ttf"),
size_pixels: support::FONT_SIZE,
config: None,
}]));
*roboto_handle = Some(ctx.fonts().add_font(&[FontSource::TtfData {
data: include_bytes!("../../resources/Roboto-Regular.ttf"),
size_pixels: support::FONT_SIZE,
config: None,
}]));
renderer
.reload_font_texture(ctx)
.expect("Failed to reload fonts");
},
move |run, ui| {
let dokdo = dokdo.borrow().unwrap();
let roboto = roboto.borrow().unwrap();
ui.window("Hello world").opened(run).build(|| {
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!");
});
},
);
}

View File

@ -1,25 +0,0 @@
use imgui::*;
mod support;
fn main() {
support::simple_init(file!(), move |run, ui| {
let w = ui
.window("Progress bar")
.opened(run)
.position([20.0, 20.0], Condition::Appearing)
.size([700.0, 200.0], Condition::Appearing);
w.build(|| {
ui.text("This is a simple progress bar:");
ProgressBar::new(0.5).build(ui);
ui.separator();
ui.text("This progress bar has a custom size:");
ProgressBar::new(0.3).size([200.0, 50.0]).build(ui);
ui.separator();
ui.text("This progress bar uses overlay text:");
ProgressBar::new(0.8).overlay_text("Lorem ipsum").build(ui);
});
});
}

View File

@ -1,120 +0,0 @@
use imgui::*;
mod support;
fn main() {
let mut state = State::default();
support::simple_init(file!(), move |run, ui| {
example_selector(run, ui, &mut state);
match state.example {
1 => example_1(ui, &mut state),
2 => example_2(ui, &mut state),
_ => (),
}
});
}
fn example_selector(run: &mut bool, ui: &mut Ui, state: &mut State) {
let w = ui
.window("Radio button examples")
.opened(run)
.position([20.0, 20.0], Condition::Appearing)
.size([700.0, 80.0], Condition::Appearing)
.resizable(false);
w.build(|| {
let mut clicked = false;
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();
}
});
}
fn example_1(ui: &Ui, state: &mut State) {
let w = ui
.window("Example 1: Boolean radio buttons")
.size([700.0, 200.0], Condition::Appearing)
.position([20.0, 120.0], Condition::Appearing);
w.build(|| {
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.",
);
ui.text(state.notify_text);
if ui.radio_button_bool("I'm permanently active", true) {
state.notify_text = "*** Permanently active radio button was clicked";
}
if ui.radio_button_bool("I'm permanently inactive", false) {
state.notify_text = "*** Permanently inactive radio button was clicked";
}
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";
}
});
}
fn example_2(ui: &Ui, state: &mut State) {
let w = ui
.window("Example 2: Radio buttons")
.size([700.0, 300.0], Condition::Appearing)
.position([20.0, 120.0], Condition::Appearing);
w.build(|| {
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",
);
ui.text(state.notify_text);
ui.separator();
if ui.radio_button("I'm number 1", &mut state.number, 1) {
state.notify_text = "*** Number 1 was clicked";
}
if ui.radio_button("I'm number 2", &mut state.number, 2) {
state.notify_text = "*** Number 2 was clicked";
}
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("I'm choice A", &mut state.choice, Some(Choice::A)) {
state.notify_text = "*** Choice A was clicked";
}
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("I'm choice C", &mut state.choice, Some(Choice::C)) {
state.notify_text = "*** Choice C was clicked";
}
});
}
#[derive(Default)]
struct State {
example: u32,
notify_text: &'static str,
simple_bool: bool,
number: u8,
// We use Option here because we don't want any initial value.
// Another choice could be to choose one of the Choice enum values to be the default.
choice: Option<Choice>,
}
#[derive(Copy, Clone, PartialEq)]
enum Choice {
A,
B,
C,
}
impl State {
fn reset(&mut self) {
self.notify_text = "";
}
}

View File

@ -1,104 +0,0 @@
use imgui::*;
mod support;
fn main() {
let mut state = State::default();
support::simple_init(file!(), move |run, ui| {
example_selector(run, ui, &mut state);
match state.example {
1 => example_1(ui, &mut state),
2 => example_2(ui, &mut state),
_ => (),
}
});
}
fn example_selector(run: &mut bool, ui: &mut Ui, state: &mut State) {
let w = ui
.window("Slider examples")
.opened(run)
.position([20.0, 20.0], Condition::Appearing)
.size([700.0, 80.0], Condition::Appearing)
.resizable(false);
w.build(|| {
let mut clicked = false;
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();
}
});
}
fn example_1(ui: &Ui, state: &mut State) {
let w = ui
.window("Example 1: Basic sliders")
.size([700.0, 340.0], Condition::Appearing)
.position([20.0, 120.0], Condition::Appearing);
w.build(|| {
ui.text("All of the following data types are supported:");
ui.text("Signed: i8 i16 i32 i64");
ui.text("Unsigned: u8 u16 u32 u64");
ui.text("Floats: f32 f64");
// Full ranges can be specified with Rust's `::MIN/MAX` constants
ui.slider("u8 value", u8::MIN, u8::MAX, &mut state.u8_value);
// However for larger data-types, it's usually best to specify
// a much smaller range. The following slider is hard to use.
ui.slider("Full range f32 value", f32::MIN/2.0, f32::MAX/2.0, &mut state.f32_value);
// Note the `... / 2.0` - anything larger is not supported by
// the upstream C++ library
ui.text("Note that for 32-bit/64-bit types, sliders are always limited to half of the natural type range!");
// Most of the time, it's best to specify the range
ui.separator();
ui.text("Slider range can be limited:");
ui.slider("i32 value with range", -999, 999, &mut state.i32_value);
ui.slider("f32 value", -10.0, 10.0, &mut state.f32_value);
ui.separator();
ui.text("Value formatting can be customized with a C-style printf string:");
ui.slider_config("f64 value with custom formatting", -999_999_999.0, 999_999_999.0).display_format("%09.0f").build(&mut state.f64_formatted);
// The display format changes the increments the slider operates in:
ui.slider_config("f32 with %.01f", 0.0, 1.0).display_format("%.01f").build(&mut state.f32_value);
ui.slider_config("Same f32 with %.05f", 0.0, 1.0).display_format("%.05f").build(&mut state.f32_value);
ui.separator();
ui.text("Vertical sliders require a size parameter but otherwise work in a similar way:");
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 = ui
.window("Example 2: Slider arrays")
.size([700.0, 260.0], Condition::Appearing)
.position([20.0, 120.0], Condition::Appearing);
w.build(|| {
ui.text("You can easily build a slider group from an array of values:");
ui.slider_config("[u8; 4]", 0, u8::MAX)
.build_array(&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];
ui.slider_config("subslice", 0, u8::MAX).build_array(slice);
});
}
#[derive(Default)]
struct State {
example: u32,
i32_value: i32,
u8_value: u8,
f32_value: f32,
f64_formatted: f64,
array: [u8; 4],
}
impl State {
fn reset(&mut self) {}
}

View File

@ -1,18 +0,0 @@
use copypasta::{ClipboardContext, ClipboardProvider};
use imgui::ClipboardBackend;
pub struct ClipboardSupport(pub ClipboardContext);
pub fn init() -> Option<ClipboardSupport> {
ClipboardContext::new().ok().map(ClipboardSupport)
}
impl ClipboardBackend for ClipboardSupport {
fn get(&mut self) -> Option<String> {
self.0.get_contents().ok()
}
fn set(&mut self, text: &str) {
// ignore errors?
let _ = self.0.set_contents(text.to_owned());
}
}

View File

@ -1,164 +0,0 @@
use glium::glutin::surface::WindowSurface;
use glium::{Display, Surface};
use imgui::{Context, FontConfig, FontGlyphRanges, FontSource, Ui};
use imgui_glium_renderer::Renderer;
use imgui_winit_support::winit::dpi::LogicalSize;
use imgui_winit_support::winit::event::{Event, WindowEvent};
use imgui_winit_support::winit::event_loop::EventLoop;
use imgui_winit_support::winit::window::WindowBuilder;
use imgui_winit_support::{HiDpiMode, WinitPlatform};
use std::path::Path;
use std::time::Instant;
mod clipboard;
pub const FONT_SIZE: f32 = 13.0;
#[allow(dead_code)] // annoyingly, RA yells that this is unusued
pub fn simple_init<F: FnMut(&mut bool, &mut Ui) + 'static>(title: &str, run_ui: F) {
init_with_startup(title, |_, _, _| {}, run_ui);
}
pub fn init_with_startup<FInit, FUi>(title: &str, mut startup: FInit, mut run_ui: FUi)
where
FInit: FnMut(&mut Context, &mut Renderer, &Display<WindowSurface>) + 'static,
FUi: FnMut(&mut bool, &mut Ui) + 'static,
{
let mut imgui = create_context();
let title = match Path::new(&title).file_name() {
Some(file_name) => file_name.to_str().unwrap(),
None => title,
};
let event_loop = EventLoop::new().expect("Failed to create EventLoop");
let builder = WindowBuilder::new()
.with_title(title)
.with_inner_size(LogicalSize::new(1024, 768));
let (window, display) = glium::backend::glutin::SimpleWindowBuilder::new()
.set_window_builder(builder)
.build(&event_loop);
let mut renderer = Renderer::init(&mut imgui, &display).expect("Failed to initialize renderer");
if let Some(backend) = clipboard::init() {
imgui.set_clipboard_backend(backend);
} else {
eprintln!("Failed to initialize clipboard");
}
let mut platform = WinitPlatform::init(&mut imgui);
{
let dpi_mode = if let Ok(factor) = std::env::var("IMGUI_EXAMPLE_FORCE_DPI_FACTOR") {
// Allow forcing of HiDPI factor for debugging purposes
match factor.parse::<f64>() {
Ok(f) => HiDpiMode::Locked(f),
Err(e) => panic!("Invalid scaling factor: {}", e),
}
} else {
HiDpiMode::Default
};
platform.attach_window(imgui.io_mut(), &window, dpi_mode);
}
let mut last_frame = Instant::now();
startup(&mut imgui, &mut renderer, &display);
event_loop
.run(move |event, window_target| match event {
Event::NewEvents(_) => {
let now = Instant::now();
imgui.io_mut().update_delta_time(now - last_frame);
last_frame = now;
}
Event::AboutToWait => {
platform
.prepare_frame(imgui.io_mut(), &window)
.expect("Failed to prepare frame");
window.request_redraw();
}
Event::WindowEvent {
event: WindowEvent::RedrawRequested,
..
} => {
let ui = imgui.frame();
let mut run = true;
run_ui(&mut run, ui);
if !run {
window_target.exit();
}
let mut target = display.draw();
target.clear_color_srgb(1.0, 1.0, 1.0, 1.0);
platform.prepare_render(ui, &window);
let draw_data = imgui.render();
renderer
.render(&mut target, draw_data)
.expect("Rendering failed");
target.finish().expect("Failed to swap buffers");
}
Event::WindowEvent {
event: WindowEvent::Resized(new_size),
..
} => {
if new_size.width > 0 && new_size.height > 0 {
display.resize((new_size.width, new_size.height));
}
platform.handle_event(imgui.io_mut(), &window, &event);
}
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => window_target.exit(),
event => {
platform.handle_event(imgui.io_mut(), &window, &event);
}
})
.expect("EventLoop error");
}
/// Creates the imgui context
pub fn create_context() -> imgui::Context {
let mut imgui = Context::create();
// Fixed font size. Note imgui_winit_support uses "logical
// pixels", which are physical pixels scaled by the devices
// scaling factor. Meaning, 13.0 pixels should look the same size
// on two different screens, and thus we do not need to scale this
// value (as the scaling is handled by winit)
imgui.fonts().add_font(&[
FontSource::TtfData {
data: include_bytes!("../../../resources/Roboto-Regular.ttf"),
size_pixels: FONT_SIZE,
config: Some(FontConfig {
// As imgui-glium-renderer isn't gamma-correct with
// it's font rendering, we apply an arbitrary
// multiplier to make the font a bit "heavier". With
// default imgui-glow-renderer this is unnecessary.
rasterizer_multiply: 1.5,
// Oversampling font helps improve text rendering at
// expense of larger font atlas texture.
oversample_h: 4,
oversample_v: 4,
..FontConfig::default()
}),
},
FontSource::TtfData {
data: include_bytes!("../../../resources/mplus-1p-regular.ttf"),
size_pixels: FONT_SIZE,
config: Some(FontConfig {
// Oversampling font helps improve text rendering at
// expense of larger font atlas texture.
oversample_h: 4,
oversample_v: 4,
// Range of glyphs to rasterize
glyph_ranges: FontGlyphRanges::japanese(),
..FontConfig::default()
}),
},
]);
imgui.set_ini_filename(None);
imgui
}

View File

@ -1,210 +0,0 @@
use imgui::*;
mod support;
fn main() {
let mut humans = vec![
HumanData {
name: "Joonas",
favorite_number: 102,
favorite_fruit_maybe: "Dutch",
},
HumanData {
name: "Thom",
favorite_number: 314,
favorite_fruit_maybe: "Rice",
},
HumanData {
name: "Jack",
favorite_number: 611,
favorite_fruit_maybe: "Mangoes",
},
];
let mut t2_flags = TableFlags::REORDERABLE
| TableFlags::HIDEABLE
| TableFlags::RESIZABLE
| TableFlags::NO_BORDERS_IN_BODY;
support::simple_init(file!(), move |_, ui| {
ui.window("Input text callbacks")
.size([800.0, 400.0], Condition::FirstUseEver)
.build(|| {
if let Some(_t) = ui.begin_table("Basic-Table", 3) {
// we must also call `next_row` here, because we declined
// to set up header rows. If we set up header rows ourselves,
// we will call `table_header_rows` instead, and if we use
// `begin_table_header`, then the initial call will be handled for us.
ui.table_next_row();
// note you MUST call `next_column` at least to START
// Let's walk through a table like it's an iterator...
ui.table_set_column_index(0);
ui.text("x: 0, y: 0");
ui.table_next_column();
ui.text("x: 1, y: 0");
ui.table_next_column();
ui.text("x: 2, y: 0");
// // calling next column again will wrap us around to 0-1,
// // since we've exhausted our 3 columns.
ui.table_next_column();
ui.text("x: 0, y: 1");
// // Let's do this manually now -- we can set each column ourselves...
ui.table_set_column_index(1);
ui.text("x: 1, y: 1");
ui.table_set_column_index(2);
ui.text("x: 2, y: 1");
// you CAN go back...
ui.table_set_column_index(1);
// however, you should call `new_line`, since otherwise
// we'd right on top of our `x: 1, y: 1` text.
ui.new_line();
ui.text("our of order txt");
}
ui.separator();
ui.text("Let's add some headers");
if let Some(_t) = ui.begin_table_header(
"table-headers",
[
TableColumnSetup::new("Name"),
TableColumnSetup::new("Age"),
TableColumnSetup::new("Favorite fruit"),
],
) {
// note that we DON'T have to call "table_next_row" here -- that's taken care
// of for us by `begin_table_header`, since it actually calls `table_headers_row`
// but we DO need to call column!
// but that's fine, we'll use a loop
for i in 0..3 {
let names = ["Joonas", "Thom", "Jack"];
let fruit = ["Dutch", "Rice", "Mangoes"];
ui.table_next_column();
ui.text(names[i]);
ui.table_next_column();
ui.text((i * 9).to_string());
ui.table_next_column();
ui.text(fruit[i]);
}
}
ui.separator();
ui.text("Let's do some context menus");
ui.text(
"context menus are created, by default, from the flags passed\
while making the table, or each row.\n\
Notice how toggling these checkboxes changes the context menu.",
);
ui.checkbox_flags("Reorderable", &mut t2_flags, TableFlags::REORDERABLE);
ui.same_line();
ui.checkbox_flags("Hideable", &mut t2_flags, TableFlags::HIDEABLE);
ui.same_line();
ui.checkbox_flags("Resizable", &mut t2_flags, TableFlags::RESIZABLE);
if let Some(_t) = ui.begin_table_header_with_flags(
"table-headers2",
[
TableColumnSetup::new("Name"),
TableColumnSetup::new("Age"),
TableColumnSetup::new("Favorite fruit"),
],
t2_flags,
) {
// note that we DON'T have to call "table_next_row" here -- that's taken care
// of for us by `begin_table_header`, since it actually calls `table_headers_row`
// but we DO need to call column!
// but that's fine, we'll use a loop
for i in 0..3 {
let names = ["Joonas", "Thom", "Jack"];
let fruit = ["Dutch", "Rice", "Mangoes"];
ui.table_next_column();
ui.text(names[i]);
ui.table_next_column();
ui.text((i * 9).to_string());
ui.table_next_column();
ui.text(fruit[i]);
}
}
ui.separator();
ui.text("Here's a table you can sort!");
ui.text("Check the code to see the two methods of doing it.");
if let Some(_t) = ui.begin_table_header_with_flags(
"table-headers3",
[
TableColumnSetup::new("Name"),
TableColumnSetup::new("Favorite Number"),
TableColumnSetup::new("Favorite fruit"),
],
TableFlags::SORTABLE,
) {
if let Some(sort_data) = ui.table_sort_specs_mut() {
sort_data
.conditional_sort(|specs| HumanData::sort_humans(&mut humans, specs));
// Can also sort this other way...
// if sort_data.should_sort() {
// HumanData::sort_humans(&mut humans, sort_data.specs());
// sort_data.set_sorted();
// }
}
for human in humans.iter() {
ui.table_next_column();
ui.text(human.name);
ui.table_next_column();
ui.text(human.favorite_number.to_string());
ui.table_next_column();
ui.text(human.favorite_fruit_maybe);
}
}
});
});
}
struct HumanData {
name: &'static str,
favorite_number: usize,
favorite_fruit_maybe: &'static str,
}
impl HumanData {
pub fn sort_humans(humans: &mut [Self], specs: Specs<'_>) {
let spec = specs.iter().next().unwrap();
if let Some(kind) = spec.sort_direction() {
match kind {
TableSortDirection::Ascending => match spec.column_idx() {
0 => humans.sort_by_key(|h| h.name),
1 => humans.sort_by_key(|h| h.favorite_number),
2 => humans.sort_by_key(|h| h.favorite_fruit_maybe),
_ => unimplemented!(),
},
TableSortDirection::Descending => match spec.column_idx() {
0 => humans.sort_by_key(|h| std::cmp::Reverse(h.name)),
1 => humans.sort_by_key(|h| std::cmp::Reverse(h.favorite_number)),
2 => humans.sort_by_key(|h| std::cmp::Reverse(h.favorite_fruit_maybe)),
_ => unimplemented!(),
},
}
}
}
}

View File

@ -1,37 +0,0 @@
mod support;
const WHITE: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
const RED: [f32; 4] = [1.0, 0.0, 0.0, 1.0];
fn main() {
support::simple_init(file!(), move |_, ui| {
let draw_list = ui.get_window_draw_list();
// Will draw channel 0 first, then channel 1, whatever the order of
// the calls in the code.
//
// Here, we draw a red line on channel 1 then a white circle on
// channel 0. As a result, the red line will always appear on top of
// the white circle.
draw_list.channels_split(2, |channels| {
const RADIUS: f32 = 100.0;
let canvas_pos = ui.cursor_screen_pos();
channels.set_current(1);
draw_list
.add_line(
canvas_pos,
[canvas_pos[0] + RADIUS, canvas_pos[1] + RADIUS],
RED,
)
.thickness(5.0)
.build();
channels.set_current(0);
let center = [canvas_pos[0] + RADIUS, canvas_pos[1] + RADIUS];
draw_list
.add_circle(center, RADIUS, WHITE)
.thickness(10.0)
.num_segments(50)
.build();
});
});
}

View File

@ -1,5 +0,0 @@
mod support;
fn main() {
support::simple_init(file!(), move |run, ui| ui.show_demo_window(run));
}

File diff suppressed because it is too large Load Diff

View File

@ -1,136 +0,0 @@
use imgui::*;
mod support;
fn main() {
let mut buffers = [String::default(), String::default(), String::default()];
support::simple_init(file!(), move |_, ui| {
ui.window("Input text callbacks")
.size([500.0, 300.0], Condition::FirstUseEver)
.build(|| {
ui.text("You can make a variety of buffer callbacks on an Input Text");
ui.text(
"or on an InputTextMultiline. In this example, we'll use \
InputText primarily.",
);
ui.text(
"The only difference is that InputTextMultiline doesn't get \
the `History` callback,",
);
ui.text("since, of course, you need the up/down keys to navigate.");
ui.separator();
ui.text("No callbacks:");
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();
ui.text("Here's a callback which printlns when each is ran.");
struct AllCallback;
impl InputTextCallbackHandler for AllCallback {
fn char_filter(&mut self, c: char) -> Option<char> {
println!("Char filter fired! This means a char was inputted.");
Some(c)
}
fn on_completion(&mut self, _: TextCallbackData) {
println!("Completion request fired! This means the tab key was hit.");
}
fn on_edit(&mut self, _: TextCallbackData) {
println!("Edit was fired! Any edit will cause this to fire.")
}
fn on_history(&mut self, dir: HistoryDirection, _: TextCallbackData) {
println!("History was fired by pressing {:?}", dir);
}
fn on_always(&mut self, _: TextCallbackData) {
// We don't actually print this out because it will flood your log a lot!
// println!("The always callback fired! It always fires.");
}
}
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 String");
ui.text("to duplicate edits to buf0 on buf1");
struct Wrapper<'a>(&'a mut String);
impl<'a> InputTextCallbackHandler for Wrapper<'a> {
fn on_always(&mut self, data: TextCallbackData) {
data.str().clone_into(self.0);
}
}
let (buf0, brwchk_dance) = buffers.split_first_mut().unwrap();
let buf1 = Wrapper(&mut brwchk_dance[0]);
ui.input_text("Edits copied to buf1", buf0)
.callback(InputTextCallback::ALWAYS, buf1)
.build();
ui.separator();
ui.text("Finally, we'll do some whacky history to show inserting and removing");
ui.text("characters from the buffer.");
ui.text(
"Here, pressing UP (while editing the below widget) will remove the\n\
first and last character from buf2",
);
ui.text("and pressing DOWN will prepend the first char from buf0 AND");
ui.text("append the last char from buf1");
let (buf0, brwchk_dance) = buffers.split_first_mut().unwrap();
let (buf1, buf2_dance) = brwchk_dance.split_first_mut().unwrap();
let buf2 = &mut buf2_dance[0];
struct Wrapper2<'a>(&'a str, &'a str);
impl<'a> InputTextCallbackHandler for Wrapper2<'a> {
fn on_history(&mut self, dir: HistoryDirection, mut data: TextCallbackData) {
match dir {
HistoryDirection::Up => {
// remove first char...
if !data.str().is_empty() {
data.remove_chars(0, 1);
if let Some((idx, _)) = data.str().char_indices().next_back() {
data.remove_chars(idx, 1);
}
}
}
HistoryDirection::Down => {
// insert first char...
if let Some(first_char) = self.0.get(0..1) {
data.insert_chars(0, first_char);
}
// insert last char
if let Some((idx, _)) = self.1.char_indices().next_back() {
data.push_str(&self.1[idx..]);
}
}
}
}
}
ui.input_text("Wild buf2 editor", buf2)
.callback(InputTextCallback::HISTORY, Wrapper2(buf0, buf1))
.build();
ui.text(
"For more examples on how to use callbacks non-chaotically, check the demo",
);
});
});
}

View File

@ -1,46 +0,0 @@
use imgui::*;
mod support;
fn main() {
let mut stable_str = String::new();
let mut callback_str = String::new();
support::simple_init(file!(), move |_, ui| {
if let Some(_window) = ui
.window("Input text callbacks")
.size([500.0, 300.0], Condition::FirstUseEver)
.begin()
{
if ui.input_text("input stable", &mut stable_str).build() {
dbg!(&stable_str);
}
let mut per_frame_buf = String::new();
ui.input_text("input per frame", &mut per_frame_buf).build();
if ui.is_item_deactivated_after_edit() {
dbg!(&per_frame_buf);
}
struct CB;
impl imgui::InputTextCallbackHandler for CB {
fn on_history(
&mut self,
_dir: imgui::HistoryDirection,
_data: imgui::TextCallbackData,
) {
}
}
let changed = ui
.input_text("input callback", &mut callback_str)
.callback(InputTextCallback::HISTORY, CB)
.enter_returns_true(true)
.build();
if changed {
println!("{:?}", callback_str);
}
}
});
}

View File

@ -17,7 +17,7 @@ features = ["freetype", "docking", "tables-api"]
[dependencies]
bitflags = "1"
imgui-sys = { version = "0.12.0", path = "../imgui-sys" }
imgui-sys = { path = "../imgui-sys", version = "0.12" }
mint = "0.5.6"
parking_lot = "0.12"
cfg-if = "1"
@ -31,4 +31,4 @@ docking = ["imgui-sys/docking"]
tables-api = []
[dev-dependencies]
memoffset = "0.9"
memoffset = "0.9"

View File

@ -184,12 +184,12 @@ impl<'ui, 'p, L: AsRef<str>> InputText<'ui, 'p, L> {
/// by appending and then removing a null terminator (`\0`) from the String you pass in.
/// This has several consequences:
/// 1. The string's backing buffer may be resized and relocated even without edits as result
/// of this pushed char.
/// of this pushed char.
/// 2. **The string will appear truncated if the string contains `\0` inside it.** This will not
/// cause memory *unsafety*, but it will limit your usage. If that's the case, please pre-process
/// your string.
/// cause memory *unsafety*, but it will limit your usage. If that's the case, please pre-process
/// your string.
/// 3. Truncations by ImGui appear to be done primarily by insertions of `\0` to the truncation point.
/// We will handle this for you and edit the string "properly" too, but this might show up in callbacks.
/// We will handle this for you and edit the string "properly" too, but this might show up in callbacks.
pub fn new(ui: &'ui Ui, label: L, buf: &'p mut String) -> Self {
InputText {
label,
@ -280,12 +280,12 @@ where
/// by appending and then removing a null terminator (`\0`) from the String you pass in.
/// This has several consequences:
/// 1. The string's backing buffer may be resized and relocated even without edits as result
/// of this pushed char.
/// of this pushed char.
/// 2. **The string will appear truncated if the string contains `\0` inside it.** This will not
/// cause memory *unsafety*, but it will limit your usage. If that's the case, please pre-process
/// your string.
/// cause memory *unsafety*, but it will limit your usage. If that's the case, please pre-process
/// your string.
/// 3. Truncations by ImGui appear to be done primarily by insertions of `\0` to the truncation point.
/// We will handle this for you and edit the string "properly" too, but this might show up in callbacks.
/// We will handle this for you and edit the string "properly" too, but this might show up in callbacks.
pub fn build(self) -> bool {
// needs to be null-terminated! this is a hack!
self.buf.push('\0');
@ -367,12 +367,12 @@ impl<'ui, 'p, L: AsRef<str>> InputTextMultiline<'ui, 'p, L, PassthroughCallback>
/// by appending and then removing a null terminator (`\0`) from the String you pass in.
/// This has several consequences:
/// 1. The string's backing buffer may be resized and relocated even without edits as result
/// of this pushed char.
/// of this pushed char.
/// 2. **The string will appear truncated if the string contains `\0` inside it.** This will not
/// cause memory *unsafety*, but it will limit your usage. If that's the case, please pre-process
/// your string.
/// cause memory *unsafety*, but it will limit your usage. If that's the case, please pre-process
/// your string.
/// 3. Truncations by ImGui appear to be done primarily by insertions of `\0` to the truncation point.
/// We will handle this for you and edit the string "properly" too, but this might show up in callbacks.
/// We will handle this for you and edit the string "properly" too, but this might show up in callbacks.
pub fn new(ui: &'ui Ui, label: L, buf: &'p mut String, size: impl Into<MintVec2>) -> Self {
InputTextMultiline {
label,
@ -441,12 +441,12 @@ impl<'ui, 'p, T: InputTextCallbackHandler, L: AsRef<str>> InputTextMultiline<'ui
/// by appending and then removing a null terminator (`\0`) from the String you pass in.
/// This has several consequences:
/// 1. The string's backing buffer may be resized and relocated even without edits as result
/// of this pushed char.
/// of this pushed char.
/// 2. **The string will appear truncated if the string contains `\0` inside it.** This will not
/// cause memory *unsafety*, but it will limit your usage. If that's the case, please pre-process
/// your string.
/// cause memory *unsafety*, but it will limit your usage. If that's the case, please pre-process
/// your string.
/// 3. Truncations by ImGui appear to be done primarily by insertions of `\0` to the truncation point.
/// We will handle this for you and edit the string "properly" too, but this might show up in callbacks.
/// We will handle this for you and edit the string "properly" too, but this might show up in callbacks.
pub fn build(self) -> bool {
// needs to be null-terminated! this is a hack!
self.buf.push('\0');

View File

@ -187,7 +187,7 @@ impl Ui {
/// - `> 0.0`: width is `item_width` pixels
/// - `= 0.0`: default to ~2/3 of window width
/// - `< 0.0`: `item_width` pixels relative to the right of window (-1.0 always aligns width to
/// the right side)
/// the right side)
#[doc(alias = "PushItemWith")]
pub fn push_item_width(&self, item_width: f32) -> ItemWidthStackToken<'_> {
unsafe { sys::igPushItemWidth(item_width) };
@ -198,7 +198,7 @@ impl Ui {
/// - `> 0.0`: width is `item_width` pixels
/// - `= 0.0`: default to ~2/3 of window width
/// - `< 0.0`: `item_width` pixels relative to the right of window (-1.0 always aligns width to
/// the right side)
/// the right side)
#[doc(alias = "SetNextItemWidth")]
pub fn set_next_item_width(&self, item_width: f32) {
unsafe { sys::igSetNextItemWidth(item_width) };

View File

@ -356,13 +356,11 @@ impl Ui {
inner_width: f32,
) -> Option<TableToken<'_>> {
self.begin_table_with_sizing(str_id, N, flags, outer_size, inner_width)
.map(|data| {
.inspect(|_data| {
for value in column_data {
self.table_setup_column_with(value);
}
self.table_headers_row();
data
})
}

View File

@ -36,6 +36,7 @@ macro_rules! create_token {
impl Drop for $token_name<'_> {
fn drop(&mut self) {
#[allow(clippy::macro_metavars_in_unsafe)]
unsafe { $on_drop }
}
}

View File

@ -22,7 +22,7 @@ impl Bindgen {
None => "".to_string(),
Some(x) => format!("-{}", x),
};
let cimgui_output = root.join(&format!(
let cimgui_output = root.join(format!(
"imgui-sys/third-party/imgui-{}{}",
variant, additional
));
@ -41,7 +41,7 @@ impl Bindgen {
generate_binding_file(&header, &output.join(&output_name), &types, &funcs, None)?;
generate_binding_file(
&header,
&output.join(&format!("wasm_{}", &output_name)),
&output.join(format!("wasm_{}", &output_name)),
&types,
&funcs,
Some(&wasm_name),