1use crate::{
2 group_bounds, AnyElement, DispatchPhase, Element, ElementId, IdentifiedElement, IntoAnyElement,
3 MouseDownEvent, MouseMoveEvent, MouseUpEvent, SharedString, Style, StyleCascade,
4 StyleRefinement, ViewContext,
5};
6use parking_lot::Mutex;
7use refineable::{CascadeSlot, Refineable};
8use smallvec::SmallVec;
9use std::sync::{
10 atomic::{AtomicBool, Ordering::SeqCst},
11 Arc,
12};
13
14trait LayoutNode<V: 'static + Send + Sync, K: ElementKind> {
15 fn state(&mut self) -> &mut LayoutNodeElement<V, K>;
16
17 fn child(mut self, child: impl IntoAnyElement<V>) -> Self
18 where
19 Self: Sized,
20 {
21 self.state().children.push(child.into_any());
22 self
23 }
24
25 fn children<C, E>(mut self, children: C) -> Self
26 where
27 C: IntoIterator<Item = E>,
28 E: IntoAnyElement<V>,
29 Self: Sized,
30 {
31 for child in children {
32 self.state().children.push(child.into_any());
33 }
34 self
35 }
36}
37
38pub trait ElementKind: 'static + Send + Sync {
39 fn id(&self) -> Option<ElementId>;
40}
41
42pub struct Identified(ElementId);
43pub struct Anonymous;
44
45impl ElementKind for Identified {
46 fn id(&self) -> Option<ElementId> {
47 Some(self.0.clone())
48 }
49}
50
51impl ElementKind for Anonymous {
52 fn id(&self) -> Option<ElementId> {
53 None
54 }
55}
56
57struct LayoutNodeElement<V: 'static + Send + Sync, K: ElementKind> {
58 style_cascade: StyleCascade,
59 computed_style: Option<Style>,
60 children: SmallVec<[AnyElement<V>; 2]>,
61 kind: K,
62}
63
64impl<V: 'static + Send + Sync> LayoutNodeElement<V, Anonymous> {
65 pub fn identify(self, id: impl Into<ElementId>) -> LayoutNodeElement<V, Identified> {
66 LayoutNodeElement {
67 style_cascade: self.style_cascade,
68 computed_style: self.computed_style,
69 children: self.children,
70 kind: Identified(id.into()),
71 }
72 }
73}
74
75impl<V: 'static + Send + Sync, K: ElementKind> Styled for LayoutNodeElement<V, K> {
76 fn style_cascade(&mut self) -> &mut StyleCascade {
77 &mut self.style_cascade
78 }
79
80 fn computed_style(&mut self) -> &Style {
81 self.computed_style
82 .get_or_insert_with(|| Style::default().refined(self.style_cascade.merged()))
83 }
84}
85
86impl<V: 'static + Send + Sync> IdentifiedElement for LayoutNodeElement<V, Identified> {
87 fn element_id(&self) -> crate::ElementId {
88 self.kind.0.clone()
89 }
90}
91
92impl<V, K> IntoAnyElement<V> for LayoutNodeElement<V, K>
93where
94 V: 'static + Send + Sync,
95 K: ElementKind,
96{
97 fn into_any(self) -> AnyElement<V> {
98 AnyElement::new(self)
99 }
100}
101
102impl<V: 'static + Send + Sync, K: ElementKind> Element for LayoutNodeElement<V, K> {
103 type ViewState = V;
104 type ElementState = ();
105
106 fn id(&self) -> Option<crate::ElementId> {
107 None
108 }
109
110 fn layout(
111 &mut self,
112 state: &mut Self::ViewState,
113 _: Option<Self::ElementState>,
114 cx: &mut crate::ViewContext<Self::ViewState>,
115 ) -> (crate::LayoutId, Self::ElementState) {
116 let layout_ids = self
117 .children
118 .iter_mut()
119 .map(|child| child.layout(state, cx))
120 .collect::<Vec<_>>();
121
122 // todo!("pass just the style cascade")
123 let style = self.computed_style().clone();
124 let layout_id = cx.request_layout(style, layout_ids);
125 (layout_id, ())
126 }
127
128 fn paint(
129 &mut self,
130 _: crate::Bounds<crate::Pixels>,
131 state: &mut Self::ViewState,
132 _: &mut Self::ElementState,
133 cx: &mut crate::ViewContext<Self::ViewState>,
134 ) {
135 for child in &mut self.children {
136 child.paint(state, None, cx);
137 }
138 }
139}
140
141pub trait Styled {
142 fn style_cascade(&mut self) -> &mut StyleCascade;
143 fn computed_style(&mut self) -> &Style;
144}
145
146pub trait Hoverable {
147 fn hover_style(&mut self) -> &mut StyleRefinement;
148
149 fn hover(mut self, f: impl FnOnce(&mut StyleRefinement) -> &mut StyleRefinement) -> Self
150 where
151 Self: Sized,
152 {
153 f(self.hover_style());
154 self
155 }
156}
157
158struct HoverableElement<E> {
159 hover_style: StyleRefinement,
160 group: Option<SharedString>,
161 cascade_slot: CascadeSlot,
162 hovered: Arc<AtomicBool>,
163 child: E,
164}
165
166impl<E: Styled + Element> HoverableElement<E> {
167 pub fn replace_child<E2: Element<ViewState = E::ViewState>>(
168 self,
169 replace: impl FnOnce(E) -> E2,
170 ) -> HoverableElement<E2> {
171 HoverableElement {
172 hover_style: self.hover_style,
173 group: self.group,
174 cascade_slot: self.cascade_slot,
175 hovered: self.hovered,
176 child: replace(self.child),
177 }
178 }
179
180 fn hover_style(&mut self) -> &mut StyleRefinement {
181 &mut self.hover_style
182 }
183}
184
185impl<E> IntoAnyElement<E::ViewState> for HoverableElement<E>
186where
187 E: Styled + Element,
188{
189 fn into_any(self) -> AnyElement<E::ViewState> {
190 AnyElement::new(self)
191 }
192}
193
194impl<E> Element for HoverableElement<E>
195where
196 E: Styled + Element,
197{
198 type ViewState = E::ViewState;
199 type ElementState = E::ElementState;
200
201 fn id(&self) -> Option<crate::ElementId> {
202 self.child.id()
203 }
204
205 fn layout(
206 &mut self,
207 state: &mut Self::ViewState,
208 element_state: Option<Self::ElementState>,
209 cx: &mut crate::ViewContext<Self::ViewState>,
210 ) -> (crate::LayoutId, Self::ElementState) {
211 self.child.layout(state, element_state, cx)
212 }
213
214 fn paint(
215 &mut self,
216 bounds: crate::Bounds<crate::Pixels>,
217 state: &mut Self::ViewState,
218 element_state: &mut Self::ElementState,
219 cx: &mut crate::ViewContext<Self::ViewState>,
220 ) {
221 let target_bounds = self
222 .group
223 .as_ref()
224 .and_then(|group| group_bounds(group, cx))
225 .unwrap_or(bounds);
226
227 let hovered = target_bounds.contains_point(cx.mouse_position());
228
229 let slot = self.cascade_slot;
230 let style = hovered.then_some(self.hover_style.clone());
231 self.child.style_cascade().set(slot, style);
232 self.hovered.store(hovered, SeqCst);
233
234 let hovered = self.hovered.clone();
235 cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
236 if phase == DispatchPhase::Capture {
237 if target_bounds.contains_point(event.position) != hovered.load(SeqCst) {
238 cx.notify();
239 }
240 }
241 });
242
243 self.child.paint(bounds, state, element_state, cx);
244 }
245}
246
247impl<E: Styled + Element> Styled for HoverableElement<E> {
248 fn style_cascade(&mut self) -> &mut StyleCascade {
249 self.child.style_cascade()
250 }
251
252 fn computed_style(&mut self) -> &Style {
253 self.child.computed_style()
254 }
255}
256
257impl<E: Styled + IdentifiedElement> IdentifiedElement for HoverableElement<E> {}
258
259pub trait Clickable: Element + Sized {
260 fn active_style(&mut self) -> &mut StyleRefinement;
261 fn listeners(&mut self) -> &mut ClickListeners<Self::ViewState>;
262
263 fn on_click(
264 &mut self,
265 f: impl Fn(&mut Self::ViewState, &MouseClickEvent, &mut ViewContext<Self::ViewState>)
266 + 'static
267 + Send
268 + Sync,
269 ) where
270 Self: Sized,
271 {
272 self.listeners().push(Arc::new(f));
273 }
274
275 fn active(mut self, f: impl FnOnce(&mut StyleRefinement) -> &mut StyleRefinement) -> Self
276 where
277 Self: Sized,
278 {
279 f(self.active_style());
280 self
281 }
282}
283
284type ClickListeners<V> =
285 SmallVec<[Arc<dyn Fn(&mut V, &MouseClickEvent, &mut ViewContext<V>) + Send + Sync>; 1]>;
286
287pub struct ClickableElementState<E: 'static + Send + Sync> {
288 mouse_down: Arc<Mutex<Option<MouseDownEvent>>>,
289 child_state: E,
290}
291
292pub struct MouseClickEvent {
293 pub down: MouseDownEvent,
294 pub up: MouseUpEvent,
295}
296
297pub struct ClickableElement<E: Element> {
298 child: E,
299 listeners: ClickListeners<E::ViewState>,
300 active_style: StyleRefinement,
301 cascade_slot: CascadeSlot,
302}
303
304impl<E: Element> ClickableElement<E> {
305 pub fn replace_child<E2: Element<ViewState = E::ViewState>>(
306 self,
307 replace: impl FnOnce(E) -> E2,
308 ) -> ClickableElement<E2> {
309 ClickableElement {
310 child: replace(self.child),
311 listeners: self.listeners,
312 active_style: self.active_style,
313 cascade_slot: self.cascade_slot,
314 }
315 }
316}
317
318impl<E> IntoAnyElement<E::ViewState> for ClickableElement<E>
319where
320 E: Styled + Element,
321{
322 fn into_any(self) -> AnyElement<E::ViewState> {
323 AnyElement::new(self)
324 }
325}
326
327impl<E> Element for ClickableElement<E>
328where
329 E: Styled + Element,
330{
331 type ViewState = E::ViewState;
332 type ElementState = ClickableElementState<E::ElementState>;
333
334 fn id(&self) -> Option<crate::ElementId> {
335 self.child.id()
336 }
337
338 fn layout(
339 &mut self,
340 state: &mut Self::ViewState,
341 element_state: Option<Self::ElementState>,
342 cx: &mut crate::ViewContext<Self::ViewState>,
343 ) -> (crate::LayoutId, Self::ElementState) {
344 if let Some(element_state) = element_state {
345 if element_state.mouse_down.lock().is_some() {
346 self.child
347 .style_cascade()
348 .set(self.cascade_slot, Some(self.active_style.clone()));
349 }
350
351 let (layout_id, child_state) =
352 self.child
353 .layout(state, Some(element_state.child_state), cx);
354 (
355 layout_id,
356 ClickableElementState {
357 mouse_down: element_state.mouse_down,
358 child_state,
359 },
360 )
361 } else {
362 let (layout_id, child_state) = self.child.layout(state, None, cx);
363 (
364 layout_id,
365 ClickableElementState {
366 mouse_down: Default::default(),
367 child_state,
368 },
369 )
370 }
371 }
372
373 fn paint(
374 &mut self,
375 bounds: crate::Bounds<crate::Pixels>,
376 state: &mut Self::ViewState,
377 element_state: &mut Self::ElementState,
378 cx: &mut crate::ViewContext<Self::ViewState>,
379 ) {
380 if !self.listeners.is_empty() || self.active_style.is_some() {
381 if let Some(mouse_down) = element_state.mouse_down.lock().clone() {
382 self.child
383 .style_cascade()
384 .set(self.cascade_slot, Some(self.active_style.clone()));
385 let listeners = self.listeners.clone();
386 let mouse_down_mutex = element_state.mouse_down.clone();
387 cx.on_mouse_event(move |view, up: &MouseUpEvent, phase, cx| {
388 if phase == DispatchPhase::Bubble && bounds.contains_point(up.position) {
389 for listener in &*listeners {
390 listener(
391 view,
392 &MouseClickEvent {
393 down: mouse_down.clone(),
394 up: up.clone(),
395 },
396 cx,
397 );
398 }
399 }
400
401 mouse_down_mutex.lock().take();
402 cx.notify();
403 });
404 } else {
405 let mouse_down_mutex = element_state.mouse_down.clone();
406 cx.on_mouse_event(move |_view, down: &MouseDownEvent, phase, cx| {
407 if phase == DispatchPhase::Bubble && bounds.contains_point(down.position) {
408 *mouse_down_mutex.lock() = Some(down.clone());
409 cx.notify();
410 }
411 });
412 }
413 }
414
415 self.child
416 .paint(bounds, state, &mut element_state.child_state, cx);
417 }
418}
419
420impl<E: Styled + IdentifiedElement> IdentifiedElement for ClickableElement<E> {}
421
422impl<E> Clickable for ClickableElement<E>
423where
424 E: Styled + IdentifiedElement,
425{
426 fn active_style(&mut self) -> &mut StyleRefinement {
427 &mut self.active_style
428 }
429
430 fn listeners(&mut self) -> &mut ClickListeners<Self::ViewState> {
431 &mut self.listeners
432 }
433}
434
435pub struct Div<V: 'static + Send + Sync, K: ElementKind>(
436 ClickableElement<HoverableElement<LayoutNodeElement<V, K>>>,
437);
438
439impl<V: 'static + Send + Sync> Div<V, Anonymous> {
440 pub fn id(self, id: impl Into<ElementId>) -> Div<V, Identified> {
441 Div(self.0.replace_child(|hoverable| {
442 hoverable.replace_child(|layout_node| layout_node.identify(id))
443 }))
444 }
445}
446
447impl<V: 'static + Send + Sync, K: ElementKind> LayoutNode<V, K> for Div<V, K> {
448 fn state(&mut self) -> &mut LayoutNodeElement<V, K> {
449 &mut self.0.child.child
450 }
451}
452
453impl<V: 'static + Send + Sync, K: ElementKind> Styled for Div<V, K> {
454 fn style_cascade(&mut self) -> &mut StyleCascade {
455 self.0.child.child.style_cascade()
456 }
457
458 fn computed_style(&mut self) -> &Style {
459 self.0.child.child.computed_style()
460 }
461}
462
463impl<V: 'static + Send + Sync, K: ElementKind> Hoverable for Div<V, K> {
464 fn hover_style(&mut self) -> &mut StyleRefinement {
465 self.0.child.hover_style()
466 }
467}
468
469impl<V: 'static + Send + Sync> Clickable for Div<V, Identified> {
470 fn active_style(&mut self) -> &mut StyleRefinement {
471 self.0.active_style()
472 }
473
474 fn listeners(&mut self) -> &mut ClickListeners<V> {
475 self.0.listeners()
476 }
477}
478
479impl<V, K> IntoAnyElement<V> for Div<V, K>
480where
481 V: 'static + Send + Sync,
482 K: ElementKind,
483{
484 fn into_any(self) -> AnyElement<V> {
485 AnyElement::new(self)
486 }
487}
488
489impl<V, K> Element for Div<V, K>
490where
491 V: 'static + Send + Sync,
492 K: ElementKind,
493{
494 type ViewState = V;
495 type ElementState = ClickableElementState<()>;
496
497 fn id(&self) -> Option<ElementId> {
498 self.0.id()
499 }
500
501 fn layout(
502 &mut self,
503 state: &mut Self::ViewState,
504 element_state: Option<Self::ElementState>,
505 cx: &mut crate::ViewContext<Self::ViewState>,
506 ) -> (crate::LayoutId, Self::ElementState) {
507 self.0.layout(state, element_state, cx)
508 }
509
510 fn paint(
511 &mut self,
512 bounds: crate::Bounds<crate::Pixels>,
513 state: &mut Self::ViewState,
514 element_state: &mut Self::ElementState,
515 cx: &mut crate::ViewContext<Self::ViewState>,
516 ) {
517 self.0.paint(bounds, state, element_state, cx);
518 }
519}