[app] Simplify interface to egui::app::App
This commit is contained in:
parent
daa7a2bdb2
commit
7638ca9962
9 changed files with 66 additions and 56 deletions
|
@ -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()
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
|
@ -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>,
|
||||||
|
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
|
@ -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")]
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
Loading…
Reference in a new issue