#![warn(missing_docs)]
#[cfg(not(target_arch = "wasm32"))]
mod icon_data;
#[cfg(not(target_arch = "wasm32"))]
pub use icon_data::IconData;
#[cfg(target_arch = "wasm32")]
use std::any::Any;
#[cfg(not(target_arch = "wasm32"))]
#[cfg(any(feature = "glow", feature = "wgpu"))]
pub use crate::native::run::UserEvent;
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);
#[cfg(target_arch = "wasm32")]
fn as_any_mut(&mut self) -> Option<&mut dyn Any> {
None
}
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) -> [f32; 4] {
egui::Color32::from_rgba_unmultiplied(12, 12, 12, 180).to_normalized_gamma_f32()
}
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"))]
pub struct NativeOptions {
pub always_on_top: bool,
pub maximized: bool,
pub decorated: bool,
pub fullscreen: bool,
#[cfg(target_os = "macos")]
pub fullsize_content: 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 mouse_passthrough: bool,
pub active: bool,
pub vsync: bool,
pub multisampling: u16,
pub depth_buffer: u8,
pub stencil_buffer: u8,
pub hardware_acceleration: HardwareAcceleration,
#[cfg(any(feature = "glow", feature = "wgpu"))]
pub renderer: Renderer,
pub follow_system_theme: bool,
pub default_theme: Theme,
pub run_and_return: bool,
#[cfg(feature = "glow")]
pub shader_version: Option<egui_glow::ShaderVersion>,
pub centered: bool,
#[cfg(feature = "wgpu")]
pub wgpu_options: egui_wgpu::WgpuConfiguration,
pub app_id: Option<String>,
}
#[cfg(not(target_arch = "wasm32"))]
impl Clone for NativeOptions {
fn clone(&self) -> Self {
Self {
icon_data: self.icon_data.clone(),
#[cfg(feature = "wgpu")]
wgpu_options: self.wgpu_options.clone(),
app_id: self.app_id.clone(),
..*self
}
}
}
#[cfg(not(target_arch = "wasm32"))]
impl Default for NativeOptions {
fn default() -> Self {
Self {
always_on_top: false,
maximized: false,
decorated: true,
fullscreen: false,
#[cfg(target_os = "macos")]
fullsize_content: false,
icon_data: Some(
IconData::try_from_png_bytes(&include_bytes!("../../data/icon.png")[..]).unwrap(),
),
drag_and_drop_support: true,
initial_window_pos: None,
initial_window_size: None,
min_window_size: None,
max_window_size: None,
resizable: true,
transparent: false,
mouse_passthrough: false,
active: true,
vsync: true,
multisampling: 0,
depth_buffer: 0,
stencil_buffer: 0,
hardware_acceleration: HardwareAcceleration::Preferred,
#[cfg(any(feature = "glow", feature = "wgpu"))]
renderer: Renderer::default(),
follow_system_theme: cfg!(target_os = "macos") || cfg!(target_os = "windows"),
default_theme: Theme::Dark,
run_and_return: true,
#[cfg(feature = "glow")]
shader_version: None,
centered: false,
#[cfg(feature = "wgpu")]
wgpu_options: egui_wgpu::WgpuConfiguration::default(),
app_id: None,
}
}
}
#[cfg(target_arch = "wasm32")]
pub struct WebOptions {
pub follow_system_theme: bool,
pub default_theme: Theme,
pub depth_buffer: u8,
#[cfg(feature = "glow")]
pub webgl_context_option: WebGlContextOption,
#[cfg(feature = "wgpu")]
pub wgpu_options: egui_wgpu::WgpuConfiguration,
}
#[cfg(target_arch = "wasm32")]
impl Default for WebOptions {
fn default() -> Self {
Self {
follow_system_theme: true,
default_theme: Theme::Dark,
depth_buffer: 0,
#[cfg(feature = "glow")]
webgl_context_option: WebGlContextOption::BestFirst,
#[cfg(feature = "wgpu")]
wgpu_options: egui_wgpu::WgpuConfiguration::default(),
}
}
}
#[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,
}
#[cfg(any(feature = "glow", feature = "wgpu"))]
#[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,
}
#[cfg(any(feature = "glow", feature = "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'");
}
}
#[cfg(any(feature = "glow", feature = "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),
}
}
}
#[cfg(any(feature = "glow", feature = "wgpu"))]
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."))
}
}
}
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>,
#[cfg(not(target_arch = "wasm32"))]
pub(crate) screenshot: std::cell::Cell<Option<egui::ColorImage>>,
}
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()
}
#[cfg(not(target_arch = "wasm32"))]
pub fn request_screenshot(&mut self) {
self.output.screenshot_requested = true;
}
#[cfg(not(target_arch = "wasm32"))]
pub fn cancel_screenshot_request(&mut self) {
self.output.screenshot_requested = false;
}
#[cfg(not(target_arch = "wasm32"))]
pub fn screenshot(&self) -> Option<egui::ColorImage> {
self.screenshot.take()
}
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) {
log::debug!("eframe_tao::Frame::close called");
self.output.close = true;
}
#[cfg(not(target_arch = "wasm32"))]
pub fn set_minimized(&mut self, minimized: bool) {
self.output.minimized = Some(minimized);
}
#[cfg(not(target_arch = "wasm32"))]
pub fn focus(&mut self) {
self.output.focus = Some(true);
}
#[cfg(not(target_arch = "wasm32"))]
pub fn request_user_attention(&mut self, kind: egui::UserAttentionType) {
self.output.attention = Some(kind);
}
#[cfg(not(target_arch = "wasm32"))]
pub fn set_maximized(&mut self, maximized: bool) {
self.output.maximized = Some(maximized);
}
#[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);
self.info.window_info.size = 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);
self.info.window_info.fullscreen = fullscreen; }
#[cfg(not(target_arch = "wasm32"))]
pub fn set_window_pos(&mut self, pos: egui::Pos2) {
self.output.window_pos = Some(pos);
self.info.window_info.position = 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);
}
#[cfg(not(target_arch = "wasm32"))]
pub fn set_always_on_top(&mut self, always_on_top: bool) {
self.output.always_on_top = Some(always_on_top);
}
#[cfg(not(target_arch = "wasm32"))]
pub fn set_centered(&mut self) {
if let Some(monitor_size) = self.info.window_info.monitor_size {
let inner_size = self.info.window_info.size;
if monitor_size.x > 1.0 && monitor_size.y > 1.0 {
let x = (monitor_size.x - inner_size.x) / 2.0;
let y = (monitor_size.y - inner_size.y) / 2.0;
self.set_window_pos(egui::Pos2 { x, y });
}
}
}
#[cfg(any(feature = "glow", feature = "wgpu"))]
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 user_agent: String,
pub location: Location,
}
#[cfg(not(target_arch = "wasm32"))]
#[derive(Clone, Debug)]
pub struct WindowInfo {
pub position: Option<egui::Pos2>,
pub fullscreen: bool,
pub minimized: bool,
pub maximized: bool,
pub focused: bool,
pub size: egui::Vec2,
pub monitor_size: Option<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| match ron::from_str(&value) {
Ok(value) => Some(value),
Err(err) => {
log::warn!("Failed to decode RON: {err}");
None
}
})
}
#[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) => log::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>,
#[cfg(not(target_arch = "wasm32"))]
pub always_on_top: Option<bool>,
#[cfg(not(target_arch = "wasm32"))]
pub minimized: Option<bool>,
#[cfg(not(target_arch = "wasm32"))]
pub maximized: Option<bool>,
#[cfg(not(target_arch = "wasm32"))]
pub focus: Option<bool>,
#[cfg(not(target_arch = "wasm32"))]
pub attention: Option<egui::UserAttentionType>,
#[cfg(not(target_arch = "wasm32"))]
pub screenshot_requested: bool,
}
}