mirror of
https://github.com/emilk/egui.git
synced 2026-09-01 06:10:06 -04:00
[app] Refactor egui::app::App interface to be more data oriented
This commit is contained in:
@@ -13,7 +13,12 @@ use crate::Ui;
|
||||
/// and deployed as a web site using the [`egui_web`](https://crates.io/crates/egui_web) crate.
|
||||
pub trait App {
|
||||
/// Called each time the UI needs repainting, which may be many times per second.
|
||||
fn ui(&mut self, ui: &mut Ui, backend: &mut dyn Backend);
|
||||
fn ui(
|
||||
&mut self,
|
||||
ui: &mut Ui,
|
||||
info: &BackendInfo,
|
||||
tex_allocator: Option<&mut dyn TextureAllocator>,
|
||||
) -> AppOutput;
|
||||
|
||||
/// Called once on shutdown. Allows you to save state.
|
||||
fn on_exit(&mut self, _storage: &mut dyn Storage) {}
|
||||
@@ -24,25 +29,29 @@ pub struct WebInfo {
|
||||
pub web_location_hash: String,
|
||||
}
|
||||
|
||||
pub trait Backend {
|
||||
/// Information about the backend passed to the use app each frame.
|
||||
pub struct BackendInfo {
|
||||
/// If the app is running in a Web context, this returns information about the environment.
|
||||
fn web_info(&self) -> Option<WebInfo> {
|
||||
None
|
||||
}
|
||||
pub web_info: Option<WebInfo>,
|
||||
|
||||
/// Seconds of cpu usage (in seconds) of UI code on the previous frame.
|
||||
/// Zero if this is the first frame.
|
||||
fn cpu_usage(&self) -> Option<f32>;
|
||||
/// `None` if this is the first frame.
|
||||
pub cpu_usage: Option<f32>,
|
||||
|
||||
/// Local time. Used for the clock in the demo app.
|
||||
fn seconds_since_midnight(&self) -> Option<f64> {
|
||||
None
|
||||
}
|
||||
/// Set to `None` if you don't know.
|
||||
pub seconds_since_midnight: Option<f64>,
|
||||
}
|
||||
|
||||
/// Signal the backend that we'd like to exit the app now.
|
||||
/// Action that can be taken by the user app.
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct AppOutput {
|
||||
/// Set to `true` to stop the app.
|
||||
/// This does nothing for web apps.
|
||||
fn quit(&mut self) {}
|
||||
pub quit: bool,
|
||||
}
|
||||
|
||||
pub trait TextureAllocator {
|
||||
/// Allocate a user texture (EXPERIMENTAL!)
|
||||
fn new_texture_srgba_premultiplied(
|
||||
&mut self,
|
||||
|
||||
@@ -314,11 +314,13 @@ impl DemoApp {
|
||||
}
|
||||
|
||||
// TODO: give cpu_usage and web_info via `struct BackendInfo`
|
||||
fn backend_ui(&mut self, ui: &mut Ui, backend: &mut dyn app::Backend) {
|
||||
fn backend_ui(&mut self, ui: &mut Ui, info: &app::BackendInfo) -> app::AppOutput {
|
||||
self.frame_history
|
||||
.on_new_frame(ui.input().time, backend.cpu_usage());
|
||||
.on_new_frame(ui.input().time, info.cpu_usage);
|
||||
|
||||
let is_web = backend.web_info().is_some();
|
||||
let is_web = info.web_info.is_some();
|
||||
|
||||
let mut options = app::AppOutput::default();
|
||||
|
||||
if is_web {
|
||||
ui.label("Egui is an immediate mode GUI written in Rust, compiled to WebAssembly, rendered with WebGL.");
|
||||
@@ -332,10 +334,7 @@ impl DemoApp {
|
||||
});
|
||||
} else {
|
||||
ui.heading("Egui");
|
||||
if ui.button("Quit").clicked {
|
||||
backend.quit();
|
||||
return;
|
||||
}
|
||||
options.quit |= ui.button("Quit").clicked;
|
||||
}
|
||||
|
||||
ui.separator();
|
||||
@@ -359,6 +358,8 @@ impl DemoApp {
|
||||
&mut self.show_color_test,
|
||||
"Show color blend test (debug backend painter)",
|
||||
);
|
||||
|
||||
options
|
||||
}
|
||||
|
||||
fn run_mode_ui(&mut self, ui: &mut Ui) {
|
||||
@@ -374,12 +375,19 @@ impl DemoApp {
|
||||
}
|
||||
|
||||
impl app::App for DemoApp {
|
||||
fn ui(&mut self, ui: &mut Ui, backend: &mut dyn app::Backend) {
|
||||
fn ui(
|
||||
&mut self,
|
||||
ui: &mut Ui,
|
||||
info: &app::BackendInfo,
|
||||
tex_allocator: Option<&mut dyn app::TextureAllocator>,
|
||||
) -> app::AppOutput {
|
||||
let mut output = app::AppOutput::default();
|
||||
|
||||
Window::new("Backend")
|
||||
.min_width(360.0)
|
||||
.scroll(false)
|
||||
.show(ui.ctx(), |ui| {
|
||||
self.backend_ui(ui, backend);
|
||||
output = self.backend_ui(ui, info);
|
||||
});
|
||||
|
||||
let Self {
|
||||
@@ -388,28 +396,31 @@ impl app::App for DemoApp {
|
||||
..
|
||||
} = self;
|
||||
|
||||
if *show_color_test {
|
||||
let mut tex_loader = |size: (usize, usize), pixels: &[Srgba]| {
|
||||
backend.new_texture_srgba_premultiplied(size, pixels)
|
||||
};
|
||||
Window::new("Color Test")
|
||||
.default_size(vec2(1024.0, 1024.0))
|
||||
.scroll(true)
|
||||
.open(show_color_test)
|
||||
.show(ui.ctx(), |ui| {
|
||||
color_test.ui(ui, &mut tex_loader);
|
||||
});
|
||||
// TODO: enable color test even without `tex_allocator`
|
||||
if let Some(tex_allocator) = tex_allocator {
|
||||
if *show_color_test {
|
||||
let mut tex_loader = |size: (usize, usize), pixels: &[Srgba]| {
|
||||
tex_allocator.new_texture_srgba_premultiplied(size, pixels)
|
||||
};
|
||||
Window::new("Color Test")
|
||||
.default_size(vec2(1024.0, 1024.0))
|
||||
.scroll(true)
|
||||
.open(show_color_test)
|
||||
.show(ui.ctx(), |ui| {
|
||||
color_test.ui(ui, &mut tex_loader);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let web_info = backend.web_info();
|
||||
let web_location_hash = web_info
|
||||
let web_location_hash = info
|
||||
.web_info
|
||||
.as_ref()
|
||||
.map(|info| info.web_location_hash.clone())
|
||||
.unwrap_or_default();
|
||||
|
||||
let environment = DemoEnvironment {
|
||||
web_location_hash,
|
||||
seconds_since_midnight: backend.seconds_since_midnight(),
|
||||
seconds_since_midnight: info.seconds_since_midnight,
|
||||
};
|
||||
|
||||
self.ui(ui, &environment);
|
||||
@@ -418,6 +429,8 @@ impl app::App for DemoApp {
|
||||
// Tell the backend to repaint as soon as possible
|
||||
ui.ctx().request_repaint();
|
||||
}
|
||||
|
||||
output
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde_json")]
|
||||
|
||||
Reference in New Issue
Block a user