1
0
mirror of https://github.com/emilk/egui.git synced 2026-08-30 05:10:03 -04:00

[app] unify web and glium demo app

This commit is contained in:
Emil Ernerfeldt
2020-07-23 18:54:16 +02:00
parent b79c76b9ce
commit 554e6e7120
15 changed files with 445 additions and 442 deletions

View File

@@ -1,38 +1,22 @@
use std::time::Instant;
use crate::{
persistence::{Persistence, WindowSettings},
storage::{FileStorage, WindowSettings},
*,
};
pub use egui::app::{App, Backend, RunMode, Storage};
const EGUI_MEMORY_KEY: &str = "egui";
const WINDOW_KEY: &str = "window";
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum RunMode {
/// Uses `request_animation_frame` to repaint the UI on each display Hz.
/// This is good for games and stuff where you want to run logic at e.g. 60 FPS.
Continuous,
/// Only repaint when there are animations or input (mouse movement, keyboard input etc).
Reactive,
}
pub trait App {
/// Called onced per frame for you to draw the UI.
fn ui(&mut self, ui: &mut egui::Ui, runner: &mut Runner);
/// Called once on shutdown. Allows you to save state.
fn on_exit(&mut self, persistence: &mut Persistence);
}
pub struct Runner {
pub struct GliumBackend {
frame_times: egui::MovementTracker<f32>,
quit: bool,
run_mode: RunMode,
}
impl Runner {
impl GliumBackend {
pub fn new(run_mode: RunMode) -> Self {
Self {
frame_times: egui::MovementTracker::new(1000, 1.0),
@@ -40,33 +24,35 @@ impl Runner {
run_mode,
}
}
}
pub fn run_mode(&self) -> RunMode {
impl Backend for GliumBackend {
fn run_mode(&self) -> RunMode {
self.run_mode
}
pub fn set_run_mode(&mut self, run_mode: RunMode) {
fn set_run_mode(&mut self, run_mode: RunMode) {
self.run_mode = run_mode;
}
pub fn quit(&mut self) {
self.quit = true;
}
pub fn cpu_time(&self) -> f32 {
fn cpu_time(&self) -> f32 {
self.frame_times.average().unwrap_or_default()
}
pub fn fps(&self) -> f32 {
fn fps(&self) -> f32 {
1.0 / self.frame_times.mean_time_interval().unwrap_or_default()
}
fn quit(&mut self) {
self.quit = true;
}
}
/// Run an egui app
pub fn run(
title: &str,
run_mode: RunMode,
mut persistence: Persistence,
mut storage: FileStorage,
mut app: impl App + 'static,
) -> ! {
let event_loop = glutin::event_loop::EventLoop::new();
@@ -76,7 +62,7 @@ pub fn run(
.with_title(title)
.with_transparent(false);
let window_settings: Option<WindowSettings> = persistence.get_value(WINDOW_KEY);
let window_settings: Option<WindowSettings> = egui::app::get_value(&storage, WINDOW_KEY);
if let Some(window_settings) = &window_settings {
window = window_settings.initialize_size(window);
}
@@ -93,14 +79,14 @@ pub fn run(
}
let mut ctx = egui::Context::new();
*ctx.memory() = persistence.get_value(EGUI_MEMORY_KEY).unwrap_or_default();
*ctx.memory() = egui::app::get_value(&storage, EGUI_MEMORY_KEY).unwrap_or_default();
let mut painter = Painter::new(&display);
let mut raw_input = make_raw_input(&display);
// used to keep track of time for animations
let start_time = Instant::now();
let mut runner = Runner::new(run_mode);
let mut runner = GliumBackend::new(run_mode);
let mut clipboard = init_clipboard();
event_loop.run(move |event, _, control_flow| {
@@ -134,10 +120,14 @@ pub fn run(
display.gl_window().window().request_redraw(); // TODO: maybe only on some events?
}
glutin::event::Event::LoopDestroyed => {
persistence.set_value(WINDOW_KEY, &WindowSettings::from_display(&display));
persistence.set_value(EGUI_MEMORY_KEY, &*ctx.memory());
app.on_exit(&mut persistence);
persistence.save();
egui::app::set_value(
&mut storage,
WINDOW_KEY,
&WindowSettings::from_display(&display),
);
egui::app::set_value(&mut storage, EGUI_MEMORY_KEY, &*ctx.memory());
app.on_exit(&mut storage);
storage.save();
}
_ => (),
}

View File

@@ -3,12 +3,12 @@
#![allow(clippy::single_match)]
#![allow(deprecated)] // TODO: remove
mod backend;
mod painter;
pub mod persistence;
mod runner;
pub mod storage;
pub use backend::*;
pub use painter::Painter;
pub use runner::*;
use {
clipboard::ClipboardProvider,

View File

@@ -4,13 +4,13 @@ use std::collections::HashMap;
/// 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 Persistence {
pub struct FileStorage {
path: String,
kv: HashMap<String, String>,
dirty: bool,
}
impl Persistence {
impl FileStorage {
pub fn from_path(path: String) -> Self {
Self {
kv: read_json(&path).unwrap_or_default(),
@@ -19,23 +19,6 @@ impl Persistence {
}
}
pub fn get_value<T: serde::de::DeserializeOwned>(&self, key: &str) -> Option<T> {
self.kv
.get(key)
.and_then(|value| serde_json::from_str(value).ok())
}
pub 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;
}
}
pub fn set_value<T: serde::Serialize>(&mut self, key: &str, value: &T) {
self.set_string(key, serde_json::to_string(value).unwrap());
}
pub fn save(&mut self) {
if self.dirty {
serde_json::to_writer(std::fs::File::create(&self.path).unwrap(), &self.kv).unwrap();
@@ -44,6 +27,19 @@ impl Persistence {
}
}
impl egui::app::Storage for FileStorage {
fn get_string(&self, key: &str) -> Option<&str> {
self.kv.get(key).map(String::as_str)
}
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;
}
}
}
// ----------------------------------------------------------------------------
pub fn read_json<T>(memory_json_path: impl AsRef<std::path::Path>) -> Option<T>
@@ -69,7 +65,7 @@ where
}
// ----------------------------------------------------------------------------
/// Alternative to `Persistence`
/// 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 {
@@ -77,7 +73,7 @@ pub fn read_memory(ctx: &egui::Context, memory_json_path: impl AsRef<std::path::
}
}
/// Alternative to `Persistence`
/// Alternative to `FileStorage`
pub fn write_memory(
ctx: &egui::Context,
memory_json_path: impl AsRef<std::path::Path>,