khora_core/ui/editor_overlay.rs
1// Copyright 2025 eraflo
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Abstract trait for editor overlay rendering.
16//!
17//! The [`EditorOverlay`] trait defines the interface for rendering an immediate-mode
18//! UI overlay on top of the engine's 3D scene. The concrete implementation lives in
19//! `khora-infra` (e.g., `EguiOverlay` backed by egui + wgpu).
20//!
21//! # Architecture
22//!
23//! ```text
24//! khora-core → EditorOverlay trait (this file)
25//! khora-infra → EguiOverlay : impl EditorOverlay (egui + custom wgpu renderer)
26//! khora-sdk → EngineState integrates the overlay into the frame loop
27//! khora-editor → Application that builds the editor UI via the shared context
28//! ```
29
30use std::any::Any;
31use std::fmt;
32
33/// Descriptor for the current screen state, passed to the overlay each frame.
34#[derive(Debug, Clone, Copy)]
35pub struct OverlayScreenDescriptor {
36 /// Width of the render target in physical pixels.
37 pub width_px: u32,
38 /// Height of the render target in physical pixels.
39 pub height_px: u32,
40 /// HiDPI scale factor (physical pixels per logical point).
41 pub scale_factor: f32,
42}
43
44/// Error type for overlay operations.
45#[derive(Debug)]
46pub struct OverlayError(pub String);
47
48impl fmt::Display for OverlayError {
49 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50 write!(f, "OverlayError: {}", self.0)
51 }
52}
53
54impl std::error::Error for OverlayError {}
55
56/// Abstract trait for an editor UI overlay rendered on top of the 3D scene.
57///
58/// The overlay manages its own UI context (e.g., `egui::Context`) and renderer.
59/// It processes input events, builds UI each frame, and renders the result
60/// as a final pass over the swapchain texture.
61///
62/// # Lifecycle per frame
63///
64/// 1. [`handle_window_event`](Self::handle_window_event) — called for each raw
65/// window event (winit). Returns `true` if the overlay consumed the event.
66/// 2. [`begin_frame`](Self::begin_frame) — starts a new UI frame.
67/// 3. *(Application builds UI using the context returned by [`ui_context`](Self::ui_context))*
68/// 4. [`end_frame_and_render`](Self::end_frame_and_render) — finalizes the UI,
69/// tessellates, and renders onto the current render target.
70pub trait EditorOverlay: Send + Sync {
71 /// Process a raw window event for the overlay.
72 ///
73 /// The `event` parameter is a type-erased `winit::event::WindowEvent`.
74 ///
75 /// Returns `true` if the overlay consumed the event (e.g., the cursor is
76 /// over an overlay panel). When `true`, the engine should **not** forward
77 /// the event to the game's input system.
78 fn handle_window_event(&mut self, window: &dyn Any, event: &dyn Any) -> bool;
79
80 /// Starts a new overlay frame.
81 ///
82 /// `window` is a type-erased `winit::window::Window` reference, used by
83 /// the input translation layer to read screen size and scale factor.
84 ///
85 /// Must be called exactly once per frame, before the application builds its UI.
86 fn begin_frame(&mut self, window: &dyn Any, screen: OverlayScreenDescriptor);
87
88 /// Returns the UI context as a type-erased reference.
89 ///
90 /// The concrete type is `egui::Context` for the egui backend.
91 /// The editor (`khora-editor`) downcasts this to build its panels.
92 fn ui_context(&self) -> &dyn Any;
93
94 /// Ends the current frame and renders the overlay.
95 ///
96 /// `render_state` is a type-erased struct containing the GPU resources
97 /// needed for rendering (device, queue, encoder, target view, etc.).
98 /// The concrete type depends on the backend.
99 fn end_frame_and_render(&mut self, render_state: &mut dyn Any) -> Result<(), OverlayError>;
100
101 /// Returns `true` if the overlay wants exclusive pointer input this frame.
102 ///
103 /// When `true`, pointer events (clicks, drags) should not be forwarded to the game.
104 fn wants_pointer_input(&self) -> bool;
105
106 /// Returns `true` if the overlay wants exclusive keyboard input this frame.
107 ///
108 /// When `true`, keyboard events should not be forwarded to the game.
109 fn wants_keyboard_input(&self) -> bool;
110
111 /// Downcasting support.
112 fn as_any(&self) -> &dyn Any;
113
114 /// Mutable downcasting support.
115 fn as_any_mut(&mut self) -> &mut dyn Any;
116}