Seregon/StratoSDK

StratoSDK is a framework with a declarative approach similar to Flutter/React, written and designed entirely for Rust.

Rust/27.3 KB/No license
crates/strato-widgets/src/widget.rs
1//! Base widget trait and common functionality
2 
3use std::any::Any;
4use std::fmt::Debug;
5use strato_core::taffy_layout::TaffyWidget;
6use strato_core::{
7 event::{Event, EventResult},
8 layout::{Constraints, Layout, Size},
9 types::Point, // Removed unused Color and Rect imports
10};
11use strato_renderer::batch::RenderBatch;
12 
13/// Unique widget identifier
14pub type WidgetId = u64;
15 
16/// Widget state
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum WidgetState {
19 Normal,
20 Hovered,
21 Pressed,
22 Focused,
23 Disabled,
24}
25 
26/// Widget context passed during updates
27pub struct WidgetContext<'a> {
28 pub theme: &'a crate::theme::Theme,
29 pub state: WidgetState,
30 pub is_focused: bool,
31 pub is_hovered: bool,
32 pub delta_time: f32,
33}
34 
35/// Base trait for all widgets
36pub trait Widget: Debug + Send + Sync {
37 /// Get the widget's unique ID
38 fn id(&self) -> WidgetId;
39 
40 /// Calculate the widget's size given constraints
41 fn layout(&mut self, constraints: Constraints) -> Size;
42 
43 /// Render the widget
44 fn render(&self, batch: &mut RenderBatch, layout: Layout);
45 
46 /// Handle an event
47 fn handle_event(&mut self, _event: &Event) -> EventResult {
48 EventResult::Ignored
49 }
50 
51 /// Update the widget state
52 fn update(&mut self, _ctx: &WidgetContext) {}
53 
54 /// Get children widgets
55 fn children(&self) -> Vec<&(dyn Widget + '_)> {
56 vec![]
57 }
58 
59 /// Get mutable children widgets
60 fn children_mut(&mut self) -> Vec<&mut (dyn Widget + '_)> {
61 vec![]
62 }
63 
64 /// Check if point is inside widget
65 fn hit_test(&self, point: Point, layout: Layout) -> bool {
66 layout.contains(point.to_vec2())
67 }
68 
69 /// Get widget as Any for downcasting
70 fn as_any(&self) -> &dyn Any;
71 
72 /// Get mutable widget as Any for downcasting
73 fn as_any_mut(&mut self) -> &mut dyn Any;
74 
75 /// Clone the widget
76 fn clone_widget(&self) -> Box<dyn Widget>;
77 
78 /// Get the widget as TaffyWidget if supported
79 /// Get the widget as TaffyWidget if supported
80 fn as_taffy(&self) -> Option<&dyn TaffyWidget> {
81 None
82 }
83 
84 /// Render using Taffy layout
85 fn render_taffy(
86 &self,
87 batch: &mut RenderBatch,
88 tree: &strato_core::taffy::tree::TaffyTree<()>,
89 node: strato_core::taffy::prelude::NodeId,
90 parent_offset: Point,
91 ) {
92 if let Ok(layout) = tree.layout(node) {
93 let position = parent_offset + Point::new(layout.location.x, layout.location.y);
94 let size = Size::new(layout.size.width, layout.size.height);
95 let widget_layout = Layout::new(position.to_vec2(), size);
96 
97 self.render(batch, widget_layout);
98 }
99 }
100}
101 
102/// Generate a unique widget ID
103pub fn generate_id() -> WidgetId {
104 use std::sync::atomic::{AtomicU64, Ordering};
105 static COUNTER: AtomicU64 = AtomicU64::new(0);
106 COUNTER.fetch_add(1, Ordering::SeqCst)
107}
108 
109/// Base widget implementation helper
110#[derive(Debug, Clone)]
111pub struct BaseWidget {
112 id: WidgetId,
113 size: Size,
114 min_size: Size,
115 max_size: Size,
116 flex_grow: f32,
117 flex_shrink: f32,
118}
119 
120impl BaseWidget {
121 /// Create a new base widget
122 pub fn new() -> Self {
123 Self {
124 id: generate_id(),
125 size: Size::zero(),
126 min_size: Size::zero(),
127 max_size: Size::new(f32::INFINITY, f32::INFINITY),
128 flex_grow: 0.0,
129 flex_shrink: 1.0,
130 }
131 }
132 
133 /// Get the widget ID
134 pub fn id(&self) -> WidgetId {
135 self.id
136 }
137 
138 /// Set minimum size
139 pub fn with_min_size(mut self, width: f32, height: f32) -> Self {
140 self.min_size = Size::new(width, height);
141 self
142 }
143 
144 /// Set maximum size
145 pub fn with_max_size(mut self, width: f32, height: f32) -> Self {
146 self.max_size = Size::new(width, height);
147 self
148 }
149 
150 /// Set flex grow factor
151 pub fn with_flex_grow(mut self, factor: f32) -> Self {
152 self.flex_grow = factor;
153 self
154 }
155 
156 /// Set flex shrink factor
157 pub fn with_flex_shrink(mut self, factor: f32) -> Self {
158 self.flex_shrink = factor;
159 self
160 }
161 
162 /// Calculate size within constraints
163 pub fn calculate_size(&self, constraints: Constraints) -> Size {
164 Size::new(
165 self.size.width.clamp(
166 self.min_size.width.max(constraints.min_width),
167 self.max_size.width.min(constraints.max_width),
168 ),
169 self.size.height.clamp(
170 self.min_size.height.max(constraints.min_height),
171 self.max_size.height.min(constraints.max_height),
172 ),
173 )
174 }
175}
176 
177impl Default for BaseWidget {
178 fn default() -> Self {
179 Self::new()
180 }
181}
182 
183/// Widget event handler
184pub trait EventHandler: Send + Sync {
185 /// Handle widget event
186 fn handle(&mut self, event: &Event, widget_id: WidgetId) -> EventResult;
187}
188 
189/// Widget lifecycle
190pub trait Lifecycle {
191 /// Called when widget is mounted
192 fn on_mount(&mut self) {}
193 
194 /// Called when widget is unmounted
195 fn on_unmount(&mut self) {}
196 
197 /// Called when widget needs to update
198 fn on_update(&mut self, _delta_time: f32) {}
199}
200 
201/// Focusable widget trait
202pub trait Focusable {
203 /// Check if widget can receive focus
204 fn can_focus(&self) -> bool {
205 true
206 }
207 
208 /// Called when widget gains focus
209 fn on_focus(&mut self) {}
210 
211 /// Called when widget loses focus
212 fn on_blur(&mut self) {}
213}
214 
215/// Hoverable widget trait
216pub trait Hoverable {
217 /// Called when mouse enters widget
218 fn on_mouse_enter(&mut self) {}
219 
220 /// Called when mouse leaves widget
221 fn on_mouse_exit(&mut self) {}
222 
223 /// Called when mouse moves over widget
224 fn on_mouse_move(&mut self, _position: Point) {}
225}
226