mod state;
use astrelis_core::profiling::profile_function;
use astrelis_render::{FrameContext, RenderableWindow};
use astrelis_winit::event::EventBatch;
use state::State;
pub use egui::{
self, Align, Align2, Color32, Context as EguiContext, FontFamily, FontId, Frame, Id, Key,
Label, Layout, Margin, Modifiers, Pos2, Rect, Response, RichText, Rounding, Sense, Slider,
Stroke, Style, TextEdit, TextStyle, Ui, Vec2, Visuals, Widget,
};
pub use state::EventResponse;
pub struct Egui {
context: egui::Context,
renderer: egui_wgpu::Renderer,
state: State,
full_output: Option<egui::FullOutput>,
}
impl Egui {
pub fn new(window: &RenderableWindow, graphics_ctx: &astrelis_render::GraphicsContext) -> Self {
let context = egui::Context::default();
let id = context.viewport_id();
let visuals = egui::Visuals::dark();
context.set_visuals(visuals);
let state = State::new(context.clone(), id, None, None);
let renderer = egui_wgpu::Renderer::new(
&graphics_ctx.device,
window.context().surface_config().format,
egui_wgpu::RendererOptions {
msaa_samples: 1,
depth_stencil_format: None,
dithering: false,
..Default::default()
},
);
Self {
context,
renderer,
state,
full_output: None,
}
}
pub fn ui(&mut self, window: &RenderableWindow, gui: impl FnMut(&egui::Context)) {
let raw_input = self.state.take_input(window);
self.full_output.replace(self.context.run(raw_input, gui));
}
pub fn render(&mut self, window: &RenderableWindow, frame: &mut FrameContext) {
profile_function!();
if self.full_output.is_none() {
return;
}
let full_output = self.full_output.take().unwrap();
self.state
.handle_platform_output(window, full_output.platform_output);
let graphics_ctx = frame.graphics_context();
let device = &graphics_ctx.device;
let queue = &graphics_ctx.queue;
let tris = self
.context
.tessellate(full_output.shapes, full_output.pixels_per_point);
for (id, image_delta) in &full_output.textures_delta.set {
self.renderer
.update_texture(device, queue, *id, &image_delta);
}
let config = window.context().surface_config();
let screen_descriptor = egui_wgpu::ScreenDescriptor {
size_in_pixels: [config.width, config.height],
pixels_per_point: full_output.pixels_per_point,
};
self.renderer
.update_buffers(device, queue, frame.encoder(), &tris, &screen_descriptor);
{
let (encoder, surface) = frame.encoder_and_surface();
let mut rpass = encoder
.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: surface.view(),
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Load,
store: wgpu::StoreOp::Store,
},
depth_slice: None,
})],
depth_stencil_attachment: None,
label: Some("Egui Render Pass"),
timestamp_writes: None,
occlusion_query_set: None,
})
.forget_lifetime();
self.renderer.render(&mut rpass, &tris, &screen_descriptor);
}
frame.increment_passes();
for x in &full_output.textures_delta.free {
self.renderer.free_texture(x)
}
}
pub fn handle_events(&mut self, window: &RenderableWindow, events: &mut EventBatch) -> bool {
let mut any_consumed = false;
events.dispatch(|event| {
let response = self.state.on_event(window, event);
if response.consumed {
any_consumed = true;
}
let mut status = astrelis_winit::event::HandleStatus::empty();
if response.repaint || response.consumed {
status |= astrelis_winit::event::HandleStatus::HANDLED;
}
if response.consumed {
status |= astrelis_winit::event::HandleStatus::CONSUMED;
}
status
});
any_consumed
}
pub fn context(&self) -> &egui::Context {
&self.context
}
pub fn register_wgpu_texture(
&mut self,
device: &wgpu::Device,
texture: &wgpu::TextureView,
filter: wgpu::FilterMode,
) -> egui::TextureId {
self.renderer
.register_native_texture(device, texture, filter)
}
pub fn unregister_texture(&mut self, id: egui::TextureId) {
self.renderer.free_texture(&id);
}
}