use crate::clip::ClipRect;
use crate::draw_list::DrawCommand;
use crate::style::Overflow;
use crate::theme::ColorPalette;
use astrelis_core::alloc::HashMap;
use astrelis_core::math::Vec2;
use astrelis_text::TextPipeline;
use std::any::TypeId;
type WidgetRenderFn =
for<'a> fn(&dyn std::any::Any, &mut WidgetRenderContext<'a>) -> Vec<DrawCommand>;
type WidgetMeasureFn = fn(&dyn std::any::Any, Vec2, Option<&astrelis_text::FontRenderer>) -> Vec2;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TraversalBehavior {
Normal,
OnlyChild(usize),
Skip,
}
pub struct WidgetRenderContext<'a> {
pub abs_position: Vec2,
pub layout_size: Vec2,
pub clip_rect: ClipRect,
pub theme_colors: &'a ColorPalette,
pub text_pipeline: &'a mut TextPipeline,
pub parent_z_index: u16,
}
pub struct WidgetOverflow {
pub overflow_x: Overflow,
pub overflow_y: Overflow,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EventResponse {
None,
RequestFocus,
ReleaseFocus,
}
pub struct WidgetTypeDescriptor {
pub name: &'static str,
pub render: Option<WidgetRenderFn>,
pub measure: Option<WidgetMeasureFn>,
pub traversal: Option<fn(&dyn std::any::Any) -> TraversalBehavior>,
pub scroll_offset: Option<fn(&dyn std::any::Any) -> Vec2>,
pub clips_children: Option<fn(&dyn std::any::Any) -> bool>,
pub overflow: Option<fn(&dyn std::any::Any) -> WidgetOverflow>,
pub caches_measurement: bool,
pub on_hover: Option<fn(&mut dyn std::any::Any, bool)>,
pub on_press: Option<fn(&mut dyn std::any::Any, bool)>,
pub on_click: Option<fn(&mut dyn std::any::Any) -> EventResponse>,
pub on_key_input:
Option<fn(&mut dyn std::any::Any, &astrelis_winit::event::PhysicalKey) -> EventResponse>,
pub on_char_input: Option<fn(&mut dyn std::any::Any, char)>,
}
impl WidgetTypeDescriptor {
pub fn new(name: &'static str) -> Self {
Self {
name,
render: None,
measure: None,
traversal: None,
scroll_offset: None,
clips_children: None,
overflow: None,
caches_measurement: false,
on_hover: None,
on_press: None,
on_click: None,
on_key_input: None,
on_char_input: None,
}
}
pub fn with_render(mut self, f: WidgetRenderFn) -> Self {
self.render = Some(f);
self
}
pub fn with_measure(mut self, f: WidgetMeasureFn) -> Self {
self.measure = Some(f);
self
}
pub fn with_traversal(mut self, f: fn(&dyn std::any::Any) -> TraversalBehavior) -> Self {
self.traversal = Some(f);
self
}
pub fn with_scroll_offset(mut self, f: fn(&dyn std::any::Any) -> Vec2) -> Self {
self.scroll_offset = Some(f);
self
}
pub fn with_clips_children(mut self, f: fn(&dyn std::any::Any) -> bool) -> Self {
self.clips_children = Some(f);
self
}
pub fn with_overflow(mut self, f: fn(&dyn std::any::Any) -> WidgetOverflow) -> Self {
self.overflow = Some(f);
self
}
pub fn with_caches_measurement(mut self) -> Self {
self.caches_measurement = true;
self
}
pub fn with_on_hover(mut self, f: fn(&mut dyn std::any::Any, bool)) -> Self {
self.on_hover = Some(f);
self
}
pub fn with_on_press(mut self, f: fn(&mut dyn std::any::Any, bool)) -> Self {
self.on_press = Some(f);
self
}
pub fn with_on_click(mut self, f: fn(&mut dyn std::any::Any) -> EventResponse) -> Self {
self.on_click = Some(f);
self
}
pub fn with_on_key_input(
mut self,
f: fn(&mut dyn std::any::Any, &astrelis_winit::event::PhysicalKey) -> EventResponse,
) -> Self {
self.on_key_input = Some(f);
self
}
pub fn with_on_char_input(mut self, f: fn(&mut dyn std::any::Any, char)) -> Self {
self.on_char_input = Some(f);
self
}
}
pub struct WidgetTypeRegistry {
descriptors: HashMap<TypeId, WidgetTypeDescriptor>,
}
impl WidgetTypeRegistry {
pub fn new() -> Self {
Self {
descriptors: HashMap::default(),
}
}
pub fn register<W: 'static>(&mut self, descriptor: WidgetTypeDescriptor) {
self.descriptors.insert(TypeId::of::<W>(), descriptor);
}
pub fn get(&self, type_id: TypeId) -> Option<&WidgetTypeDescriptor> {
self.descriptors.get(&type_id)
}
pub fn contains(&self, type_id: TypeId) -> bool {
self.descriptors.contains_key(&type_id)
}
pub fn caches_measurement(&self, type_id: TypeId) -> bool {
self.descriptors
.get(&type_id)
.is_some_and(|desc| desc.caches_measurement)
}
pub fn len(&self) -> usize {
self.descriptors.len()
}
pub fn is_empty(&self) -> bool {
self.descriptors.is_empty()
}
}
impl Default for WidgetTypeRegistry {
fn default() -> Self {
Self::new()
}
}