diff --git a/Cargo.lock b/Cargo.lock index a8958685..adb8bc13 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -422,6 +422,7 @@ dependencies = [ "clipboard 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "egui 0.1.2", "glium 0.27.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", "webbrowser 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/egui_glium/Cargo.toml b/egui_glium/Cargo.toml index 6b444c06..7b3e9acd 100644 --- a/egui_glium/Cargo.toml +++ b/egui_glium/Cargo.toml @@ -11,5 +11,6 @@ egui = { path = "../egui", features = ["with_serde"] } chrono = { version = "0.4" } clipboard = "0.5" glium = "0.27" +serde = "1" serde_json = "1" webbrowser = "0.5" diff --git a/egui_glium/src/lib.rs b/egui_glium/src/lib.rs index b2d3ffaa..e2ca1c93 100644 --- a/egui_glium/src/lib.rs +++ b/egui_glium/src/lib.rs @@ -6,31 +6,17 @@ mod painter; pub use painter::Painter; -use { - clipboard::{ClipboardContext, ClipboardProvider}, - egui::*, - glium::glutin::{self, event::VirtualKeyCode}, -}; - -pub fn init_clipboard() -> Option { - match ClipboardContext::new() { - Ok(clipboard) => Some(clipboard), - Err(err) => { - eprintln!("Failed to initialize clipboard: {}", err); - None - } - } -} +use glutin::event_loop::ControlFlow; pub fn input_to_egui( event: glutin::event::WindowEvent, clipboard: Option<&mut ClipboardContext>, raw_input: &mut RawInput, - running: &mut bool, + control_flow: &mut ControlFlow, ) { use glutin::event::WindowEvent::*; match event { - CloseRequested | Destroyed => *running = false, + CloseRequested | Destroyed => *control_flow = ControlFlow::Exit, Resized(physical_size) => { raw_input.screen_size = @@ -73,7 +59,7 @@ pub fn input_to_egui( if let Some(virtual_keycode) = input.virtual_keycode { // TODO: If mac if input.modifiers.logo() && virtual_keycode == VirtualKeyCode::Q { - *running = false; + *control_flow = ControlFlow::Exit; } match virtual_keycode { @@ -208,27 +194,53 @@ 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), + Err(err) => { + eprintln!("Failed to initialize clipboard: {}", err); + None + } + } +} + // ---------------------------------------------------------------------------- -pub fn read_memory(ctx: &Context, memory_json_path: impl AsRef) { +pub fn read_json(memory_json_path: impl AsRef) -> Option +where + T: serde::de::DeserializeOwned, +{ match std::fs::File::open(memory_json_path) { Ok(file) => { let reader = std::io::BufReader::new(file); match serde_json::from_reader(reader) { - Ok(memory) => { - *ctx.memory() = memory; - } + Ok(value) => Some(value), Err(err) => { - eprintln!("ERROR: Failed to parse memory json: {}", err); + eprintln!("ERROR: Failed to parse json: {}", err); + None } } } Err(_err) => { // File probably doesn't exist. That's fine. + None } } } +pub fn read_memory(ctx: &Context, memory_json_path: impl AsRef) { + let memory: Option = read_json(memory_json_path); + if let Some(memory) = memory { + *ctx.memory() = memory; + } +} + pub fn write_memory( ctx: &Context, memory_json_path: impl AsRef, @@ -245,3 +257,91 @@ pub fn local_time_of_day() -> f64 { let time = chrono::Local::now().time(); time.num_seconds_from_midnight() as f64 + 1e-9 * (time.nanosecond() as f64) } + +// ---------------------------------------------------------------------------- + +#[derive(Default, serde::Deserialize, serde::Serialize)] +pub struct WindowSettings { + pos: Option, + size: Option, +} + +impl WindowSettings { + pub fn from_json_file( + settings_json_path: impl AsRef, + ) -> Option { + read_json(settings_json_path) + } + + pub fn from_display(display: &glium::Display) -> Self { + Self { + pos: display + .gl_window() + .window() + .outer_position() + .ok() + .map(|p| pos2(p.x as f32, p.y as f32)), + + size: Some(vec2( + display.gl_window().window().inner_size().width as f32, + display.gl_window().window().inner_size().height as f32, + )), + } + } + + pub fn initialize_size( + &self, + window: glutin::window::WindowBuilder, + ) -> glutin::window::WindowBuilder { + if let Some(size) = self.size { + window.with_inner_size(glutin::dpi::PhysicalSize { + width: size.x as f64, + height: size.y as f64, + }) + } else { + window + } + + // Not yet available in winit: https://github.com/rust-windowing/winit/issues/1190 + // if let Some(pos) = self.pos { + // *window = window.with_outer_pos(glutin::dpi::PhysicalPosition { + // x: pos.x as f64, + // y: pos.y as f64, + // }); + // } + } + + pub fn restore_positions(&self, display: &glium::Display) { + // not needed, done by `initialize_size` + // let size = self.size.unwrap_or_else(|| vec2(1024.0, 800.0)); + // display + // .gl_window() + // .window() + // .set_inner_size(glutin::dpi::PhysicalSize { + // width: size.x as f64, + // height: size.y as f64, + // }); + + if let Some(pos) = self.pos { + display + .gl_window() + .window() + .set_outer_position(glutin::dpi::PhysicalPosition::new( + pos.x as f64, + pos.y as f64, + )); + } + } +} + +pub fn make_raw_input(display: &glium::Display) -> egui::RawInput { + let pixels_per_point = display.gl_window().window().scale_factor() as f32; + egui::RawInput { + screen_size: { + let (width, height) = display.get_framebuffer_dimensions(); + vec2(width as f32, height as f32) / pixels_per_point + }, + pixels_per_point: Some(pixels_per_point), + ..Default::default() + } +} diff --git a/example_glium/src/main.rs b/example_glium/src/main.rs index 93267111..f2feb7a7 100644 --- a/example_glium/src/main.rs +++ b/example_glium/src/main.rs @@ -4,54 +4,31 @@ use std::time::Instant; use { - egui::{examples::ExampleApp, pos2, vec2, Pos2, Vec2}, + egui::examples::ExampleApp, + egui_glium::{make_raw_input, read_json, WindowSettings}, glium::glutin, - glutin::dpi::PhysicalPosition, }; -#[derive(Default, serde::Deserialize, serde::Serialize)] -struct Window { - pos: Option, - size: Option, -} - -fn read_json(memory_json_path: impl AsRef) -> Option -where - T: serde::de::DeserializeOwned, -{ - match std::fs::File::open(memory_json_path) { - Ok(file) => { - let reader = std::io::BufReader::new(file); - match serde_json::from_reader(reader) { - Ok(value) => Some(value), - Err(err) => { - eprintln!("ERROR: Failed to parse json: {}", err); - None - } - } - } - Err(_err) => { - // File probably doesn't exist. That's fine. - None - } - } -} - fn main() { - // TODO: combine + // TODO: combine into one json file? let memory_path = "egui.json"; let settings_json_path: &str = "window.json"; let app_json_path: &str = "egui_example_app.json"; let mut egui_example_app: ExampleApp = read_json(app_json_path).unwrap_or_default(); - let mut window_settings: Window = read_json(settings_json_path).unwrap_or_default(); let event_loop = glutin::event_loop::EventLoop::new(); - let window = glutin::window::WindowBuilder::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 = WindowSettings::from_json_file(settings_json_path); + 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) @@ -59,36 +36,13 @@ fn main() { .with_vsync(true); let display = glium::Display::new(window, context, &event_loop).unwrap(); - let size = window_settings.size.unwrap_or_else(|| vec2(1024.0, 800.0)); - - display - .gl_window() - .window() - .set_inner_size(glutin::dpi::PhysicalSize { - width: size.x as f64, - height: size.y as f64, - }); - - if let Some(pos) = window_settings.pos { - display - .gl_window() - .window() - .set_outer_position(PhysicalPosition::new(pos.x as f64, pos.y as f64)); + if let Some(window_settings) = &window_settings { + window_settings.restore_positions(&display); } - let pixels_per_point = display.gl_window().window().scale_factor() as f32; - let mut ctx = egui::Context::new(); let mut painter = egui_glium::Painter::new(&display); - - let mut raw_input = egui::RawInput { - screen_size: { - let (width, height) = display.get_framebuffer_dimensions(); - vec2(width as f32, height as f32) / pixels_per_point - }, - pixels_per_point: Some(pixels_per_point), - ..Default::default() - }; + let mut raw_input = make_raw_input(&display); // used to keep track of time for animations let start_time = Instant::now(); @@ -146,25 +100,10 @@ fn main() { display.gl_window().window().request_redraw(); // TODO: only if needed (new events etc) } glutin::event::Event::WindowEvent { event, .. } => { - let mut running = true; - egui_glium::input_to_egui(event, clipboard.as_mut(), &mut raw_input, &mut running); - if !running { - *control_flow = glutin::event_loop::ControlFlow::Exit; - } + egui_glium::input_to_egui(event, clipboard.as_mut(), &mut raw_input, control_flow); } glutin::event::Event::LoopDestroyed => { // Save state to disk: - window_settings.pos = display - .gl_window() - .window() - .outer_position() - .ok() - .map(|p| pos2(p.x as f32, p.y as f32)); - window_settings.size = Some(vec2( - display.gl_window().window().inner_size().width as f32, - display.gl_window().window().inner_size().height as f32, - )); - if let Err(err) = egui_glium::write_memory(&ctx, memory_path) { eprintln!("ERROR: Failed to save egui state: {}", err); } @@ -177,7 +116,7 @@ fn main() { serde_json::to_writer_pretty( std::fs::File::create(settings_json_path).unwrap(), - &window_settings, + &WindowSettings::from_display(&display), ) .unwrap(); }