1//! Div is the central, reusable element that most GPUI trees will be built from.
2//! It functions as a container for other elements, and provides a number of
3//! useful features for laying out and styling its children as well as binding
4//! mouse events and action handlers. It is meant to be similar to the HTML `<div>`
5//! element, but for GPUI.
6//!
7//! # Build your own div
8//!
9//! GPUI does not directly provide APIs for stateful, multi step events like `click`
10//! and `drag`. We want GPUI users to be able to build their own abstractions for
11//! their own needs. However, as a UI framework, we're also obliged to provide some
12//! building blocks to make the process of building your own elements easier.
13//! For this we have the [`Interactivity`] and the [`StyleRefinement`] structs, as well
14//! as several associated traits. Together, these provide the full suite of Dom-like events
15//! and Tailwind-like styling that you can use to build your own custom elements. Div is
16//! constructed by combining these two systems into an all-in-one element.
17
18use crate::{
19 point, px, size, Action, AnyDrag, AnyElement, AnyTooltip, AnyView, AppContext, Bounds,
20 ClickEvent, DispatchPhase, Element, ElementContext, ElementId, FocusHandle, IntoElement,
21 IsZero, KeyContext, KeyDownEvent, KeyUpEvent, LayoutId, MouseButton, MouseDownEvent,
22 MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, Point, Render, ScrollWheelEvent,
23 SharedString, Size, StackingOrder, Style, StyleRefinement, Styled, Task, View, Visibility,
24 WindowContext,
25};
26
27use collections::HashMap;
28use refineable::Refineable;
29use smallvec::SmallVec;
30use std::{
31 any::{Any, TypeId},
32 cell::RefCell,
33 cmp::Ordering,
34 fmt::Debug,
35 marker::PhantomData,
36 mem,
37 ops::DerefMut,
38 rc::Rc,
39 time::Duration,
40};
41use taffy::style::Overflow;
42use util::ResultExt;
43
44const DRAG_THRESHOLD: f64 = 2.;
45pub(crate) const TOOLTIP_DELAY: Duration = Duration::from_millis(500);
46
47/// The styling information for a given group.
48pub struct GroupStyle {
49 /// The identifier for this group.
50 pub group: SharedString,
51
52 /// The specific style refinement that this group would apply
53 /// to its children.
54 pub style: Box<StyleRefinement>,
55}
56
57/// An event for when a drag is moving over this element, with the given state type.
58pub struct DragMoveEvent<T> {
59 /// The mouse move event that triggered this drag move event.
60 pub event: MouseMoveEvent,
61
62 /// The bounds of this element.
63 pub bounds: Bounds<Pixels>,
64 drag: PhantomData<T>,
65}
66
67impl<T: 'static> DragMoveEvent<T> {
68 /// Returns the drag state for this event.
69 pub fn drag<'b>(&self, cx: &'b AppContext) -> &'b T {
70 cx.active_drag
71 .as_ref()
72 .and_then(|drag| drag.value.downcast_ref::<T>())
73 .expect("DragMoveEvent is only valid when the stored active drag is of the same type.")
74 }
75}
76
77impl Interactivity {
78 /// Bind the given callback to the mouse down event for the given mouse button, during the bubble phase
79 /// The imperative API equivalent of [`InteractiveElement::on_mouse_down`]
80 ///
81 /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to the view state from this callback.
82 pub fn on_mouse_down(
83 &mut self,
84 button: MouseButton,
85 listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
86 ) {
87 self.mouse_down_listeners
88 .push(Box::new(move |event, bounds, phase, cx| {
89 if phase == DispatchPhase::Bubble
90 && event.button == button
91 && bounds.visibly_contains(&event.position, cx)
92 {
93 (listener)(event, cx)
94 }
95 }));
96 }
97
98 /// Bind the given callback to the mouse down event for any button, during the capture phase
99 /// The imperative API equivalent of [`InteractiveElement::capture_any_mouse_down`]
100 ///
101 /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
102 pub fn capture_any_mouse_down(
103 &mut self,
104 listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
105 ) {
106 self.mouse_down_listeners
107 .push(Box::new(move |event, bounds, phase, cx| {
108 if phase == DispatchPhase::Capture && bounds.visibly_contains(&event.position, cx) {
109 (listener)(event, cx)
110 }
111 }));
112 }
113
114 /// Bind the given callback to the mouse down event for any button, during the bubble phase
115 /// the imperative API equivalent to [`InteractiveElement::on_any_mouse_down`]
116 ///
117 /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
118 pub fn on_any_mouse_down(
119 &mut self,
120 listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
121 ) {
122 self.mouse_down_listeners
123 .push(Box::new(move |event, bounds, phase, cx| {
124 if phase == DispatchPhase::Bubble && bounds.visibly_contains(&event.position, cx) {
125 (listener)(event, cx)
126 }
127 }));
128 }
129
130 /// Bind the given callback to the mouse up event for the given button, during the bubble phase
131 /// the imperative API equivalent to [`InteractiveElement::on_mouse_up`]
132 ///
133 /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
134 pub fn on_mouse_up(
135 &mut self,
136 button: MouseButton,
137 listener: impl Fn(&MouseUpEvent, &mut WindowContext) + 'static,
138 ) {
139 self.mouse_up_listeners
140 .push(Box::new(move |event, bounds, phase, cx| {
141 if phase == DispatchPhase::Bubble
142 && event.button == button
143 && bounds.visibly_contains(&event.position, cx)
144 {
145 (listener)(event, cx)
146 }
147 }));
148 }
149
150 /// Bind the given callback to the mouse up event for any button, during the capture phase
151 /// the imperative API equivalent to [`InteractiveElement::capture_any_mouse_up`]
152 ///
153 /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
154 pub fn capture_any_mouse_up(
155 &mut self,
156 listener: impl Fn(&MouseUpEvent, &mut WindowContext) + 'static,
157 ) {
158 self.mouse_up_listeners
159 .push(Box::new(move |event, bounds, phase, cx| {
160 if phase == DispatchPhase::Capture && bounds.visibly_contains(&event.position, cx) {
161 (listener)(event, cx)
162 }
163 }));
164 }
165
166 /// Bind the given callback to the mouse up event for any button, during the bubble phase
167 /// the imperative API equivalent to [`Interactivity::on_any_mouse_up`]
168 ///
169 /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
170 pub fn on_any_mouse_up(
171 &mut self,
172 listener: impl Fn(&MouseUpEvent, &mut WindowContext) + 'static,
173 ) {
174 self.mouse_up_listeners
175 .push(Box::new(move |event, bounds, phase, cx| {
176 if phase == DispatchPhase::Bubble && bounds.visibly_contains(&event.position, cx) {
177 (listener)(event, cx)
178 }
179 }));
180 }
181
182 /// Bind the given callback to the mouse down event, on any button, during the capture phase,
183 /// when the mouse is outside of the bounds of this element.
184 /// The imperative API equivalent to [`InteractiveElement::on_mouse_down_out`]
185 ///
186 /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
187 pub fn on_mouse_down_out(
188 &mut self,
189 listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
190 ) {
191 self.mouse_down_listeners
192 .push(Box::new(move |event, bounds, phase, cx| {
193 if phase == DispatchPhase::Capture && !bounds.visibly_contains(&event.position, cx)
194 {
195 (listener)(event, cx)
196 }
197 }));
198 }
199
200 /// Bind the given callback to the mouse up event, for the given button, during the capture phase,
201 /// when the mouse is outside of the bounds of this element.
202 /// The imperative API equivalent to [`InteractiveElement::on_mouse_up_out`]
203 ///
204 /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
205 pub fn on_mouse_up_out(
206 &mut self,
207 button: MouseButton,
208 listener: impl Fn(&MouseUpEvent, &mut WindowContext) + 'static,
209 ) {
210 self.mouse_up_listeners
211 .push(Box::new(move |event, bounds, phase, cx| {
212 if phase == DispatchPhase::Capture
213 && event.button == button
214 && !bounds.visibly_contains(&event.position, cx)
215 {
216 (listener)(event, cx);
217 }
218 }));
219 }
220
221 /// Bind the given callback to the mouse move event, during the bubble phase
222 /// The imperative API equivalent to [`InteractiveElement::on_mouse_move`]
223 ///
224 /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
225 pub fn on_mouse_move(
226 &mut self,
227 listener: impl Fn(&MouseMoveEvent, &mut WindowContext) + 'static,
228 ) {
229 self.mouse_move_listeners
230 .push(Box::new(move |event, bounds, phase, cx| {
231 if phase == DispatchPhase::Bubble && bounds.visibly_contains(&event.position, cx) {
232 (listener)(event, cx);
233 }
234 }));
235 }
236
237 /// Bind the given callback to the mouse drag event of the given type. Note that this
238 /// will be called for all move events, inside or outside of this element, as long as the
239 /// drag was started with this element under the mouse. Useful for implementing draggable
240 /// UIs that don't conform to a drag and drop style interaction, like resizing.
241 /// The imperative API equivalent to [`InteractiveElement::on_drag_move`]
242 ///
243 /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
244 pub fn on_drag_move<T>(
245 &mut self,
246 listener: impl Fn(&DragMoveEvent<T>, &mut WindowContext) + 'static,
247 ) where
248 T: 'static,
249 {
250 self.mouse_move_listeners
251 .push(Box::new(move |event, bounds, phase, cx| {
252 if phase == DispatchPhase::Capture
253 && cx
254 .active_drag
255 .as_ref()
256 .is_some_and(|drag| drag.value.as_ref().type_id() == TypeId::of::<T>())
257 {
258 (listener)(
259 &DragMoveEvent {
260 event: event.clone(),
261 bounds: bounds.bounds,
262 drag: PhantomData,
263 },
264 cx,
265 );
266 }
267 }));
268 }
269
270 /// Bind the given callback to scroll wheel events during the bubble phase
271 /// The imperative API equivalent to [`InteractiveElement::on_scroll_wheel`]
272 ///
273 /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
274 pub fn on_scroll_wheel(
275 &mut self,
276 listener: impl Fn(&ScrollWheelEvent, &mut WindowContext) + 'static,
277 ) {
278 self.scroll_wheel_listeners
279 .push(Box::new(move |event, bounds, phase, cx| {
280 if phase == DispatchPhase::Bubble && bounds.visibly_contains(&event.position, cx) {
281 (listener)(event, cx);
282 }
283 }));
284 }
285
286 /// Bind the given callback to an action dispatch during the capture phase
287 /// The imperative API equivalent to [`InteractiveElement::capture_action`]
288 ///
289 /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
290 pub fn capture_action<A: Action>(
291 &mut self,
292 listener: impl Fn(&A, &mut WindowContext) + 'static,
293 ) {
294 self.action_listeners.push((
295 TypeId::of::<A>(),
296 Box::new(move |action, phase, cx| {
297 let action = action.downcast_ref().unwrap();
298 if phase == DispatchPhase::Capture {
299 (listener)(action, cx)
300 }
301 }),
302 ));
303 }
304
305 /// Bind the given callback to an action dispatch during the bubble phase
306 /// The imperative API equivalent to [`InteractiveElement::on_action`]
307 ///
308 /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
309 pub fn on_action<A: Action>(&mut self, listener: impl Fn(&A, &mut WindowContext) + 'static) {
310 self.action_listeners.push((
311 TypeId::of::<A>(),
312 Box::new(move |action, phase, cx| {
313 let action = action.downcast_ref().unwrap();
314 if phase == DispatchPhase::Bubble {
315 (listener)(action, cx)
316 }
317 }),
318 ));
319 }
320
321 /// Bind the given callback to an action dispatch, based on a dynamic action parameter
322 /// instead of a type parameter. Useful for component libraries that want to expose
323 /// action bindings to their users.
324 /// The imperative API equivalent to [`InteractiveElement::on_boxed_action`]
325 ///
326 /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
327 pub fn on_boxed_action(
328 &mut self,
329 action: &dyn Action,
330 listener: impl Fn(&Box<dyn Action>, &mut WindowContext) + 'static,
331 ) {
332 let action = action.boxed_clone();
333 self.action_listeners.push((
334 (*action).type_id(),
335 Box::new(move |_, phase, cx| {
336 if phase == DispatchPhase::Bubble {
337 (listener)(&action, cx)
338 }
339 }),
340 ));
341 }
342
343 /// Bind the given callback to key down events during the bubble phase
344 /// The imperative API equivalent to [`InteractiveElement::on_key_down`]
345 ///
346 /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
347 pub fn on_key_down(&mut self, listener: impl Fn(&KeyDownEvent, &mut WindowContext) + 'static) {
348 self.key_down_listeners
349 .push(Box::new(move |event, phase, cx| {
350 if phase == DispatchPhase::Bubble {
351 (listener)(event, cx)
352 }
353 }));
354 }
355
356 /// Bind the given callback to key down events during the capture phase
357 /// The imperative API equivalent to [`InteractiveElement::capture_key_down`]
358 ///
359 /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
360 pub fn capture_key_down(
361 &mut self,
362 listener: impl Fn(&KeyDownEvent, &mut WindowContext) + 'static,
363 ) {
364 self.key_down_listeners
365 .push(Box::new(move |event, phase, cx| {
366 if phase == DispatchPhase::Capture {
367 listener(event, cx)
368 }
369 }));
370 }
371
372 /// Bind the given callback to key up events during the bubble phase
373 /// The imperative API equivalent to [`InteractiveElement::on_key_up`]
374 ///
375 /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
376 pub fn on_key_up(&mut self, listener: impl Fn(&KeyUpEvent, &mut WindowContext) + 'static) {
377 self.key_up_listeners
378 .push(Box::new(move |event, phase, cx| {
379 if phase == DispatchPhase::Bubble {
380 listener(event, cx)
381 }
382 }));
383 }
384
385 /// Bind the given callback to key up events during the capture phase
386 /// The imperative API equivalent to [`InteractiveElement::on_key_up`]
387 ///
388 /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
389 pub fn capture_key_up(&mut self, listener: impl Fn(&KeyUpEvent, &mut WindowContext) + 'static) {
390 self.key_up_listeners
391 .push(Box::new(move |event, phase, cx| {
392 if phase == DispatchPhase::Capture {
393 listener(event, cx)
394 }
395 }));
396 }
397
398 /// Bind the given callback to drop events of the given type, whether or not the drag started on this element
399 /// The imperative API equivalent to [`InteractiveElement::on_drop`]
400 ///
401 /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
402 pub fn on_drop<T: 'static>(&mut self, listener: impl Fn(&T, &mut WindowContext) + 'static) {
403 self.drop_listeners.push((
404 TypeId::of::<T>(),
405 Box::new(move |dragged_value, cx| {
406 listener(dragged_value.downcast_ref().unwrap(), cx);
407 }),
408 ));
409 }
410
411 /// Use the given predicate to determine whether or not a drop event should be dispatched to this element
412 /// The imperative API equivalent to [`InteractiveElement::can_drop`]
413 pub fn can_drop(&mut self, predicate: impl Fn(&dyn Any, &mut WindowContext) -> bool + 'static) {
414 self.can_drop_predicate = Some(Box::new(predicate));
415 }
416
417 /// Bind the given callback to click events of this element
418 /// The imperative API equivalent to [`StatefulInteractiveElement::on_click`]
419 ///
420 /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
421 pub fn on_click(&mut self, listener: impl Fn(&ClickEvent, &mut WindowContext) + 'static)
422 where
423 Self: Sized,
424 {
425 self.click_listeners
426 .push(Box::new(move |event, cx| listener(event, cx)));
427 }
428
429 /// On drag initiation, this callback will be used to create a new view to render the dragged value for a
430 /// drag and drop operation. This API should also be used as the equivalent of 'on drag start' with
431 /// the [`Self::on_drag_move`] API
432 /// The imperative API equivalent to [`StatefulInteractiveElement::on_drag`]
433 ///
434 /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
435 pub fn on_drag<T, W>(
436 &mut self,
437 value: T,
438 constructor: impl Fn(&T, &mut WindowContext) -> View<W> + 'static,
439 ) where
440 Self: Sized,
441 T: 'static,
442 W: 'static + Render,
443 {
444 debug_assert!(
445 self.drag_listener.is_none(),
446 "calling on_drag more than once on the same element is not supported"
447 );
448 self.drag_listener = Some((
449 Box::new(value),
450 Box::new(move |value, cx| constructor(value.downcast_ref().unwrap(), cx).into()),
451 ));
452 }
453
454 /// Bind the given callback on the hover start and end events of this element. Note that the boolean
455 /// passed to the callback is true when the hover starts and false when it ends.
456 /// The imperative API equivalent to [`StatefulInteractiveElement::on_drag`]
457 ///
458 /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
459 pub fn on_hover(&mut self, listener: impl Fn(&bool, &mut WindowContext) + 'static)
460 where
461 Self: Sized,
462 {
463 debug_assert!(
464 self.hover_listener.is_none(),
465 "calling on_hover more than once on the same element is not supported"
466 );
467 self.hover_listener = Some(Box::new(listener));
468 }
469
470 /// Use the given callback to construct a new tooltip view when the mouse hovers over this element.
471 /// The imperative API equivalent to [`InteractiveElement::tooltip`]
472 pub fn tooltip(&mut self, build_tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static)
473 where
474 Self: Sized,
475 {
476 debug_assert!(
477 self.tooltip_builder.is_none(),
478 "calling tooltip more than once on the same element is not supported"
479 );
480 self.tooltip_builder = Some(Rc::new(build_tooltip));
481 }
482
483 /// Block the mouse from interacting with this element or any of it's children
484 /// The imperative API equivalent to [`InteractiveElement::block_mouse`]
485 pub fn block_mouse(&mut self) {
486 self.block_mouse = true;
487 }
488}
489
490/// A trait for elements that want to use the standard GPUI event handlers that don't
491/// require any state.
492pub trait InteractiveElement: Sized {
493 /// Retrieve the interactivity state associated with this element
494 fn interactivity(&mut self) -> &mut Interactivity;
495
496 /// Assign this element to a group of elements that can be styled together
497 fn group(mut self, group: impl Into<SharedString>) -> Self {
498 self.interactivity().group = Some(group.into());
499 self
500 }
501
502 /// Assign this elements
503 fn id(mut self, id: impl Into<ElementId>) -> Stateful<Self> {
504 self.interactivity().element_id = Some(id.into());
505
506 Stateful { element: self }
507 }
508
509 /// Track the focus state of the given focus handle on this element.
510 /// If the focus handle is focused by the application, this element will
511 /// apply it's focused styles.
512 fn track_focus(mut self, focus_handle: &FocusHandle) -> Focusable<Self> {
513 self.interactivity().focusable = true;
514 self.interactivity().tracked_focus_handle = Some(focus_handle.clone());
515 Focusable { element: self }
516 }
517
518 /// Set the keymap context for this element. This will be used to determine
519 /// which action to dispatch from the keymap.
520 fn key_context<C, E>(mut self, key_context: C) -> Self
521 where
522 C: TryInto<KeyContext, Error = E>,
523 E: Debug,
524 {
525 if let Some(key_context) = key_context.try_into().log_err() {
526 self.interactivity().key_context = Some(key_context);
527 }
528 self
529 }
530
531 /// Apply the given style to this element when the mouse hovers over it
532 fn hover(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self {
533 debug_assert!(
534 self.interactivity().hover_style.is_none(),
535 "hover style already set"
536 );
537 self.interactivity().hover_style = Some(Box::new(f(StyleRefinement::default())));
538 self
539 }
540
541 /// Apply the given style to this element when the mouse hovers over a group member
542 fn group_hover(
543 mut self,
544 group_name: impl Into<SharedString>,
545 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
546 ) -> Self {
547 self.interactivity().group_hover_style = Some(GroupStyle {
548 group: group_name.into(),
549 style: Box::new(f(StyleRefinement::default())),
550 });
551 self
552 }
553
554 /// Bind the given callback to the mouse down event for the given mouse button,
555 /// the fluent API equivalent to [`Interactivity::on_mouse_down`]
556 ///
557 /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to the view state from this callback.
558 fn on_mouse_down(
559 mut self,
560 button: MouseButton,
561 listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
562 ) -> Self {
563 self.interactivity().on_mouse_down(button, listener);
564 self
565 }
566
567 #[cfg(any(test, feature = "test-support"))]
568 /// Set a key that can be used to look up this element's bounds
569 /// in the [`VisualTestContext::debug_bounds`] map
570 /// This is a noop in release builds
571 fn debug_selector(mut self, f: impl FnOnce() -> String) -> Self {
572 self.interactivity().debug_selector = Some(f());
573 self
574 }
575
576 #[cfg(not(any(test, feature = "test-support")))]
577 /// Set a key that can be used to look up this element's bounds
578 /// in the [`VisualTestContext::debug_bounds`] map
579 /// This is a noop in release builds
580 #[inline]
581 fn debug_selector(self, _: impl FnOnce() -> String) -> Self {
582 self
583 }
584
585 /// Bind the given callback to the mouse down event for any button, during the capture phase
586 /// the fluent API equivalent to [`Interactivity::capture_any_mouse_down`]
587 ///
588 /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
589 fn capture_any_mouse_down(
590 mut self,
591 listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
592 ) -> Self {
593 self.interactivity().capture_any_mouse_down(listener);
594 self
595 }
596
597 /// Bind the given callback to the mouse down event for any button, during the capture phase
598 /// the fluent API equivalent to [`Interactivity::on_any_mouse_down`]
599 ///
600 /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
601 fn on_any_mouse_down(
602 mut self,
603 listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
604 ) -> Self {
605 self.interactivity().on_any_mouse_down(listener);
606 self
607 }
608
609 /// Bind the given callback to the mouse up event for the given button, during the bubble phase
610 /// the fluent API equivalent to [`Interactivity::on_mouse_up`]
611 ///
612 /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
613 fn on_mouse_up(
614 mut self,
615 button: MouseButton,
616 listener: impl Fn(&MouseUpEvent, &mut WindowContext) + 'static,
617 ) -> Self {
618 self.interactivity().on_mouse_up(button, listener);
619 self
620 }
621
622 /// Bind the given callback to the mouse up event for any button, during the capture phase
623 /// the fluent API equivalent to [`Interactivity::capture_any_mouse_up`]
624 ///
625 /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
626 fn capture_any_mouse_up(
627 mut self,
628 listener: impl Fn(&MouseUpEvent, &mut WindowContext) + 'static,
629 ) -> Self {
630 self.interactivity().capture_any_mouse_up(listener);
631 self
632 }
633
634 /// Bind the given callback to the mouse down event, on any button, during the capture phase,
635 /// when the mouse is outside of the bounds of this element.
636 /// The fluent API equivalent to [`Interactivity::on_mouse_down_out`]
637 ///
638 /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
639 fn on_mouse_down_out(
640 mut self,
641 listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
642 ) -> Self {
643 self.interactivity().on_mouse_down_out(listener);
644 self
645 }
646
647 /// Bind the given callback to the mouse up event, for the given button, during the capture phase,
648 /// when the mouse is outside of the bounds of this element.
649 /// The fluent API equivalent to [`Interactivity::on_mouse_up_out`]
650 ///
651 /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
652 fn on_mouse_up_out(
653 mut self,
654 button: MouseButton,
655 listener: impl Fn(&MouseUpEvent, &mut WindowContext) + 'static,
656 ) -> Self {
657 self.interactivity().on_mouse_up_out(button, listener);
658 self
659 }
660
661 /// Bind the given callback to the mouse move event, during the bubble phase
662 /// The fluent API equivalent to [`Interactivity::on_mouse_move`]
663 ///
664 /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
665 fn on_mouse_move(
666 mut self,
667 listener: impl Fn(&MouseMoveEvent, &mut WindowContext) + 'static,
668 ) -> Self {
669 self.interactivity().on_mouse_move(listener);
670 self
671 }
672
673 /// Bind the given callback to the mouse drag event of the given type. Note that this
674 /// will be called for all move events, inside or outside of this element, as long as the
675 /// drag was started with this element under the mouse. Useful for implementing draggable
676 /// UIs that don't conform to a drag and drop style interaction, like resizing.
677 /// The fluent API equivalent to [`Interactivity::on_drag_move`]
678 ///
679 /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
680 fn on_drag_move<T: 'static>(
681 mut self,
682 listener: impl Fn(&DragMoveEvent<T>, &mut WindowContext) + 'static,
683 ) -> Self
684 where
685 T: 'static,
686 {
687 self.interactivity().on_drag_move(listener);
688 self
689 }
690
691 /// Bind the given callback to scroll wheel events during the bubble phase
692 /// The fluent API equivalent to [`Interactivity::on_scroll_wheel`]
693 ///
694 /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
695 fn on_scroll_wheel(
696 mut self,
697 listener: impl Fn(&ScrollWheelEvent, &mut WindowContext) + 'static,
698 ) -> Self {
699 self.interactivity().on_scroll_wheel(listener);
700 self
701 }
702
703 /// Capture the given action, before normal action dispatch can fire
704 /// The fluent API equivalent to [`Interactivity::on_scroll_wheel`]
705 ///
706 /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
707 fn capture_action<A: Action>(
708 mut self,
709 listener: impl Fn(&A, &mut WindowContext) + 'static,
710 ) -> Self {
711 self.interactivity().capture_action(listener);
712 self
713 }
714
715 /// Bind the given callback to an action dispatch during the bubble phase
716 /// The fluent API equivalent to [`Interactivity::on_action`]
717 ///
718 /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
719 fn on_action<A: Action>(mut self, listener: impl Fn(&A, &mut WindowContext) + 'static) -> Self {
720 self.interactivity().on_action(listener);
721 self
722 }
723
724 /// Bind the given callback to an action dispatch, based on a dynamic action parameter
725 /// instead of a type parameter. Useful for component libraries that want to expose
726 /// action bindings to their users.
727 /// The fluent API equivalent to [`Interactivity::on_boxed_action`]
728 ///
729 /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
730 fn on_boxed_action(
731 mut self,
732 action: &dyn Action,
733 listener: impl Fn(&Box<dyn Action>, &mut WindowContext) + 'static,
734 ) -> Self {
735 self.interactivity().on_boxed_action(action, listener);
736 self
737 }
738
739 /// Bind the given callback to key down events during the bubble phase
740 /// The fluent API equivalent to [`Interactivity::on_key_down`]
741 ///
742 /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
743 fn on_key_down(
744 mut self,
745 listener: impl Fn(&KeyDownEvent, &mut WindowContext) + 'static,
746 ) -> Self {
747 self.interactivity().on_key_down(listener);
748 self
749 }
750
751 /// Bind the given callback to key down events during the capture phase
752 /// The fluent API equivalent to [`Interactivity::capture_key_down`]
753 ///
754 /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
755 fn capture_key_down(
756 mut self,
757 listener: impl Fn(&KeyDownEvent, &mut WindowContext) + 'static,
758 ) -> Self {
759 self.interactivity().capture_key_down(listener);
760 self
761 }
762
763 /// Bind the given callback to key up events during the bubble phase
764 /// The fluent API equivalent to [`Interactivity::on_key_up`]
765 ///
766 /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
767 fn on_key_up(mut self, listener: impl Fn(&KeyUpEvent, &mut WindowContext) + 'static) -> Self {
768 self.interactivity().on_key_up(listener);
769 self
770 }
771
772 /// Bind the given callback to key up events during the capture phase
773 /// The fluent API equivalent to [`Interactivity::capture_key_up`]
774 ///
775 /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
776 fn capture_key_up(
777 mut self,
778 listener: impl Fn(&KeyUpEvent, &mut WindowContext) + 'static,
779 ) -> Self {
780 self.interactivity().capture_key_up(listener);
781 self
782 }
783
784 /// Apply the given style when the given data type is dragged over this element
785 fn drag_over<S: 'static>(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self {
786 self.interactivity()
787 .drag_over_styles
788 .push((TypeId::of::<S>(), f(StyleRefinement::default())));
789 self
790 }
791
792 /// Apply the given style when the given data type is dragged over this element's group
793 fn group_drag_over<S: 'static>(
794 mut self,
795 group_name: impl Into<SharedString>,
796 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
797 ) -> Self {
798 self.interactivity().group_drag_over_styles.push((
799 TypeId::of::<S>(),
800 GroupStyle {
801 group: group_name.into(),
802 style: Box::new(f(StyleRefinement::default())),
803 },
804 ));
805 self
806 }
807
808 /// Bind the given callback to drop events of the given type, whether or not the drag started on this element
809 /// The fluent API equivalent to [`Interactivity::on_drop`]
810 ///
811 /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
812 fn on_drop<T: 'static>(mut self, listener: impl Fn(&T, &mut WindowContext) + 'static) -> Self {
813 self.interactivity().on_drop(listener);
814 self
815 }
816
817 /// Use the given predicate to determine whether or not a drop event should be dispatched to this element
818 /// The fluent API equivalent to [`Interactivity::can_drop`]
819 fn can_drop(
820 mut self,
821 predicate: impl Fn(&dyn Any, &mut WindowContext) -> bool + 'static,
822 ) -> Self {
823 self.interactivity().can_drop(predicate);
824 self
825 }
826
827 /// Block the mouse from interacting with this element or any of it's children
828 /// The fluent API equivalent to [`Interactivity::block_mouse`]
829 fn block_mouse(mut self) -> Self {
830 self.interactivity().block_mouse();
831 self
832 }
833}
834
835/// A trait for elements that want to use the standard GPUI interactivity features
836/// that require state.
837pub trait StatefulInteractiveElement: InteractiveElement {
838 /// Set this element to focusable.
839 fn focusable(mut self) -> Focusable<Self> {
840 self.interactivity().focusable = true;
841 Focusable { element: self }
842 }
843
844 /// Set the overflow x and y to scroll.
845 fn overflow_scroll(mut self) -> Self {
846 self.interactivity().base_style.overflow.x = Some(Overflow::Scroll);
847 self.interactivity().base_style.overflow.y = Some(Overflow::Scroll);
848 self
849 }
850
851 /// Set the overflow x to scroll.
852 fn overflow_x_scroll(mut self) -> Self {
853 self.interactivity().base_style.overflow.x = Some(Overflow::Scroll);
854 self
855 }
856
857 /// Set the overflow y to scroll.
858 fn overflow_y_scroll(mut self) -> Self {
859 self.interactivity().base_style.overflow.y = Some(Overflow::Scroll);
860 self
861 }
862
863 /// Track the scroll state of this element with the given handle.
864 fn track_scroll(mut self, scroll_handle: &ScrollHandle) -> Self {
865 self.interactivity().scroll_handle = Some(scroll_handle.clone());
866 self
867 }
868
869 /// Set the given styles to be applied when this element is active.
870 fn active(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
871 where
872 Self: Sized,
873 {
874 self.interactivity().active_style = Some(Box::new(f(StyleRefinement::default())));
875 self
876 }
877
878 /// Set the given styles to be applied when this element's group is active.
879 fn group_active(
880 mut self,
881 group_name: impl Into<SharedString>,
882 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
883 ) -> Self
884 where
885 Self: Sized,
886 {
887 self.interactivity().group_active_style = Some(GroupStyle {
888 group: group_name.into(),
889 style: Box::new(f(StyleRefinement::default())),
890 });
891 self
892 }
893
894 /// Bind the given callback to click events of this element
895 /// The fluent API equivalent to [`Interactivity::on_click`]
896 ///
897 /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
898 fn on_click(mut self, listener: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self
899 where
900 Self: Sized,
901 {
902 self.interactivity().on_click(listener);
903 self
904 }
905
906 /// On drag initiation, this callback will be used to create a new view to render the dragged value for a
907 /// drag and drop operation. This API should also be used as the equivalent of 'on drag start' with
908 /// the [`Self::on_drag_move`] API
909 /// The fluent API equivalent to [`Interactivity::on_drag`]
910 ///
911 /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
912 fn on_drag<T, W>(
913 mut self,
914 value: T,
915 constructor: impl Fn(&T, &mut WindowContext) -> View<W> + 'static,
916 ) -> Self
917 where
918 Self: Sized,
919 T: 'static,
920 W: 'static + Render,
921 {
922 self.interactivity().on_drag(value, constructor);
923 self
924 }
925
926 /// Bind the given callback on the hover start and end events of this element. Note that the boolean
927 /// passed to the callback is true when the hover starts and false when it ends.
928 /// The fluent API equivalent to [`Interactivity::on_hover`]
929 ///
930 /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
931 fn on_hover(mut self, listener: impl Fn(&bool, &mut WindowContext) + 'static) -> Self
932 where
933 Self: Sized,
934 {
935 self.interactivity().on_hover(listener);
936 self
937 }
938
939 /// Use the given callback to construct a new tooltip view when the mouse hovers over this element.
940 /// The fluent API equivalent to [`Interactivity::tooltip`]
941 fn tooltip(mut self, build_tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self
942 where
943 Self: Sized,
944 {
945 self.interactivity().tooltip(build_tooltip);
946 self
947 }
948}
949
950/// A trait for providing focus related APIs to interactive elements
951pub trait FocusableElement: InteractiveElement {
952 /// Set the given styles to be applied when this element, specifically, is focused.
953 fn focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
954 where
955 Self: Sized,
956 {
957 self.interactivity().focus_style = Some(Box::new(f(StyleRefinement::default())));
958 self
959 }
960
961 /// Set the given styles to be applied when this element is inside another element that is focused.
962 fn in_focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
963 where
964 Self: Sized,
965 {
966 self.interactivity().in_focus_style = Some(Box::new(f(StyleRefinement::default())));
967 self
968 }
969}
970
971pub(crate) type MouseDownListener =
972 Box<dyn Fn(&MouseDownEvent, &InteractiveBounds, DispatchPhase, &mut WindowContext) + 'static>;
973pub(crate) type MouseUpListener =
974 Box<dyn Fn(&MouseUpEvent, &InteractiveBounds, DispatchPhase, &mut WindowContext) + 'static>;
975
976pub(crate) type MouseMoveListener =
977 Box<dyn Fn(&MouseMoveEvent, &InteractiveBounds, DispatchPhase, &mut WindowContext) + 'static>;
978
979pub(crate) type ScrollWheelListener =
980 Box<dyn Fn(&ScrollWheelEvent, &InteractiveBounds, DispatchPhase, &mut WindowContext) + 'static>;
981
982pub(crate) type ClickListener = Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>;
983
984pub(crate) type DragListener = Box<dyn Fn(&dyn Any, &mut WindowContext) -> AnyView + 'static>;
985
986type DropListener = Box<dyn Fn(&dyn Any, &mut WindowContext) + 'static>;
987
988type CanDropPredicate = Box<dyn Fn(&dyn Any, &mut WindowContext) -> bool + 'static>;
989
990pub(crate) type TooltipBuilder = Rc<dyn Fn(&mut WindowContext) -> AnyView + 'static>;
991
992pub(crate) type KeyDownListener =
993 Box<dyn Fn(&KeyDownEvent, DispatchPhase, &mut WindowContext) + 'static>;
994
995pub(crate) type KeyUpListener =
996 Box<dyn Fn(&KeyUpEvent, DispatchPhase, &mut WindowContext) + 'static>;
997
998pub(crate) type ActionListener = Box<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext) + 'static>;
999
1000/// Construct a new [`Div`] element
1001#[track_caller]
1002pub fn div() -> Div {
1003 #[cfg(debug_assertions)]
1004 let interactivity = Interactivity {
1005 location: Some(*core::panic::Location::caller()),
1006 ..Default::default()
1007 };
1008
1009 #[cfg(not(debug_assertions))]
1010 let interactivity = Interactivity::default();
1011
1012 Div {
1013 interactivity,
1014 children: SmallVec::default(),
1015 }
1016}
1017
1018/// A [`Div`] element, the all-in-one element for building complex UIs in GPUI
1019pub struct Div {
1020 interactivity: Interactivity,
1021 children: SmallVec<[AnyElement; 2]>,
1022}
1023
1024impl Styled for Div {
1025 fn style(&mut self) -> &mut StyleRefinement {
1026 &mut self.interactivity.base_style
1027 }
1028}
1029
1030impl InteractiveElement for Div {
1031 fn interactivity(&mut self) -> &mut Interactivity {
1032 &mut self.interactivity
1033 }
1034}
1035
1036impl ParentElement for Div {
1037 fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
1038 self.children.extend(elements)
1039 }
1040}
1041
1042impl Element for Div {
1043 type State = DivState;
1044
1045 fn request_layout(
1046 &mut self,
1047 element_state: Option<Self::State>,
1048 cx: &mut ElementContext,
1049 ) -> (LayoutId, Self::State) {
1050 let mut child_layout_ids = SmallVec::new();
1051 let (layout_id, interactive_state) = self.interactivity.layout(
1052 element_state.map(|s| s.interactive_state),
1053 cx,
1054 |style, cx| {
1055 cx.with_text_style(style.text_style().cloned(), |cx| {
1056 child_layout_ids = self
1057 .children
1058 .iter_mut()
1059 .map(|child| child.request_layout(cx))
1060 .collect::<SmallVec<_>>();
1061 cx.request_layout(&style, child_layout_ids.iter().copied())
1062 })
1063 },
1064 );
1065 (
1066 layout_id,
1067 DivState {
1068 interactive_state,
1069 child_layout_ids,
1070 },
1071 )
1072 }
1073
1074 fn paint(
1075 &mut self,
1076 bounds: Bounds<Pixels>,
1077 element_state: &mut Self::State,
1078 cx: &mut ElementContext,
1079 ) {
1080 let mut child_min = point(Pixels::MAX, Pixels::MAX);
1081 let mut child_max = Point::default();
1082 let content_size = if element_state.child_layout_ids.is_empty() {
1083 bounds.size
1084 } else if let Some(scroll_handle) = self.interactivity.scroll_handle.as_ref() {
1085 let mut state = scroll_handle.0.borrow_mut();
1086 state.child_bounds = Vec::with_capacity(element_state.child_layout_ids.len());
1087 state.bounds = bounds;
1088 let requested = state.requested_scroll_top.take();
1089
1090 for (ix, child_layout_id) in element_state.child_layout_ids.iter().enumerate() {
1091 let child_bounds = cx.layout_bounds(*child_layout_id);
1092 child_min = child_min.min(&child_bounds.origin);
1093 child_max = child_max.max(&child_bounds.lower_right());
1094 state.child_bounds.push(child_bounds);
1095
1096 if let Some(requested) = requested.as_ref() {
1097 if requested.0 == ix {
1098 *state.offset.borrow_mut() =
1099 bounds.origin - (child_bounds.origin - point(px(0.), requested.1));
1100 }
1101 }
1102 }
1103 (child_max - child_min).into()
1104 } else {
1105 for child_layout_id in &element_state.child_layout_ids {
1106 let child_bounds = cx.layout_bounds(*child_layout_id);
1107 child_min = child_min.min(&child_bounds.origin);
1108 child_max = child_max.max(&child_bounds.lower_right());
1109 }
1110 (child_max - child_min).into()
1111 };
1112
1113 self.interactivity.paint(
1114 bounds,
1115 content_size,
1116 &mut element_state.interactive_state,
1117 cx,
1118 |_style, scroll_offset, cx| {
1119 cx.with_element_offset(scroll_offset, |cx| {
1120 for child in &mut self.children {
1121 child.paint(cx);
1122 }
1123 })
1124 },
1125 );
1126 }
1127}
1128
1129impl IntoElement for Div {
1130 type Element = Self;
1131
1132 fn element_id(&self) -> Option<ElementId> {
1133 self.interactivity.element_id.clone()
1134 }
1135
1136 fn into_element(self) -> Self::Element {
1137 self
1138 }
1139}
1140
1141/// The state a div needs to keep track of between frames.
1142pub struct DivState {
1143 child_layout_ids: SmallVec<[LayoutId; 2]>,
1144 interactive_state: InteractiveElementState,
1145}
1146
1147impl DivState {
1148 /// Is the div currently being clicked on?
1149 pub fn is_active(&self) -> bool {
1150 self.interactive_state
1151 .pending_mouse_down
1152 .as_ref()
1153 .map_or(false, |pending| pending.borrow().is_some())
1154 }
1155}
1156
1157/// The interactivity struct. Powers all of the general-purpose
1158/// interactivity in the `Div` element.
1159#[derive(Default)]
1160pub struct Interactivity {
1161 /// The element ID of the element
1162 pub element_id: Option<ElementId>,
1163 pub(crate) key_context: Option<KeyContext>,
1164 pub(crate) focusable: bool,
1165 pub(crate) tracked_focus_handle: Option<FocusHandle>,
1166 pub(crate) scroll_handle: Option<ScrollHandle>,
1167 pub(crate) group: Option<SharedString>,
1168 /// The base style of the element, before any modifications are applied
1169 /// by focus, active, etc.
1170 pub base_style: Box<StyleRefinement>,
1171 pub(crate) focus_style: Option<Box<StyleRefinement>>,
1172 pub(crate) in_focus_style: Option<Box<StyleRefinement>>,
1173 pub(crate) hover_style: Option<Box<StyleRefinement>>,
1174 pub(crate) group_hover_style: Option<GroupStyle>,
1175 pub(crate) active_style: Option<Box<StyleRefinement>>,
1176 pub(crate) group_active_style: Option<GroupStyle>,
1177 pub(crate) drag_over_styles: Vec<(TypeId, StyleRefinement)>,
1178 pub(crate) group_drag_over_styles: Vec<(TypeId, GroupStyle)>,
1179 pub(crate) mouse_down_listeners: Vec<MouseDownListener>,
1180 pub(crate) mouse_up_listeners: Vec<MouseUpListener>,
1181 pub(crate) mouse_move_listeners: Vec<MouseMoveListener>,
1182 pub(crate) scroll_wheel_listeners: Vec<ScrollWheelListener>,
1183 pub(crate) key_down_listeners: Vec<KeyDownListener>,
1184 pub(crate) key_up_listeners: Vec<KeyUpListener>,
1185 pub(crate) action_listeners: Vec<(TypeId, ActionListener)>,
1186 pub(crate) drop_listeners: Vec<(TypeId, DropListener)>,
1187 pub(crate) can_drop_predicate: Option<CanDropPredicate>,
1188 pub(crate) click_listeners: Vec<ClickListener>,
1189 pub(crate) drag_listener: Option<(Box<dyn Any>, DragListener)>,
1190 pub(crate) hover_listener: Option<Box<dyn Fn(&bool, &mut WindowContext)>>,
1191 pub(crate) tooltip_builder: Option<TooltipBuilder>,
1192 pub(crate) block_mouse: bool,
1193
1194 #[cfg(debug_assertions)]
1195 pub(crate) location: Option<core::panic::Location<'static>>,
1196
1197 #[cfg(any(test, feature = "test-support"))]
1198 pub(crate) debug_selector: Option<String>,
1199}
1200
1201/// The bounds and depth of an element in the computed element tree.
1202#[derive(Clone, Debug)]
1203pub struct InteractiveBounds {
1204 /// The 2D bounds of the element
1205 pub bounds: Bounds<Pixels>,
1206 /// The 'stacking order', or depth, for this element
1207 pub stacking_order: StackingOrder,
1208}
1209
1210impl InteractiveBounds {
1211 /// Checks whether this point was inside these bounds, and that these bounds where the topmost layer
1212 pub fn visibly_contains(&self, point: &Point<Pixels>, cx: &WindowContext) -> bool {
1213 self.bounds.contains(point) && cx.was_top_layer(point, &self.stacking_order)
1214 }
1215
1216 /// Checks whether this point was inside these bounds, and that these bounds where the topmost layer
1217 /// under an active drag
1218 pub fn drag_target_contains(&self, point: &Point<Pixels>, cx: &WindowContext) -> bool {
1219 self.bounds.contains(point)
1220 && cx.was_top_layer_under_active_drag(point, &self.stacking_order)
1221 }
1222}
1223
1224impl Interactivity {
1225 /// Layout this element according to this interactivity state's configured styles
1226 pub fn layout(
1227 &mut self,
1228 element_state: Option<InteractiveElementState>,
1229 cx: &mut ElementContext,
1230 f: impl FnOnce(Style, &mut ElementContext) -> LayoutId,
1231 ) -> (LayoutId, InteractiveElementState) {
1232 let mut element_state = element_state.unwrap_or_default();
1233
1234 if cx.has_active_drag() {
1235 if let Some(pending_mouse_down) = element_state.pending_mouse_down.as_ref() {
1236 *pending_mouse_down.borrow_mut() = None;
1237 }
1238 if let Some(clicked_state) = element_state.clicked_state.as_ref() {
1239 *clicked_state.borrow_mut() = ElementClickedState::default();
1240 }
1241 }
1242
1243 // Ensure we store a focus handle in our element state if we're focusable.
1244 // If there's an explicit focus handle we're tracking, use that. Otherwise
1245 // create a new handle and store it in the element state, which lives for as
1246 // as frames contain an element with this id.
1247 if self.focusable {
1248 element_state.focus_handle.get_or_insert_with(|| {
1249 self.tracked_focus_handle
1250 .clone()
1251 .unwrap_or_else(|| cx.focus_handle())
1252 });
1253 }
1254
1255 if let Some(scroll_handle) = self.scroll_handle.as_ref() {
1256 element_state.scroll_offset = Some(scroll_handle.0.borrow().offset.clone());
1257 }
1258
1259 let style = self.compute_style(None, &mut element_state, cx);
1260 let layout_id = f(style, cx);
1261 (layout_id, element_state)
1262 }
1263
1264 /// Paint this element according to this interactivity state's configured styles
1265 /// and bind the element's mouse and keyboard events.
1266 ///
1267 /// content_size is the size of the content of the element, which may be larger than the
1268 /// element's bounds if the element is scrollable.
1269 ///
1270 /// the final computed style will be passed to the provided function, along
1271 /// with the current scroll offset
1272 pub fn paint(
1273 &mut self,
1274 bounds: Bounds<Pixels>,
1275 content_size: Size<Pixels>,
1276 element_state: &mut InteractiveElementState,
1277 cx: &mut ElementContext,
1278 f: impl FnOnce(&Style, Point<Pixels>, &mut ElementContext),
1279 ) {
1280 let style = self.compute_style(Some(bounds), element_state, cx);
1281 let z_index = style.z_index.unwrap_or(0);
1282
1283 #[cfg(any(feature = "test-support", test))]
1284 if let Some(debug_selector) = &self.debug_selector {
1285 cx.window
1286 .next_frame
1287 .debug_bounds
1288 .insert(debug_selector.clone(), bounds);
1289 }
1290
1291 let paint_hover_group_handler = |cx: &mut ElementContext| {
1292 let hover_group_bounds = self
1293 .group_hover_style
1294 .as_ref()
1295 .and_then(|group_hover| GroupBounds::get(&group_hover.group, cx));
1296
1297 if let Some(group_bounds) = hover_group_bounds {
1298 let hovered = group_bounds.contains(&cx.mouse_position());
1299 cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
1300 if phase == DispatchPhase::Capture
1301 && group_bounds.contains(&event.position) != hovered
1302 {
1303 cx.refresh();
1304 }
1305 });
1306 }
1307 };
1308
1309 if style.visibility == Visibility::Hidden {
1310 cx.with_z_index(z_index, |cx| paint_hover_group_handler(cx));
1311 return;
1312 }
1313
1314 cx.with_z_index(z_index, |cx| {
1315 style.paint(bounds, cx, |cx: &mut ElementContext| {
1316 cx.with_text_style(style.text_style().cloned(), |cx| {
1317 cx.with_content_mask(style.overflow_mask(bounds, cx.rem_size()), |cx| {
1318 #[cfg(debug_assertions)]
1319 if self.element_id.is_some()
1320 && (style.debug
1321 || style.debug_below
1322 || cx.has_global::<crate::DebugBelow>())
1323 && bounds.contains(&cx.mouse_position())
1324 {
1325 const FONT_SIZE: crate::Pixels = crate::Pixels(10.);
1326 let element_id = format!("{:?}", self.element_id.as_ref().unwrap());
1327 let str_len = element_id.len();
1328
1329 let render_debug_text = |cx: &mut ElementContext| {
1330 if let Some(text) = cx
1331 .text_system()
1332 .shape_text(
1333 element_id.into(),
1334 FONT_SIZE,
1335 &[cx.text_style().to_run(str_len)],
1336 None,
1337 )
1338 .ok()
1339 .and_then(|mut text| text.pop())
1340 {
1341 text.paint(bounds.origin, FONT_SIZE, cx).ok();
1342
1343 let text_bounds = crate::Bounds {
1344 origin: bounds.origin,
1345 size: text.size(FONT_SIZE),
1346 };
1347 if self.location.is_some()
1348 && text_bounds.contains(&cx.mouse_position())
1349 && cx.modifiers().command
1350 {
1351 let command_held = cx.modifiers().command;
1352 cx.on_key_event({
1353 move |e: &crate::ModifiersChangedEvent, _phase, cx| {
1354 if e.modifiers.command != command_held
1355 && text_bounds.contains(&cx.mouse_position())
1356 {
1357 cx.refresh();
1358 }
1359 }
1360 });
1361
1362 let hovered = bounds.contains(&cx.mouse_position());
1363 cx.on_mouse_event(
1364 move |event: &MouseMoveEvent, phase, cx| {
1365 if phase == DispatchPhase::Capture
1366 && bounds.contains(&event.position) != hovered
1367 {
1368 cx.refresh();
1369 }
1370 },
1371 );
1372
1373 cx.on_mouse_event({
1374 let location = self.location.unwrap();
1375 move |e: &crate::MouseDownEvent, phase, cx| {
1376 if text_bounds.contains(&e.position)
1377 && phase.capture()
1378 {
1379 cx.stop_propagation();
1380 let Ok(dir) = std::env::current_dir() else {
1381 return;
1382 };
1383
1384 eprintln!(
1385 "This element was created at:\n{}:{}:{}",
1386 dir.join(location.file()).to_string_lossy(),
1387 location.line(),
1388 location.column()
1389 );
1390 }
1391 }
1392 });
1393 cx.paint_quad(crate::outline(
1394 crate::Bounds {
1395 origin: bounds.origin
1396 + crate::point(
1397 crate::px(0.),
1398 FONT_SIZE - px(2.),
1399 ),
1400 size: crate::Size {
1401 width: text_bounds.size.width,
1402 height: crate::px(1.),
1403 },
1404 },
1405 crate::red(),
1406 ))
1407 }
1408 }
1409 };
1410
1411 cx.with_z_index(1, |cx| {
1412 cx.with_text_style(
1413 Some(crate::TextStyleRefinement {
1414 color: Some(crate::red()),
1415 line_height: Some(FONT_SIZE.into()),
1416 background_color: Some(crate::white()),
1417 ..Default::default()
1418 }),
1419 render_debug_text,
1420 )
1421 });
1422 }
1423
1424 let interactive_bounds = InteractiveBounds {
1425 bounds: bounds.intersect(&cx.content_mask().bounds),
1426 stacking_order: cx.stacking_order().clone(),
1427 };
1428
1429 if self.block_mouse
1430 || style.background.as_ref().is_some_and(|fill| {
1431 fill.color().is_some_and(|color| !color.is_transparent())
1432 })
1433 {
1434 cx.add_opaque_layer(interactive_bounds.bounds);
1435 }
1436
1437 if !cx.has_active_drag() {
1438 if let Some(mouse_cursor) = style.mouse_cursor {
1439 let mouse_position = &cx.mouse_position();
1440 let hovered =
1441 interactive_bounds.visibly_contains(mouse_position, cx);
1442 if hovered {
1443 cx.set_cursor_style(mouse_cursor);
1444 }
1445 }
1446 }
1447
1448 // If this element can be focused, register a mouse down listener
1449 // that will automatically transfer focus when hitting the element.
1450 // This behavior can be suppressed by using `cx.prevent_default()`.
1451 if let Some(focus_handle) = element_state.focus_handle.clone() {
1452 cx.on_mouse_event({
1453 let interactive_bounds = interactive_bounds.clone();
1454 move |event: &MouseDownEvent, phase, cx| {
1455 if phase == DispatchPhase::Bubble
1456 && !cx.default_prevented()
1457 && interactive_bounds.visibly_contains(&event.position, cx)
1458 {
1459 cx.focus(&focus_handle);
1460 // If there is a parent that is also focusable, prevent it
1461 // from transferring focus because we already did so.
1462 cx.prevent_default();
1463 }
1464 }
1465 });
1466 }
1467
1468 for listener in self.mouse_down_listeners.drain(..) {
1469 let interactive_bounds = interactive_bounds.clone();
1470 cx.on_mouse_event(move |event: &MouseDownEvent, phase, cx| {
1471 listener(event, &interactive_bounds, phase, cx);
1472 })
1473 }
1474
1475 for listener in self.mouse_up_listeners.drain(..) {
1476 let interactive_bounds = interactive_bounds.clone();
1477 cx.on_mouse_event(move |event: &MouseUpEvent, phase, cx| {
1478 listener(event, &interactive_bounds, phase, cx);
1479 })
1480 }
1481
1482 for listener in self.mouse_move_listeners.drain(..) {
1483 let interactive_bounds = interactive_bounds.clone();
1484 cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
1485 listener(event, &interactive_bounds, phase, cx);
1486 })
1487 }
1488
1489 for listener in self.scroll_wheel_listeners.drain(..) {
1490 let interactive_bounds = interactive_bounds.clone();
1491 cx.on_mouse_event(move |event: &ScrollWheelEvent, phase, cx| {
1492 listener(event, &interactive_bounds, phase, cx);
1493 })
1494 }
1495
1496 paint_hover_group_handler(cx);
1497
1498 if self.hover_style.is_some()
1499 || self.base_style.mouse_cursor.is_some()
1500 || cx.active_drag.is_some() && !self.drag_over_styles.is_empty()
1501 {
1502 let bounds = bounds.intersect(&cx.content_mask().bounds);
1503 let hovered = bounds.contains(&cx.mouse_position());
1504 cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
1505 if phase == DispatchPhase::Capture
1506 && bounds.contains(&event.position) != hovered
1507 {
1508 cx.refresh();
1509 }
1510 });
1511 }
1512
1513 let mut drag_listener = mem::take(&mut self.drag_listener);
1514 let drop_listeners = mem::take(&mut self.drop_listeners);
1515 let click_listeners = mem::take(&mut self.click_listeners);
1516 let can_drop_predicate = mem::take(&mut self.can_drop_predicate);
1517
1518 if !drop_listeners.is_empty() {
1519 cx.on_mouse_event({
1520 let interactive_bounds = interactive_bounds.clone();
1521 move |event: &MouseUpEvent, phase, cx| {
1522 if let Some(drag) = &cx.active_drag {
1523 if phase == DispatchPhase::Bubble
1524 && interactive_bounds
1525 .drag_target_contains(&event.position, cx)
1526 {
1527 let drag_state_type = drag.value.as_ref().type_id();
1528 for (drop_state_type, listener) in &drop_listeners {
1529 if *drop_state_type == drag_state_type {
1530 let drag = cx.active_drag.take().expect(
1531 "checked for type drag state type above",
1532 );
1533
1534 let mut can_drop = true;
1535 if let Some(predicate) = &can_drop_predicate {
1536 can_drop = predicate(
1537 drag.value.as_ref(),
1538 cx.deref_mut(),
1539 );
1540 }
1541
1542 if can_drop {
1543 listener(
1544 drag.value.as_ref(),
1545 cx.deref_mut(),
1546 );
1547 cx.refresh();
1548 cx.stop_propagation();
1549 }
1550 }
1551 }
1552 }
1553 }
1554 }
1555 });
1556 }
1557
1558 if !click_listeners.is_empty() || drag_listener.is_some() {
1559 let pending_mouse_down = element_state
1560 .pending_mouse_down
1561 .get_or_insert_with(Default::default)
1562 .clone();
1563
1564 let clicked_state = element_state
1565 .clicked_state
1566 .get_or_insert_with(Default::default)
1567 .clone();
1568
1569 cx.on_mouse_event({
1570 let interactive_bounds = interactive_bounds.clone();
1571 let pending_mouse_down = pending_mouse_down.clone();
1572 move |event: &MouseDownEvent, phase, cx| {
1573 if phase == DispatchPhase::Bubble
1574 && event.button == MouseButton::Left
1575 && interactive_bounds.visibly_contains(&event.position, cx)
1576 {
1577 *pending_mouse_down.borrow_mut() = Some(event.clone());
1578 cx.refresh();
1579 }
1580 }
1581 });
1582
1583 cx.on_mouse_event({
1584 let pending_mouse_down = pending_mouse_down.clone();
1585 move |event: &MouseMoveEvent, phase, cx| {
1586 if phase == DispatchPhase::Capture {
1587 return;
1588 }
1589
1590 let mut pending_mouse_down = pending_mouse_down.borrow_mut();
1591 if let Some(mouse_down) = pending_mouse_down.clone() {
1592 if !cx.has_active_drag()
1593 && (event.position - mouse_down.position).magnitude()
1594 > DRAG_THRESHOLD
1595 {
1596 if let Some((drag_value, drag_listener)) =
1597 drag_listener.take()
1598 {
1599 *clicked_state.borrow_mut() =
1600 ElementClickedState::default();
1601 let cursor_offset = event.position - bounds.origin;
1602 let drag = (drag_listener)(drag_value.as_ref(), cx);
1603 cx.active_drag = Some(AnyDrag {
1604 view: drag,
1605 value: drag_value,
1606 cursor_offset,
1607 });
1608 pending_mouse_down.take();
1609 cx.refresh();
1610 cx.stop_propagation();
1611 }
1612 }
1613 }
1614 }
1615 });
1616
1617 cx.on_mouse_event({
1618 let interactive_bounds = interactive_bounds.clone();
1619 let mut captured_mouse_down = None;
1620 move |event: &MouseUpEvent, phase, cx| match phase {
1621 // Clear the pending mouse down during the capture phase,
1622 // so that it happens even if another event handler stops
1623 // propagation.
1624 DispatchPhase::Capture => {
1625 let mut pending_mouse_down =
1626 pending_mouse_down.borrow_mut();
1627 if pending_mouse_down.is_some() {
1628 captured_mouse_down = pending_mouse_down.take();
1629 cx.refresh();
1630 }
1631 }
1632 // Fire click handlers during the bubble phase.
1633 DispatchPhase::Bubble => {
1634 if let Some(mouse_down) = captured_mouse_down.take() {
1635 if interactive_bounds
1636 .visibly_contains(&event.position, cx)
1637 {
1638 let mouse_click = ClickEvent {
1639 down: mouse_down,
1640 up: event.clone(),
1641 };
1642 for listener in &click_listeners {
1643 listener(&mouse_click, cx);
1644 }
1645 }
1646 }
1647 }
1648 }
1649 });
1650 }
1651
1652 if let Some(hover_listener) = self.hover_listener.take() {
1653 let was_hovered = element_state
1654 .hover_state
1655 .get_or_insert_with(Default::default)
1656 .clone();
1657 let has_mouse_down = element_state
1658 .pending_mouse_down
1659 .get_or_insert_with(Default::default)
1660 .clone();
1661 let interactive_bounds = interactive_bounds.clone();
1662
1663 cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
1664 if phase != DispatchPhase::Bubble {
1665 return;
1666 }
1667 let is_hovered = interactive_bounds
1668 .visibly_contains(&event.position, cx)
1669 && has_mouse_down.borrow().is_none()
1670 && !cx.has_active_drag();
1671 let mut was_hovered = was_hovered.borrow_mut();
1672
1673 if is_hovered != *was_hovered {
1674 *was_hovered = is_hovered;
1675 drop(was_hovered);
1676
1677 hover_listener(&is_hovered, cx.deref_mut());
1678 }
1679 });
1680 }
1681
1682 if let Some(tooltip_builder) = self.tooltip_builder.take() {
1683 let active_tooltip = element_state
1684 .active_tooltip
1685 .get_or_insert_with(Default::default)
1686 .clone();
1687 let pending_mouse_down = element_state
1688 .pending_mouse_down
1689 .get_or_insert_with(Default::default)
1690 .clone();
1691 let interactive_bounds = interactive_bounds.clone();
1692
1693 cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
1694 let is_hovered = interactive_bounds
1695 .visibly_contains(&event.position, cx)
1696 && pending_mouse_down.borrow().is_none();
1697 if !is_hovered {
1698 active_tooltip.borrow_mut().take();
1699 return;
1700 }
1701
1702 if phase != DispatchPhase::Bubble {
1703 return;
1704 }
1705
1706 if active_tooltip.borrow().is_none() {
1707 let task = cx.spawn({
1708 let active_tooltip = active_tooltip.clone();
1709 let tooltip_builder = tooltip_builder.clone();
1710
1711 move |mut cx| async move {
1712 cx.background_executor().timer(TOOLTIP_DELAY).await;
1713 cx.update(|cx| {
1714 active_tooltip.borrow_mut().replace(
1715 ActiveTooltip {
1716 tooltip: Some(AnyTooltip {
1717 view: tooltip_builder(cx),
1718 cursor_offset: cx.mouse_position(),
1719 }),
1720 _task: None,
1721 },
1722 );
1723 cx.refresh();
1724 })
1725 .ok();
1726 }
1727 });
1728 active_tooltip.borrow_mut().replace(ActiveTooltip {
1729 tooltip: None,
1730 _task: Some(task),
1731 });
1732 }
1733 });
1734
1735 let active_tooltip = element_state
1736 .active_tooltip
1737 .get_or_insert_with(Default::default)
1738 .clone();
1739 cx.on_mouse_event(move |_: &MouseDownEvent, _, _| {
1740 active_tooltip.borrow_mut().take();
1741 });
1742
1743 if let Some(active_tooltip) = element_state
1744 .active_tooltip
1745 .get_or_insert_with(Default::default)
1746 .borrow()
1747 .as_ref()
1748 {
1749 if let Some(tooltip) = active_tooltip.tooltip.clone() {
1750 cx.set_tooltip(tooltip);
1751 }
1752 }
1753 }
1754
1755 let active_state = element_state
1756 .clicked_state
1757 .get_or_insert_with(Default::default)
1758 .clone();
1759 if active_state.borrow().is_clicked() {
1760 cx.on_mouse_event(move |_: &MouseUpEvent, phase, cx| {
1761 if phase == DispatchPhase::Capture {
1762 *active_state.borrow_mut() = ElementClickedState::default();
1763 cx.refresh();
1764 }
1765 });
1766 } else {
1767 let active_group_bounds = self
1768 .group_active_style
1769 .as_ref()
1770 .and_then(|group_active| GroupBounds::get(&group_active.group, cx));
1771 let interactive_bounds = interactive_bounds.clone();
1772 cx.on_mouse_event(move |down: &MouseDownEvent, phase, cx| {
1773 if phase == DispatchPhase::Bubble && !cx.default_prevented() {
1774 let group = active_group_bounds
1775 .map_or(false, |bounds| bounds.contains(&down.position));
1776 let element =
1777 interactive_bounds.visibly_contains(&down.position, cx);
1778 if group || element {
1779 *active_state.borrow_mut() =
1780 ElementClickedState { group, element };
1781 cx.refresh();
1782 }
1783 }
1784 });
1785 }
1786
1787 let overflow = style.overflow;
1788 if overflow.x == Overflow::Scroll || overflow.y == Overflow::Scroll {
1789 if let Some(scroll_handle) = &self.scroll_handle {
1790 scroll_handle.0.borrow_mut().overflow = overflow;
1791 }
1792
1793 let scroll_offset = element_state
1794 .scroll_offset
1795 .get_or_insert_with(Rc::default)
1796 .clone();
1797 let line_height = cx.line_height();
1798 let rem_size = cx.rem_size();
1799 let padding_size = size(
1800 style
1801 .padding
1802 .left
1803 .to_pixels(bounds.size.width.into(), rem_size)
1804 + style
1805 .padding
1806 .right
1807 .to_pixels(bounds.size.width.into(), rem_size),
1808 style
1809 .padding
1810 .top
1811 .to_pixels(bounds.size.height.into(), rem_size)
1812 + style
1813 .padding
1814 .bottom
1815 .to_pixels(bounds.size.height.into(), rem_size),
1816 );
1817 let scroll_max =
1818 (content_size + padding_size - bounds.size).max(&Size::default());
1819 // Clamp scroll offset in case scroll max is smaller now (e.g., if children
1820 // were removed or the bounds became larger).
1821 {
1822 let mut scroll_offset = scroll_offset.borrow_mut();
1823 scroll_offset.x = scroll_offset.x.clamp(-scroll_max.width, px(0.));
1824 scroll_offset.y = scroll_offset.y.clamp(-scroll_max.height, px(0.));
1825 }
1826
1827 let interactive_bounds = interactive_bounds.clone();
1828 cx.on_mouse_event(move |event: &ScrollWheelEvent, phase, cx| {
1829 if phase == DispatchPhase::Bubble
1830 && interactive_bounds.visibly_contains(&event.position, cx)
1831 {
1832 let mut scroll_offset = scroll_offset.borrow_mut();
1833 let old_scroll_offset = *scroll_offset;
1834 let delta = event.delta.pixel_delta(line_height);
1835
1836 if overflow.x == Overflow::Scroll {
1837 let mut delta_x = Pixels::ZERO;
1838 if !delta.x.is_zero() {
1839 delta_x = delta.x;
1840 } else if overflow.y != Overflow::Scroll {
1841 delta_x = delta.y;
1842 }
1843
1844 scroll_offset.x = (scroll_offset.x + delta_x)
1845 .clamp(-scroll_max.width, px(0.));
1846 }
1847
1848 if overflow.y == Overflow::Scroll {
1849 let mut delta_y = Pixels::ZERO;
1850 if !delta.y.is_zero() {
1851 delta_y = delta.y;
1852 } else if overflow.x != Overflow::Scroll {
1853 delta_y = delta.x;
1854 }
1855
1856 scroll_offset.y = (scroll_offset.y + delta_y)
1857 .clamp(-scroll_max.height, px(0.));
1858 }
1859
1860 if *scroll_offset != old_scroll_offset {
1861 cx.refresh();
1862 cx.stop_propagation();
1863 }
1864 }
1865 });
1866 }
1867
1868 if let Some(group) = self.group.clone() {
1869 GroupBounds::push(group, bounds, cx);
1870 }
1871
1872 let scroll_offset = element_state
1873 .scroll_offset
1874 .as_ref()
1875 .map(|scroll_offset| *scroll_offset.borrow());
1876
1877 let key_down_listeners = mem::take(&mut self.key_down_listeners);
1878 let key_up_listeners = mem::take(&mut self.key_up_listeners);
1879 let action_listeners = mem::take(&mut self.action_listeners);
1880 cx.with_key_dispatch(
1881 self.key_context.clone(),
1882 element_state.focus_handle.clone(),
1883 |_, cx| {
1884 for listener in key_down_listeners {
1885 cx.on_key_event(move |event: &KeyDownEvent, phase, cx| {
1886 listener(event, phase, cx);
1887 })
1888 }
1889
1890 for listener in key_up_listeners {
1891 cx.on_key_event(move |event: &KeyUpEvent, phase, cx| {
1892 listener(event, phase, cx);
1893 })
1894 }
1895
1896 for (action_type, listener) in action_listeners {
1897 cx.on_action(action_type, listener)
1898 }
1899
1900 f(&style, scroll_offset.unwrap_or_default(), cx)
1901 },
1902 );
1903
1904 if let Some(group) = self.group.as_ref() {
1905 GroupBounds::pop(group, cx);
1906 }
1907 });
1908 });
1909 });
1910 });
1911 }
1912
1913 /// Compute the visual style for this element, based on the current bounds and the element's state.
1914 pub fn compute_style(
1915 &self,
1916 bounds: Option<Bounds<Pixels>>,
1917 element_state: &mut InteractiveElementState,
1918 cx: &mut ElementContext,
1919 ) -> Style {
1920 let mut style = Style::default();
1921 style.refine(&self.base_style);
1922
1923 cx.with_z_index(style.z_index.unwrap_or(0), |cx| {
1924 if let Some(focus_handle) = self.tracked_focus_handle.as_ref() {
1925 if let Some(in_focus_style) = self.in_focus_style.as_ref() {
1926 if focus_handle.within_focused(cx) {
1927 style.refine(in_focus_style);
1928 }
1929 }
1930
1931 if let Some(focus_style) = self.focus_style.as_ref() {
1932 if focus_handle.is_focused(cx) {
1933 style.refine(focus_style);
1934 }
1935 }
1936 }
1937
1938 if let Some(bounds) = bounds {
1939 let mouse_position = cx.mouse_position();
1940 if !cx.has_active_drag() {
1941 if let Some(group_hover) = self.group_hover_style.as_ref() {
1942 if let Some(group_bounds) =
1943 GroupBounds::get(&group_hover.group, cx.deref_mut())
1944 {
1945 if group_bounds.contains(&mouse_position)
1946 && cx.was_top_layer(&mouse_position, cx.stacking_order())
1947 {
1948 style.refine(&group_hover.style);
1949 }
1950 }
1951 }
1952
1953 if let Some(hover_style) = self.hover_style.as_ref() {
1954 if bounds
1955 .intersect(&cx.content_mask().bounds)
1956 .contains(&mouse_position)
1957 && cx.was_top_layer(&mouse_position, cx.stacking_order())
1958 {
1959 style.refine(hover_style);
1960 }
1961 }
1962 }
1963
1964 if let Some(drag) = cx.active_drag.take() {
1965 let mut can_drop = true;
1966 if let Some(can_drop_predicate) = &self.can_drop_predicate {
1967 can_drop = can_drop_predicate(drag.value.as_ref(), cx.deref_mut());
1968 }
1969
1970 if can_drop {
1971 for (state_type, group_drag_style) in &self.group_drag_over_styles {
1972 if let Some(group_bounds) =
1973 GroupBounds::get(&group_drag_style.group, cx.deref_mut())
1974 {
1975 if *state_type == drag.value.as_ref().type_id()
1976 && group_bounds.contains(&mouse_position)
1977 {
1978 style.refine(&group_drag_style.style);
1979 }
1980 }
1981 }
1982
1983 for (state_type, drag_over_style) in &self.drag_over_styles {
1984 if *state_type == drag.value.as_ref().type_id()
1985 && bounds
1986 .intersect(&cx.content_mask().bounds)
1987 .contains(&mouse_position)
1988 && cx.was_top_layer_under_active_drag(
1989 &mouse_position,
1990 cx.stacking_order(),
1991 )
1992 {
1993 style.refine(drag_over_style);
1994 }
1995 }
1996 }
1997
1998 cx.active_drag = Some(drag);
1999 }
2000 }
2001
2002 let clicked_state = element_state
2003 .clicked_state
2004 .get_or_insert_with(Default::default)
2005 .borrow();
2006 if clicked_state.group {
2007 if let Some(group) = self.group_active_style.as_ref() {
2008 style.refine(&group.style)
2009 }
2010 }
2011
2012 if let Some(active_style) = self.active_style.as_ref() {
2013 if clicked_state.element {
2014 style.refine(active_style)
2015 }
2016 }
2017 });
2018
2019 style
2020 }
2021}
2022
2023/// The per-frame state of an interactive element. Used for tracking stateful interactions like clicks
2024/// and scroll offsets.
2025#[derive(Default)]
2026pub struct InteractiveElementState {
2027 pub(crate) focus_handle: Option<FocusHandle>,
2028 pub(crate) clicked_state: Option<Rc<RefCell<ElementClickedState>>>,
2029 pub(crate) hover_state: Option<Rc<RefCell<bool>>>,
2030 pub(crate) pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
2031 pub(crate) scroll_offset: Option<Rc<RefCell<Point<Pixels>>>>,
2032 pub(crate) active_tooltip: Option<Rc<RefCell<Option<ActiveTooltip>>>>,
2033}
2034
2035/// The current active tooltip
2036pub struct ActiveTooltip {
2037 pub(crate) tooltip: Option<AnyTooltip>,
2038 pub(crate) _task: Option<Task<()>>,
2039}
2040
2041/// Whether or not the element or a group that contains it is clicked by the mouse.
2042#[derive(Copy, Clone, Default, Eq, PartialEq)]
2043pub struct ElementClickedState {
2044 /// True if this element's group has been clicked, false otherwise
2045 pub group: bool,
2046
2047 /// True if this element has been clicked, false otherwise
2048 pub element: bool,
2049}
2050
2051impl ElementClickedState {
2052 fn is_clicked(&self) -> bool {
2053 self.group || self.element
2054 }
2055}
2056
2057#[derive(Default)]
2058pub(crate) struct GroupBounds(HashMap<SharedString, SmallVec<[Bounds<Pixels>; 1]>>);
2059
2060impl GroupBounds {
2061 pub fn get(name: &SharedString, cx: &mut AppContext) -> Option<Bounds<Pixels>> {
2062 cx.default_global::<Self>()
2063 .0
2064 .get(name)
2065 .and_then(|bounds_stack| bounds_stack.last())
2066 .cloned()
2067 }
2068
2069 pub fn push(name: SharedString, bounds: Bounds<Pixels>, cx: &mut AppContext) {
2070 cx.default_global::<Self>()
2071 .0
2072 .entry(name)
2073 .or_default()
2074 .push(bounds);
2075 }
2076
2077 pub fn pop(name: &SharedString, cx: &mut AppContext) {
2078 cx.default_global::<Self>().0.get_mut(name).unwrap().pop();
2079 }
2080}
2081
2082/// A wrapper around an element that can be focused.
2083pub struct Focusable<E> {
2084 /// The element that is focusable
2085 pub element: E,
2086}
2087
2088impl<E: InteractiveElement> FocusableElement for Focusable<E> {}
2089
2090impl<E> InteractiveElement for Focusable<E>
2091where
2092 E: InteractiveElement,
2093{
2094 fn interactivity(&mut self) -> &mut Interactivity {
2095 self.element.interactivity()
2096 }
2097}
2098
2099impl<E: StatefulInteractiveElement> StatefulInteractiveElement for Focusable<E> {}
2100
2101impl<E> Styled for Focusable<E>
2102where
2103 E: Styled,
2104{
2105 fn style(&mut self) -> &mut StyleRefinement {
2106 self.element.style()
2107 }
2108}
2109
2110impl<E> Element for Focusable<E>
2111where
2112 E: Element,
2113{
2114 type State = E::State;
2115
2116 fn request_layout(
2117 &mut self,
2118 state: Option<Self::State>,
2119 cx: &mut ElementContext,
2120 ) -> (LayoutId, Self::State) {
2121 self.element.request_layout(state, cx)
2122 }
2123
2124 fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut ElementContext) {
2125 self.element.paint(bounds, state, cx)
2126 }
2127}
2128
2129impl<E> IntoElement for Focusable<E>
2130where
2131 E: IntoElement,
2132{
2133 type Element = E::Element;
2134
2135 fn element_id(&self) -> Option<ElementId> {
2136 self.element.element_id()
2137 }
2138
2139 fn into_element(self) -> Self::Element {
2140 self.element.into_element()
2141 }
2142}
2143
2144impl<E> ParentElement for Focusable<E>
2145where
2146 E: ParentElement,
2147{
2148 fn extend(&mut self, elements: impl Iterator<Item = AnyElement>) {
2149 self.element.extend(elements)
2150 }
2151}
2152
2153/// A wrapper around an element that can store state, produced after assigning an ElementId.
2154pub struct Stateful<E> {
2155 element: E,
2156}
2157
2158impl<E> Styled for Stateful<E>
2159where
2160 E: Styled,
2161{
2162 fn style(&mut self) -> &mut StyleRefinement {
2163 self.element.style()
2164 }
2165}
2166
2167impl<E> StatefulInteractiveElement for Stateful<E>
2168where
2169 E: Element,
2170 Self: InteractiveElement,
2171{
2172}
2173
2174impl<E> InteractiveElement for Stateful<E>
2175where
2176 E: InteractiveElement,
2177{
2178 fn interactivity(&mut self) -> &mut Interactivity {
2179 self.element.interactivity()
2180 }
2181}
2182
2183impl<E: FocusableElement> FocusableElement for Stateful<E> {}
2184
2185impl<E> Element for Stateful<E>
2186where
2187 E: Element,
2188{
2189 type State = E::State;
2190
2191 fn request_layout(
2192 &mut self,
2193 state: Option<Self::State>,
2194 cx: &mut ElementContext,
2195 ) -> (LayoutId, Self::State) {
2196 self.element.request_layout(state, cx)
2197 }
2198
2199 fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut ElementContext) {
2200 self.element.paint(bounds, state, cx)
2201 }
2202}
2203
2204impl<E> IntoElement for Stateful<E>
2205where
2206 E: Element,
2207{
2208 type Element = Self;
2209
2210 fn element_id(&self) -> Option<ElementId> {
2211 self.element.element_id()
2212 }
2213
2214 fn into_element(self) -> Self::Element {
2215 self
2216 }
2217}
2218
2219impl<E> ParentElement for Stateful<E>
2220where
2221 E: ParentElement,
2222{
2223 fn extend(&mut self, elements: impl Iterator<Item = AnyElement>) {
2224 self.element.extend(elements)
2225 }
2226}
2227
2228#[derive(Default)]
2229struct ScrollHandleState {
2230 offset: Rc<RefCell<Point<Pixels>>>,
2231 bounds: Bounds<Pixels>,
2232 child_bounds: Vec<Bounds<Pixels>>,
2233 requested_scroll_top: Option<(usize, Pixels)>,
2234 overflow: Point<Overflow>,
2235}
2236
2237/// A handle to the scrollable aspects of an element.
2238/// Used for accessing scroll state, like the current scroll offset,
2239/// and for mutating the scroll state, like scrolling to a specific child.
2240#[derive(Clone)]
2241pub struct ScrollHandle(Rc<RefCell<ScrollHandleState>>);
2242
2243impl Default for ScrollHandle {
2244 fn default() -> Self {
2245 Self::new()
2246 }
2247}
2248
2249impl ScrollHandle {
2250 /// Construct a new scroll handle.
2251 pub fn new() -> Self {
2252 Self(Rc::default())
2253 }
2254
2255 /// Get the current scroll offset.
2256 pub fn offset(&self) -> Point<Pixels> {
2257 *self.0.borrow().offset.borrow()
2258 }
2259
2260 /// Get the top child that's scrolled into view.
2261 pub fn top_item(&self) -> usize {
2262 let state = self.0.borrow();
2263 let top = state.bounds.top() - state.offset.borrow().y;
2264
2265 match state.child_bounds.binary_search_by(|bounds| {
2266 if top < bounds.top() {
2267 Ordering::Greater
2268 } else if top > bounds.bottom() {
2269 Ordering::Less
2270 } else {
2271 Ordering::Equal
2272 }
2273 }) {
2274 Ok(ix) => ix,
2275 Err(ix) => ix.min(state.child_bounds.len().saturating_sub(1)),
2276 }
2277 }
2278
2279 /// Get the bounds for a specific child.
2280 pub fn bounds_for_item(&self, ix: usize) -> Option<Bounds<Pixels>> {
2281 self.0.borrow().child_bounds.get(ix).cloned()
2282 }
2283
2284 /// scroll_to_item scrolls the minimal amount to ensure that the child is
2285 /// fully visible
2286 pub fn scroll_to_item(&self, ix: usize) {
2287 let state = self.0.borrow();
2288
2289 let Some(bounds) = state.child_bounds.get(ix) else {
2290 return;
2291 };
2292
2293 let mut scroll_offset = state.offset.borrow_mut();
2294
2295 if state.overflow.y == Overflow::Scroll {
2296 if bounds.top() + scroll_offset.y < state.bounds.top() {
2297 scroll_offset.y = state.bounds.top() - bounds.top();
2298 } else if bounds.bottom() + scroll_offset.y > state.bounds.bottom() {
2299 scroll_offset.y = state.bounds.bottom() - bounds.bottom();
2300 }
2301 }
2302
2303 if state.overflow.x == Overflow::Scroll {
2304 if bounds.left() + scroll_offset.x < state.bounds.left() {
2305 scroll_offset.x = state.bounds.left() - bounds.left();
2306 } else if bounds.right() + scroll_offset.x > state.bounds.right() {
2307 scroll_offset.x = state.bounds.right() - bounds.right();
2308 }
2309 }
2310 }
2311
2312 /// Get the logical scroll top, based on a child index and a pixel offset.
2313 pub fn logical_scroll_top(&self) -> (usize, Pixels) {
2314 let ix = self.top_item();
2315 let state = self.0.borrow();
2316
2317 if let Some(child_bounds) = state.child_bounds.get(ix) {
2318 (
2319 ix,
2320 child_bounds.top() + state.offset.borrow().y - state.bounds.top(),
2321 )
2322 } else {
2323 (ix, px(0.))
2324 }
2325 }
2326
2327 /// Set the logical scroll top, based on a child index and a pixel offset.
2328 pub fn set_logical_scroll_top(&self, ix: usize, px: Pixels) {
2329 self.0.borrow_mut().requested_scroll_top = Some((ix, px));
2330 }
2331}