1use crate::{
2 AppContext, BorrowWindow, Bounds, DispatchPhase, ElementId, FocusHandle, FocusListeners,
3 KeyDownEvent, KeyListener, KeyMatch, LayoutId, MouseClickEvent, MouseClickListener,
4 MouseDownEvent, MouseDownListener, MouseMoveEvent, MouseMoveListener, MouseUpEvent,
5 MouseUpListener, Pixels, Point, ScrollWheelEvent, ScrollWheelListener, SharedString, Style,
6 StyleRefinement, ViewContext, WindowContext,
7};
8use collections::HashMap;
9use derive_more::{Deref, DerefMut};
10use parking_lot::Mutex;
11use refineable::Refineable;
12pub(crate) use smallvec::SmallVec;
13use std::{any::TypeId, mem, sync::Arc};
14
15pub trait Element: 'static + Send + Sync + IntoAnyElement<Self::ViewState> {
16 type ViewState: 'static + Send + Sync;
17 type ElementState: 'static + Send + Sync;
18
19 fn id(&self) -> Option<ElementId>;
20
21 fn initialize(
22 &mut self,
23 view_state: &mut Self::ViewState,
24 element_state: Option<Self::ElementState>,
25 cx: &mut ViewContext<Self::ViewState>,
26 ) -> Self::ElementState;
27
28 fn layout(
29 &mut self,
30 view_state: &mut Self::ViewState,
31 element_state: &mut Self::ElementState,
32 cx: &mut ViewContext<Self::ViewState>,
33 ) -> LayoutId;
34
35 fn paint(
36 &mut self,
37 bounds: Bounds<Pixels>,
38 view_state: &mut Self::ViewState,
39 element_state: &mut Self::ElementState,
40 cx: &mut ViewContext<Self::ViewState>,
41 );
42}
43
44#[derive(Deref, DerefMut, Default, Clone, Debug, Eq, PartialEq, Hash)]
45pub struct GlobalElementId(SmallVec<[ElementId; 32]>);
46
47pub trait ElementInteractivity<V: 'static + Send + Sync>: 'static + Send + Sync {
48 fn as_stateless(&self) -> &StatelessInteractivity<V>;
49 fn as_stateless_mut(&mut self) -> &mut StatelessInteractivity<V>;
50 fn as_stateful(&self) -> Option<&StatefulInteractivity<V>>;
51 fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteractivity<V>>;
52
53 fn initialize<R>(
54 &mut self,
55 cx: &mut ViewContext<V>,
56 f: impl FnOnce(&mut ViewContext<V>) -> R,
57 ) -> R {
58 if let Some(stateful) = self.as_stateful_mut() {
59 cx.with_element_id(stateful.id.clone(), |global_id, cx| {
60 stateful.key_listeners.push((
61 TypeId::of::<KeyDownEvent>(),
62 Arc::new(move |_, key_down, context, phase, cx| {
63 if phase == DispatchPhase::Bubble {
64 let key_down = key_down.downcast_ref::<KeyDownEvent>().unwrap();
65 if let KeyMatch::Some(action) =
66 cx.match_keystroke(&global_id, &key_down.keystroke, context)
67 {
68 return Some(action);
69 }
70 }
71
72 None
73 }),
74 ));
75 let result = stateful.stateless.initialize(cx, f);
76 stateful.key_listeners.pop();
77 result
78 })
79 } else {
80 cx.with_key_listeners(&self.as_stateless().key_listeners, f)
81 }
82 }
83
84 fn refine_style(
85 &self,
86 style: &mut Style,
87 bounds: Bounds<Pixels>,
88 element_state: &InteractiveElementState,
89 cx: &mut ViewContext<V>,
90 ) {
91 let mouse_position = cx.mouse_position();
92 let stateless = self.as_stateless();
93 if let Some(group_hover) = stateless.group_hover_style.as_ref() {
94 if let Some(group_bounds) = GroupBounds::get(&group_hover.group, cx) {
95 if group_bounds.contains_point(&mouse_position) {
96 style.refine(&group_hover.style);
97 }
98 }
99 }
100 if bounds.contains_point(&mouse_position) {
101 style.refine(&stateless.hover_style);
102 }
103
104 if let Some(stateful) = self.as_stateful() {
105 let active_state = element_state.active_state.lock();
106 if active_state.group {
107 if let Some(group_style) = stateful.group_active_style.as_ref() {
108 style.refine(&group_style.style);
109 }
110 }
111 if active_state.element {
112 style.refine(&stateful.active_style);
113 }
114 }
115 }
116
117 fn paint(
118 &mut self,
119 bounds: Bounds<Pixels>,
120 element_state: &InteractiveElementState,
121 cx: &mut ViewContext<V>,
122 ) {
123 let stateless = self.as_stateless();
124 for listener in stateless.mouse_down_listeners.iter().cloned() {
125 cx.on_mouse_event(move |state, event: &MouseDownEvent, phase, cx| {
126 listener(state, event, &bounds, phase, cx);
127 })
128 }
129
130 for listener in stateless.mouse_up_listeners.iter().cloned() {
131 cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| {
132 listener(state, event, &bounds, phase, cx);
133 })
134 }
135
136 for listener in stateless.mouse_move_listeners.iter().cloned() {
137 cx.on_mouse_event(move |state, event: &MouseMoveEvent, phase, cx| {
138 listener(state, event, &bounds, phase, cx);
139 })
140 }
141
142 for listener in stateless.scroll_wheel_listeners.iter().cloned() {
143 cx.on_mouse_event(move |state, event: &ScrollWheelEvent, phase, cx| {
144 listener(state, event, &bounds, phase, cx);
145 })
146 }
147
148 let hover_group_bounds = stateless
149 .group_hover_style
150 .as_ref()
151 .and_then(|group_hover| GroupBounds::get(&group_hover.group, cx));
152
153 if let Some(group_bounds) = hover_group_bounds {
154 paint_hover_listener(group_bounds, cx);
155 }
156
157 if stateless.hover_style.is_some() {
158 paint_hover_listener(bounds, cx);
159 }
160
161 if let Some(stateful) = self.as_stateful() {
162 let click_listeners = stateful.mouse_click_listeners.clone();
163
164 let pending_click = element_state.pending_click.clone();
165 let mouse_down = pending_click.lock().clone();
166 if let Some(mouse_down) = mouse_down {
167 cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| {
168 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
169 let mouse_click = MouseClickEvent {
170 down: mouse_down.clone(),
171 up: event.clone(),
172 };
173 for listener in &click_listeners {
174 listener(state, &mouse_click, cx);
175 }
176 }
177
178 *pending_click.lock() = None;
179 });
180 } else {
181 cx.on_mouse_event(move |_state, event: &MouseDownEvent, phase, _cx| {
182 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
183 *pending_click.lock() = Some(event.clone());
184 }
185 });
186 }
187
188 let active_state = element_state.active_state.clone();
189 if active_state.lock().is_none() {
190 let active_group_bounds = stateful
191 .group_active_style
192 .as_ref()
193 .and_then(|group_active| GroupBounds::get(&group_active.group, cx));
194 cx.on_mouse_event(move |_view, down: &MouseDownEvent, phase, cx| {
195 if phase == DispatchPhase::Bubble {
196 let group = active_group_bounds
197 .map_or(false, |bounds| bounds.contains_point(&down.position));
198 let element = bounds.contains_point(&down.position);
199 if group || element {
200 *active_state.lock() = ActiveState { group, element };
201 cx.notify();
202 }
203 }
204 });
205 } else {
206 cx.on_mouse_event(move |_, _: &MouseUpEvent, phase, cx| {
207 if phase == DispatchPhase::Capture {
208 *active_state.lock() = ActiveState::default();
209 cx.notify();
210 }
211 });
212 }
213 }
214 }
215}
216
217fn paint_hover_listener<V>(bounds: Bounds<Pixels>, cx: &mut ViewContext<V>)
218where
219 V: 'static + Send + Sync,
220{
221 let hovered = bounds.contains_point(&cx.mouse_position());
222 cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
223 if phase == DispatchPhase::Capture {
224 if bounds.contains_point(&event.position) != hovered {
225 cx.notify();
226 }
227 }
228 });
229}
230
231#[derive(Deref, DerefMut)]
232pub struct StatefulInteractivity<V: 'static + Send + Sync> {
233 pub id: ElementId,
234 #[deref]
235 #[deref_mut]
236 stateless: StatelessInteractivity<V>,
237 pub mouse_click_listeners: SmallVec<[MouseClickListener<V>; 2]>,
238 pub active_style: StyleRefinement,
239 pub group_active_style: Option<GroupStyle>,
240}
241
242impl<V> ElementInteractivity<V> for StatefulInteractivity<V>
243where
244 V: 'static + Send + Sync,
245{
246 fn as_stateful(&self) -> Option<&StatefulInteractivity<V>> {
247 Some(self)
248 }
249
250 fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteractivity<V>> {
251 Some(self)
252 }
253
254 fn as_stateless(&self) -> &StatelessInteractivity<V> {
255 &self.stateless
256 }
257
258 fn as_stateless_mut(&mut self) -> &mut StatelessInteractivity<V> {
259 &mut self.stateless
260 }
261}
262
263impl<V> From<ElementId> for StatefulInteractivity<V>
264where
265 V: 'static + Send + Sync,
266{
267 fn from(id: ElementId) -> Self {
268 Self {
269 id,
270 stateless: StatelessInteractivity::default(),
271 mouse_click_listeners: SmallVec::new(),
272 active_style: StyleRefinement::default(),
273 group_active_style: None,
274 }
275 }
276}
277
278pub struct StatelessInteractivity<V> {
279 pub mouse_down_listeners: SmallVec<[MouseDownListener<V>; 2]>,
280 pub mouse_up_listeners: SmallVec<[MouseUpListener<V>; 2]>,
281 pub mouse_move_listeners: SmallVec<[MouseMoveListener<V>; 2]>,
282 pub scroll_wheel_listeners: SmallVec<[ScrollWheelListener<V>; 2]>,
283 pub key_listeners: SmallVec<[(TypeId, KeyListener<V>); 32]>,
284 pub hover_style: StyleRefinement,
285 pub group_hover_style: Option<GroupStyle>,
286}
287
288pub struct GroupStyle {
289 pub group: SharedString,
290 pub style: StyleRefinement,
291}
292
293#[derive(Default)]
294pub struct GroupBounds(HashMap<SharedString, SmallVec<[Bounds<Pixels>; 1]>>);
295
296impl GroupBounds {
297 pub fn get(name: &SharedString, cx: &mut AppContext) -> Option<Bounds<Pixels>> {
298 cx.default_global::<Self>()
299 .0
300 .get(name)
301 .and_then(|bounds_stack| bounds_stack.last())
302 .cloned()
303 }
304
305 pub fn push(name: SharedString, bounds: Bounds<Pixels>, cx: &mut AppContext) {
306 cx.default_global::<Self>()
307 .0
308 .entry(name)
309 .or_default()
310 .push(bounds);
311 }
312
313 pub fn pop(name: &SharedString, cx: &mut AppContext) {
314 cx.default_global::<GroupBounds>()
315 .0
316 .get_mut(name)
317 .unwrap()
318 .pop();
319 }
320}
321
322#[derive(Copy, Clone, Default, Eq, PartialEq)]
323struct ActiveState {
324 pub group: bool,
325 pub element: bool,
326}
327
328impl ActiveState {
329 pub fn is_none(&self) -> bool {
330 !self.group && !self.element
331 }
332}
333
334#[derive(Default)]
335pub struct InteractiveElementState {
336 active_state: Arc<Mutex<ActiveState>>,
337 pending_click: Arc<Mutex<Option<MouseDownEvent>>>,
338}
339
340impl<V> Default for StatelessInteractivity<V> {
341 fn default() -> Self {
342 Self {
343 mouse_down_listeners: SmallVec::new(),
344 mouse_up_listeners: SmallVec::new(),
345 mouse_move_listeners: SmallVec::new(),
346 scroll_wheel_listeners: SmallVec::new(),
347 key_listeners: SmallVec::new(),
348 hover_style: StyleRefinement::default(),
349 group_hover_style: None,
350 }
351 }
352}
353
354impl<V> ElementInteractivity<V> for StatelessInteractivity<V>
355where
356 V: 'static + Send + Sync,
357{
358 fn as_stateful(&self) -> Option<&StatefulInteractivity<V>> {
359 None
360 }
361
362 fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteractivity<V>> {
363 None
364 }
365
366 fn as_stateless(&self) -> &StatelessInteractivity<V> {
367 self
368 }
369
370 fn as_stateless_mut(&mut self) -> &mut StatelessInteractivity<V> {
371 self
372 }
373}
374
375pub trait ElementFocusability<V: 'static + Send + Sync>: 'static + Send + Sync {
376 fn as_focusable(&self) -> Option<&Focusable<V>>;
377
378 fn initialize<R>(
379 &self,
380 cx: &mut ViewContext<V>,
381 f: impl FnOnce(&mut ViewContext<V>) -> R,
382 ) -> R {
383 if let Some(focusable) = self.as_focusable() {
384 for listener in focusable.focus_listeners.iter().cloned() {
385 cx.on_focus_changed(move |view, event, cx| listener(view, event, cx));
386 }
387 cx.with_focus(focusable.focus_handle.clone(), |cx| f(cx))
388 } else {
389 f(cx)
390 }
391 }
392
393 fn refine_style(&self, style: &mut Style, cx: &WindowContext) {
394 if let Some(focusable) = self.as_focusable() {
395 if focusable.focus_handle.contains_focused(cx) {
396 style.refine(&focusable.focus_in_style);
397 }
398
399 if focusable.focus_handle.within_focused(cx) {
400 style.refine(&focusable.in_focus_style);
401 }
402
403 if focusable.focus_handle.is_focused(cx) {
404 style.refine(&focusable.focus_style);
405 }
406 }
407 }
408
409 fn paint(&self, bounds: Bounds<Pixels>, cx: &mut WindowContext) {
410 if let Some(focusable) = self.as_focusable() {
411 let focus_handle = focusable.focus_handle.clone();
412 cx.on_mouse_event(move |event: &MouseDownEvent, phase, cx| {
413 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
414 if !cx.default_prevented() {
415 cx.focus(&focus_handle);
416 cx.prevent_default();
417 }
418 }
419 })
420 }
421 }
422}
423
424pub struct Focusable<V: 'static + Send + Sync> {
425 pub focus_handle: FocusHandle,
426 pub focus_listeners: FocusListeners<V>,
427 pub focus_style: StyleRefinement,
428 pub focus_in_style: StyleRefinement,
429 pub in_focus_style: StyleRefinement,
430}
431
432impl<V> ElementFocusability<V> for Focusable<V>
433where
434 V: 'static + Send + Sync,
435{
436 fn as_focusable(&self) -> Option<&Focusable<V>> {
437 Some(self)
438 }
439}
440
441impl<V> From<FocusHandle> for Focusable<V>
442where
443 V: 'static + Send + Sync,
444{
445 fn from(value: FocusHandle) -> Self {
446 Self {
447 focus_handle: value,
448 focus_listeners: FocusListeners::default(),
449 focus_style: StyleRefinement::default(),
450 focus_in_style: StyleRefinement::default(),
451 in_focus_style: StyleRefinement::default(),
452 }
453 }
454}
455
456pub struct NonFocusable;
457
458impl<V> ElementFocusability<V> for NonFocusable
459where
460 V: 'static + Send + Sync,
461{
462 fn as_focusable(&self) -> Option<&Focusable<V>> {
463 None
464 }
465}
466
467pub trait ParentElement: Element {
468 fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<Self::ViewState>; 2]>;
469
470 fn child(mut self, child: impl IntoAnyElement<Self::ViewState>) -> Self
471 where
472 Self: Sized,
473 {
474 self.children_mut().push(child.into_any());
475 self
476 }
477
478 fn children(
479 mut self,
480 iter: impl IntoIterator<Item = impl IntoAnyElement<Self::ViewState>>,
481 ) -> Self
482 where
483 Self: Sized,
484 {
485 self.children_mut()
486 .extend(iter.into_iter().map(|item| item.into_any()));
487 self
488 }
489}
490
491trait ElementObject<V>: 'static + Send + Sync {
492 fn initialize(&mut self, view_state: &mut V, cx: &mut ViewContext<V>);
493 fn layout(&mut self, view_state: &mut V, cx: &mut ViewContext<V>) -> LayoutId;
494 fn paint(&mut self, view_state: &mut V, offset: Option<Point<Pixels>>, cx: &mut ViewContext<V>);
495}
496
497struct RenderedElement<E: Element> {
498 element: E,
499 phase: ElementRenderPhase<E::ElementState>,
500}
501
502#[derive(Default)]
503enum ElementRenderPhase<V> {
504 #[default]
505 Start,
506 Initialized {
507 frame_state: Option<V>,
508 },
509 LayoutRequested {
510 layout_id: LayoutId,
511 frame_state: Option<V>,
512 },
513 Painted,
514}
515
516/// Internal struct that wraps an element to store Layout and ElementState after the element is rendered.
517/// It's allocated as a trait object to erase the element type and wrapped in AnyElement<E::State> for
518/// improved usability.
519impl<E: Element> RenderedElement<E> {
520 fn new(element: E) -> Self {
521 RenderedElement {
522 element,
523 phase: ElementRenderPhase::Start,
524 }
525 }
526}
527
528impl<E> ElementObject<E::ViewState> for RenderedElement<E>
529where
530 E: Element,
531{
532 fn initialize(&mut self, view_state: &mut E::ViewState, cx: &mut ViewContext<E::ViewState>) {
533 let frame_state = if let Some(id) = self.element.id() {
534 cx.with_element_state(id, |element_state, cx| {
535 let element_state = self.element.initialize(view_state, element_state, cx);
536 ((), element_state)
537 });
538 None
539 } else {
540 let frame_state = self.element.initialize(view_state, None, cx);
541 Some(frame_state)
542 };
543
544 self.phase = ElementRenderPhase::Initialized { frame_state };
545 }
546
547 fn layout(&mut self, state: &mut E::ViewState, cx: &mut ViewContext<E::ViewState>) -> LayoutId {
548 let layout_id;
549 let mut frame_state;
550 match mem::take(&mut self.phase) {
551 ElementRenderPhase::Initialized {
552 frame_state: initial_frame_state,
553 } => {
554 frame_state = initial_frame_state;
555 if let Some(id) = self.element.id() {
556 layout_id = cx.with_element_state(id, |element_state, cx| {
557 let mut element_state = element_state.unwrap();
558 let layout_id = self.element.layout(state, &mut element_state, cx);
559 (layout_id, element_state)
560 });
561 } else {
562 layout_id = self
563 .element
564 .layout(state, frame_state.as_mut().unwrap(), cx);
565 }
566 }
567 _ => panic!("must call initialize before layout"),
568 };
569
570 self.phase = ElementRenderPhase::LayoutRequested {
571 layout_id,
572 frame_state,
573 };
574 layout_id
575 }
576
577 fn paint(
578 &mut self,
579 view_state: &mut E::ViewState,
580 offset: Option<Point<Pixels>>,
581 cx: &mut ViewContext<E::ViewState>,
582 ) {
583 self.phase = match mem::take(&mut self.phase) {
584 ElementRenderPhase::LayoutRequested {
585 layout_id,
586 mut frame_state,
587 } => {
588 let mut bounds = cx.layout_bounds(layout_id);
589 offset.map(|offset| bounds.origin += offset);
590 if let Some(id) = self.element.id() {
591 cx.with_element_state(id, |element_state, cx| {
592 let mut element_state = element_state.unwrap();
593 self.element
594 .paint(bounds, view_state, &mut element_state, cx);
595 ((), element_state)
596 });
597 } else {
598 self.element
599 .paint(bounds, view_state, frame_state.as_mut().unwrap(), cx);
600 }
601 ElementRenderPhase::Painted
602 }
603
604 _ => panic!("must call layout before paint"),
605 };
606 }
607}
608
609pub struct AnyElement<V>(Box<dyn ElementObject<V>>);
610
611impl<V: 'static + Send + Sync> AnyElement<V> {
612 pub fn new<E: Element<ViewState = V>>(element: E) -> Self {
613 AnyElement(Box::new(RenderedElement::new(element)))
614 }
615
616 pub fn initialize(&mut self, view_state: &mut V, cx: &mut ViewContext<V>) {
617 self.0.initialize(view_state, cx);
618 }
619
620 pub fn layout(&mut self, view_state: &mut V, cx: &mut ViewContext<V>) -> LayoutId {
621 self.0.layout(view_state, cx)
622 }
623
624 pub fn paint(
625 &mut self,
626 view_state: &mut V,
627 offset: Option<Point<Pixels>>,
628 cx: &mut ViewContext<V>,
629 ) {
630 self.0.paint(view_state, offset, cx)
631 }
632}
633
634pub trait IntoAnyElement<V> {
635 fn into_any(self) -> AnyElement<V>;
636}
637
638impl<V> IntoAnyElement<V> for AnyElement<V> {
639 fn into_any(self) -> AnyElement<V> {
640 self
641 }
642}