[eframe] Make persistence, http and time optional features
Saves on compile times.
This commit is contained in:
parent
00269f96c0
commit
69d31a5e47
30 changed files with 412 additions and 303 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -692,7 +692,6 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"eframe",
|
"eframe",
|
||||||
"egui_demo_lib",
|
"egui_demo_lib",
|
||||||
"serde",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -704,7 +703,6 @@ dependencies = [
|
||||||
"epi",
|
"epi",
|
||||||
"image",
|
"image",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
|
||||||
"syntect",
|
"syntect",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
8
check.sh
8
check.sh
|
@ -1,15 +1,15 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -eu
|
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 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_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 --all-targets --all-features
|
||||||
cargo test --workspace --doc
|
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:
|
# For finding bloat:
|
||||||
# cargo bloat --release --bin demo_glium -n 200 | rg egui
|
# cargo bloat --release --bin demo_glium -n 200 | rg egui
|
||||||
|
|
||||||
|
|
|
@ -15,13 +15,19 @@ include = [ "**/*.rs", "Cargo.toml"]
|
||||||
[lib]
|
[lib]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
egui = { version = "0.6.0", path = "../egui", features = ["serde"] }
|
egui = { version = "0.6.0", path = "../egui" }
|
||||||
epi = { version = "0.6.0", path = "../epi", features = ["serde", "serde_json"] }
|
epi = { version = "0.6.0", path = "../epi" }
|
||||||
|
|
||||||
# For compiling natively:
|
# For compiling natively:
|
||||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||||
egui_glium = { path = "../egui_glium", features = ["http"] }
|
egui_glium = { path = "../egui_glium" }
|
||||||
|
|
||||||
# For compiling to web:
|
# For compiling to web:
|
||||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||||
egui_web = { path = "../egui_web" }
|
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
|
||||||
|
|
|
@ -9,6 +9,10 @@ edition = "2018"
|
||||||
crate-type = ["cdylib", "rlib"]
|
crate-type = ["cdylib", "rlib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
eframe = { version = "0.6.0", path = "../eframe"}
|
eframe = { version = "0.6.0", path = "../eframe", features = ["time"] }
|
||||||
egui_demo_lib = { version = "0.6.0", path = "../egui_demo_lib"}
|
egui_demo_lib = { version = "0.6.0", path = "../egui_demo_lib" }
|
||||||
serde = { version = "1", features = ["derive"] }
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
http = ["eframe/http", "egui_demo_lib/http"]
|
||||||
|
persistence = ["eframe/persistence", "egui_demo_lib/persistence"]
|
||||||
|
|
|
@ -15,18 +15,24 @@ include = [ "**/*.rs", "Cargo.toml"]
|
||||||
[lib]
|
[lib]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
egui = { version = "0.6.0", path = "../egui", features = ["serde"] }
|
egui = { version = "0.6.0", path = "../egui" }
|
||||||
epi = { version = "0.6.0", path = "../epi", features = ["serde", "serde_json"] }
|
epi = { version = "0.6.0", path = "../epi" }
|
||||||
serde = { version = "1", features = ["derive"] }
|
|
||||||
serde_json = "1"
|
|
||||||
|
|
||||||
# Http fetch app:
|
# feature "http":
|
||||||
image = { version = "0.23", default_features = false, features = ["jpeg", "png"] }
|
image = { version = "0.23", default_features = false, features = ["jpeg", "png"], optional = true }
|
||||||
syntect = { version = "4", default_features = false, features = ["default-fancy"] }
|
syntect = { version = "4", default_features = false, features = ["default-fancy"], optional = true }
|
||||||
|
|
||||||
|
# feature "persistence":
|
||||||
|
serde = { version = "1", features = ["derive"], optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = { version = "0.3", default-features = false }
|
criterion = { version = "0.3", default-features = false }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
http = ["image", "syntect", "epi/http"]
|
||||||
|
persistence = ["epi/persistence", "serde"]
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "benchmark"
|
name = "benchmark"
|
||||||
harness = false
|
harness = false
|
||||||
|
|
|
@ -9,9 +9,9 @@ const RED: Color32 = Color32::RED;
|
||||||
const TRANSPARENT: Color32 = Color32::TRANSPARENT;
|
const TRANSPARENT: Color32 = Color32::TRANSPARENT;
|
||||||
const WHITE: Color32 = Color32::WHITE;
|
const WHITE: Color32 = Color32::WHITE;
|
||||||
|
|
||||||
#[derive(serde::Deserialize, serde::Serialize)]
|
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||||
pub struct ColorTest {
|
pub struct ColorTest {
|
||||||
#[serde(skip)]
|
#[cfg_attr(feature = "persistence", serde(skip))]
|
||||||
tex_mngr: TextureManager,
|
tex_mngr: TextureManager,
|
||||||
vertex_gradients: bool,
|
vertex_gradients: bool,
|
||||||
texture_gradients: bool,
|
texture_gradients: bool,
|
||||||
|
|
|
@ -2,8 +2,9 @@
|
||||||
///
|
///
|
||||||
/// Implements `epi::App` so it can be used with
|
/// 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).
|
/// [`egui_glium`](https://crates.io/crates/egui_glium) and [`egui_web`](https://crates.io/crates/egui_web).
|
||||||
#[derive(Default, serde::Deserialize, serde::Serialize)]
|
#[derive(Default)]
|
||||||
#[serde(default)]
|
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||||
|
#[cfg_attr(feature = "persistence", serde(default))]
|
||||||
pub struct DemoApp {
|
pub struct DemoApp {
|
||||||
demo_windows: super::DemoWindows,
|
demo_windows: super::DemoWindows,
|
||||||
}
|
}
|
||||||
|
@ -13,10 +14,12 @@ impl epi::App for DemoApp {
|
||||||
"✨ Egui Demo"
|
"✨ Egui Demo"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "persistence")]
|
||||||
fn load(&mut self, storage: &dyn epi::Storage) {
|
fn load(&mut self, storage: &dyn epi::Storage) {
|
||||||
*self = epi::get_value(storage, epi::APP_KEY).unwrap_or_default()
|
*self = epi::get_value(storage, epi::APP_KEY).unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "persistence")]
|
||||||
fn save(&mut self, storage: &mut dyn epi::Storage) {
|
fn save(&mut self, storage: &mut dyn epi::Storage) {
|
||||||
epi::set_value(storage, epi::APP_KEY, self);
|
epi::set_value(storage, epi::APP_KEY, self);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use egui::{containers::*, *};
|
use egui::{containers::*, *};
|
||||||
|
|
||||||
#[derive(serde::Deserialize, serde::Serialize)]
|
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||||
#[serde(default)]
|
#[cfg_attr(feature = "persistence", serde(default))]
|
||||||
pub struct DancingStrings {}
|
pub struct DancingStrings {}
|
||||||
|
|
||||||
impl Default for DancingStrings {
|
impl Default for DancingStrings {
|
||||||
|
|
|
@ -2,8 +2,8 @@ use super::*;
|
||||||
use egui::{color::*, *};
|
use egui::{color::*, *};
|
||||||
|
|
||||||
/// Showcase some ui code
|
/// Showcase some ui code
|
||||||
#[derive(serde::Deserialize, serde::Serialize)]
|
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||||
#[serde(default)]
|
#[cfg_attr(feature = "persistence", serde(default))]
|
||||||
pub struct DemoWindow {
|
pub struct DemoWindow {
|
||||||
num_columns: usize,
|
num_columns: usize,
|
||||||
|
|
||||||
|
@ -106,8 +106,9 @@ impl DemoWindow {
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
#[derive(PartialEq, serde::Deserialize, serde::Serialize)]
|
#[derive(PartialEq)]
|
||||||
#[serde(default)]
|
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||||
|
#[cfg_attr(feature = "persistence", serde(default))]
|
||||||
struct ColorWidgets {
|
struct ColorWidgets {
|
||||||
srgba_unmul: [u8; 4],
|
srgba_unmul: [u8; 4],
|
||||||
srgba_premul: [u8; 4],
|
srgba_premul: [u8; 4],
|
||||||
|
@ -176,8 +177,8 @@ impl ColorWidgets {
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
#[derive(serde::Deserialize, serde::Serialize)]
|
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||||
#[serde(default)]
|
#[cfg_attr(feature = "persistence", serde(default))]
|
||||||
struct BoxPainting {
|
struct BoxPainting {
|
||||||
size: Vec2,
|
size: Vec2,
|
||||||
corner_radius: f32,
|
corner_radius: f32,
|
||||||
|
@ -220,8 +221,8 @@ impl BoxPainting {
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
#[derive(serde::Deserialize, serde::Serialize)]
|
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||||
#[serde(default)]
|
#[cfg_attr(feature = "persistence", serde(default))]
|
||||||
struct LayoutDemo {
|
struct LayoutDemo {
|
||||||
// Identical to contents of `egui::Layout`
|
// Identical to contents of `egui::Layout`
|
||||||
main_dir: Direction,
|
main_dir: Direction,
|
||||||
|
@ -363,7 +364,8 @@ enum Action {
|
||||||
Delete,
|
Delete,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Default, serde::Deserialize, serde::Serialize)]
|
#[derive(Clone, Default)]
|
||||||
|
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||||
struct Tree(Vec<Tree>);
|
struct Tree(Vec<Tree>);
|
||||||
|
|
||||||
impl Tree {
|
impl Tree {
|
||||||
|
|
|
@ -2,11 +2,11 @@ use egui::{CtxRef, Resize, ScrollArea, Ui, Window};
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
#[derive(serde::Deserialize, serde::Serialize)]
|
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||||
#[serde(default)]
|
#[cfg_attr(feature = "persistence", serde(default))]
|
||||||
struct Demos {
|
struct Demos {
|
||||||
/// open, view
|
/// open, view
|
||||||
#[serde(skip)] // TODO: serialize the `open` state.
|
#[cfg_attr(feature = "persistence", serde(skip))] // TODO: serialize the `open` state.
|
||||||
demos: Vec<(bool, Box<dyn super::Demo>)>,
|
demos: Vec<(bool, Box<dyn super::Demo>)>,
|
||||||
}
|
}
|
||||||
impl Default for Demos {
|
impl Default for Demos {
|
||||||
|
@ -40,8 +40,9 @@ impl Demos {
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
/// A menu bar in which you can select different demo windows to show.
|
/// A menu bar in which you can select different demo windows to show.
|
||||||
#[derive(Default, serde::Deserialize, serde::Serialize)]
|
#[derive(Default)]
|
||||||
#[serde(default)]
|
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||||
|
#[cfg_attr(feature = "persistence", serde(default))]
|
||||||
pub struct DemoWindows {
|
pub struct DemoWindows {
|
||||||
open_windows: OpenWindows,
|
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 {
|
struct OpenWindows {
|
||||||
demo: bool,
|
demo: bool,
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use egui::*;
|
use egui::*;
|
||||||
|
|
||||||
#[derive(serde::Deserialize, serde::Serialize)]
|
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||||
#[serde(default)]
|
#[cfg_attr(feature = "persistence", serde(default))]
|
||||||
pub struct Painting {
|
pub struct Painting {
|
||||||
lines: Vec<Vec<Vec2>>,
|
lines: Vec<Vec<Vec2>>,
|
||||||
stroke: Stroke,
|
stroke: Stroke,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use egui::{color::*, *};
|
use egui::{color::*, *};
|
||||||
|
|
||||||
#[derive(serde::Deserialize, serde::Serialize)]
|
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||||
#[serde(default)]
|
#[cfg_attr(feature = "persistence", serde(default))]
|
||||||
pub struct Scrolls {
|
pub struct Scrolls {
|
||||||
track_item: usize,
|
track_item: usize,
|
||||||
tracking: bool,
|
tracking: bool,
|
||||||
|
|
|
@ -2,8 +2,9 @@ use egui::*;
|
||||||
use std::f64::INFINITY;
|
use std::f64::INFINITY;
|
||||||
|
|
||||||
/// Showcase sliders
|
/// Showcase sliders
|
||||||
#[derive(PartialEq, serde::Deserialize, serde::Serialize)]
|
#[derive(PartialEq)]
|
||||||
#[serde(default)]
|
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||||
|
#[cfg_attr(feature = "persistence", serde(default))]
|
||||||
pub struct Sliders {
|
pub struct Sliders {
|
||||||
pub min: f64,
|
pub min: f64,
|
||||||
pub max: f64,
|
pub max: f64,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use egui::{color::*, *};
|
use egui::{color::*, *};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||||
enum Enum {
|
enum Enum {
|
||||||
First,
|
First,
|
||||||
Second,
|
Second,
|
||||||
|
@ -13,8 +14,8 @@ impl Default for Enum {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(serde::Deserialize, serde::Serialize)]
|
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||||
#[serde(default)]
|
#[cfg_attr(feature = "persistence", serde(default))]
|
||||||
pub struct Widgets {
|
pub struct Widgets {
|
||||||
button_enabled: bool,
|
button_enabled: bool,
|
||||||
count: usize,
|
count: usize,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::__egui_github_link_file;
|
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 {
|
pub struct WindowOptions {
|
||||||
title: String,
|
title: String,
|
||||||
title_bar: bool,
|
title_bar: bool,
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
use egui::{containers::*, widgets::*, *};
|
use egui::{containers::*, widgets::*, *};
|
||||||
use std::f32::consts::TAU;
|
use std::f32::consts::TAU;
|
||||||
|
|
||||||
#[derive(PartialEq, serde::Deserialize, serde::Serialize)]
|
#[derive(PartialEq)]
|
||||||
#[serde(default)]
|
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||||
|
#[cfg_attr(feature = "persistence", serde(default))]
|
||||||
pub struct FractalClock {
|
pub struct FractalClock {
|
||||||
paused: bool,
|
paused: bool,
|
||||||
time: f64,
|
time: f64,
|
||||||
|
|
|
@ -30,17 +30,17 @@ impl Resource {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(serde::Deserialize, serde::Serialize)]
|
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||||
pub struct HttpApp {
|
pub struct HttpApp {
|
||||||
url: String,
|
url: String,
|
||||||
|
|
||||||
#[serde(skip)]
|
#[cfg_attr(feature = "persistence", serde(skip))]
|
||||||
in_progress: Option<Receiver<Result<Response, String>>>,
|
in_progress: Option<Receiver<Result<Response, String>>>,
|
||||||
|
|
||||||
#[serde(skip)]
|
#[cfg_attr(feature = "persistence", serde(skip))]
|
||||||
result: Option<Result<Resource, String>>,
|
result: Option<Result<Resource, String>>,
|
||||||
|
|
||||||
#[serde(skip)]
|
#[cfg_attr(feature = "persistence", serde(skip))]
|
||||||
tex_mngr: TexMngr,
|
tex_mngr: TexMngr,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
mod color_test;
|
mod color_test;
|
||||||
mod demo;
|
mod demo;
|
||||||
mod fractal_clock;
|
mod fractal_clock;
|
||||||
|
#[cfg(feature = "http")]
|
||||||
mod http_app;
|
mod http_app;
|
||||||
|
|
||||||
pub use color_test::ColorTest;
|
pub use color_test::ColorTest;
|
||||||
pub use demo::DemoApp;
|
pub use demo::DemoApp;
|
||||||
pub use fractal_clock::FractalClock;
|
pub use fractal_clock::FractalClock;
|
||||||
|
#[cfg(feature = "http")]
|
||||||
pub use http_app::HttpApp;
|
pub use http_app::HttpApp;
|
||||||
|
|
||||||
pub use demo::DemoWindows; // used for tests
|
pub use demo::DemoWindows; // used for tests
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
/// All the different demo apps.
|
/// All the different demo apps.
|
||||||
#[derive(Default, serde::Deserialize, serde::Serialize)]
|
#[derive(Default)]
|
||||||
#[serde(default)]
|
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||||
|
#[cfg_attr(feature = "persistence", serde(default))]
|
||||||
pub struct Apps {
|
pub struct Apps {
|
||||||
demo: crate::apps::DemoApp,
|
demo: crate::apps::DemoApp,
|
||||||
|
#[cfg(feature = "http")]
|
||||||
http: crate::apps::HttpApp,
|
http: crate::apps::HttpApp,
|
||||||
clock: crate::apps::FractalClock,
|
clock: crate::apps::FractalClock,
|
||||||
color_test: crate::apps::ColorTest,
|
color_test: crate::apps::ColorTest,
|
||||||
|
@ -12,6 +14,7 @@ impl Apps {
|
||||||
fn iter_mut(&mut self) -> impl Iterator<Item = (&str, &mut dyn epi::App)> {
|
fn iter_mut(&mut self) -> impl Iterator<Item = (&str, &mut dyn epi::App)> {
|
||||||
vec![
|
vec![
|
||||||
("demo", &mut self.demo as &mut dyn epi::App),
|
("demo", &mut self.demo as &mut dyn epi::App),
|
||||||
|
#[cfg(feature = "http")]
|
||||||
("http", &mut self.http as &mut dyn epi::App),
|
("http", &mut self.http as &mut dyn epi::App),
|
||||||
("clock", &mut self.clock as &mut dyn epi::App),
|
("clock", &mut self.clock as &mut dyn epi::App),
|
||||||
("colors", &mut self.color_test 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.
|
/// Wraps many demo/test apps into one.
|
||||||
#[derive(Default, serde::Deserialize, serde::Serialize)]
|
#[derive(Default)]
|
||||||
#[serde(default)]
|
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||||
|
#[cfg_attr(feature = "persistence", serde(default))]
|
||||||
pub struct WrapApp {
|
pub struct WrapApp {
|
||||||
selected_anchor: String,
|
selected_anchor: String,
|
||||||
apps: Apps,
|
apps: Apps,
|
||||||
|
@ -34,10 +38,12 @@ impl epi::App for WrapApp {
|
||||||
"Egui Demo Apps"
|
"Egui Demo Apps"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "persistence")]
|
||||||
fn load(&mut self, storage: &dyn epi::Storage) {
|
fn load(&mut self, storage: &dyn epi::Storage) {
|
||||||
*self = epi::get_value(storage, epi::APP_KEY).unwrap_or_default()
|
*self = epi::get_value(storage, epi::APP_KEY).unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "persistence")]
|
||||||
fn save(&mut self, storage: &mut dyn epi::Storage) {
|
fn save(&mut self, storage: &mut dyn epi::Storage) {
|
||||||
epi::set_value(storage, epi::APP_KEY, self);
|
epi::set_value(storage, epi::APP_KEY, self);
|
||||||
}
|
}
|
||||||
|
@ -167,18 +173,20 @@ impl Default for RunMode {
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
#[derive(Default, serde::Deserialize, serde::Serialize)]
|
#[derive(Default)]
|
||||||
#[serde(default)]
|
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||||
|
#[cfg_attr(feature = "persistence", serde(default))]
|
||||||
struct BackendPanel {
|
struct BackendPanel {
|
||||||
open: bool,
|
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,
|
run_mode: RunMode,
|
||||||
|
|
||||||
/// current slider value for current gui scale
|
/// current slider value for current gui scale
|
||||||
pixels_per_point: Option<f32>,
|
pixels_per_point: Option<f32>,
|
||||||
|
|
||||||
#[serde(skip)]
|
#[cfg_attr(feature = "persistence", serde(skip))]
|
||||||
frame_history: crate::frame_history::FrameHistory,
|
frame_history: crate::frame_history::FrameHistory,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,17 +13,32 @@ keywords = ["glium", "egui", "gui", "gamedev"]
|
||||||
include = [ "**/*.rs", "Cargo.toml"]
|
include = [ "**/*.rs", "Cargo.toml"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chrono = { version = "0.4" }
|
|
||||||
clipboard = "0.5"
|
clipboard = "0.5"
|
||||||
directories-next = "2"
|
egui = { version = "0.6.0", path = "../egui" }
|
||||||
egui = { version = "0.6.0", path = "../egui", features = ["serde"] }
|
epi = { version = "0.6.0", path = "../epi" }
|
||||||
epi = { version = "0.6.0", path = "../epi", features = ["serde", "serde_json"] }
|
|
||||||
glium = "0.29"
|
glium = "0.29"
|
||||||
serde = "1"
|
|
||||||
serde_json = "1"
|
|
||||||
ureq = { version = "1.5", optional = true }
|
|
||||||
webbrowser = "0.5"
|
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]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
http = ["ureq"]
|
http = ["ureq"]
|
||||||
|
persistence = [
|
||||||
|
"directories-next",
|
||||||
|
"egui/serde",
|
||||||
|
"epi/serde_json",
|
||||||
|
"epi/serde",
|
||||||
|
"serde_json",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
time = ["chrono"] # for seconds_since_midnight
|
||||||
|
|
|
@ -1,12 +1,32 @@
|
||||||
|
use crate::{window_settings::WindowSettings, *};
|
||||||
|
use egui::Color32;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
use crate::{storage::WindowSettings, *};
|
#[cfg(feature = "persistence")]
|
||||||
|
|
||||||
pub use egui::Color32;
|
|
||||||
|
|
||||||
const EGUI_MEMORY_KEY: &str = "egui";
|
const EGUI_MEMORY_KEY: &str = "egui";
|
||||||
|
#[cfg(feature = "persistence")]
|
||||||
const WINDOW_KEY: &str = "window";
|
const WINDOW_KEY: &str = "window";
|
||||||
|
|
||||||
|
#[cfg(feature = "persistence")]
|
||||||
|
fn deserialize_window_settings(storage: &Option<Box<dyn epi::Storage>>) -> Option<WindowSettings> {
|
||||||
|
epi::get_value(&**storage.as_ref()?, WINDOW_KEY)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "persistence"))]
|
||||||
|
fn deserialize_window_settings(_: &Option<Box<dyn epi::Storage>>) -> Option<WindowSettings> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "persistence")]
|
||||||
|
fn deserialize_memory(storage: &Option<Box<dyn epi::Storage>>) -> Option<egui::Memory> {
|
||||||
|
epi::get_value(&**storage.as_ref()?, EGUI_MEMORY_KEY)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "persistence"))]
|
||||||
|
fn deserialize_memory(_: &Option<Box<dyn epi::Storage>>) -> Option<egui::Memory> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
impl epi::TextureAllocator for Painter {
|
impl epi::TextureAllocator for Painter {
|
||||||
fn alloc(&mut self) -> egui::TextureId {
|
fn alloc(&mut self) -> egui::TextureId {
|
||||||
self.alloc_user_texture()
|
self.alloc_user_texture()
|
||||||
|
@ -69,6 +89,12 @@ fn create_display(
|
||||||
display
|
display
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "persistence"))]
|
||||||
|
fn create_storage(_app_name: &str) -> Option<Box<dyn epi::Storage>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "persistence")]
|
||||||
fn create_storage(app_name: &str) -> Option<Box<dyn epi::Storage>> {
|
fn create_storage(app_name: &str) -> Option<Box<dyn epi::Storage>> {
|
||||||
if let Some(proj_dirs) = directories_next::ProjectDirs::from("", "", app_name) {
|
if let Some(proj_dirs) = directories_next::ProjectDirs::from("", "", app_name) {
|
||||||
let data_dir = proj_dirs.data_dir().to_path_buf();
|
let data_dir = proj_dirs.data_dir().to_path_buf();
|
||||||
|
@ -81,7 +107,7 @@ fn create_storage(app_name: &str) -> Option<Box<dyn epi::Storage>> {
|
||||||
} else {
|
} else {
|
||||||
let mut config_dir = data_dir;
|
let mut config_dir = data_dir;
|
||||||
config_dir.push("app.json");
|
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))
|
Some(Box::new(storage))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -97,7 +123,7 @@ fn integration_info(
|
||||||
epi::IntegrationInfo {
|
epi::IntegrationInfo {
|
||||||
web_info: None,
|
web_info: None,
|
||||||
cpu_usage: previous_frame_time,
|
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)),
|
native_pixels_per_point: Some(native_pixels_per_point(&display)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,9 +136,7 @@ pub fn run(mut app: Box<dyn epi::App>) -> ! {
|
||||||
app.load(storage.as_ref());
|
app.load(storage.as_ref());
|
||||||
}
|
}
|
||||||
|
|
||||||
let window_settings: Option<WindowSettings> = storage
|
let window_settings = deserialize_window_settings(&storage);
|
||||||
.as_mut()
|
|
||||||
.and_then(|storage| epi::get_value(storage.as_ref(), WINDOW_KEY));
|
|
||||||
let event_loop = glutin::event_loop::EventLoop::with_user_event();
|
let event_loop = glutin::event_loop::EventLoop::with_user_event();
|
||||||
let display = create_display(app.name(), window_settings, app.is_resizable(), &event_loop);
|
let display = create_display(app.name(), window_settings, app.is_resizable(), &event_loop);
|
||||||
|
|
||||||
|
@ -121,10 +145,7 @@ pub fn run(mut app: Box<dyn epi::App>) -> ! {
|
||||||
)));
|
)));
|
||||||
|
|
||||||
let mut ctx = egui::CtxRef::default();
|
let mut ctx = egui::CtxRef::default();
|
||||||
*ctx.memory() = storage
|
*ctx.memory() = deserialize_memory(&storage).unwrap_or_default();
|
||||||
.as_mut()
|
|
||||||
.and_then(|storage| epi::get_value(storage.as_ref(), EGUI_MEMORY_KEY))
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
app.setup(&ctx);
|
app.setup(&ctx);
|
||||||
|
|
||||||
|
@ -135,8 +156,10 @@ pub fn run(mut app: Box<dyn epi::App>) -> ! {
|
||||||
let mut painter = Painter::new(&display);
|
let mut painter = Painter::new(&display);
|
||||||
let mut clipboard = init_clipboard();
|
let mut clipboard = init_clipboard();
|
||||||
|
|
||||||
|
#[cfg(feature = "persistence")]
|
||||||
let mut last_auto_save = Instant::now();
|
let mut last_auto_save = Instant::now();
|
||||||
|
|
||||||
|
#[cfg(feature = "http")]
|
||||||
let http = std::sync::Arc::new(crate::http::GliumHttp {});
|
let http = std::sync::Arc::new(crate::http::GliumHttp {});
|
||||||
|
|
||||||
if app.warm_up_enabled() {
|
if app.warm_up_enabled() {
|
||||||
|
@ -151,6 +174,7 @@ pub fn run(mut app: Box<dyn epi::App>) -> ! {
|
||||||
let mut frame = epi::backend::FrameBuilder {
|
let mut frame = epi::backend::FrameBuilder {
|
||||||
info: integration_info(&display, None),
|
info: integration_info(&display, None),
|
||||||
tex_allocator: Some(&mut painter),
|
tex_allocator: Some(&mut painter),
|
||||||
|
#[cfg(feature = "http")]
|
||||||
http: http.clone(),
|
http: http.clone(),
|
||||||
output: &mut app_output,
|
output: &mut app_output,
|
||||||
repaint_signal: repaint_signal.clone(),
|
repaint_signal: repaint_signal.clone(),
|
||||||
|
@ -183,6 +207,7 @@ pub fn run(mut app: Box<dyn epi::App>) -> ! {
|
||||||
let mut frame = epi::backend::FrameBuilder {
|
let mut frame = epi::backend::FrameBuilder {
|
||||||
info: integration_info(&display, previous_frame_time),
|
info: integration_info(&display, previous_frame_time),
|
||||||
tex_allocator: Some(&mut painter),
|
tex_allocator: Some(&mut painter),
|
||||||
|
#[cfg(feature = "http")]
|
||||||
http: http.clone(),
|
http: http.clone(),
|
||||||
output: &mut app_output,
|
output: &mut app_output,
|
||||||
repaint_signal: repaint_signal.clone(),
|
repaint_signal: repaint_signal.clone(),
|
||||||
|
@ -236,6 +261,7 @@ pub fn run(mut app: Box<dyn epi::App>) -> ! {
|
||||||
|
|
||||||
handle_output(egui_output, &display, clipboard.as_mut());
|
handle_output(egui_output, &display, clipboard.as_mut());
|
||||||
|
|
||||||
|
#[cfg(feature = "persistence")]
|
||||||
if let Some(storage) = &mut storage {
|
if let Some(storage) = &mut storage {
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
if now - last_auto_save > app.auto_save_interval() {
|
if now - last_auto_save > app.auto_save_interval() {
|
||||||
|
@ -265,6 +291,7 @@ pub fn run(mut app: Box<dyn epi::App>) -> ! {
|
||||||
}
|
}
|
||||||
glutin::event::Event::LoopDestroyed => {
|
glutin::event::Event::LoopDestroyed => {
|
||||||
app.on_exit();
|
app.on_exit();
|
||||||
|
#[cfg(feature = "persistence")]
|
||||||
if let Some(storage) = &mut storage {
|
if let Some(storage) = &mut storage {
|
||||||
epi::set_value(
|
epi::set_value(
|
||||||
storage.as_mut(),
|
storage.as_mut(),
|
||||||
|
|
|
@ -13,7 +13,9 @@ mod backend;
|
||||||
#[cfg(feature = "http")]
|
#[cfg(feature = "http")]
|
||||||
pub mod http;
|
pub mod http;
|
||||||
mod painter;
|
mod painter;
|
||||||
pub mod storage;
|
#[cfg(feature = "persistence")]
|
||||||
|
pub mod persistence;
|
||||||
|
pub mod window_settings;
|
||||||
|
|
||||||
pub use backend::*;
|
pub use backend::*;
|
||||||
pub use painter::Painter;
|
pub use painter::Painter;
|
||||||
|
@ -279,10 +281,17 @@ pub fn init_clipboard() -> Option<ClipboardContext> {
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
/// Time of day as seconds since midnight. Used for clock in demo app.
|
/// Time of day as seconds since midnight. Used for clock in demo app.
|
||||||
pub fn seconds_since_midnight() -> f64 {
|
pub fn seconds_since_midnight() -> Option<f64> {
|
||||||
|
#[cfg(feature = "time")]
|
||||||
|
{
|
||||||
use chrono::Timelike;
|
use chrono::Timelike;
|
||||||
let time = chrono::Local::now().time();
|
let time = chrono::Local::now().time();
|
||||||
time.num_seconds_from_midnight() as f64 + 1e-9 * (time.nanosecond() as f64)
|
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 {
|
pub fn screen_size_in_pixels(display: &glium::Display) -> Vec2 {
|
||||||
|
|
87
egui_glium/src/persistence.rs
Normal file
87
egui_glium/src/persistence.rs
Normal file
|
@ -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<String, String>,
|
||||||
|
dirty: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FileStorage {
|
||||||
|
pub fn from_path(path: impl Into<PathBuf>) -> 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<String> {
|
||||||
|
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<T>(memory_json_path: impl AsRef<Path>) -> Option<T>
|
||||||
|
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<std::path::Path>) {
|
||||||
|
let memory: Option<egui::Memory> = 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<std::path::Path>,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
serde_json::to_writer_pretty(std::fs::File::create(memory_json_path)?, &*ctx.memory())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -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<String, String>,
|
|
||||||
dirty: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FileStorage {
|
|
||||||
pub fn from_path(path: impl Into<PathBuf>) -> 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<String> {
|
|
||||||
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<T>(memory_json_path: impl AsRef<Path>) -> Option<T>
|
|
||||||
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<std::path::Path>) {
|
|
||||||
let memory: Option<egui::Memory> = 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<std::path::Path>,
|
|
||||||
) -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
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<egui::Pos2>,
|
|
||||||
/// Inner size of window in logical pixels
|
|
||||||
inner_size_points: Option<egui::Vec2>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WindowSettings {
|
|
||||||
pub fn from_json_file(
|
|
||||||
settings_json_path: impl AsRef<std::path::Path>,
|
|
||||||
) -> Option<WindowSettings> {
|
|
||||||
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::<f32>(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,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
85
egui_glium/src/window_settings.rs
Normal file
85
egui_glium/src/window_settings.rs
Normal file
|
@ -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<egui::Pos2>,
|
||||||
|
/// Inner size of window in logical pixels
|
||||||
|
inner_size_points: Option<egui::Vec2>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WindowSettings {
|
||||||
|
#[cfg(feature = "persistence")]
|
||||||
|
pub fn from_json_file(
|
||||||
|
settings_json_path: impl AsRef<std::path::Path>,
|
||||||
|
) -> Option<WindowSettings> {
|
||||||
|
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::<f32>(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,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,47 +16,54 @@ include = [ "**/*.rs", "Cargo.toml"]
|
||||||
crate-type = ["cdylib", "rlib"]
|
crate-type = ["cdylib", "rlib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
egui = { version = "0.6.0", path = "../egui", features = ["serde"] }
|
egui = { version = "0.6.0", path = "../egui" }
|
||||||
epi = { version = "0.6.0", path = "../epi", features = ["serde", "serde_json"] }
|
epi = { version = "0.6.0", path = "../epi" }
|
||||||
js-sys = "0.3"
|
js-sys = "0.3"
|
||||||
serde = "1"
|
serde = { version = "1", optional = true }
|
||||||
serde_json = "1"
|
serde_json = { version = "1", optional = true }
|
||||||
wasm-bindgen = "0.2"
|
wasm-bindgen = "0.2"
|
||||||
wasm-bindgen-futures = "0.4"
|
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]
|
[dependencies.web-sys]
|
||||||
version = "0.3"
|
version = "0.3"
|
||||||
features = [
|
features = [
|
||||||
'Clipboard',
|
"Clipboard",
|
||||||
'ClipboardEvent',
|
"ClipboardEvent",
|
||||||
'console',
|
"console",
|
||||||
'CssStyleDeclaration',
|
"CssStyleDeclaration",
|
||||||
'DataTransfer',
|
"DataTransfer",
|
||||||
'Document',
|
"Document",
|
||||||
'DomRect',
|
"DomRect",
|
||||||
'Element',
|
"Element",
|
||||||
'Headers',
|
"HtmlCanvasElement",
|
||||||
'HtmlCanvasElement',
|
"HtmlElement",
|
||||||
'HtmlElement',
|
"KeyboardEvent",
|
||||||
'KeyboardEvent',
|
"Location",
|
||||||
'Location',
|
"MouseEvent",
|
||||||
'MouseEvent',
|
"Navigator",
|
||||||
'Navigator',
|
"Performance",
|
||||||
'Performance',
|
"Storage",
|
||||||
'Request',
|
"Touch",
|
||||||
'RequestInit',
|
"TouchEvent",
|
||||||
'RequestMode',
|
"TouchList",
|
||||||
'Response',
|
"WebGlBuffer",
|
||||||
'Storage',
|
"WebGlProgram",
|
||||||
'Touch',
|
"WebGlRenderingContext",
|
||||||
'TouchEvent',
|
"WebGlShader",
|
||||||
'TouchList',
|
"WebGlTexture",
|
||||||
'WebGlBuffer',
|
"WebGlUniformLocation",
|
||||||
'WebGlProgram',
|
"WheelEvent",
|
||||||
'WebGlRenderingContext',
|
"Window",
|
||||||
'WebGlShader',
|
|
||||||
'WebGlTexture',
|
|
||||||
'WebGlUniformLocation',
|
|
||||||
'WheelEvent',
|
|
||||||
'Window',
|
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
pub use egui::{pos2, Color32};
|
pub use egui::{pos2, Color32};
|
||||||
use http::WebHttp;
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -144,7 +143,8 @@ pub struct AppRunner {
|
||||||
pub(crate) needs_repaint: std::sync::Arc<NeedRepaint>,
|
pub(crate) needs_repaint: std::sync::Arc<NeedRepaint>,
|
||||||
storage: LocalStorage,
|
storage: LocalStorage,
|
||||||
last_save_time: f64,
|
last_save_time: f64,
|
||||||
http: Arc<WebHttp>,
|
#[cfg(feature = "http")]
|
||||||
|
http: Arc<http::WebHttp>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AppRunner {
|
impl AppRunner {
|
||||||
|
@ -160,7 +160,8 @@ impl AppRunner {
|
||||||
needs_repaint: Default::default(),
|
needs_repaint: Default::default(),
|
||||||
storage,
|
storage,
|
||||||
last_save_time: now_sec(),
|
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()),
|
native_pixels_per_point: Some(native_pixels_per_point()),
|
||||||
},
|
},
|
||||||
tex_allocator: Some(&mut self.web_backend.painter),
|
tex_allocator: Some(&mut self.web_backend.painter),
|
||||||
|
#[cfg(feature = "http")]
|
||||||
http: self.http.clone(),
|
http: self.http.clone(),
|
||||||
output: &mut app_output,
|
output: &mut app_output,
|
||||||
repaint_signal: self.needs_repaint.clone(),
|
repaint_signal: self.needs_repaint.clone(),
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#![warn(clippy::all, rust_2018_idioms)]
|
#![warn(clippy::all, rust_2018_idioms)]
|
||||||
|
|
||||||
pub mod backend;
|
pub mod backend;
|
||||||
|
#[cfg(feature = "http")]
|
||||||
pub mod http;
|
pub mod http;
|
||||||
pub mod webgl;
|
pub mod webgl;
|
||||||
|
|
||||||
|
@ -154,6 +155,7 @@ pub fn local_storage_remove(key: &str) {
|
||||||
local_storage().map(|storage| storage.remove_item(key));
|
local_storage().map(|storage| storage.remove_item(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "persistence")]
|
||||||
pub fn load_memory(ctx: &egui::Context) {
|
pub fn load_memory(ctx: &egui::Context) {
|
||||||
if let Some(memory_string) = local_storage_get("egui_memory_json") {
|
if let Some(memory_string) = local_storage_get("egui_memory_json") {
|
||||||
match serde_json::from_str(&memory_string) {
|
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) {
|
pub fn save_memory(ctx: &egui::Context) {
|
||||||
match serde_json::to_string(&*ctx.memory()) {
|
match serde_json::to_string(&*ctx.memory()) {
|
||||||
Ok(json) => {
|
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)]
|
#[derive(Default)]
|
||||||
pub struct LocalStorage {}
|
pub struct LocalStorage {}
|
||||||
|
|
||||||
|
|
|
@ -18,3 +18,8 @@ include = [ "**/*.rs", "Cargo.toml"]
|
||||||
egui = { version = "0.6.0", path = "../egui" }
|
egui = { version = "0.6.0", path = "../egui" }
|
||||||
serde = { version = "1", optional = true }
|
serde = { version = "1", optional = true }
|
||||||
serde_json = { version = "1", optional = true }
|
serde_json = { version = "1", optional = true }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
http = []
|
||||||
|
persistence = ["serde", "serde_json"]
|
||||||
|
|
|
@ -150,6 +150,7 @@ impl<'a> Frame<'a> {
|
||||||
|
|
||||||
/// Very simple Http fetch API.
|
/// Very simple Http fetch API.
|
||||||
/// Calls the given callback when done.
|
/// Calls the given callback when done.
|
||||||
|
#[cfg(feature = "http")]
|
||||||
pub fn http_fetch(
|
pub fn http_fetch(
|
||||||
&self,
|
&self,
|
||||||
request: http::Request,
|
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`].
|
/// `epi` supports simple HTTP requests with [`Frame::http_fetch`].
|
||||||
pub mod http {
|
pub mod http {
|
||||||
/// A simple http requests.
|
/// A simple http requests.
|
||||||
|
@ -310,6 +312,7 @@ pub mod backend {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
/// Implements `Http` requests.
|
/// Implements `Http` requests.
|
||||||
|
#[cfg(feature = "http")]
|
||||||
pub trait Http {
|
pub trait Http {
|
||||||
/// Calls the given callback when done.
|
/// Calls the given callback when done.
|
||||||
fn fetch_dyn(
|
fn fetch_dyn(
|
||||||
|
@ -326,6 +329,7 @@ pub mod backend {
|
||||||
/// A way to allocate textures (on integrations that support it).
|
/// A way to allocate textures (on integrations that support it).
|
||||||
pub tex_allocator: Option<&'a mut dyn TextureAllocator>,
|
pub tex_allocator: Option<&'a mut dyn TextureAllocator>,
|
||||||
/// Do http requests.
|
/// Do http requests.
|
||||||
|
#[cfg(feature = "http")]
|
||||||
pub http: std::sync::Arc<dyn backend::Http>,
|
pub http: std::sync::Arc<dyn backend::Http>,
|
||||||
/// Where the app can issue commands back to the integration.
|
/// Where the app can issue commands back to the integration.
|
||||||
pub output: &'a mut AppOutput,
|
pub output: &'a mut AppOutput,
|
||||||
|
|
Loading…
Reference in a new issue