Support returning errors when creating the app (#4565)

The closure passed to `eframe::run_native` now returns a `Result`,
allowing you to return an error during app creation, which will be
returned to the caller of `run_native`.

This means you need to wrap your `Box::new(MyApp::new(…))` in an
`Ok(…)`.

* Closes https://github.com/emilk/egui/issues/4474
This commit is contained in:
Emil Ernerfeldt 2024-05-28 21:59:19 +02:00 committed by GitHub
parent 54429e0549
commit 942fe4ab31
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
28 changed files with 44 additions and 36 deletions

View File

@ -41,10 +41,12 @@ pub type EventLoopBuilderHook = Box<dyn FnOnce(&mut EventLoopBuilder<UserEvent>)
#[cfg(any(feature = "glow", feature = "wgpu"))] #[cfg(any(feature = "glow", feature = "wgpu"))]
pub type WindowBuilderHook = Box<dyn FnOnce(egui::ViewportBuilder) -> egui::ViewportBuilder>; pub type WindowBuilderHook = Box<dyn FnOnce(egui::ViewportBuilder) -> egui::ViewportBuilder>;
type DynError = Box<dyn std::error::Error>;
/// This is how your app is created. /// This is how your app is created.
/// ///
/// You can use the [`CreationContext`] to setup egui, restore state, setup OpenGL things, etc. /// You can use the [`CreationContext`] to setup egui, restore state, setup OpenGL things, etc.
pub type AppCreator = Box<dyn FnOnce(&CreationContext<'_>) -> Box<dyn App>>; pub type AppCreator = Box<dyn FnOnce(&CreationContext<'_>) -> Result<Box<dyn App>, DynError>>;
/// Data that is passed to [`AppCreator`] that can be used to setup and initialize your app. /// Data that is passed to [`AppCreator`] that can be used to setup and initialize your app.
pub struct CreationContext<'s> { pub struct CreationContext<'s> {

View File

@ -30,7 +30,7 @@
//! //!
//! fn main() { //! fn main() {
//! let native_options = eframe::NativeOptions::default(); //! let native_options = eframe::NativeOptions::default();
//! eframe::run_native("My egui App", native_options, Box::new(|cc| Box::new(MyEguiApp::new(cc)))); //! eframe::run_native("My egui App", native_options, Box::new(|cc| Ok(Box::new(MyEguiApp::new(cc)))));
//! } //! }
//! //!
//! #[derive(Default)] //! #[derive(Default)]
@ -90,7 +90,7 @@
//! .start( //! .start(
//! canvas_id, //! canvas_id,
//! eframe::WebOptions::default(), //! eframe::WebOptions::default(),
//! Box::new(|cc| Box::new(MyEguiApp::new(cc))), //! Box::new(|cc| Ok(Box::new(MyEguiApp::new(cc))),)
//! ) //! )
//! .await //! .await
//! } //! }
@ -199,7 +199,7 @@ pub mod icon_data;
/// ///
/// fn main() -> eframe::Result<()> { /// fn main() -> eframe::Result<()> {
/// let native_options = eframe::NativeOptions::default(); /// let native_options = eframe::NativeOptions::default();
/// eframe::run_native("MyApp", native_options, Box::new(|cc| Box::new(MyEguiApp::new(cc)))) /// eframe::run_native("MyApp", native_options, Box::new(|cc| Ok(Box::new(MyEguiApp::new(cc)))))
/// } /// }
/// ///
/// #[derive(Default)] /// #[derive(Default)]
@ -324,7 +324,7 @@ pub fn run_simple_native(
run_native( run_native(
app_name, app_name,
native_options, native_options,
Box::new(|_cc| Box::new(SimpleApp { update_fun })), Box::new(|_cc| Ok(Box::new(SimpleApp { update_fun }))),
) )
} }
@ -333,6 +333,9 @@ pub fn run_simple_native(
/// The different problems that can occur when trying to run `eframe`. /// The different problems that can occur when trying to run `eframe`.
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
/// Something went wrong in user code when creating the app.
AppCreation(Box<dyn std::error::Error>),
/// An error from [`winit`]. /// An error from [`winit`].
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
Winit(winit::error::OsError), Winit(winit::error::OsError),
@ -403,6 +406,8 @@ impl From<egui_wgpu::WgpuError> for Error {
impl std::fmt::Display for Error { impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
Self::AppCreation(err) => write!(f, "app creation error: {err}"),
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
Self::Winit(err) => { Self::Winit(err) => {
write!(f, "winit error: {err}") write!(f, "winit error: {err}")

View File

@ -313,7 +313,7 @@ impl GlowWinitApp {
raw_window_handle: window.window_handle().map(|h| h.as_raw()), raw_window_handle: window.window_handle().map(|h| h.as_raw()),
}; };
crate::profile_scope!("app_creator"); crate::profile_scope!("app_creator");
app_creator(&cc) app_creator(&cc).map_err(crate::Error::AppCreation)?
}; };
let glutin = Rc::new(RefCell::new(glutin)); let glutin = Rc::new(RefCell::new(glutin));

View File

@ -182,7 +182,7 @@ impl WgpuWinitApp {
storage: Option<Box<dyn Storage>>, storage: Option<Box<dyn Storage>>,
window: Window, window: Window,
builder: ViewportBuilder, builder: ViewportBuilder,
) -> Result<&mut WgpuWinitRunning, egui_wgpu::WgpuError> { ) -> crate::Result<&mut WgpuWinitRunning> {
crate::profile_function!(); crate::profile_function!();
#[allow(unsafe_code, unused_mut, unused_unsafe)] #[allow(unsafe_code, unused_mut, unused_unsafe)]
@ -272,7 +272,7 @@ impl WgpuWinitApp {
}; };
let app = { let app = {
crate::profile_scope!("user_app_creator"); crate::profile_scope!("user_app_creator");
app_creator(&cc) app_creator(&cc).map_err(crate::Error::AppCreation)?
}; };
let mut viewport_from_window = HashMap::default(); let mut viewport_from_window = HashMap::default();

View File

@ -30,7 +30,7 @@ impl Drop for AppRunner {
impl AppRunner { impl AppRunner {
/// # Errors /// # Errors
/// Failure to initialize WebGL renderer. /// Failure to initialize WebGL renderer, or failure to create app.
pub async fn new( pub async fn new(
canvas_id: &str, canvas_id: &str,
web_options: crate::WebOptions, web_options: crate::WebOptions,
@ -71,7 +71,7 @@ impl AppRunner {
let theme = system_theme.unwrap_or(web_options.default_theme); let theme = system_theme.unwrap_or(web_options.default_theme);
egui_ctx.set_visuals(theme.egui_visuals()); egui_ctx.set_visuals(theme.egui_visuals());
let app = app_creator(&epi::CreationContext { let cc = epi::CreationContext {
egui_ctx: egui_ctx.clone(), egui_ctx: egui_ctx.clone(),
integration_info: info.clone(), integration_info: info.clone(),
storage: Some(&storage), storage: Some(&storage),
@ -86,7 +86,8 @@ impl AppRunner {
wgpu_render_state: painter.render_state(), wgpu_render_state: painter.render_state(),
#[cfg(all(feature = "wgpu", feature = "glow"))] #[cfg(all(feature = "wgpu", feature = "glow"))]
wgpu_render_state: None, wgpu_render_state: None,
}); };
let app = app_creator(&cc).map_err(|err| err.to_string())?;
let frame = epi::Frame { let frame = epi::Frame {
info, info,

View File

@ -54,7 +54,7 @@ impl WebRunner {
/// Create the application, install callbacks, and start running the app. /// Create the application, install callbacks, and start running the app.
/// ///
/// # Errors /// # Errors
/// Failing to initialize graphics. /// Failing to initialize graphics, or failure to create app.
pub async fn start( pub async fn start(
&self, &self,
canvas_id: &str, canvas_id: &str,

View File

@ -48,7 +48,7 @@ fn main() -> Result<(), eframe::Error> {
eframe::run_native( eframe::run_native(
"egui demo app", "egui demo app",
options, options,
Box::new(|cc| Box::new(egui_demo_app::WrapApp::new(cc))), Box::new(|cc| Ok(Box::new(egui_demo_app::WrapApp::new(cc)))),
) )
} }

View File

@ -32,7 +32,7 @@ impl WebHandle {
.start( .start(
canvas_id, canvas_id,
eframe::WebOptions::default(), eframe::WebOptions::default(),
Box::new(|cc| Box::new(WrapApp::new(cc))), Box::new(|cc| Ok(Box::new(WrapApp::new(cc)))),
) )
.await .await
} }

View File

@ -12,7 +12,7 @@ fn main() -> Result<(), eframe::Error> {
eframe::run_native( eframe::run_native(
"Confirm exit", "Confirm exit",
options, options,
Box::new(|_cc| Box::<MyApp>::default()), Box::new(|_cc| Ok(Box::<MyApp>::default())),
) )
} }

View File

@ -19,7 +19,7 @@ fn main() -> Result<(), eframe::Error> {
eframe::run_native( eframe::run_native(
"Custom 3D painting in eframe using glow", "Custom 3D painting in eframe using glow",
options, options,
Box::new(|cc| Box::new(MyApp::new(cc))), Box::new(|cc| Ok(Box::new(MyApp::new(cc)))),
) )
} }

View File

@ -12,7 +12,7 @@ fn main() -> Result<(), eframe::Error> {
eframe::run_native( eframe::run_native(
"egui example: custom font", "egui example: custom font",
options, options,
Box::new(|cc| Box::new(MyApp::new(cc))), Box::new(|cc| Ok(Box::new(MyApp::new(cc)))),
) )
} }

View File

@ -11,7 +11,7 @@ fn main() -> Result<(), eframe::Error> {
eframe::run_native( eframe::run_native(
"egui example: global font style", "egui example: global font style",
options, options,
Box::new(|cc| Box::new(MyApp::new(cc))), Box::new(|cc| Ok(Box::new(MyApp::new(cc)))),
) )
} }

View File

@ -20,7 +20,7 @@ fn main() -> Result<(), eframe::Error> {
// This gives us image support: // This gives us image support:
egui_extras::install_image_loaders(&cc.egui_ctx); egui_extras::install_image_loaders(&cc.egui_ctx);
Box::<MyApp>::default() Ok(Box::<MyApp>::default())
}), }),
) )
} }

View File

@ -11,7 +11,7 @@ fn main() -> Result<(), eframe::Error> {
eframe::run_native( eframe::run_native(
"Plot", "Plot",
options, options,
Box::new(|_cc| Box::<PlotExample>::default()), Box::new(|_cc| Ok(Box::<PlotExample>::default())),
) )
} }

View File

@ -19,7 +19,7 @@ fn main() -> Result<(), eframe::Error> {
eframe::run_native( eframe::run_native(
"Custom window frame", // unused title "Custom window frame", // unused title
options, options,
Box::new(|_cc| Box::<MyApp>::default()), Box::new(|_cc| Ok(Box::<MyApp>::default())),
) )
} }

View File

@ -14,7 +14,7 @@ fn main() -> Result<(), eframe::Error> {
eframe::run_native( eframe::run_native(
"Native file dialogs and drag-and-drop files", "Native file dialogs and drag-and-drop files",
options, options,
Box::new(|_cc| Box::<MyApp>::default()), Box::new(|_cc| Ok(Box::<MyApp>::default())),
) )
} }

View File

@ -16,7 +16,7 @@ fn main() -> Result<(), eframe::Error> {
// This gives us image support: // This gives us image support:
egui_extras::install_image_loaders(&cc.egui_ctx); egui_extras::install_image_loaders(&cc.egui_ctx);
Box::<MyApp>::default() Ok(Box::<MyApp>::default())
}), }),
) )
} }

View File

@ -17,7 +17,7 @@ fn main() -> Result<(), eframe::Error> {
eframe::run_native( eframe::run_native(
"My parallel egui App", "My parallel egui App",
options, options,
Box::new(|_cc| Box::new(MyApp::new())), Box::new(|_cc| Ok(Box::new(MyApp::new()))),
) )
} }

View File

@ -15,7 +15,7 @@ fn main() -> Result<(), eframe::Error> {
Box::new(|cc| { Box::new(|cc| {
// This gives us image support: // This gives us image support:
egui_extras::install_image_loaders(&cc.egui_ctx); egui_extras::install_image_loaders(&cc.egui_ctx);
Box::<MyApp>::default() Ok(Box::<MyApp>::default())
}), }),
) )
} }

View File

@ -10,7 +10,7 @@ fn main() -> Result<(), eframe::Error> {
eframe::run_native( eframe::run_native(
"Keyboard events", "Keyboard events",
options, options,
Box::new(|_cc| Box::<Content>::default()), Box::new(|_cc| Ok(Box::<Content>::default())),
) )
} }

View File

@ -17,7 +17,7 @@ fn main() -> Result<(), eframe::Error> {
eframe::run_native( eframe::run_native(
"Multiple viewports", "Multiple viewports",
options, options,
Box::new(|_cc| Box::<MyApp>::default()), Box::new(|_cc| Ok(Box::<MyApp>::default())),
) )
} }

View File

@ -22,7 +22,7 @@ fn main() -> Result<(), eframe::Error> {
..Default::default() ..Default::default()
}, },
Box::new(|_cc| Box::<MyApp>::default()), Box::new(|_cc| Ok(Box::<MyApp>::default())),
) )
} }

View File

@ -14,7 +14,7 @@ fn main() -> Result<(), eframe::Error> {
eframe::run_native( eframe::run_native(
"My egui App with a plot", "My egui App with a plot",
options, options,
Box::new(|_cc| Box::<MyApp>::default()), Box::new(|_cc| Ok(Box::<MyApp>::default())),
) )
} }

View File

@ -14,7 +14,7 @@ fn main() -> Result<(), eframe::Error> {
eframe::run_native( eframe::run_native(
"Take screenshots and display with eframe/egui", "Take screenshots and display with eframe/egui",
options, options,
Box::new(|_cc| Box::<MyApp>::default()), Box::new(|_cc| Ok(Box::<MyApp>::default())),
) )
} }

View File

@ -23,7 +23,7 @@ fn main() -> Result<(), eframe::Error> {
eframe::run_native( eframe::run_native(
"First Window", "First Window",
options.clone(), options.clone(),
Box::new(|_cc| Box::new(MyApp { has_next: true })), Box::new(|_cc| Ok(Box::new(MyApp { has_next: true }))),
)?; )?;
std::thread::sleep(std::time::Duration::from_secs(2)); std::thread::sleep(std::time::Duration::from_secs(2));
@ -32,7 +32,7 @@ fn main() -> Result<(), eframe::Error> {
eframe::run_native( eframe::run_native(
"Second Window", "Second Window",
options.clone(), options.clone(),
Box::new(|_cc| Box::new(MyApp { has_next: true })), Box::new(|_cc| Ok(Box::new(MyApp { has_next: true }))),
)?; )?;
std::thread::sleep(std::time::Duration::from_secs(2)); std::thread::sleep(std::time::Duration::from_secs(2));
@ -41,7 +41,7 @@ fn main() -> Result<(), eframe::Error> {
eframe::run_native( eframe::run_native(
"Third Window", "Third Window",
options, options,
Box::new(|_cc| Box::new(MyApp { has_next: false })), Box::new(|_cc| Ok(Box::new(MyApp { has_next: false }))),
) )
} }

View File

@ -15,7 +15,7 @@ fn main() -> eframe::Result<()> {
eframe::run_native( eframe::run_native(
"User attention test", "User attention test",
native_options, native_options,
Box::new(|cc| Box::new(Application::new(cc))), Box::new(|cc| Ok(Box::new(Application::new(cc)))),
) )
} }

View File

@ -16,7 +16,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
eframe::run_native( eframe::run_native(
"My test app", "My test app",
options, options,
Box::new(|_cc| Box::<MyTestApp>::default()), Box::new(|_cc| Ok(Box::<MyTestApp>::default())),
)?; )?;
Ok(()) Ok(())
} }

View File

@ -22,7 +22,7 @@ fn main() {
..Default::default() ..Default::default()
}, },
Box::new(|_| Box::<App>::default()), Box::new(|_cc| Ok(Box::<App>::default())),
); );
} }