use crate::context::Context;
use crate::render::Buffer;
use crate::render::style::Style;
use crate::util::Rect;
use crate::ui::{UIEvent, UIResult};
use std::any::Any;
pub type WidgetId = u32;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct WidgetState {
pub visible: bool,
pub enabled: bool,
pub focused: bool,
pub hovered: bool,
pub pressed: bool,
pub dirty: bool, }
impl Default for WidgetState {
fn default() -> Self {
Self {
visible: true,
enabled: true,
focused: false,
hovered: false,
pressed: false,
dirty: true,
}
}
}
pub trait Widget: Any {
fn id(&self) -> WidgetId;
fn bounds(&self) -> Rect;
fn set_bounds(&mut self, bounds: Rect);
fn state(&self) -> &WidgetState;
fn state_mut(&mut self) -> &mut WidgetState;
fn render(&self, buffer: &mut Buffer, ctx: &Context) -> UIResult<()>;
fn handle_event(&mut self, event: &UIEvent, ctx: &mut Context) -> UIResult<bool>;
fn update(&mut self, dt: f32, ctx: &mut Context) -> UIResult<()>;
fn preferred_size(&self, available: Rect) -> Rect;
fn hit_test(&self, x: u16, y: u16) -> bool {
let bounds = self.bounds();
x >= bounds.x && x < bounds.x + bounds.width &&
y >= bounds.y && y < bounds.y + bounds.height
}
fn set_visible(&mut self, visible: bool) {
self.state_mut().visible = visible;
self.state_mut().dirty = true;
}
fn set_enabled(&mut self, enabled: bool) {
self.state_mut().enabled = enabled;
self.state_mut().dirty = true;
}
fn set_focused(&mut self, focused: bool) {
self.state_mut().focused = focused;
self.state_mut().dirty = true;
}
fn mark_dirty(&mut self) {
self.state_mut().dirty = true;
}
fn clear_dirty(&mut self) {
self.state_mut().dirty = false;
}
fn is_dirty(&self) -> bool {
self.state().dirty
}
fn as_any(&self) -> &dyn Any;
fn as_any_mut(&mut self) -> &mut dyn Any;
fn layout_children(&mut self) {
}
}
pub trait Container: Widget {
fn add_child(&mut self, child: Box<dyn Widget>);
fn remove_child(&mut self, id: WidgetId) -> Option<Box<dyn Widget>>;
fn get_child(&self, id: WidgetId) -> Option<&dyn Widget>;
fn get_child_mut(&mut self, id: WidgetId) -> Option<&mut dyn Widget>;
fn children(&self) -> &[Box<dyn Widget>];
fn children_mut(&mut self) -> &mut Vec<Box<dyn Widget>>;
fn child_at_point(&self, x: u16, y: u16) -> Option<WidgetId> {
for child in self.children().iter().rev() { if child.state().visible && child.hit_test(x, y) {
return Some(child.id());
}
}
None
}
fn layout(&mut self);
fn layout_recursive(&mut self) {
self.layout();
for child in self.children_mut() {
child.layout_children();
}
}
}
pub struct BaseWidget {
pub id: WidgetId,
pub bounds: Rect,
pub state: WidgetState,
pub style: Style,
}
impl BaseWidget {
pub fn new(id: WidgetId) -> Self {
Self {
id,
bounds: Rect::default(),
state: WidgetState::default(),
style: Style::default(),
}
}
pub fn with_bounds(mut self, bounds: Rect) -> Self {
self.bounds = bounds;
self
}
pub fn with_style(mut self, style: Style) -> Self {
self.style = style;
self
}
}
use std::sync::atomic::{AtomicU32, Ordering};
static WIDGET_ID_COUNTER: AtomicU32 = AtomicU32::new(1);
pub fn next_widget_id() -> WidgetId {
WIDGET_ID_COUNTER.fetch_add(1, Ordering::Relaxed)
}
#[macro_export]
macro_rules! impl_widget_base {
($widget:ty, $base_field:ident) => {
fn id(&self) -> WidgetId {
self.$base_field.id
}
fn bounds(&self) -> Rect {
self.$base_field.bounds
}
fn set_bounds(&mut self, bounds: Rect) {
self.$base_field.bounds = bounds;
self.$base_field.state.dirty = true;
}
fn state(&self) -> &WidgetState {
&self.$base_field.state
}
fn state_mut(&mut self) -> &mut WidgetState {
&mut self.$base_field.state
}
fn update(&mut self, _dt: f32, _ctx: &mut Context) -> UIResult<()> {
Ok(())
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
};
}