From 69d31a5e475686480cad60bfba4fa3694fa4d89e Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Mon, 4 Jan 2021 01:44:02 +0100 Subject: [PATCH] [eframe] Make persistence, http and time optional features Saves on compile times. --- Cargo.lock | 2 - check.sh | 8 +- eframe/Cargo.toml | 12 +- egui_demo_app/Cargo.toml | 10 +- egui_demo_lib/Cargo.toml | 20 +- egui_demo_lib/src/apps/color_test.rs | 4 +- egui_demo_lib/src/apps/demo/app.rs | 7 +- .../src/apps/demo/dancing_strings.rs | 4 +- egui_demo_lib/src/apps/demo/demo_window.rs | 20 +- egui_demo_lib/src/apps/demo/demo_windows.rs | 13 +- egui_demo_lib/src/apps/demo/painting.rs | 4 +- egui_demo_lib/src/apps/demo/scrolls.rs | 4 +- egui_demo_lib/src/apps/demo/sliders.rs | 5 +- egui_demo_lib/src/apps/demo/widgets.rs | 7 +- egui_demo_lib/src/apps/demo/window_options.rs | 3 +- egui_demo_lib/src/apps/fractal_clock.rs | 5 +- egui_demo_lib/src/apps/http_app.rs | 8 +- egui_demo_lib/src/apps/mod.rs | 2 + egui_demo_lib/src/wrap_app.rs | 24 ++- egui_glium/Cargo.toml | 29 ++- egui_glium/src/backend.rs | 53 ++++-- egui_glium/src/lib.rs | 19 +- egui_glium/src/persistence.rs | 87 +++++++++ egui_glium/src/storage.rs | 175 ------------------ egui_glium/src/window_settings.rs | 85 +++++++++ egui_web/Cargo.toml | 79 ++++---- egui_web/src/backend.rs | 8 +- egui_web/src/lib.rs | 9 + epi/Cargo.toml | 5 + epi/src/lib.rs | 4 + 30 files changed, 412 insertions(+), 303 deletions(-) create mode 100644 egui_glium/src/persistence.rs delete mode 100644 egui_glium/src/storage.rs create mode 100644 egui_glium/src/window_settings.rs diff --git a/Cargo.lock b/Cargo.lock index a3de8bc8..95d75765 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -692,7 +692,6 @@ version = "0.1.0" dependencies = [ "eframe", "egui_demo_lib", - "serde", ] [[package]] @@ -704,7 +703,6 @@ dependencies = [ "epi", "image", "serde", - "serde_json", "syntect", ] diff --git a/check.sh b/check.sh index a48e7983..188afc66 100755 --- a/check.sh +++ b/check.sh @@ -1,15 +1,15 @@ #!/bin/bash set -eu -cargo check --workspace --all-targets --all-features --release +cargo check --workspace --all-targets +cargo check --workspace --all-targets --all-features +cargo check -p egui_demo_app --lib --target wasm32-unknown-unknown +cargo check -p egui_demo_app --lib --target wasm32-unknown-unknown --all-features cargo fmt --all -- --check CARGO_INCREMENTAL=0 cargo clippy --workspace --all-targets --all-features -- -D warnings -W clippy::all #-W clippy::pedantic -W clippy::restriction -W clippy::nursery cargo test --workspace --all-targets --all-features cargo test --workspace --doc -cargo check -p egui_web --lib --target wasm32-unknown-unknown -cargo check -p egui_demo_app --lib --target wasm32-unknown-unknown - # For finding bloat: # cargo bloat --release --bin demo_glium -n 200 | rg egui diff --git a/eframe/Cargo.toml b/eframe/Cargo.toml index 7b3462ca..3b72aa0a 100644 --- a/eframe/Cargo.toml +++ b/eframe/Cargo.toml @@ -15,13 +15,19 @@ include = [ "**/*.rs", "Cargo.toml"] [lib] [dependencies] -egui = { version = "0.6.0", path = "../egui", features = ["serde"] } -epi = { version = "0.6.0", path = "../epi", features = ["serde", "serde_json"] } +egui = { version = "0.6.0", path = "../egui" } +epi = { version = "0.6.0", path = "../epi" } # For compiling natively: [target.'cfg(not(target_arch = "wasm32"))'.dependencies] -egui_glium = { path = "../egui_glium", features = ["http"] } +egui_glium = { path = "../egui_glium" } # For compiling to web: [target.'cfg(target_arch = "wasm32")'.dependencies] egui_web = { path = "../egui_web" } + +[features] +default = [] +http = ["egui_glium/http", "egui_web/http"] +persistence = ["epi/persistence", "egui_glium/persistence", "egui_web/persistence"] +time = ["egui_glium/time"] # for seconds_since_midnight diff --git a/egui_demo_app/Cargo.toml b/egui_demo_app/Cargo.toml index 21f4b6e3..216b44c7 100644 --- a/egui_demo_app/Cargo.toml +++ b/egui_demo_app/Cargo.toml @@ -9,6 +9,10 @@ edition = "2018" crate-type = ["cdylib", "rlib"] [dependencies] -eframe = { version = "0.6.0", path = "../eframe"} -egui_demo_lib = { version = "0.6.0", path = "../egui_demo_lib"} -serde = { version = "1", features = ["derive"] } +eframe = { version = "0.6.0", path = "../eframe", features = ["time"] } +egui_demo_lib = { version = "0.6.0", path = "../egui_demo_lib" } + +[features] +default = [] +http = ["eframe/http", "egui_demo_lib/http"] +persistence = ["eframe/persistence", "egui_demo_lib/persistence"] diff --git a/egui_demo_lib/Cargo.toml b/egui_demo_lib/Cargo.toml index 8be86b95..3af2d188 100644 --- a/egui_demo_lib/Cargo.toml +++ b/egui_demo_lib/Cargo.toml @@ -15,18 +15,24 @@ include = [ "**/*.rs", "Cargo.toml"] [lib] [dependencies] -egui = { version = "0.6.0", path = "../egui", features = ["serde"] } -epi = { version = "0.6.0", path = "../epi", features = ["serde", "serde_json"] } -serde = { version = "1", features = ["derive"] } -serde_json = "1" +egui = { version = "0.6.0", path = "../egui" } +epi = { version = "0.6.0", path = "../epi" } -# Http fetch app: -image = { version = "0.23", default_features = false, features = ["jpeg", "png"] } -syntect = { version = "4", default_features = false, features = ["default-fancy"] } +# feature "http": +image = { version = "0.23", default_features = false, features = ["jpeg", "png"], optional = true } +syntect = { version = "4", default_features = false, features = ["default-fancy"], optional = true } + +# feature "persistence": +serde = { version = "1", features = ["derive"], optional = true } [dev-dependencies] criterion = { version = "0.3", default-features = false } +[features] +default = [] +http = ["image", "syntect", "epi/http"] +persistence = ["epi/persistence", "serde"] + [[bench]] name = "benchmark" harness = false diff --git a/egui_demo_lib/src/apps/color_test.rs b/egui_demo_lib/src/apps/color_test.rs index da8385af..0747600c 100644 --- a/egui_demo_lib/src/apps/color_test.rs +++ b/egui_demo_lib/src/apps/color_test.rs @@ -9,9 +9,9 @@ const RED: Color32 = Color32::RED; const TRANSPARENT: Color32 = Color32::TRANSPARENT; const WHITE: Color32 = Color32::WHITE; -#[derive(serde::Deserialize, serde::Serialize)] +#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] pub struct ColorTest { - #[serde(skip)] + #[cfg_attr(feature = "persistence", serde(skip))] tex_mngr: TextureManager, vertex_gradients: bool, texture_gradients: bool, diff --git a/egui_demo_lib/src/apps/demo/app.rs b/egui_demo_lib/src/apps/demo/app.rs index 69d1f764..d31f71a0 100644 --- a/egui_demo_lib/src/apps/demo/app.rs +++ b/egui_demo_lib/src/apps/demo/app.rs @@ -2,8 +2,9 @@ /// /// Implements `epi::App` so it can be used with /// [`egui_glium`](https://crates.io/crates/egui_glium) and [`egui_web`](https://crates.io/crates/egui_web). -#[derive(Default, serde::Deserialize, serde::Serialize)] -#[serde(default)] +#[derive(Default)] +#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "persistence", serde(default))] pub struct DemoApp { demo_windows: super::DemoWindows, } @@ -13,10 +14,12 @@ impl epi::App for DemoApp { "✨ Egui Demo" } + #[cfg(feature = "persistence")] fn load(&mut self, storage: &dyn epi::Storage) { *self = epi::get_value(storage, epi::APP_KEY).unwrap_or_default() } + #[cfg(feature = "persistence")] fn save(&mut self, storage: &mut dyn epi::Storage) { epi::set_value(storage, epi::APP_KEY, self); } diff --git a/egui_demo_lib/src/apps/demo/dancing_strings.rs b/egui_demo_lib/src/apps/demo/dancing_strings.rs index babd2e17..0650c202 100644 --- a/egui_demo_lib/src/apps/demo/dancing_strings.rs +++ b/egui_demo_lib/src/apps/demo/dancing_strings.rs @@ -1,7 +1,7 @@ use egui::{containers::*, *}; -#[derive(serde::Deserialize, serde::Serialize)] -#[serde(default)] +#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "persistence", serde(default))] pub struct DancingStrings {} impl Default for DancingStrings { diff --git a/egui_demo_lib/src/apps/demo/demo_window.rs b/egui_demo_lib/src/apps/demo/demo_window.rs index 3c514721..81d13cb6 100644 --- a/egui_demo_lib/src/apps/demo/demo_window.rs +++ b/egui_demo_lib/src/apps/demo/demo_window.rs @@ -2,8 +2,8 @@ use super::*; use egui::{color::*, *}; /// Showcase some ui code -#[derive(serde::Deserialize, serde::Serialize)] -#[serde(default)] +#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "persistence", serde(default))] pub struct DemoWindow { num_columns: usize, @@ -106,8 +106,9 @@ impl DemoWindow { // ---------------------------------------------------------------------------- -#[derive(PartialEq, serde::Deserialize, serde::Serialize)] -#[serde(default)] +#[derive(PartialEq)] +#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "persistence", serde(default))] struct ColorWidgets { srgba_unmul: [u8; 4], srgba_premul: [u8; 4], @@ -176,8 +177,8 @@ impl ColorWidgets { // ---------------------------------------------------------------------------- -#[derive(serde::Deserialize, serde::Serialize)] -#[serde(default)] +#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "persistence", serde(default))] struct BoxPainting { size: Vec2, corner_radius: f32, @@ -220,8 +221,8 @@ impl BoxPainting { // ---------------------------------------------------------------------------- -#[derive(serde::Deserialize, serde::Serialize)] -#[serde(default)] +#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "persistence", serde(default))] struct LayoutDemo { // Identical to contents of `egui::Layout` main_dir: Direction, @@ -363,7 +364,8 @@ enum Action { Delete, } -#[derive(Clone, Default, serde::Deserialize, serde::Serialize)] +#[derive(Clone, Default)] +#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] struct Tree(Vec); impl Tree { diff --git a/egui_demo_lib/src/apps/demo/demo_windows.rs b/egui_demo_lib/src/apps/demo/demo_windows.rs index 33bcf0f3..12d5e98a 100644 --- a/egui_demo_lib/src/apps/demo/demo_windows.rs +++ b/egui_demo_lib/src/apps/demo/demo_windows.rs @@ -2,11 +2,11 @@ use egui::{CtxRef, Resize, ScrollArea, Ui, Window}; // ---------------------------------------------------------------------------- -#[derive(serde::Deserialize, serde::Serialize)] -#[serde(default)] +#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "persistence", serde(default))] struct Demos { /// open, view - #[serde(skip)] // TODO: serialize the `open` state. + #[cfg_attr(feature = "persistence", serde(skip))] // TODO: serialize the `open` state. demos: Vec<(bool, Box)>, } impl Default for Demos { @@ -40,8 +40,9 @@ impl Demos { // ---------------------------------------------------------------------------- /// A menu bar in which you can select different demo windows to show. -#[derive(Default, serde::Deserialize, serde::Serialize)] -#[serde(default)] +#[derive(Default)] +#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "persistence", serde(default))] pub struct DemoWindows { open_windows: OpenWindows, @@ -189,7 +190,7 @@ impl DemoWindows { // ---------------------------------------------------------------------------- -#[derive(serde::Deserialize, serde::Serialize)] +#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] struct OpenWindows { demo: bool, diff --git a/egui_demo_lib/src/apps/demo/painting.rs b/egui_demo_lib/src/apps/demo/painting.rs index d41d282b..63b28a5d 100644 --- a/egui_demo_lib/src/apps/demo/painting.rs +++ b/egui_demo_lib/src/apps/demo/painting.rs @@ -1,7 +1,7 @@ use egui::*; -#[derive(serde::Deserialize, serde::Serialize)] -#[serde(default)] +#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "persistence", serde(default))] pub struct Painting { lines: Vec>, stroke: Stroke, diff --git a/egui_demo_lib/src/apps/demo/scrolls.rs b/egui_demo_lib/src/apps/demo/scrolls.rs index 44a693ce..49c8f61c 100644 --- a/egui_demo_lib/src/apps/demo/scrolls.rs +++ b/egui_demo_lib/src/apps/demo/scrolls.rs @@ -1,7 +1,7 @@ use egui::{color::*, *}; -#[derive(serde::Deserialize, serde::Serialize)] -#[serde(default)] +#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "persistence", serde(default))] pub struct Scrolls { track_item: usize, tracking: bool, diff --git a/egui_demo_lib/src/apps/demo/sliders.rs b/egui_demo_lib/src/apps/demo/sliders.rs index 5088fadc..92192deb 100644 --- a/egui_demo_lib/src/apps/demo/sliders.rs +++ b/egui_demo_lib/src/apps/demo/sliders.rs @@ -2,8 +2,9 @@ use egui::*; use std::f64::INFINITY; /// Showcase sliders -#[derive(PartialEq, serde::Deserialize, serde::Serialize)] -#[serde(default)] +#[derive(PartialEq)] +#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "persistence", serde(default))] pub struct Sliders { pub min: f64, pub max: f64, diff --git a/egui_demo_lib/src/apps/demo/widgets.rs b/egui_demo_lib/src/apps/demo/widgets.rs index e407f5f0..c6b82091 100644 --- a/egui_demo_lib/src/apps/demo/widgets.rs +++ b/egui_demo_lib/src/apps/demo/widgets.rs @@ -1,6 +1,7 @@ use egui::{color::*, *}; -#[derive(Debug, PartialEq, serde::Deserialize, serde::Serialize)] +#[derive(Debug, PartialEq)] +#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] enum Enum { First, Second, @@ -13,8 +14,8 @@ impl Default for Enum { } } -#[derive(serde::Deserialize, serde::Serialize)] -#[serde(default)] +#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "persistence", serde(default))] pub struct Widgets { button_enabled: bool, count: usize, diff --git a/egui_demo_lib/src/apps/demo/window_options.rs b/egui_demo_lib/src/apps/demo/window_options.rs index b33d2efe..b62b3bb4 100644 --- a/egui_demo_lib/src/apps/demo/window_options.rs +++ b/egui_demo_lib/src/apps/demo/window_options.rs @@ -1,6 +1,7 @@ use crate::__egui_github_link_file; -#[derive(Clone, PartialEq, serde::Deserialize, serde::Serialize)] +#[derive(Clone, PartialEq)] +#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] pub struct WindowOptions { title: String, title_bar: bool, diff --git a/egui_demo_lib/src/apps/fractal_clock.rs b/egui_demo_lib/src/apps/fractal_clock.rs index a9262d16..630a6b33 100644 --- a/egui_demo_lib/src/apps/fractal_clock.rs +++ b/egui_demo_lib/src/apps/fractal_clock.rs @@ -1,8 +1,9 @@ use egui::{containers::*, widgets::*, *}; use std::f32::consts::TAU; -#[derive(PartialEq, serde::Deserialize, serde::Serialize)] -#[serde(default)] +#[derive(PartialEq)] +#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "persistence", serde(default))] pub struct FractalClock { paused: bool, time: f64, diff --git a/egui_demo_lib/src/apps/http_app.rs b/egui_demo_lib/src/apps/http_app.rs index 61d917e3..aaf2f815 100644 --- a/egui_demo_lib/src/apps/http_app.rs +++ b/egui_demo_lib/src/apps/http_app.rs @@ -30,17 +30,17 @@ impl Resource { } } -#[derive(serde::Deserialize, serde::Serialize)] +#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] pub struct HttpApp { url: String, - #[serde(skip)] + #[cfg_attr(feature = "persistence", serde(skip))] in_progress: Option>>, - #[serde(skip)] + #[cfg_attr(feature = "persistence", serde(skip))] result: Option>, - #[serde(skip)] + #[cfg_attr(feature = "persistence", serde(skip))] tex_mngr: TexMngr, } diff --git a/egui_demo_lib/src/apps/mod.rs b/egui_demo_lib/src/apps/mod.rs index 15abcf05..f801529f 100644 --- a/egui_demo_lib/src/apps/mod.rs +++ b/egui_demo_lib/src/apps/mod.rs @@ -1,11 +1,13 @@ mod color_test; mod demo; mod fractal_clock; +#[cfg(feature = "http")] mod http_app; pub use color_test::ColorTest; pub use demo::DemoApp; pub use fractal_clock::FractalClock; +#[cfg(feature = "http")] pub use http_app::HttpApp; pub use demo::DemoWindows; // used for tests diff --git a/egui_demo_lib/src/wrap_app.rs b/egui_demo_lib/src/wrap_app.rs index ead2a44c..081b8a3e 100644 --- a/egui_demo_lib/src/wrap_app.rs +++ b/egui_demo_lib/src/wrap_app.rs @@ -1,8 +1,10 @@ /// All the different demo apps. -#[derive(Default, serde::Deserialize, serde::Serialize)] -#[serde(default)] +#[derive(Default)] +#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "persistence", serde(default))] pub struct Apps { demo: crate::apps::DemoApp, + #[cfg(feature = "http")] http: crate::apps::HttpApp, clock: crate::apps::FractalClock, color_test: crate::apps::ColorTest, @@ -12,6 +14,7 @@ impl Apps { fn iter_mut(&mut self) -> impl Iterator { vec![ ("demo", &mut self.demo as &mut dyn epi::App), + #[cfg(feature = "http")] ("http", &mut self.http as &mut dyn epi::App), ("clock", &mut self.clock as &mut dyn epi::App), ("colors", &mut self.color_test as &mut dyn epi::App), @@ -21,8 +24,9 @@ impl Apps { } /// Wraps many demo/test apps into one. -#[derive(Default, serde::Deserialize, serde::Serialize)] -#[serde(default)] +#[derive(Default)] +#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "persistence", serde(default))] pub struct WrapApp { selected_anchor: String, apps: Apps, @@ -34,10 +38,12 @@ impl epi::App for WrapApp { "Egui Demo Apps" } + #[cfg(feature = "persistence")] fn load(&mut self, storage: &dyn epi::Storage) { *self = epi::get_value(storage, epi::APP_KEY).unwrap_or_default() } + #[cfg(feature = "persistence")] fn save(&mut self, storage: &mut dyn epi::Storage) { epi::set_value(storage, epi::APP_KEY, self); } @@ -167,18 +173,20 @@ impl Default for RunMode { // ---------------------------------------------------------------------------- -#[derive(Default, serde::Deserialize, serde::Serialize)] -#[serde(default)] +#[derive(Default)] +#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "persistence", serde(default))] struct BackendPanel { open: bool, - #[serde(skip)] // go back to `Reactive` mode each time we start + #[cfg_attr(feature = "persistence", serde(skip))] + // go back to `Reactive` mode each time we start run_mode: RunMode, /// current slider value for current gui scale pixels_per_point: Option, - #[serde(skip)] + #[cfg_attr(feature = "persistence", serde(skip))] frame_history: crate::frame_history::FrameHistory, } diff --git a/egui_glium/Cargo.toml b/egui_glium/Cargo.toml index fc787705..fe44243e 100644 --- a/egui_glium/Cargo.toml +++ b/egui_glium/Cargo.toml @@ -13,17 +13,32 @@ keywords = ["glium", "egui", "gui", "gamedev"] include = [ "**/*.rs", "Cargo.toml"] [dependencies] -chrono = { version = "0.4" } clipboard = "0.5" -directories-next = "2" -egui = { version = "0.6.0", path = "../egui", features = ["serde"] } -epi = { version = "0.6.0", path = "../epi", features = ["serde", "serde_json"] } +egui = { version = "0.6.0", path = "../egui" } +epi = { version = "0.6.0", path = "../epi" } glium = "0.29" -serde = "1" -serde_json = "1" -ureq = { version = "1.5", optional = true } webbrowser = "0.5" +# feature "http": +ureq = { version = "1.5", optional = true } + +# feature "persistence": +directories-next = { version = "2", optional = true } +serde = { version = "1", optional = true } +serde_json = { version = "1", optional = true } + +# feature "time" +chrono = { version = "0.4", optional = true } + [features] default = [] http = ["ureq"] +persistence = [ + "directories-next", + "egui/serde", + "epi/serde_json", + "epi/serde", + "serde_json", + "serde", +] +time = ["chrono"] # for seconds_since_midnight diff --git a/egui_glium/src/backend.rs b/egui_glium/src/backend.rs index c5ff7119..4c1ec502 100644 --- a/egui_glium/src/backend.rs +++ b/egui_glium/src/backend.rs @@ -1,12 +1,32 @@ +use crate::{window_settings::WindowSettings, *}; +use egui::Color32; use std::time::Instant; -use crate::{storage::WindowSettings, *}; - -pub use egui::Color32; - +#[cfg(feature = "persistence")] const EGUI_MEMORY_KEY: &str = "egui"; +#[cfg(feature = "persistence")] const WINDOW_KEY: &str = "window"; +#[cfg(feature = "persistence")] +fn deserialize_window_settings(storage: &Option>) -> Option { + epi::get_value(&**storage.as_ref()?, WINDOW_KEY) +} + +#[cfg(not(feature = "persistence"))] +fn deserialize_window_settings(_: &Option>) -> Option { + None +} + +#[cfg(feature = "persistence")] +fn deserialize_memory(storage: &Option>) -> Option { + epi::get_value(&**storage.as_ref()?, EGUI_MEMORY_KEY) +} + +#[cfg(not(feature = "persistence"))] +fn deserialize_memory(_: &Option>) -> Option { + None +} + impl epi::TextureAllocator for Painter { fn alloc(&mut self) -> egui::TextureId { self.alloc_user_texture() @@ -69,6 +89,12 @@ fn create_display( display } +#[cfg(not(feature = "persistence"))] +fn create_storage(_app_name: &str) -> Option> { + None +} + +#[cfg(feature = "persistence")] fn create_storage(app_name: &str) -> Option> { if let Some(proj_dirs) = directories_next::ProjectDirs::from("", "", app_name) { let data_dir = proj_dirs.data_dir().to_path_buf(); @@ -81,7 +107,7 @@ fn create_storage(app_name: &str) -> Option> { } else { let mut config_dir = data_dir; config_dir.push("app.json"); - let storage = crate::storage::FileStorage::from_path(config_dir); + let storage = crate::persistence::FileStorage::from_path(config_dir); Some(Box::new(storage)) } } else { @@ -97,7 +123,7 @@ fn integration_info( epi::IntegrationInfo { web_info: None, cpu_usage: previous_frame_time, - seconds_since_midnight: Some(seconds_since_midnight()), + seconds_since_midnight: seconds_since_midnight(), native_pixels_per_point: Some(native_pixels_per_point(&display)), } } @@ -110,9 +136,7 @@ pub fn run(mut app: Box) -> ! { app.load(storage.as_ref()); } - let window_settings: Option = storage - .as_mut() - .and_then(|storage| epi::get_value(storage.as_ref(), WINDOW_KEY)); + let window_settings = deserialize_window_settings(&storage); let event_loop = glutin::event_loop::EventLoop::with_user_event(); let display = create_display(app.name(), window_settings, app.is_resizable(), &event_loop); @@ -121,10 +145,7 @@ pub fn run(mut app: Box) -> ! { ))); let mut ctx = egui::CtxRef::default(); - *ctx.memory() = storage - .as_mut() - .and_then(|storage| epi::get_value(storage.as_ref(), EGUI_MEMORY_KEY)) - .unwrap_or_default(); + *ctx.memory() = deserialize_memory(&storage).unwrap_or_default(); app.setup(&ctx); @@ -135,8 +156,10 @@ pub fn run(mut app: Box) -> ! { let mut painter = Painter::new(&display); let mut clipboard = init_clipboard(); + #[cfg(feature = "persistence")] let mut last_auto_save = Instant::now(); + #[cfg(feature = "http")] let http = std::sync::Arc::new(crate::http::GliumHttp {}); if app.warm_up_enabled() { @@ -151,6 +174,7 @@ pub fn run(mut app: Box) -> ! { let mut frame = epi::backend::FrameBuilder { info: integration_info(&display, None), tex_allocator: Some(&mut painter), + #[cfg(feature = "http")] http: http.clone(), output: &mut app_output, repaint_signal: repaint_signal.clone(), @@ -183,6 +207,7 @@ pub fn run(mut app: Box) -> ! { let mut frame = epi::backend::FrameBuilder { info: integration_info(&display, previous_frame_time), tex_allocator: Some(&mut painter), + #[cfg(feature = "http")] http: http.clone(), output: &mut app_output, repaint_signal: repaint_signal.clone(), @@ -236,6 +261,7 @@ pub fn run(mut app: Box) -> ! { handle_output(egui_output, &display, clipboard.as_mut()); + #[cfg(feature = "persistence")] if let Some(storage) = &mut storage { let now = Instant::now(); if now - last_auto_save > app.auto_save_interval() { @@ -265,6 +291,7 @@ pub fn run(mut app: Box) -> ! { } glutin::event::Event::LoopDestroyed => { app.on_exit(); + #[cfg(feature = "persistence")] if let Some(storage) = &mut storage { epi::set_value( storage.as_mut(), diff --git a/egui_glium/src/lib.rs b/egui_glium/src/lib.rs index 3dd38ec6..a4e6d6ac 100644 --- a/egui_glium/src/lib.rs +++ b/egui_glium/src/lib.rs @@ -13,7 +13,9 @@ mod backend; #[cfg(feature = "http")] pub mod http; mod painter; -pub mod storage; +#[cfg(feature = "persistence")] +pub mod persistence; +pub mod window_settings; pub use backend::*; pub use painter::Painter; @@ -279,10 +281,17 @@ pub fn init_clipboard() -> Option { // ---------------------------------------------------------------------------- /// Time of day as seconds since midnight. Used for clock in demo app. -pub fn seconds_since_midnight() -> f64 { - use chrono::Timelike; - let time = chrono::Local::now().time(); - time.num_seconds_from_midnight() as f64 + 1e-9 * (time.nanosecond() as f64) +pub fn seconds_since_midnight() -> Option { + #[cfg(feature = "time")] + { + use chrono::Timelike; + let time = chrono::Local::now().time(); + let seconds_since_midnight = + time.num_seconds_from_midnight() as f64 + 1e-9 * (time.nanosecond() as f64); + Some(seconds_since_midnight) + } + #[cfg(not(feature = "time"))] + None } pub fn screen_size_in_pixels(display: &glium::Display) -> Vec2 { diff --git a/egui_glium/src/persistence.rs b/egui_glium/src/persistence.rs new file mode 100644 index 00000000..2c3ac2ff --- /dev/null +++ b/egui_glium/src/persistence.rs @@ -0,0 +1,87 @@ +use std::{ + collections::HashMap, + path::{Path, PathBuf}, +}; + +// ---------------------------------------------------------------------------- + +/// A key-value store backed by a JSON file on disk. +/// Used to restore egui state, glium window position/size and app state. +pub struct FileStorage { + path: PathBuf, + kv: HashMap, + dirty: bool, +} + +impl FileStorage { + pub fn from_path(path: impl Into) -> Self { + let path: PathBuf = path.into(); + Self { + kv: read_json(&path).unwrap_or_default(), + path, + dirty: false, + } + } +} + +impl epi::Storage for FileStorage { + fn get_string(&self, key: &str) -> Option { + self.kv.get(key).cloned() + } + + fn set_string(&mut self, key: &str, value: String) { + if self.kv.get(key) != Some(&value) { + self.kv.insert(key.to_owned(), value); + self.dirty = true; + } + } + + fn flush(&mut self) { + if self.dirty { + serde_json::to_writer(std::fs::File::create(&self.path).unwrap(), &self.kv).unwrap(); + self.dirty = false; + } + } +} + +// ---------------------------------------------------------------------------- + +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(value) => Some(value), + Err(err) => { + eprintln!("ERROR: Failed to parse json: {}", err); + None + } + } + } + Err(_err) => { + // File probably doesn't exist. That's fine. + None + } + } +} +// ---------------------------------------------------------------------------- + +/// Alternative to `FileStorage` +pub fn read_memory(ctx: &egui::Context, memory_json_path: impl AsRef) { + let memory: Option = read_json(memory_json_path); + if let Some(memory) = memory { + *ctx.memory() = memory; + } +} + +/// Alternative to `FileStorage` +pub fn write_memory( + ctx: &egui::Context, + memory_json_path: impl AsRef, +) -> Result<(), Box> { + serde_json::to_writer_pretty(std::fs::File::create(memory_json_path)?, &*ctx.memory())?; + Ok(()) +} diff --git a/egui_glium/src/storage.rs b/egui_glium/src/storage.rs deleted file mode 100644 index 31bfef81..00000000 --- a/egui_glium/src/storage.rs +++ /dev/null @@ -1,175 +0,0 @@ -use std::{ - collections::HashMap, - path::{Path, PathBuf}, -}; - -// ---------------------------------------------------------------------------- - -/// A key-value store backed by a JSON file on disk. -/// Used to restore egui state, glium window position/size and app state. -pub struct FileStorage { - path: PathBuf, - kv: HashMap, - dirty: bool, -} - -impl FileStorage { - pub fn from_path(path: impl Into) -> Self { - let path: PathBuf = path.into(); - Self { - kv: read_json(&path).unwrap_or_default(), - path, - dirty: false, - } - } -} - -impl epi::Storage for FileStorage { - fn get_string(&self, key: &str) -> Option { - self.kv.get(key).cloned() - } - - fn set_string(&mut self, key: &str, value: String) { - if self.kv.get(key) != Some(&value) { - self.kv.insert(key.to_owned(), value); - self.dirty = true; - } - } - - fn flush(&mut self) { - if self.dirty { - serde_json::to_writer(std::fs::File::create(&self.path).unwrap(), &self.kv).unwrap(); - self.dirty = false; - } - } -} - -// ---------------------------------------------------------------------------- - -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(value) => Some(value), - Err(err) => { - eprintln!("ERROR: Failed to parse json: {}", err); - None - } - } - } - Err(_err) => { - // File probably doesn't exist. That's fine. - None - } - } -} -// ---------------------------------------------------------------------------- - -/// Alternative to `FileStorage` -pub fn read_memory(ctx: &egui::Context, memory_json_path: impl AsRef) { - let memory: Option = read_json(memory_json_path); - if let Some(memory) = memory { - *ctx.memory() = memory; - } -} - -/// Alternative to `FileStorage` -pub fn write_memory( - ctx: &egui::Context, - memory_json_path: impl AsRef, -) -> Result<(), Box> { - serde_json::to_writer_pretty(std::fs::File::create(memory_json_path)?, &*ctx.memory())?; - Ok(()) -} - -// ---------------------------------------------------------------------------- - -use glium::glutin; - -#[derive(Default, serde::Deserialize, serde::Serialize)] -#[serde(default)] -pub struct WindowSettings { - /// outer position of window in physical pixels - pos: Option, - /// Inner size of window in logical pixels - inner_size_points: 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 { - let scale_factor = display.gl_window().window().scale_factor(); - let inner_size_points = display - .gl_window() - .window() - .inner_size() - .to_logical::(scale_factor); - - Self { - pos: display - .gl_window() - .window() - .outer_position() - .ok() - .map(|p| egui::pos2(p.x as f32, p.y as f32)), - - inner_size_points: Some(egui::vec2( - inner_size_points.width as f32, - inner_size_points.height as f32, - )), - } - } - - pub fn initialize_size( - &self, - window: glutin::window::WindowBuilder, - ) -> glutin::window::WindowBuilder { - if let Some(inner_size_points) = self.inner_size_points { - window.with_inner_size(glutin::dpi::LogicalSize { - width: inner_size_points.x as f64, - height: inner_size_points.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, - )); - } - } -} diff --git a/egui_glium/src/window_settings.rs b/egui_glium/src/window_settings.rs new file mode 100644 index 00000000..2feaf901 --- /dev/null +++ b/egui_glium/src/window_settings.rs @@ -0,0 +1,85 @@ +use glium::glutin; + +#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] +pub struct WindowSettings { + /// outer position of window in physical pixels + pos: Option, + /// Inner size of window in logical pixels + inner_size_points: Option, +} + +impl WindowSettings { + #[cfg(feature = "persistence")] + pub fn from_json_file( + settings_json_path: impl AsRef, + ) -> Option { + crate::persistence::read_json(settings_json_path) + } + + pub fn from_display(display: &glium::Display) -> Self { + let scale_factor = display.gl_window().window().scale_factor(); + let inner_size_points = display + .gl_window() + .window() + .inner_size() + .to_logical::(scale_factor); + + Self { + pos: display + .gl_window() + .window() + .outer_position() + .ok() + .map(|p| egui::pos2(p.x as f32, p.y as f32)), + + inner_size_points: Some(egui::vec2( + inner_size_points.width as f32, + inner_size_points.height as f32, + )), + } + } + + pub fn initialize_size( + &self, + window: glutin::window::WindowBuilder, + ) -> glutin::window::WindowBuilder { + if let Some(inner_size_points) = self.inner_size_points { + window.with_inner_size(glutin::dpi::LogicalSize { + width: inner_size_points.x as f64, + height: inner_size_points.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, + )); + } + } +} diff --git a/egui_web/Cargo.toml b/egui_web/Cargo.toml index 25ae9c75..a0e32b84 100644 --- a/egui_web/Cargo.toml +++ b/egui_web/Cargo.toml @@ -16,47 +16,54 @@ include = [ "**/*.rs", "Cargo.toml"] crate-type = ["cdylib", "rlib"] [dependencies] -egui = { version = "0.6.0", path = "../egui", features = ["serde"] } -epi = { version = "0.6.0", path = "../epi", features = ["serde", "serde_json"] } +egui = { version = "0.6.0", path = "../egui" } +epi = { version = "0.6.0", path = "../epi" } js-sys = "0.3" -serde = "1" -serde_json = "1" +serde = { version = "1", optional = true } +serde_json = { version = "1", optional = true } wasm-bindgen = "0.2" wasm-bindgen-futures = "0.4" +[features] +default = [] +http = [ + "epi/http", + "web-sys/Headers", + "web-sys/Request", + "web-sys/RequestInit", + "web-sys/RequestMode", + "web-sys/Response", +] +persistence = ["serde", "serde_json"] + [dependencies.web-sys] version = "0.3" features = [ - 'Clipboard', - 'ClipboardEvent', - 'console', - 'CssStyleDeclaration', - 'DataTransfer', - 'Document', - 'DomRect', - 'Element', - 'Headers', - 'HtmlCanvasElement', - 'HtmlElement', - 'KeyboardEvent', - 'Location', - 'MouseEvent', - 'Navigator', - 'Performance', - 'Request', - 'RequestInit', - 'RequestMode', - 'Response', - 'Storage', - 'Touch', - 'TouchEvent', - 'TouchList', - 'WebGlBuffer', - 'WebGlProgram', - 'WebGlRenderingContext', - 'WebGlShader', - 'WebGlTexture', - 'WebGlUniformLocation', - 'WheelEvent', - 'Window', + "Clipboard", + "ClipboardEvent", + "console", + "CssStyleDeclaration", + "DataTransfer", + "Document", + "DomRect", + "Element", + "HtmlCanvasElement", + "HtmlElement", + "KeyboardEvent", + "Location", + "MouseEvent", + "Navigator", + "Performance", + "Storage", + "Touch", + "TouchEvent", + "TouchList", + "WebGlBuffer", + "WebGlProgram", + "WebGlRenderingContext", + "WebGlShader", + "WebGlTexture", + "WebGlUniformLocation", + "WheelEvent", + "Window", ] diff --git a/egui_web/src/backend.rs b/egui_web/src/backend.rs index f9f4f5c2..8a1bcdea 100644 --- a/egui_web/src/backend.rs +++ b/egui_web/src/backend.rs @@ -1,7 +1,6 @@ use crate::*; pub use egui::{pos2, Color32}; -use http::WebHttp; // ---------------------------------------------------------------------------- @@ -144,7 +143,8 @@ pub struct AppRunner { pub(crate) needs_repaint: std::sync::Arc, storage: LocalStorage, last_save_time: f64, - http: Arc, + #[cfg(feature = "http")] + http: Arc, } impl AppRunner { @@ -160,7 +160,8 @@ impl AppRunner { needs_repaint: Default::default(), storage, last_save_time: now_sec(), - http: Arc::new(WebHttp {}), + #[cfg(feature = "http")] + http: Arc::new(http::WebHttp {}), }) } @@ -210,6 +211,7 @@ impl AppRunner { native_pixels_per_point: Some(native_pixels_per_point()), }, tex_allocator: Some(&mut self.web_backend.painter), + #[cfg(feature = "http")] http: self.http.clone(), output: &mut app_output, repaint_signal: self.needs_repaint.clone(), diff --git a/egui_web/src/lib.rs b/egui_web/src/lib.rs index ad0c2a26..1d610501 100644 --- a/egui_web/src/lib.rs +++ b/egui_web/src/lib.rs @@ -9,6 +9,7 @@ #![warn(clippy::all, rust_2018_idioms)] pub mod backend; +#[cfg(feature = "http")] pub mod http; pub mod webgl; @@ -154,6 +155,7 @@ pub fn local_storage_remove(key: &str) { local_storage().map(|storage| storage.remove_item(key)); } +#[cfg(feature = "persistence")] pub fn load_memory(ctx: &egui::Context) { if let Some(memory_string) = local_storage_get("egui_memory_json") { match serde_json::from_str(&memory_string) { @@ -167,6 +169,10 @@ pub fn load_memory(ctx: &egui::Context) { } } +#[cfg(not(feature = "persistence"))] +pub fn load_memory(_: &egui::Context) {} + +#[cfg(feature = "persistence")] pub fn save_memory(ctx: &egui::Context) { match serde_json::to_string(&*ctx.memory()) { Ok(json) => { @@ -178,6 +184,9 @@ pub fn save_memory(ctx: &egui::Context) { } } +#[cfg(not(feature = "persistence"))] +pub fn save_memory(_: &egui::Context) {} + #[derive(Default)] pub struct LocalStorage {} diff --git a/epi/Cargo.toml b/epi/Cargo.toml index 3b189b6e..ef58e6ed 100644 --- a/epi/Cargo.toml +++ b/epi/Cargo.toml @@ -18,3 +18,8 @@ include = [ "**/*.rs", "Cargo.toml"] egui = { version = "0.6.0", path = "../egui" } serde = { version = "1", optional = true } serde_json = { version = "1", optional = true } + +[features] +default = [] +http = [] +persistence = ["serde", "serde_json"] diff --git a/epi/src/lib.rs b/epi/src/lib.rs index 9b2938f9..71d14832 100644 --- a/epi/src/lib.rs +++ b/epi/src/lib.rs @@ -150,6 +150,7 @@ impl<'a> Frame<'a> { /// Very simple Http fetch API. /// Calls the given callback when done. + #[cfg(feature = "http")] pub fn http_fetch( &self, request: http::Request, @@ -257,6 +258,7 @@ pub const APP_KEY: &str = "app"; // ---------------------------------------------------------------------------- +#[cfg(feature = "http")] /// `epi` supports simple HTTP requests with [`Frame::http_fetch`]. pub mod http { /// A simple http requests. @@ -310,6 +312,7 @@ pub mod backend { use super::*; /// Implements `Http` requests. + #[cfg(feature = "http")] pub trait Http { /// Calls the given callback when done. fn fetch_dyn( @@ -326,6 +329,7 @@ pub mod backend { /// A way to allocate textures (on integrations that support it). pub tex_allocator: Option<&'a mut dyn TextureAllocator>, /// Do http requests. + #[cfg(feature = "http")] pub http: std::sync::Arc, /// Where the app can issue commands back to the integration. pub output: &'a mut AppOutput,