epi: merge App::load into App::setup, and provide Frame argument
This gives users more control over the order of load/setup. It also allows users to load textures in setup.
This commit is contained in:
parent
31769d400f
commit
44b573f6a6
7 changed files with 105 additions and 53 deletions
|
@ -3,6 +3,8 @@ All notable changes to the `eframe` crate.
|
||||||
|
|
||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
* `App::setup` now takes a `Frame` and `Storage` by argument.
|
||||||
|
* `App::load` has been removed. Implement `App::setup` instead.
|
||||||
|
|
||||||
|
|
||||||
## 0.12.0 - 2021-05-10
|
## 0.12.0 - 2021-05-10
|
||||||
|
|
|
@ -15,8 +15,15 @@ impl epi::App for DemoApp {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "persistence")]
|
#[cfg(feature = "persistence")]
|
||||||
fn load(&mut self, storage: &dyn epi::Storage) {
|
fn setup(
|
||||||
*self = epi::get_value(storage, epi::APP_KEY).unwrap_or_default()
|
&mut self,
|
||||||
|
_ctx: &egui::CtxRef,
|
||||||
|
_frame: &mut epi::Frame<'_>,
|
||||||
|
storage: Option<&dyn epi::Storage>,
|
||||||
|
) {
|
||||||
|
if let Some(storage) = storage {
|
||||||
|
*self = epi::get_value(storage, epi::APP_KEY).unwrap_or_default()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "persistence")]
|
#[cfg(feature = "persistence")]
|
||||||
|
|
|
@ -40,9 +40,16 @@ impl epi::App for WrapApp {
|
||||||
"egui demo apps"
|
"egui demo apps"
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "persistence")]
|
fn setup(
|
||||||
fn load(&mut self, storage: &dyn epi::Storage) {
|
&mut self,
|
||||||
*self = epi::get_value(storage, epi::APP_KEY).unwrap_or_default()
|
ctx: &egui::CtxRef,
|
||||||
|
frame: &mut epi::Frame<'_>,
|
||||||
|
storage: Option<&dyn epi::Storage>,
|
||||||
|
) {
|
||||||
|
#[cfg(feature = "persistence")]
|
||||||
|
if let Some(storage) = storage {
|
||||||
|
*self = epi::get_value(storage, epi::APP_KEY).unwrap_or_default()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "persistence")]
|
#[cfg(feature = "persistence")]
|
||||||
|
|
|
@ -35,16 +35,16 @@ All notable changes to the `egui_glium` integration will be noted in this file.
|
||||||
|
|
||||||
|
|
||||||
## 0.7.0 - 2021-01-04
|
## 0.7.0 - 2021-01-04
|
||||||
### Changed
|
### Changed 🔧
|
||||||
* `http` `persistence` and `time` are now optional (and opt-in) features.
|
* `http` `persistence` and `time` are now optional (and opt-in) features.
|
||||||
|
|
||||||
|
|
||||||
## 0.6.0 - 2020-12-26
|
## 0.6.0 - 2020-12-26
|
||||||
### Added
|
### Added ⭐
|
||||||
* `egui_glium` will auto-save your app state every 30 seconds.
|
* `egui_glium` will auto-save your app state every 30 seconds.
|
||||||
* `egui_glium` can now set windows as fixed size (e.g. the user can't resize the window). See `egui::App::is_resizable()`.
|
* `egui_glium` can now set windows as fixed size (e.g. the user can't resize the window). See `egui::App::is_resizable()`.
|
||||||
|
|
||||||
### Changed
|
### Changed 🔧
|
||||||
* `egui_glium` will now save you app state to [a better directory](https://docs.rs/directories-next/2.0.0/directories_next/struct.ProjectDirs.html#method.data_dir).
|
* `egui_glium` will now save you app state to [a better directory](https://docs.rs/directories-next/2.0.0/directories_next/struct.ProjectDirs.html#method.data_dir).
|
||||||
* `egui_glium::run`: the parameter `app` now has signature `Box<dyn App>` (you need to add `Box::new(app)` to your code).
|
* `egui_glium::run`: the parameter `app` now has signature `Box<dyn App>` (you need to add `Box::new(app)` to your code).
|
||||||
* Window title is now passed via the `trait` function `egui::App::name()`.
|
* Window title is now passed via the `trait` function `egui::App::name()`.
|
||||||
|
@ -55,7 +55,7 @@ All notable changes to the `egui_glium` integration will be noted in this file.
|
||||||
|
|
||||||
|
|
||||||
## 0.5.0 - 2020-12-13
|
## 0.5.0 - 2020-12-13
|
||||||
### Changed
|
### Changed 🔧
|
||||||
* FileStorage::from_path now takes `Into<Path>` instead of `String`
|
* FileStorage::from_path now takes `Into<Path>` instead of `String`
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -167,9 +167,8 @@ fn load_icon(icon_data: epi::IconData) -> Option<glutin::window::Icon> {
|
||||||
pub fn run(mut app: Box<dyn epi::App>, nativve_options: epi::NativeOptions) -> ! {
|
pub fn run(mut app: Box<dyn epi::App>, nativve_options: epi::NativeOptions) -> ! {
|
||||||
let mut storage = create_storage(app.name());
|
let mut storage = create_storage(app.name());
|
||||||
|
|
||||||
if let Some(storage) = &mut storage {
|
#[cfg(feature = "http")]
|
||||||
app.load(storage.as_ref());
|
let http = std::sync::Arc::new(crate::http::GliumHttp {});
|
||||||
}
|
|
||||||
|
|
||||||
let window_settings = deserialize_window_settings(&storage);
|
let window_settings = deserialize_window_settings(&storage);
|
||||||
let event_loop = glutin::event_loop::EventLoop::with_user_event();
|
let event_loop = glutin::event_loop::EventLoop::with_user_event();
|
||||||
|
@ -183,7 +182,20 @@ pub fn run(mut app: Box<dyn epi::App>, nativve_options: epi::NativeOptions) -> !
|
||||||
let mut egui = EguiGlium::new(&display);
|
let mut egui = EguiGlium::new(&display);
|
||||||
*egui.ctx().memory() = deserialize_memory(&storage).unwrap_or_default();
|
*egui.ctx().memory() = deserialize_memory(&storage).unwrap_or_default();
|
||||||
|
|
||||||
app.setup(&egui.ctx());
|
{
|
||||||
|
let (ctx, painter) = egui.ctx_and_painter_mut();
|
||||||
|
let mut app_output = epi::backend::AppOutput::default();
|
||||||
|
let mut frame = epi::backend::FrameBuilder {
|
||||||
|
info: integration_info(&display, None),
|
||||||
|
tex_allocator: painter,
|
||||||
|
#[cfg(feature = "http")]
|
||||||
|
http: http.clone(),
|
||||||
|
output: &mut app_output,
|
||||||
|
repaint_signal: repaint_signal.clone(),
|
||||||
|
}
|
||||||
|
.build();
|
||||||
|
app.setup(&ctx, &mut frame, storage.as_deref());
|
||||||
|
}
|
||||||
|
|
||||||
let mut previous_frame_time = None;
|
let mut previous_frame_time = None;
|
||||||
|
|
||||||
|
@ -192,9 +204,6 @@ pub fn run(mut app: Box<dyn epi::App>, nativve_options: epi::NativeOptions) -> !
|
||||||
#[cfg(feature = "persistence")]
|
#[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 {});
|
|
||||||
|
|
||||||
if app.warm_up_enabled() {
|
if app.warm_up_enabled() {
|
||||||
let saved_memory = egui.ctx().memory().clone();
|
let saved_memory = egui.ctx().memory().clone();
|
||||||
egui.ctx().memory().set_everything_is_visible(true);
|
egui.ctx().memory().set_everything_is_visible(true);
|
||||||
|
|
|
@ -5,7 +5,7 @@ pub use egui::{pos2, Color32};
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
pub struct WebBackend {
|
pub struct WebBackend {
|
||||||
ctx: egui::CtxRef,
|
egui_ctx: egui::CtxRef,
|
||||||
painter: Box<dyn Painter>,
|
painter: Box<dyn Painter>,
|
||||||
previous_frame_time: Option<f32>,
|
previous_frame_time: Option<f32>,
|
||||||
frame_start: Option<f64>,
|
frame_start: Option<f64>,
|
||||||
|
@ -25,7 +25,7 @@ impl WebBackend {
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
ctx,
|
egui_ctx: ctx,
|
||||||
painter,
|
painter,
|
||||||
previous_frame_time: None,
|
previous_frame_time: None,
|
||||||
frame_start: None,
|
frame_start: None,
|
||||||
|
@ -39,7 +39,7 @@ impl WebBackend {
|
||||||
|
|
||||||
pub fn begin_frame(&mut self, raw_input: egui::RawInput) {
|
pub fn begin_frame(&mut self, raw_input: egui::RawInput) {
|
||||||
self.frame_start = Some(now_sec());
|
self.frame_start = Some(now_sec());
|
||||||
self.ctx.begin_frame(raw_input)
|
self.egui_ctx.begin_frame(raw_input)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn end_frame(&mut self) -> Result<(egui::Output, Vec<egui::ClippedMesh>), JsValue> {
|
pub fn end_frame(&mut self) -> Result<(egui::Output, Vec<egui::ClippedMesh>), JsValue> {
|
||||||
|
@ -48,8 +48,8 @@ impl WebBackend {
|
||||||
.take()
|
.take()
|
||||||
.expect("unmatched calls to begin_frame/end_frame");
|
.expect("unmatched calls to begin_frame/end_frame");
|
||||||
|
|
||||||
let (output, shapes) = self.ctx.end_frame();
|
let (output, shapes) = self.egui_ctx.end_frame();
|
||||||
let clipped_meshes = self.ctx.tessellate(shapes);
|
let clipped_meshes = self.egui_ctx.tessellate(shapes);
|
||||||
|
|
||||||
let now = now_sec();
|
let now = now_sec();
|
||||||
self.previous_frame_time = Some((now - frame_start) as f32);
|
self.previous_frame_time = Some((now - frame_start) as f32);
|
||||||
|
@ -62,10 +62,10 @@ impl WebBackend {
|
||||||
clear_color: egui::Rgba,
|
clear_color: egui::Rgba,
|
||||||
clipped_meshes: Vec<egui::ClippedMesh>,
|
clipped_meshes: Vec<egui::ClippedMesh>,
|
||||||
) -> Result<(), JsValue> {
|
) -> Result<(), JsValue> {
|
||||||
self.painter.upload_egui_texture(&self.ctx.texture());
|
self.painter.upload_egui_texture(&self.egui_ctx.texture());
|
||||||
self.painter.clear(clear_color);
|
self.painter.clear(clear_color);
|
||||||
self.painter
|
self.painter
|
||||||
.paint_meshes(clipped_meshes, self.ctx.pixels_per_point())
|
.paint_meshes(clipped_meshes, self.egui_ctx.pixels_per_point())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn painter_debug_info(&self) -> String {
|
pub fn painter_debug_info(&self) -> String {
|
||||||
|
@ -145,12 +145,11 @@ pub struct AppRunner {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AppRunner {
|
impl AppRunner {
|
||||||
pub fn new(web_backend: WebBackend, mut app: Box<dyn epi::App>) -> Result<Self, JsValue> {
|
pub fn new(web_backend: WebBackend, app: Box<dyn epi::App>) -> Result<Self, JsValue> {
|
||||||
load_memory(&web_backend.ctx);
|
load_memory(&web_backend.egui_ctx);
|
||||||
let storage = LocalStorage::default();
|
let storage = LocalStorage::default();
|
||||||
app.load(&storage);
|
let prefer_dark_mode = crate::prefer_dark_mode();
|
||||||
app.setup(&web_backend.ctx);
|
let mut runner = Self {
|
||||||
Ok(Self {
|
|
||||||
web_backend,
|
web_backend,
|
||||||
input: Default::default(),
|
input: Default::default(),
|
||||||
app,
|
app,
|
||||||
|
@ -161,11 +160,31 @@ impl AppRunner {
|
||||||
#[cfg(feature = "http")]
|
#[cfg(feature = "http")]
|
||||||
http: Arc::new(http::WebHttp {}),
|
http: Arc::new(http::WebHttp {}),
|
||||||
last_text_cursor_pos: None,
|
last_text_cursor_pos: None,
|
||||||
})
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut app_output = epi::backend::AppOutput::default();
|
||||||
|
let mut frame = epi::backend::FrameBuilder {
|
||||||
|
info: runner.integration_info(),
|
||||||
|
tex_allocator: runner.web_backend.painter.as_tex_allocator(),
|
||||||
|
#[cfg(feature = "http")]
|
||||||
|
http: runner.http.clone(),
|
||||||
|
output: &mut app_output,
|
||||||
|
repaint_signal: runner.needs_repaint.clone(),
|
||||||
|
}
|
||||||
|
.build();
|
||||||
|
runner.app.setup(
|
||||||
|
&runner.web_backend.egui_ctx,
|
||||||
|
&mut frame,
|
||||||
|
Some(&runner.storage),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(runner)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn egui_ctx(&self) -> &egui::CtxRef {
|
pub fn egui_ctx(&self) -> &egui::CtxRef {
|
||||||
&self.web_backend.ctx
|
&self.web_backend.egui_ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn auto_save(&mut self) {
|
pub fn auto_save(&mut self) {
|
||||||
|
@ -173,7 +192,7 @@ impl AppRunner {
|
||||||
let time_since_last_save = now - self.last_save_time;
|
let time_since_last_save = now - self.last_save_time;
|
||||||
|
|
||||||
if time_since_last_save > self.app.auto_save_interval().as_secs_f64() {
|
if time_since_last_save > self.app.auto_save_interval().as_secs_f64() {
|
||||||
save_memory(&self.web_backend.ctx);
|
save_memory(&self.web_backend.egui_ctx);
|
||||||
self.app.save(&mut self.storage);
|
self.app.save(&mut self.storage);
|
||||||
self.last_save_time = now;
|
self.last_save_time = now;
|
||||||
}
|
}
|
||||||
|
@ -185,37 +204,39 @@ impl AppRunner {
|
||||||
|
|
||||||
pub fn warm_up(&mut self) -> Result<(), JsValue> {
|
pub fn warm_up(&mut self) -> Result<(), JsValue> {
|
||||||
if self.app.warm_up_enabled() {
|
if self.app.warm_up_enabled() {
|
||||||
let saved_memory = self.web_backend.ctx.memory().clone();
|
let saved_memory = self.web_backend.egui_ctx.memory().clone();
|
||||||
self.web_backend
|
self.web_backend
|
||||||
.ctx
|
.egui_ctx
|
||||||
.memory()
|
.memory()
|
||||||
.set_everything_is_visible(true);
|
.set_everything_is_visible(true);
|
||||||
self.logic()?;
|
self.logic()?;
|
||||||
*self.web_backend.ctx.memory() = saved_memory; // We don't want to remember that windows were huge.
|
*self.web_backend.egui_ctx.memory() = saved_memory; // We don't want to remember that windows were huge.
|
||||||
self.web_backend.ctx.clear_animations();
|
self.web_backend.egui_ctx.clear_animations();
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn integration_info(&self) -> epi::IntegrationInfo {
|
||||||
|
epi::IntegrationInfo {
|
||||||
|
web_info: Some(epi::WebInfo {
|
||||||
|
web_location_hash: location_hash().unwrap_or_default(),
|
||||||
|
}),
|
||||||
|
cpu_usage: self.web_backend.previous_frame_time,
|
||||||
|
seconds_since_midnight: Some(seconds_since_midnight()),
|
||||||
|
native_pixels_per_point: Some(native_pixels_per_point()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn logic(&mut self) -> Result<(egui::Output, Vec<egui::ClippedMesh>), JsValue> {
|
pub fn logic(&mut self) -> Result<(egui::Output, Vec<egui::ClippedMesh>), JsValue> {
|
||||||
resize_canvas_to_screen_size(self.web_backend.canvas_id(), self.app.max_size_points());
|
resize_canvas_to_screen_size(self.web_backend.canvas_id(), self.app.max_size_points());
|
||||||
let canvas_size = canvas_size_in_points(self.web_backend.canvas_id());
|
let canvas_size = canvas_size_in_points(self.web_backend.canvas_id());
|
||||||
let raw_input = self.input.new_frame(canvas_size);
|
let raw_input = self.input.new_frame(canvas_size);
|
||||||
|
|
||||||
let info = epi::IntegrationInfo {
|
|
||||||
web_info: Some(epi::WebInfo {
|
|
||||||
web_location_hash: location_hash().unwrap_or_default(),
|
|
||||||
}),
|
|
||||||
cpu_usage: self.web_backend.previous_frame_time,
|
|
||||||
seconds_since_midnight: Some(seconds_since_midnight()),
|
|
||||||
native_pixels_per_point: Some(native_pixels_per_point()),
|
|
||||||
};
|
|
||||||
|
|
||||||
self.web_backend.begin_frame(raw_input);
|
self.web_backend.begin_frame(raw_input);
|
||||||
|
|
||||||
let mut app_output = epi::backend::AppOutput::default();
|
let mut app_output = epi::backend::AppOutput::default();
|
||||||
let mut frame = epi::backend::FrameBuilder {
|
let mut frame = epi::backend::FrameBuilder {
|
||||||
info,
|
info: self.integration_info(),
|
||||||
tex_allocator: self.web_backend.painter.as_tex_allocator(),
|
tex_allocator: self.web_backend.painter.as_tex_allocator(),
|
||||||
#[cfg(feature = "http")]
|
#[cfg(feature = "http")]
|
||||||
http: self.http.clone(),
|
http: self.http.clone(),
|
||||||
|
@ -224,10 +245,10 @@ impl AppRunner {
|
||||||
}
|
}
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
self.app.update(&self.web_backend.ctx, &mut frame);
|
self.app.update(&self.web_backend.egui_ctx, &mut frame);
|
||||||
let (egui_output, clipped_meshes) = self.web_backend.end_frame()?;
|
let (egui_output, clipped_meshes) = self.web_backend.end_frame()?;
|
||||||
|
|
||||||
if self.web_backend.ctx.memory().options.screen_reader {
|
if self.web_backend.egui_ctx.memory().options.screen_reader {
|
||||||
self.screen_reader.speak(&egui_output.events_description());
|
self.screen_reader.speak(&egui_output.events_description());
|
||||||
}
|
}
|
||||||
handle_output(&egui_output, self);
|
handle_output(&egui_output, self);
|
||||||
|
|
|
@ -95,9 +95,18 @@ pub trait App {
|
||||||
fn update(&mut self, ctx: &egui::CtxRef, frame: &mut Frame<'_>);
|
fn update(&mut self, ctx: &egui::CtxRef, frame: &mut Frame<'_>);
|
||||||
|
|
||||||
/// Called once before the first frame.
|
/// Called once before the first frame.
|
||||||
/// Allows you to do setup code and to call `ctx.set_fonts()`.
|
///
|
||||||
/// Optional.
|
/// Allows you to do setup code, e.g to call `[Context::set_fonts]`,
|
||||||
fn setup(&mut self, _ctx: &egui::CtxRef) {}
|
/// `[Context::set_visuals]` etc.
|
||||||
|
///
|
||||||
|
/// Also allows you to restore state, if there is a storage.
|
||||||
|
fn setup(
|
||||||
|
&mut self,
|
||||||
|
_ctx: &egui::CtxRef,
|
||||||
|
_frame: &mut Frame<'_>,
|
||||||
|
_storage: Option<&dyn Storage>,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
/// If `true` a warm-up call to [`Self::update`] will be issued where
|
/// If `true` a warm-up call to [`Self::update`] will be issued where
|
||||||
/// `ctx.memory().everything_is_visible()` will be set to `true`.
|
/// `ctx.memory().everything_is_visible()` will be set to `true`.
|
||||||
|
@ -107,9 +116,6 @@ pub trait App {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called once on start. Allows you to restore state.
|
|
||||||
fn load(&mut self, _storage: &dyn Storage) {}
|
|
||||||
|
|
||||||
/// Called on shutdown, and perhaps at regular intervals. Allows you to save state.
|
/// Called on shutdown, and perhaps at regular intervals. Allows you to save state.
|
||||||
///
|
///
|
||||||
/// On web the states is stored to "Local Storage".
|
/// On web the states is stored to "Local Storage".
|
||||||
|
|
Loading…
Reference in a new issue