mirror of
https://github.com/emilk/egui.git
synced 2026-08-30 13:20:05 -04:00
[app] unify web and glium demo app
This commit is contained in:
@@ -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();
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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>,
|
||||
Reference in New Issue
Block a user