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, Global,
21 IntoElement, IsZero, KeyContext, KeyDownEvent, KeyUpEvent, LayoutId, MouseButton,
22 MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, Point, Render,
23 ScrollWheelEvent, SharedString, Size, StackingOrder, Style, StyleRefinement, Styled, Task,
24 View, Visibility, 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 its 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 element an ID, so that it can be used with interactivity
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 its 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>(
786 mut self,
787 f: impl 'static + Fn(StyleRefinement, &S, &WindowContext) -> StyleRefinement,
788 ) -> Self {
789 self.interactivity().drag_over_styles.push((
790 TypeId::of::<S>(),
791 Box::new(move |currently_dragged: &dyn Any, cx| {
792 f(
793 StyleRefinement::default(),
794 currently_dragged.downcast_ref::<S>().unwrap(),
795 cx,
796 )
797 }),
798 ));
799 self
800 }
801
802 /// Apply the given style when the given data type is dragged over this element's group
803 fn group_drag_over<S: 'static>(
804 mut self,
805 group_name: impl Into<SharedString>,
806 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
807 ) -> Self {
808 self.interactivity().group_drag_over_styles.push((
809 TypeId::of::<S>(),
810 GroupStyle {
811 group: group_name.into(),
812 style: Box::new(f(StyleRefinement::default())),
813 },
814 ));
815 self
816 }
817
818 /// Bind the given callback to drop events of the given type, whether or not the drag started on this element
819 /// The fluent API equivalent to [`Interactivity::on_drop`]
820 ///
821 /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
822 fn on_drop<T: 'static>(mut self, listener: impl Fn(&T, &mut WindowContext) + 'static) -> Self {
823 self.interactivity().on_drop(listener);
824 self
825 }
826
827 /// Use the given predicate to determine whether or not a drop event should be dispatched to this element
828 /// The fluent API equivalent to [`Interactivity::can_drop`]
829 fn can_drop(
830 mut self,
831 predicate: impl Fn(&dyn Any, &mut WindowContext) -> bool + 'static,
832 ) -> Self {
833 self.interactivity().can_drop(predicate);
834 self
835 }
836
837 /// Block the mouse from interacting with this element or any of its children
838 /// The fluent API equivalent to [`Interactivity::block_mouse`]
839 fn block_mouse(mut self) -> Self {
840 self.interactivity().block_mouse();
841 self
842 }
843}
844
845/// A trait for elements that want to use the standard GPUI interactivity features
846/// that require state.
847pub trait StatefulInteractiveElement: InteractiveElement {
848 /// Set this element to focusable.
849 fn focusable(mut self) -> Focusable<Self> {
850 self.interactivity().focusable = true;
851 Focusable { element: self }
852 }
853
854 /// Set the overflow x and y to scroll.
855 fn overflow_scroll(mut self) -> Self {
856 self.interactivity().base_style.overflow.x = Some(Overflow::Scroll);
857 self.interactivity().base_style.overflow.y = Some(Overflow::Scroll);
858 self
859 }
860
861 /// Set the overflow x to scroll.
862 fn overflow_x_scroll(mut self) -> Self {
863 self.interactivity().base_style.overflow.x = Some(Overflow::Scroll);
864 self
865 }
866
867 /// Set the overflow y to scroll.
868 fn overflow_y_scroll(mut self) -> Self {
869 self.interactivity().base_style.overflow.y = Some(Overflow::Scroll);
870 self
871 }
872
873 /// Track the scroll state of this element with the given handle.
874 fn track_scroll(mut self, scroll_handle: &ScrollHandle) -> Self {
875 self.interactivity().scroll_handle = Some(scroll_handle.clone());
876 self
877 }
878
879 /// Set the given styles to be applied when this element is active.
880 fn active(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
881 where
882 Self: Sized,
883 {
884 self.interactivity().active_style = Some(Box::new(f(StyleRefinement::default())));
885 self
886 }
887
888 /// Set the given styles to be applied when this element's group is active.
889 fn group_active(
890 mut self,
891 group_name: impl Into<SharedString>,
892 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
893 ) -> Self
894 where
895 Self: Sized,
896 {
897 self.interactivity().group_active_style = Some(GroupStyle {
898 group: group_name.into(),
899 style: Box::new(f(StyleRefinement::default())),
900 });
901 self
902 }
903
904 /// Bind the given callback to click events of this element
905 /// The fluent API equivalent to [`Interactivity::on_click`]
906 ///
907 /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
908 fn on_click(mut self, listener: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self
909 where
910 Self: Sized,
911 {
912 self.interactivity().on_click(listener);
913 self
914 }
915
916 /// On drag initiation, this callback will be used to create a new view to render the dragged value for a
917 /// drag and drop operation. This API should also be used as the equivalent of 'on drag start' with
918 /// the [`Self::on_drag_move`] API
919 /// The fluent API equivalent to [`Interactivity::on_drag`]
920 ///
921 /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
922 fn on_drag<T, W>(
923 mut self,
924 value: T,
925 constructor: impl Fn(&T, &mut WindowContext) -> View<W> + 'static,
926 ) -> Self
927 where
928 Self: Sized,
929 T: 'static,
930 W: 'static + Render,
931 {
932 self.interactivity().on_drag(value, constructor);
933 self
934 }
935
936 /// Bind the given callback on the hover start and end events of this element. Note that the boolean
937 /// passed to the callback is true when the hover starts and false when it ends.
938 /// The fluent API equivalent to [`Interactivity::on_hover`]
939 ///
940 /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
941 fn on_hover(mut self, listener: impl Fn(&bool, &mut WindowContext) + 'static) -> Self
942 where
943 Self: Sized,
944 {
945 self.interactivity().on_hover(listener);
946 self
947 }
948
949 /// Use the given callback to construct a new tooltip view when the mouse hovers over this element.
950 /// The fluent API equivalent to [`Interactivity::tooltip`]
951 fn tooltip(mut self, build_tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self
952 where
953 Self: Sized,
954 {
955 self.interactivity().tooltip(build_tooltip);
956 self
957 }
958}
959
960/// A trait for providing focus related APIs to interactive elements
961pub trait FocusableElement: InteractiveElement {
962 /// Set the given styles to be applied when this element, specifically, is focused.
963 fn focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
964 where
965 Self: Sized,
966 {
967 self.interactivity().focus_style = Some(Box::new(f(StyleRefinement::default())));
968 self
969 }
970
971 /// Set the given styles to be applied when this element is inside another element that is focused.
972 fn in_focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
973 where
974 Self: Sized,
975 {
976 self.interactivity().in_focus_style = Some(Box::new(f(StyleRefinement::default())));
977 self
978 }
979}
980
981pub(crate) type MouseDownListener =
982 Box<dyn Fn(&MouseDownEvent, &InteractiveBounds, DispatchPhase, &mut WindowContext) + 'static>;
983pub(crate) type MouseUpListener =
984 Box<dyn Fn(&MouseUpEvent, &InteractiveBounds, DispatchPhase, &mut WindowContext) + 'static>;
985
986pub(crate) type MouseMoveListener =
987 Box<dyn Fn(&MouseMoveEvent, &InteractiveBounds, DispatchPhase, &mut WindowContext) + 'static>;
988
989pub(crate) type ScrollWheelListener =
990 Box<dyn Fn(&ScrollWheelEvent, &InteractiveBounds, DispatchPhase, &mut WindowContext) + 'static>;
991
992pub(crate) type ClickListener = Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>;
993
994pub(crate) type DragListener = Box<dyn Fn(&dyn Any, &mut WindowContext) -> AnyView + 'static>;
995
996type DropListener = Box<dyn Fn(&dyn Any, &mut WindowContext) + 'static>;
997
998type CanDropPredicate = Box<dyn Fn(&dyn Any, &mut WindowContext) -> bool + 'static>;
999
1000pub(crate) type TooltipBuilder = Rc<dyn Fn(&mut WindowContext) -> AnyView + 'static>;
1001
1002pub(crate) type KeyDownListener =
1003 Box<dyn Fn(&KeyDownEvent, DispatchPhase, &mut WindowContext) + 'static>;
1004
1005pub(crate) type KeyUpListener =
1006 Box<dyn Fn(&KeyUpEvent, DispatchPhase, &mut WindowContext) + 'static>;
1007
1008pub(crate) type ActionListener = Box<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext) + 'static>;
1009
1010/// Construct a new [`Div`] element
1011#[track_caller]
1012pub fn div() -> Div {
1013 #[cfg(debug_assertions)]
1014 let interactivity = Interactivity {
1015 location: Some(*core::panic::Location::caller()),
1016 ..Default::default()
1017 };
1018
1019 #[cfg(not(debug_assertions))]
1020 let interactivity = Interactivity::default();
1021
1022 Div {
1023 interactivity,
1024 children: SmallVec::default(),
1025 }
1026}
1027
1028/// A [`Div`] element, the all-in-one element for building complex UIs in GPUI
1029pub struct Div {
1030 interactivity: Interactivity,
1031 children: SmallVec<[AnyElement; 2]>,
1032}
1033
1034impl Styled for Div {
1035 fn style(&mut self) -> &mut StyleRefinement {
1036 &mut self.interactivity.base_style
1037 }
1038}
1039
1040impl InteractiveElement for Div {
1041 fn interactivity(&mut self) -> &mut Interactivity {
1042 &mut self.interactivity
1043 }
1044}
1045
1046impl ParentElement for Div {
1047 fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
1048 self.children.extend(elements)
1049 }
1050}
1051
1052impl Element for Div {
1053 type State = DivState;
1054
1055 fn request_layout(
1056 &mut self,
1057 element_state: Option<Self::State>,
1058 cx: &mut ElementContext,
1059 ) -> (LayoutId, Self::State) {
1060 let mut child_layout_ids = SmallVec::new();
1061 let (layout_id, interactive_state) = self.interactivity.layout(
1062 element_state.map(|s| s.interactive_state),
1063 cx,
1064 |style, cx| {
1065 cx.with_text_style(style.text_style().cloned(), |cx| {
1066 child_layout_ids = self
1067 .children
1068 .iter_mut()
1069 .map(|child| child.request_layout(cx))
1070 .collect::<SmallVec<_>>();
1071 cx.request_layout(&style, child_layout_ids.iter().copied())
1072 })
1073 },
1074 );
1075 (
1076 layout_id,
1077 DivState {
1078 interactive_state,
1079 child_layout_ids,
1080 },
1081 )
1082 }
1083
1084 fn paint(
1085 &mut self,
1086 bounds: Bounds<Pixels>,
1087 element_state: &mut Self::State,
1088 cx: &mut ElementContext,
1089 ) {
1090 let mut child_min = point(Pixels::MAX, Pixels::MAX);
1091 let mut child_max = Point::default();
1092 let content_size = if element_state.child_layout_ids.is_empty() {
1093 bounds.size
1094 } else if let Some(scroll_handle) = self.interactivity.scroll_handle.as_ref() {
1095 let mut state = scroll_handle.0.borrow_mut();
1096 state.child_bounds = Vec::with_capacity(element_state.child_layout_ids.len());
1097 state.bounds = bounds;
1098 let requested = state.requested_scroll_top.take();
1099
1100 for (ix, child_layout_id) in element_state.child_layout_ids.iter().enumerate() {
1101 let child_bounds = cx.layout_bounds(*child_layout_id);
1102 child_min = child_min.min(&child_bounds.origin);
1103 child_max = child_max.max(&child_bounds.lower_right());
1104 state.child_bounds.push(child_bounds);
1105
1106 if let Some(requested) = requested.as_ref() {
1107 if requested.0 == ix {
1108 *state.offset.borrow_mut() =
1109 bounds.origin - (child_bounds.origin - point(px(0.), requested.1));
1110 }
1111 }
1112 }
1113 (child_max - child_min).into()
1114 } else {
1115 for child_layout_id in &element_state.child_layout_ids {
1116 let child_bounds = cx.layout_bounds(*child_layout_id);
1117 child_min = child_min.min(&child_bounds.origin);
1118 child_max = child_max.max(&child_bounds.lower_right());
1119 }
1120 (child_max - child_min).into()
1121 };
1122
1123 self.interactivity.paint(
1124 bounds,
1125 content_size,
1126 &mut element_state.interactive_state,
1127 cx,
1128 |_style, scroll_offset, cx| {
1129 cx.with_element_offset(scroll_offset, |cx| {
1130 for child in &mut self.children {
1131 child.paint(cx);
1132 }
1133 })
1134 },
1135 );
1136 }
1137}
1138
1139impl IntoElement for Div {
1140 type Element = Self;
1141
1142 fn element_id(&self) -> Option<ElementId> {
1143 self.interactivity.element_id.clone()
1144 }
1145
1146 fn into_element(self) -> Self::Element {
1147 self
1148 }
1149}
1150
1151/// The state a div needs to keep track of between frames.
1152pub struct DivState {
1153 child_layout_ids: SmallVec<[LayoutId; 2]>,
1154 interactive_state: InteractiveElementState,
1155}
1156
1157impl DivState {
1158 /// Is the div currently being clicked on?
1159 pub fn is_active(&self) -> bool {
1160 self.interactive_state
1161 .pending_mouse_down
1162 .as_ref()
1163 .map_or(false, |pending| pending.borrow().is_some())
1164 }
1165}
1166
1167/// The interactivity struct. Powers all of the general-purpose
1168/// interactivity in the `Div` element.
1169#[derive(Default)]
1170pub struct Interactivity {
1171 /// The element ID of the element
1172 pub element_id: Option<ElementId>,
1173 pub(crate) key_context: Option<KeyContext>,
1174 pub(crate) focusable: bool,
1175 pub(crate) tracked_focus_handle: Option<FocusHandle>,
1176 pub(crate) scroll_handle: Option<ScrollHandle>,
1177 pub(crate) group: Option<SharedString>,
1178 /// The base style of the element, before any modifications are applied
1179 /// by focus, active, etc.
1180 pub base_style: Box<StyleRefinement>,
1181 pub(crate) focus_style: Option<Box<StyleRefinement>>,
1182 pub(crate) in_focus_style: Option<Box<StyleRefinement>>,
1183 pub(crate) hover_style: Option<Box<StyleRefinement>>,
1184 pub(crate) group_hover_style: Option<GroupStyle>,
1185 pub(crate) active_style: Option<Box<StyleRefinement>>,
1186 pub(crate) group_active_style: Option<GroupStyle>,
1187 pub(crate) drag_over_styles: Vec<(
1188 TypeId,
1189 Box<dyn Fn(&dyn Any, &mut WindowContext) -> StyleRefinement>,
1190 )>,
1191 pub(crate) group_drag_over_styles: Vec<(TypeId, GroupStyle)>,
1192 pub(crate) mouse_down_listeners: Vec<MouseDownListener>,
1193 pub(crate) mouse_up_listeners: Vec<MouseUpListener>,
1194 pub(crate) mouse_move_listeners: Vec<MouseMoveListener>,
1195 pub(crate) scroll_wheel_listeners: Vec<ScrollWheelListener>,
1196 pub(crate) key_down_listeners: Vec<KeyDownListener>,
1197 pub(crate) key_up_listeners: Vec<KeyUpListener>,
1198 pub(crate) action_listeners: Vec<(TypeId, ActionListener)>,
1199 pub(crate) drop_listeners: Vec<(TypeId, DropListener)>,
1200 pub(crate) can_drop_predicate: Option<CanDropPredicate>,
1201 pub(crate) click_listeners: Vec<ClickListener>,
1202 pub(crate) drag_listener: Option<(Box<dyn Any>, DragListener)>,
1203 pub(crate) hover_listener: Option<Box<dyn Fn(&bool, &mut WindowContext)>>,
1204 pub(crate) tooltip_builder: Option<TooltipBuilder>,
1205 pub(crate) block_mouse: bool,
1206
1207 #[cfg(debug_assertions)]
1208 pub(crate) location: Option<core::panic::Location<'static>>,
1209
1210 #[cfg(any(test, feature = "test-support"))]
1211 pub(crate) debug_selector: Option<String>,
1212}
1213
1214/// The bounds and depth of an element in the computed element tree.
1215#[derive(Clone, Debug)]
1216pub struct InteractiveBounds {
1217 /// The 2D bounds of the element
1218 pub bounds: Bounds<Pixels>,
1219 /// The 'stacking order', or depth, for this element
1220 pub stacking_order: StackingOrder,
1221}
1222
1223impl InteractiveBounds {
1224 /// Checks whether this point was inside these bounds in the rendered frame, and that these bounds where the topmost layer
1225 /// Never call this during paint to perform hover calculations. It will reference the previous frame and could cause flicker.
1226 pub fn visibly_contains(&self, point: &Point<Pixels>, cx: &WindowContext) -> bool {
1227 self.bounds.contains(point) && cx.was_top_layer(point, &self.stacking_order)
1228 }
1229
1230 /// Checks whether this point was inside these bounds, and that these bounds where the topmost layer
1231 /// under an active drag
1232 pub fn drag_target_contains(&self, point: &Point<Pixels>, cx: &WindowContext) -> bool {
1233 self.bounds.contains(point)
1234 && cx.was_top_layer_under_active_drag(point, &self.stacking_order)
1235 }
1236}
1237
1238impl Interactivity {
1239 /// Layout this element according to this interactivity state's configured styles
1240 pub fn layout(
1241 &mut self,
1242 element_state: Option<InteractiveElementState>,
1243 cx: &mut ElementContext,
1244 f: impl FnOnce(Style, &mut ElementContext) -> LayoutId,
1245 ) -> (LayoutId, InteractiveElementState) {
1246 let mut element_state = element_state.unwrap_or_default();
1247
1248 if cx.has_active_drag() {
1249 if let Some(pending_mouse_down) = element_state.pending_mouse_down.as_ref() {
1250 *pending_mouse_down.borrow_mut() = None;
1251 }
1252 if let Some(clicked_state) = element_state.clicked_state.as_ref() {
1253 *clicked_state.borrow_mut() = ElementClickedState::default();
1254 }
1255 }
1256
1257 // Ensure we store a focus handle in our element state if we're focusable.
1258 // If there's an explicit focus handle we're tracking, use that. Otherwise
1259 // create a new handle and store it in the element state, which lives for as
1260 // as frames contain an element with this id.
1261 if self.focusable {
1262 element_state.focus_handle.get_or_insert_with(|| {
1263 self.tracked_focus_handle
1264 .clone()
1265 .unwrap_or_else(|| cx.focus_handle())
1266 });
1267 }
1268
1269 if let Some(scroll_handle) = self.scroll_handle.as_ref() {
1270 element_state.scroll_offset = Some(scroll_handle.0.borrow().offset.clone());
1271 }
1272
1273 let style = self.compute_style(None, &mut element_state, cx);
1274 let layout_id = f(style, cx);
1275 (layout_id, element_state)
1276 }
1277
1278 /// Paint this element according to this interactivity state's configured styles
1279 /// and bind the element's mouse and keyboard events.
1280 ///
1281 /// content_size is the size of the content of the element, which may be larger than the
1282 /// element's bounds if the element is scrollable.
1283 ///
1284 /// the final computed style will be passed to the provided function, along
1285 /// with the current scroll offset
1286 pub fn paint(
1287 &mut self,
1288 bounds: Bounds<Pixels>,
1289 content_size: Size<Pixels>,
1290 element_state: &mut InteractiveElementState,
1291 cx: &mut ElementContext,
1292 f: impl FnOnce(&Style, Point<Pixels>, &mut ElementContext),
1293 ) {
1294 let style = self.compute_style(Some(bounds), element_state, cx);
1295 let z_index = style.z_index.unwrap_or(0);
1296
1297 #[cfg(any(feature = "test-support", test))]
1298 if let Some(debug_selector) = &self.debug_selector {
1299 cx.window
1300 .next_frame
1301 .debug_bounds
1302 .insert(debug_selector.clone(), bounds);
1303 }
1304
1305 let paint_hover_group_handler = |cx: &mut ElementContext| {
1306 let hover_group_bounds = self
1307 .group_hover_style
1308 .as_ref()
1309 .and_then(|group_hover| GroupBounds::get(&group_hover.group, cx));
1310
1311 if let Some(group_bounds) = hover_group_bounds {
1312 let hovered = group_bounds.contains(&cx.mouse_position());
1313 cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
1314 if phase == DispatchPhase::Capture
1315 && group_bounds.contains(&event.position) != hovered
1316 {
1317 cx.refresh();
1318 }
1319 });
1320 }
1321 };
1322
1323 if style.visibility == Visibility::Hidden {
1324 cx.with_z_index(z_index, |cx| paint_hover_group_handler(cx));
1325 return;
1326 }
1327
1328 cx.with_z_index(z_index, |cx| {
1329 style.paint(bounds, cx, |cx: &mut ElementContext| {
1330 cx.with_text_style(style.text_style().cloned(), |cx| {
1331 cx.with_content_mask(style.overflow_mask(bounds, cx.rem_size()), |cx| {
1332 #[cfg(debug_assertions)]
1333 if self.element_id.is_some()
1334 && (style.debug
1335 || style.debug_below
1336 || cx.has_global::<crate::DebugBelow>())
1337 && bounds.contains(&cx.mouse_position())
1338 {
1339 const FONT_SIZE: crate::Pixels = crate::Pixels(10.);
1340 let element_id = format!("{:?}", self.element_id.as_ref().unwrap());
1341 let str_len = element_id.len();
1342
1343 let render_debug_text = |cx: &mut ElementContext| {
1344 if let Some(text) = cx
1345 .text_system()
1346 .shape_text(
1347 element_id.into(),
1348 FONT_SIZE,
1349 &[cx.text_style().to_run(str_len)],
1350 None,
1351 )
1352 .ok()
1353 .and_then(|mut text| text.pop())
1354 {
1355 text.paint(bounds.origin, FONT_SIZE, cx).ok();
1356
1357 let text_bounds = crate::Bounds {
1358 origin: bounds.origin,
1359 size: text.size(FONT_SIZE),
1360 };
1361 if self.location.is_some()
1362 && text_bounds.contains(&cx.mouse_position())
1363 && cx.modifiers().command
1364 {
1365 let command_held = cx.modifiers().command;
1366 cx.on_key_event({
1367 move |e: &crate::ModifiersChangedEvent, _phase, cx| {
1368 if e.modifiers.command != command_held
1369 && text_bounds.contains(&cx.mouse_position())
1370 {
1371 cx.refresh();
1372 }
1373 }
1374 });
1375
1376 let hovered = bounds.contains(&cx.mouse_position());
1377 cx.on_mouse_event(
1378 move |event: &MouseMoveEvent, phase, cx| {
1379 if phase == DispatchPhase::Capture
1380 && bounds.contains(&event.position) != hovered
1381 {
1382 cx.refresh();
1383 }
1384 },
1385 );
1386
1387 cx.on_mouse_event({
1388 let location = self.location.unwrap();
1389 move |e: &crate::MouseDownEvent, phase, cx| {
1390 if text_bounds.contains(&e.position)
1391 && phase.capture()
1392 {
1393 cx.stop_propagation();
1394 let Ok(dir) = std::env::current_dir() else {
1395 return;
1396 };
1397
1398 eprintln!(
1399 "This element was created at:\n{}:{}:{}",
1400 dir.join(location.file()).to_string_lossy(),
1401 location.line(),
1402 location.column()
1403 );
1404 }
1405 }
1406 });
1407 cx.paint_quad(crate::outline(
1408 crate::Bounds {
1409 origin: bounds.origin
1410 + crate::point(
1411 crate::px(0.),
1412 FONT_SIZE - px(2.),
1413 ),
1414 size: crate::Size {
1415 width: text_bounds.size.width,
1416 height: crate::px(1.),
1417 },
1418 },
1419 crate::red(),
1420 ))
1421 }
1422 }
1423 };
1424
1425 cx.with_z_index(1, |cx| {
1426 cx.with_text_style(
1427 Some(crate::TextStyleRefinement {
1428 color: Some(crate::red()),
1429 line_height: Some(FONT_SIZE.into()),
1430 background_color: Some(crate::white()),
1431 ..Default::default()
1432 }),
1433 render_debug_text,
1434 )
1435 });
1436 }
1437
1438 let interactive_bounds = InteractiveBounds {
1439 bounds: bounds.intersect(&cx.content_mask().bounds),
1440 stacking_order: cx.stacking_order().clone(),
1441 };
1442
1443 if self.block_mouse
1444 || style.background.as_ref().is_some_and(|fill| {
1445 fill.color().is_some_and(|color| !color.is_transparent())
1446 })
1447 {
1448 cx.add_opaque_layer(interactive_bounds.bounds);
1449 }
1450
1451 if !cx.has_active_drag() {
1452 if let Some(mouse_cursor) = style.mouse_cursor {
1453 let hovered = bounds.contains(&cx.mouse_position());
1454 if hovered {
1455 cx.set_cursor_style(
1456 mouse_cursor,
1457 interactive_bounds.stacking_order.clone(),
1458 );
1459 }
1460 }
1461 }
1462
1463 // If this element can be focused, register a mouse down listener
1464 // that will automatically transfer focus when hitting the element.
1465 // This behavior can be suppressed by using `cx.prevent_default()`.
1466 if let Some(focus_handle) = element_state.focus_handle.clone() {
1467 cx.on_mouse_event({
1468 let interactive_bounds = interactive_bounds.clone();
1469 move |event: &MouseDownEvent, phase, cx| {
1470 if phase == DispatchPhase::Bubble
1471 && !cx.default_prevented()
1472 && interactive_bounds.visibly_contains(&event.position, cx)
1473 {
1474 cx.focus(&focus_handle);
1475 // If there is a parent that is also focusable, prevent it
1476 // from transferring focus because we already did so.
1477 cx.prevent_default();
1478 }
1479 }
1480 });
1481 }
1482
1483 for listener in self.mouse_down_listeners.drain(..) {
1484 let interactive_bounds = interactive_bounds.clone();
1485 cx.on_mouse_event(move |event: &MouseDownEvent, phase, cx| {
1486 listener(event, &interactive_bounds, phase, cx);
1487 })
1488 }
1489
1490 for listener in self.mouse_up_listeners.drain(..) {
1491 let interactive_bounds = interactive_bounds.clone();
1492 cx.on_mouse_event(move |event: &MouseUpEvent, phase, cx| {
1493 listener(event, &interactive_bounds, phase, cx);
1494 })
1495 }
1496
1497 for listener in self.mouse_move_listeners.drain(..) {
1498 let interactive_bounds = interactive_bounds.clone();
1499 cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
1500 listener(event, &interactive_bounds, phase, cx);
1501 })
1502 }
1503
1504 for listener in self.scroll_wheel_listeners.drain(..) {
1505 let interactive_bounds = interactive_bounds.clone();
1506 cx.on_mouse_event(move |event: &ScrollWheelEvent, phase, cx| {
1507 listener(event, &interactive_bounds, phase, cx);
1508 })
1509 }
1510
1511 paint_hover_group_handler(cx);
1512
1513 if self.hover_style.is_some()
1514 || self.base_style.mouse_cursor.is_some()
1515 || cx.active_drag.is_some() && !self.drag_over_styles.is_empty()
1516 {
1517 let bounds = bounds.intersect(&cx.content_mask().bounds);
1518 let hovered = bounds.contains(&cx.mouse_position());
1519 cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
1520 if phase == DispatchPhase::Capture
1521 && bounds.contains(&event.position) != hovered
1522 {
1523 cx.refresh();
1524 }
1525 });
1526 }
1527
1528 let mut drag_listener = mem::take(&mut self.drag_listener);
1529 let drop_listeners = mem::take(&mut self.drop_listeners);
1530 let click_listeners = mem::take(&mut self.click_listeners);
1531 let can_drop_predicate = mem::take(&mut self.can_drop_predicate);
1532
1533 if !drop_listeners.is_empty() {
1534 cx.on_mouse_event({
1535 let interactive_bounds = interactive_bounds.clone();
1536 move |event: &MouseUpEvent, phase, cx| {
1537 if let Some(drag) = &cx.active_drag {
1538 if phase == DispatchPhase::Bubble
1539 && interactive_bounds
1540 .drag_target_contains(&event.position, cx)
1541 {
1542 let drag_state_type = drag.value.as_ref().type_id();
1543 for (drop_state_type, listener) in &drop_listeners {
1544 if *drop_state_type == drag_state_type {
1545 let drag = cx.active_drag.take().expect(
1546 "checked for type drag state type above",
1547 );
1548
1549 let mut can_drop = true;
1550 if let Some(predicate) = &can_drop_predicate {
1551 can_drop = predicate(
1552 drag.value.as_ref(),
1553 cx.deref_mut(),
1554 );
1555 }
1556
1557 if can_drop {
1558 listener(
1559 drag.value.as_ref(),
1560 cx.deref_mut(),
1561 );
1562 cx.refresh();
1563 cx.stop_propagation();
1564 }
1565 }
1566 }
1567 }
1568 }
1569 }
1570 });
1571 }
1572
1573 if !click_listeners.is_empty() || drag_listener.is_some() {
1574 let pending_mouse_down = element_state
1575 .pending_mouse_down
1576 .get_or_insert_with(Default::default)
1577 .clone();
1578
1579 let clicked_state = element_state
1580 .clicked_state
1581 .get_or_insert_with(Default::default)
1582 .clone();
1583
1584 cx.on_mouse_event({
1585 let interactive_bounds = interactive_bounds.clone();
1586 let pending_mouse_down = pending_mouse_down.clone();
1587 move |event: &MouseDownEvent, phase, cx| {
1588 if phase == DispatchPhase::Bubble
1589 && event.button == MouseButton::Left
1590 && interactive_bounds.visibly_contains(&event.position, cx)
1591 {
1592 *pending_mouse_down.borrow_mut() = Some(event.clone());
1593 cx.refresh();
1594 }
1595 }
1596 });
1597
1598 cx.on_mouse_event({
1599 let pending_mouse_down = pending_mouse_down.clone();
1600 move |event: &MouseMoveEvent, phase, cx| {
1601 if phase == DispatchPhase::Capture {
1602 return;
1603 }
1604
1605 let mut pending_mouse_down = pending_mouse_down.borrow_mut();
1606 if let Some(mouse_down) = pending_mouse_down.clone() {
1607 if !cx.has_active_drag()
1608 && (event.position - mouse_down.position).magnitude()
1609 > DRAG_THRESHOLD
1610 {
1611 if let Some((drag_value, drag_listener)) =
1612 drag_listener.take()
1613 {
1614 *clicked_state.borrow_mut() =
1615 ElementClickedState::default();
1616 let cursor_offset = event.position - bounds.origin;
1617 let drag = (drag_listener)(drag_value.as_ref(), cx);
1618 cx.active_drag = Some(AnyDrag {
1619 view: drag,
1620 value: drag_value,
1621 cursor_offset,
1622 });
1623 pending_mouse_down.take();
1624 cx.refresh();
1625 cx.stop_propagation();
1626 }
1627 }
1628 }
1629 }
1630 });
1631
1632 cx.on_mouse_event({
1633 let interactive_bounds = interactive_bounds.clone();
1634 let mut captured_mouse_down = None;
1635 move |event: &MouseUpEvent, phase, cx| match phase {
1636 // Clear the pending mouse down during the capture phase,
1637 // so that it happens even if another event handler stops
1638 // propagation.
1639 DispatchPhase::Capture => {
1640 let mut pending_mouse_down =
1641 pending_mouse_down.borrow_mut();
1642 if pending_mouse_down.is_some() {
1643 captured_mouse_down = pending_mouse_down.take();
1644 cx.refresh();
1645 }
1646 }
1647 // Fire click handlers during the bubble phase.
1648 DispatchPhase::Bubble => {
1649 if let Some(mouse_down) = captured_mouse_down.take() {
1650 if interactive_bounds
1651 .visibly_contains(&event.position, cx)
1652 {
1653 let mouse_click = ClickEvent {
1654 down: mouse_down,
1655 up: event.clone(),
1656 };
1657 for listener in &click_listeners {
1658 listener(&mouse_click, cx);
1659 }
1660 }
1661 }
1662 }
1663 }
1664 });
1665 }
1666
1667 if let Some(hover_listener) = self.hover_listener.take() {
1668 let was_hovered = element_state
1669 .hover_state
1670 .get_or_insert_with(Default::default)
1671 .clone();
1672 let has_mouse_down = element_state
1673 .pending_mouse_down
1674 .get_or_insert_with(Default::default)
1675 .clone();
1676 let interactive_bounds = interactive_bounds.clone();
1677
1678 cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
1679 if phase != DispatchPhase::Bubble {
1680 return;
1681 }
1682 let is_hovered = interactive_bounds
1683 .visibly_contains(&event.position, cx)
1684 && has_mouse_down.borrow().is_none()
1685 && !cx.has_active_drag();
1686 let mut was_hovered = was_hovered.borrow_mut();
1687
1688 if is_hovered != *was_hovered {
1689 *was_hovered = is_hovered;
1690 drop(was_hovered);
1691
1692 hover_listener(&is_hovered, cx.deref_mut());
1693 }
1694 });
1695 }
1696
1697 if let Some(tooltip_builder) = self.tooltip_builder.take() {
1698 let active_tooltip = element_state
1699 .active_tooltip
1700 .get_or_insert_with(Default::default)
1701 .clone();
1702 let pending_mouse_down = element_state
1703 .pending_mouse_down
1704 .get_or_insert_with(Default::default)
1705 .clone();
1706 let interactive_bounds = interactive_bounds.clone();
1707
1708 cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
1709 let is_hovered = interactive_bounds
1710 .visibly_contains(&event.position, cx)
1711 && pending_mouse_down.borrow().is_none();
1712 if !is_hovered {
1713 active_tooltip.borrow_mut().take();
1714 return;
1715 }
1716
1717 if phase != DispatchPhase::Bubble {
1718 return;
1719 }
1720
1721 if active_tooltip.borrow().is_none() {
1722 let task = cx.spawn({
1723 let active_tooltip = active_tooltip.clone();
1724 let tooltip_builder = tooltip_builder.clone();
1725
1726 move |mut cx| async move {
1727 cx.background_executor().timer(TOOLTIP_DELAY).await;
1728 cx.update(|cx| {
1729 active_tooltip.borrow_mut().replace(
1730 ActiveTooltip {
1731 tooltip: Some(AnyTooltip {
1732 view: tooltip_builder(cx),
1733 cursor_offset: cx.mouse_position(),
1734 }),
1735 _task: None,
1736 },
1737 );
1738 cx.refresh();
1739 })
1740 .ok();
1741 }
1742 });
1743 active_tooltip.borrow_mut().replace(ActiveTooltip {
1744 tooltip: None,
1745 _task: Some(task),
1746 });
1747 }
1748 });
1749
1750 let active_tooltip = element_state
1751 .active_tooltip
1752 .get_or_insert_with(Default::default)
1753 .clone();
1754 cx.on_mouse_event(move |_: &MouseDownEvent, _, _| {
1755 active_tooltip.borrow_mut().take();
1756 });
1757
1758 if let Some(active_tooltip) = element_state
1759 .active_tooltip
1760 .get_or_insert_with(Default::default)
1761 .borrow()
1762 .as_ref()
1763 {
1764 if let Some(tooltip) = active_tooltip.tooltip.clone() {
1765 cx.set_tooltip(tooltip);
1766 }
1767 }
1768 }
1769
1770 let active_state = element_state
1771 .clicked_state
1772 .get_or_insert_with(Default::default)
1773 .clone();
1774 if active_state.borrow().is_clicked() {
1775 cx.on_mouse_event(move |_: &MouseUpEvent, phase, cx| {
1776 if phase == DispatchPhase::Capture {
1777 *active_state.borrow_mut() = ElementClickedState::default();
1778 cx.refresh();
1779 }
1780 });
1781 } else {
1782 let active_group_bounds = self
1783 .group_active_style
1784 .as_ref()
1785 .and_then(|group_active| GroupBounds::get(&group_active.group, cx));
1786 let interactive_bounds = interactive_bounds.clone();
1787 cx.on_mouse_event(move |down: &MouseDownEvent, phase, cx| {
1788 if phase == DispatchPhase::Bubble && !cx.default_prevented() {
1789 let group = active_group_bounds
1790 .map_or(false, |bounds| bounds.contains(&down.position));
1791 let element =
1792 interactive_bounds.visibly_contains(&down.position, cx);
1793 if group || element {
1794 *active_state.borrow_mut() =
1795 ElementClickedState { group, element };
1796 cx.refresh();
1797 }
1798 }
1799 });
1800 }
1801
1802 let overflow = style.overflow;
1803 if overflow.x == Overflow::Scroll || overflow.y == Overflow::Scroll {
1804 if let Some(scroll_handle) = &self.scroll_handle {
1805 scroll_handle.0.borrow_mut().overflow = overflow;
1806 }
1807
1808 let scroll_offset = element_state
1809 .scroll_offset
1810 .get_or_insert_with(Rc::default)
1811 .clone();
1812 let line_height = cx.line_height();
1813 let rem_size = cx.rem_size();
1814 let padding_size = size(
1815 style
1816 .padding
1817 .left
1818 .to_pixels(bounds.size.width.into(), rem_size)
1819 + style
1820 .padding
1821 .right
1822 .to_pixels(bounds.size.width.into(), rem_size),
1823 style
1824 .padding
1825 .top
1826 .to_pixels(bounds.size.height.into(), rem_size)
1827 + style
1828 .padding
1829 .bottom
1830 .to_pixels(bounds.size.height.into(), rem_size),
1831 );
1832 let scroll_max =
1833 (content_size + padding_size - bounds.size).max(&Size::default());
1834 // Clamp scroll offset in case scroll max is smaller now (e.g., if children
1835 // were removed or the bounds became larger).
1836 {
1837 let mut scroll_offset = scroll_offset.borrow_mut();
1838 scroll_offset.x = scroll_offset.x.clamp(-scroll_max.width, px(0.));
1839 scroll_offset.y = scroll_offset.y.clamp(-scroll_max.height, px(0.));
1840 }
1841
1842 let interactive_bounds = interactive_bounds.clone();
1843 cx.on_mouse_event(move |event: &ScrollWheelEvent, phase, cx| {
1844 if phase == DispatchPhase::Bubble
1845 && interactive_bounds.visibly_contains(&event.position, cx)
1846 {
1847 let mut scroll_offset = scroll_offset.borrow_mut();
1848 let old_scroll_offset = *scroll_offset;
1849 let delta = event.delta.pixel_delta(line_height);
1850
1851 if overflow.x == Overflow::Scroll {
1852 let mut delta_x = Pixels::ZERO;
1853 if !delta.x.is_zero() {
1854 delta_x = delta.x;
1855 } else if overflow.y != Overflow::Scroll {
1856 delta_x = delta.y;
1857 }
1858
1859 scroll_offset.x = (scroll_offset.x + delta_x)
1860 .clamp(-scroll_max.width, px(0.));
1861 }
1862
1863 if overflow.y == Overflow::Scroll {
1864 let mut delta_y = Pixels::ZERO;
1865 if !delta.y.is_zero() {
1866 delta_y = delta.y;
1867 } else if overflow.x != Overflow::Scroll {
1868 delta_y = delta.x;
1869 }
1870
1871 scroll_offset.y = (scroll_offset.y + delta_y)
1872 .clamp(-scroll_max.height, px(0.));
1873 }
1874
1875 if *scroll_offset != old_scroll_offset {
1876 cx.refresh();
1877 cx.stop_propagation();
1878 }
1879 }
1880 });
1881 }
1882
1883 if let Some(group) = self.group.clone() {
1884 GroupBounds::push(group, bounds, cx);
1885 }
1886
1887 let scroll_offset = element_state
1888 .scroll_offset
1889 .as_ref()
1890 .map(|scroll_offset| *scroll_offset.borrow());
1891
1892 let key_down_listeners = mem::take(&mut self.key_down_listeners);
1893 let key_up_listeners = mem::take(&mut self.key_up_listeners);
1894 let action_listeners = mem::take(&mut self.action_listeners);
1895 cx.with_key_dispatch(
1896 self.key_context.clone(),
1897 element_state.focus_handle.clone(),
1898 |_, cx| {
1899 for listener in key_down_listeners {
1900 cx.on_key_event(move |event: &KeyDownEvent, phase, cx| {
1901 listener(event, phase, cx);
1902 })
1903 }
1904
1905 for listener in key_up_listeners {
1906 cx.on_key_event(move |event: &KeyUpEvent, phase, cx| {
1907 listener(event, phase, cx);
1908 })
1909 }
1910
1911 for (action_type, listener) in action_listeners {
1912 cx.on_action(action_type, listener)
1913 }
1914
1915 f(&style, scroll_offset.unwrap_or_default(), cx)
1916 },
1917 );
1918
1919 if let Some(group) = self.group.as_ref() {
1920 GroupBounds::pop(group, cx);
1921 }
1922 });
1923 });
1924 });
1925 });
1926 }
1927
1928 /// Compute the visual style for this element, based on the current bounds and the element's state.
1929 pub fn compute_style(
1930 &self,
1931 bounds: Option<Bounds<Pixels>>,
1932 element_state: &mut InteractiveElementState,
1933 cx: &mut ElementContext,
1934 ) -> Style {
1935 let mut style = Style::default();
1936 style.refine(&self.base_style);
1937
1938 cx.with_z_index(style.z_index.unwrap_or(0), |cx| {
1939 if let Some(focus_handle) = self.tracked_focus_handle.as_ref() {
1940 if let Some(in_focus_style) = self.in_focus_style.as_ref() {
1941 if focus_handle.within_focused(cx) {
1942 style.refine(in_focus_style);
1943 }
1944 }
1945
1946 if let Some(focus_style) = self.focus_style.as_ref() {
1947 if focus_handle.is_focused(cx) {
1948 style.refine(focus_style);
1949 }
1950 }
1951 }
1952
1953 if let Some(bounds) = bounds {
1954 let mouse_position = cx.mouse_position();
1955 if !cx.has_active_drag() {
1956 if let Some(group_hover) = self.group_hover_style.as_ref() {
1957 if let Some(group_bounds) =
1958 GroupBounds::get(&group_hover.group, cx.deref_mut())
1959 {
1960 if group_bounds.contains(&mouse_position) {
1961 style.refine(&group_hover.style);
1962 }
1963 }
1964 }
1965
1966 if let Some(hover_style) = self.hover_style.as_ref() {
1967 if bounds
1968 .intersect(&cx.content_mask().bounds)
1969 .contains(&mouse_position)
1970 {
1971 style.refine(hover_style);
1972 }
1973 }
1974 }
1975
1976 if let Some(drag) = cx.active_drag.take() {
1977 let mut can_drop = true;
1978 if let Some(can_drop_predicate) = &self.can_drop_predicate {
1979 can_drop = can_drop_predicate(drag.value.as_ref(), cx.deref_mut());
1980 }
1981
1982 if can_drop {
1983 for (state_type, group_drag_style) in &self.group_drag_over_styles {
1984 if let Some(group_bounds) =
1985 GroupBounds::get(&group_drag_style.group, cx.deref_mut())
1986 {
1987 if *state_type == drag.value.as_ref().type_id()
1988 && group_bounds.contains(&mouse_position)
1989 {
1990 style.refine(&group_drag_style.style);
1991 }
1992 }
1993 }
1994
1995 for (state_type, build_drag_over_style) in &self.drag_over_styles {
1996 if *state_type == drag.value.as_ref().type_id()
1997 && bounds
1998 .intersect(&cx.content_mask().bounds)
1999 .contains(&mouse_position)
2000 && cx.was_top_layer_under_active_drag(
2001 &mouse_position,
2002 cx.stacking_order(),
2003 )
2004 {
2005 style.refine(&build_drag_over_style(drag.value.as_ref(), cx));
2006 }
2007 }
2008 }
2009
2010 cx.active_drag = Some(drag);
2011 }
2012 }
2013
2014 let clicked_state = element_state
2015 .clicked_state
2016 .get_or_insert_with(Default::default)
2017 .borrow();
2018 if clicked_state.group {
2019 if let Some(group) = self.group_active_style.as_ref() {
2020 style.refine(&group.style)
2021 }
2022 }
2023
2024 if let Some(active_style) = self.active_style.as_ref() {
2025 if clicked_state.element {
2026 style.refine(active_style)
2027 }
2028 }
2029 });
2030
2031 style
2032 }
2033}
2034
2035/// The per-frame state of an interactive element. Used for tracking stateful interactions like clicks
2036/// and scroll offsets.
2037#[derive(Default)]
2038pub struct InteractiveElementState {
2039 pub(crate) focus_handle: Option<FocusHandle>,
2040 pub(crate) clicked_state: Option<Rc<RefCell<ElementClickedState>>>,
2041 pub(crate) hover_state: Option<Rc<RefCell<bool>>>,
2042 pub(crate) pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
2043 pub(crate) scroll_offset: Option<Rc<RefCell<Point<Pixels>>>>,
2044 pub(crate) active_tooltip: Option<Rc<RefCell<Option<ActiveTooltip>>>>,
2045}
2046
2047/// The current active tooltip
2048pub struct ActiveTooltip {
2049 pub(crate) tooltip: Option<AnyTooltip>,
2050 pub(crate) _task: Option<Task<()>>,
2051}
2052
2053/// Whether or not the element or a group that contains it is clicked by the mouse.
2054#[derive(Copy, Clone, Default, Eq, PartialEq)]
2055pub struct ElementClickedState {
2056 /// True if this element's group has been clicked, false otherwise
2057 pub group: bool,
2058
2059 /// True if this element has been clicked, false otherwise
2060 pub element: bool,
2061}
2062
2063impl ElementClickedState {
2064 fn is_clicked(&self) -> bool {
2065 self.group || self.element
2066 }
2067}
2068
2069#[derive(Default)]
2070pub(crate) struct GroupBounds(HashMap<SharedString, SmallVec<[Bounds<Pixels>; 1]>>);
2071
2072impl Global for GroupBounds {}
2073
2074impl GroupBounds {
2075 pub fn get(name: &SharedString, cx: &mut AppContext) -> Option<Bounds<Pixels>> {
2076 cx.default_global::<Self>()
2077 .0
2078 .get(name)
2079 .and_then(|bounds_stack| bounds_stack.last())
2080 .cloned()
2081 }
2082
2083 pub fn push(name: SharedString, bounds: Bounds<Pixels>, cx: &mut AppContext) {
2084 cx.default_global::<Self>()
2085 .0
2086 .entry(name)
2087 .or_default()
2088 .push(bounds);
2089 }
2090
2091 pub fn pop(name: &SharedString, cx: &mut AppContext) {
2092 cx.default_global::<Self>().0.get_mut(name).unwrap().pop();
2093 }
2094}
2095
2096/// A wrapper around an element that can be focused.
2097pub struct Focusable<E> {
2098 /// The element that is focusable
2099 pub element: E,
2100}
2101
2102impl<E: InteractiveElement> FocusableElement for Focusable<E> {}
2103
2104impl<E> InteractiveElement for Focusable<E>
2105where
2106 E: InteractiveElement,
2107{
2108 fn interactivity(&mut self) -> &mut Interactivity {
2109 self.element.interactivity()
2110 }
2111}
2112
2113impl<E: StatefulInteractiveElement> StatefulInteractiveElement for Focusable<E> {}
2114
2115impl<E> Styled for Focusable<E>
2116where
2117 E: Styled,
2118{
2119 fn style(&mut self) -> &mut StyleRefinement {
2120 self.element.style()
2121 }
2122}
2123
2124impl<E> Element for Focusable<E>
2125where
2126 E: Element,
2127{
2128 type State = E::State;
2129
2130 fn request_layout(
2131 &mut self,
2132 state: Option<Self::State>,
2133 cx: &mut ElementContext,
2134 ) -> (LayoutId, Self::State) {
2135 self.element.request_layout(state, cx)
2136 }
2137
2138 fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut ElementContext) {
2139 self.element.paint(bounds, state, cx)
2140 }
2141}
2142
2143impl<E> IntoElement for Focusable<E>
2144where
2145 E: IntoElement,
2146{
2147 type Element = E::Element;
2148
2149 fn element_id(&self) -> Option<ElementId> {
2150 self.element.element_id()
2151 }
2152
2153 fn into_element(self) -> Self::Element {
2154 self.element.into_element()
2155 }
2156}
2157
2158impl<E> ParentElement for Focusable<E>
2159where
2160 E: ParentElement,
2161{
2162 fn extend(&mut self, elements: impl Iterator<Item = AnyElement>) {
2163 self.element.extend(elements)
2164 }
2165}
2166
2167/// A wrapper around an element that can store state, produced after assigning an ElementId.
2168pub struct Stateful<E> {
2169 element: E,
2170}
2171
2172impl<E> Styled for Stateful<E>
2173where
2174 E: Styled,
2175{
2176 fn style(&mut self) -> &mut StyleRefinement {
2177 self.element.style()
2178 }
2179}
2180
2181impl<E> StatefulInteractiveElement for Stateful<E>
2182where
2183 E: Element,
2184 Self: InteractiveElement,
2185{
2186}
2187
2188impl<E> InteractiveElement for Stateful<E>
2189where
2190 E: InteractiveElement,
2191{
2192 fn interactivity(&mut self) -> &mut Interactivity {
2193 self.element.interactivity()
2194 }
2195}
2196
2197impl<E: FocusableElement> FocusableElement for Stateful<E> {}
2198
2199impl<E> Element for Stateful<E>
2200where
2201 E: Element,
2202{
2203 type State = E::State;
2204
2205 fn request_layout(
2206 &mut self,
2207 state: Option<Self::State>,
2208 cx: &mut ElementContext,
2209 ) -> (LayoutId, Self::State) {
2210 self.element.request_layout(state, cx)
2211 }
2212
2213 fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut ElementContext) {
2214 self.element.paint(bounds, state, cx)
2215 }
2216}
2217
2218impl<E> IntoElement for Stateful<E>
2219where
2220 E: Element,
2221{
2222 type Element = Self;
2223
2224 fn element_id(&self) -> Option<ElementId> {
2225 self.element.element_id()
2226 }
2227
2228 fn into_element(self) -> Self::Element {
2229 self
2230 }
2231}
2232
2233impl<E> ParentElement for Stateful<E>
2234where
2235 E: ParentElement,
2236{
2237 fn extend(&mut self, elements: impl Iterator<Item = AnyElement>) {
2238 self.element.extend(elements)
2239 }
2240}
2241
2242#[derive(Default)]
2243struct ScrollHandleState {
2244 offset: Rc<RefCell<Point<Pixels>>>,
2245 bounds: Bounds<Pixels>,
2246 child_bounds: Vec<Bounds<Pixels>>,
2247 requested_scroll_top: Option<(usize, Pixels)>,
2248 overflow: Point<Overflow>,
2249}
2250
2251/// A handle to the scrollable aspects of an element.
2252/// Used for accessing scroll state, like the current scroll offset,
2253/// and for mutating the scroll state, like scrolling to a specific child.
2254#[derive(Clone)]
2255pub struct ScrollHandle(Rc<RefCell<ScrollHandleState>>);
2256
2257impl Default for ScrollHandle {
2258 fn default() -> Self {
2259 Self::new()
2260 }
2261}
2262
2263impl ScrollHandle {
2264 /// Construct a new scroll handle.
2265 pub fn new() -> Self {
2266 Self(Rc::default())
2267 }
2268
2269 /// Get the current scroll offset.
2270 pub fn offset(&self) -> Point<Pixels> {
2271 *self.0.borrow().offset.borrow()
2272 }
2273
2274 /// Get the top child that's scrolled into view.
2275 pub fn top_item(&self) -> usize {
2276 let state = self.0.borrow();
2277 let top = state.bounds.top() - state.offset.borrow().y;
2278
2279 match state.child_bounds.binary_search_by(|bounds| {
2280 if top < bounds.top() {
2281 Ordering::Greater
2282 } else if top > bounds.bottom() {
2283 Ordering::Less
2284 } else {
2285 Ordering::Equal
2286 }
2287 }) {
2288 Ok(ix) => ix,
2289 Err(ix) => ix.min(state.child_bounds.len().saturating_sub(1)),
2290 }
2291 }
2292
2293 /// Get the bounds for a specific child.
2294 pub fn bounds_for_item(&self, ix: usize) -> Option<Bounds<Pixels>> {
2295 self.0.borrow().child_bounds.get(ix).cloned()
2296 }
2297
2298 /// scroll_to_item scrolls the minimal amount to ensure that the child is
2299 /// fully visible
2300 pub fn scroll_to_item(&self, ix: usize) {
2301 let state = self.0.borrow();
2302
2303 let Some(bounds) = state.child_bounds.get(ix) else {
2304 return;
2305 };
2306
2307 let mut scroll_offset = state.offset.borrow_mut();
2308
2309 if state.overflow.y == Overflow::Scroll {
2310 if bounds.top() + scroll_offset.y < state.bounds.top() {
2311 scroll_offset.y = state.bounds.top() - bounds.top();
2312 } else if bounds.bottom() + scroll_offset.y > state.bounds.bottom() {
2313 scroll_offset.y = state.bounds.bottom() - bounds.bottom();
2314 }
2315 }
2316
2317 if state.overflow.x == Overflow::Scroll {
2318 if bounds.left() + scroll_offset.x < state.bounds.left() {
2319 scroll_offset.x = state.bounds.left() - bounds.left();
2320 } else if bounds.right() + scroll_offset.x > state.bounds.right() {
2321 scroll_offset.x = state.bounds.right() - bounds.right();
2322 }
2323 }
2324 }
2325
2326 /// Get the logical scroll top, based on a child index and a pixel offset.
2327 pub fn logical_scroll_top(&self) -> (usize, Pixels) {
2328 let ix = self.top_item();
2329 let state = self.0.borrow();
2330
2331 if let Some(child_bounds) = state.child_bounds.get(ix) {
2332 (
2333 ix,
2334 child_bounds.top() + state.offset.borrow().y - state.bounds.top(),
2335 )
2336 } else {
2337 (ix, px(0.))
2338 }
2339 }
2340
2341 /// Set the logical scroll top, based on a child index and a pixel offset.
2342 pub fn set_logical_scroll_top(&self, ix: usize, px: Pixels) {
2343 self.0.borrow_mut().requested_scroll_top = Some((ix, px));
2344 }
2345}