1use crate::{
2 point, Action, AnyDrag, AnyElement, AnyView, AppContext, BorrowWindow, Bounds, ClickEvent,
3 DispatchPhase, Element, FocusHandle, KeyContext, KeyDownEvent, KeyUpEvent, LayoutId,
4 MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, Point, Render,
5 ScrollWheelEvent, SharedString, Style, StyleRefinement, Styled, View, ViewContext, Visibility,
6};
7use collections::HashMap;
8use refineable::Refineable;
9use smallvec::SmallVec;
10use std::{
11 any::{Any, TypeId},
12 marker::PhantomData,
13 sync::Arc,
14};
15
16pub struct GroupStyle {
17 pub group: SharedString,
18 pub style: StyleRefinement,
19}
20
21pub trait InteractiveComponent<V: 'static> {
22 fn interactivity(&mut self) -> &mut Interactivity<V>;
23
24 fn hover(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
25 where
26 Self: Sized,
27 {
28 self.interactivity().hover_style = f(StyleRefinement::default());
29 self
30 }
31
32 fn group_hover(
33 mut self,
34 group_name: impl Into<SharedString>,
35 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
36 ) -> Self
37 where
38 Self: Sized,
39 {
40 self.interactivity().group_hover_style = Some(GroupStyle {
41 group: group_name.into(),
42 style: f(StyleRefinement::default()),
43 });
44 self
45 }
46
47 fn on_mouse_down(
48 mut self,
49 button: MouseButton,
50 handler: impl Fn(&mut V, &MouseDownEvent, &mut ViewContext<V>) + 'static,
51 ) -> Self
52 where
53 Self: Sized,
54 {
55 self.interactivity().mouse_down_listeners.push(Box::new(
56 move |view, event, bounds, phase, cx| {
57 if phase == DispatchPhase::Bubble
58 && event.button == button
59 && bounds.contains_point(&event.position)
60 {
61 handler(view, event, cx)
62 }
63 },
64 ));
65 self
66 }
67
68 fn on_mouse_up(
69 mut self,
70 button: MouseButton,
71 handler: impl Fn(&mut V, &MouseUpEvent, &mut ViewContext<V>) + 'static,
72 ) -> Self
73 where
74 Self: Sized,
75 {
76 self.interactivity().mouse_up_listeners.push(Box::new(
77 move |view, event, bounds, phase, cx| {
78 if phase == DispatchPhase::Bubble
79 && event.button == button
80 && bounds.contains_point(&event.position)
81 {
82 handler(view, event, cx)
83 }
84 },
85 ));
86 self
87 }
88
89 fn on_mouse_down_out(
90 mut self,
91 handler: impl Fn(&mut V, &MouseDownEvent, &mut ViewContext<V>) + 'static,
92 ) -> Self
93 where
94 Self: Sized,
95 {
96 self.interactivity().mouse_down_listeners.push(Box::new(
97 move |view, event, bounds, phase, cx| {
98 if phase == DispatchPhase::Capture && !bounds.contains_point(&event.position) {
99 handler(view, event, cx)
100 }
101 },
102 ));
103 self
104 }
105
106 fn on_mouse_up_out(
107 mut self,
108 button: MouseButton,
109 handler: impl Fn(&mut V, &MouseUpEvent, &mut ViewContext<V>) + 'static,
110 ) -> Self
111 where
112 Self: Sized,
113 {
114 self.interactivity().mouse_up_listeners.push(Box::new(
115 move |view, event, bounds, phase, cx| {
116 if phase == DispatchPhase::Capture
117 && event.button == button
118 && !bounds.contains_point(&event.position)
119 {
120 handler(view, event, cx);
121 }
122 },
123 ));
124 self
125 }
126
127 fn on_mouse_move(
128 mut self,
129 handler: impl Fn(&mut V, &MouseMoveEvent, &mut ViewContext<V>) + 'static,
130 ) -> Self
131 where
132 Self: Sized,
133 {
134 self.interactivity().mouse_move_listeners.push(Box::new(
135 move |view, event, bounds, phase, cx| {
136 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
137 handler(view, event, cx);
138 }
139 },
140 ));
141 self
142 }
143
144 fn on_scroll_wheel(
145 mut self,
146 handler: impl Fn(&mut V, &ScrollWheelEvent, &mut ViewContext<V>) + 'static,
147 ) -> Self
148 where
149 Self: Sized,
150 {
151 self.interactivity().scroll_wheel_listeners.push(Box::new(
152 move |view, event, bounds, phase, cx| {
153 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
154 handler(view, event, cx);
155 }
156 },
157 ));
158 self
159 }
160
161 /// Capture the given action, fires during the capture phase
162 fn capture_action<A: Action>(
163 mut self,
164 listener: impl Fn(&mut V, &A, &mut ViewContext<V>) + 'static,
165 ) -> Self
166 where
167 Self: Sized,
168 {
169 self.interactivity().action_listeners.push((
170 TypeId::of::<A>(),
171 Box::new(move |view, action, phase, cx| {
172 let action = action.downcast_ref().unwrap();
173 if phase == DispatchPhase::Capture {
174 listener(view, action, cx)
175 }
176 }),
177 ));
178 self
179 }
180
181 /// Add a listener for the given action, fires during the bubble event phase
182 fn on_action<A: Action>(
183 mut self,
184 listener: impl Fn(&mut V, &A, &mut ViewContext<V>) + 'static,
185 ) -> Self
186 where
187 Self: Sized,
188 {
189 self.interactivity().action_listeners.push((
190 TypeId::of::<A>(),
191 Box::new(move |view, action, phase, cx| {
192 let action = action.downcast_ref().unwrap();
193 if phase == DispatchPhase::Bubble {
194 listener(view, action, cx)
195 }
196 }),
197 ));
198 self
199 }
200
201 fn on_key_down(
202 mut self,
203 listener: impl Fn(&mut V, &KeyDownEvent, DispatchPhase, &mut ViewContext<V>) + 'static,
204 ) -> Self
205 where
206 Self: Sized,
207 {
208 self.interactivity()
209 .key_down_listeners
210 .push(Box::new(move |view, event, phase, cx| {
211 listener(view, event, phase, cx)
212 }));
213 self
214 }
215
216 fn on_key_up(
217 mut self,
218 listener: impl Fn(&mut V, &KeyUpEvent, DispatchPhase, &mut ViewContext<V>) + 'static,
219 ) -> Self
220 where
221 Self: Sized,
222 {
223 self.interactivity()
224 .key_up_listeners
225 .push(Box::new(move |view, event, phase, cx| {
226 listener(view, event, phase, cx)
227 }));
228 self
229 }
230
231 fn drag_over<S: 'static>(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
232 where
233 Self: Sized,
234 {
235 self.interactivity()
236 .drag_over_styles
237 .push((TypeId::of::<S>(), f(StyleRefinement::default())));
238 self
239 }
240
241 fn group_drag_over<S: 'static>(
242 mut self,
243 group_name: impl Into<SharedString>,
244 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
245 ) -> Self
246 where
247 Self: Sized,
248 {
249 self.interactivity().group_drag_over_styles.push((
250 TypeId::of::<S>(),
251 GroupStyle {
252 group: group_name.into(),
253 style: f(StyleRefinement::default()),
254 },
255 ));
256 self
257 }
258
259 fn on_drop<W: 'static>(
260 mut self,
261 listener: impl Fn(&mut V, View<W>, &mut ViewContext<V>) + 'static,
262 ) -> Self
263 where
264 Self: Sized,
265 {
266 self.interactivity().drop_listeners.push((
267 TypeId::of::<W>(),
268 Box::new(move |view, dragged_view, cx| {
269 listener(view, dragged_view.downcast().unwrap(), cx);
270 }),
271 ));
272 self
273 }
274}
275
276pub trait StatefulInteractiveComponent<V: 'static> {
277 fn interactivity(&mut self) -> &mut StatefulInteractivity<V>;
278
279 fn active(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
280 where
281 Self: Sized,
282 {
283 self.interactivity().active_style = f(StyleRefinement::default());
284 self
285 }
286
287 fn group_active(
288 mut self,
289 group_name: impl Into<SharedString>,
290 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
291 ) -> Self
292 where
293 Self: Sized,
294 {
295 self.interactivity().group_active_style = Some(GroupStyle {
296 group: group_name.into(),
297 style: f(StyleRefinement::default()),
298 });
299 self
300 }
301
302 fn on_click(
303 mut self,
304 listener: impl Fn(&mut V, &ClickEvent, &mut ViewContext<V>) + 'static,
305 ) -> Self
306 where
307 Self: Sized,
308 {
309 self.interactivity()
310 .click_listeners
311 .push(Box::new(move |view, event, cx| listener(view, event, cx)));
312 self
313 }
314
315 fn on_drag<W>(
316 mut self,
317 listener: impl Fn(&mut V, &mut ViewContext<V>) -> View<W> + 'static,
318 ) -> Self
319 where
320 Self: Sized,
321 W: 'static + Render,
322 {
323 debug_assert!(
324 self.interactivity().drag_listener.is_none(),
325 "calling on_drag more than once on the same element is not supported"
326 );
327 self.interactivity().drag_listener =
328 Some(Box::new(move |view_state, cursor_offset, cx| AnyDrag {
329 view: listener(view_state, cx).into(),
330 cursor_offset,
331 }));
332 self
333 }
334
335 fn on_hover(mut self, listener: impl 'static + Fn(&mut V, bool, &mut ViewContext<V>)) -> Self
336 where
337 Self: Sized,
338 {
339 debug_assert!(
340 self.interactivity().hover_listener.is_none(),
341 "calling on_hover more than once on the same element is not supported"
342 );
343 self.interactivity().hover_listener = Some(Box::new(listener));
344 self
345 }
346
347 fn tooltip<W>(
348 mut self,
349 build_tooltip: impl Fn(&mut V, &mut ViewContext<V>) -> View<W> + 'static,
350 ) -> Self
351 where
352 Self: Sized,
353 W: 'static + Render,
354 {
355 debug_assert!(
356 self.interactivity().tooltip_builder.is_none(),
357 "calling tooltip more than once on the same element is not supported"
358 );
359 self.interactivity().tooltip_builder = Some(Arc::new(move |view_state, cx| {
360 build_tooltip(view_state, cx).into()
361 }));
362
363 self
364 }
365}
366
367pub trait FocusableComponent<V> {
368 fn focusability(&mut self) -> &mut Focusability<V>;
369
370 fn focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
371 where
372 Self: Sized,
373 {
374 self.focusability().focus_style = f(StyleRefinement::default());
375 self
376 }
377
378 fn focus_in(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
379 where
380 Self: Sized,
381 {
382 self.focusability().focus_in_style = f(StyleRefinement::default());
383 self
384 }
385
386 fn in_focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
387 where
388 Self: Sized,
389 {
390 // self.focusability(). (f(StyleRefinement::default()));
391 self
392 }
393
394 fn on_focus(
395 mut self,
396 listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 'static,
397 ) -> Self
398 where
399 Self: Sized,
400 {
401 self.focusability()
402 .focus_listeners
403 .push(Box::new(move |view, focus_handle, event, cx| {
404 if event.focused.as_ref() == Some(focus_handle) {
405 listener(view, event, cx)
406 }
407 }));
408 self
409 }
410
411 fn on_blur(
412 mut self,
413 listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 'static,
414 ) -> Self
415 where
416 Self: Sized,
417 {
418 self.focusability()
419 .focus_listeners
420 .push(Box::new(move |view, focus_handle, event, cx| {
421 if event.blurred.as_ref() == Some(focus_handle) {
422 listener(view, event, cx)
423 }
424 }));
425 self
426 }
427
428 fn on_focus_in(
429 mut self,
430 listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 'static,
431 ) -> Self
432 where
433 Self: Sized,
434 {
435 self.focusability()
436 .focus_listeners
437 .push(Box::new(move |view, focus_handle, event, cx| {
438 let descendant_blurred = event
439 .blurred
440 .as_ref()
441 .map_or(false, |blurred| focus_handle.contains(blurred, cx));
442 let descendant_focused = event
443 .focused
444 .as_ref()
445 .map_or(false, |focused| focus_handle.contains(focused, cx));
446
447 if !descendant_blurred && descendant_focused {
448 listener(view, event, cx)
449 }
450 }));
451 self
452 }
453
454 fn on_focus_out(
455 mut self,
456 listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 'static,
457 ) -> Self
458 where
459 Self: Sized,
460 {
461 self.focusability()
462 .focus_listeners
463 .push(Box::new(move |view, focus_handle, event, cx| {
464 let descendant_blurred = event
465 .blurred
466 .as_ref()
467 .map_or(false, |blurred| focus_handle.contains(blurred, cx));
468 let descendant_focused = event
469 .focused
470 .as_ref()
471 .map_or(false, |focused| focus_handle.contains(focused, cx));
472 if descendant_blurred && !descendant_focused {
473 listener(view, event, cx)
474 }
475 }));
476 self
477 }
478}
479
480pub type FocusListeners<V> = SmallVec<[FocusListener<V>; 2]>;
481
482pub type FocusListener<V> =
483 Box<dyn Fn(&mut V, &FocusHandle, &FocusEvent, &mut ViewContext<V>) + 'static>;
484
485pub type MouseDownListener<V> = Box<
486 dyn Fn(&mut V, &MouseDownEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>) + 'static,
487>;
488pub type MouseUpListener<V> = Box<
489 dyn Fn(&mut V, &MouseUpEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>) + 'static,
490>;
491
492pub type MouseMoveListener<V> = Box<
493 dyn Fn(&mut V, &MouseMoveEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>) + 'static,
494>;
495
496pub type ScrollWheelListener<V> = Box<
497 dyn Fn(&mut V, &ScrollWheelEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
498 + 'static,
499>;
500
501pub type ClickListener<V> = Box<dyn Fn(&mut V, &ClickEvent, &mut ViewContext<V>) + 'static>;
502
503pub type DragListener<V> =
504 Box<dyn Fn(&mut V, Point<Pixels>, &mut ViewContext<V>) -> AnyDrag + 'static>;
505
506type DropListener<V> = dyn Fn(&mut V, AnyView, &mut ViewContext<V>) + 'static;
507
508pub type HoverListener<V> = Box<dyn Fn(&mut V, bool, &mut ViewContext<V>) + 'static>;
509
510pub type TooltipBuilder<V> = Arc<dyn Fn(&mut V, &mut ViewContext<V>) -> AnyView + 'static>;
511
512pub type KeyDownListener<V> =
513 Box<dyn Fn(&mut V, &KeyDownEvent, DispatchPhase, &mut ViewContext<V>) + 'static>;
514
515pub type KeyUpListener<V> =
516 Box<dyn Fn(&mut V, &KeyUpEvent, DispatchPhase, &mut ViewContext<V>) + 'static>;
517
518pub type ActionListener<V> =
519 Box<dyn Fn(&mut V, &dyn Any, DispatchPhase, &mut ViewContext<V>) + 'static>;
520
521pub struct FocusEvent {
522 pub blurred: Option<FocusHandle>,
523 pub focused: Option<FocusHandle>,
524}
525
526pub struct Node<V> {
527 style: StyleRefinement,
528 key_context: KeyContext,
529 interactivity: Interactivity<V>,
530 children: Vec<AnyElement<V>>,
531}
532
533pub struct Interactivity<V> {
534 group: Option<SharedString>,
535 pub dispatch_context: KeyContext,
536 pub mouse_down_listeners: SmallVec<[MouseDownListener<V>; 2]>,
537 pub mouse_up_listeners: SmallVec<[MouseUpListener<V>; 2]>,
538 pub mouse_move_listeners: SmallVec<[MouseMoveListener<V>; 2]>,
539 pub scroll_wheel_listeners: SmallVec<[ScrollWheelListener<V>; 2]>,
540 pub key_down_listeners: SmallVec<[KeyDownListener<V>; 2]>,
541 pub key_up_listeners: SmallVec<[KeyUpListener<V>; 2]>,
542 pub action_listeners: SmallVec<[(TypeId, ActionListener<V>); 8]>,
543 pub hover_style: StyleRefinement,
544 pub group_hover_style: Option<GroupStyle>,
545 drag_over_styles: SmallVec<[(TypeId, StyleRefinement); 2]>,
546 group_drag_over_styles: SmallVec<[(TypeId, GroupStyle); 2]>,
547 drop_listeners: SmallVec<[(TypeId, Box<DropListener<V>>); 2]>,
548 scroll_offset: Point<Pixels>,
549}
550
551impl<V> Node<V> {
552 fn compute_style(&self) -> Style {
553 let mut style = Style::default();
554 style.refine(&self.style);
555 style
556 }
557}
558
559impl<V> Styled for Node<V> {
560 fn style(&mut self) -> &mut StyleRefinement {
561 &mut self.style
562 }
563}
564
565impl<V: 'static> InteractiveComponent<V> for Node<V> {
566 fn interactivity(&mut self) -> &mut Interactivity<V> {
567 &mut self.interactivity
568 }
569}
570
571pub struct NodeState {
572 child_layout_ids: SmallVec<[LayoutId; 4]>,
573}
574
575impl<V: 'static> Element<V> for Node<V> {
576 type ElementState = NodeState;
577
578 fn id(&self) -> Option<crate::ElementId> {
579 None
580 }
581
582 fn initialize(
583 &mut self,
584 view_state: &mut V,
585 _: Option<Self::ElementState>,
586 cx: &mut ViewContext<V>,
587 ) -> Self::ElementState {
588 for child in &mut self.children {
589 child.initialize(view_state, cx);
590 }
591 NodeState {
592 child_layout_ids: SmallVec::new(),
593 }
594 }
595
596 fn layout(
597 &mut self,
598 view_state: &mut V,
599 element_state: &mut Self::ElementState,
600 cx: &mut ViewContext<V>,
601 ) -> crate::LayoutId {
602 let style = self.compute_style();
603 style.with_text_style(cx, |cx| {
604 element_state.child_layout_ids = self
605 .children
606 .iter_mut()
607 .map(|child| child.layout(view_state, cx))
608 .collect::<SmallVec<_>>();
609 cx.request_layout(&style, element_state.child_layout_ids.iter().copied())
610 })
611 }
612
613 fn paint(
614 &mut self,
615 bounds: Bounds<Pixels>,
616 view_state: &mut V,
617 element_state: &mut Self::ElementState,
618 cx: &mut ViewContext<V>,
619 ) {
620 let style = self.compute_style();
621 if style.visibility == Visibility::Hidden {
622 return;
623 }
624
625 if let Some(mouse_cursor) = style.mouse_cursor {
626 let hovered = bounds.contains_point(&cx.mouse_position());
627 if hovered {
628 cx.set_cursor_style(mouse_cursor);
629 }
630 }
631
632 if let Some(group) = self.interactivity.group.clone() {
633 GroupBounds::push(group, bounds, cx);
634 }
635
636 let z_index = style.z_index.unwrap_or(0);
637
638 let mut child_min = point(Pixels::MAX, Pixels::MAX);
639 let mut child_max = Point::default();
640
641 let content_size = if element_state.child_layout_ids.is_empty() {
642 bounds.size
643 } else {
644 for child_layout_id in &element_state.child_layout_ids {
645 let child_bounds = cx.layout_bounds(*child_layout_id);
646 child_min = child_min.min(&child_bounds.origin);
647 child_max = child_max.max(&child_bounds.lower_right());
648 }
649 (child_max - child_min).into()
650 };
651
652 cx.with_z_index(z_index, |cx| {
653 cx.with_z_index(0, |cx| {
654 style.paint(bounds, cx);
655 });
656 cx.with_z_index(1, |cx| {
657 style.with_text_style(cx, |cx| {
658 style.apply_overflow(bounds, cx, |cx| {
659 let scroll_offset = self.interactivity.scroll_offset;
660 cx.with_element_offset2(scroll_offset, |cx| {
661 for child in &mut self.children {
662 child.paint(view_state, cx);
663 }
664 });
665 })
666 })
667 });
668 });
669
670 if let Some(group) = self.interactivity.group.as_ref() {
671 GroupBounds::pop(group, cx);
672 }
673 }
674}
675
676#[derive(Default)]
677pub struct GroupBounds(HashMap<SharedString, SmallVec<[Bounds<Pixels>; 1]>>);
678
679impl GroupBounds {
680 pub fn get(name: &SharedString, cx: &mut AppContext) -> Option<Bounds<Pixels>> {
681 cx.default_global::<Self>()
682 .0
683 .get(name)
684 .and_then(|bounds_stack| bounds_stack.last())
685 .cloned()
686 }
687
688 pub fn push(name: SharedString, bounds: Bounds<Pixels>, cx: &mut AppContext) {
689 cx.default_global::<Self>()
690 .0
691 .entry(name)
692 .or_default()
693 .push(bounds);
694 }
695
696 pub fn pop(name: &SharedString, cx: &mut AppContext) {
697 cx.default_global::<Self>().0.get_mut(name).unwrap().pop();
698 }
699}
700
701pub struct Focusable<V, E> {
702 focusability: Focusability<V>,
703 view_type: PhantomData<V>,
704 element: E,
705}
706
707pub struct Focusability<V> {
708 focus_handle: Option<FocusHandle>,
709 focus_listeners: FocusListeners<V>,
710 focus_style: StyleRefinement,
711 focus_in_style: StyleRefinement,
712 in_focus_style: StyleRefinement,
713}
714
715impl<V, E> FocusableComponent<V> for Focusable<V, E> {
716 fn focusability(&mut self) -> &mut Focusability<V> {
717 &mut self.focusability
718 }
719}
720
721impl<V: 'static, E: InteractiveComponent<V>> InteractiveComponent<V> for Focusable<V, E> {
722 fn interactivity(&mut self) -> &mut Interactivity<V> {
723 self.element.interactivity()
724 }
725}
726
727impl<V: 'static, E: StatefulInteractiveComponent<V>> StatefulInteractiveComponent<V>
728 for Focusable<V, E>
729{
730 fn interactivity(&mut self) -> &mut StatefulInteractivity<V> {
731 self.element.interactivity()
732 }
733}
734
735pub struct Stateful<V, E> {
736 id: SharedString,
737 interactivity: StatefulInteractivity<V>,
738 view_type: PhantomData<V>,
739 element: E,
740}
741
742pub struct StatefulInteractivity<V> {
743 click_listeners: SmallVec<[ClickListener<V>; 2]>,
744 active_style: StyleRefinement,
745 group_active_style: Option<GroupStyle>,
746 drag_listener: Option<DragListener<V>>,
747 hover_listener: Option<HoverListener<V>>,
748 tooltip_builder: Option<TooltipBuilder<V>>,
749}
750
751impl<V: 'static, E> StatefulInteractiveComponent<V> for Stateful<V, E> {
752 fn interactivity(&mut self) -> &mut StatefulInteractivity<V> {
753 &mut self.interactivity
754 }
755}
756
757impl<V: 'static, E: InteractiveComponent<V>> InteractiveComponent<V> for Stateful<V, E> {
758 fn interactivity(&mut self) -> &mut Interactivity<V> {
759 self.element.interactivity()
760 }
761}
762
763impl<V, E: FocusableComponent<V>> FocusableComponent<V> for Stateful<V, E> {
764 fn focusability(&mut self) -> &mut Focusability<V> {
765 self.element.focusability()
766 }
767}