use std::{cell::RefCell, rc::Rc}; use cosmic_text::Buffer; use glam::{Mat4, Vec2}; use taffy::TraversePartialTree; use crate::{ layout::BoxWidget, renderer_vk::text::custom_glyph::CustomGlyph, transform_stack::{self, TransformStack}, widget::{self}, }; use super::{layout::Layout, widget::DrawState}; pub struct ImageHandle { // to be implemented, will contain pixel data (RGB or RGBA) loaded via "ImageBank" or something by the gui } #[derive(Debug, Default, Clone, Copy, PartialEq)] pub struct Boundary { pub pos: Vec2, pub size: Vec2, } impl Boundary { pub fn from_pos_size(pos: Vec2, size: Vec2) -> Self { Self { pos, size } } pub fn construct(transform_stack: &TransformStack) -> Self { let transform = transform_stack.get(); Self { pos: Vec2::new(transform.pos.x, transform.pos.y), size: Vec2::new(transform.dim.x, transform.dim.y), } } } #[derive(Copy, Clone)] pub struct Color { pub r: f32, pub g: f32, pub b: f32, pub a: f32, } impl Color { pub fn new(r: f32, g: f32, b: f32, a: f32) -> Self { Self { r, g, b, a } } } impl Default for Color { fn default() -> Self { // opaque black Self::new(0.0, 0.0, 0.0, 1.0) } } #[repr(u8)] #[derive(Default, Clone, Copy)] pub enum GradientMode { #[default] None, Horizontal, Vertical, Radial, } #[derive(Default, Clone, Copy)] pub struct Rectangle { pub color: Color, pub color2: Color, pub gradient: GradientMode, pub border: f32, // width in pixels pub border_color: Color, pub round_units: u8, } pub struct RenderPrimitive { pub(super) boundary: Boundary, pub(super) transform: Mat4, pub(super) depth: f32, pub(super) payload: PrimitivePayload, } pub enum PrimitivePayload { Rectangle(Rectangle), Text(Rc>), Sprite(Option), //option because we want as_slice } fn draw_widget( layout: &Layout, state: &mut DrawState, node_id: taffy::NodeId, style: &taffy::Style, widget: &BoxWidget, parent_transform: &glam::Mat4, ) { let Ok(l) = layout.tree.layout(node_id) else { debug_assert!(false); return; }; let mut widget_state = widget.lock().unwrap(); let transform = widget_state.data.transform * *parent_transform; let (shift, info) = match widget::get_scrollbar_info(l) { Some(info) => (widget_state.get_scroll_shift(&info, l), Some(info)), None => (Vec2::default(), None), }; state.transform_stack.push(transform_stack::Transform { pos: Vec2::new(l.location.x, l.location.y) - shift, transform, dim: Vec2::new(l.size.width, l.size.height), }); let draw_params = widget::DrawParams { node_id, taffy_layout: l, style, }; widget_state.draw_all(state, &draw_params); draw_children(layout, state, node_id, &transform); state.transform_stack.pop(); if let Some(info) = &info { widget_state.draw_scrollbars(state, &draw_params, info); } } fn draw_children( layout: &Layout, state: &mut DrawState, parent_node_id: taffy::NodeId, model: &glam::Mat4, ) { for node_id in layout.tree.child_ids(parent_node_id) { let Some(widget_id) = layout.tree.get_node_context(node_id).cloned() else { debug_assert!(false); continue; }; let Ok(style) = layout.tree.style(node_id) else { debug_assert!(false); continue; }; let Some(widget) = layout.widget_states.get(widget_id) else { debug_assert!(false); continue; }; state.depth += 0.01; draw_widget(layout, state, node_id, style, widget, model); state.depth -= 0.01; } } pub fn draw(layout: &Layout) -> anyhow::Result> { let mut primitives = Vec::::new(); let mut transform_stack = TransformStack::new(); let model = glam::Mat4::IDENTITY; let Some(root_widget) = layout.widget_states.get(layout.root_widget) else { panic!(); }; let Ok(style) = layout.tree.style(layout.root_node) else { panic!(); }; let mut params = DrawState { primitives: &mut primitives, transform_stack: &mut transform_stack, layout, depth: 0.0, }; draw_widget( layout, &mut params, layout.root_node, style, root_widget, &model, ); Ok(primitives) }