Skip to main content

cvkg_core/
renderer_trait.rs

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