pub use egui;
use egui::{ClippedPrimitive, FullOutput, PlatformOutput, RawInput, TexturesDelta};
pub use raw_window_handle;
use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle};
use std::time::Duration;
#[derive(Debug, Clone)]
pub struct BackendConfig {
pub is_opengl: bool,
pub opengl_config: Option<OpenGlConfig>,
pub transparent: Option<bool>,
}
impl Default for BackendConfig {
fn default() -> Self {
let is_opengl = true;
Self {
is_opengl,
transparent: None,
opengl_config: Default::default(),
}
}
}
pub trait WindowBackend: Sized {
type Configuration: Default + Sized;
type WindowType: HasRawDisplayHandle + HasRawWindowHandle + Sized;
fn new(config: Self::Configuration, backend_config: BackendConfig) -> Self;
fn take_raw_input(&mut self) -> RawInput;
fn get_window(&mut self) -> Option<&mut Self::WindowType>;
fn get_live_physical_size_framebuffer(&mut self) -> Option<[u32; 2]>;
fn run_event_loop<U: UserApp<UserWindowBackend = Self> + 'static>(user_app: U);
fn get_config(&self) -> &BackendConfig;
fn swap_buffers(&mut self) {
unimplemented!("swap buffers is not implemented for this window backend");
}
fn is_opengl(&self) -> bool;
fn get_proc_address(&mut self, symbol: &str) -> *const core::ffi::c_void {
unimplemented!(
"get_proc_address is not implemented for this window backend. called with {symbol}"
);
}
fn set_window_title(&mut self, title: &str);
fn get_window_position(&mut self) -> Option<[f32; 2]>;
fn set_window_position(&mut self, pos: [f32; 2]);
fn get_window_size(&mut self) -> Option<[f32; 2]>;
fn set_window_size(&mut self, size: [f32; 2]);
fn get_window_minimized(&mut self) -> Option<bool>;
fn set_minimize_window(&mut self, min: bool);
fn get_window_maximized(&mut self) -> Option<bool>;
fn set_maximize_window(&mut self, max: bool);
fn get_window_visibility(&mut self) -> Option<bool>;
fn set_window_visibility(&mut self, vis: bool);
fn get_always_on_top(&mut self) -> Option<bool>;
fn set_always_on_top(&mut self, always_on_top: bool);
fn get_passthrough(&mut self) -> Option<bool>;
fn set_passthrough(&mut self, passthrough: bool);
}
pub trait GfxBackend {
type Configuration: Default;
fn new(window_backend: &mut impl WindowBackend, config: Self::Configuration) -> Self;
fn suspend(&mut self, _window_backend: &mut impl WindowBackend) {
unimplemented!("This window backend doesn't implement suspend event");
}
fn resume(&mut self, _window_backend: &mut impl WindowBackend) {}
fn resize_framebuffer(&mut self, window_backend: &mut impl WindowBackend);
fn prepare_frame(&mut self, window_backend: &mut impl WindowBackend);
fn render_egui(
&mut self,
meshes: Vec<ClippedPrimitive>,
textures_delta: TexturesDelta,
logical_screen_size: [f32; 2],
);
fn present(&mut self, window_backend: &mut impl WindowBackend);
}
pub trait UserApp {
type UserGfxBackend: GfxBackend;
type UserWindowBackend: WindowBackend;
fn get_all(
&mut self,
) -> (
&mut Self::UserWindowBackend,
&mut Self::UserGfxBackend,
&egui::Context,
);
fn resize_framebuffer(&mut self) {
let (wb, gb, _) = self.get_all();
gb.resize_framebuffer(wb);
}
fn resume(&mut self) {
let (wb, gb, _) = self.get_all();
gb.resume(wb);
}
fn suspend(&mut self) {
let (wb, gb, _) = self.get_all();
gb.suspend(wb);
}
fn run(&mut self, logical_size: [f32; 2]) -> Option<(PlatformOutput, Duration)> {
let (wb, gb, egui_context) = self.get_all();
let egui_context = egui_context.clone();
if let Some(full_output) = if wb.get_window().is_some() {
let input = wb.take_raw_input();
gb.prepare_frame(wb);
egui_context.begin_frame(input);
self.gui_run();
Some(egui_context.end_frame())
} else {
None
} {
let FullOutput {
platform_output,
repaint_after,
textures_delta,
shapes,
} = full_output;
let (wb, gb, egui_context) = self.get_all();
let egui_context = egui_context.clone();
gb.render_egui(
egui_context.tessellate(shapes),
textures_delta,
logical_size,
);
gb.present(wb);
return Some((platform_output, repaint_after));
}
None
}
fn gui_run(&mut self);
}
pub mod util {
pub fn scissor_from_clip_rect(
clip_rect: &egui::Rect,
scale: f32,
physical_framebuffer_size: [u32; 2],
) -> Option<[u32; 4]> {
let clip_min_x = scale * clip_rect.min.x;
let clip_min_y = scale * clip_rect.min.y;
let clip_max_x = scale * clip_rect.max.x;
let clip_max_y = scale * clip_rect.max.y;
let clip_min_x = clip_min_x.round() as i32;
let clip_min_y = clip_min_y.round() as i32;
let clip_max_x = clip_max_x.round() as i32;
let clip_max_y = clip_max_y.round() as i32;
let clip_min_x = clip_min_x.clamp(0, physical_framebuffer_size[0] as i32);
let clip_min_y = clip_min_y.clamp(0, physical_framebuffer_size[1] as i32);
let clip_max_x = clip_max_x.clamp(clip_min_x, physical_framebuffer_size[0] as i32);
let clip_max_y = clip_max_y.clamp(clip_min_y, physical_framebuffer_size[1] as i32);
let x = clip_min_x as u32;
let y = clip_min_y as u32;
let width = (clip_max_x - clip_min_x) as u32;
let height = (clip_max_y - clip_min_y) as u32;
(width != 0 && height != 0).then_some([x, y, width, height])
}
pub fn scissor_from_clip_rect_opengl(
clip_rect: &egui::Rect,
scale: f32,
physical_framebuffer_size: [u32; 2],
) -> Option<[u32; 4]> {
scissor_from_clip_rect(clip_rect, scale, physical_framebuffer_size).map(|mut arr| {
arr[1] = physical_framebuffer_size[1] - (arr[1] + arr[3]);
arr
})
}
}
#[derive(Debug, Clone, Default)]
pub struct OpenGlConfig {
pub major: Option<u8>,
pub minor: Option<u8>,
pub es: Option<bool>,
pub srgb: Option<bool>,
pub depth: Option<u8>,
pub stencil: Option<u8>,
pub color_bits: Option<[u8; 4]>,
pub multi_samples: Option<u8>,
pub core: Option<bool>,
}