[app] Simplify interface to egui::app::App

This commit is contained in:
Emil Ernerfeldt 2020-10-24 19:23:16 +02:00
parent daa7a2bdb2
commit 7638ca9962
9 changed files with 66 additions and 56 deletions

View file

@ -13,7 +13,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("demo_windows_minimal", |b| { c.bench_function("demo_windows_minimal", |b| {
b.iter(|| { b.iter(|| {
ctx.begin_frame(raw_input.clone()); ctx.begin_frame(raw_input.clone());
demo_windows.ui(&ctx, &Default::default(), None); demo_windows.ui(&ctx, &Default::default(), &mut None);
ctx.end_frame() ctx.end_frame()
}) })
}); });
@ -27,7 +27,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("demo_windows_full", |b| { c.bench_function("demo_windows_full", |b| {
b.iter(|| { b.iter(|| {
ctx.begin_frame(raw_input.clone()); ctx.begin_frame(raw_input.clone());
demo_windows.ui(&ctx, &Default::default(), None); demo_windows.ui(&ctx, &Default::default(), &mut None);
ctx.end_frame() ctx.end_frame()
}) })
}); });

View file

@ -17,23 +17,31 @@ pub trait App {
fn ui( fn ui(
&mut self, &mut self,
ctx: &std::sync::Arc<Context>, ctx: &std::sync::Arc<Context>,
info: &BackendInfo, integration_context: &mut IntegrationContext<'_>,
tex_allocator: Option<&mut dyn TextureAllocator>, );
) -> AppOutput;
/// Called once on shutdown. Allows you to save state. /// Called once on shutdown. Allows you to save state.
fn on_exit(&mut self, _storage: &mut dyn Storage) {} fn on_exit(&mut self, _storage: &mut dyn Storage) {}
} }
pub struct IntegrationContext<'a> {
/// Information about the integration.
pub info: IntegrationInfo,
/// A way to allocate textures (on integrations that support it).
pub tex_allocator: Option<&'a mut dyn TextureAllocator>,
/// Where the app can issue commands back to the integration.
pub output: AppOutput,
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct WebInfo { pub struct WebInfo {
/// e.g. "#fragment" part of "www.example.com/index.html#fragment" /// e.g. "#fragment" part of "www.example.com/index.html#fragment"
pub web_location_hash: String, pub web_location_hash: String,
} }
/// Information about the backend passed to the use app each frame. /// Information about the integration passed to the use app each frame.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct BackendInfo { pub struct IntegrationInfo {
/// If the app is running in a Web context, this returns information about the environment. /// If the app is running in a Web context, this returns information about the environment.
pub web_info: Option<WebInfo>, pub web_info: Option<WebInfo>,

View file

@ -90,7 +90,7 @@ impl Context {
pub fn available_rect(&self) -> Rect { pub fn available_rect(&self) -> Rect {
self.available_rect self.available_rect
.lock() .lock()
.expect("Called `avaiblable_rect()` before `begin_frame()`") .expect("Called `available_rect()` before `begin_frame()`")
} }
pub fn memory(&self) -> MutexGuard<'_, Memory> { pub fn memory(&self) -> MutexGuard<'_, Memory> {

View file

@ -183,11 +183,11 @@ pub struct DemoApp {
} }
impl DemoApp { impl DemoApp {
fn backend_ui(&mut self, ui: &mut Ui, info: &app::BackendInfo) -> app::AppOutput { fn backend_ui(&mut self, ui: &mut Ui, integration_context: &mut app::IntegrationContext<'_>) {
self.frame_history self.frame_history
.on_new_frame(ui.input().time, info.cpu_usage); .on_new_frame(ui.input().time, integration_context.info.cpu_usage);
let is_web = info.web_info.is_some(); let is_web = integration_context.info.web_info.is_some();
if is_web { if is_web {
ui.label("Egui is an immediate mode GUI written in Rust, compiled to WebAssembly, rendered with WebGL."); ui.label("Egui is an immediate mode GUI written in Rust, compiled to WebAssembly, rendered with WebGL.");
@ -210,18 +210,16 @@ impl DemoApp {
ui.separator(); ui.separator();
let mut output = app::AppOutput::default(); integration_context.output.pixels_per_point =
output.pixels_per_point = self.pixels_per_point_ui(ui, info); self.pixels_per_point_ui(ui, &integration_context.info);
if !is_web { if !is_web {
ui.separator(); ui.separator();
output.quit |= ui.button("Quit").clicked; integration_context.output.quit |= ui.button("Quit").clicked;
} }
output
} }
fn pixels_per_point_ui(&mut self, ui: &mut Ui, info: &app::BackendInfo) -> Option<f32> { fn pixels_per_point_ui(&mut self, ui: &mut Ui, info: &app::IntegrationInfo) -> Option<f32> {
self.pixels_per_point = self self.pixels_per_point = self
.pixels_per_point .pixels_per_point
.or(info.native_pixels_per_point) .or(info.native_pixels_per_point)
@ -276,10 +274,10 @@ impl app::App for DemoApp {
fn ui( fn ui(
&mut self, &mut self,
ctx: &Arc<Context>, ctx: &Arc<Context>,
info: &app::BackendInfo, integration_context: &mut crate::app::IntegrationContext<'_>,
tex_allocator: Option<&mut dyn app::TextureAllocator>, ) {
) -> app::AppOutput { let web_location_hash = integration_context
let web_location_hash = info .info
.web_info .web_info
.as_ref() .as_ref()
.map(|info| info.web_location_hash.clone()) .map(|info| info.web_location_hash.clone())
@ -292,27 +290,27 @@ impl app::App for DemoApp {
}; };
let demo_environment = demos::DemoEnvironment { let demo_environment = demos::DemoEnvironment {
seconds_since_midnight: info.seconds_since_midnight, seconds_since_midnight: integration_context.info.seconds_since_midnight,
link, link,
}; };
self.demo_windows.ui(ctx, &demo_environment, tex_allocator); self.demo_windows.ui(
ctx,
let mut output = app::AppOutput::default(); &demo_environment,
&mut integration_context.tex_allocator,
);
crate::Window::new("Backend") crate::Window::new("Backend")
.min_width(360.0) .min_width(360.0)
.scroll(false) .scroll(false)
.show(ctx, |ui| { .show(ctx, |ui| {
output = self.backend_ui(ui, info); self.backend_ui(ui, integration_context);
}); });
if self.run_mode == RunMode::Continuous { if self.run_mode == RunMode::Continuous {
// Tell the backend to repaint as soon as possible // Tell the backend to repaint as soon as possible
ctx.request_repaint(); ctx.request_repaint();
} }
output
} }
#[cfg(feature = "serde_json")] #[cfg(feature = "serde_json")]

View file

@ -46,7 +46,7 @@ impl DemoWindows {
&mut self, &mut self,
ctx: &Arc<Context>, ctx: &Arc<Context>,
env: &DemoEnvironment, env: &DemoEnvironment,
tex_allocator: Option<&mut dyn app::TextureAllocator>, tex_allocator: &mut Option<&mut dyn app::TextureAllocator>,
) { ) {
if self.previous_link != env.link { if self.previous_link != env.link {
match env.link { match env.link {
@ -93,7 +93,7 @@ impl DemoWindows {
&mut self, &mut self,
ctx: &Arc<Context>, ctx: &Arc<Context>,
env: &DemoEnvironment, env: &DemoEnvironment,
mut tex_allocator: Option<&mut dyn app::TextureAllocator>, tex_allocator: &mut Option<&mut dyn app::TextureAllocator>,
) { ) {
let Self { let Self {
open_windows, open_windows,
@ -135,7 +135,7 @@ impl DemoWindows {
.scroll(true) .scroll(true)
.open(&mut open_windows.color_test) .open(&mut open_windows.color_test)
.show(ctx, |ui| { .show(ctx, |ui| {
color_test.ui(ui, &mut tex_allocator); color_test.ui(ui, tex_allocator);
}); });
fractal_clock.window( fractal_clock.window(

View file

@ -99,7 +99,7 @@ pub fn text_egui_e2e() {
const NUM_FRAMES: usize = 5; const NUM_FRAMES: usize = 5;
for _ in 0..NUM_FRAMES { for _ in 0..NUM_FRAMES {
ctx.begin_frame(raw_input.clone()); ctx.begin_frame(raw_input.clone());
demo_windows.ui(&ctx, &Default::default(), None); demo_windows.ui(&ctx, &Default::default(), &mut None);
let (_output, paint_jobs) = ctx.end_frame(); let (_output, paint_jobs) = ctx.end_frame();
assert!(!paint_jobs.is_empty()); assert!(!paint_jobs.is_empty());
} }

View file

@ -75,15 +75,19 @@ pub fn run(
raw_input.screen_size = raw_input.screen_size =
screen_size_in_pixels(&display) / raw_input.pixels_per_point.unwrap(); screen_size_in_pixels(&display) / raw_input.pixels_per_point.unwrap();
let backend_info = egui::app::BackendInfo {
web_info: None,
cpu_usage: previous_frame_time,
seconds_since_midnight: Some(seconds_since_midnight()),
native_pixels_per_point: Some(native_pixels_per_point(&display)),
};
ctx.begin_frame(raw_input.take()); ctx.begin_frame(raw_input.take());
let app_output = app.ui(&ctx, &backend_info, Some(&mut painter)); let mut integration_context = egui::app::IntegrationContext {
info: egui::app::IntegrationInfo {
web_info: None,
cpu_usage: previous_frame_time,
seconds_since_midnight: Some(seconds_since_midnight()),
native_pixels_per_point: Some(native_pixels_per_point(&display)),
},
tex_allocator: Some(&mut painter),
output: Default::default(),
};
app.ui(&ctx, &mut integration_context);
let app_output = integration_context.output;
let (egui_output, paint_jobs) = ctx.end_frame(); let (egui_output, paint_jobs) = ctx.end_frame();
let frame_time = (Instant::now() - egui_start).as_secs_f64() as f32; let frame_time = (Instant::now() - egui_start).as_secs_f64() as f32;

View file

@ -150,21 +150,24 @@ impl AppRunner {
resize_canvas_to_screen_size(self.web_backend.canvas_id()); resize_canvas_to_screen_size(self.web_backend.canvas_id());
let raw_input = self.web_input.new_frame(self.pixels_per_point); let raw_input = self.web_input.new_frame(self.pixels_per_point);
self.web_backend.begin_frame(raw_input);
let backend_info = egui::app::BackendInfo { let mut integration_context = egui::app::IntegrationContext {
web_info: Some(WebInfo { info: egui::app::IntegrationInfo {
web_location_hash: location_hash().unwrap_or_default(), web_info: Some(WebInfo {
}), web_location_hash: location_hash().unwrap_or_default(),
cpu_usage: self.web_backend.previous_frame_time, }),
seconds_since_midnight: Some(seconds_since_midnight()), cpu_usage: self.web_backend.previous_frame_time,
native_pixels_per_point: Some(native_pixels_per_point()), seconds_since_midnight: Some(seconds_since_midnight()),
native_pixels_per_point: Some(native_pixels_per_point()),
},
tex_allocator: Some(&mut self.web_backend.painter),
output: Default::default(),
}; };
self.web_backend.begin_frame(raw_input);
let egui_ctx = &self.web_backend.ctx; let egui_ctx = &self.web_backend.ctx;
let app_output = self self.app.ui(egui_ctx, &mut integration_context);
.app let app_output = integration_context.output;
.ui(egui_ctx, &backend_info, Some(&mut self.web_backend.painter));
let (egui_output, paint_jobs) = self.web_backend.end_frame()?; let (egui_output, paint_jobs) = self.web_backend.end_frame()?;
handle_output(&egui_output); handle_output(&egui_output);

View file

@ -16,9 +16,8 @@ impl egui::app::App for MyApp {
fn ui( fn ui(
&mut self, &mut self,
ctx: &std::sync::Arc<egui::Context>, ctx: &std::sync::Arc<egui::Context>,
_info: &egui::app::BackendInfo, _integration_context: &mut egui::app::IntegrationContext,
_tex_allocator: Option<&mut dyn egui::app::TextureAllocator>, ) {
) -> egui::app::AppOutput {
let MyApp { my_string, value } = self; let MyApp { my_string, value } = self;
// Example used in `README.md`. // Example used in `README.md`.
@ -30,8 +29,6 @@ impl egui::app::App for MyApp {
ui.text_edit(my_string); ui.text_edit(my_string);
ui.add(egui::Slider::f32(value, 0.0..=1.0).text("float")); ui.add(egui::Slider::f32(value, 0.0..=1.0).text("float"));
}); });
Default::default()
} }
fn on_exit(&mut self, storage: &mut dyn egui::app::Storage) { fn on_exit(&mut self, storage: &mut dyn egui::app::Storage) {