mirror of
https://github.com/eliasstepanik/imgui-rs.git
synced 2026-01-09 20:48:36 +00:00
simplified repository down
This commit is contained in:
parent
4a8bf5cd9b
commit
0caa9bd5fb
@ -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"
|
||||
@ -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" }
|
||||
@ -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,
|
||||
}
|
||||
@ -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 = "";
|
||||
}
|
||||
}
|
||||
@ -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");
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -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);
|
||||
},
|
||||
);
|
||||
}
|
||||
@ -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
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -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();
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
mod support;
|
||||
|
||||
fn main() {
|
||||
support::simple_init(file!(), move |_, _ui| {
|
||||
// nothing! don't actually do any imgui funtimes
|
||||
});
|
||||
}
|
||||
@ -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]
|
||||
));
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -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));
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -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]);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -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!");
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
@ -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);
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -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 = "";
|
||||
}
|
||||
}
|
||||
@ -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) {}
|
||||
}
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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!(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -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
@ -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",
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -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"
|
||||
@ -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');
|
||||
|
||||
@ -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) };
|
||||
|
||||
@ -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
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -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 }
|
||||
}
|
||||
}
|
||||
|
||||
@ -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),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user