//! The [`WindowApp`] trait and per-callback context. use winit::event::WindowEvent; use winit::window::Window; use crate::input::InputState; use crate::math::Color; use crate::render::{Gpu, RenderContext}; /// An application driven by the engine's event loop. /// /// Implement this and pass the value to [`run`](super::run). All methods have /// empty defaults so minimal apps only override what they need. Per frame the /// engine calls [`event`](Self::event) for each pending window event, then /// [`update`](Self::update), then clears and presents the surface. /// /// This trait is the **window-event handler** — the per-frame plumbing between /// `winit` and a renderer. It is distinct from the engine's /// [`App`](crate::app::App) **container**, which owns the scene, assets, and /// scheduled systems. The editor's main loop typically implements this trait /// on a struct that *also* owns an `oxide_engine::app::App`. pub trait WindowApp { /// Called once, after the window and GPU context exist but before the /// first frame. fn init(&mut self, ctx: &mut AppCtx<'_>) { let _ = ctx; } /// Called for every raw window event (keyboard, mouse, resize, focus, …). /// /// Events the engine itself reacts to (close request, resize) are still /// forwarded here afterwards, so apps observe everything. fn event(&mut self, ctx: &mut AppCtx<'_>, event: &WindowEvent) { let _ = (ctx, event); } /// Called once per frame, before the frame is rendered. fn update(&mut self, ctx: &mut AppCtx<'_>) { let _ = ctx; } /// Called each frame after the surface has been cleared and before it is /// presented, so the app can record its own draw commands into the frame. /// /// This is the hook editor/overlay UI (egui) and, in later stages, the /// scene renderer draw through. The surface is cleared with /// [`LoadOp::Clear`](wgpu::LoadOp::Clear) *before* this runs; record passes /// here with [`LoadOp::Load`](wgpu::LoadOp::Load) to draw on top of the /// clear color rather than wiping it. fn render(&mut self, ctx: &RenderCtx<'_>) { let _ = ctx; } } /// Per-frame rendering context passed to [`WindowApp::render`]. /// /// Unlike [`AppCtx`], this borrows the GPU and surface immutably: by the time /// the draw hook runs the frame's surface texture is already acquired, so the /// app receives the handles it needs to record additional passes into /// [`view`](Self::view) without re-entering the render context. pub struct RenderCtx<'a> { /// The GPU device/queue to record and submit commands with. pub gpu: &'a Gpu, /// The current frame's surface texture view (the render target). pub view: &'a wgpu::TextureView, /// The window being rendered, e.g. for input/UI integration that needs it. pub window: &'a Window, /// The surface's texture format, needed to build matching pipelines. pub surface_format: wgpu::TextureFormat, /// Surface size in physical pixels (`width`, `height`). pub size: (u32, u32), } /// Engine state handed to every [`WindowApp`] callback. pub struct AppCtx<'a> { pub(crate) render: &'a mut RenderContext, pub(crate) window: &'a Window, pub(crate) exit: &'a mut bool, pub(crate) input: &'a InputState, /// Seconds elapsed since the previous frame (`0.0` during /// [`App::init`] and the first frame). pub dt: f32, } impl AppCtx<'_> { /// The render context driving the window surface. pub fn render(&mut self) -> &mut RenderContext { self.render } /// Sets the color the surface is cleared to, effective next frame. pub fn set_clear_color(&mut self, color: Color) { self.render.set_clear_color(color); } /// The current clear color. pub fn clear_color(&self) -> Color { self.render.clear_color() } /// Current surface size in physical pixels. pub fn size(&self) -> (u32, u32) { self.render.size() } /// Sets the window title. pub fn set_title(&self, title: &str) { self.window.set_title(title); } /// The window being driven, e.g. to construct UI/input integration that /// needs a window handle. pub fn window(&self) -> &Window { self.window } /// Asks the event loop to exit after the current callback returns. pub fn request_exit(&mut self) { *self.exit = true; } /// The per-frame input snapshot. /// /// Reflects every keyboard / mouse / scroll event delivered since the /// previous frame's `update` returned. In [`WindowApp::event`] callbacks /// it includes the event currently being delivered (the runner pumps it /// before invoking the callback). In [`WindowApp::update`] it is the /// accumulated state for the new frame; the runner clears edges (pressed/ /// released, mouse delta, scroll) automatically after `update` returns. pub fn input(&self) -> &InputState { self.input } }