Skip to content

Commit 676795b

Browse files
committed
imgui-glium-renderer: Add AppContext API to renderer
1 parent 41605b5 commit 676795b

File tree

2 files changed

+283
-0
lines changed

2 files changed

+283
-0
lines changed

imgui-glium-renderer/src/context.rs

+281
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
1+
use std::cell::Ref;
2+
use std::rc::Rc;
3+
use std::time::Instant;
4+
5+
use glium::backend::glutin::DisplayCreationError;
6+
use glium::backend::{Context, Facade};
7+
use glium::{glutin, Display, Surface, SwapBuffersError};
8+
9+
use super::{Renderer, RendererError};
10+
use imgui::{ImGui, ImGuiMouseCursor, ImString, Ui};
11+
12+
#[derive(Copy, Clone, PartialEq, Debug, Default)]
13+
struct MouseState {
14+
pos: (i32, i32),
15+
pressed: (bool, bool, bool),
16+
wheel: f32,
17+
}
18+
19+
pub struct AppContext {
20+
renderer: Renderer,
21+
display: Display,
22+
events_loop: glutin::EventsLoop,
23+
imgui: ImGui,
24+
quit: bool,
25+
mouse_state: MouseState,
26+
last_frame: Instant,
27+
clear_color: [f32; 4],
28+
}
29+
30+
#[derive(Debug)]
31+
pub enum ContextError {
32+
Glutin(DisplayCreationError),
33+
Render(RendererError),
34+
SwapBuffers(SwapBuffersError),
35+
Message(String),
36+
}
37+
38+
impl From<DisplayCreationError> for ContextError {
39+
fn from(e: DisplayCreationError) -> Self { ContextError::Glutin(e) }
40+
}
41+
42+
impl From<RendererError> for ContextError {
43+
fn from(e: RendererError) -> Self { ContextError::Render(e) }
44+
}
45+
46+
impl From<SwapBuffersError> for ContextError {
47+
fn from(e: SwapBuffersError) -> Self { ContextError::SwapBuffers(e) }
48+
}
49+
50+
#[derive(Clone, Debug)]
51+
pub struct AppConfig {
52+
pub clear_color: [f32; 4],
53+
pub ini_filename: Option<ImString>,
54+
pub log_filename: Option<ImString>,
55+
pub window_width: u32,
56+
pub window_height: u32,
57+
}
58+
59+
impl Default for AppConfig {
60+
fn default() -> Self {
61+
Self {
62+
clear_color: [1.0, 1.0, 1.0, 1.0],
63+
ini_filename: None,
64+
log_filename: None,
65+
window_width: 1024,
66+
window_height: 768,
67+
}
68+
}
69+
}
70+
71+
impl Facade for AppContext {
72+
fn get_context(&self) -> &Rc<Context> { self.display.get_context() }
73+
}
74+
75+
impl AppContext {
76+
pub fn init(title: String, config: AppConfig) -> Result<Self, ContextError> {
77+
let events_loop = glutin::EventsLoop::new();
78+
let context = glutin::ContextBuilder::new().with_vsync(true);
79+
let window = glutin::WindowBuilder::new()
80+
.with_title(title)
81+
.with_dimensions((config.window_width, config.window_height).into());
82+
let display = Display::new(window, context, &events_loop)?;
83+
let mut imgui = ImGui::init();
84+
imgui.set_ini_filename(config.ini_filename);
85+
imgui.set_log_filename(config.log_filename);
86+
87+
let renderer = Renderer::init(&mut imgui, &display)?;
88+
89+
configure_keys(&mut imgui);
90+
91+
Ok(AppContext {
92+
renderer,
93+
display,
94+
events_loop,
95+
imgui,
96+
quit: false,
97+
mouse_state: Default::default(),
98+
last_frame: Instant::now(),
99+
clear_color: config.clear_color,
100+
})
101+
}
102+
103+
pub fn run<F: FnMut(&Ui) -> bool>(&mut self, mut run_ui: F) -> Result<(), ContextError> {
104+
loop {
105+
self.poll_events();
106+
let now = Instant::now();
107+
let delta = now - self.last_frame;
108+
let delta_s = delta.as_secs() as f32 + delta.subsec_nanos() as f32 / 1_000_000_000.0;
109+
update_mouse(&mut self.imgui, &mut self.mouse_state);
110+
let gl_window = self.display.gl_window();
111+
112+
update_os_cursor(&self.imgui, &gl_window);
113+
114+
let size_pixels = gl_window
115+
.get_inner_size()
116+
.ok_or_else(|| ContextError::Message("Window no longer exists!".to_owned()))?;
117+
let hdipi = gl_window.get_hidpi_factor();
118+
let size_points = (
119+
(size_pixels.width as f64 / hdipi) as u32,
120+
(size_pixels.height as f64 / hdipi) as u32,
121+
);
122+
123+
let ui = self.imgui.frame(size_points, size_pixels.into(), delta_s);
124+
if !run_ui(&ui) {
125+
break;
126+
}
127+
let mut target = self.display.draw();
128+
target.clear_color(
129+
self.clear_color[0],
130+
self.clear_color[1],
131+
self.clear_color[2],
132+
self.clear_color[3],
133+
);
134+
self.renderer.render(&mut target, ui)?;
135+
target.finish()?;
136+
137+
if self.quit {
138+
break;
139+
}
140+
}
141+
Ok(())
142+
}
143+
144+
fn poll_events(&mut self) {
145+
let quit = &mut self.quit;
146+
let imgui = &mut self.imgui;
147+
let events_loop = &mut self.events_loop;
148+
let mouse_state = &mut self.mouse_state;
149+
events_loop.poll_events(|event| {
150+
use glium::glutin::ElementState::Pressed;
151+
use glium::glutin::WindowEvent::*;
152+
use glium::glutin::{Event, MouseButton, MouseScrollDelta, TouchPhase};
153+
154+
if let Event::WindowEvent { event, .. } = event {
155+
match event {
156+
CloseRequested => *quit = true,
157+
KeyboardInput { input, .. } => {
158+
use glium::glutin::VirtualKeyCode as Key;
159+
160+
let pressed = input.state == Pressed;
161+
match input.virtual_keycode {
162+
Some(Key::Tab) => imgui.set_key(0, pressed),
163+
Some(Key::Left) => imgui.set_key(1, pressed),
164+
Some(Key::Right) => imgui.set_key(2, pressed),
165+
Some(Key::Up) => imgui.set_key(3, pressed),
166+
Some(Key::Down) => imgui.set_key(4, pressed),
167+
Some(Key::PageUp) => imgui.set_key(5, pressed),
168+
Some(Key::PageDown) => imgui.set_key(6, pressed),
169+
Some(Key::Home) => imgui.set_key(7, pressed),
170+
Some(Key::End) => imgui.set_key(8, pressed),
171+
Some(Key::Delete) => imgui.set_key(9, pressed),
172+
Some(Key::Back) => imgui.set_key(10, pressed),
173+
Some(Key::Return) => imgui.set_key(11, pressed),
174+
Some(Key::Escape) => imgui.set_key(12, pressed),
175+
Some(Key::A) => imgui.set_key(13, pressed),
176+
Some(Key::C) => imgui.set_key(14, pressed),
177+
Some(Key::V) => imgui.set_key(15, pressed),
178+
Some(Key::X) => imgui.set_key(16, pressed),
179+
Some(Key::Y) => imgui.set_key(17, pressed),
180+
Some(Key::Z) => imgui.set_key(18, pressed),
181+
Some(Key::LControl) | Some(Key::RControl) => {
182+
imgui.set_key_ctrl(pressed)
183+
}
184+
Some(Key::LShift) | Some(Key::RShift) => imgui.set_key_shift(pressed),
185+
Some(Key::LAlt) | Some(Key::RAlt) => imgui.set_key_alt(pressed),
186+
Some(Key::LWin) | Some(Key::RWin) => imgui.set_key_super(pressed),
187+
_ => {}
188+
}
189+
}
190+
CursorMoved {
191+
position, ..
192+
} => mouse_state.pos = position.into(),
193+
MouseInput { state, button, .. } => match button {
194+
MouseButton::Left => mouse_state.pressed.0 = state == Pressed,
195+
MouseButton::Right => mouse_state.pressed.1 = state == Pressed,
196+
MouseButton::Middle => mouse_state.pressed.2 = state == Pressed,
197+
_ => {}
198+
},
199+
MouseWheel {
200+
delta: MouseScrollDelta::LineDelta(_, y),
201+
phase: TouchPhase::Moved,
202+
..
203+
} => mouse_state.wheel = y,
204+
MouseWheel {
205+
delta: MouseScrollDelta::PixelDelta(pos),
206+
phase: TouchPhase::Moved,
207+
..
208+
} => mouse_state.wheel = pos.y as f32,
209+
ReceivedCharacter(c) => imgui.add_input_character(c),
210+
_ => (),
211+
}
212+
}
213+
});
214+
}
215+
216+
pub fn imgui(&self) -> &ImGui { &self.imgui }
217+
218+
pub fn imgui_mut(&mut self) -> &mut ImGui { &mut self.imgui }
219+
}
220+
221+
fn configure_keys(imgui: &mut ImGui) {
222+
use imgui::ImGuiKey;
223+
224+
imgui.set_imgui_key(ImGuiKey::Tab, 0);
225+
imgui.set_imgui_key(ImGuiKey::LeftArrow, 1);
226+
imgui.set_imgui_key(ImGuiKey::RightArrow, 2);
227+
imgui.set_imgui_key(ImGuiKey::UpArrow, 3);
228+
imgui.set_imgui_key(ImGuiKey::DownArrow, 4);
229+
imgui.set_imgui_key(ImGuiKey::PageUp, 5);
230+
imgui.set_imgui_key(ImGuiKey::PageDown, 6);
231+
imgui.set_imgui_key(ImGuiKey::Home, 7);
232+
imgui.set_imgui_key(ImGuiKey::End, 8);
233+
imgui.set_imgui_key(ImGuiKey::Delete, 9);
234+
imgui.set_imgui_key(ImGuiKey::Backspace, 10);
235+
imgui.set_imgui_key(ImGuiKey::Enter, 11);
236+
imgui.set_imgui_key(ImGuiKey::Escape, 12);
237+
imgui.set_imgui_key(ImGuiKey::A, 13);
238+
imgui.set_imgui_key(ImGuiKey::C, 14);
239+
imgui.set_imgui_key(ImGuiKey::V, 15);
240+
imgui.set_imgui_key(ImGuiKey::X, 16);
241+
imgui.set_imgui_key(ImGuiKey::Y, 17);
242+
imgui.set_imgui_key(ImGuiKey::Z, 18);
243+
}
244+
245+
fn update_mouse(imgui: &mut ImGui, mouse_state: &mut MouseState) {
246+
let scale = imgui.display_framebuffer_scale();
247+
imgui.set_mouse_pos(
248+
mouse_state.pos.0 as f32 / scale.0,
249+
mouse_state.pos.1 as f32 / scale.1,
250+
);
251+
imgui.set_mouse_down(&[
252+
mouse_state.pressed.0,
253+
mouse_state.pressed.1,
254+
mouse_state.pressed.2,
255+
false,
256+
false,
257+
]);
258+
imgui.set_mouse_wheel(mouse_state.wheel / scale.1);
259+
mouse_state.wheel = 0.0;
260+
}
261+
262+
fn update_os_cursor(imgui: &ImGui, gl_window: &Ref<glutin::GlWindow>) {
263+
let mouse_cursor = imgui.mouse_cursor();
264+
if imgui.mouse_draw_cursor() || mouse_cursor == ImGuiMouseCursor::None {
265+
// Hide OS cursor
266+
gl_window.hide_cursor(true);
267+
} else {
268+
// Set OS cursor
269+
gl_window.hide_cursor(false);
270+
gl_window.set_cursor(match mouse_cursor {
271+
ImGuiMouseCursor::None => unreachable!("mouse_cursor was None!"),
272+
ImGuiMouseCursor::Arrow => glutin::MouseCursor::Arrow,
273+
ImGuiMouseCursor::TextInput => glutin::MouseCursor::Text,
274+
ImGuiMouseCursor::Move => glutin::MouseCursor::Move,
275+
ImGuiMouseCursor::ResizeNS => glutin::MouseCursor::NsResize,
276+
ImGuiMouseCursor::ResizeEW => glutin::MouseCursor::EwResize,
277+
ImGuiMouseCursor::ResizeNESW => glutin::MouseCursor::NeswResize,
278+
ImGuiMouseCursor::ResizeNWSE => glutin::MouseCursor::NwseResize,
279+
});
280+
}
281+
}

imgui-glium-renderer/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
extern crate glium;
33
extern crate imgui;
44

5+
mod context;
56
mod im_texture;
7+
pub use context::{AppConfig, AppContext, ContextError};
68
pub use im_texture::Texture;
79

810
use glium::{DrawError, IndexBuffer, Program, Surface, Texture2d, VertexBuffer};

0 commit comments

Comments
 (0)