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/main/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_global_style.
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 ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame) {
48//! egui::CentralPanel::default().show_inside(ui, |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//! #[expect(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
146// Limitation imposed by `accesskit_winit`:
147// https://github.com/AccessKit/accesskit/tree/accesskit-v0.18.0/platforms/winit#android-activity-compatibility`
148#[cfg(all(
149 target_os = "android",
150 feature = "accesskit",
151 feature = "android-native-activity"
152))]
153compile_error!("`accesskit` feature is only available with `android-game-activity`");
154
155// Re-export all useful libraries:
156pub use {egui, egui::emath, egui::epaint};
157
158#[cfg(feature = "glow")]
159pub use {egui_glow, glow};
160
161#[cfg(feature = "wgpu_no_default_features")]
162pub use {egui_wgpu, egui_wgpu::wgpu};
163
164mod epi;
165
166// Re-export everything in `epi` so `eframe` users don't have to care about what `epi` is:
167pub use epi::*;
168
169pub(crate) mod stopwatch;
170
171// ----------------------------------------------------------------------------
172// When compiling for web
173
174#[cfg(target_arch = "wasm32")]
175pub use wasm_bindgen;
176
177#[cfg(target_arch = "wasm32")]
178pub use web_sys;
179
180#[cfg(target_arch = "wasm32")]
181pub mod web;
182
183#[cfg(target_arch = "wasm32")]
184pub use web::{WebLogger, WebRunner};
185
186// ----------------------------------------------------------------------------
187// When compiling natively
188
189#[cfg(not(target_arch = "wasm32"))]
190#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
191mod native;
192
193#[cfg(target_os = "macos")]
194pub use native::macos::WindowChromeMetrics;
195
196#[cfg(not(target_arch = "wasm32"))]
197#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
198pub use native::run::EframeWinitApplication;
199
200#[cfg(not(any(target_arch = "wasm32", target_os = "ios")))]
201#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
202pub use native::run::EframePumpStatus;
203
204#[cfg(not(target_arch = "wasm32"))]
205#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
206#[cfg(feature = "persistence")]
207pub use native::file_storage::storage_dir;
208
209#[cfg(not(target_arch = "wasm32"))]
210pub mod icon_data;
211
212/// This is how you start a native (desktop) app.
213///
214/// The first argument is name of your app, which is an identifier
215/// used for the save location of persistence (see [`App::save`]).
216/// It is also used as the application id on wayland.
217/// If you set no title on the viewport, the app id will be used
218/// as the title.
219///
220/// For details about application ID conventions, see the
221/// [Desktop Entry Spec](https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#desktop-file-id)
222///
223/// Call from `fn main` like this:
224/// ``` no_run
225/// use eframe::egui;
226///
227/// fn main() -> eframe::Result {
228/// let native_options = eframe::NativeOptions::default();
229/// eframe::run_native("MyApp", native_options, Box::new(|cc| Ok(Box::new(MyEguiApp::new(cc)))))
230/// }
231///
232/// #[derive(Default)]
233/// struct MyEguiApp {}
234///
235/// impl MyEguiApp {
236/// fn new(cc: &eframe::CreationContext<'_>) -> Self {
237/// // Customize egui here with cc.egui_ctx.set_fonts and cc.egui_ctx.set_global_style.
238/// // Restore app state using cc.storage (requires the "persistence" feature).
239/// // Use the cc.gl (a glow::Context) to create graphics shaders and buffers that you can use
240/// // for e.g. egui::PaintCallback.
241/// Self::default()
242/// }
243/// }
244///
245/// impl eframe::App for MyEguiApp {
246/// fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame) {
247/// egui::CentralPanel::default().show_inside(ui, |ui| {
248/// ui.heading("Hello World!");
249/// });
250/// }
251/// }
252/// ```
253///
254/// # Errors
255/// This function can fail if we fail to set up a graphics context.
256#[cfg(not(target_arch = "wasm32"))]
257#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
258#[allow(clippy::allow_attributes, clippy::needless_pass_by_value)]
259pub fn run_native(
260 app_name: &str,
261 mut native_options: NativeOptions,
262 app_creator: AppCreator<'_>,
263) -> Result {
264 let renderer = init_native(app_name, &mut native_options);
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_no_default_features")]
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/// Provides a proxy for your native eframe application to run on your own event loop.
282///
283/// See `run_native` for details about `app_name`.
284///
285/// Call from `fn main` like this:
286/// ``` no_run
287/// use eframe::{egui, UserEvent};
288/// use winit::event_loop::{ControlFlow, EventLoop};
289///
290/// fn main() -> eframe::Result {
291/// let native_options = eframe::NativeOptions::default();
292/// let eventloop = EventLoop::<UserEvent>::with_user_event().build()?;
293/// eventloop.set_control_flow(ControlFlow::Poll);
294///
295/// let mut winit_app = eframe::create_native(
296/// "MyExtApp",
297/// native_options,
298/// Box::new(|cc| Ok(Box::new(MyEguiApp::new(cc)))),
299/// &eventloop,
300/// );
301///
302/// eventloop.run_app(&mut winit_app)?;
303///
304/// Ok(())
305/// }
306///
307/// #[derive(Default)]
308/// struct MyEguiApp {}
309///
310/// impl MyEguiApp {
311/// fn new(cc: &eframe::CreationContext<'_>) -> Self {
312/// Self::default()
313/// }
314/// }
315///
316/// impl eframe::App for MyEguiApp {
317/// fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame) {
318/// egui::CentralPanel::default().show_inside(ui, |ui| {
319/// ui.heading("Hello World!");
320/// });
321/// }
322/// }
323/// ```
324///
325/// See the `external_eventloop` example for a more complete example.
326#[cfg(not(target_arch = "wasm32"))]
327#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
328pub fn create_native<'a>(
329 app_name: &str,
330 mut native_options: NativeOptions,
331 app_creator: AppCreator<'a>,
332 event_loop: &winit::event_loop::EventLoop<UserEvent>,
333) -> EframeWinitApplication<'a> {
334 let renderer = init_native(app_name, &mut native_options);
335
336 match renderer {
337 #[cfg(feature = "glow")]
338 Renderer::Glow => {
339 log::debug!("Using the glow renderer");
340 EframeWinitApplication::new(native::run::create_glow(
341 app_name,
342 native_options,
343 app_creator,
344 event_loop,
345 ))
346 }
347
348 #[cfg(feature = "wgpu_no_default_features")]
349 Renderer::Wgpu => {
350 log::debug!("Using the wgpu renderer");
351 EframeWinitApplication::new(native::run::create_wgpu(
352 app_name,
353 native_options,
354 app_creator,
355 event_loop,
356 ))
357 }
358 }
359}
360
361#[cfg(not(target_arch = "wasm32"))]
362#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
363fn init_native(app_name: &str, native_options: &mut NativeOptions) -> Renderer {
364 #[cfg(not(feature = "__screenshot"))]
365 assert!(
366 std::env::var("EFRAME_SCREENSHOT_TO").is_err(),
367 "EFRAME_SCREENSHOT_TO found without compiling with the '__screenshot' feature"
368 );
369
370 if native_options.viewport.title.is_none() {
371 native_options.viewport.title = Some(app_name.to_owned());
372 }
373 if native_options.viewport.app_id.is_none() {
374 native_options.viewport.app_id = Some(app_name.to_owned());
375 }
376
377 let renderer = native_options.renderer;
378
379 #[cfg(all(feature = "glow", feature = "wgpu_no_default_features"))]
380 {
381 match native_options.renderer {
382 Renderer::Glow => "glow",
383 Renderer::Wgpu => "wgpu",
384 };
385 log::info!("Both the glow and wgpu renderers are available. Using {renderer}.");
386 }
387
388 renderer
389}
390
391// ----------------------------------------------------------------------------
392
393/// The simplest way to get started when writing a native app.
394///
395/// This does NOT support persistence of custom user data. For that you need to use [`run_native`].
396/// However, it DOES support persistence of egui data (window positions and sizes, how far the user has scrolled in a
397/// [`ScrollArea`](egui::ScrollArea), etc.) if the persistence feature is enabled.
398///
399/// # Example
400/// ``` no_run
401/// fn main() -> eframe::Result {
402/// // Our application state:
403/// let mut name = "Arthur".to_owned();
404/// let mut age = 42;
405///
406/// let options = eframe::NativeOptions::default();
407/// eframe::run_ui_native("My egui App", options, move |ui, _frame| {
408/// // Wrap everything in a CentralPanel so we get some margins and a background color:
409/// egui::CentralPanel::default().show_inside(ui, |ui| {
410/// ui.heading("My egui Application");
411/// ui.horizontal(|ui| {
412/// let name_label = ui.label("Your name: ");
413/// ui.text_edit_singleline(&mut name)
414/// .labelled_by(name_label.id);
415/// });
416/// ui.add(egui::Slider::new(&mut age, 0..=120).text("age"));
417/// if ui.button("Increment").clicked() {
418/// age += 1;
419/// }
420/// ui.label(format!("Hello '{name}', age {age}"));
421/// });
422/// })
423/// }
424/// ```
425///
426/// # Errors
427/// This function can fail if we fail to set up a graphics context.
428#[cfg(not(target_arch = "wasm32"))]
429#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
430pub fn run_ui_native(
431 app_name: &str,
432 native_options: NativeOptions,
433 ui_fun: impl FnMut(&mut egui::Ui, &mut Frame) + 'static,
434) -> Result {
435 struct SimpleApp<U> {
436 ui_fun: U,
437 }
438
439 impl<U: FnMut(&mut egui::Ui, &mut Frame) + 'static> App for SimpleApp<U> {
440 fn ui(&mut self, ui: &mut egui::Ui, frame: &mut Frame) {
441 (self.ui_fun)(ui, frame);
442 }
443 }
444
445 run_native(
446 app_name,
447 native_options,
448 Box::new(|_cc| Ok(Box::new(SimpleApp { ui_fun }))),
449 )
450}
451
452/// The simplest way to get started when writing a native app.
453///
454/// This does NOT support persistence of custom user data. For that you need to use [`run_native`].
455/// However, it DOES support persistence of egui data (window positions and sizes, how far the user has scrolled in a
456/// [`ScrollArea`](egui::ScrollArea), etc.) if the persistence feature is enabled.
457///
458/// # Example
459/// ``` no_run
460/// fn main() -> eframe::Result {
461/// // Our application state:
462/// let mut name = "Arthur".to_owned();
463/// let mut age = 42;
464///
465/// let options = eframe::NativeOptions::default();
466/// eframe::run_simple_native("My egui App", options, move |ctx, _frame| {
467/// egui::CentralPanel::default().show(ctx, |ui| {
468/// ui.heading("My egui Application");
469/// ui.horizontal(|ui| {
470/// let name_label = ui.label("Your name: ");
471/// ui.text_edit_singleline(&mut name)
472/// .labelled_by(name_label.id);
473/// });
474/// ui.add(egui::Slider::new(&mut age, 0..=120).text("age"));
475/// if ui.button("Increment").clicked() {
476/// age += 1;
477/// }
478/// ui.label(format!("Hello '{name}', age {age}"));
479/// });
480/// })
481/// }
482/// ```
483///
484/// # Errors
485/// This function can fail if we fail to set up a graphics context.
486#[deprecated = "Use run_ui_native instead"]
487#[cfg(not(target_arch = "wasm32"))]
488#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
489pub fn run_simple_native(
490 app_name: &str,
491 native_options: NativeOptions,
492 update_fun: impl FnMut(&egui::Context, &mut Frame) + 'static,
493) -> Result {
494 struct SimpleApp<U> {
495 update_fun: U,
496 }
497
498 impl<U: FnMut(&egui::Context, &mut Frame) + 'static> App for SimpleApp<U> {
499 fn ui(&mut self, _ui: &mut egui::Ui, _frame: &mut Frame) {}
500
501 fn update(&mut self, ctx: &egui::Context, frame: &mut Frame) {
502 (self.update_fun)(ctx, frame);
503 }
504 }
505
506 run_native(
507 app_name,
508 native_options,
509 Box::new(|_cc| Ok(Box::new(SimpleApp { update_fun }))),
510 )
511}
512
513// ----------------------------------------------------------------------------
514
515/// The different problems that can occur when trying to run `eframe`.
516#[derive(Debug)]
517pub enum Error {
518 /// Something went wrong in user code when creating the app.
519 AppCreation(Box<dyn std::error::Error + Send + Sync>),
520
521 /// An error from [`winit`].
522 #[cfg(not(target_arch = "wasm32"))]
523 Winit(winit::error::OsError),
524
525 /// An error from [`winit::event_loop::EventLoop`].
526 #[cfg(not(target_arch = "wasm32"))]
527 WinitEventLoop(winit::error::EventLoopError),
528
529 /// An error from [`glutin`] when using [`glow`].
530 #[cfg(all(feature = "glow", not(target_arch = "wasm32")))]
531 Glutin(glutin::error::Error),
532
533 /// An error from [`glutin`] when using [`glow`].
534 #[cfg(all(feature = "glow", not(target_arch = "wasm32")))]
535 NoGlutinConfigs(glutin::config::ConfigTemplate, Box<dyn std::error::Error>),
536
537 /// An error from [`glutin`] when using [`glow`].
538 #[cfg(feature = "glow")]
539 OpenGL(egui_glow::PainterError),
540
541 /// An error from [`wgpu`].
542 #[cfg(feature = "wgpu_no_default_features")]
543 Wgpu(egui_wgpu::WgpuError),
544}
545
546impl std::error::Error for Error {}
547
548#[cfg(not(target_arch = "wasm32"))]
549impl From<winit::error::OsError> for Error {
550 #[inline]
551 fn from(err: winit::error::OsError) -> Self {
552 Self::Winit(err)
553 }
554}
555
556#[cfg(not(target_arch = "wasm32"))]
557impl From<winit::error::EventLoopError> for Error {
558 #[inline]
559 fn from(err: winit::error::EventLoopError) -> Self {
560 Self::WinitEventLoop(err)
561 }
562}
563
564#[cfg(all(feature = "glow", not(target_arch = "wasm32")))]
565impl From<glutin::error::Error> for Error {
566 #[inline]
567 fn from(err: glutin::error::Error) -> Self {
568 Self::Glutin(err)
569 }
570}
571
572#[cfg(feature = "glow")]
573impl From<egui_glow::PainterError> for Error {
574 #[inline]
575 fn from(err: egui_glow::PainterError) -> Self {
576 Self::OpenGL(err)
577 }
578}
579
580#[cfg(feature = "wgpu_no_default_features")]
581impl From<egui_wgpu::WgpuError> for Error {
582 #[inline]
583 fn from(err: egui_wgpu::WgpuError) -> Self {
584 Self::Wgpu(err)
585 }
586}
587
588impl std::fmt::Display for Error {
589 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
590 match self {
591 Self::AppCreation(err) => write!(f, "app creation error: {err}"),
592
593 #[cfg(not(target_arch = "wasm32"))]
594 Self::Winit(err) => {
595 write!(f, "winit error: {err}")
596 }
597
598 #[cfg(not(target_arch = "wasm32"))]
599 Self::WinitEventLoop(err) => {
600 write!(f, "winit EventLoopError: {err}")
601 }
602
603 #[cfg(all(feature = "glow", not(target_arch = "wasm32")))]
604 Self::Glutin(err) => {
605 write!(f, "glutin error: {err}")
606 }
607
608 #[cfg(all(feature = "glow", not(target_arch = "wasm32")))]
609 Self::NoGlutinConfigs(template, err) => {
610 write!(
611 f,
612 "Found no glutin configs matching the template: {template:?}. Error: {err}"
613 )
614 }
615
616 #[cfg(feature = "glow")]
617 Self::OpenGL(err) => {
618 write!(f, "egui_glow: {err}")
619 }
620
621 #[cfg(feature = "wgpu_no_default_features")]
622 Self::Wgpu(err) => {
623 write!(f, "WGPU error: {err}")
624 }
625 }
626 }
627}
628
629/// Short for `Result<T, eframe::Error>`.
630pub type Result<T = (), E = Error> = std::result::Result<T, E>;