initial commit
commit
e6595cd7fd
@ -0,0 +1 @@
|
|||||||
|
/target
|
@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
name = "imgui-test"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
glium = { version = "0.30", default-features = true }
|
||||||
|
image = "0.23"
|
||||||
|
imgui = { version = "0.8.2", features = ["tables-api"] }
|
||||||
|
imgui-glium-renderer = "0.8.2"
|
||||||
|
imgui-winit-support = "0.8.2"
|
Binary file not shown.
@ -0,0 +1,138 @@
|
|||||||
|
use glium::glutin;
|
||||||
|
use glium::glutin::event::{Event, WindowEvent};
|
||||||
|
use glium::glutin::event_loop::{ControlFlow, EventLoop};
|
||||||
|
use glium::glutin::window::WindowBuilder;
|
||||||
|
use glium::{Display, Surface};
|
||||||
|
use imgui::{Context, FontConfig, FontSource, Ui};
|
||||||
|
use imgui_glium_renderer::Renderer;
|
||||||
|
use imgui_winit_support::{HiDpiMode, WinitPlatform};
|
||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
|
pub struct System {
|
||||||
|
pub event_loop: EventLoop<()>,
|
||||||
|
pub display: glium::Display,
|
||||||
|
pub imgui: Context,
|
||||||
|
pub platform: WinitPlatform,
|
||||||
|
pub renderer: Renderer,
|
||||||
|
pub font_size: f32
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(title: &str, size: (f64, f64)) -> System {
|
||||||
|
let event_loop = EventLoop::new();
|
||||||
|
let context = glutin::ContextBuilder::new().with_vsync(true);
|
||||||
|
let builder = WindowBuilder::new()
|
||||||
|
.with_title(title.to_owned())
|
||||||
|
.with_inner_size(glium::glutin::dpi::LogicalSize::new(size.0, size.1));
|
||||||
|
let display = Display::new(builder, context, &event_loop)
|
||||||
|
.expect("Failed to initialize display!");
|
||||||
|
|
||||||
|
let mut imgui = Context::create();
|
||||||
|
imgui.set_ini_filename(None);
|
||||||
|
|
||||||
|
let mut platform = WinitPlatform::init(&mut imgui);
|
||||||
|
|
||||||
|
{
|
||||||
|
let gl_window = display.gl_window();
|
||||||
|
let window = gl_window.window();
|
||||||
|
|
||||||
|
let dpi_mode = if let Ok(factor) = std::env::var("FORCE_DPI_FACTOR") {
|
||||||
|
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 font_size = 13.0;
|
||||||
|
|
||||||
|
imgui.fonts().add_font(&[
|
||||||
|
FontSource::TtfData {
|
||||||
|
data: include_bytes!("/home/skybl/code/rs/imgui-test/assets/SFProDisplay_Regular.ttf"),
|
||||||
|
size_pixels: font_size,
|
||||||
|
config: Some(FontConfig {
|
||||||
|
rasterizer_multiply: 1.5,
|
||||||
|
oversample_h: 4,
|
||||||
|
oversample_v: 4,
|
||||||
|
..FontConfig::default()
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
let renderer = Renderer::init(&mut imgui, &display)
|
||||||
|
.expect("failed to initialize renderer!");
|
||||||
|
|
||||||
|
System {
|
||||||
|
event_loop,
|
||||||
|
display,
|
||||||
|
imgui,
|
||||||
|
platform,
|
||||||
|
renderer,
|
||||||
|
font_size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl System {
|
||||||
|
pub fn main_loop<F: FnMut(&mut bool, &mut Ui, &Display) + 'static>(self, mut run_ui: F) {
|
||||||
|
let System {
|
||||||
|
event_loop,
|
||||||
|
display,
|
||||||
|
mut imgui,
|
||||||
|
mut platform,
|
||||||
|
mut renderer,
|
||||||
|
..
|
||||||
|
} = self;
|
||||||
|
|
||||||
|
let mut last_frame = Instant::now();
|
||||||
|
|
||||||
|
event_loop.run(move |event, _, control_flow| match event {
|
||||||
|
Event::NewEvents(_) => {
|
||||||
|
let now = Instant::now();
|
||||||
|
imgui.io_mut().update_delta_time(now - last_frame);
|
||||||
|
last_frame = now;
|
||||||
|
}
|
||||||
|
Event::MainEventsCleared => {
|
||||||
|
let gl_window = display.gl_window();
|
||||||
|
platform
|
||||||
|
.prepare_frame(imgui.io_mut(), gl_window.window())
|
||||||
|
.expect("Failed to prepare frame");
|
||||||
|
gl_window.window().request_redraw();
|
||||||
|
}
|
||||||
|
Event::RedrawRequested(_) => {
|
||||||
|
let mut ui = imgui.frame();
|
||||||
|
|
||||||
|
let mut run = true;
|
||||||
|
run_ui(&mut run, &mut ui, &display);
|
||||||
|
if !run {
|
||||||
|
println!("exiting");
|
||||||
|
*control_flow = ControlFlow::Exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
let gl_window = display.gl_window();
|
||||||
|
let mut target = display.draw();
|
||||||
|
target.clear_color_srgb(1.0, 1.0, 1.0, 1.0);
|
||||||
|
platform.prepare_render(&ui, gl_window.window());
|
||||||
|
let draw_data = ui.render();
|
||||||
|
renderer
|
||||||
|
.render(&mut target, draw_data)
|
||||||
|
.expect("Rendering failed!");
|
||||||
|
target
|
||||||
|
.finish()
|
||||||
|
.expect("Failed to swap buffers!");
|
||||||
|
}
|
||||||
|
Event::WindowEvent {
|
||||||
|
event: WindowEvent::CloseRequested,
|
||||||
|
..
|
||||||
|
} => *control_flow = ControlFlow::Exit,
|
||||||
|
event => {
|
||||||
|
let gl_window = display.gl_window();
|
||||||
|
platform.handle_event(imgui.io_mut(), gl_window.window(), &event);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,92 @@
|
|||||||
|
// Node editor UI element
|
||||||
|
|
||||||
|
use glium::glutin::window::Window as GlutinWindow;
|
||||||
|
use glium::glutin::dpi::LogicalPosition;
|
||||||
|
use imgui::{Ui, MouseButton};
|
||||||
|
use imgui::color::ImColor32;
|
||||||
|
use imgui::draw_list::DrawListMut;
|
||||||
|
|
||||||
|
const COLOR_BLUE: ImColor32 = ImColor32::from_rgb(76u8, 177u8, 230u8);
|
||||||
|
|
||||||
|
pub struct DragArea {
|
||||||
|
pub offset: [f32; 2],
|
||||||
|
pub size: [f32; 2],
|
||||||
|
pub border_color: ImColor32,
|
||||||
|
default_offset: [f32; 2],
|
||||||
|
default_size: [f32; 2]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(
|
||||||
|
offset: [f32; 2],
|
||||||
|
size: [f32; 2]
|
||||||
|
) -> DragArea {
|
||||||
|
DragArea {
|
||||||
|
offset,
|
||||||
|
size,
|
||||||
|
border_color: COLOR_BLUE,
|
||||||
|
default_offset: offset,
|
||||||
|
default_size: size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DragArea {
|
||||||
|
pub fn build
|
||||||
|
<F: FnMut(&DrawListMut, &[f32; 2], &[f32; 2], &[f32; 2]) + 'static>
|
||||||
|
(&mut self, ui: &Ui, window: &GlutinWindow, mut build_children: F)
|
||||||
|
{
|
||||||
|
let mouse_delta = ui.io().mouse_delta;
|
||||||
|
let mouse_pos = ui.io().mouse_pos;
|
||||||
|
|
||||||
|
if ui.button("reset view") {
|
||||||
|
self.offset = self.default_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.invisible_button("##empty", self.size);
|
||||||
|
|
||||||
|
let rect_min = ui.item_rect_min();
|
||||||
|
let rect_max = ui.item_rect_max();
|
||||||
|
|
||||||
|
let lmb_down = ui.is_mouse_dragging(MouseButton::Left);
|
||||||
|
let mmb_down = ui.is_mouse_dragging(MouseButton::Middle);
|
||||||
|
|
||||||
|
if ui.is_item_active() && (lmb_down || mmb_down) {
|
||||||
|
if rect_min[0] > mouse_pos[0] {
|
||||||
|
window.set_cursor_position(
|
||||||
|
LogicalPosition { x: rect_max[0], y: mouse_pos[1] }
|
||||||
|
).unwrap();
|
||||||
|
self.offset[0] -= self.size[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if rect_max[0] < mouse_pos[0] {
|
||||||
|
window.set_cursor_position(
|
||||||
|
LogicalPosition { x: rect_min[0], y: mouse_pos[1] }
|
||||||
|
).unwrap();
|
||||||
|
self.offset[0] += self.size[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if rect_min[1] > mouse_pos[1] {
|
||||||
|
window.set_cursor_position(
|
||||||
|
LogicalPosition { x: mouse_pos[0], y: rect_max[1] }
|
||||||
|
).unwrap();
|
||||||
|
self.offset[1] -= self.size[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if rect_max[1] < mouse_pos[1] {
|
||||||
|
window.set_cursor_position(
|
||||||
|
LogicalPosition { x: mouse_pos[0], y: rect_min[1] }
|
||||||
|
).unwrap();
|
||||||
|
self.offset[1] += self.size[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
self.offset[0] += mouse_delta[0];
|
||||||
|
self.offset[1] += mouse_delta[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
let draw_list = ui.get_window_draw_list();
|
||||||
|
|
||||||
|
draw_list.with_clip_rect(rect_min, rect_max, || {
|
||||||
|
build_children(&draw_list, &rect_min, &rect_max, &self.offset);
|
||||||
|
draw_list.add_rect(rect_min, rect_max, self.border_color).build();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,61 @@
|
|||||||
|
use glium::glutin::dpi::LogicalSize;
|
||||||
|
use imgui::*;
|
||||||
|
use imgui::color::ImColor32;
|
||||||
|
|
||||||
|
mod app;
|
||||||
|
mod drag_area;
|
||||||
|
|
||||||
|
const INIT_SIZE: (f64, f64) = (640.0, 480.0);
|
||||||
|
const MIN_SIZE: (f64, f64) = (300.0, 250.0);
|
||||||
|
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let main_app = app::init("muh window", INIT_SIZE);
|
||||||
|
|
||||||
|
main_app.display
|
||||||
|
.gl_window()
|
||||||
|
.window()
|
||||||
|
.set_min_inner_size(Some(LogicalSize::new(MIN_SIZE.0, MIN_SIZE.1)));
|
||||||
|
|
||||||
|
let mut main_drag_area = drag_area::init(
|
||||||
|
[30.0, 30.0],
|
||||||
|
[200.0, 200.0]
|
||||||
|
);
|
||||||
|
|
||||||
|
main_app.main_loop(move |_, ui, display| {
|
||||||
|
Window::new("Node Editor")
|
||||||
|
.size([300.0, 110.0], Condition::FirstUseEver)
|
||||||
|
.build(&ui, || {
|
||||||
|
ui.text("Basic Drag Area");
|
||||||
|
ui.separator();
|
||||||
|
|
||||||
|
main_drag_area.build(
|
||||||
|
&ui,
|
||||||
|
&display.gl_window().window(),
|
||||||
|
move |draw_list, area_min, area_max, offset| {
|
||||||
|
let text_pos: [f32; 2] = [
|
||||||
|
area_min[0] + offset[0],
|
||||||
|
area_min[1] + offset[1]
|
||||||
|
];
|
||||||
|
|
||||||
|
draw_list.add_text(
|
||||||
|
text_pos,
|
||||||
|
ImColor32::WHITE,
|
||||||
|
"Basic Drag Area Contents"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
draw_mouse_pos_text(ui);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_mouse_pos_text(ui: &Ui) {
|
||||||
|
let mouse_pos = ui.io().mouse_pos;
|
||||||
|
|
||||||
|
ui.text(format!(
|
||||||
|
"mouse pos: ({:.1}, {:.1})",
|
||||||
|
mouse_pos[0], mouse_pos[1]
|
||||||
|
));
|
||||||
|
}
|
@ -0,0 +1,77 @@
|
|||||||
|
// Node editor UI element
|
||||||
|
|
||||||
|
pub struct NodeEditor {
|
||||||
|
mut offset: [f32; 2],
|
||||||
|
mut size: [f32; 2]
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NodeEditor {
|
||||||
|
pub fn build(&self, ui: &Ui) {
|
||||||
|
const MOUSE_DELTA = ui.io().mouse_delta;
|
||||||
|
const MOUSE_POS = ui.io().mouse_pos;
|
||||||
|
|
||||||
|
ui.invisible_button("##empty", self.size);
|
||||||
|
|
||||||
|
const RECT_MIN = ui.item_rect_min();
|
||||||
|
const RECT_MAX = ui.item_rect_max();
|
||||||
|
|
||||||
|
const LMB_DOWN = ui.is_mouse_dragging(MouseButton::Left);
|
||||||
|
const MMB_DOWN = ui.is_mouse_dragging(MouseButton::Middle);
|
||||||
|
|
||||||
|
let mut info = String::new();
|
||||||
|
|
||||||
|
if ui.is_item_active() && (lmb_down || mmb_down) {
|
||||||
|
let cursor_wrapping = false
|
||||||
|
|
||||||
|
if rect_min[0] > mouse_pos[0] {
|
||||||
|
info += "mouse x outside of left boundary\n";
|
||||||
|
window.set_cursor_position(
|
||||||
|
LogicalPosition { x: rect_max[0] - 1.0, y: mouse_pos[1] }
|
||||||
|
).unwrap();
|
||||||
|
cursor_wrapping = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if rect_max[0] < mouse_pos[0] {
|
||||||
|
info += "mouse x outside of right boundary\n";
|
||||||
|
window.set_cursor_position(
|
||||||
|
LogicalPosition { x: rect_min[0] + 1.0, y: mouse_pos[1] }
|
||||||
|
).unwrap();
|
||||||
|
cursor_wrapping = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if rect_min[1] > mouse_pos[1] {
|
||||||
|
info += "mouse y outside of top boundary\n";
|
||||||
|
window.set_cursor_position(
|
||||||
|
LogicalPosition { x: mouse_pos[0], y: rect_max[1] - 1.0 }
|
||||||
|
).unwrap();
|
||||||
|
cursor_wrapping = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if rect_max[1] < mouse_pos[1] {
|
||||||
|
info += "mouse y outside of bottom boundary\n";
|
||||||
|
window.set_cursor_position(
|
||||||
|
LogicalPosition { x: mouse_pos[0], y: rect_min[1] + 1.0 }
|
||||||
|
).unwrap();
|
||||||
|
cursor_wrapping = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cursor_wrapping {
|
||||||
|
offset[0] += mouse_delta[0];
|
||||||
|
offset[1] += mouse_delta[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let text_str = "i hope this works!";
|
||||||
|
let text_pos: [f32; 2] = [
|
||||||
|
rect_min[0] + self.offset[0],
|
||||||
|
rect_min[1] + self.offset[1]
|
||||||
|
];
|
||||||
|
|
||||||
|
let draw_list = ui.get_window_draw_list();
|
||||||
|
|
||||||
|
draw_list.with_clip_rect(rect_min, rect_max, || {
|
||||||
|
draw_list.add_text(text_pos, ImColor32::WHITE, text_str);
|
||||||
|
draw_list.add_rect(rect_min, rect_max, COLOR_BLUE).build();
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.text(format!("{}", info));
|
@ -0,0 +1,138 @@
|
|||||||
|
use glium::glutin;
|
||||||
|
use glium::glutin::event::{Event, WindowEvent};
|
||||||
|
use glium::glutin::event_loop::{ControlFlow, EventLoop};
|
||||||
|
use glium::glutin::window::WindowBuilder;
|
||||||
|
use glium::{Display, Surface};
|
||||||
|
use imgui::{Context, FontConfig, FontSource, Ui};
|
||||||
|
use imgui_glium_renderer::Renderer;
|
||||||
|
use imgui_winit_support::{HiDpiMode, WinitPlatform};
|
||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
|
pub struct System {
|
||||||
|
pub event_loop: EventLoop<()>,
|
||||||
|
pub display: glium::Display,
|
||||||
|
pub imgui: Context,
|
||||||
|
pub platform: WinitPlatform,
|
||||||
|
pub renderer: Renderer,
|
||||||
|
pub font_size: f32
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(title: &str, size: (f64, f64)) -> System {
|
||||||
|
let event_loop = EventLoop::new();
|
||||||
|
let context = glutin::ContextBuilder::new().with_vsync(true);
|
||||||
|
let builder = WindowBuilder::new()
|
||||||
|
.with_title(title.to_owned())
|
||||||
|
.with_inner_size(glium::glutin::dpi::LogicalSize::new(size.0, size.1));
|
||||||
|
let display = Display::new(builder, context, &event_loop)
|
||||||
|
.expect("Failed to initialize display!");
|
||||||
|
|
||||||
|
let mut imgui = Context::create();
|
||||||
|
imgui.set_ini_filename(None);
|
||||||
|
|
||||||
|
let mut platform = WinitPlatform::init(&mut imgui);
|
||||||
|
|
||||||
|
{
|
||||||
|
let gl_window = display.gl_window();
|
||||||
|
let window = gl_window.window();
|
||||||
|
|
||||||
|
let dpi_mode = if let Ok(factor) = std::env::var("FORCE_DPI_FACTOR") {
|
||||||
|
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 font_size = 13.0;
|
||||||
|
|
||||||
|
imgui.fonts().add_font(&[
|
||||||
|
FontSource::TtfData {
|
||||||
|
data: include_bytes!("/home/skybl/code/rs/imgui-test/assets/SFProDisplay_Regular.ttf"),
|
||||||
|
size_pixels: font_size,
|
||||||
|
config: Some(FontConfig {
|
||||||
|
rasterizer_multiply: 1.5,
|
||||||
|
oversample_h: 4,
|
||||||
|
oversample_v: 4,
|
||||||
|
..FontConfig::default()
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
let renderer = Renderer::init(&mut imgui, &display)
|
||||||
|
.expect("failed to initialize renderer!");
|
||||||
|
|
||||||
|
System {
|
||||||
|
event_loop,
|
||||||
|
display,
|
||||||
|
imgui,
|
||||||
|
platform,
|
||||||
|
renderer,
|
||||||
|
font_size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl System {
|
||||||
|
pub fn main_loop<F: FnMut(&mut bool, &mut Ui, &Display) + 'static>(self, mut run_ui: F) {
|
||||||
|
let System {
|
||||||
|
event_loop,
|
||||||
|
display,
|
||||||
|
mut imgui,
|
||||||
|
mut platform,
|
||||||
|
mut renderer,
|
||||||
|
..
|
||||||
|
} = self;
|
||||||
|
|
||||||
|
let mut last_frame = Instant::now();
|
||||||
|
|
||||||
|
event_loop.run(move |event, _, control_flow| match event {
|
||||||
|
Event::NewEvents(_) => {
|
||||||
|
let now = Instant::now();
|
||||||
|
imgui.io_mut().update_delta_time(now - last_frame);
|
||||||
|
last_frame = now;
|
||||||
|
}
|
||||||
|
Event::MainEventsCleared => {
|
||||||
|
let gl_window = display.gl_window();
|
||||||
|
platform
|
||||||
|
.prepare_frame(imgui.io_mut(), gl_window.window())
|
||||||
|
.expect("Failed to prepare frame");
|
||||||
|
gl_window.window().request_redraw();
|
||||||
|
}
|
||||||
|
Event::RedrawRequested(_) => {
|
||||||
|
let mut ui = imgui.frame();
|
||||||
|
|
||||||
|
let mut run = true;
|
||||||
|
run_ui(&mut run, &mut ui, &display);
|
||||||
|
if !run {
|
||||||
|
println!("exiting");
|
||||||
|
*control_flow = ControlFlow::Exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
let gl_window = display.gl_window();
|
||||||
|
let mut target = display.draw();
|
||||||
|
target.clear_color_srgb(1.0, 1.0, 1.0, 1.0);
|
||||||
|
platform.prepare_render(&ui, gl_window.window());
|
||||||
|
let draw_data = ui.render();
|
||||||
|
renderer
|
||||||
|
.render(&mut target, draw_data)
|
||||||
|
.expect("Rendering failed!");
|
||||||
|
target
|
||||||
|
.finish()
|
||||||
|
.expect("Failed to swap buffers!");
|
||||||
|
}
|
||||||
|
Event::WindowEvent {
|
||||||
|
event: WindowEvent::CloseRequested,
|
||||||
|
..
|
||||||
|
} => *control_flow = ControlFlow::Exit,
|
||||||
|
event => {
|
||||||
|
let gl_window = display.gl_window();
|
||||||
|
platform.handle_event(imgui.io_mut(), gl_window.window(), &event);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue