eframe/
lib.rs

1//! eframe - the [`egui`] framework crate
2//!
3//! If you are planning to write an app for web or native,
4//! and want to use [`egui`] for everything, then `eframe` is for you!
5//!
6//! To get started, see the [examples](https://github.com/emilk/egui/tree/master/examples).
7//! To learn how to set up `eframe` for web and native, go to <https://github.com/emilk/eframe_template/> and follow the instructions there!
8//!
9//! In short, you implement [`App`] (especially [`App::update`]) and then
10//! call [`crate::run_native`] from your `main.rs`, and/or use `eframe::WebRunner` from your `lib.rs`.
11//!
12//! ## Compiling for web
13//! You need to install the `wasm32` target with `rustup target add wasm32-unknown-unknown`.
14//!
15//! Build the `.wasm` using `cargo build --target wasm32-unknown-unknown`
16//! and then use [`wasm-bindgen`](https://github.com/rustwasm/wasm-bindgen) to generate the JavaScript glue code.
17//!
18//! See the [`eframe_template` repository](https://github.com/emilk/eframe_template/) for more.
19//!
20//! ## Simplified usage
21//! If your app is only for native, and you don't need advanced features like state persistence,
22//! then you can use the simpler function [`run_simple_native`].
23//!
24//! ## Usage, native:
25//! ``` no_run
26//! use eframe::egui;
27//!
28//! fn main() {
29//!     let native_options = eframe::NativeOptions::default();
30//!     eframe::run_native("My egui App", native_options, Box::new(|cc| Ok(Box::new(MyEguiApp::new(cc)))));
31//! }
32//!
33//! #[derive(Default)]
34//! struct MyEguiApp {}
35//!
36//! impl MyEguiApp {
37//!     fn new(cc: &eframe::CreationContext<'_>) -> Self {
38//!         // Customize egui here with cc.egui_ctx.set_fonts and cc.egui_ctx.set_visuals.
39//!         // Restore app state using cc.storage (requires the "persistence" feature).
40//!         // Use the cc.gl (a glow::Context) to create graphics shaders and buffers that you can use
41//!         // for e.g. egui::PaintCallback.
42//!         Self::default()
43//!     }
44//! }
45//!
46//! impl eframe::App for MyEguiApp {
47//!    fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
48//!        egui::CentralPanel::default().show(ctx, |ui| {
49//!            ui.heading("Hello World!");
50//!        });
51//!    }
52//! }
53//! ```
54//!
55//! ## Usage, web:
56//! ``` no_run
57//! # #[cfg(target_arch = "wasm32")]
58//! use wasm_bindgen::prelude::*;
59//!
60//! /// Your handle to the web app from JavaScript.
61//! # #[cfg(target_arch = "wasm32")]
62//! #[derive(Clone)]
63//! #[wasm_bindgen]
64//! pub struct WebHandle {
65//!     runner: eframe::WebRunner,
66//! }
67//!
68//! # #[cfg(target_arch = "wasm32")]
69//! #[wasm_bindgen]
70//! impl WebHandle {
71//!     /// Installs a panic hook, then returns.
72//!     #[allow(clippy::new_without_default)]
73//!     #[wasm_bindgen(constructor)]
74//!     pub fn new() -> Self {
75//!         // Redirect [`log`] message to `console.log` and friends:
76//!         eframe::WebLogger::init(log::LevelFilter::Debug).ok();
77//!
78//!         Self {
79//!             runner: eframe::WebRunner::new(),
80//!         }
81//!     }
82//!
83//!     /// Call this once from JavaScript to start your app.
84//!     #[wasm_bindgen]
85//!     pub async fn start(&self, canvas: web_sys::HtmlCanvasElement) -> Result<(), wasm_bindgen::JsValue> {
86//!         self.runner
87//!             .start(
88//!                 canvas,
89//!                 eframe::WebOptions::default(),
90//!                 Box::new(|cc| Ok(Box::new(MyEguiApp::new(cc))),)
91//!             )
92//!             .await
93//!     }
94//!
95//!     // The following are optional:
96//!
97//!     /// Shut down eframe and clean up resources.
98//!     #[wasm_bindgen]
99//!     pub fn destroy(&self) {
100//!         self.runner.destroy();
101//!     }
102//!
103//!     /// Example on how to call into your app from JavaScript.
104//!     #[wasm_bindgen]
105//!     pub fn example(&self) {
106//!         if let Some(app) = self.runner.app_mut::<MyEguiApp>() {
107//!             app.example();
108//!         }
109//!     }
110//!
111//!     /// The JavaScript can check whether or not your app has crashed:
112//!     #[wasm_bindgen]
113//!     pub fn has_panicked(&self) -> bool {
114//!         self.runner.has_panicked()
115//!     }
116//!
117//!     #[wasm_bindgen]
118//!     pub fn panic_message(&self) -> Option<String> {
119//!         self.runner.panic_summary().map(|s| s.message())
120//!     }
121//!
122//!     #[wasm_bindgen]
123//!     pub fn panic_callstack(&self) -> Option<String> {
124//!         self.runner.panic_summary().map(|s| s.callstack())
125//!     }
126//! }
127//! ```
128//!
129//! ## Feature flags
130#![doc = document_features::document_features!()]
131//!
132//! ## Instrumentation
133//! This crate supports using the [profiling](https://crates.io/crates/profiling) crate for instrumentation.
134//! You can enable features on the profiling crates in your application to add instrumentation for all
135//! crates that support it, including egui. See the profiling crate docs for more information.
136//! ```toml
137//! [dependencies]
138//! profiling = "1.0"
139//! [features]
140//! profile-with-puffin = ["profiling/profile-with-puffin"]
141//! ```
142//!
143
144#![warn(missing_docs)] // let's keep eframe well-documented
145#![allow(clippy::needless_doctest_main)]
146
147// Re-export all useful libraries:
148pub use {egui, egui::emath, egui::epaint};
149
150#[cfg(feature = "glow")]
151pub use {egui_glow, glow};
152
153#[cfg(feature = "wgpu")]
154pub use {egui_wgpu, wgpu};
155
156mod epi;
157
158// Re-export everything in `epi` so `eframe` users don't have to care about what `epi` is:
159pub use epi::*;
160
161pub(crate) mod stopwatch;
162
163// ----------------------------------------------------------------------------
164// When compiling for web
165
166#[cfg(target_arch = "wasm32")]
167pub use wasm_bindgen;
168
169#[cfg(target_arch = "wasm32")]
170pub use web_sys;
171
172#[cfg(target_arch = "wasm32")]
173pub mod web;
174
175#[cfg(target_arch = "wasm32")]
176pub use web::{WebLogger, WebRunner};
177
178// ----------------------------------------------------------------------------
179// When compiling natively
180
181#[cfg(not(target_arch = "wasm32"))]
182#[cfg(any(feature = "glow", feature = "wgpu"))]
183mod native;
184
185#[cfg(not(target_arch = "wasm32"))]
186#[cfg(any(feature = "glow", feature = "wgpu"))]
187#[cfg(feature = "persistence")]
188pub use native::file_storage::storage_dir;
189
190#[cfg(not(target_arch = "wasm32"))]
191pub mod icon_data;
192
193/// This is how you start a native (desktop) app.
194///
195/// The first argument is name of your app, which is a an identifier
196/// used for the save location of persistence (see [`App::save`]).
197/// It is also used as the application id on wayland.
198/// If you set no title on the viewport, the app id will be used
199/// as the title.
200///
201/// For details about application ID conventions, see the
202/// [Desktop Entry Spec](https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#desktop-file-id)
203///
204/// Call from `fn main` like this:
205/// ``` no_run
206/// use eframe::egui;
207///
208/// fn main() -> eframe::Result {
209///     let native_options = eframe::NativeOptions::default();
210///     eframe::run_native("MyApp", native_options, Box::new(|cc| Ok(Box::new(MyEguiApp::new(cc)))))
211/// }
212///
213/// #[derive(Default)]
214/// struct MyEguiApp {}
215///
216/// impl MyEguiApp {
217///     fn new(cc: &eframe::CreationContext<'_>) -> Self {
218///         // Customize egui here with cc.egui_ctx.set_fonts and cc.egui_ctx.set_visuals.
219///         // Restore app state using cc.storage (requires the "persistence" feature).
220///         // Use the cc.gl (a glow::Context) to create graphics shaders and buffers that you can use
221///         // for e.g. egui::PaintCallback.
222///         Self::default()
223///     }
224/// }
225///
226/// impl eframe::App for MyEguiApp {
227///    fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
228///        egui::CentralPanel::default().show(ctx, |ui| {
229///            ui.heading("Hello World!");
230///        });
231///    }
232/// }
233/// ```
234///
235/// # Errors
236/// This function can fail if we fail to set up a graphics context.
237#[cfg(not(target_arch = "wasm32"))]
238#[cfg(any(feature = "glow", feature = "wgpu"))]
239#[allow(clippy::needless_pass_by_value)]
240pub fn run_native(
241    app_name: &str,
242    mut native_options: NativeOptions,
243    app_creator: AppCreator<'_>,
244) -> Result {
245    #[cfg(not(feature = "__screenshot"))]
246    assert!(
247        std::env::var("EFRAME_SCREENSHOT_TO").is_err(),
248        "EFRAME_SCREENSHOT_TO found without compiling with the '__screenshot' feature"
249    );
250
251    if native_options.viewport.title.is_none() {
252        native_options.viewport.title = Some(app_name.to_owned());
253    }
254
255    let renderer = native_options.renderer;
256
257    #[cfg(all(feature = "glow", feature = "wgpu"))]
258    {
259        match renderer {
260            Renderer::Glow => "glow",
261            Renderer::Wgpu => "wgpu",
262        };
263        log::info!("Both the glow and wgpu renderers are available. Using {renderer}.");
264    }
265
266    match renderer {
267        #[cfg(feature = "glow")]
268        Renderer::Glow => {
269            log::debug!("Using the glow renderer");
270            native::run::run_glow(app_name, native_options, app_creator)
271        }
272
273        #[cfg(feature = "wgpu")]
274        Renderer::Wgpu => {
275            log::debug!("Using the wgpu renderer");
276            native::run::run_wgpu(app_name, native_options, app_creator)
277        }
278    }
279}
280
281// ----------------------------------------------------------------------------
282
283/// The simplest way to get started when writing a native app.
284///
285/// This does NOT support persistence of custom user data. For that you need to use [`run_native`].
286/// However, it DOES support persistence of egui data (window positions and sizes, how far the user has scrolled in a
287/// [`ScrollArea`](egui::ScrollArea), etc.) if the persistence feature is enabled.
288///
289/// # Example
290/// ``` no_run
291/// fn main() -> eframe::Result {
292///     // Our application state:
293///     let mut name = "Arthur".to_owned();
294///     let mut age = 42;
295///
296///     let options = eframe::NativeOptions::default();
297///     eframe::run_simple_native("My egui App", options, move |ctx, _frame| {
298///         egui::CentralPanel::default().show(ctx, |ui| {
299///             ui.heading("My egui Application");
300///             ui.horizontal(|ui| {
301///                 let name_label = ui.label("Your name: ");
302///                 ui.text_edit_singleline(&mut name)
303///                     .labelled_by(name_label.id);
304///             });
305///             ui.add(egui::Slider::new(&mut age, 0..=120).text("age"));
306///             if ui.button("Increment").clicked() {
307///                 age += 1;
308///             }
309///             ui.label(format!("Hello '{name}', age {age}"));
310///         });
311///     })
312/// }
313/// ```
314///
315/// # Errors
316/// This function can fail if we fail to set up a graphics context.
317#[cfg(not(target_arch = "wasm32"))]
318#[cfg(any(feature = "glow", feature = "wgpu"))]
319pub fn run_simple_native(
320    app_name: &str,
321    native_options: NativeOptions,
322    update_fun: impl FnMut(&egui::Context, &mut Frame) + 'static,
323) -> Result {
324    struct SimpleApp<U> {
325        update_fun: U,
326    }
327
328    impl<U: FnMut(&egui::Context, &mut Frame) + 'static> App for SimpleApp<U> {
329        fn update(&mut self, ctx: &egui::Context, frame: &mut Frame) {
330            (self.update_fun)(ctx, frame);
331        }
332    }
333
334    run_native(
335        app_name,
336        native_options,
337        Box::new(|_cc| Ok(Box::new(SimpleApp { update_fun }))),
338    )
339}
340
341// ----------------------------------------------------------------------------
342
343/// The different problems that can occur when trying to run `eframe`.
344#[derive(Debug)]
345pub enum Error {
346    /// Something went wrong in user code when creating the app.
347    AppCreation(Box<dyn std::error::Error + Send + Sync>),
348
349    /// An error from [`winit`].
350    #[cfg(not(target_arch = "wasm32"))]
351    Winit(winit::error::OsError),
352
353    /// An error from [`winit::event_loop::EventLoop`].
354    #[cfg(not(target_arch = "wasm32"))]
355    WinitEventLoop(winit::error::EventLoopError),
356
357    /// An error from [`glutin`] when using [`glow`].
358    #[cfg(all(feature = "glow", not(target_arch = "wasm32")))]
359    Glutin(glutin::error::Error),
360
361    /// An error from [`glutin`] when using [`glow`].
362    #[cfg(all(feature = "glow", not(target_arch = "wasm32")))]
363    NoGlutinConfigs(glutin::config::ConfigTemplate, Box<dyn std::error::Error>),
364
365    /// An error from [`glutin`] when using [`glow`].
366    #[cfg(feature = "glow")]
367    OpenGL(egui_glow::PainterError),
368
369    /// An error from [`wgpu`].
370    #[cfg(feature = "wgpu")]
371    Wgpu(egui_wgpu::WgpuError),
372}
373
374impl std::error::Error for Error {}
375
376#[cfg(not(target_arch = "wasm32"))]
377impl From<winit::error::OsError> for Error {
378    #[inline]
379    fn from(err: winit::error::OsError) -> Self {
380        Self::Winit(err)
381    }
382}
383
384#[cfg(not(target_arch = "wasm32"))]
385impl From<winit::error::EventLoopError> for Error {
386    #[inline]
387    fn from(err: winit::error::EventLoopError) -> Self {
388        Self::WinitEventLoop(err)
389    }
390}
391
392#[cfg(all(feature = "glow", not(target_arch = "wasm32")))]
393impl From<glutin::error::Error> for Error {
394    #[inline]
395    fn from(err: glutin::error::Error) -> Self {
396        Self::Glutin(err)
397    }
398}
399
400#[cfg(feature = "glow")]
401impl From<egui_glow::PainterError> for Error {
402    #[inline]
403    fn from(err: egui_glow::PainterError) -> Self {
404        Self::OpenGL(err)
405    }
406}
407
408#[cfg(feature = "wgpu")]
409impl From<egui_wgpu::WgpuError> for Error {
410    #[inline]
411    fn from(err: egui_wgpu::WgpuError) -> Self {
412        Self::Wgpu(err)
413    }
414}
415
416impl std::fmt::Display for Error {
417    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
418        match self {
419            Self::AppCreation(err) => write!(f, "app creation error: {err}"),
420
421            #[cfg(not(target_arch = "wasm32"))]
422            Self::Winit(err) => {
423                write!(f, "winit error: {err}")
424            }
425
426            #[cfg(not(target_arch = "wasm32"))]
427            Self::WinitEventLoop(err) => {
428                write!(f, "winit EventLoopError: {err}")
429            }
430
431            #[cfg(all(feature = "glow", not(target_arch = "wasm32")))]
432            Self::Glutin(err) => {
433                write!(f, "glutin error: {err}")
434            }
435
436            #[cfg(all(feature = "glow", not(target_arch = "wasm32")))]
437            Self::NoGlutinConfigs(template, err) => {
438                write!(
439                    f,
440                    "Found no glutin configs matching the template: {template:?}. Error: {err}"
441                )
442            }
443
444            #[cfg(feature = "glow")]
445            Self::OpenGL(err) => {
446                write!(f, "egui_glow: {err}")
447            }
448
449            #[cfg(feature = "wgpu")]
450            Self::Wgpu(err) => {
451                write!(f, "WGPU error: {err}")
452            }
453        }
454    }
455}
456
457/// Short for `Result<T, eframe::Error>`.
458pub type Result<T = (), E = Error> = std::result::Result<T, E>;