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