Skip to main content

cvkg_core/
renderer_trait.rs

1use crate::error_types::CvkgError;
2use crate::*;
3
4/// Vertical alignment of text within its bounding box.
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum TextVAlign {
7    Top,
8    Middle,
9    Bottom,
10}
11
12/// Horizontal alignment of text within its bounding box.
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum TextHAlign {
15    Left,
16    Center,
17    Right,
18}
19
20pub trait ElapsedTime {
21    /// Returns the cumulative time since the renderer started in seconds.
22    fn elapsed_time(&self) -> f32;
23
24    /// Returns the time elapsed since the last frame in seconds.
25    fn delta_time(&self) -> f32;
26}
27
28/// The Renderer trait defines the atomic drawing operations for all CVKG backends.
29/// This trait is object-safe and used by the View::render system.
30/// # Implementation Requirements
31/// 1. Coordinate system is origin-top-left (0,0) with Y increasing downwards.
32/// 2. Colors are [R, G, B, A] in the [0.0, 1.0] range.
33/// 3. All operations must be batchable by the underlying backend.
34///
35/// Sub-traits in `renderer/mod.rs` (RendererCore, RendererShapes, etc.) are
36/// capability markers. Backends implement the monolithic `Renderer` trait.
37/// The sub-traits exist so consumer code can depend on only the capability
38/// slice it needs (e.g., `fn render<R: RendererShapes>(shapes: R)`).
39/// Callback interface for renderer error reporting.
40///
41/// Backends override `on_render_error` to intercept non-fatal errors that occur
42/// during drawing operations. The default implementation logs the error.
43///
44/// Design note: `render()` stays infallible to avoid proliferating `Result`
45/// through the entire View trait hierarchy. Errors that cannot be recovered from
46/// within a draw call are routed through this trait method instead.
47pub trait RendererErrorHandler {
48    /// Called when a non-fatal render error occurs during a draw operation.
49    /// The renderer continues operating. Backends should log and optionally
50    /// track error counts for health monitoring.
51    fn on_render_error(&mut self, error: &CvkgError) {
52        tracing::error!("[RenderError] {error}");
53    }
54
55    /// Called when a fatal error occurs that prevents further rendering.
56    /// The backend should attempt graceful shutdown.
57    fn on_fatal_error(&mut self, error: &CvkgError) {
58        tracing::error!("[Fatal] {error}");
59    }
60
61    /// Returns true if the backend is in an error state.
62    fn has_error(&self) -> bool {
63        false
64    }
65}
66
67pub trait Renderer: ElapsedTime + Send + RendererErrorHandler {
68    /// Requests that the renderer redraws as soon as possible.
69    /// Used for continuous animations.
70    fn request_redraw(&mut self) {}
71
72    /// Returns true if the current frame is over the time budget.
73    /// This can be used to skip expensive visual effects.
74    fn is_over_budget(&self) -> bool {
75        false
76    }
77
78    // ── Filled shapes ────────────────────────────────────────────────────
79    fn fill_rect(&mut self, rect: Rect, color: [f32; 4]);
80    fn fill_rounded_rect(&mut self, rect: Rect, radius: f32, color: [f32; 4]);
81    /// Fill an ellipse/circle that fits inside `rect`.
82    fn fill_ellipse(&mut self, rect: Rect, color: [f32; 4]);
83
84    /// Draw a background image that fills the entire rect.
85    /// This is a convenience wrapper around `draw_image` for the common case
86    /// of a full-rect background. The image must have been pre-warmed via
87    /// `prewarm_vram` before the first frame.
88    fn draw_background_image(&mut self, image_name: &str, rect: Rect) {
89        Renderer::draw_image(self, image_name, rect);
90    }
91
92    /// Fill a rounded rect with glass material for frosted backdrop effect.
93    /// This is the proper way to render glass cards for macOS Tahoe-style blur.
94    /// The blur_radius controls the intensity of the backdrop blur.
95    fn fill_glass_rect(&mut self, rect: Rect, radius: f32, blur_radius: f32) {
96        // Default no-op implementation; GPU backend overrides
97        let _ = (rect, radius, blur_radius);
98    }
99    /// Fill a rounded rect with glass material with explicit intensity control.
100    /// `glass_intensity` ranges from 0.0 (solid) to 1.0 (full glass). Default: 1.0.
101    fn fill_glass_rect_with_intensity(
102        &mut self,
103        rect: Rect,
104        radius: f32,
105        blur_radius: f32,
106        glass_intensity: f32,
107    ) {
108        let _ = (rect, radius, blur_radius, glass_intensity);
109    }
110    /// Fill a rounded rect with glass material with explicit tint color and intensity.
111    /// `tint_color` is the glass fill color (RGBA). `glass_intensity` ranges from 0.0 (solid) to 1.0 (full glass).
112    fn fill_glass_rect_with_tint(
113        &mut self,
114        rect: Rect,
115        radius: f32,
116        blur_radius: f32,
117        tint_color: [f32; 4],
118        glass_intensity: f32,
119    ) {
120        // Default: delegate to intensity-only version using tint color as a simple fill
121        let _ = (rect, radius, blur_radius, tint_color, glass_intensity);
122    }
123    /// Fill a rounded rect with glass material, modulated by touch pressure.
124    /// `pressure` ranges from 0.0 (no touch) to 1.0 (full pressure).
125    /// When pressure > 0, refraction distortion is scaled by pressure amount.
126    /// Desktop stub: pressure is always 1.0 for mouse clicks, 0.0 otherwise.
127    fn fill_glass_rect_with_pressure(
128        &mut self,
129        rect: Rect,
130        radius: f32,
131        blur_radius: f32,
132        pressure: f32,
133    ) {
134        // Default: delegate to standard glass with intensity = pressure
135        Renderer::fill_glass_rect_with_intensity(self, rect, radius, blur_radius, pressure);
136    }
137
138    /// Fill a squircle (superellipse) for Apple-style icon silhouettes.
139    /// `n` controls the squareness: 2.0 = rounded rect, 4.0 = classic squircle, higher = more square.
140    fn fill_squircle(&mut self, rect: Rect, _n: f32, color: [f32; 4]) {
141        // Default fallback to rounded rect
142        Renderer::fill_rounded_rect(self, rect, rect.width.min(rect.height) * 0.22, color);
143    }
144
145    /// Stroke a squircle (superellipse) outline.
146    fn stroke_squircle(&mut self, rect: Rect, _n: f32, color: [f32; 4], stroke_width: f32) {
147        Renderer::stroke_rounded_rect(
148            self,
149            rect,
150            rect.width.min(rect.height) * 0.22,
151            color,
152            stroke_width,
153        );
154    }
155
156    /// Draw a focus ring around a rect (for keyboard navigation accessibility).
157    /// `offset` is the gap between the rect and the ring, `width` is the ring thickness.
158    fn draw_focus_ring(
159        &mut self,
160        rect: Rect,
161        radius: f32,
162        offset: f32,
163        width: f32,
164        color: [f32; 4],
165    ) {
166        // Default fallback to a stroked rounded rect
167        let ring_rect = Rect {
168            x: rect.x - offset,
169            y: rect.y - offset,
170            width: rect.width + 2.0 * offset,
171            height: rect.height + 2.0 * offset,
172        };
173        Renderer::stroke_rounded_rect(self, ring_rect, radius + offset, color, width);
174    }
175
176    /// Draw a high-fidelity 3D cube inside the given rectangle using specialized shader logic.
177    /// `rotation` is [pitch, yaw, roll] in radians.
178    fn draw_3d_cube(&mut self, _rect: Rect, _color: [f32; 4], _rotation: [f32; 3]) {}
179
180    // ── Stroked shapes ───────────────────────────────────────────────────
181    fn stroke_rect(&mut self, rect: Rect, color: [f32; 4], stroke_width: f32);
182    fn stroke_rounded_rect(&mut self, rect: Rect, radius: f32, color: [f32; 4], stroke_width: f32);
183    /// Stroke an ellipse/circle that fits inside `rect`.
184    fn stroke_ellipse(&mut self, rect: Rect, color: [f32; 4], stroke_width: f32);
185    /// Draw a straight line from (x1,y1) to (x2,y2).
186    fn draw_line(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, color: [f32; 4], stroke_width: f32);
187    /// Fill a polygon defined by a set of vertices.
188    fn fill_polygon(&mut self, _vertices: &[[f32; 2]], _color: [f32; 4]) {}
189    /// Stroke a polygon defined by a set of vertices.
190    fn stroke_polygon(&mut self, _vertices: &[[f32; 2]], _color: [f32; 4], _stroke_width: f32) {}
191
192    // ── Text ─────────────────────────────────────────────────────────────
193    /// Draw text aligned within a bounding box.
194    /// This is the primary method — callers should use this for proper
195    /// vertical and horizontal alignment within their layout rects.
196    fn draw_text(
197        &mut self,
198        text: &str,
199        rect: &Rect,
200        size: f32,
201        color: [f32; 4],
202        h_align: TextHAlign,
203        v_align: TextVAlign,
204    ) {
205        // Default implementation delegates to draw_text_raw for backward compat
206        let text_w = self.measure_text(text, size).0;
207        let x = match h_align {
208            TextHAlign::Left => rect.x,
209            TextHAlign::Center => rect.x + (rect.width - text_w) / 2.0,
210            TextHAlign::Right => rect.x + rect.width - text_w,
211        };
212        let ascent = size * 0.8;
213        let y = match v_align {
214            TextVAlign::Top => rect.y + ascent,
215            TextVAlign::Middle => rect.y + (rect.height + ascent * 0.75) / 2.0,
216            TextVAlign::Bottom => rect.y + rect.height - (size - ascent),
217        };
218        self.draw_text_raw(text, x, y, size, color);
219    }
220
221    /// Draw text at a raw baseline position (x = left, y = baseline).
222    /// Use this when you need precise control over baseline positioning.
223    fn draw_text_raw(&mut self, text: &str, x: f32, y: f32, size: f32, color: [f32; 4]) {
224        let r = (color[0] * 255.0).clamp(0.0, 255.0) as u8;
225        let g = (color[1] * 255.0).clamp(0.0, 255.0) as u8;
226        let b = (color[2] * 255.0).clamp(0.0, 255.0) as u8;
227        let a = (color[3] * 255.0).clamp(0.0, 255.0) as u8;
228
229        let mut style = cvkg_runic_text::TextStyle::new("Inter", size);
230        style.color = [r, g, b, a];
231        let spans = [cvkg_runic_text::TextSpan::new(text, style)];
232
233        if let Some(shaped) = self.shape_rich_text(
234            &spans,
235            None,
236            cvkg_runic_text::TextAlign::Start,
237            cvkg_runic_text::TextOverflow::Visible,
238        ) {
239            self.draw_shaped_text(&shaped, x, y);
240        }
241    }
242
243    /// Draw text centered within a bounding box (convenience).
244    fn draw_text_centered(&mut self, text: &str, rect: &Rect, size: f32, color: [f32; 4]) {
245        self.draw_text(
246            text,
247            rect,
248            size,
249            color,
250            TextHAlign::Center,
251            TextVAlign::Middle,
252        )
253    }
254
255    /// Measure the width and height of the specified text.
256    fn measure_text(&mut self, text: &str, size: f32) -> (f32, f32) {
257        let span = cvkg_runic_text::TextSpan::new(
258            text,
259            cvkg_runic_text::TextStyle {
260                family: "Inter".to_string(),
261                font_size: size,
262                fallback_families: vec![
263                    "SF Pro".to_string(),
264                    "SF Pro Text".to_string(),
265                    "Helvetica Neue".to_string(),
266                    "Helvetica".to_string(),
267                    "Arial".to_string(),
268                    "sans-serif".to_string(),
269                ],
270                ..Default::default()
271            },
272        );
273        if let Some(shaped) = Renderer::shape_rich_text(
274            self,
275            &[span],
276            None,
277            cvkg_runic_text::TextAlign::Start,
278            cvkg_runic_text::TextOverflow::Visible,
279        ) {
280            let scale = self.text_scale_factor().max(1.0);
281            (shaped.width / scale, shaped.height / scale)
282        } else {
283            (0.0, 0.0)
284        }
285    }
286
287    /// Return the baseline offset (ascent) for the given text and size.
288    /// This is the distance from the text origin (y in draw_text) to the baseline.
289    /// Default returns 0.0; override in renderers that support text shaping.
290    fn measure_text_baseline(&mut self, text: &str, size: f32) -> f32 {
291        let span = cvkg_runic_text::TextSpan::new(
292            text,
293            cvkg_runic_text::TextStyle {
294                family: "Inter".to_string(),
295                font_size: size,
296                fallback_families: vec![
297                    "SF Pro".to_string(),
298                    "SF Pro Text".to_string(),
299                    "Helvetica Neue".to_string(),
300                    "Helvetica".to_string(),
301                    "Arial".to_string(),
302                    "sans-serif".to_string(),
303                ],
304                ..Default::default()
305            },
306        );
307        if let Some(shaped) = Renderer::shape_rich_text(
308            self,
309            &[span],
310            None,
311            cvkg_runic_text::TextAlign::Start,
312            cvkg_runic_text::TextOverflow::Visible,
313        ) {
314            shaped.ascent / self.text_scale_factor().max(1.0)
315        } else {
316            0.0
317        }
318    }
319
320    /// Scale factor used by text measurement helpers.
321    ///
322    /// Renderers that shape text in device pixels should return their current
323    /// device scale so `measure_text` and `measure_text_baseline` stay in logical pixels.
324    fn text_scale_factor(&self) -> f32 {
325        1.0
326    }
327
328    fn shape_rich_text(
329        &mut self,
330        _spans: &[cvkg_runic_text::TextSpan],
331        _max_width: Option<f32>,
332        _align: cvkg_runic_text::TextAlign,
333        _overflow: cvkg_runic_text::TextOverflow,
334    ) -> Option<cvkg_runic_text::ShapedText> {
335        None
336    }
337
338    fn draw_shaped_text(&mut self, _text: &cvkg_runic_text::ShapedText, _x: f32, _y: f32) {}
339
340    // ── Images & textures ────────────────────────────────────────────────
341    /// Draw a texture (GPU-side) at the specified rect.
342    fn draw_texture(&mut self, _texture_id: u32, _rect: Rect) {}
343    /// Draw an image asset by name or path.
344    fn draw_image(&mut self, _image_name: &str, _rect: Rect) {}
345    /// Load an image asset from memory.
346    fn load_image(&mut self, _name: &str, _data: &[u8]) {}
347    /// Pre-warm the renderer with assets. Implementations can use this
348    /// to populate texture atlases or warm up shader caches.
349    fn prewarm_vram(&mut self, _assets: Vec<(String, Vec<u8>)>) {}
350
351    /// Get the current pointer (mouse/touch) position.
352    fn get_pointer_position(&self) -> [f32; 2] {
353        [0.0, 0.0]
354    }
355
356    // ── Data Visualization ───────────────────────────────────────────────
357    /// Upload raw float data as a GPU texture for heatmap rendering.
358    fn upload_data_texture(&mut self, _id: &str, _data: &[f32], _width: u32, _height: u32) {}
359    /// Draw a heatmap using a previously uploaded data texture.
360    fn draw_heatmap(&mut self, _texture_id: &str, _rect: Rect, _palette: &str) {}
361
362    // ── 3D Objects ───────────────────────────────────────────────────────
363    /// Draw a 3D mesh.
364    fn draw_mesh(&mut self, _mesh: &Mesh, _color: [f32; 4], _transform: glam::Mat4) {}
365
366    /// Draw a 3D mesh with full material and transform support.
367    fn draw_mesh_3d(&mut self, _mesh: &Mesh, _material: &Material3D, _transform: &Transform3D) {}
368
369    /// Set the 3D camera for perspective/orthographic projection.
370    /// If not called, rendering defaults to the 2D orthographic projection.
371    fn set_camera_3d(&mut self, _camera: &Camera3D) {}
372
373    /// Push a 3D transform onto the transform stack.
374    /// All subsequent drawing is affected until `pop_transform_3d`.
375    fn push_transform_3d(&mut self, _transform: &Transform3D) {}
376
377    /// Pop the most recently pushed 3D transform.
378    fn pop_transform_3d(&mut self) {}
379
380    /// Render a 3D scene graph node. Reads position_3d, rotation_3d, scale_3d
381    /// from the node and emits the appropriate draw call.
382    /// Default implementation is a no-op; 3D renderers override this.
383    ///
384    /// `position`: [x, y, z] world-space position
385    /// `rotation`: [x, y, z, w] quaternion rotation
386    /// `scale`: [x, y, z] scale factors
387    /// `color`: [r, g, b, a] base color for unlit rendering
388    fn render_scene_node_3d(
389        &mut self,
390        _position: [f32; 3],
391        _rotation: [f32; 4],
392        _scale: [f32; 3],
393        _color: [f32; 4],
394        _meshes: &[Mesh],
395    ) {
396        // Default no-op: 2D renderers ignore 3D scene nodes
397    }
398
399    /// Draw a linear gradient between two colors at the specified angle.
400    fn draw_linear_gradient(
401        &mut self,
402        _rect: Rect,
403        _start_color: [f32; 4],
404        _end_color: [f32; 4],
405        _angle: f32,
406    ) {
407    }
408    /// Draw a radial gradient between two colors.
409    fn draw_radial_gradient(
410        &mut self,
411        _rect: Rect,
412        _inner_color: [f32; 4],
413        _outer_color: [f32; 4],
414    ) {
415    }
416    /// Draw a multi-stop linear gradient (GPU-accelerated).
417    /// stops: array of [R, G, B, position] where position is 0.0-1.0.
418    /// angle: gradient angle in radians.
419    fn draw_linear_gradient_multi(&mut self, _rect: Rect, _stops: &[[f32; 4]], _angle: f32) {}
420    /// Draw a multi-stop radial gradient (GPU-accelerated).
421    /// stops: array of [R, G, B, position] where position is 0.0-1.0.
422    fn draw_radial_gradient_multi(&mut self, _rect: Rect, _stops: &[[f32; 4]]) {}
423    /// Draw a high-fidelity drop shadow for a rounded rectangle.
424    fn draw_drop_shadow(
425        &mut self,
426        _rect: Rect,
427        _radius: f32,
428        _color: [f32; 4],
429        _blur: f32,
430        _spread: f32,
431    ) {
432    }
433    /// Draw a dashed border for a rounded rectangle.
434    fn stroke_dashed_rounded_rect(
435        &mut self,
436        _rect: Rect,
437        _radius: f32,
438        _color: [f32; 4],
439        _width: f32,
440        _dash: f32,
441        _gap: f32,
442    ) {
443    }
444    /// Draw a 9-slice / patch scaled image.
445    fn draw_9slice(
446        &mut self,
447        _image_name: &str,
448        _rect: Rect,
449        _left: f32,
450        _top: f32,
451        _right: f32,
452        _bottom: f32,
453    ) {
454    }
455
456    // ── Clipping ─────────────────────────────────────────────────────────
457    /// Push a clip rectangle.  All subsequent drawing is clipped to `rect`.
458    /// Implementations that do not support clipping may ignore this call.
459    fn push_clip_rect(&mut self, _rect: Rect) {}
460    /// Pop the most recently pushed clip rectangle.
461    fn pop_clip_rect(&mut self) {}
462    /// Get the current clip rectangle in screen coordinates.
463    /// Returns a rect covering the entire screen if no clip is active.
464    fn current_clip_rect(&self) -> Rect {
465        Rect::new(-10000.0, -10000.0, 20000.0, 20000.0)
466    }
467
468    // ── Global opacity ───────────────────────────────────────────────────
469    /// Set a global opacity multiplier applied to all subsequent draw calls
470    /// until `pop_opacity` is called.  `opacity` is in [0.0, 1.0].
471    fn push_opacity(&mut self, _opacity: f32) {}
472    /// Restore the previous opacity level.
473    fn pop_opacity(&mut self) {}
474
475    // ── Berserker Pipeline State ─────────────────────────────────────────
476    fn set_theme(&mut self, _theme: ColorTheme) {}
477    fn set_rage(&mut self, _rage: f32) {}
478    fn set_berserker_mode(&mut self, _state: RenderIntensityMode) {}
479    fn trigger_shatter_event(&mut self, _origin: [f32; 2], _force: f32) {}
480    /// Set the fireball position for dynamic glass specular highlights.
481    fn set_fireball_pos(&mut self, _pos: [f32; 2]) {}
482    /// Set the desktop scene preset (Aurora, Void, Nebula, Glitch, Yggdrasil).
483    fn set_scene(&mut self, _scene: &str) {}
484    /// Set the desktop scene by name. Case-insensitive.
485    /// Supports: "aurora", "void", "nebula", "glitch", "yggdrasil".
486    /// Aliases: "empty", "none", "blank" → Void.
487    fn set_scene_by_name(&mut self, name: &str) {
488        if let Some(preset) = resolve_scene_by_name(name) {
489            Renderer::set_scene_preset(self, preset);
490        }
491    }
492
493    // ── Export & Print ───────────────────────────────────────────────────
494    /// Capture the current frame as a PNG byte buffer.
495    fn capture_png(&mut self) -> Vec<u8> {
496        Vec::new()
497    }
498    /// Trigger a native print dialog or spooling operation.
499    fn print(&mut self) {}
500
501    fn set_scene_preset(&mut self, _preset: u32) {}
502
503    // ── Cyberpunk Effects ────────────────────────────────────────────────
504    /// Apply a Bifrost (Frosted Glass) effect to the specified rect.
505    fn bifrost(&mut self, _rect: Rect, _blur: f32, _saturation: f32, _opacity: f32) {}
506    /// Apply a Gungnir (Neon Glow) effect to the specified rect.
507    fn gungnir(&mut self, _rect: Rect, _color: [f32; 4], _radius: f32, _intensity: f32) {}
508    /// Soft glow variant -- half the intensity of gungnir(). Use for hover highlights.
509    fn gungnir_soft(&mut self, _rect: Rect, _color: [f32; 4], _radius: f32, _intensity: f32) {}
510    /// Set the default background color for the canvas (RGBA).
511    /// Used when the app does not draw its own background.
512    fn set_default_background_color(&mut self, _color: [f32; 4]) {}
513    /// Apply a ManiGlow (Lunar Illuminator) effect.
514    fn mani_glow(&mut self, _rect: Rect, _color: [f32; 4], _radius: f32) {}
515    /// Push a Mjolnir Slice (geometric clipping).
516    fn push_mjolnir_slice(&mut self, _angle: f32, _offset: f32) {}
517    fn pop_mjolnir_slice(&mut self) {}
518    /// Execute a render function with memoization.
519    /// If the renderer supports caching and the `id` + `data_hash` match a previous run,
520    /// it may replay cached commands instead of executing the function.
521    fn memoize(&mut self, id: u64, data_hash: u64, render_fn: &dyn Fn(&mut dyn Renderer));
522    /// Capture current renderer stack depths for later panic recovery.
523    /// The default implementation returns `RenderStateSnapshot::default()`,
524    /// which is safe but does nothing useful -- backends with stack state
525    /// must override this to record their actual depths.
526    fn snapshot_render_state(&self) -> RenderStateSnapshot {
527        RenderStateSnapshot::default()
528    }
529    /// Restore renderer stack state by popping items pushed beyond the
530    /// snapshot point. Used by `ErrorBoundary` to recover from mid-render
531    /// panics so sibling views don't inherit leaked clip/opacity/transform
532    /// state. Idempotent: a no-op if stacks are already at or below the
533    /// snapshot depths. Default implementation is a no-op for backends
534    /// that have no stack state.
535    fn restore_render_state(&mut self, _snap: RenderStateSnapshot) {}
536    /// Apply a Mjolnir Shatter effect (fragmentation) to the specified rect.
537    fn mjolnir_shatter(&mut self, _rect: Rect, _pieces: u32, _force: f32, _color: [f32; 4]) {}
538    fn mjolnir_fluid_shatter(&mut self, _rect: Rect, _pieces: u32, _force: f32, _color: [f32; 4]) {}
539    fn draw_mjolnir_bolt(&mut self, _from: [f32; 2], _to: [f32; 2], _color: [f32; 4]) {}
540
541    // ── Futuristic UI Compute & Volumetric ───────────────────────────────
542    /// Dispatches a burst of GPU particles (e.g. fireworks, data streams).
543    fn dispatch_particles(
544        &mut self,
545        _origin: [f32; 2],
546        _count: u32,
547        _effect_type: &str,
548        _color: [f32; 4],
549    ) {
550    }
551
552    /// Draws a volumetric hologram into the specified bounding rectangle.
553    fn draw_hologram(&mut self, _rect: Rect, _hologram_id: &str, _time: f32) {}
554
555    // ── Accessibility (ShieldWall) ───────────────────────────────────────
556    fn set_aria_role(&mut self, _role: &str) {}
557    fn set_aria_label(&mut self, _label: &str) {}
558    fn set_aria_valuemin(&mut self, _min: f32) {}
559    fn set_aria_valuemax(&mut self, _max: f32) {}
560    fn set_aria_valuenow(&mut self, _now: f32) {}
561
562    /// Convenience method: register aria role and label in one call.
563    /// Components with both `role` and `label` props should call this.
564    fn register_a11y(&mut self, role: &str, label: &str) {
565        self.set_aria_role(role);
566        self.set_aria_label(label);
567    }
568
569    /// Push a focus trap onto the stack. While active, keyboard focus is
570    /// trapped within the specified element and its children.
571    /// Returns a trap ID that must be passed to `pop_focus_trap`.
572    fn push_focus_trap(&mut self, _element_id: &str) -> u64 {
573        0
574    }
575
576    /// Pop the most recently pushed focus trap.
577    fn pop_focus_trap(&mut self, _trap_id: u64) {}
578
579    /// Register a shared element for Bifrost Bridge transitions.
580    fn register_shared_element(&mut self, _id: &str, _rect: Rect) {}
581
582    /// Set a unique key for the current VDOM node to ensure stable identity during diffing.
583    fn set_key(&mut self, _key: &str) {}
584
585    // ── Telemetry ────────────────────────────────────────────────────────
586    /// Get real-time performance telemetry.
587    fn get_telemetry(&self) -> TelemetryData {
588        TelemetryData::default()
589    }
590
591    // ── GPU State Management ─────────────────────────────────────────────
592    /// Push a shadow state to the stack. All following draw calls will have this shadow.
593    fn push_shadow(&mut self, _radius: f32, _color: [f32; 4], _offset: [f32; 2]) {}
594    /// Pop the last shadow state from the stack.
595    fn pop_shadow(&mut self) {}
596
597    // ── VDOM & Scene Graph ───────────────────────────────────────────────
598    /// Push a Virtual DOM node onto the stack for hierarchy tracking.
599    fn push_vnode(&mut self, _rect: Rect, _name: &'static str) {}
600
601    /// Push a Virtual DOM node with companion state auto-initialization.
602    /// Default implementation ignores companions and delegates to `push_vnode`.
603    fn push_vnode_with_companions(
604        &mut self,
605        rect: Rect,
606        name: &'static str,
607        _companions: Vec<Box<dyn Companion>>,
608    ) {
609        self.push_vnode(rect, name);
610    }
611
612    /// Pop the current Virtual DOM node from the stack.
613    fn pop_vnode(&mut self) {}
614
615    /// Register an event handler for the current VDOM node.
616    fn register_handler(
617        &mut self,
618        _event_type: &str,
619        _handler: std::sync::Arc<dyn Fn(Event) + Send + Sync>,
620    ) {
621    }
622
623    /// Retrieve a companion state for the VNode currently at the top of the stack.
624    /// Returns None if the companion type is not registered on this node.
625    fn current_companion(&self) -> Option<&dyn Companion> {
626        None
627    }
628
629    /// Set the current Z-index for depth sorting.
630    /// Higher values appear closer to the viewer.
631    fn set_z_index(&mut self, _z: f32) {}
632
633    /// Get the current Z-index.
634    fn get_z_index(&self) -> f32 {
635        0.0
636    }
637
638    // ── Vector Graphics ──────────────────────────────────────────────────
639    fn load_svg(&mut self, _name: &str, _svg_data: &[u8]) {}
640    /// Draw a pre-loaded SVG model.
641    fn draw_svg(&mut self, _name: &str, _rect: Rect) {}
642    /// Draw a pre-loaded SVG model with a per-instance animation time offset.
643    /// The offset shifts the animation phase, allowing multiple draws of the same
644    /// SVG to animate independently. Default delegates to draw_svg (no offset).
645    fn draw_svg_with_offset(&mut self, name: &str, rect: Rect, _animation_time_offset: f32) {
646        Renderer::draw_svg(self, name, rect);
647    }
648    /// Draw a pre-loaded SVG model with explicit draw_order for z-sorting.
649    /// draw_order=200 renders above UI chrome (draw_order=0).
650    fn draw_svg_with_order(&mut self, name: &str, rect: Rect, _draw_order: i32) {
651        Renderer::draw_svg(self, name, rect);
652    }
653    /// Serialize a pre-loaded SVG model back to SVG XML markup.
654    /// Returns the serialized SVG string, or an error if the model is not loaded
655    /// or serialization is not supported by this renderer.
656    fn serialize_svg(&mut self, _name: &str) -> Result<String, String> {
657        Err("SVG serialization not supported by this renderer".into())
658    }
659    /// Apply an SVG filter to a pre-loaded SVG model by filter element ID.
660    /// The filter is evaluated and the result composited back into the SVG.
661    /// Returns the filtered SVG as XML, or an error if not supported.
662    fn apply_svg_filter(
663        &mut self,
664        _name: &str,
665        _filter_id: &str,
666        _region: Rect,
667    ) -> Result<String, String> {
668        Err("SVG filter not supported by this renderer".into())
669    }
670
671    // ── 3D World Space Panels ────────────────────────────────────────────────
672
673    /// Called when the traversal enters a VNode with a WorldSpacePanel configured.
674    /// Redirects subsequent draw calls into an offscreen texture for the panel,
675    /// similar to push_target.
676    fn begin_world_space_panel(
677        &mut self,
678        node_id: u64,
679        transform: &Transform3D,
680        glass: Option<cvkg_materials::GlassMaterial>,
681        pixels_per_unit: f32,
682        world_size: (f32, f32),
683    ) {
684        let _ = (node_id, transform, glass, pixels_per_unit, world_size);
685    }
686
687    /// Called when the traversal exits a VNode with a WorldSpacePanel configured.
688    /// Ends offscreen redirection and records the panel into the renderer state
689    /// for 3D compositing later in the frame graph.
690    fn end_world_space_panel(&mut self, node_id: u64) {
691        let _ = node_id;
692    }
693
694    // ── Coordinate Transforms ──────────────────────────────────────────────
695    /// Push a 2D transform (translation, scale, rotation) onto the stack.
696    /// This transform should be applied to all subsequent draw calls until popped.
697    /// Transform-only animations use this to avoid re-triggering the layout engine.
698    fn push_transform(&mut self, _translation: [f32; 2], _scale: [f32; 2], _rotation: f32) {}
699    /// Push a raw 2D affine transform matrix [a, b, c, d, e, f] corresponding to
700    /// [m11, m12, m21, m22, tx, ty].
701    fn push_affine(&mut self, _transform: [f32; 6]) {}
702    /// Pop the last 2D transform from the stack.
703    fn pop_transform(&mut self) {}
704    /// Return the resolved layout bounds for a specific node ID if it exists.
705    fn query_layout(&self, _node_id: scene_graph::NodeId) -> Option<Rect> {
706        None
707    }
708    /// Enable or disable the layout debug overlay (bounds, padding, margin).
709    fn set_debug_layout(&mut self, _enabled: bool) {}
710    /// Check if the layout debug overlay is currently enabled.
711    fn get_debug_layout(&self) -> bool {
712        false
713    }
714
715    // ── Material Routing ─────────────────────────────────────────────────
716    /// Set the active material for subsequent draw calls.
717    /// Controls which pass a draw call is routed to in the multi-pass pipeline.
718    fn set_material(&mut self, _material: crate::material::DrawMaterial) {}
719    /// Return the currently active material (defaults to Opaque).
720    fn current_material(&self) -> crate::material::DrawMaterial {
721        crate::material::DrawMaterial::Opaque
722    }
723
724    // ── Vili Interaction Paradigm ──────────────────────────────────────────
725    /// Compute the user's velocity/intent vector.
726    fn mimir_intent(&self) -> [f32; 2] {
727        [0.0, 0.0]
728    }
729    /// Calculate magnetic coordinate warp towards an anchor.
730    fn magnetic_warp(&self, pointer: [f32; 2], anchor_rect: Rect, strength: f32) -> [f32; 2] {
731        if strength <= 0.0 {
732            return pointer;
733        }
734        let cx = anchor_rect.x + anchor_rect.width / 2.0;
735        let cy = anchor_rect.y + anchor_rect.height / 2.0;
736        let dx = pointer[0] - cx;
737        let dy = pointer[1] - cy;
738        let dist = (dx * dx + dy * dy).sqrt();
739        let radius = 120.0;
740        if dist < radius && dist > 0.0 {
741            let force = (1.0 - dist / radius) * strength;
742            [pointer[0] - dx * force, pointer[1] - dy * force]
743        } else {
744            pointer
745        }
746    }
747    /// Calculate kinematic glow intensity based on proximity.
748    fn mani_glow_intensity(&self, pointer: [f32; 2], bounds: Rect, radius: f32) -> f32 {
749        let cx = bounds.x + bounds.width / 2.0;
750        let cy = bounds.y + bounds.height / 2.0;
751        let dist = ((pointer[0] - cx).powi(2) + (pointer[1] - cy).powi(2)).sqrt();
752        if dist < radius {
753            (1.0 - dist / radius).clamp(0.0, 1.0)
754        } else {
755            0.0
756        }
757    }
758    /// Calculate dynamic element attention (scaling/morphing) statelessly per frame.
759    fn fafnir_evolve(&self, pointer: [f32; 2], bounds: Rect, max_scale: f32) -> f32 {
760        let prox = self.mani_glow_intensity(pointer, bounds, 120.0);
761        1.0 + (max_scale - 1.0) * prox
762    }
763    /// Sets the precise Vili SDF Shape boundary for hit-testing.
764    fn set_sdf_shape(&mut self, _shape: crate::layout::SdfShape) {}
765
766    // -- Portal / PhaseGate rendering -----------------------------------------
767
768    // ── Theme stack ──────────────────────────────────────────────────────
769
770    /// Push a theme override for the current subtree.
771    /// All child nodes rendered until `pop_theme()` are drawn with this theme.
772    fn push_theme(&mut self, _theme: ColorTheme) {}
773
774    /// Pop the theme override and restore the previous theme.
775    fn pop_theme(&mut self) {}
776
777    /// Return the current theme at the current stack depth.
778    /// Falls back to the default theme if no theme has been pushed.
779    fn current_theme(&self) -> ColorTheme {
780        ColorTheme::default()
781    }
782
783    /// Save theme state for portal inheritance before entering a new buffer.
784    /// Legacy — prefer using the theme stack (`push_theme`/`pop_theme`) instead.
785    fn save_theme(&mut self) {}
786    /// Restore the saved theme when exiting a portal buffer.
787    /// Legacy — prefer using the theme stack (`push_theme`/`pop_theme`) instead.
788    fn restore_theme(&mut self) {}
789
790    /// Begin rendering into the portal root layer instead of the inline tree.
791    /// All draw calls between `enter_portal` and `exit_portal` are collected
792    /// into a separate buffer that is composited AFTER the main tree.
793    ///
794    /// WHY separate buffer: The main tree may have clipping, transforms, or
795    /// opacity that should NOT affect overlays. The portal layer renders on top
796    /// of everything, ignoring the local coordinate system.
797    fn enter_portal(&mut self, _z_index: i32) {}
798
799    /// Exit the portal layer and return to inline rendering.
800    /// The portal content collected since `enter_portal` is now sealed --
801    /// no more draw calls will be appended to it.
802    fn exit_portal(&mut self) {}
803
804    /// Get the current viewport size in logical pixels.
805    /// Used by portal content to size itself to the full screen.
806    fn viewport_size(&self) -> Rect {
807        Rect::new(0.0, 0.0, 1920.0, 1080.0)
808    }
809
810    // -- Accessibility announcements -----------------------------------------
811
812    /// Announce a message to screen readers via the platform accessibility API.
813    /// This call is non-blocking. The message is queued and the screen reader
814    /// will speak it at its own pace.
815    fn announce(&mut self, _message: &str, _priority: AnnouncementPriority) {}
816}