diff --git a/egui_glium/src/lib.rs b/egui_glium/src/lib.rs index 034e6827..51c1aeb8 100644 --- a/egui_glium/src/lib.rs +++ b/egui_glium/src/lib.rs @@ -5,10 +5,18 @@ mod painter; pub mod persistence; +mod runner; pub use painter::Painter; +pub use runner::*; -use glutin::event_loop::ControlFlow; +use { + clipboard::ClipboardProvider, + egui::*, + glium::glutin::{self, event::VirtualKeyCode, event_loop::ControlFlow}, +}; + +pub use clipboard::ClipboardContext; // TODO: remove pub fn input_to_egui( event: glutin::event::WindowEvent, @@ -196,12 +204,6 @@ pub fn handle_output( .set_cursor_icon(translate_cursor(output.cursor_icon)); } -use { - clipboard::{ClipboardContext, ClipboardProvider}, - egui::*, - glium::glutin::{self, event::VirtualKeyCode}, -}; - pub fn init_clipboard() -> Option { match ClipboardContext::new() { Ok(clipboard) => Some(clipboard), diff --git a/egui_glium/src/persistence.rs b/egui_glium/src/persistence.rs index 25a7b9b3..1df7d2ce 100644 --- a/egui_glium/src/persistence.rs +++ b/egui_glium/src/persistence.rs @@ -92,7 +92,9 @@ use glium::glutin; #[derive(Default, serde::Deserialize, serde::Serialize)] pub struct WindowSettings { + /// outer position of window in physical pixels pos: Option, + /// inner size of window in physical pixels size: Option, } diff --git a/egui_glium/src/runner.rs b/egui_glium/src/runner.rs new file mode 100644 index 00000000..6072f35a --- /dev/null +++ b/egui_glium/src/runner.rs @@ -0,0 +1,118 @@ +use std::time::Instant; + +use crate::{ + persistence::{Persistence, WindowSettings}, + *, +}; + +const EGUI_MEMORY_KEY: &str = "egui"; +const WINDOW_KEY: &str = "window"; + +pub trait App { + /// Called onced per frame for you to draw the UI. + fn ui(&mut self, ui: &mut egui::Ui, runner: &mut Runner); + + /// Called once on shutdown. Allows you to save state. + fn on_exit(&mut self, persistence: &mut Persistence); +} + +pub struct Runner { + frame_times: egui::MovementTracker, + quit: bool, +} + +impl Runner { + pub fn new() -> Self { + Self { + frame_times: egui::MovementTracker::new(1000, 1.0), + quit: false, + } + } + + pub fn quit(&mut self) { + self.quit = true; + } + + pub fn cpu_usage(&self) -> f32 { + self.frame_times.average().unwrap_or_default() + } + + pub fn fps(&self) -> f32 { + 1.0 / self.frame_times.mean_time_interval().unwrap_or_default() + } +} + +/// Run an egui app +pub fn run(title: &str, mut persistence: Persistence, mut app: impl App + 'static) -> ! { + let event_loop = glutin::event_loop::EventLoop::new(); + let mut window = glutin::window::WindowBuilder::new() + .with_decorations(true) + .with_resizable(true) + .with_title(title) + .with_transparent(false); + + let window_settings: Option = persistence.get_value(WINDOW_KEY); + if let Some(window_settings) = &window_settings { + window = window_settings.initialize_size(window); + } + + let context = glutin::ContextBuilder::new() + .with_depth_buffer(0) + .with_srgb(true) + .with_stencil_buffer(0) + .with_vsync(true); + let display = glium::Display::new(window, context, &event_loop).unwrap(); + + if let Some(window_settings) = &window_settings { + window_settings.restore_positions(&display); + } + + let mut ctx = egui::Context::new(); + *ctx.memory() = persistence.get_value(EGUI_MEMORY_KEY).unwrap_or_default(); + + let mut painter = Painter::new(&display); + let mut raw_input = make_raw_input(&display); + + // used to keep track of time for animations + let start_time = Instant::now(); + let mut runner = Runner::new(); + let mut clipboard = init_clipboard(); + + event_loop.run(move |event, _, control_flow| { + *control_flow = glutin::event_loop::ControlFlow::Wait; + + match event { + glutin::event::Event::RedrawRequested(_) => { + let egui_start = Instant::now(); + raw_input.time = start_time.elapsed().as_nanos() as f64 * 1e-9; + raw_input.seconds_since_midnight = Some(local_time_of_day()); + + let mut ui = ctx.begin_frame(raw_input.take()); + app.ui(&mut ui, &mut runner); + let (output, paint_jobs) = ctx.end_frame(); + + let frame_time = (Instant::now() - egui_start).as_secs_f64() as f32; + runner.frame_times.add(raw_input.time, frame_time); + + painter.paint_jobs(&display, paint_jobs, ctx.texture()); + handle_output(output, &display, clipboard.as_mut()); + + if runner.quit { + *control_flow = glutin::event_loop::ControlFlow::Exit + } else { + display.gl_window().window().request_redraw(); // TODO: only if needed (new events etc) + } + } + glutin::event::Event::WindowEvent { event, .. } => { + input_to_egui(event, clipboard.as_mut(), &mut raw_input, control_flow); + } + glutin::event::Event::LoopDestroyed => { + persistence.set_value(WINDOW_KEY, &WindowSettings::from_display(&display)); + persistence.set_value(EGUI_MEMORY_KEY, &*ctx.memory()); + app.on_exit(&mut persistence); + persistence.save(); + } + _ => (), + } + }); +} diff --git a/example_glium/src/main.rs b/example_glium/src/main.rs index b43e8706..580597fb 100644 --- a/example_glium/src/main.rs +++ b/example_glium/src/main.rs @@ -1,29 +1,23 @@ #![deny(warnings)] #![warn(clippy::all)] -use std::time::Instant; +use egui_glium::{persistence::Persistence, Runner}; -use { - egui_glium::{ - make_raw_input, - persistence::{Persistence, WindowSettings}, - }, - glium::glutin, -}; +const APP_KEY: &str = "app"; #[derive(Default, serde::Deserialize, serde::Serialize)] -struct App { +struct MyApp { egui_example_app: egui::ExampleApp, } -impl App { - pub fn ui(&mut self, ui: &mut egui::Ui, runner: &mut Runner) { +impl egui_glium::App for MyApp { + fn ui(&mut self, ui: &mut egui::Ui, runner: &mut Runner) { self.egui_example_app.ui(ui, ""); use egui::*; let mut ui = ui.centered_column(ui.available().width().min(480.0)); ui.set_layout(Layout::vertical(Align::Min)); - ui.add(label!("Egui quit inside of Glium").text_style(TextStyle::Heading)); + ui.add(label!("Egui inside of Glium").text_style(TextStyle::Heading)); if ui.add(Button::new("Quit")).clicked { runner.quit(); return; @@ -38,114 +32,15 @@ impl App { ); ui.add(label!("FPS: {:.1}", runner.fps()).text_style(TextStyle::Monospace)); } -} -struct Runner { - frame_times: egui::MovementTracker, - quit: bool, -} - -impl Runner { - pub fn new() -> Self { - Self { - frame_times: egui::MovementTracker::new(1000, 1.0), - quit: false, - } - } - - pub fn quit(&mut self) { - self.quit = true; - } - - pub fn cpu_usage(&self) -> f32 { - self.frame_times.average().unwrap_or_default() - } - - pub fn fps(&self) -> f32 { - 1.0 / self.frame_times.mean_time_interval().unwrap_or_default() + fn on_exit(&mut self, persistence: &mut Persistence) { + persistence.set_value(APP_KEY, self); } } fn main() { - const EGUI_MEMORY_KEY: &str = "egui"; - const WINDOW_KEY: &str = "window"; - const APP_KEY: &str = "app"; - - let mut persistence = Persistence::from_path("egui_example_glium.json".into()); - - let mut app: App = persistence.get_value("app").unwrap_or_default(); - - let event_loop = glutin::event_loop::EventLoop::new(); - let mut window = glutin::window::WindowBuilder::new() - .with_decorations(true) - .with_resizable(true) - .with_title("Egui glium example") - .with_transparent(false); - - let window_settings: Option = persistence.get_value(WINDOW_KEY); - if let Some(window_settings) = &window_settings { - window = window_settings.initialize_size(window); - } - - let context = glutin::ContextBuilder::new() - .with_depth_buffer(0) - .with_srgb(true) - .with_stencil_buffer(0) - .with_vsync(true); - let display = glium::Display::new(window, context, &event_loop).unwrap(); - - if let Some(window_settings) = &window_settings { - window_settings.restore_positions(&display); - } - - let mut ctx = egui::Context::new(); - *ctx.memory() = persistence.get_value(EGUI_MEMORY_KEY).unwrap_or_default(); - - let mut painter = egui_glium::Painter::new(&display); - let mut raw_input = make_raw_input(&display); - - // used to keep track of time for animations - let start_time = Instant::now(); - let mut runner = Runner::new(); - let mut clipboard = egui_glium::init_clipboard(); - - event_loop.run(move |event, _, control_flow| { - *control_flow = glutin::event_loop::ControlFlow::Wait; - - match event { - glutin::event::Event::RedrawRequested(_) => { - let egui_start = Instant::now(); - raw_input.time = start_time.elapsed().as_nanos() as f64 * 1e-9; - raw_input.seconds_since_midnight = Some(egui_glium::local_time_of_day()); - - let mut ui = ctx.begin_frame(raw_input.take()); - app.ui(&mut ui, &mut runner); - let (output, paint_jobs) = ctx.end_frame(); - - runner.frame_times.add( - raw_input.time, - (Instant::now() - egui_start).as_secs_f64() as f32, - ); - - painter.paint_jobs(&display, paint_jobs, ctx.texture()); - egui_glium::handle_output(output, &display, clipboard.as_mut()); - - if runner.quit { - *control_flow = glutin::event_loop::ControlFlow::Exit - } else { - display.gl_window().window().request_redraw(); // TODO: only if needed (new events etc) - } - } - glutin::event::Event::WindowEvent { event, .. } => { - egui_glium::input_to_egui(event, clipboard.as_mut(), &mut raw_input, control_flow); - } - glutin::event::Event::LoopDestroyed => { - persistence.set_value(APP_KEY, &app); - persistence.set_value(WINDOW_KEY, &WindowSettings::from_display(&display)); - persistence.set_value(EGUI_MEMORY_KEY, &*ctx.memory()); - persistence.save(); - } - _ => (), - } - }); + let title = "Egui glium example"; + let persistence = Persistence::from_path(".egui_example_glium.json".into()); + let app: MyApp = persistence.get_value(APP_KEY).unwrap_or_default(); + egui_glium::run(title, persistence, app); }