1use crate::animation::AnimationCoordinator;
10use crate::input::InputState;
11use crate::layout::tree::LayoutTree;
12use crate::state::StateRegistry;
13use crate::types::{Rect, WidgetId, WidgetState, ScrollState};
14use crate::widgets;
15use serde::{Deserialize, Serialize};
16
17#[derive(Clone, Debug, Default, Serialize, Deserialize)]
19pub struct ButtonResponse {
20 pub clicked: bool,
22 pub hovered: bool,
24 pub pressed: bool,
26 pub state: WidgetState,
28 pub rect: Rect,
30}
31
32pub struct Context {
34 pub input: InputState,
36
37 pub layout: LayoutTree,
39
40 pub registry: StateRegistry,
42
43 pub animations: AnimationCoordinator,
45
46 pub time: f64,
48}
49
50impl Context {
51 pub fn new(root_node: crate::layout::types::LayoutNode) -> Self {
52 Self {
53 input: InputState::default(),
54 layout: LayoutTree::new(root_node),
55 registry: StateRegistry::new(),
56 animations: AnimationCoordinator::new(),
57 time: 0.0,
58 }
59 }
60
61 pub fn begin_frame(&mut self, input: InputState, viewport: Rect) {
63 self.input = input;
64 self.time = self.input.time;
65
66 self.animations.update(self.time);
68
69 self.layout.compute(viewport);
71 }
72
73 pub fn state<T: 'static + Send + Sync + Default>(&mut self, id: impl Into<WidgetId>) -> &mut T {
75 self.registry.get_or_insert_with(id.into(), T::default)
76 }
77
78 pub fn widget_rect(&self, id: &WidgetId) -> Rect {
80 self.layout.get_rect(id).unwrap_or_default()
81 }
82
83 pub fn button(&mut self, id: impl Into<WidgetId>) -> ButtonResponse {
89 let id = id.into();
90 let rect = self.widget_rect(&id);
91 let is_hovered = self.input.is_hovered(&rect);
92 let clicked = is_hovered && self.input.is_clicked();
93
94 let state = if clicked {
95 WidgetState::Pressed
96 } else if is_hovered {
97 WidgetState::Hovered
98 } else {
99 WidgetState::Normal
100 };
101
102 ButtonResponse {
103 clicked,
104 hovered: is_hovered,
105 pressed: clicked,
106 state,
107 rect,
108 }
109 }
110
111 pub fn checkbox(&mut self, id: impl Into<WidgetId>, checked: bool) -> widgets::checkbox::CheckboxResponse {
113 let id = id.into();
114 let rect = self.widget_rect(&id);
115 let is_hovered = self.input.is_hovered(&rect);
116 let clicked = is_hovered && self.input.is_clicked();
117
118 let state = if clicked {
119 WidgetState::Pressed
120 } else if is_hovered {
121 WidgetState::Hovered
122 } else {
123 WidgetState::Normal
124 };
125
126 let toggled = clicked;
127 let new_checked = if toggled { !checked } else { checked };
128
129 widgets::checkbox::CheckboxResponse {
130 toggled,
131 new_checked,
132 hovered: is_hovered,
133 state,
134 rect,
135 }
136 }
137
138 pub fn scroll_area(&mut self, id: impl Into<WidgetId>, content_height: f64) -> (Rect, ScrollState) {
140 let id = id.into();
141 let viewport = self.widget_rect(&id);
142 let is_hovered = self.input.is_hovered(&viewport);
143
144 let dt = self.input.dt;
145 let scroll_delta_y = if is_hovered { self.input.scroll_delta.1 } else { 0.0 };
146
147 let state = self.state::<ScrollState>(id);
148 state.content_size = content_height;
149
150 if scroll_delta_y != 0.0 {
151 state.velocity -= scroll_delta_y * 1500.0;
152 }
153
154 state.offset += state.velocity * dt;
155 state.velocity *= 0.90;
156
157 if state.velocity.abs() < 0.1 {
158 state.velocity = 0.0;
159 }
160
161 let max_scroll = (content_height - viewport.height).max(0.0);
162 if state.offset < 0.0 {
163 state.offset = 0.0;
164 state.velocity = 0.0;
165 } else if state.offset > max_scroll {
166 state.offset = max_scroll;
167 state.velocity = 0.0;
168 }
169
170 (viewport, state.clone())
171 }
172
173 pub fn icon_button(
175 &mut self,
176 id: impl Into<WidgetId>,
177 ) -> widgets::icon_button::IconButtonResponse {
178 let id = id.into();
179 let rect = self.widget_rect(&id);
180 let is_hovered = self.input.is_hovered(&rect);
181 let clicked = is_hovered && self.input.is_clicked();
182
183 let state = if is_hovered {
184 if self.input.is_mouse_down() {
185 WidgetState::Pressed
186 } else {
187 WidgetState::Hovered
188 }
189 } else {
190 WidgetState::Normal
191 };
192
193 if clicked {
194 println!("[UZOR Core] Button '{:?}' CLICKED at rect {:?}", id, rect);
195 } else if is_hovered {
196 println!("[UZOR Core] Button '{:?}' HOVERED at rect {:?}", id, rect);
197 }
198
199 widgets::icon_button::IconButtonResponse {
200 clicked,
201 hovered: is_hovered,
202 state,
203 }
204 }
205}