1use super::*;
2
3struct State {
4 size: vec2<f64>,
5 scale: f64,
6 constraints: HashMap<*const c_void, Constraints>,
7 positions: HashMap<*const c_void, Aabb2<f64>>,
8 states: Vec<std::cell::UnsafeCell<Box<dyn std::any::Any>>>,
9 next_state: usize,
10 cursor_pos: Option<vec2<f64>>,
11}
12
13impl State {
14 fn get_constraints(&self, widget: &dyn Widget) -> Constraints {
15 self.constraints[&widget_ptr(widget)]
16 }
17 fn set_constraints(&mut self, widget: &dyn Widget, constraints: Constraints) {
18 self.constraints.insert(widget_ptr(widget), constraints);
19 }
20 fn get_position(&self, widget: &dyn Widget) -> Aabb2<f64> {
21 self.positions[&widget_ptr(widget)]
22 }
23 fn set_position(&mut self, widget: &dyn Widget, position: Aabb2<f64>) {
24 self.positions.insert(widget_ptr(widget), position);
25 }
26}
27
28fn widget_ptr(widget: &dyn Widget) -> *const c_void {
29 widget as *const _ as _
30}
31
32pub struct ConstraintsContext<'a> {
33 pub theme: &'a Theme,
34 state: &'a State,
35}
36
37impl ConstraintsContext<'_> {
38 pub fn get_constraints(&self, widget: &dyn Widget) -> Constraints {
39 self.state.get_constraints(widget)
40 }
41}
42
43pub struct LayoutContext<'a> {
44 pub theme: &'a Theme,
45 pub position: Aabb2<f64>,
46 state: &'a mut State,
47}
48
49impl LayoutContext<'_> {
50 pub fn get_constraints(&self, widget: &dyn Widget) -> Constraints {
51 self.state.get_constraints(widget)
52 }
53 pub fn set_position(&mut self, widget: &dyn Widget, position: Aabb2<f64>) {
54 self.state.set_position(widget, position);
55 }
56}
57
58pub struct Controller {
59 target_ui_resolution: Option<vec2<f64>>,
60 draw2d: draw2d::Helper,
61 theme: Theme,
62 state: RefCell<State>,
63}
64
65impl Controller {
66 pub fn new(ugli: &Ugli, theme: Theme, target_ui_resolution: Option<vec2<f64>>) -> Self {
67 Self {
68 target_ui_resolution,
69 draw2d: draw2d::Helper::new(ugli, true),
70 theme,
71 state: RefCell::new(State {
72 size: vec2(1.0, 1.0),
73 scale: 1.0,
74 constraints: Default::default(),
75 positions: Default::default(),
76 states: Vec::new(),
77 next_state: 0,
78 cursor_pos: None,
79 }),
80 }
81 }
82
83 pub fn draw2d(&self) -> &draw2d::Helper {
84 &self.draw2d
85 }
86 pub fn theme(&self) -> &Theme {
87 &self.theme
88 }
89 pub fn get_state<T: Default + 'static>(&self) -> &mut T {
90 self.get_state_with(T::default)
91 }
92 #[allow(clippy::mut_from_ref)]
93 pub fn get_state_with<T: 'static>(&self, f: impl FnOnce() -> T) -> &mut T {
94 let mut f = Some(f);
95 let mut state = self.state.borrow_mut();
96 if state.next_state >= state.states.len() {
97 state
98 .states
99 .push(std::cell::UnsafeCell::new(Box::new(f.take().unwrap()())));
100 }
101 let current: &mut Box<dyn std::any::Any> =
102 unsafe { &mut *state.states[state.next_state].get() };
103 if !current.is::<T>() {
104 *current = Box::new(f.take().unwrap()());
105 }
106 state.next_state += 1;
107 current.downcast_mut().unwrap()
108 }
109}
110
111impl Controller {
112 pub fn update(&self, root: &mut dyn Widget, delta_time: f64) {
113 self.layout(root);
114 traverse_mut(root, &mut |widget| widget.update(delta_time), &mut |_| {});
115 }
116 fn layout(&self, root: &mut dyn Widget) {
117 let mut state = self.state.borrow_mut();
118 let state = state.deref_mut();
119 state.constraints.clear();
120 state.positions.clear();
121 traverse_mut(root, &mut |_| {}, &mut |widget| {
122 let constraints = widget.calc_constraints(&ConstraintsContext {
123 theme: &self.theme,
124 state,
125 });
126 state.set_constraints(widget, constraints);
127 });
128 let root_position = Aabb2::ZERO.extend_positive(state.size);
129 state.set_position(root, root_position);
130 traverse_mut(
131 root,
132 &mut |widget| {
133 widget.layout_children(&mut LayoutContext {
134 theme: &self.theme,
135 position: state.get_position(widget),
136 state,
137 });
138 },
139 &mut |_| {},
140 );
141 for position in state.positions.values_mut() {
142 *position = position.map(|x| x * state.scale);
143 }
144
145 while state.states.len() > state.next_state {
146 state.states.pop();
147 }
148 state.next_state = 0;
149 }
150 pub fn draw(&self, root: &mut dyn Widget, framebuffer: &mut ugli::Framebuffer) {
151 {
152 let mut state = self.state.borrow_mut();
153 let framebuffer_size = framebuffer.size().map(|x| x as f64);
154 state.scale = match self.target_ui_resolution {
155 Some(target_size) => {
156 (framebuffer_size.x / target_size.x).max(framebuffer_size.y / target_size.y)
157 }
158 None => 1.0,
159 };
160 state.size = framebuffer_size / state.scale;
161 }
162 self.layout(root);
163 let state = self.state.borrow();
164 traverse_mut(
165 root,
166 &mut |widget| {
167 widget.draw(&mut DrawContext {
168 draw2d: &self.draw2d,
169 theme: &self.theme,
170 position: state.get_position(widget),
171 framebuffer,
172 });
173 },
174 &mut |_| {},
175 );
176 }
177 pub fn handle_event(&self, root: &mut dyn Widget, event: Event) -> bool {
178 let event = &event;
179 self.layout(root);
180 let mut state = self.state.borrow_mut();
181 let mut captured = false;
182 traverse_mut(
183 root,
184 &mut |widget| {
185 let widget_position = state.get_position(widget);
186 enum CursorEvent {
187 Move(vec2<f64>),
188 Press(vec2<f64>),
189 Release(vec2<f64>),
190 }
191 let cursor = match *event {
192 Event::CursorMove { position } => {
193 state.cursor_pos = Some(position);
194 CursorEvent::Move(position)
195 }
196 Event::MousePress { .. } => {
197 if let Some(position) = state.cursor_pos {
198 CursorEvent::Press(position)
199 } else {
200 return;
201 }
202 }
203 Event::MouseRelease { .. } => {
204 if let Some(position) = state.cursor_pos {
205 CursorEvent::Release(position)
206 } else {
207 return;
208 }
209 }
210 Event::TouchMove(Touch { position, .. }) => CursorEvent::Move(position),
211 Event::TouchStart(Touch { position, .. }) => CursorEvent::Press(position),
212 Event::TouchEnd(Touch { position, .. }) => CursorEvent::Release(position),
213 _ => return,
214 };
215 match cursor {
216 CursorEvent::Move(position) => {
217 if let Some(sense) = widget.sense() {
218 sense.set_hovered(widget_position.contains(position));
219 }
220 widget.handle_event(event);
221 }
222 CursorEvent::Press(position) => {
223 if widget_position.contains(position) {
224 if let Some(sense) = widget.sense() {
225 sense.set_captured(true);
226 widget.handle_event(event);
227 }
228 } else if let Some(sense) = widget.sense() {
229 if sense.is_captured() {
230 widget.handle_event(event);
231 }
232 }
233 }
234 CursorEvent::Release(position) => {
235 let mut default_sense = Sense::default();
236 let sense = widget.sense().unwrap_or(&mut default_sense);
237 let was_captured = sense.is_captured();
238 sense.set_captured(false);
239 if was_captured && widget_position.contains(position) {
240 sense.click();
241 }
242 if was_captured || widget_position.contains(position) {
243 widget.handle_event(event);
244 }
245 }
246 }
247 },
248 &mut |widget| {
249 if let Some(sense) = widget.sense() {
250 if sense.is_captured() {
251 captured = true;
252 }
253 }
254 },
255 );
256 captured
257 }
258}