use egui::Frame; type AppKindUiState<'a, State> = Box; type AppKindUi<'a> = Box; /// In order to access the [`eframe::App`] trait from the generic `State`, we store a function pointer /// here that will return the dyn trait from the struct. /// In the builder we have the correct `where`-clause to be able to create this. /// Later we can use it anywhere to get the [`eframe::App`] from the `State`. #[cfg(feature = "eframe")] pub(crate) struct AppKindEframe { pub get_app: fn(&mut State) -> &mut dyn eframe::App, #[cfg(not(target_arch = "wasm32"))] pub take_app: fn(State) -> Box, pub frame: eframe::Frame, } pub(crate) enum AppKind<'a, State> { Ui(AppKindUi<'a>), UiState(AppKindUiState<'a, State>), #[cfg(feature = "eframe")] Eframe(AppKindEframe), } impl AppKind<'_, State> { // The `Option` is needed when the `eframe` feature is enabled, because the // `Eframe` variant has no `egui::Response` to return. Without `eframe` the // wrap is unnecessary, so we silence `clippy::unnecessary_wraps` for that case. #[cfg_attr(not(feature = "eframe"), expect(clippy::unnecessary_wraps))] pub fn run( &mut self, ui: &mut egui::Ui, state: &mut State, sizing_pass: bool, ) -> Option { match self { #[cfg(feature = "eframe")] AppKind::Eframe(AppKindEframe { get_app, frame, .. }) => { let app = get_app(state); app.logic(ui, frame); app.ui(ui, frame); None } kind_ui => Some(kind_ui.run_ui(ui, state, sizing_pass)), } } fn run_ui( &mut self, ui: &mut egui::Ui, state: &mut State, sizing_pass: bool, ) -> egui::Response { let mut builder = egui::UiBuilder::new(); if sizing_pass { builder.sizing_pass = true; } ui.scope_builder(builder, |ui| { Frame::central_panel(ui.style()) // Only set outer margin, so we show no frame for tests with only free-floating windows/popups: .outer_margin(8.0) .inner_margin(0.0) .show(ui, |ui| match self { AppKind::Ui(f) => f(ui), AppKind::UiState(f) => f(ui, state), #[cfg(feature = "eframe")] AppKind::Eframe(_) => unreachable!( "run_ui should only be called with AppKind::Ui or AppKind::UiState" ), }); }) .response } }