#![warn(missing_docs)]
pub type AppCreator = Box<dyn FnOnce(&CreationContext<'_>) -> Box<dyn App>>;
pub struct CreationContext<'s> {
pub egui_ctx: egui::Context,
pub integration_info: IntegrationInfo,
pub storage: Option<&'s dyn Storage>,
#[cfg(feature = "glow")]
pub gl: Option<std::sync::Arc<glow::Context>>,
#[cfg(feature = "wgpu")]
pub wgpu_render_state: Option<egui_wgpu::RenderState>,
}
pub trait App {
fn update(&mut self, ctx: &egui::Context, frame: &mut Frame);
fn save(&mut self, _storage: &mut dyn Storage) {}
#[cfg(not(target_arch = "wasm32"))]
#[doc(alias = "exit")]
#[doc(alias = "quit")]
fn on_close_event(&mut self) -> bool {
true
}
#[cfg(feature = "glow")]
fn on_exit(&mut self, _gl: Option<&glow::Context>) {}
#[cfg(not(feature = "glow"))]
fn on_exit(&mut self) {}
fn auto_save_interval(&self) -> std::time::Duration {
std::time::Duration::from_secs(30)
}
fn max_size_points(&self) -> egui::Vec2 {
egui::Vec2::INFINITY
}
fn clear_color(&self, _visuals: &egui::Visuals) -> egui::Rgba {
egui::Color32::from_rgba_unmultiplied(12, 12, 12, 180).into()
}
fn persist_native_window(&self) -> bool {
true
}
fn persist_egui_memory(&self) -> bool {
true
}
fn warm_up_enabled(&self) -> bool {
false
}
fn post_rendering(&mut self, _window_size_px: [u32; 2], _frame: &Frame) {}
}
#[cfg(not(target_arch = "wasm32"))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum HardwareAcceleration {
Required,
Preferred,
Off,
}
#[cfg(not(target_arch = "wasm32"))]
#[derive(Clone)]
pub struct NativeOptions {
pub always_on_top: bool,
pub maximized: bool,
pub decorated: bool,
pub fullscreen: bool,
pub drag_and_drop_support: bool,
pub icon_data: Option<IconData>,
pub initial_window_pos: Option<egui::Pos2>,
pub initial_window_size: Option<egui::Vec2>,
pub min_window_size: Option<egui::Vec2>,
pub max_window_size: Option<egui::Vec2>,
pub resizable: bool,
pub transparent: bool,
pub vsync: bool,
pub multisampling: u16,
pub depth_buffer: u8,
pub stencil_buffer: u8,
pub hardware_acceleration: HardwareAcceleration,
pub renderer: Renderer,
pub follow_system_theme: bool,
pub default_theme: Theme,
pub run_and_return: bool,
}
#[cfg(not(target_arch = "wasm32"))]
impl Default for NativeOptions {
fn default() -> Self {
Self {
always_on_top: false,
maximized: false,
decorated: true,
fullscreen: false,
drag_and_drop_support: true,
icon_data: None,
initial_window_pos: None,
initial_window_size: None,
min_window_size: None,
max_window_size: None,
resizable: true,
transparent: false,
vsync: true,
multisampling: 0,
depth_buffer: 0,
stencil_buffer: 0,
hardware_acceleration: HardwareAcceleration::Preferred,
renderer: Renderer::default(),
follow_system_theme: cfg!(target_os = "macos") || cfg!(target_os = "windows"),
default_theme: Theme::Dark,
run_and_return: true,
}
}
}
#[cfg(not(target_arch = "wasm32"))]
impl NativeOptions {
#[cfg(feature = "dark-light")]
pub fn system_theme(&self) -> Option<Theme> {
if self.follow_system_theme {
crate::profile_scope!("dark_light::detect");
match dark_light::detect() {
dark_light::Mode::Dark => Some(Theme::Dark),
dark_light::Mode::Light => Some(Theme::Light),
}
} else {
None
}
}
#[cfg(not(feature = "dark-light"))]
pub fn system_theme(&self) -> Option<Theme> {
None
}
}
#[cfg(target_arch = "wasm32")]
pub struct WebOptions {
pub follow_system_theme: bool,
pub default_theme: Theme,
pub webgl_context_option: WebGlContextOption,
}
#[cfg(target_arch = "wasm32")]
impl Default for WebOptions {
fn default() -> Self {
Self {
follow_system_theme: true,
default_theme: Theme::Dark,
webgl_context_option: WebGlContextOption::BestFirst,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub enum Theme {
Dark,
Light,
}
impl Theme {
pub fn egui_visuals(self) -> egui::Visuals {
match self {
Self::Dark => egui::Visuals::dark(),
Self::Light => egui::Visuals::light(),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub enum WebGlContextOption {
WebGl1,
WebGl2,
BestFirst,
CompatibilityFirst,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub enum Renderer {
#[cfg(feature = "glow")]
Glow,
#[cfg(feature = "wgpu")]
Wgpu,
}
impl Default for Renderer {
fn default() -> Self {
#[cfg(feature = "glow")]
return Self::Glow;
#[cfg(not(feature = "glow"))]
#[cfg(feature = "wgpu")]
return Self::Wgpu;
#[cfg(not(feature = "glow"))]
#[cfg(not(feature = "wgpu"))]
compile_error!("eframe: you must enable at least one of the rendering backend features: 'glow' or 'wgpu'");
}
}
impl std::fmt::Display for Renderer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
#[cfg(feature = "glow")]
Self::Glow => "glow".fmt(f),
#[cfg(feature = "wgpu")]
Self::Wgpu => "wgpu".fmt(f),
}
}
}
impl std::str::FromStr for Renderer {
type Err = String;
fn from_str(name: &str) -> Result<Self, String> {
match name.to_lowercase().as_str() {
#[cfg(feature = "glow")]
"glow" => Ok(Self::Glow),
#[cfg(feature = "wgpu")]
"wgpu" => Ok(Self::Wgpu),
_ => Err(format!("eframe renderer {name:?} is not available. Make sure that the corresponding eframe feature is enabled."))
}
}
}
#[derive(Clone)]
pub struct IconData {
pub rgba: Vec<u8>,
pub width: u32,
pub height: u32,
}
pub struct Frame {
pub(crate) info: IntegrationInfo,
pub(crate) output: backend::AppOutput,
pub(crate) storage: Option<Box<dyn Storage>>,
#[cfg(feature = "glow")]
pub(crate) gl: Option<std::sync::Arc<glow::Context>>,
#[cfg(feature = "wgpu")]
pub(crate) wgpu_render_state: Option<egui_wgpu::RenderState>,
}
impl Frame {
#[allow(clippy::unused_self)]
pub fn is_web(&self) -> bool {
cfg!(target_arch = "wasm32")
}
pub fn info(&self) -> IntegrationInfo {
self.info.clone()
}
pub fn storage(&self) -> Option<&dyn Storage> {
self.storage.as_deref()
}
pub fn storage_mut(&mut self) -> Option<&mut (dyn Storage + 'static)> {
self.storage.as_deref_mut()
}
#[cfg(feature = "glow")]
pub fn gl(&self) -> Option<&std::sync::Arc<glow::Context>> {
self.gl.as_ref()
}
#[cfg(feature = "wgpu")]
pub fn wgpu_render_state(&self) -> Option<&egui_wgpu::RenderState> {
self.wgpu_render_state.as_ref()
}
#[cfg(not(target_arch = "wasm32"))]
#[doc(alias = "exit")]
#[doc(alias = "quit")]
pub fn close(&mut self) {
self.output.close = true;
}
#[cfg(not(target_arch = "wasm32"))]
#[deprecated = "Renamed `close`"]
pub fn quit(&mut self) {
self.close();
}
#[cfg(not(target_arch = "wasm32"))]
pub fn set_window_size(&mut self, size: egui::Vec2) {
self.output.window_size = Some(size);
}
#[cfg(not(target_arch = "wasm32"))]
pub fn set_window_title(&mut self, title: &str) {
self.output.window_title = Some(title.to_owned());
}
#[cfg(not(target_arch = "wasm32"))]
pub fn set_decorations(&mut self, decorated: bool) {
self.output.decorated = Some(decorated);
}
#[cfg(not(target_arch = "wasm32"))]
pub fn set_fullscreen(&mut self, fullscreen: bool) {
self.output.fullscreen = Some(fullscreen);
}
#[cfg(not(target_arch = "wasm32"))]
pub fn set_window_pos(&mut self, pos: egui::Pos2) {
self.output.window_pos = Some(pos);
}
#[cfg(not(target_arch = "wasm32"))]
pub fn drag_window(&mut self) {
self.output.drag_window = true;
}
#[cfg(not(target_arch = "wasm32"))]
pub fn set_visible(&mut self, visible: bool) {
self.output.visible = Some(visible);
}
pub(crate) fn take_app_output(&mut self) -> backend::AppOutput {
std::mem::take(&mut self.output)
}
}
#[derive(Clone, Debug)]
#[cfg(target_arch = "wasm32")]
pub struct WebInfo {
pub location: Location,
}
#[cfg(not(target_arch = "wasm32"))]
#[derive(Clone, Debug)]
pub struct WindowInfo {
pub position: Option<egui::Pos2>,
pub fullscreen: bool,
pub size: egui::Vec2,
}
#[cfg(target_arch = "wasm32")]
#[derive(Clone, Debug)]
pub struct Location {
pub url: String,
pub protocol: String,
pub host: String,
pub hostname: String,
pub port: String,
pub hash: String,
pub query: String,
pub query_map: std::collections::BTreeMap<String, String>,
pub origin: String,
}
#[derive(Clone, Debug)]
pub struct IntegrationInfo {
#[cfg(target_arch = "wasm32")]
pub web_info: WebInfo,
pub system_theme: Option<Theme>,
pub cpu_usage: Option<f32>,
pub native_pixels_per_point: Option<f32>,
#[cfg(not(target_arch = "wasm32"))]
pub window_info: WindowInfo,
}
pub trait Storage {
fn get_string(&self, key: &str) -> Option<String>;
fn set_string(&mut self, key: &str, value: String);
fn flush(&mut self);
}
#[derive(Clone, Default)]
pub(crate) struct DummyStorage {}
impl Storage for DummyStorage {
fn get_string(&self, _key: &str) -> Option<String> {
None
}
fn set_string(&mut self, _key: &str, _value: String) {}
fn flush(&mut self) {}
}
#[cfg(feature = "ron")]
pub fn get_value<T: serde::de::DeserializeOwned>(storage: &dyn Storage, key: &str) -> Option<T> {
storage
.get_string(key)
.and_then(|value| ron::from_str(&value).ok())
}
#[cfg(feature = "ron")]
pub fn set_value<T: serde::Serialize>(storage: &mut dyn Storage, key: &str, value: &T) {
match ron::ser::to_string(value) {
Ok(string) => storage.set_string(key, string),
Err(err) => tracing::error!("eframe failed to encode data using ron: {}", err),
}
}
pub const APP_KEY: &str = "app";
pub(crate) mod backend {
#[derive(Clone, Debug, Default)]
#[must_use]
pub struct AppOutput {
#[cfg(not(target_arch = "wasm32"))]
pub close: bool,
#[cfg(not(target_arch = "wasm32"))]
pub window_size: Option<egui::Vec2>,
#[cfg(not(target_arch = "wasm32"))]
pub window_title: Option<String>,
#[cfg(not(target_arch = "wasm32"))]
pub decorated: Option<bool>,
#[cfg(not(target_arch = "wasm32"))] pub fullscreen: Option<bool>,
#[cfg(not(target_arch = "wasm32"))]
pub drag_window: bool,
#[cfg(not(target_arch = "wasm32"))]
pub window_pos: Option<egui::Pos2>,
#[cfg(not(target_arch = "wasm32"))]
pub visible: Option<bool>,
}
}