1use crate::{
2 point, px, Action, AnyDrag, AnyElement, AnyTooltip, AnyView, AppContext, BorrowAppContext,
3 BorrowWindow, Bounds, ClickEvent, Component, DispatchPhase, Element, ElementId, FocusEvent,
4 FocusHandle, KeyContext, KeyDownEvent, KeyUpEvent, LayoutId, MouseButton, MouseDownEvent,
5 MouseMoveEvent, MouseUpEvent, ParentComponent, Pixels, Point, Render, ScrollWheelEvent,
6 SharedString, Size, Style, StyleRefinement, Styled, Task, View, ViewContext, Visibility,
7};
8use collections::HashMap;
9use refineable::Refineable;
10use smallvec::SmallVec;
11use std::{
12 any::{Any, TypeId},
13 cell::RefCell,
14 fmt::Debug,
15 marker::PhantomData,
16 mem,
17 rc::Rc,
18 time::Duration,
19};
20use taffy::style::Overflow;
21use util::ResultExt;
22
23const DRAG_THRESHOLD: f64 = 2.;
24const TOOLTIP_DELAY: Duration = Duration::from_millis(500);
25const TOOLTIP_OFFSET: Point<Pixels> = Point::new(px(10.0), px(8.0));
26
27pub struct GroupStyle {
28 pub group: SharedString,
29 pub style: StyleRefinement,
30}
31
32pub trait InteractiveComponent<V: 'static>: Sized + Element<V> {
33 fn interactivity(&mut self) -> &mut Interactivity<V>;
34
35 fn group(mut self, group: impl Into<SharedString>) -> Self {
36 self.interactivity().group = Some(group.into());
37 self
38 }
39
40 fn id(mut self, id: impl Into<ElementId>) -> Stateful<V, Self> {
41 self.interactivity().element_id = Some(id.into());
42
43 Stateful {
44 element: self,
45 view_type: PhantomData,
46 }
47 }
48
49 fn track_focus(mut self, focus_handle: &FocusHandle) -> Focusable<V, Self> {
50 self.interactivity().focusable = true;
51 self.interactivity().tracked_focus_handle = Some(focus_handle.clone());
52 Focusable {
53 element: self,
54 view_type: PhantomData,
55 }
56 }
57
58 fn key_context<C, E>(mut self, key_context: C) -> Self
59 where
60 C: TryInto<KeyContext, Error = E>,
61 E: Debug,
62 {
63 if let Some(key_context) = key_context.try_into().log_err() {
64 self.interactivity().key_context = key_context;
65 }
66 self
67 }
68
69 fn hover(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self {
70 self.interactivity().hover_style = f(StyleRefinement::default());
71 self
72 }
73
74 fn group_hover(
75 mut self,
76 group_name: impl Into<SharedString>,
77 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
78 ) -> Self {
79 self.interactivity().group_hover_style = Some(GroupStyle {
80 group: group_name.into(),
81 style: f(StyleRefinement::default()),
82 });
83 self
84 }
85
86 fn on_mouse_down(
87 mut self,
88 button: MouseButton,
89 handler: impl Fn(&mut V, &MouseDownEvent, &mut ViewContext<V>) + 'static,
90 ) -> Self {
91 self.interactivity().mouse_down_listeners.push(Box::new(
92 move |view, event, bounds, phase, cx| {
93 if phase == DispatchPhase::Bubble
94 && event.button == button
95 && bounds.contains_point(&event.position)
96 {
97 handler(view, event, cx)
98 }
99 },
100 ));
101 self
102 }
103
104 fn on_any_mouse_down(
105 mut self,
106 handler: impl Fn(&mut V, &MouseDownEvent, &mut ViewContext<V>) + 'static,
107 ) -> Self {
108 self.interactivity().mouse_down_listeners.push(Box::new(
109 move |view, event, bounds, phase, cx| {
110 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
111 handler(view, event, cx)
112 }
113 },
114 ));
115 self
116 }
117
118 fn on_mouse_up(
119 mut self,
120 button: MouseButton,
121 handler: impl Fn(&mut V, &MouseUpEvent, &mut ViewContext<V>) + 'static,
122 ) -> Self {
123 self.interactivity().mouse_up_listeners.push(Box::new(
124 move |view, event, bounds, phase, cx| {
125 if phase == DispatchPhase::Bubble
126 && event.button == button
127 && bounds.contains_point(&event.position)
128 {
129 handler(view, event, cx)
130 }
131 },
132 ));
133 self
134 }
135
136 fn on_any_mouse_up(
137 mut self,
138 handler: impl Fn(&mut V, &MouseUpEvent, &mut ViewContext<V>) + 'static,
139 ) -> Self {
140 self.interactivity().mouse_up_listeners.push(Box::new(
141 move |view, event, bounds, phase, cx| {
142 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
143 handler(view, event, cx)
144 }
145 },
146 ));
147 self
148 }
149
150 fn on_mouse_down_out(
151 mut self,
152 handler: impl Fn(&mut V, &MouseDownEvent, &mut ViewContext<V>) + 'static,
153 ) -> Self {
154 self.interactivity().mouse_down_listeners.push(Box::new(
155 move |view, event, bounds, phase, cx| {
156 if phase == DispatchPhase::Capture && !bounds.contains_point(&event.position) {
157 handler(view, event, cx)
158 }
159 },
160 ));
161 self
162 }
163
164 fn on_mouse_up_out(
165 mut self,
166 button: MouseButton,
167 handler: impl Fn(&mut V, &MouseUpEvent, &mut ViewContext<V>) + 'static,
168 ) -> Self {
169 self.interactivity().mouse_up_listeners.push(Box::new(
170 move |view, event, bounds, phase, cx| {
171 if phase == DispatchPhase::Capture
172 && event.button == button
173 && !bounds.contains_point(&event.position)
174 {
175 handler(view, event, cx);
176 }
177 },
178 ));
179 self
180 }
181
182 fn on_mouse_move(
183 mut self,
184 handler: impl Fn(&mut V, &MouseMoveEvent, &mut ViewContext<V>) + 'static,
185 ) -> Self {
186 self.interactivity().mouse_move_listeners.push(Box::new(
187 move |view, event, bounds, phase, cx| {
188 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
189 handler(view, event, cx);
190 }
191 },
192 ));
193 self
194 }
195
196 fn on_scroll_wheel(
197 mut self,
198 handler: impl Fn(&mut V, &ScrollWheelEvent, &mut ViewContext<V>) + 'static,
199 ) -> Self {
200 self.interactivity().scroll_wheel_listeners.push(Box::new(
201 move |view, event, bounds, phase, cx| {
202 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
203 handler(view, event, cx);
204 }
205 },
206 ));
207 self
208 }
209
210 /// Capture the given action, fires during the capture phase
211 fn capture_action<A: Action>(
212 mut self,
213 listener: impl Fn(&mut V, &A, &mut ViewContext<V>) + 'static,
214 ) -> Self {
215 self.interactivity().action_listeners.push((
216 TypeId::of::<A>(),
217 Box::new(move |view, action, phase, cx| {
218 let action = action.downcast_ref().unwrap();
219 if phase == DispatchPhase::Capture {
220 listener(view, action, cx)
221 }
222 }),
223 ));
224 self
225 }
226
227 /// Add a listener for the given action, fires during the bubble event phase
228 fn on_action<A: Action>(
229 mut self,
230 listener: impl Fn(&mut V, &A, &mut ViewContext<V>) + 'static,
231 ) -> Self {
232 // NOTE: this debug assert has the side-effect of working around
233 // a bug where a crate consisting only of action definitions does
234 // not register the actions in debug builds:
235 //
236 // https://github.com/rust-lang/rust/issues/47384
237 // https://github.com/mmastrac/rust-ctor/issues/280
238 //
239 // if we are relying on this side-effect still, removing the debug_assert!
240 // likely breaks the command_palette tests.
241 debug_assert!(
242 A::is_registered(),
243 "{:?} is not registered as an action",
244 A::qualified_name()
245 );
246 self.interactivity().action_listeners.push((
247 TypeId::of::<A>(),
248 Box::new(move |view, action, phase, cx| {
249 let action = action.downcast_ref().unwrap();
250 if phase == DispatchPhase::Bubble {
251 listener(view, action, cx)
252 }
253 }),
254 ));
255 self
256 }
257
258 fn on_key_down(
259 mut self,
260 listener: impl Fn(&mut V, &KeyDownEvent, DispatchPhase, &mut ViewContext<V>) + 'static,
261 ) -> Self {
262 self.interactivity()
263 .key_down_listeners
264 .push(Box::new(move |view, event, phase, cx| {
265 listener(view, event, phase, cx)
266 }));
267 self
268 }
269
270 fn on_key_up(
271 mut self,
272 listener: impl Fn(&mut V, &KeyUpEvent, DispatchPhase, &mut ViewContext<V>) + 'static,
273 ) -> Self {
274 self.interactivity()
275 .key_up_listeners
276 .push(Box::new(move |view, event, phase, cx| {
277 listener(view, event, phase, cx)
278 }));
279 self
280 }
281
282 fn drag_over<S: 'static>(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self {
283 self.interactivity()
284 .drag_over_styles
285 .push((TypeId::of::<S>(), f(StyleRefinement::default())));
286 self
287 }
288
289 fn group_drag_over<S: 'static>(
290 mut self,
291 group_name: impl Into<SharedString>,
292 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
293 ) -> Self {
294 self.interactivity().group_drag_over_styles.push((
295 TypeId::of::<S>(),
296 GroupStyle {
297 group: group_name.into(),
298 style: f(StyleRefinement::default()),
299 },
300 ));
301 self
302 }
303
304 fn on_drop<W: 'static>(
305 mut self,
306 listener: impl Fn(&mut V, View<W>, &mut ViewContext<V>) + 'static,
307 ) -> Self {
308 self.interactivity().drop_listeners.push((
309 TypeId::of::<W>(),
310 Box::new(move |view, dragged_view, cx| {
311 listener(view, dragged_view.downcast().unwrap(), cx);
312 }),
313 ));
314 self
315 }
316}
317
318pub trait StatefulInteractiveComponent<V: 'static, E: Element<V>>: InteractiveComponent<V> {
319 fn focusable(mut self) -> Focusable<V, Self> {
320 self.interactivity().focusable = true;
321 Focusable {
322 element: self,
323 view_type: PhantomData,
324 }
325 }
326
327 fn overflow_scroll(mut self) -> Self {
328 self.interactivity().base_style.overflow.x = Some(Overflow::Scroll);
329 self.interactivity().base_style.overflow.y = Some(Overflow::Scroll);
330 self
331 }
332
333 fn overflow_x_scroll(mut self) -> Self {
334 self.interactivity().base_style.overflow.x = Some(Overflow::Scroll);
335 self
336 }
337
338 fn overflow_y_scroll(mut self) -> Self {
339 self.interactivity().base_style.overflow.y = Some(Overflow::Scroll);
340 self
341 }
342
343 fn active(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
344 where
345 Self: Sized,
346 {
347 self.interactivity().active_style = f(StyleRefinement::default());
348 self
349 }
350
351 fn group_active(
352 mut self,
353 group_name: impl Into<SharedString>,
354 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
355 ) -> Self
356 where
357 Self: Sized,
358 {
359 self.interactivity().group_active_style = Some(GroupStyle {
360 group: group_name.into(),
361 style: f(StyleRefinement::default()),
362 });
363 self
364 }
365
366 fn on_click(
367 mut self,
368 listener: impl Fn(&mut V, &ClickEvent, &mut ViewContext<V>) + 'static,
369 ) -> Self
370 where
371 Self: Sized,
372 {
373 self.interactivity()
374 .click_listeners
375 .push(Box::new(move |view, event, cx| listener(view, event, cx)));
376 self
377 }
378
379 fn on_drag<W>(
380 mut self,
381 listener: impl Fn(&mut V, &mut ViewContext<V>) -> View<W> + 'static,
382 ) -> Self
383 where
384 Self: Sized,
385 W: 'static + Render,
386 {
387 debug_assert!(
388 self.interactivity().drag_listener.is_none(),
389 "calling on_drag more than once on the same element is not supported"
390 );
391 self.interactivity().drag_listener =
392 Some(Box::new(move |view_state, cursor_offset, cx| AnyDrag {
393 view: listener(view_state, cx).into(),
394 cursor_offset,
395 }));
396 self
397 }
398
399 fn on_hover(mut self, listener: impl 'static + Fn(&mut V, bool, &mut ViewContext<V>)) -> Self
400 where
401 Self: Sized,
402 {
403 debug_assert!(
404 self.interactivity().hover_listener.is_none(),
405 "calling on_hover more than once on the same element is not supported"
406 );
407 self.interactivity().hover_listener = Some(Box::new(listener));
408 self
409 }
410
411 fn tooltip(
412 mut self,
413 build_tooltip: impl Fn(&mut V, &mut ViewContext<V>) -> AnyView + 'static,
414 ) -> Self
415 where
416 Self: Sized,
417 {
418 debug_assert!(
419 self.interactivity().tooltip_builder.is_none(),
420 "calling tooltip more than once on the same element is not supported"
421 );
422 self.interactivity().tooltip_builder = Some(Rc::new(move |view_state, cx| {
423 build_tooltip(view_state, cx).into()
424 }));
425
426 self
427 }
428}
429
430pub trait FocusableComponent<V: 'static>: InteractiveComponent<V> {
431 fn focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
432 where
433 Self: Sized,
434 {
435 self.interactivity().focus_style = f(StyleRefinement::default());
436 self
437 }
438
439 fn focus_in(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
440 where
441 Self: Sized,
442 {
443 self.interactivity().focus_in_style = f(StyleRefinement::default());
444 self
445 }
446
447 fn in_focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
448 where
449 Self: Sized,
450 {
451 self.interactivity().in_focus_style = f(StyleRefinement::default());
452 self
453 }
454
455 fn on_focus(
456 mut self,
457 listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 'static,
458 ) -> Self
459 where
460 Self: Sized,
461 {
462 self.interactivity().focus_listeners.push(Box::new(
463 move |view, focus_handle, event, cx| {
464 if event.focused.as_ref() == Some(focus_handle) {
465 listener(view, event, cx)
466 }
467 },
468 ));
469 self
470 }
471
472 fn on_blur(
473 mut self,
474 listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 'static,
475 ) -> Self
476 where
477 Self: Sized,
478 {
479 self.interactivity().focus_listeners.push(Box::new(
480 move |view, focus_handle, event, cx| {
481 if event.blurred.as_ref() == Some(focus_handle) {
482 listener(view, event, cx)
483 }
484 },
485 ));
486 self
487 }
488
489 fn on_focus_in(
490 mut self,
491 listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 'static,
492 ) -> Self
493 where
494 Self: Sized,
495 {
496 self.interactivity().focus_listeners.push(Box::new(
497 move |view, focus_handle, event, cx| {
498 let descendant_blurred = event
499 .blurred
500 .as_ref()
501 .map_or(false, |blurred| focus_handle.contains(blurred, cx));
502 let descendant_focused = event
503 .focused
504 .as_ref()
505 .map_or(false, |focused| focus_handle.contains(focused, cx));
506
507 if !descendant_blurred && descendant_focused {
508 listener(view, event, cx)
509 }
510 },
511 ));
512 self
513 }
514
515 fn on_focus_out(
516 mut self,
517 listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 'static,
518 ) -> Self
519 where
520 Self: Sized,
521 {
522 self.interactivity().focus_listeners.push(Box::new(
523 move |view, focus_handle, event, cx| {
524 let descendant_blurred = event
525 .blurred
526 .as_ref()
527 .map_or(false, |blurred| focus_handle.contains(blurred, cx));
528 let descendant_focused = event
529 .focused
530 .as_ref()
531 .map_or(false, |focused| focus_handle.contains(focused, cx));
532 if descendant_blurred && !descendant_focused {
533 listener(view, event, cx)
534 }
535 },
536 ));
537 self
538 }
539}
540
541pub type FocusListeners<V> = SmallVec<[FocusListener<V>; 2]>;
542
543pub type FocusListener<V> =
544 Box<dyn Fn(&mut V, &FocusHandle, &FocusEvent, &mut ViewContext<V>) + 'static>;
545
546pub type MouseDownListener<V> = Box<
547 dyn Fn(&mut V, &MouseDownEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>) + 'static,
548>;
549pub type MouseUpListener<V> = Box<
550 dyn Fn(&mut V, &MouseUpEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>) + 'static,
551>;
552
553pub type MouseMoveListener<V> = Box<
554 dyn Fn(&mut V, &MouseMoveEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>) + 'static,
555>;
556
557pub type ScrollWheelListener<V> = Box<
558 dyn Fn(&mut V, &ScrollWheelEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
559 + 'static,
560>;
561
562pub type ClickListener<V> = Box<dyn Fn(&mut V, &ClickEvent, &mut ViewContext<V>) + 'static>;
563
564pub type DragListener<V> =
565 Box<dyn Fn(&mut V, Point<Pixels>, &mut ViewContext<V>) -> AnyDrag + 'static>;
566
567type DropListener<V> = dyn Fn(&mut V, AnyView, &mut ViewContext<V>) + 'static;
568
569pub type HoverListener<V> = Box<dyn Fn(&mut V, bool, &mut ViewContext<V>) + 'static>;
570
571pub type TooltipBuilder<V> = Rc<dyn Fn(&mut V, &mut ViewContext<V>) -> AnyView + 'static>;
572
573pub type KeyDownListener<V> =
574 Box<dyn Fn(&mut V, &KeyDownEvent, DispatchPhase, &mut ViewContext<V>) + 'static>;
575
576pub type KeyUpListener<V> =
577 Box<dyn Fn(&mut V, &KeyUpEvent, DispatchPhase, &mut ViewContext<V>) + 'static>;
578
579pub type ActionListener<V> =
580 Box<dyn Fn(&mut V, &dyn Any, DispatchPhase, &mut ViewContext<V>) + 'static>;
581
582pub fn div<V: 'static>() -> Div<V> {
583 Div {
584 interactivity: Interactivity::default(),
585 children: SmallVec::default(),
586 }
587}
588
589pub struct Div<V> {
590 interactivity: Interactivity<V>,
591 children: SmallVec<[AnyElement<V>; 2]>,
592}
593
594impl<V> Styled for Div<V> {
595 fn style(&mut self) -> &mut StyleRefinement {
596 &mut self.interactivity.base_style
597 }
598}
599
600impl<V: 'static> InteractiveComponent<V> for Div<V> {
601 fn interactivity(&mut self) -> &mut Interactivity<V> {
602 &mut self.interactivity
603 }
604}
605
606impl<V: 'static> ParentComponent<V> for Div<V> {
607 fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
608 &mut self.children
609 }
610}
611
612impl<V: 'static> Element<V> for Div<V> {
613 type ElementState = DivState;
614
615 fn element_id(&self) -> Option<ElementId> {
616 self.interactivity.element_id.clone()
617 }
618
619 fn initialize(
620 &mut self,
621 view_state: &mut V,
622 element_state: Option<Self::ElementState>,
623 cx: &mut ViewContext<V>,
624 ) -> Self::ElementState {
625 let interactive_state = self
626 .interactivity
627 .initialize(element_state.map(|s| s.interactive_state), cx);
628
629 for child in &mut self.children {
630 child.initialize(view_state, cx);
631 }
632
633 DivState {
634 interactive_state,
635 child_layout_ids: SmallVec::new(),
636 }
637 }
638
639 fn layout(
640 &mut self,
641 view_state: &mut V,
642 element_state: &mut Self::ElementState,
643 cx: &mut ViewContext<V>,
644 ) -> crate::LayoutId {
645 let mut interactivity = mem::take(&mut self.interactivity);
646 let layout_id =
647 interactivity.layout(&mut element_state.interactive_state, cx, |style, cx| {
648 cx.with_text_style(style.text_style().cloned(), |cx| {
649 element_state.child_layout_ids = self
650 .children
651 .iter_mut()
652 .map(|child| child.layout(view_state, cx))
653 .collect::<SmallVec<_>>();
654 cx.request_layout(&style, element_state.child_layout_ids.iter().copied())
655 })
656 });
657 self.interactivity = interactivity;
658 layout_id
659 }
660
661 fn paint(
662 &mut self,
663 bounds: Bounds<Pixels>,
664 view_state: &mut V,
665 element_state: &mut Self::ElementState,
666 cx: &mut ViewContext<V>,
667 ) {
668 let mut child_min = point(Pixels::MAX, Pixels::MAX);
669 let mut child_max = Point::default();
670 let content_size = if element_state.child_layout_ids.is_empty() {
671 bounds.size
672 } else {
673 for child_layout_id in &element_state.child_layout_ids {
674 let child_bounds = cx.layout_bounds(*child_layout_id);
675 child_min = child_min.min(&child_bounds.origin);
676 child_max = child_max.max(&child_bounds.lower_right());
677 }
678 (child_max - child_min).into()
679 };
680
681 let mut interactivity = mem::take(&mut self.interactivity);
682 interactivity.paint(
683 bounds,
684 content_size,
685 &mut element_state.interactive_state,
686 cx,
687 |style, scroll_offset, cx| {
688 if style.visibility == Visibility::Hidden {
689 return;
690 }
691
692 let z_index = style.z_index.unwrap_or(0);
693
694 cx.with_z_index(z_index, |cx| {
695 cx.with_z_index(0, |cx| {
696 style.paint(bounds, cx);
697 });
698 cx.with_z_index(1, |cx| {
699 cx.with_text_style(style.text_style().cloned(), |cx| {
700 cx.with_content_mask(style.overflow_mask(bounds), |cx| {
701 cx.with_element_offset(scroll_offset, |cx| {
702 for child in &mut self.children {
703 child.paint(view_state, cx);
704 }
705 })
706 })
707 })
708 })
709 })
710 },
711 );
712 self.interactivity = interactivity;
713 }
714}
715
716impl<V: 'static> Component<V> for Div<V> {
717 fn render(self) -> AnyElement<V> {
718 AnyElement::new(self)
719 }
720}
721
722pub struct DivState {
723 child_layout_ids: SmallVec<[LayoutId; 4]>,
724 interactive_state: InteractiveElementState,
725}
726
727impl DivState {
728 pub fn is_active(&self) -> bool {
729 self.interactive_state.pending_mouse_down.borrow().is_some()
730 }
731}
732
733pub struct Interactivity<V> {
734 pub element_id: Option<ElementId>,
735 pub key_context: KeyContext,
736 pub focusable: bool,
737 pub tracked_focus_handle: Option<FocusHandle>,
738 pub focus_listeners: FocusListeners<V>,
739 pub group: Option<SharedString>,
740 pub base_style: StyleRefinement,
741 pub focus_style: StyleRefinement,
742 pub focus_in_style: StyleRefinement,
743 pub in_focus_style: StyleRefinement,
744 pub hover_style: StyleRefinement,
745 pub group_hover_style: Option<GroupStyle>,
746 pub active_style: StyleRefinement,
747 pub group_active_style: Option<GroupStyle>,
748 pub drag_over_styles: SmallVec<[(TypeId, StyleRefinement); 2]>,
749 pub group_drag_over_styles: SmallVec<[(TypeId, GroupStyle); 2]>,
750 pub mouse_down_listeners: SmallVec<[MouseDownListener<V>; 2]>,
751 pub mouse_up_listeners: SmallVec<[MouseUpListener<V>; 2]>,
752 pub mouse_move_listeners: SmallVec<[MouseMoveListener<V>; 2]>,
753 pub scroll_wheel_listeners: SmallVec<[ScrollWheelListener<V>; 2]>,
754 pub key_down_listeners: SmallVec<[KeyDownListener<V>; 2]>,
755 pub key_up_listeners: SmallVec<[KeyUpListener<V>; 2]>,
756 pub action_listeners: SmallVec<[(TypeId, ActionListener<V>); 8]>,
757 pub drop_listeners: SmallVec<[(TypeId, Box<DropListener<V>>); 2]>,
758 pub click_listeners: SmallVec<[ClickListener<V>; 2]>,
759 pub drag_listener: Option<DragListener<V>>,
760 pub hover_listener: Option<HoverListener<V>>,
761 pub tooltip_builder: Option<TooltipBuilder<V>>,
762}
763
764impl<V> Interactivity<V>
765where
766 V: 'static,
767{
768 pub fn initialize(
769 &mut self,
770 element_state: Option<InteractiveElementState>,
771 cx: &mut ViewContext<V>,
772 ) -> InteractiveElementState {
773 let mut element_state = element_state.unwrap_or_default();
774
775 // Ensure we store a focus handle in our element state if we're focusable.
776 // If there's an explicit focus handle we're tracking, use that. Otherwise
777 // create a new handle and store it in the element state, which lives for as
778 // as frames contain an element with this id.
779 if self.focusable {
780 element_state.focus_handle.get_or_insert_with(|| {
781 self.tracked_focus_handle
782 .clone()
783 .unwrap_or_else(|| cx.focus_handle())
784 });
785 }
786
787 element_state
788 }
789
790 pub fn layout(
791 &mut self,
792 element_state: &mut InteractiveElementState,
793 cx: &mut ViewContext<V>,
794 f: impl FnOnce(Style, &mut ViewContext<V>) -> LayoutId,
795 ) -> LayoutId {
796 let style = self.compute_style(None, element_state, cx);
797 f(style, cx)
798 }
799
800 pub fn paint(
801 &mut self,
802 bounds: Bounds<Pixels>,
803 content_size: Size<Pixels>,
804 element_state: &mut InteractiveElementState,
805 cx: &mut ViewContext<V>,
806 f: impl FnOnce(Style, Point<Pixels>, &mut ViewContext<V>),
807 ) {
808 let style = self.compute_style(Some(bounds), element_state, cx);
809
810 if let Some(mouse_cursor) = style.mouse_cursor {
811 let hovered = bounds.contains_point(&cx.mouse_position());
812 if hovered {
813 cx.set_cursor_style(mouse_cursor);
814 }
815 }
816
817 for listener in self.mouse_down_listeners.drain(..) {
818 cx.on_mouse_event(move |state, event: &MouseDownEvent, phase, cx| {
819 listener(state, event, &bounds, phase, cx);
820 })
821 }
822
823 for listener in self.mouse_up_listeners.drain(..) {
824 cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| {
825 listener(state, event, &bounds, phase, cx);
826 })
827 }
828
829 for listener in self.mouse_move_listeners.drain(..) {
830 cx.on_mouse_event(move |state, event: &MouseMoveEvent, phase, cx| {
831 listener(state, event, &bounds, phase, cx);
832 })
833 }
834
835 for listener in self.scroll_wheel_listeners.drain(..) {
836 cx.on_mouse_event(move |state, event: &ScrollWheelEvent, phase, cx| {
837 listener(state, event, &bounds, phase, cx);
838 })
839 }
840
841 let hover_group_bounds = self
842 .group_hover_style
843 .as_ref()
844 .and_then(|group_hover| GroupBounds::get(&group_hover.group, cx));
845
846 if let Some(group_bounds) = hover_group_bounds {
847 let hovered = group_bounds.contains_point(&cx.mouse_position());
848 cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
849 if phase == DispatchPhase::Capture {
850 if group_bounds.contains_point(&event.position) != hovered {
851 cx.notify();
852 }
853 }
854 });
855 }
856
857 if self.hover_style.is_some()
858 || (cx.active_drag.is_some() && !self.drag_over_styles.is_empty())
859 {
860 let hovered = bounds.contains_point(&cx.mouse_position());
861 cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
862 if phase == DispatchPhase::Capture {
863 if bounds.contains_point(&event.position) != hovered {
864 cx.notify();
865 }
866 }
867 });
868 }
869
870 if cx.active_drag.is_some() {
871 let drop_listeners = mem::take(&mut self.drop_listeners);
872 cx.on_mouse_event(move |view, event: &MouseUpEvent, phase, cx| {
873 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
874 if let Some(drag_state_type) =
875 cx.active_drag.as_ref().map(|drag| drag.view.entity_type())
876 {
877 for (drop_state_type, listener) in &drop_listeners {
878 if *drop_state_type == drag_state_type {
879 let drag = cx
880 .active_drag
881 .take()
882 .expect("checked for type drag state type above");
883 listener(view, drag.view.clone(), cx);
884 cx.notify();
885 cx.stop_propagation();
886 }
887 }
888 }
889 }
890 });
891 }
892
893 let click_listeners = mem::take(&mut self.click_listeners);
894 let drag_listener = mem::take(&mut self.drag_listener);
895
896 if !click_listeners.is_empty() || drag_listener.is_some() {
897 let pending_mouse_down = element_state.pending_mouse_down.clone();
898 let mouse_down = pending_mouse_down.borrow().clone();
899 if let Some(mouse_down) = mouse_down {
900 if let Some(drag_listener) = drag_listener {
901 let active_state = element_state.clicked_state.clone();
902
903 cx.on_mouse_event(move |view_state, event: &MouseMoveEvent, phase, cx| {
904 if cx.active_drag.is_some() {
905 if phase == DispatchPhase::Capture {
906 cx.notify();
907 }
908 } else if phase == DispatchPhase::Bubble
909 && bounds.contains_point(&event.position)
910 && (event.position - mouse_down.position).magnitude() > DRAG_THRESHOLD
911 {
912 *active_state.borrow_mut() = ElementClickedState::default();
913 let cursor_offset = event.position - bounds.origin;
914 let drag = drag_listener(view_state, cursor_offset, cx);
915 cx.active_drag = Some(drag);
916 cx.notify();
917 cx.stop_propagation();
918 }
919 });
920 }
921
922 cx.on_mouse_event(move |view_state, event: &MouseUpEvent, phase, cx| {
923 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
924 let mouse_click = ClickEvent {
925 down: mouse_down.clone(),
926 up: event.clone(),
927 };
928 for listener in &click_listeners {
929 listener(view_state, &mouse_click, cx);
930 }
931 }
932 *pending_mouse_down.borrow_mut() = None;
933 cx.notify();
934 });
935 } else {
936 cx.on_mouse_event(move |_view_state, event: &MouseDownEvent, phase, cx| {
937 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
938 *pending_mouse_down.borrow_mut() = Some(event.clone());
939 cx.notify();
940 }
941 });
942 }
943 }
944
945 if let Some(hover_listener) = self.hover_listener.take() {
946 let was_hovered = element_state.hover_state.clone();
947 let has_mouse_down = element_state.pending_mouse_down.clone();
948
949 cx.on_mouse_event(move |view_state, event: &MouseMoveEvent, phase, cx| {
950 if phase != DispatchPhase::Bubble {
951 return;
952 }
953 let is_hovered =
954 bounds.contains_point(&event.position) && has_mouse_down.borrow().is_none();
955 let mut was_hovered = was_hovered.borrow_mut();
956
957 if is_hovered != was_hovered.clone() {
958 *was_hovered = is_hovered;
959 drop(was_hovered);
960
961 hover_listener(view_state, is_hovered, cx);
962 }
963 });
964 }
965
966 if let Some(tooltip_builder) = self.tooltip_builder.take() {
967 let active_tooltip = element_state.active_tooltip.clone();
968 let pending_mouse_down = element_state.pending_mouse_down.clone();
969
970 cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
971 if phase != DispatchPhase::Bubble {
972 return;
973 }
974
975 let is_hovered =
976 bounds.contains_point(&event.position) && pending_mouse_down.borrow().is_none();
977 if !is_hovered {
978 active_tooltip.borrow_mut().take();
979 return;
980 }
981
982 if active_tooltip.borrow().is_none() {
983 let task = cx.spawn({
984 let active_tooltip = active_tooltip.clone();
985 let tooltip_builder = tooltip_builder.clone();
986
987 move |view, mut cx| async move {
988 cx.background_executor().timer(TOOLTIP_DELAY).await;
989 view.update(&mut cx, move |view_state, cx| {
990 active_tooltip.borrow_mut().replace(ActiveTooltip {
991 waiting: None,
992 tooltip: Some(AnyTooltip {
993 view: tooltip_builder(view_state, cx),
994 cursor_offset: cx.mouse_position() + TOOLTIP_OFFSET,
995 }),
996 });
997 cx.notify();
998 })
999 .ok();
1000 }
1001 });
1002 active_tooltip.borrow_mut().replace(ActiveTooltip {
1003 waiting: Some(task),
1004 tooltip: None,
1005 });
1006 }
1007 });
1008
1009 if let Some(active_tooltip) = element_state.active_tooltip.borrow().as_ref() {
1010 if active_tooltip.tooltip.is_some() {
1011 cx.active_tooltip = active_tooltip.tooltip.clone()
1012 }
1013 }
1014 }
1015
1016 let active_state = element_state.clicked_state.clone();
1017 if !active_state.borrow().is_clicked() {
1018 cx.on_mouse_event(move |_, _: &MouseUpEvent, phase, cx| {
1019 if phase == DispatchPhase::Capture {
1020 *active_state.borrow_mut() = ElementClickedState::default();
1021 cx.notify();
1022 }
1023 });
1024 } else {
1025 let active_group_bounds = self
1026 .group_active_style
1027 .as_ref()
1028 .and_then(|group_active| GroupBounds::get(&group_active.group, cx));
1029 cx.on_mouse_event(move |_view, down: &MouseDownEvent, phase, cx| {
1030 if phase == DispatchPhase::Bubble {
1031 let group = active_group_bounds
1032 .map_or(false, |bounds| bounds.contains_point(&down.position));
1033 let element = bounds.contains_point(&down.position);
1034 if group || element {
1035 *active_state.borrow_mut() = ElementClickedState { group, element };
1036 cx.notify();
1037 }
1038 }
1039 });
1040 }
1041
1042 let overflow = style.overflow;
1043 if overflow.x == Overflow::Scroll || overflow.y == Overflow::Scroll {
1044 let scroll_offset = element_state
1045 .scroll_offset
1046 .get_or_insert_with(Rc::default)
1047 .clone();
1048 let line_height = cx.line_height();
1049 let scroll_max = (content_size - bounds.size).max(&Size::default());
1050
1051 cx.on_mouse_event(move |_, event: &ScrollWheelEvent, phase, cx| {
1052 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
1053 let mut scroll_offset = scroll_offset.borrow_mut();
1054 let old_scroll_offset = *scroll_offset;
1055 let delta = event.delta.pixel_delta(line_height);
1056
1057 if overflow.x == Overflow::Scroll {
1058 scroll_offset.x =
1059 (scroll_offset.x + delta.x).clamp(-scroll_max.width, px(0.));
1060 }
1061
1062 if overflow.y == Overflow::Scroll {
1063 scroll_offset.y =
1064 (scroll_offset.y + delta.y).clamp(-scroll_max.height, px(0.));
1065 }
1066
1067 if *scroll_offset != old_scroll_offset {
1068 cx.notify();
1069 cx.stop_propagation();
1070 }
1071 }
1072 });
1073 }
1074
1075 if let Some(group) = self.group.clone() {
1076 GroupBounds::push(group, bounds, cx);
1077 }
1078
1079 let scroll_offset = element_state
1080 .scroll_offset
1081 .as_ref()
1082 .map(|scroll_offset| *scroll_offset.borrow());
1083
1084 cx.with_key_dispatch(
1085 self.key_context.clone(),
1086 element_state.focus_handle.clone(),
1087 |_, cx| {
1088 for listener in self.key_down_listeners.drain(..) {
1089 cx.on_key_event(move |state, event: &KeyDownEvent, phase, cx| {
1090 listener(state, event, phase, cx);
1091 })
1092 }
1093
1094 for listener in self.key_up_listeners.drain(..) {
1095 cx.on_key_event(move |state, event: &KeyUpEvent, phase, cx| {
1096 listener(state, event, phase, cx);
1097 })
1098 }
1099
1100 for (action_type, listener) in self.action_listeners.drain(..) {
1101 cx.on_action(action_type, listener)
1102 }
1103
1104 if let Some(focus_handle) = element_state.focus_handle.as_ref() {
1105 for listener in self.focus_listeners.drain(..) {
1106 let focus_handle = focus_handle.clone();
1107 cx.on_focus_changed(move |view, event, cx| {
1108 listener(view, &focus_handle, event, cx)
1109 });
1110 }
1111 }
1112
1113 f(style, scroll_offset.unwrap_or_default(), cx)
1114 },
1115 );
1116
1117 if let Some(group) = self.group.as_ref() {
1118 GroupBounds::pop(group, cx);
1119 }
1120 }
1121
1122 pub fn compute_style(
1123 &self,
1124 bounds: Option<Bounds<Pixels>>,
1125 element_state: &mut InteractiveElementState,
1126 cx: &mut ViewContext<V>,
1127 ) -> Style {
1128 let mut style = Style::default();
1129 style.refine(&self.base_style);
1130
1131 if let Some(focus_handle) = self.tracked_focus_handle.as_ref() {
1132 if focus_handle.contains_focused(cx) {
1133 style.refine(&self.focus_in_style);
1134 }
1135
1136 if focus_handle.within_focused(cx) {
1137 style.refine(&self.in_focus_style);
1138 }
1139
1140 if focus_handle.is_focused(cx) {
1141 style.refine(&self.focus_style);
1142 }
1143 }
1144
1145 if let Some(bounds) = bounds {
1146 let mouse_position = cx.mouse_position();
1147 if let Some(group_hover) = self.group_hover_style.as_ref() {
1148 if let Some(group_bounds) = GroupBounds::get(&group_hover.group, cx) {
1149 if group_bounds.contains_point(&mouse_position) {
1150 style.refine(&group_hover.style);
1151 }
1152 }
1153 }
1154 if bounds.contains_point(&mouse_position) {
1155 style.refine(&self.hover_style);
1156 }
1157
1158 if let Some(drag) = cx.active_drag.take() {
1159 for (state_type, group_drag_style) in &self.group_drag_over_styles {
1160 if let Some(group_bounds) = GroupBounds::get(&group_drag_style.group, cx) {
1161 if *state_type == drag.view.entity_type()
1162 && group_bounds.contains_point(&mouse_position)
1163 {
1164 style.refine(&group_drag_style.style);
1165 }
1166 }
1167 }
1168
1169 for (state_type, drag_over_style) in &self.drag_over_styles {
1170 if *state_type == drag.view.entity_type()
1171 && bounds.contains_point(&mouse_position)
1172 {
1173 style.refine(drag_over_style);
1174 }
1175 }
1176
1177 cx.active_drag = Some(drag);
1178 }
1179 }
1180
1181 let clicked_state = element_state.clicked_state.borrow();
1182 if clicked_state.group {
1183 if let Some(group) = self.group_active_style.as_ref() {
1184 style.refine(&group.style)
1185 }
1186 }
1187
1188 if clicked_state.element {
1189 style.refine(&self.active_style)
1190 }
1191
1192 style
1193 }
1194}
1195
1196impl<V: 'static> Default for Interactivity<V> {
1197 fn default() -> Self {
1198 Self {
1199 element_id: None,
1200 key_context: KeyContext::default(),
1201 focusable: false,
1202 tracked_focus_handle: None,
1203 focus_listeners: SmallVec::default(),
1204 // scroll_offset: Point::default(),
1205 group: None,
1206 base_style: StyleRefinement::default(),
1207 focus_style: StyleRefinement::default(),
1208 focus_in_style: StyleRefinement::default(),
1209 in_focus_style: StyleRefinement::default(),
1210 hover_style: StyleRefinement::default(),
1211 group_hover_style: None,
1212 active_style: StyleRefinement::default(),
1213 group_active_style: None,
1214 drag_over_styles: SmallVec::new(),
1215 group_drag_over_styles: SmallVec::new(),
1216 mouse_down_listeners: SmallVec::new(),
1217 mouse_up_listeners: SmallVec::new(),
1218 mouse_move_listeners: SmallVec::new(),
1219 scroll_wheel_listeners: SmallVec::new(),
1220 key_down_listeners: SmallVec::new(),
1221 key_up_listeners: SmallVec::new(),
1222 action_listeners: SmallVec::new(),
1223 drop_listeners: SmallVec::new(),
1224 click_listeners: SmallVec::new(),
1225 drag_listener: None,
1226 hover_listener: None,
1227 tooltip_builder: None,
1228 }
1229 }
1230}
1231
1232#[derive(Default)]
1233pub struct InteractiveElementState {
1234 pub focus_handle: Option<FocusHandle>,
1235 pub clicked_state: Rc<RefCell<ElementClickedState>>,
1236 pub hover_state: Rc<RefCell<bool>>,
1237 pub pending_mouse_down: Rc<RefCell<Option<MouseDownEvent>>>,
1238 pub scroll_offset: Option<Rc<RefCell<Point<Pixels>>>>,
1239 pub active_tooltip: Rc<RefCell<Option<ActiveTooltip>>>,
1240}
1241
1242pub struct ActiveTooltip {
1243 #[allow(unused)] // used to drop the task
1244 waiting: Option<Task<()>>,
1245 tooltip: Option<AnyTooltip>,
1246}
1247
1248/// Whether or not the element or a group that contains it is clicked by the mouse.
1249#[derive(Copy, Clone, Default, Eq, PartialEq)]
1250pub struct ElementClickedState {
1251 pub group: bool,
1252 pub element: bool,
1253}
1254
1255impl ElementClickedState {
1256 fn is_clicked(&self) -> bool {
1257 self.group || self.element
1258 }
1259}
1260
1261#[derive(Default)]
1262pub struct GroupBounds(HashMap<SharedString, SmallVec<[Bounds<Pixels>; 1]>>);
1263
1264impl GroupBounds {
1265 pub fn get(name: &SharedString, cx: &mut AppContext) -> Option<Bounds<Pixels>> {
1266 cx.default_global::<Self>()
1267 .0
1268 .get(name)
1269 .and_then(|bounds_stack| bounds_stack.last())
1270 .cloned()
1271 }
1272
1273 pub fn push(name: SharedString, bounds: Bounds<Pixels>, cx: &mut AppContext) {
1274 cx.default_global::<Self>()
1275 .0
1276 .entry(name)
1277 .or_default()
1278 .push(bounds);
1279 }
1280
1281 pub fn pop(name: &SharedString, cx: &mut AppContext) {
1282 cx.default_global::<Self>().0.get_mut(name).unwrap().pop();
1283 }
1284}
1285
1286pub struct Focusable<V, E> {
1287 element: E,
1288 view_type: PhantomData<V>,
1289}
1290
1291impl<V: 'static, E: InteractiveComponent<V>> FocusableComponent<V> for Focusable<V, E> {}
1292
1293impl<V, E> InteractiveComponent<V> for Focusable<V, E>
1294where
1295 V: 'static,
1296 E: InteractiveComponent<V>,
1297{
1298 fn interactivity(&mut self) -> &mut Interactivity<V> {
1299 self.element.interactivity()
1300 }
1301}
1302
1303impl<V: 'static, E: StatefulInteractiveComponent<V, E>> StatefulInteractiveComponent<V, E>
1304 for Focusable<V, E>
1305{
1306}
1307
1308impl<V, E> Styled for Focusable<V, E>
1309where
1310 V: 'static,
1311 E: Styled,
1312{
1313 fn style(&mut self) -> &mut StyleRefinement {
1314 self.element.style()
1315 }
1316}
1317
1318impl<V, E> Element<V> for Focusable<V, E>
1319where
1320 V: 'static,
1321 E: Element<V>,
1322{
1323 type ElementState = E::ElementState;
1324
1325 fn element_id(&self) -> Option<ElementId> {
1326 self.element.element_id()
1327 }
1328
1329 fn initialize(
1330 &mut self,
1331 view_state: &mut V,
1332 element_state: Option<Self::ElementState>,
1333 cx: &mut ViewContext<V>,
1334 ) -> Self::ElementState {
1335 self.element.initialize(view_state, element_state, cx)
1336 }
1337
1338 fn layout(
1339 &mut self,
1340 view_state: &mut V,
1341 element_state: &mut Self::ElementState,
1342 cx: &mut ViewContext<V>,
1343 ) -> LayoutId {
1344 self.element.layout(view_state, element_state, cx)
1345 }
1346
1347 fn paint(
1348 &mut self,
1349 bounds: Bounds<Pixels>,
1350 view_state: &mut V,
1351 element_state: &mut Self::ElementState,
1352 cx: &mut ViewContext<V>,
1353 ) {
1354 self.element.paint(bounds, view_state, element_state, cx);
1355 }
1356}
1357
1358impl<V, E> Component<V> for Focusable<V, E>
1359where
1360 V: 'static,
1361 E: 'static + Element<V>,
1362{
1363 fn render(self) -> AnyElement<V> {
1364 AnyElement::new(self)
1365 }
1366}
1367
1368impl<V, E> ParentComponent<V> for Focusable<V, E>
1369where
1370 V: 'static,
1371 E: ParentComponent<V>,
1372{
1373 fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
1374 self.element.children_mut()
1375 }
1376}
1377
1378pub struct Stateful<V, E> {
1379 element: E,
1380 view_type: PhantomData<V>,
1381}
1382
1383impl<V, E> Styled for Stateful<V, E>
1384where
1385 V: 'static,
1386 E: Styled,
1387{
1388 fn style(&mut self) -> &mut StyleRefinement {
1389 self.element.style()
1390 }
1391}
1392
1393impl<V, E> StatefulInteractiveComponent<V, E> for Stateful<V, E>
1394where
1395 V: 'static,
1396 E: Element<V>,
1397 Self: InteractiveComponent<V>,
1398{
1399}
1400
1401impl<V, E> InteractiveComponent<V> for Stateful<V, E>
1402where
1403 V: 'static,
1404 E: InteractiveComponent<V>,
1405{
1406 fn interactivity(&mut self) -> &mut Interactivity<V> {
1407 self.element.interactivity()
1408 }
1409}
1410
1411impl<V: 'static, E: FocusableComponent<V>> FocusableComponent<V> for Stateful<V, E> {}
1412
1413impl<V, E> Element<V> for Stateful<V, E>
1414where
1415 V: 'static,
1416 E: Element<V>,
1417{
1418 type ElementState = E::ElementState;
1419
1420 fn element_id(&self) -> Option<ElementId> {
1421 self.element.element_id()
1422 }
1423
1424 fn initialize(
1425 &mut self,
1426 view_state: &mut V,
1427 element_state: Option<Self::ElementState>,
1428 cx: &mut ViewContext<V>,
1429 ) -> Self::ElementState {
1430 self.element.initialize(view_state, element_state, cx)
1431 }
1432
1433 fn layout(
1434 &mut self,
1435 view_state: &mut V,
1436 element_state: &mut Self::ElementState,
1437 cx: &mut ViewContext<V>,
1438 ) -> LayoutId {
1439 self.element.layout(view_state, element_state, cx)
1440 }
1441
1442 fn paint(
1443 &mut self,
1444 bounds: Bounds<Pixels>,
1445 view_state: &mut V,
1446 element_state: &mut Self::ElementState,
1447 cx: &mut ViewContext<V>,
1448 ) {
1449 self.element.paint(bounds, view_state, element_state, cx)
1450 }
1451}
1452
1453impl<V, E> Component<V> for Stateful<V, E>
1454where
1455 V: 'static,
1456 E: 'static + Element<V>,
1457{
1458 fn render(self) -> AnyElement<V> {
1459 AnyElement::new(self)
1460 }
1461}
1462
1463impl<V, E> ParentComponent<V> for Stateful<V, E>
1464where
1465 V: 'static,
1466 E: ParentComponent<V>,
1467{
1468 fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
1469 self.element.children_mut()
1470 }
1471}