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 Action, AnyDrag, AnyElement, AnyTooltip, AnyView, App, Bounds, ClickEvent, DispatchPhase,
20 Element, ElementId, Entity, FocusHandle, Global, GlobalElementId, Hitbox, HitboxBehavior,
21 HitboxId, InspectorElementId, IntoElement, IsZero, KeyContext, KeyDownEvent, KeyUpEvent,
22 LayoutId, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
23 Overflow, ParentElement, Pixels, Point, Render, ScrollWheelEvent, SharedString, Size, Style,
24 StyleRefinement, Styled, Task, TooltipId, Visibility, Window, point, px, size,
25};
26use collections::HashMap;
27use refineable::Refineable;
28use smallvec::SmallVec;
29use std::{
30 any::{Any, TypeId},
31 cell::RefCell,
32 cmp::Ordering,
33 fmt::Debug,
34 marker::PhantomData,
35 mem,
36 rc::Rc,
37 sync::Arc,
38 time::Duration,
39};
40use util::ResultExt;
41
42use super::ImageCacheProvider;
43
44const DRAG_THRESHOLD: f64 = 2.;
45const TOOLTIP_SHOW_DELAY: Duration = Duration::from_millis(500);
46const HOVERABLE_TOOLTIP_HIDE_DELAY: Duration = Duration::from_millis(500);
47
48/// The styling information for a given group.
49pub struct GroupStyle {
50 /// The identifier for this group.
51 pub group: SharedString,
52
53 /// The specific style refinement that this group would apply
54 /// to its children.
55 pub style: Box<StyleRefinement>,
56}
57
58/// An event for when a drag is moving over this element, with the given state type.
59pub struct DragMoveEvent<T> {
60 /// The mouse move event that triggered this drag move event.
61 pub event: MouseMoveEvent,
62
63 /// The bounds of this element.
64 pub bounds: Bounds<Pixels>,
65 drag: PhantomData<T>,
66 dragged_item: Arc<dyn Any>,
67}
68
69impl<T: 'static> DragMoveEvent<T> {
70 /// Returns the drag state for this event.
71 pub fn drag<'b>(&self, cx: &'b App) -> &'b T {
72 cx.active_drag
73 .as_ref()
74 .and_then(|drag| drag.value.downcast_ref::<T>())
75 .expect("DragMoveEvent is only valid when the stored active drag is of the same type.")
76 }
77
78 /// An item that is about to be dropped.
79 pub fn dragged_item(&self) -> &dyn Any {
80 self.dragged_item.as_ref()
81 }
82}
83
84impl Interactivity {
85 /// Create an `Interactivity`, capturing the caller location in debug mode.
86 #[cfg(any(feature = "inspector", debug_assertions))]
87 #[track_caller]
88 pub fn new() -> Interactivity {
89 Interactivity {
90 source_location: Some(core::panic::Location::caller()),
91 ..Default::default()
92 }
93 }
94
95 /// Create an `Interactivity`, capturing the caller location in debug mode.
96 #[cfg(not(any(feature = "inspector", debug_assertions)))]
97 pub fn new() -> Interactivity {
98 Interactivity::default()
99 }
100
101 /// Gets the source location of construction. Returns `None` when not in debug mode.
102 pub fn source_location(&self) -> Option<&'static std::panic::Location<'static>> {
103 #[cfg(any(feature = "inspector", debug_assertions))]
104 {
105 self.source_location
106 }
107
108 #[cfg(not(any(feature = "inspector", debug_assertions)))]
109 {
110 None
111 }
112 }
113
114 /// Bind the given callback to the mouse down event for the given mouse button, during the bubble phase
115 /// The imperative API equivalent of [`InteractiveElement::on_mouse_down`]
116 ///
117 /// See [`Context::listener`](crate::Context::listener) to get access to the view state from this callback.
118 pub fn on_mouse_down(
119 &mut self,
120 button: MouseButton,
121 listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
122 ) {
123 self.mouse_down_listeners
124 .push(Box::new(move |event, phase, hitbox, window, cx| {
125 if phase == DispatchPhase::Bubble
126 && event.button == button
127 && hitbox.is_hovered(window)
128 {
129 (listener)(event, window, cx)
130 }
131 }));
132 }
133
134 /// Bind the given callback to the mouse down event for any button, during the capture phase
135 /// The imperative API equivalent of [`InteractiveElement::capture_any_mouse_down`]
136 ///
137 /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
138 pub fn capture_any_mouse_down(
139 &mut self,
140 listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
141 ) {
142 self.mouse_down_listeners
143 .push(Box::new(move |event, phase, hitbox, window, cx| {
144 if phase == DispatchPhase::Capture && hitbox.is_hovered(window) {
145 (listener)(event, window, cx)
146 }
147 }));
148 }
149
150 /// Bind the given callback to the mouse down event for any button, during the bubble phase
151 /// the imperative API equivalent to [`InteractiveElement::on_any_mouse_down`]
152 ///
153 /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
154 pub fn on_any_mouse_down(
155 &mut self,
156 listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
157 ) {
158 self.mouse_down_listeners
159 .push(Box::new(move |event, phase, hitbox, window, cx| {
160 if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
161 (listener)(event, window, cx)
162 }
163 }));
164 }
165
166 /// Bind the given callback to the mouse up event for the given button, during the bubble phase
167 /// the imperative API equivalent to [`InteractiveElement::on_mouse_up`]
168 ///
169 /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
170 pub fn on_mouse_up(
171 &mut self,
172 button: MouseButton,
173 listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
174 ) {
175 self.mouse_up_listeners
176 .push(Box::new(move |event, phase, hitbox, window, cx| {
177 if phase == DispatchPhase::Bubble
178 && event.button == button
179 && hitbox.is_hovered(window)
180 {
181 (listener)(event, window, cx)
182 }
183 }));
184 }
185
186 /// Bind the given callback to the mouse up event for any button, during the capture phase
187 /// the imperative API equivalent to [`InteractiveElement::capture_any_mouse_up`]
188 ///
189 /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
190 pub fn capture_any_mouse_up(
191 &mut self,
192 listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
193 ) {
194 self.mouse_up_listeners
195 .push(Box::new(move |event, phase, hitbox, window, cx| {
196 if phase == DispatchPhase::Capture && hitbox.is_hovered(window) {
197 (listener)(event, window, cx)
198 }
199 }));
200 }
201
202 /// Bind the given callback to the mouse up event for any button, during the bubble phase
203 /// the imperative API equivalent to [`Interactivity::on_any_mouse_up`]
204 ///
205 /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
206 pub fn on_any_mouse_up(
207 &mut self,
208 listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
209 ) {
210 self.mouse_up_listeners
211 .push(Box::new(move |event, phase, hitbox, window, cx| {
212 if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
213 (listener)(event, window, cx)
214 }
215 }));
216 }
217
218 /// Bind the given callback to the mouse down event, on any button, during the capture phase,
219 /// when the mouse is outside of the bounds of this element.
220 /// The imperative API equivalent to [`InteractiveElement::on_mouse_down_out`]
221 ///
222 /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
223 pub fn on_mouse_down_out(
224 &mut self,
225 listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
226 ) {
227 self.mouse_down_listeners
228 .push(Box::new(move |event, phase, hitbox, window, cx| {
229 if phase == DispatchPhase::Capture && !hitbox.contains(&window.mouse_position()) {
230 (listener)(event, window, cx)
231 }
232 }));
233 }
234
235 /// Bind the given callback to the mouse up event, for the given button, during the capture phase,
236 /// when the mouse is outside of the bounds of this element.
237 /// The imperative API equivalent to [`InteractiveElement::on_mouse_up_out`]
238 ///
239 /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
240 pub fn on_mouse_up_out(
241 &mut self,
242 button: MouseButton,
243 listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
244 ) {
245 self.mouse_up_listeners
246 .push(Box::new(move |event, phase, hitbox, window, cx| {
247 if phase == DispatchPhase::Capture
248 && event.button == button
249 && !hitbox.is_hovered(window)
250 {
251 (listener)(event, window, cx);
252 }
253 }));
254 }
255
256 /// Bind the given callback to the mouse move event, during the bubble phase
257 /// The imperative API equivalent to [`InteractiveElement::on_mouse_move`]
258 ///
259 /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
260 pub fn on_mouse_move(
261 &mut self,
262 listener: impl Fn(&MouseMoveEvent, &mut Window, &mut App) + 'static,
263 ) {
264 self.mouse_move_listeners
265 .push(Box::new(move |event, phase, hitbox, window, cx| {
266 if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
267 (listener)(event, window, cx);
268 }
269 }));
270 }
271
272 /// Bind the given callback to the mouse drag event of the given type. Note that this
273 /// will be called for all move events, inside or outside of this element, as long as the
274 /// drag was started with this element under the mouse. Useful for implementing draggable
275 /// UIs that don't conform to a drag and drop style interaction, like resizing.
276 /// The imperative API equivalent to [`InteractiveElement::on_drag_move`]
277 ///
278 /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
279 pub fn on_drag_move<T>(
280 &mut self,
281 listener: impl Fn(&DragMoveEvent<T>, &mut Window, &mut App) + 'static,
282 ) where
283 T: 'static,
284 {
285 self.mouse_move_listeners
286 .push(Box::new(move |event, phase, hitbox, window, cx| {
287 if phase == DispatchPhase::Capture {
288 if let Some(drag) = &cx.active_drag {
289 if drag.value.as_ref().type_id() == TypeId::of::<T>() {
290 (listener)(
291 &DragMoveEvent {
292 event: event.clone(),
293 bounds: hitbox.bounds,
294 drag: PhantomData,
295 dragged_item: Arc::clone(&drag.value),
296 },
297 window,
298 cx,
299 );
300 }
301 }
302 }
303 }));
304 }
305
306 /// Bind the given callback to scroll wheel events during the bubble phase
307 /// The imperative API equivalent to [`InteractiveElement::on_scroll_wheel`]
308 ///
309 /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
310 pub fn on_scroll_wheel(
311 &mut self,
312 listener: impl Fn(&ScrollWheelEvent, &mut Window, &mut App) + 'static,
313 ) {
314 self.scroll_wheel_listeners
315 .push(Box::new(move |event, phase, hitbox, window, cx| {
316 if phase == DispatchPhase::Bubble && hitbox.should_handle_scroll(window) {
317 (listener)(event, window, cx);
318 }
319 }));
320 }
321
322 /// Bind the given callback to an action dispatch during the capture phase
323 /// The imperative API equivalent to [`InteractiveElement::capture_action`]
324 ///
325 /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
326 pub fn capture_action<A: Action>(
327 &mut self,
328 listener: impl Fn(&A, &mut Window, &mut App) + 'static,
329 ) {
330 self.action_listeners.push((
331 TypeId::of::<A>(),
332 Box::new(move |action, phase, window, cx| {
333 let action = action.downcast_ref().unwrap();
334 if phase == DispatchPhase::Capture {
335 (listener)(action, window, cx)
336 } else {
337 cx.propagate();
338 }
339 }),
340 ));
341 }
342
343 /// Bind the given callback to an action dispatch during the bubble phase
344 /// The imperative API equivalent to [`InteractiveElement::on_action`]
345 ///
346 /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
347 pub fn on_action<A: Action>(&mut self, listener: impl Fn(&A, &mut Window, &mut App) + 'static) {
348 self.action_listeners.push((
349 TypeId::of::<A>(),
350 Box::new(move |action, phase, window, cx| {
351 let action = action.downcast_ref().unwrap();
352 if phase == DispatchPhase::Bubble {
353 (listener)(action, window, cx)
354 }
355 }),
356 ));
357 }
358
359 /// Bind the given callback to an action dispatch, based on a dynamic action parameter
360 /// instead of a type parameter. Useful for component libraries that want to expose
361 /// action bindings to their users.
362 /// The imperative API equivalent to [`InteractiveElement::on_boxed_action`]
363 ///
364 /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
365 pub fn on_boxed_action(
366 &mut self,
367 action: &dyn Action,
368 listener: impl Fn(&dyn Action, &mut Window, &mut App) + 'static,
369 ) {
370 let action = action.boxed_clone();
371 self.action_listeners.push((
372 (*action).type_id(),
373 Box::new(move |_, phase, window, cx| {
374 if phase == DispatchPhase::Bubble {
375 (listener)(&*action, window, cx)
376 }
377 }),
378 ));
379 }
380
381 /// Bind the given callback to key down events during the bubble phase
382 /// The imperative API equivalent to [`InteractiveElement::on_key_down`]
383 ///
384 /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
385 pub fn on_key_down(
386 &mut self,
387 listener: impl Fn(&KeyDownEvent, &mut Window, &mut App) + 'static,
388 ) {
389 self.key_down_listeners
390 .push(Box::new(move |event, phase, window, cx| {
391 if phase == DispatchPhase::Bubble {
392 (listener)(event, window, cx)
393 }
394 }));
395 }
396
397 /// Bind the given callback to key down events during the capture phase
398 /// The imperative API equivalent to [`InteractiveElement::capture_key_down`]
399 ///
400 /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
401 pub fn capture_key_down(
402 &mut self,
403 listener: impl Fn(&KeyDownEvent, &mut Window, &mut App) + 'static,
404 ) {
405 self.key_down_listeners
406 .push(Box::new(move |event, phase, window, cx| {
407 if phase == DispatchPhase::Capture {
408 listener(event, window, cx)
409 }
410 }));
411 }
412
413 /// Bind the given callback to key up events during the bubble phase
414 /// The imperative API equivalent to [`InteractiveElement::on_key_up`]
415 ///
416 /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
417 pub fn on_key_up(&mut self, listener: impl Fn(&KeyUpEvent, &mut Window, &mut App) + 'static) {
418 self.key_up_listeners
419 .push(Box::new(move |event, phase, window, cx| {
420 if phase == DispatchPhase::Bubble {
421 listener(event, window, cx)
422 }
423 }));
424 }
425
426 /// Bind the given callback to key up events during the capture phase
427 /// The imperative API equivalent to [`InteractiveElement::on_key_up`]
428 ///
429 /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
430 pub fn capture_key_up(
431 &mut self,
432 listener: impl Fn(&KeyUpEvent, &mut Window, &mut App) + 'static,
433 ) {
434 self.key_up_listeners
435 .push(Box::new(move |event, phase, window, cx| {
436 if phase == DispatchPhase::Capture {
437 listener(event, window, cx)
438 }
439 }));
440 }
441
442 /// Bind the given callback to modifiers changing events.
443 /// The imperative API equivalent to [`InteractiveElement::on_modifiers_changed`]
444 ///
445 /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
446 pub fn on_modifiers_changed(
447 &mut self,
448 listener: impl Fn(&ModifiersChangedEvent, &mut Window, &mut App) + 'static,
449 ) {
450 self.modifiers_changed_listeners
451 .push(Box::new(move |event, window, cx| {
452 listener(event, window, cx)
453 }));
454 }
455
456 /// Bind the given callback to drop events of the given type, whether or not the drag started on this element
457 /// The imperative API equivalent to [`InteractiveElement::on_drop`]
458 ///
459 /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
460 pub fn on_drop<T: 'static>(&mut self, listener: impl Fn(&T, &mut Window, &mut App) + 'static) {
461 self.drop_listeners.push((
462 TypeId::of::<T>(),
463 Box::new(move |dragged_value, window, cx| {
464 listener(dragged_value.downcast_ref().unwrap(), window, cx);
465 }),
466 ));
467 }
468
469 /// Use the given predicate to determine whether or not a drop event should be dispatched to this element
470 /// The imperative API equivalent to [`InteractiveElement::can_drop`]
471 pub fn can_drop(
472 &mut self,
473 predicate: impl Fn(&dyn Any, &mut Window, &mut App) -> bool + 'static,
474 ) {
475 self.can_drop_predicate = Some(Box::new(predicate));
476 }
477
478 /// Bind the given callback to click events of this element
479 /// The imperative API equivalent to [`StatefulInteractiveElement::on_click`]
480 ///
481 /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
482 pub fn on_click(&mut self, listener: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static)
483 where
484 Self: Sized,
485 {
486 self.click_listeners
487 .push(Box::new(move |event, window, cx| {
488 listener(event, window, cx)
489 }));
490 }
491
492 /// On drag initiation, this callback will be used to create a new view to render the dragged value for a
493 /// drag and drop operation. This API should also be used as the equivalent of 'on drag start' with
494 /// the [`Self::on_drag_move`] API
495 /// The imperative API equivalent to [`StatefulInteractiveElement::on_drag`]
496 ///
497 /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
498 pub fn on_drag<T, W>(
499 &mut self,
500 value: T,
501 constructor: impl Fn(&T, Point<Pixels>, &mut Window, &mut App) -> Entity<W> + 'static,
502 ) where
503 Self: Sized,
504 T: 'static,
505 W: 'static + Render,
506 {
507 debug_assert!(
508 self.drag_listener.is_none(),
509 "calling on_drag more than once on the same element is not supported"
510 );
511 self.drag_listener = Some((
512 Arc::new(value),
513 Box::new(move |value, offset, window, cx| {
514 constructor(value.downcast_ref().unwrap(), offset, window, cx).into()
515 }),
516 ));
517 }
518
519 /// Bind the given callback on the hover start and end events of this element. Note that the boolean
520 /// passed to the callback is true when the hover starts and false when it ends.
521 /// The imperative API equivalent to [`StatefulInteractiveElement::on_hover`]
522 ///
523 /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
524 pub fn on_hover(&mut self, listener: impl Fn(&bool, &mut Window, &mut App) + 'static)
525 where
526 Self: Sized,
527 {
528 debug_assert!(
529 self.hover_listener.is_none(),
530 "calling on_hover more than once on the same element is not supported"
531 );
532 self.hover_listener = Some(Box::new(listener));
533 }
534
535 /// Use the given callback to construct a new tooltip view when the mouse hovers over this element.
536 /// The imperative API equivalent to [`InteractiveElement::tooltip`]
537 pub fn tooltip(&mut self, build_tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static)
538 where
539 Self: Sized,
540 {
541 debug_assert!(
542 self.tooltip_builder.is_none(),
543 "calling tooltip more than once on the same element is not supported"
544 );
545 self.tooltip_builder = Some(TooltipBuilder {
546 build: Rc::new(build_tooltip),
547 hoverable: false,
548 });
549 }
550
551 /// Use the given callback to construct a new tooltip view when the mouse hovers over this element.
552 /// The tooltip itself is also hoverable and won't disappear when the user moves the mouse into
553 /// the tooltip. The imperative API equivalent to [`InteractiveElement::hoverable_tooltip`]
554 pub fn hoverable_tooltip(
555 &mut self,
556 build_tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static,
557 ) where
558 Self: Sized,
559 {
560 debug_assert!(
561 self.tooltip_builder.is_none(),
562 "calling tooltip more than once on the same element is not supported"
563 );
564 self.tooltip_builder = Some(TooltipBuilder {
565 build: Rc::new(build_tooltip),
566 hoverable: true,
567 });
568 }
569
570 /// Block the mouse from all interactions with elements behind this element's hitbox. Typically
571 /// `block_mouse_except_scroll` should be preferred.
572 ///
573 /// The imperative API equivalent to [`InteractiveElement::occlude`]
574 pub fn occlude_mouse(&mut self) {
575 self.hitbox_behavior = HitboxBehavior::BlockMouse;
576 }
577
578 /// Block non-scroll mouse interactions with elements behind this element's hitbox. See
579 /// [`Hitbox::is_hovered`] for details.
580 ///
581 /// The imperative API equivalent to [`InteractiveElement::block_mouse_except_scroll`]
582 pub fn block_mouse_except_scroll(&mut self) {
583 self.hitbox_behavior = HitboxBehavior::BlockMouseExceptScroll;
584 }
585}
586
587/// A trait for elements that want to use the standard GPUI event handlers that don't
588/// require any state.
589pub trait InteractiveElement: Sized {
590 /// Retrieve the interactivity state associated with this element
591 fn interactivity(&mut self) -> &mut Interactivity;
592
593 /// Assign this element to a group of elements that can be styled together
594 fn group(mut self, group: impl Into<SharedString>) -> Self {
595 self.interactivity().group = Some(group.into());
596 self
597 }
598
599 /// Assign this element an ID, so that it can be used with interactivity
600 fn id(mut self, id: impl Into<ElementId>) -> Stateful<Self> {
601 self.interactivity().element_id = Some(id.into());
602
603 Stateful { element: self }
604 }
605
606 /// Track the focus state of the given focus handle on this element.
607 /// If the focus handle is focused by the application, this element will
608 /// apply its focused styles.
609 fn track_focus(mut self, focus_handle: &FocusHandle) -> FocusableWrapper<Self> {
610 self.interactivity().focusable = true;
611 self.interactivity().tracked_focus_handle = Some(focus_handle.clone());
612 FocusableWrapper { element: self }
613 }
614
615 /// Set the keymap context for this element. This will be used to determine
616 /// which action to dispatch from the keymap.
617 fn key_context<C, E>(mut self, key_context: C) -> Self
618 where
619 C: TryInto<KeyContext, Error = E>,
620 E: Debug,
621 {
622 if let Some(key_context) = key_context.try_into().log_err() {
623 self.interactivity().key_context = Some(key_context);
624 }
625 self
626 }
627
628 /// Apply the given style to this element when the mouse hovers over it
629 fn hover(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self {
630 debug_assert!(
631 self.interactivity().hover_style.is_none(),
632 "hover style already set"
633 );
634 self.interactivity().hover_style = Some(Box::new(f(StyleRefinement::default())));
635 self
636 }
637
638 /// Apply the given style to this element when the mouse hovers over a group member
639 fn group_hover(
640 mut self,
641 group_name: impl Into<SharedString>,
642 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
643 ) -> Self {
644 self.interactivity().group_hover_style = Some(GroupStyle {
645 group: group_name.into(),
646 style: Box::new(f(StyleRefinement::default())),
647 });
648 self
649 }
650
651 /// Bind the given callback to the mouse down event for the given mouse button,
652 /// the fluent API equivalent to [`Interactivity::on_mouse_down`]
653 ///
654 /// See [`Context::listener`](crate::Context::listener) to get access to the view state from this callback.
655 fn on_mouse_down(
656 mut self,
657 button: MouseButton,
658 listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
659 ) -> Self {
660 self.interactivity().on_mouse_down(button, listener);
661 self
662 }
663
664 #[cfg(any(test, feature = "test-support"))]
665 /// Set a key that can be used to look up this element's bounds
666 /// in the [`VisualTestContext::debug_bounds`] map
667 /// This is a noop in release builds
668 fn debug_selector(mut self, f: impl FnOnce() -> String) -> Self {
669 self.interactivity().debug_selector = Some(f());
670 self
671 }
672
673 #[cfg(not(any(test, feature = "test-support")))]
674 /// Set a key that can be used to look up this element's bounds
675 /// in the [`VisualTestContext::debug_bounds`] map
676 /// This is a noop in release builds
677 #[inline]
678 fn debug_selector(self, _: impl FnOnce() -> String) -> Self {
679 self
680 }
681
682 /// Bind the given callback to the mouse down event for any button, during the capture phase
683 /// the fluent API equivalent to [`Interactivity::capture_any_mouse_down`]
684 ///
685 /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
686 fn capture_any_mouse_down(
687 mut self,
688 listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
689 ) -> Self {
690 self.interactivity().capture_any_mouse_down(listener);
691 self
692 }
693
694 /// Bind the given callback to the mouse down event for any button, during the capture phase
695 /// the fluent API equivalent to [`Interactivity::on_any_mouse_down`]
696 ///
697 /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
698 fn on_any_mouse_down(
699 mut self,
700 listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
701 ) -> Self {
702 self.interactivity().on_any_mouse_down(listener);
703 self
704 }
705
706 /// Bind the given callback to the mouse up event for the given button, during the bubble phase
707 /// the fluent API equivalent to [`Interactivity::on_mouse_up`]
708 ///
709 /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
710 fn on_mouse_up(
711 mut self,
712 button: MouseButton,
713 listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
714 ) -> Self {
715 self.interactivity().on_mouse_up(button, listener);
716 self
717 }
718
719 /// Bind the given callback to the mouse up event for any button, during the capture phase
720 /// the fluent API equivalent to [`Interactivity::capture_any_mouse_up`]
721 ///
722 /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
723 fn capture_any_mouse_up(
724 mut self,
725 listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
726 ) -> Self {
727 self.interactivity().capture_any_mouse_up(listener);
728 self
729 }
730
731 /// Bind the given callback to the mouse down event, on any button, during the capture phase,
732 /// when the mouse is outside of the bounds of this element.
733 /// The fluent API equivalent to [`Interactivity::on_mouse_down_out`]
734 ///
735 /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
736 fn on_mouse_down_out(
737 mut self,
738 listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
739 ) -> Self {
740 self.interactivity().on_mouse_down_out(listener);
741 self
742 }
743
744 /// Bind the given callback to the mouse up event, for the given button, during the capture phase,
745 /// when the mouse is outside of the bounds of this element.
746 /// The fluent API equivalent to [`Interactivity::on_mouse_up_out`]
747 ///
748 /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
749 fn on_mouse_up_out(
750 mut self,
751 button: MouseButton,
752 listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
753 ) -> Self {
754 self.interactivity().on_mouse_up_out(button, listener);
755 self
756 }
757
758 /// Bind the given callback to the mouse move event, during the bubble phase
759 /// The fluent API equivalent to [`Interactivity::on_mouse_move`]
760 ///
761 /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
762 fn on_mouse_move(
763 mut self,
764 listener: impl Fn(&MouseMoveEvent, &mut Window, &mut App) + 'static,
765 ) -> Self {
766 self.interactivity().on_mouse_move(listener);
767 self
768 }
769
770 /// Bind the given callback to the mouse drag event of the given type. Note that this
771 /// will be called for all move events, inside or outside of this element, as long as the
772 /// drag was started with this element under the mouse. Useful for implementing draggable
773 /// UIs that don't conform to a drag and drop style interaction, like resizing.
774 /// The fluent API equivalent to [`Interactivity::on_drag_move`]
775 ///
776 /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
777 fn on_drag_move<T: 'static>(
778 mut self,
779 listener: impl Fn(&DragMoveEvent<T>, &mut Window, &mut App) + 'static,
780 ) -> Self {
781 self.interactivity().on_drag_move(listener);
782 self
783 }
784
785 /// Bind the given callback to scroll wheel events during the bubble phase
786 /// The fluent API equivalent to [`Interactivity::on_scroll_wheel`]
787 ///
788 /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
789 fn on_scroll_wheel(
790 mut self,
791 listener: impl Fn(&ScrollWheelEvent, &mut Window, &mut App) + 'static,
792 ) -> Self {
793 self.interactivity().on_scroll_wheel(listener);
794 self
795 }
796
797 /// Capture the given action, before normal action dispatch can fire
798 /// The fluent API equivalent to [`Interactivity::on_scroll_wheel`]
799 ///
800 /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
801 fn capture_action<A: Action>(
802 mut self,
803 listener: impl Fn(&A, &mut Window, &mut App) + 'static,
804 ) -> Self {
805 self.interactivity().capture_action(listener);
806 self
807 }
808
809 /// Bind the given callback to an action dispatch during the bubble phase
810 /// The fluent API equivalent to [`Interactivity::on_action`]
811 ///
812 /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
813 fn on_action<A: Action>(
814 mut self,
815 listener: impl Fn(&A, &mut Window, &mut App) + 'static,
816 ) -> Self {
817 self.interactivity().on_action(listener);
818 self
819 }
820
821 /// Bind the given callback to an action dispatch, based on a dynamic action parameter
822 /// instead of a type parameter. Useful for component libraries that want to expose
823 /// action bindings to their users.
824 /// The fluent API equivalent to [`Interactivity::on_boxed_action`]
825 ///
826 /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
827 fn on_boxed_action(
828 mut self,
829 action: &dyn Action,
830 listener: impl Fn(&dyn Action, &mut Window, &mut App) + 'static,
831 ) -> Self {
832 self.interactivity().on_boxed_action(action, listener);
833 self
834 }
835
836 /// Bind the given callback to key down events during the bubble phase
837 /// The fluent API equivalent to [`Interactivity::on_key_down`]
838 ///
839 /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
840 fn on_key_down(
841 mut self,
842 listener: impl Fn(&KeyDownEvent, &mut Window, &mut App) + 'static,
843 ) -> Self {
844 self.interactivity().on_key_down(listener);
845 self
846 }
847
848 /// Bind the given callback to key down events during the capture phase
849 /// The fluent API equivalent to [`Interactivity::capture_key_down`]
850 ///
851 /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
852 fn capture_key_down(
853 mut self,
854 listener: impl Fn(&KeyDownEvent, &mut Window, &mut App) + 'static,
855 ) -> Self {
856 self.interactivity().capture_key_down(listener);
857 self
858 }
859
860 /// Bind the given callback to key up events during the bubble phase
861 /// The fluent API equivalent to [`Interactivity::on_key_up`]
862 ///
863 /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
864 fn on_key_up(
865 mut self,
866 listener: impl Fn(&KeyUpEvent, &mut Window, &mut App) + 'static,
867 ) -> Self {
868 self.interactivity().on_key_up(listener);
869 self
870 }
871
872 /// Bind the given callback to key up events during the capture phase
873 /// The fluent API equivalent to [`Interactivity::capture_key_up`]
874 ///
875 /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
876 fn capture_key_up(
877 mut self,
878 listener: impl Fn(&KeyUpEvent, &mut Window, &mut App) + 'static,
879 ) -> Self {
880 self.interactivity().capture_key_up(listener);
881 self
882 }
883
884 /// Bind the given callback to modifiers changing events.
885 /// The fluent API equivalent to [`Interactivity::on_modifiers_changed`]
886 ///
887 /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
888 fn on_modifiers_changed(
889 mut self,
890 listener: impl Fn(&ModifiersChangedEvent, &mut Window, &mut App) + 'static,
891 ) -> Self {
892 self.interactivity().on_modifiers_changed(listener);
893 self
894 }
895
896 /// Apply the given style when the given data type is dragged over this element
897 fn drag_over<S: 'static>(
898 mut self,
899 f: impl 'static + Fn(StyleRefinement, &S, &Window, &App) -> StyleRefinement,
900 ) -> Self {
901 self.interactivity().drag_over_styles.push((
902 TypeId::of::<S>(),
903 Box::new(move |currently_dragged: &dyn Any, window, cx| {
904 f(
905 StyleRefinement::default(),
906 currently_dragged.downcast_ref::<S>().unwrap(),
907 window,
908 cx,
909 )
910 }),
911 ));
912 self
913 }
914
915 /// Apply the given style when the given data type is dragged over this element's group
916 fn group_drag_over<S: 'static>(
917 mut self,
918 group_name: impl Into<SharedString>,
919 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
920 ) -> Self {
921 self.interactivity().group_drag_over_styles.push((
922 TypeId::of::<S>(),
923 GroupStyle {
924 group: group_name.into(),
925 style: Box::new(f(StyleRefinement::default())),
926 },
927 ));
928 self
929 }
930
931 /// Bind the given callback to drop events of the given type, whether or not the drag started on this element
932 /// The fluent API equivalent to [`Interactivity::on_drop`]
933 ///
934 /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
935 fn on_drop<T: 'static>(
936 mut self,
937 listener: impl Fn(&T, &mut Window, &mut App) + 'static,
938 ) -> Self {
939 self.interactivity().on_drop(listener);
940 self
941 }
942
943 /// Use the given predicate to determine whether or not a drop event should be dispatched to this element
944 /// The fluent API equivalent to [`Interactivity::can_drop`]
945 fn can_drop(
946 mut self,
947 predicate: impl Fn(&dyn Any, &mut Window, &mut App) -> bool + 'static,
948 ) -> Self {
949 self.interactivity().can_drop(predicate);
950 self
951 }
952
953 /// Block the mouse from all interactions with elements behind this element's hitbox. Typically
954 /// `block_mouse_except_scroll` should be preferred.
955 /// The fluent API equivalent to [`Interactivity::occlude_mouse`]
956 fn occlude(mut self) -> Self {
957 self.interactivity().occlude_mouse();
958 self
959 }
960
961 /// Stops propagation of left mouse down event.
962 fn block_mouse_down(mut self) -> Self {
963 self.on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
964 }
965
966 /// Block non-scroll mouse interactions with elements behind this element's hitbox. See
967 /// [`Hitbox::is_hovered`] for details.
968 ///
969 /// The fluent API equivalent to [`Interactivity::block_mouse_except_scroll`]
970 fn block_mouse_except_scroll(mut self) -> Self {
971 self.interactivity().block_mouse_except_scroll();
972 self
973 }
974}
975
976/// A trait for elements that want to use the standard GPUI interactivity features
977/// that require state.
978pub trait StatefulInteractiveElement: InteractiveElement {
979 /// Set this element to focusable.
980 fn focusable(mut self) -> FocusableWrapper<Self> {
981 self.interactivity().focusable = true;
982 FocusableWrapper { element: self }
983 }
984
985 /// Set the overflow x and y to scroll.
986 fn overflow_scroll(mut self) -> Self {
987 self.interactivity().base_style.overflow.x = Some(Overflow::Scroll);
988 self.interactivity().base_style.overflow.y = Some(Overflow::Scroll);
989 self
990 }
991
992 /// Set the overflow x to scroll.
993 fn overflow_x_scroll(mut self) -> Self {
994 self.interactivity().base_style.overflow.x = Some(Overflow::Scroll);
995 self
996 }
997
998 /// Set the overflow y to scroll.
999 fn overflow_y_scroll(mut self) -> Self {
1000 self.interactivity().base_style.overflow.y = Some(Overflow::Scroll);
1001 self
1002 }
1003
1004 /// Track the scroll state of this element with the given handle.
1005 fn track_scroll(mut self, scroll_handle: &ScrollHandle) -> Self {
1006 self.interactivity().tracked_scroll_handle = Some(scroll_handle.clone());
1007 self
1008 }
1009
1010 /// Track the scroll state of this element with the given handle.
1011 fn anchor_scroll(mut self, scroll_anchor: Option<ScrollAnchor>) -> Self {
1012 self.interactivity().scroll_anchor = scroll_anchor;
1013 self
1014 }
1015
1016 /// Set the given styles to be applied when this element is active.
1017 fn active(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
1018 where
1019 Self: Sized,
1020 {
1021 self.interactivity().active_style = Some(Box::new(f(StyleRefinement::default())));
1022 self
1023 }
1024
1025 /// Set the given styles to be applied when this element's group is active.
1026 fn group_active(
1027 mut self,
1028 group_name: impl Into<SharedString>,
1029 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
1030 ) -> Self
1031 where
1032 Self: Sized,
1033 {
1034 self.interactivity().group_active_style = Some(GroupStyle {
1035 group: group_name.into(),
1036 style: Box::new(f(StyleRefinement::default())),
1037 });
1038 self
1039 }
1040
1041 /// Bind the given callback to click events of this element
1042 /// The fluent API equivalent to [`Interactivity::on_click`]
1043 ///
1044 /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
1045 fn on_click(mut self, listener: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static) -> Self
1046 where
1047 Self: Sized,
1048 {
1049 self.interactivity().on_click(listener);
1050 self
1051 }
1052
1053 /// On drag initiation, this callback will be used to create a new view to render the dragged value for a
1054 /// drag and drop operation. This API should also be used as the equivalent of 'on drag start' with
1055 /// the [`Self::on_drag_move`] API.
1056 /// The callback also has access to the offset of triggering click from the origin of parent element.
1057 /// The fluent API equivalent to [`Interactivity::on_drag`]
1058 ///
1059 /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
1060 fn on_drag<T, W>(
1061 mut self,
1062 value: T,
1063 constructor: impl Fn(&T, Point<Pixels>, &mut Window, &mut App) -> Entity<W> + 'static,
1064 ) -> Self
1065 where
1066 Self: Sized,
1067 T: 'static,
1068 W: 'static + Render,
1069 {
1070 self.interactivity().on_drag(value, constructor);
1071 self
1072 }
1073
1074 /// Bind the given callback on the hover start and end events of this element. Note that the boolean
1075 /// passed to the callback is true when the hover starts and false when it ends.
1076 /// The fluent API equivalent to [`Interactivity::on_hover`]
1077 ///
1078 /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
1079 fn on_hover(mut self, listener: impl Fn(&bool, &mut Window, &mut App) + 'static) -> Self
1080 where
1081 Self: Sized,
1082 {
1083 self.interactivity().on_hover(listener);
1084 self
1085 }
1086
1087 /// Use the given callback to construct a new tooltip view when the mouse hovers over this element.
1088 /// The fluent API equivalent to [`Interactivity::tooltip`]
1089 fn tooltip(mut self, build_tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static) -> Self
1090 where
1091 Self: Sized,
1092 {
1093 self.interactivity().tooltip(build_tooltip);
1094 self
1095 }
1096
1097 /// Use the given callback to construct a new tooltip view when the mouse hovers over this element.
1098 /// The tooltip itself is also hoverable and won't disappear when the user moves the mouse into
1099 /// the tooltip. The fluent API equivalent to [`Interactivity::hoverable_tooltip`]
1100 fn hoverable_tooltip(
1101 mut self,
1102 build_tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static,
1103 ) -> Self
1104 where
1105 Self: Sized,
1106 {
1107 self.interactivity().hoverable_tooltip(build_tooltip);
1108 self
1109 }
1110}
1111
1112/// A trait for providing focus related APIs to interactive elements
1113pub trait FocusableElement: InteractiveElement {
1114 /// Set the given styles to be applied when this element, specifically, is focused.
1115 fn focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
1116 where
1117 Self: Sized,
1118 {
1119 self.interactivity().focus_style = Some(Box::new(f(StyleRefinement::default())));
1120 self
1121 }
1122
1123 /// Set the given styles to be applied when this element is inside another element that is focused.
1124 fn in_focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
1125 where
1126 Self: Sized,
1127 {
1128 self.interactivity().in_focus_style = Some(Box::new(f(StyleRefinement::default())));
1129 self
1130 }
1131}
1132
1133pub(crate) type MouseDownListener =
1134 Box<dyn Fn(&MouseDownEvent, DispatchPhase, &Hitbox, &mut Window, &mut App) + 'static>;
1135pub(crate) type MouseUpListener =
1136 Box<dyn Fn(&MouseUpEvent, DispatchPhase, &Hitbox, &mut Window, &mut App) + 'static>;
1137
1138pub(crate) type MouseMoveListener =
1139 Box<dyn Fn(&MouseMoveEvent, DispatchPhase, &Hitbox, &mut Window, &mut App) + 'static>;
1140
1141pub(crate) type ScrollWheelListener =
1142 Box<dyn Fn(&ScrollWheelEvent, DispatchPhase, &Hitbox, &mut Window, &mut App) + 'static>;
1143
1144pub(crate) type ClickListener = Box<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>;
1145
1146pub(crate) type DragListener =
1147 Box<dyn Fn(&dyn Any, Point<Pixels>, &mut Window, &mut App) -> AnyView + 'static>;
1148
1149type DropListener = Box<dyn Fn(&dyn Any, &mut Window, &mut App) + 'static>;
1150
1151type CanDropPredicate = Box<dyn Fn(&dyn Any, &mut Window, &mut App) -> bool + 'static>;
1152
1153pub(crate) struct TooltipBuilder {
1154 build: Rc<dyn Fn(&mut Window, &mut App) -> AnyView + 'static>,
1155 hoverable: bool,
1156}
1157
1158pub(crate) type KeyDownListener =
1159 Box<dyn Fn(&KeyDownEvent, DispatchPhase, &mut Window, &mut App) + 'static>;
1160
1161pub(crate) type KeyUpListener =
1162 Box<dyn Fn(&KeyUpEvent, DispatchPhase, &mut Window, &mut App) + 'static>;
1163
1164pub(crate) type ModifiersChangedListener =
1165 Box<dyn Fn(&ModifiersChangedEvent, &mut Window, &mut App) + 'static>;
1166
1167pub(crate) type ActionListener =
1168 Box<dyn Fn(&dyn Any, DispatchPhase, &mut Window, &mut App) + 'static>;
1169
1170/// Construct a new [`Div`] element
1171#[track_caller]
1172pub fn div() -> Div {
1173 Div {
1174 interactivity: Interactivity::new(),
1175 children: SmallVec::default(),
1176 prepaint_listener: None,
1177 image_cache: None,
1178 }
1179}
1180
1181/// A [`Div`] element, the all-in-one element for building complex UIs in GPUI
1182pub struct Div {
1183 interactivity: Interactivity,
1184 children: SmallVec<[AnyElement; 2]>,
1185 prepaint_listener: Option<Box<dyn Fn(Vec<Bounds<Pixels>>, &mut Window, &mut App) + 'static>>,
1186 image_cache: Option<Box<dyn ImageCacheProvider>>,
1187}
1188
1189impl Div {
1190 /// Add a listener to be called when the children of this `Div` are prepainted.
1191 /// This allows you to store the [`Bounds`] of the children for later use.
1192 pub fn on_children_prepainted(
1193 mut self,
1194 listener: impl Fn(Vec<Bounds<Pixels>>, &mut Window, &mut App) + 'static,
1195 ) -> Self {
1196 self.prepaint_listener = Some(Box::new(listener));
1197 self
1198 }
1199
1200 /// Add an image cache at the location of this div in the element tree.
1201 pub fn image_cache(mut self, cache: impl ImageCacheProvider) -> Self {
1202 self.image_cache = Some(Box::new(cache));
1203 self
1204 }
1205}
1206
1207/// A frame state for a `Div` element, which contains layout IDs for its children.
1208///
1209/// This struct is used internally by the `Div` element to manage the layout state of its children
1210/// during the UI update cycle. It holds a small vector of `LayoutId` values, each corresponding to
1211/// a child element of the `Div`. These IDs are used to query the layout engine for the computed
1212/// bounds of the children after the layout phase is complete.
1213pub struct DivFrameState {
1214 child_layout_ids: SmallVec<[LayoutId; 2]>,
1215}
1216
1217/// Interactivity state displayed an manipulated in the inspector.
1218#[derive(Clone)]
1219pub struct DivInspectorState {
1220 /// The inspected element's base style. This is used for both inspecting and modifying the
1221 /// state. In the future it will make sense to separate the read and write, possibly tracking
1222 /// the modifications.
1223 #[cfg(any(feature = "inspector", debug_assertions))]
1224 pub base_style: Box<StyleRefinement>,
1225 /// Inspects the bounds of the element.
1226 pub bounds: Bounds<Pixels>,
1227 /// Size of the children of the element, or `bounds.size` if it has no children.
1228 pub content_size: Size<Pixels>,
1229}
1230
1231impl Styled for Div {
1232 fn style(&mut self) -> &mut StyleRefinement {
1233 &mut self.interactivity.base_style
1234 }
1235}
1236
1237impl InteractiveElement for Div {
1238 fn interactivity(&mut self) -> &mut Interactivity {
1239 &mut self.interactivity
1240 }
1241}
1242
1243impl ParentElement for Div {
1244 fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
1245 self.children.extend(elements)
1246 }
1247}
1248
1249impl Element for Div {
1250 type RequestLayoutState = DivFrameState;
1251 type PrepaintState = Option<Hitbox>;
1252
1253 fn id(&self) -> Option<ElementId> {
1254 self.interactivity.element_id.clone()
1255 }
1256
1257 fn source_location(&self) -> Option<&'static std::panic::Location<'static>> {
1258 self.interactivity.source_location()
1259 }
1260
1261 fn request_layout(
1262 &mut self,
1263 global_id: Option<&GlobalElementId>,
1264 inspector_id: Option<&InspectorElementId>,
1265 window: &mut Window,
1266 cx: &mut App,
1267 ) -> (LayoutId, Self::RequestLayoutState) {
1268 let mut child_layout_ids = SmallVec::new();
1269 let image_cache = self
1270 .image_cache
1271 .as_mut()
1272 .map(|provider| provider.provide(window, cx));
1273
1274 let layout_id = window.with_image_cache(image_cache, |window| {
1275 self.interactivity.request_layout(
1276 global_id,
1277 inspector_id,
1278 window,
1279 cx,
1280 |style, window, cx| {
1281 window.with_text_style(style.text_style().cloned(), |window| {
1282 child_layout_ids = self
1283 .children
1284 .iter_mut()
1285 .map(|child| child.request_layout(window, cx))
1286 .collect::<SmallVec<_>>();
1287 window.request_layout(style, child_layout_ids.iter().copied(), cx)
1288 })
1289 },
1290 )
1291 });
1292
1293 (layout_id, DivFrameState { child_layout_ids })
1294 }
1295
1296 fn prepaint(
1297 &mut self,
1298 global_id: Option<&GlobalElementId>,
1299 inspector_id: Option<&InspectorElementId>,
1300 bounds: Bounds<Pixels>,
1301 request_layout: &mut Self::RequestLayoutState,
1302 window: &mut Window,
1303 cx: &mut App,
1304 ) -> Option<Hitbox> {
1305 let has_prepaint_listener = self.prepaint_listener.is_some();
1306 let mut children_bounds = Vec::with_capacity(if has_prepaint_listener {
1307 request_layout.child_layout_ids.len()
1308 } else {
1309 0
1310 });
1311
1312 let mut child_min = point(Pixels::MAX, Pixels::MAX);
1313 let mut child_max = Point::default();
1314 if let Some(handle) = self.interactivity.scroll_anchor.as_ref() {
1315 *handle.last_origin.borrow_mut() = bounds.origin - window.element_offset();
1316 }
1317 let content_size = if request_layout.child_layout_ids.is_empty() {
1318 bounds.size
1319 } else if let Some(scroll_handle) = self.interactivity.tracked_scroll_handle.as_ref() {
1320 let mut state = scroll_handle.0.borrow_mut();
1321 state.child_bounds = Vec::with_capacity(request_layout.child_layout_ids.len());
1322 state.bounds = bounds;
1323 for child_layout_id in &request_layout.child_layout_ids {
1324 let child_bounds = window.layout_bounds(*child_layout_id);
1325 child_min = child_min.min(&child_bounds.origin);
1326 child_max = child_max.max(&child_bounds.bottom_right());
1327 state.child_bounds.push(child_bounds);
1328 }
1329 (child_max - child_min).into()
1330 } else {
1331 for child_layout_id in &request_layout.child_layout_ids {
1332 let child_bounds = window.layout_bounds(*child_layout_id);
1333 child_min = child_min.min(&child_bounds.origin);
1334 child_max = child_max.max(&child_bounds.bottom_right());
1335
1336 if has_prepaint_listener {
1337 children_bounds.push(child_bounds);
1338 }
1339 }
1340 (child_max - child_min).into()
1341 };
1342
1343 self.interactivity.prepaint(
1344 global_id,
1345 inspector_id,
1346 bounds,
1347 content_size,
1348 window,
1349 cx,
1350 |_style, scroll_offset, hitbox, window, cx| {
1351 window.with_element_offset(scroll_offset, |window| {
1352 for child in &mut self.children {
1353 child.prepaint(window, cx);
1354 }
1355 });
1356
1357 if let Some(listener) = self.prepaint_listener.as_ref() {
1358 listener(children_bounds, window, cx);
1359 }
1360
1361 hitbox
1362 },
1363 )
1364 }
1365
1366 fn paint(
1367 &mut self,
1368 global_id: Option<&GlobalElementId>,
1369 inspector_id: Option<&InspectorElementId>,
1370 bounds: Bounds<Pixels>,
1371 _request_layout: &mut Self::RequestLayoutState,
1372 hitbox: &mut Option<Hitbox>,
1373 window: &mut Window,
1374 cx: &mut App,
1375 ) {
1376 let image_cache = self
1377 .image_cache
1378 .as_mut()
1379 .map(|provider| provider.provide(window, cx));
1380
1381 window.with_image_cache(image_cache, |window| {
1382 self.interactivity.paint(
1383 global_id,
1384 inspector_id,
1385 bounds,
1386 hitbox.as_ref(),
1387 window,
1388 cx,
1389 |_style, window, cx| {
1390 for child in &mut self.children {
1391 child.paint(window, cx);
1392 }
1393 },
1394 )
1395 });
1396 }
1397}
1398
1399impl IntoElement for Div {
1400 type Element = Self;
1401
1402 fn into_element(self) -> Self::Element {
1403 self
1404 }
1405}
1406
1407/// The interactivity struct. Powers all of the general-purpose
1408/// interactivity in the `Div` element.
1409#[derive(Default)]
1410pub struct Interactivity {
1411 /// The element ID of the element. In id is required to support a stateful subset of the interactivity such as on_click.
1412 pub element_id: Option<ElementId>,
1413 /// Whether the element was clicked. This will only be present after layout.
1414 pub active: Option<bool>,
1415 /// Whether the element was hovered. This will only be present after paint if an hitbox
1416 /// was created for the interactive element.
1417 pub hovered: Option<bool>,
1418 pub(crate) tooltip_id: Option<TooltipId>,
1419 pub(crate) content_size: Size<Pixels>,
1420 pub(crate) key_context: Option<KeyContext>,
1421 pub(crate) focusable: bool,
1422 pub(crate) tracked_focus_handle: Option<FocusHandle>,
1423 pub(crate) tracked_scroll_handle: Option<ScrollHandle>,
1424 pub(crate) scroll_anchor: Option<ScrollAnchor>,
1425 pub(crate) scroll_offset: Option<Rc<RefCell<Point<Pixels>>>>,
1426 pub(crate) group: Option<SharedString>,
1427 /// The base style of the element, before any modifications are applied
1428 /// by focus, active, etc.
1429 pub base_style: Box<StyleRefinement>,
1430 pub(crate) focus_style: Option<Box<StyleRefinement>>,
1431 pub(crate) in_focus_style: Option<Box<StyleRefinement>>,
1432 pub(crate) hover_style: Option<Box<StyleRefinement>>,
1433 pub(crate) group_hover_style: Option<GroupStyle>,
1434 pub(crate) active_style: Option<Box<StyleRefinement>>,
1435 pub(crate) group_active_style: Option<GroupStyle>,
1436 pub(crate) drag_over_styles: Vec<(
1437 TypeId,
1438 Box<dyn Fn(&dyn Any, &mut Window, &mut App) -> StyleRefinement>,
1439 )>,
1440 pub(crate) group_drag_over_styles: Vec<(TypeId, GroupStyle)>,
1441 pub(crate) mouse_down_listeners: Vec<MouseDownListener>,
1442 pub(crate) mouse_up_listeners: Vec<MouseUpListener>,
1443 pub(crate) mouse_move_listeners: Vec<MouseMoveListener>,
1444 pub(crate) scroll_wheel_listeners: Vec<ScrollWheelListener>,
1445 pub(crate) key_down_listeners: Vec<KeyDownListener>,
1446 pub(crate) key_up_listeners: Vec<KeyUpListener>,
1447 pub(crate) modifiers_changed_listeners: Vec<ModifiersChangedListener>,
1448 pub(crate) action_listeners: Vec<(TypeId, ActionListener)>,
1449 pub(crate) drop_listeners: Vec<(TypeId, DropListener)>,
1450 pub(crate) can_drop_predicate: Option<CanDropPredicate>,
1451 pub(crate) click_listeners: Vec<ClickListener>,
1452 pub(crate) drag_listener: Option<(Arc<dyn Any>, DragListener)>,
1453 pub(crate) hover_listener: Option<Box<dyn Fn(&bool, &mut Window, &mut App)>>,
1454 pub(crate) tooltip_builder: Option<TooltipBuilder>,
1455 pub(crate) hitbox_behavior: HitboxBehavior,
1456
1457 #[cfg(any(feature = "inspector", debug_assertions))]
1458 pub(crate) source_location: Option<&'static core::panic::Location<'static>>,
1459
1460 #[cfg(any(test, feature = "test-support"))]
1461 pub(crate) debug_selector: Option<String>,
1462}
1463
1464impl Interactivity {
1465 /// Layout this element according to this interactivity state's configured styles
1466 pub fn request_layout(
1467 &mut self,
1468 global_id: Option<&GlobalElementId>,
1469 _inspector_id: Option<&InspectorElementId>,
1470 window: &mut Window,
1471 cx: &mut App,
1472 f: impl FnOnce(Style, &mut Window, &mut App) -> LayoutId,
1473 ) -> LayoutId {
1474 #[cfg(any(feature = "inspector", debug_assertions))]
1475 window.with_inspector_state(
1476 _inspector_id,
1477 cx,
1478 |inspector_state: &mut Option<DivInspectorState>, _window| {
1479 if let Some(inspector_state) = inspector_state {
1480 self.base_style = inspector_state.base_style.clone();
1481 } else {
1482 *inspector_state = Some(DivInspectorState {
1483 base_style: self.base_style.clone(),
1484 bounds: Default::default(),
1485 content_size: Default::default(),
1486 })
1487 }
1488 },
1489 );
1490
1491 window.with_optional_element_state::<InteractiveElementState, _>(
1492 global_id,
1493 |element_state, window| {
1494 let mut element_state =
1495 element_state.map(|element_state| element_state.unwrap_or_default());
1496
1497 if let Some(element_state) = element_state.as_ref() {
1498 if cx.has_active_drag() {
1499 if let Some(pending_mouse_down) = element_state.pending_mouse_down.as_ref()
1500 {
1501 *pending_mouse_down.borrow_mut() = None;
1502 }
1503 if let Some(clicked_state) = element_state.clicked_state.as_ref() {
1504 *clicked_state.borrow_mut() = ElementClickedState::default();
1505 }
1506 }
1507 }
1508
1509 // Ensure we store a focus handle in our element state if we're focusable.
1510 // If there's an explicit focus handle we're tracking, use that. Otherwise
1511 // create a new handle and store it in the element state, which lives for as
1512 // as frames contain an element with this id.
1513 if self.focusable && self.tracked_focus_handle.is_none() {
1514 if let Some(element_state) = element_state.as_mut() {
1515 self.tracked_focus_handle = Some(
1516 element_state
1517 .focus_handle
1518 .get_or_insert_with(|| cx.focus_handle())
1519 .clone(),
1520 );
1521 }
1522 }
1523
1524 if let Some(scroll_handle) = self.tracked_scroll_handle.as_ref() {
1525 self.scroll_offset = Some(scroll_handle.0.borrow().offset.clone());
1526 } else if self.base_style.overflow.x == Some(Overflow::Scroll)
1527 || self.base_style.overflow.y == Some(Overflow::Scroll)
1528 {
1529 if let Some(element_state) = element_state.as_mut() {
1530 self.scroll_offset = Some(
1531 element_state
1532 .scroll_offset
1533 .get_or_insert_with(Rc::default)
1534 .clone(),
1535 );
1536 }
1537 }
1538
1539 let style = self.compute_style_internal(None, element_state.as_mut(), window, cx);
1540 let layout_id = f(style, window, cx);
1541 (layout_id, element_state)
1542 },
1543 )
1544 }
1545
1546 /// Commit the bounds of this element according to this interactivity state's configured styles.
1547 pub fn prepaint<R>(
1548 &mut self,
1549 global_id: Option<&GlobalElementId>,
1550 _inspector_id: Option<&InspectorElementId>,
1551 bounds: Bounds<Pixels>,
1552 content_size: Size<Pixels>,
1553 window: &mut Window,
1554 cx: &mut App,
1555 f: impl FnOnce(&Style, Point<Pixels>, Option<Hitbox>, &mut Window, &mut App) -> R,
1556 ) -> R {
1557 self.content_size = content_size;
1558
1559 #[cfg(any(feature = "inspector", debug_assertions))]
1560 window.with_inspector_state(
1561 _inspector_id,
1562 cx,
1563 |inspector_state: &mut Option<DivInspectorState>, _window| {
1564 if let Some(inspector_state) = inspector_state {
1565 inspector_state.bounds = bounds;
1566 inspector_state.content_size = content_size;
1567 }
1568 },
1569 );
1570
1571 if let Some(focus_handle) = self.tracked_focus_handle.as_ref() {
1572 window.set_focus_handle(focus_handle, cx);
1573 }
1574 window.with_optional_element_state::<InteractiveElementState, _>(
1575 global_id,
1576 |element_state, window| {
1577 let mut element_state =
1578 element_state.map(|element_state| element_state.unwrap_or_default());
1579 let style = self.compute_style_internal(None, element_state.as_mut(), window, cx);
1580
1581 if let Some(element_state) = element_state.as_mut() {
1582 if let Some(clicked_state) = element_state.clicked_state.as_ref() {
1583 let clicked_state = clicked_state.borrow();
1584 self.active = Some(clicked_state.element);
1585 }
1586 if let Some(active_tooltip) = element_state.active_tooltip.as_ref() {
1587 if self.tooltip_builder.is_some() {
1588 self.tooltip_id = set_tooltip_on_window(active_tooltip, window);
1589 } else {
1590 // If there is no longer a tooltip builder, remove the active tooltip.
1591 element_state.active_tooltip.take();
1592 }
1593 }
1594 }
1595
1596 window.with_text_style(style.text_style().cloned(), |window| {
1597 window.with_content_mask(
1598 style.overflow_mask(bounds, window.rem_size()),
1599 |window| {
1600 let hitbox = if self.should_insert_hitbox(&style, window, cx) {
1601 Some(window.insert_hitbox(bounds, self.hitbox_behavior))
1602 } else {
1603 None
1604 };
1605
1606 let scroll_offset =
1607 self.clamp_scroll_position(bounds, &style, window, cx);
1608 let result = f(&style, scroll_offset, hitbox, window, cx);
1609 (result, element_state)
1610 },
1611 )
1612 })
1613 },
1614 )
1615 }
1616
1617 fn should_insert_hitbox(&self, style: &Style, window: &Window, cx: &App) -> bool {
1618 self.hitbox_behavior != HitboxBehavior::Normal
1619 || style.mouse_cursor.is_some()
1620 || self.group.is_some()
1621 || self.scroll_offset.is_some()
1622 || self.tracked_focus_handle.is_some()
1623 || self.hover_style.is_some()
1624 || self.group_hover_style.is_some()
1625 || self.hover_listener.is_some()
1626 || !self.mouse_up_listeners.is_empty()
1627 || !self.mouse_down_listeners.is_empty()
1628 || !self.mouse_move_listeners.is_empty()
1629 || !self.click_listeners.is_empty()
1630 || !self.scroll_wheel_listeners.is_empty()
1631 || self.drag_listener.is_some()
1632 || !self.drop_listeners.is_empty()
1633 || self.tooltip_builder.is_some()
1634 || window.is_inspector_picking(cx)
1635 }
1636
1637 fn clamp_scroll_position(
1638 &self,
1639 bounds: Bounds<Pixels>,
1640 style: &Style,
1641 window: &mut Window,
1642 _cx: &mut App,
1643 ) -> Point<Pixels> {
1644 if let Some(scroll_offset) = self.scroll_offset.as_ref() {
1645 let mut scroll_to_bottom = false;
1646 let mut tracked_scroll_handle = self
1647 .tracked_scroll_handle
1648 .as_ref()
1649 .map(|handle| handle.0.borrow_mut());
1650 if let Some(mut scroll_handle_state) = tracked_scroll_handle.as_deref_mut() {
1651 scroll_handle_state.overflow = style.overflow;
1652 scroll_to_bottom = mem::take(&mut scroll_handle_state.scroll_to_bottom);
1653 }
1654
1655 let rem_size = window.rem_size();
1656 let padding = style.padding.to_pixels(bounds.size.into(), rem_size);
1657 let padding_size = size(padding.left + padding.right, padding.top + padding.bottom);
1658 let padded_content_size = self.content_size + padding_size;
1659 let scroll_max = (padded_content_size - bounds.size).max(&Size::default());
1660 // Clamp scroll offset in case scroll max is smaller now (e.g., if children
1661 // were removed or the bounds became larger).
1662 let mut scroll_offset = scroll_offset.borrow_mut();
1663
1664 scroll_offset.x = scroll_offset.x.clamp(-scroll_max.width, px(0.));
1665 if scroll_to_bottom {
1666 scroll_offset.y = -scroll_max.height;
1667 } else {
1668 scroll_offset.y = scroll_offset.y.clamp(-scroll_max.height, px(0.));
1669 }
1670
1671 if let Some(mut scroll_handle_state) = tracked_scroll_handle {
1672 scroll_handle_state.padded_content_size = padded_content_size;
1673 }
1674
1675 *scroll_offset
1676 } else {
1677 Point::default()
1678 }
1679 }
1680
1681 /// Paint this element according to this interactivity state's configured styles
1682 /// and bind the element's mouse and keyboard events.
1683 ///
1684 /// content_size is the size of the content of the element, which may be larger than the
1685 /// element's bounds if the element is scrollable.
1686 ///
1687 /// the final computed style will be passed to the provided function, along
1688 /// with the current scroll offset
1689 pub fn paint(
1690 &mut self,
1691 global_id: Option<&GlobalElementId>,
1692 _inspector_id: Option<&InspectorElementId>,
1693 bounds: Bounds<Pixels>,
1694 hitbox: Option<&Hitbox>,
1695 window: &mut Window,
1696 cx: &mut App,
1697 f: impl FnOnce(&Style, &mut Window, &mut App),
1698 ) {
1699 self.hovered = hitbox.map(|hitbox| hitbox.is_hovered(window));
1700 window.with_optional_element_state::<InteractiveElementState, _>(
1701 global_id,
1702 |element_state, window| {
1703 let mut element_state =
1704 element_state.map(|element_state| element_state.unwrap_or_default());
1705
1706 let style = self.compute_style_internal(hitbox, element_state.as_mut(), window, cx);
1707
1708 #[cfg(any(feature = "test-support", test))]
1709 if let Some(debug_selector) = &self.debug_selector {
1710 window
1711 .next_frame
1712 .debug_bounds
1713 .insert(debug_selector.clone(), bounds);
1714 }
1715
1716 self.paint_hover_group_handler(window, cx);
1717
1718 if style.visibility == Visibility::Hidden {
1719 return ((), element_state);
1720 }
1721
1722 window.with_element_opacity(style.opacity, |window| {
1723 style.paint(bounds, window, cx, |window: &mut Window, cx: &mut App| {
1724 window.with_text_style(style.text_style().cloned(), |window| {
1725 window.with_content_mask(
1726 style.overflow_mask(bounds, window.rem_size()),
1727 |window| {
1728 if let Some(hitbox) = hitbox {
1729 #[cfg(debug_assertions)]
1730 self.paint_debug_info(
1731 global_id, hitbox, &style, window, cx,
1732 );
1733
1734 if let Some(drag) = cx.active_drag.as_ref() {
1735 if let Some(mouse_cursor) = drag.cursor_style {
1736 window.set_cursor_style(mouse_cursor, None);
1737 }
1738 } else {
1739 if let Some(mouse_cursor) = style.mouse_cursor {
1740 window.set_cursor_style(mouse_cursor, Some(hitbox));
1741 }
1742 }
1743
1744 if let Some(group) = self.group.clone() {
1745 GroupHitboxes::push(group, hitbox.id, cx);
1746 }
1747
1748 self.paint_mouse_listeners(
1749 hitbox,
1750 element_state.as_mut(),
1751 window,
1752 cx,
1753 );
1754 self.paint_scroll_listener(hitbox, &style, window, cx);
1755 }
1756
1757 self.paint_keyboard_listeners(window, cx);
1758 f(&style, window, cx);
1759
1760 if let Some(_hitbox) = hitbox {
1761 #[cfg(any(feature = "inspector", debug_assertions))]
1762 window.insert_inspector_hitbox(
1763 _hitbox.id,
1764 _inspector_id,
1765 cx,
1766 );
1767
1768 if let Some(group) = self.group.as_ref() {
1769 GroupHitboxes::pop(group, cx);
1770 }
1771 }
1772 },
1773 );
1774 });
1775 });
1776 });
1777
1778 ((), element_state)
1779 },
1780 );
1781 }
1782
1783 #[cfg(debug_assertions)]
1784 fn paint_debug_info(
1785 &self,
1786 global_id: Option<&GlobalElementId>,
1787 hitbox: &Hitbox,
1788 style: &Style,
1789 window: &mut Window,
1790 cx: &mut App,
1791 ) {
1792 use crate::{BorderStyle, TextAlign};
1793
1794 if global_id.is_some()
1795 && (style.debug || style.debug_below || cx.has_global::<crate::DebugBelow>())
1796 && hitbox.is_hovered(window)
1797 {
1798 const FONT_SIZE: crate::Pixels = crate::Pixels(10.);
1799 let element_id = format!("{:?}", global_id.unwrap());
1800 let str_len = element_id.len();
1801
1802 let render_debug_text = |window: &mut Window| {
1803 if let Some(text) = window
1804 .text_system()
1805 .shape_text(
1806 element_id.into(),
1807 FONT_SIZE,
1808 &[window.text_style().to_run(str_len)],
1809 None,
1810 None,
1811 )
1812 .ok()
1813 .and_then(|mut text| text.pop())
1814 {
1815 text.paint(hitbox.origin, FONT_SIZE, TextAlign::Left, None, window, cx)
1816 .ok();
1817
1818 let text_bounds = crate::Bounds {
1819 origin: hitbox.origin,
1820 size: text.size(FONT_SIZE),
1821 };
1822 if self.source_location.is_some()
1823 && text_bounds.contains(&window.mouse_position())
1824 && window.modifiers().secondary()
1825 {
1826 let secondary_held = window.modifiers().secondary();
1827 window.on_key_event({
1828 move |e: &crate::ModifiersChangedEvent, _phase, window, _cx| {
1829 if e.modifiers.secondary() != secondary_held
1830 && text_bounds.contains(&window.mouse_position())
1831 {
1832 window.refresh();
1833 }
1834 }
1835 });
1836
1837 let was_hovered = hitbox.is_hovered(window);
1838 let current_view = window.current_view();
1839 window.on_mouse_event({
1840 let hitbox = hitbox.clone();
1841 move |_: &MouseMoveEvent, phase, window, cx| {
1842 if phase == DispatchPhase::Capture {
1843 let hovered = hitbox.is_hovered(window);
1844 if hovered != was_hovered {
1845 cx.notify(current_view)
1846 }
1847 }
1848 }
1849 });
1850
1851 window.on_mouse_event({
1852 let hitbox = hitbox.clone();
1853 let location = self.source_location.unwrap();
1854 move |e: &crate::MouseDownEvent, phase, window, cx| {
1855 if text_bounds.contains(&e.position)
1856 && phase.capture()
1857 && hitbox.is_hovered(window)
1858 {
1859 cx.stop_propagation();
1860 let Ok(dir) = std::env::current_dir() else {
1861 return;
1862 };
1863
1864 eprintln!(
1865 "This element was created at:\n{}:{}:{}",
1866 dir.join(location.file()).to_string_lossy(),
1867 location.line(),
1868 location.column()
1869 );
1870 }
1871 }
1872 });
1873 window.paint_quad(crate::outline(
1874 crate::Bounds {
1875 origin: hitbox.origin
1876 + crate::point(crate::px(0.), FONT_SIZE - px(2.)),
1877 size: crate::Size {
1878 width: text_bounds.size.width,
1879 height: crate::px(1.),
1880 },
1881 },
1882 crate::red(),
1883 BorderStyle::default(),
1884 ))
1885 }
1886 }
1887 };
1888
1889 window.with_text_style(
1890 Some(crate::TextStyleRefinement {
1891 color: Some(crate::red()),
1892 line_height: Some(FONT_SIZE.into()),
1893 background_color: Some(crate::white()),
1894 ..Default::default()
1895 }),
1896 render_debug_text,
1897 )
1898 }
1899 }
1900
1901 fn paint_mouse_listeners(
1902 &mut self,
1903 hitbox: &Hitbox,
1904 element_state: Option<&mut InteractiveElementState>,
1905 window: &mut Window,
1906 cx: &mut App,
1907 ) {
1908 // If this element can be focused, register a mouse down listener
1909 // that will automatically transfer focus when hitting the element.
1910 // This behavior can be suppressed by using `cx.prevent_default()`.
1911 if let Some(focus_handle) = self.tracked_focus_handle.clone() {
1912 let hitbox = hitbox.clone();
1913 window.on_mouse_event(move |_: &MouseDownEvent, phase, window, _| {
1914 if phase == DispatchPhase::Bubble
1915 && hitbox.is_hovered(window)
1916 && !window.default_prevented()
1917 {
1918 window.focus(&focus_handle);
1919 // If there is a parent that is also focusable, prevent it
1920 // from transferring focus because we already did so.
1921 window.prevent_default();
1922 }
1923 });
1924 }
1925
1926 for listener in self.mouse_down_listeners.drain(..) {
1927 let hitbox = hitbox.clone();
1928 window.on_mouse_event(move |event: &MouseDownEvent, phase, window, cx| {
1929 listener(event, phase, &hitbox, window, cx);
1930 })
1931 }
1932
1933 for listener in self.mouse_up_listeners.drain(..) {
1934 let hitbox = hitbox.clone();
1935 window.on_mouse_event(move |event: &MouseUpEvent, phase, window, cx| {
1936 listener(event, phase, &hitbox, window, cx);
1937 })
1938 }
1939
1940 for listener in self.mouse_move_listeners.drain(..) {
1941 let hitbox = hitbox.clone();
1942 window.on_mouse_event(move |event: &MouseMoveEvent, phase, window, cx| {
1943 listener(event, phase, &hitbox, window, cx);
1944 })
1945 }
1946
1947 for listener in self.scroll_wheel_listeners.drain(..) {
1948 let hitbox = hitbox.clone();
1949 window.on_mouse_event(move |event: &ScrollWheelEvent, phase, window, cx| {
1950 listener(event, phase, &hitbox, window, cx);
1951 })
1952 }
1953
1954 if self.hover_style.is_some()
1955 || self.base_style.mouse_cursor.is_some()
1956 || cx.active_drag.is_some() && !self.drag_over_styles.is_empty()
1957 {
1958 let hitbox = hitbox.clone();
1959 let was_hovered = hitbox.is_hovered(window);
1960 let current_view = window.current_view();
1961 window.on_mouse_event(move |_: &MouseMoveEvent, phase, window, cx| {
1962 let hovered = hitbox.is_hovered(window);
1963 if phase == DispatchPhase::Capture && hovered != was_hovered {
1964 cx.notify(current_view);
1965 }
1966 });
1967 }
1968 let drag_cursor_style = self.base_style.as_ref().mouse_cursor;
1969
1970 let mut drag_listener = mem::take(&mut self.drag_listener);
1971 let drop_listeners = mem::take(&mut self.drop_listeners);
1972 let click_listeners = mem::take(&mut self.click_listeners);
1973 let can_drop_predicate = mem::take(&mut self.can_drop_predicate);
1974
1975 if !drop_listeners.is_empty() {
1976 let hitbox = hitbox.clone();
1977 window.on_mouse_event({
1978 move |_: &MouseUpEvent, phase, window, cx| {
1979 if let Some(drag) = &cx.active_drag {
1980 if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
1981 let drag_state_type = drag.value.as_ref().type_id();
1982 for (drop_state_type, listener) in &drop_listeners {
1983 if *drop_state_type == drag_state_type {
1984 let drag = cx
1985 .active_drag
1986 .take()
1987 .expect("checked for type drag state type above");
1988
1989 let mut can_drop = true;
1990 if let Some(predicate) = &can_drop_predicate {
1991 can_drop = predicate(drag.value.as_ref(), window, cx);
1992 }
1993
1994 if can_drop {
1995 listener(drag.value.as_ref(), window, cx);
1996 window.refresh();
1997 cx.stop_propagation();
1998 }
1999 }
2000 }
2001 }
2002 }
2003 }
2004 });
2005 }
2006
2007 if let Some(element_state) = element_state {
2008 if !click_listeners.is_empty() || drag_listener.is_some() {
2009 let pending_mouse_down = element_state
2010 .pending_mouse_down
2011 .get_or_insert_with(Default::default)
2012 .clone();
2013
2014 let clicked_state = element_state
2015 .clicked_state
2016 .get_or_insert_with(Default::default)
2017 .clone();
2018
2019 window.on_mouse_event({
2020 let pending_mouse_down = pending_mouse_down.clone();
2021 let hitbox = hitbox.clone();
2022 move |event: &MouseDownEvent, phase, window, _cx| {
2023 if phase == DispatchPhase::Bubble
2024 && event.button == MouseButton::Left
2025 && hitbox.is_hovered(window)
2026 {
2027 *pending_mouse_down.borrow_mut() = Some(event.clone());
2028 window.refresh();
2029 }
2030 }
2031 });
2032
2033 window.on_mouse_event({
2034 let pending_mouse_down = pending_mouse_down.clone();
2035 let hitbox = hitbox.clone();
2036 move |event: &MouseMoveEvent, phase, window, cx| {
2037 if phase == DispatchPhase::Capture {
2038 return;
2039 }
2040
2041 let mut pending_mouse_down = pending_mouse_down.borrow_mut();
2042 if let Some(mouse_down) = pending_mouse_down.clone() {
2043 if !cx.has_active_drag()
2044 && (event.position - mouse_down.position).magnitude()
2045 > DRAG_THRESHOLD
2046 {
2047 if let Some((drag_value, drag_listener)) = drag_listener.take() {
2048 *clicked_state.borrow_mut() = ElementClickedState::default();
2049 let cursor_offset = event.position - hitbox.origin;
2050 let drag = (drag_listener)(
2051 drag_value.as_ref(),
2052 cursor_offset,
2053 window,
2054 cx,
2055 );
2056 cx.active_drag = Some(AnyDrag {
2057 view: drag,
2058 value: drag_value,
2059 cursor_offset,
2060 cursor_style: drag_cursor_style,
2061 });
2062 pending_mouse_down.take();
2063 window.refresh();
2064 cx.stop_propagation();
2065 }
2066 }
2067 }
2068 }
2069 });
2070
2071 window.on_mouse_event({
2072 let mut captured_mouse_down = None;
2073 let hitbox = hitbox.clone();
2074 move |event: &MouseUpEvent, phase, window, cx| match phase {
2075 // Clear the pending mouse down during the capture phase,
2076 // so that it happens even if another event handler stops
2077 // propagation.
2078 DispatchPhase::Capture => {
2079 let mut pending_mouse_down = pending_mouse_down.borrow_mut();
2080 if pending_mouse_down.is_some() && hitbox.is_hovered(window) {
2081 captured_mouse_down = pending_mouse_down.take();
2082 window.refresh();
2083 } else if pending_mouse_down.is_some() {
2084 // Clear the pending mouse down event (without firing click handlers)
2085 // if the hitbox is not being hovered.
2086 // This avoids dragging elements that changed their position
2087 // immediately after being clicked.
2088 // See https://github.com/zed-industries/zed/issues/24600 for more details
2089 pending_mouse_down.take();
2090 window.refresh();
2091 }
2092 }
2093 // Fire click handlers during the bubble phase.
2094 DispatchPhase::Bubble => {
2095 if let Some(mouse_down) = captured_mouse_down.take() {
2096 let mouse_click = ClickEvent {
2097 down: mouse_down,
2098 up: event.clone(),
2099 };
2100 for listener in &click_listeners {
2101 listener(&mouse_click, window, cx);
2102 }
2103 }
2104 }
2105 }
2106 });
2107 }
2108
2109 if let Some(hover_listener) = self.hover_listener.take() {
2110 let hitbox = hitbox.clone();
2111 let was_hovered = element_state
2112 .hover_state
2113 .get_or_insert_with(Default::default)
2114 .clone();
2115 let has_mouse_down = element_state
2116 .pending_mouse_down
2117 .get_or_insert_with(Default::default)
2118 .clone();
2119
2120 window.on_mouse_event(move |_: &MouseMoveEvent, phase, window, cx| {
2121 if phase != DispatchPhase::Bubble {
2122 return;
2123 }
2124 let is_hovered = has_mouse_down.borrow().is_none()
2125 && !cx.has_active_drag()
2126 && hitbox.is_hovered(window);
2127 let mut was_hovered = was_hovered.borrow_mut();
2128
2129 if is_hovered != *was_hovered {
2130 *was_hovered = is_hovered;
2131 drop(was_hovered);
2132
2133 hover_listener(&is_hovered, window, cx);
2134 }
2135 });
2136 }
2137
2138 if let Some(tooltip_builder) = self.tooltip_builder.take() {
2139 let active_tooltip = element_state
2140 .active_tooltip
2141 .get_or_insert_with(Default::default)
2142 .clone();
2143 let pending_mouse_down = element_state
2144 .pending_mouse_down
2145 .get_or_insert_with(Default::default)
2146 .clone();
2147
2148 let tooltip_is_hoverable = tooltip_builder.hoverable;
2149 let build_tooltip = Rc::new(move |window: &mut Window, cx: &mut App| {
2150 Some(((tooltip_builder.build)(window, cx), tooltip_is_hoverable))
2151 });
2152 // Use bounds instead of testing hitbox since this is called during prepaint.
2153 let check_is_hovered_during_prepaint = Rc::new({
2154 let pending_mouse_down = pending_mouse_down.clone();
2155 let source_bounds = hitbox.bounds;
2156 move |window: &Window| {
2157 pending_mouse_down.borrow().is_none()
2158 && source_bounds.contains(&window.mouse_position())
2159 }
2160 });
2161 let check_is_hovered = Rc::new({
2162 let hitbox = hitbox.clone();
2163 move |window: &Window| {
2164 pending_mouse_down.borrow().is_none() && hitbox.is_hovered(window)
2165 }
2166 });
2167 register_tooltip_mouse_handlers(
2168 &active_tooltip,
2169 self.tooltip_id,
2170 build_tooltip,
2171 check_is_hovered,
2172 check_is_hovered_during_prepaint,
2173 window,
2174 );
2175 }
2176
2177 let active_state = element_state
2178 .clicked_state
2179 .get_or_insert_with(Default::default)
2180 .clone();
2181 if active_state.borrow().is_clicked() {
2182 window.on_mouse_event(move |_: &MouseUpEvent, phase, window, _cx| {
2183 if phase == DispatchPhase::Capture {
2184 *active_state.borrow_mut() = ElementClickedState::default();
2185 window.refresh();
2186 }
2187 });
2188 } else {
2189 let active_group_hitbox = self
2190 .group_active_style
2191 .as_ref()
2192 .and_then(|group_active| GroupHitboxes::get(&group_active.group, cx));
2193 let hitbox = hitbox.clone();
2194 window.on_mouse_event(move |_: &MouseDownEvent, phase, window, _cx| {
2195 if phase == DispatchPhase::Bubble && !window.default_prevented() {
2196 let group_hovered = active_group_hitbox
2197 .map_or(false, |group_hitbox_id| group_hitbox_id.is_hovered(window));
2198 let element_hovered = hitbox.is_hovered(window);
2199 if group_hovered || element_hovered {
2200 *active_state.borrow_mut() = ElementClickedState {
2201 group: group_hovered,
2202 element: element_hovered,
2203 };
2204 window.refresh();
2205 }
2206 }
2207 });
2208 }
2209 }
2210 }
2211
2212 fn paint_keyboard_listeners(&mut self, window: &mut Window, _cx: &mut App) {
2213 let key_down_listeners = mem::take(&mut self.key_down_listeners);
2214 let key_up_listeners = mem::take(&mut self.key_up_listeners);
2215 let modifiers_changed_listeners = mem::take(&mut self.modifiers_changed_listeners);
2216 let action_listeners = mem::take(&mut self.action_listeners);
2217 if let Some(context) = self.key_context.clone() {
2218 window.set_key_context(context);
2219 }
2220
2221 for listener in key_down_listeners {
2222 window.on_key_event(move |event: &KeyDownEvent, phase, window, cx| {
2223 listener(event, phase, window, cx);
2224 })
2225 }
2226
2227 for listener in key_up_listeners {
2228 window.on_key_event(move |event: &KeyUpEvent, phase, window, cx| {
2229 listener(event, phase, window, cx);
2230 })
2231 }
2232
2233 for listener in modifiers_changed_listeners {
2234 window.on_modifiers_changed(move |event: &ModifiersChangedEvent, window, cx| {
2235 listener(event, window, cx);
2236 })
2237 }
2238
2239 for (action_type, listener) in action_listeners {
2240 window.on_action(action_type, listener)
2241 }
2242 }
2243
2244 fn paint_hover_group_handler(&self, window: &mut Window, cx: &mut App) {
2245 let group_hitbox = self
2246 .group_hover_style
2247 .as_ref()
2248 .and_then(|group_hover| GroupHitboxes::get(&group_hover.group, cx));
2249
2250 if let Some(group_hitbox) = group_hitbox {
2251 let was_hovered = group_hitbox.is_hovered(window);
2252 let current_view = window.current_view();
2253 window.on_mouse_event(move |_: &MouseMoveEvent, phase, window, cx| {
2254 let hovered = group_hitbox.is_hovered(window);
2255 if phase == DispatchPhase::Capture && hovered != was_hovered {
2256 cx.notify(current_view);
2257 }
2258 });
2259 }
2260 }
2261
2262 fn paint_scroll_listener(
2263 &self,
2264 hitbox: &Hitbox,
2265 style: &Style,
2266 window: &mut Window,
2267 _cx: &mut App,
2268 ) {
2269 if let Some(scroll_offset) = self.scroll_offset.clone() {
2270 let overflow = style.overflow;
2271 let allow_concurrent_scroll = style.allow_concurrent_scroll;
2272 let restrict_scroll_to_axis = style.restrict_scroll_to_axis;
2273 let line_height = window.line_height();
2274 let hitbox = hitbox.clone();
2275 let current_view = window.current_view();
2276 window.on_mouse_event(move |event: &ScrollWheelEvent, phase, window, cx| {
2277 if phase == DispatchPhase::Bubble && hitbox.should_handle_scroll(window) {
2278 let mut scroll_offset = scroll_offset.borrow_mut();
2279 let old_scroll_offset = *scroll_offset;
2280 let delta = event.delta.pixel_delta(line_height);
2281
2282 let mut delta_x = Pixels::ZERO;
2283 if overflow.x == Overflow::Scroll {
2284 if !delta.x.is_zero() {
2285 delta_x = delta.x;
2286 } else if !restrict_scroll_to_axis && overflow.y != Overflow::Scroll {
2287 delta_x = delta.y;
2288 }
2289 }
2290 let mut delta_y = Pixels::ZERO;
2291 if overflow.y == Overflow::Scroll {
2292 if !delta.y.is_zero() {
2293 delta_y = delta.y;
2294 } else if !restrict_scroll_to_axis && overflow.x != Overflow::Scroll {
2295 delta_y = delta.x;
2296 }
2297 }
2298 if !allow_concurrent_scroll && !delta_x.is_zero() && !delta_y.is_zero() {
2299 if delta_x.abs() > delta_y.abs() {
2300 delta_y = Pixels::ZERO;
2301 } else {
2302 delta_x = Pixels::ZERO;
2303 }
2304 }
2305 scroll_offset.y += delta_y;
2306 scroll_offset.x += delta_x;
2307 cx.stop_propagation();
2308 if *scroll_offset != old_scroll_offset {
2309 cx.notify(current_view);
2310 }
2311 }
2312 });
2313 }
2314 }
2315
2316 /// Compute the visual style for this element, based on the current bounds and the element's state.
2317 pub fn compute_style(
2318 &self,
2319 global_id: Option<&GlobalElementId>,
2320 hitbox: Option<&Hitbox>,
2321 window: &mut Window,
2322 cx: &mut App,
2323 ) -> Style {
2324 window.with_optional_element_state(global_id, |element_state, window| {
2325 let mut element_state =
2326 element_state.map(|element_state| element_state.unwrap_or_default());
2327 let style = self.compute_style_internal(hitbox, element_state.as_mut(), window, cx);
2328 (style, element_state)
2329 })
2330 }
2331
2332 /// Called from internal methods that have already called with_element_state.
2333 fn compute_style_internal(
2334 &self,
2335 hitbox: Option<&Hitbox>,
2336 element_state: Option<&mut InteractiveElementState>,
2337 window: &mut Window,
2338 cx: &mut App,
2339 ) -> Style {
2340 let mut style = Style::default();
2341 style.refine(&self.base_style);
2342
2343 if let Some(focus_handle) = self.tracked_focus_handle.as_ref() {
2344 if let Some(in_focus_style) = self.in_focus_style.as_ref() {
2345 if focus_handle.within_focused(window, cx) {
2346 style.refine(in_focus_style);
2347 }
2348 }
2349
2350 if let Some(focus_style) = self.focus_style.as_ref() {
2351 if focus_handle.is_focused(window) {
2352 style.refine(focus_style);
2353 }
2354 }
2355 }
2356
2357 if let Some(hitbox) = hitbox {
2358 if !cx.has_active_drag() {
2359 if let Some(group_hover) = self.group_hover_style.as_ref() {
2360 if let Some(group_hitbox_id) = GroupHitboxes::get(&group_hover.group, cx) {
2361 if group_hitbox_id.is_hovered(window) {
2362 style.refine(&group_hover.style);
2363 }
2364 }
2365 }
2366
2367 if let Some(hover_style) = self.hover_style.as_ref() {
2368 if hitbox.is_hovered(window) {
2369 style.refine(hover_style);
2370 }
2371 }
2372 }
2373
2374 if let Some(drag) = cx.active_drag.take() {
2375 let mut can_drop = true;
2376 if let Some(can_drop_predicate) = &self.can_drop_predicate {
2377 can_drop = can_drop_predicate(drag.value.as_ref(), window, cx);
2378 }
2379
2380 if can_drop {
2381 for (state_type, group_drag_style) in &self.group_drag_over_styles {
2382 if let Some(group_hitbox_id) =
2383 GroupHitboxes::get(&group_drag_style.group, cx)
2384 {
2385 if *state_type == drag.value.as_ref().type_id()
2386 && group_hitbox_id.is_hovered(window)
2387 {
2388 style.refine(&group_drag_style.style);
2389 }
2390 }
2391 }
2392
2393 for (state_type, build_drag_over_style) in &self.drag_over_styles {
2394 if *state_type == drag.value.as_ref().type_id() && hitbox.is_hovered(window)
2395 {
2396 style.refine(&build_drag_over_style(drag.value.as_ref(), window, cx));
2397 }
2398 }
2399 }
2400
2401 style.mouse_cursor = drag.cursor_style;
2402 cx.active_drag = Some(drag);
2403 }
2404 }
2405
2406 if let Some(element_state) = element_state {
2407 let clicked_state = element_state
2408 .clicked_state
2409 .get_or_insert_with(Default::default)
2410 .borrow();
2411 if clicked_state.group {
2412 if let Some(group) = self.group_active_style.as_ref() {
2413 style.refine(&group.style)
2414 }
2415 }
2416
2417 if let Some(active_style) = self.active_style.as_ref() {
2418 if clicked_state.element {
2419 style.refine(active_style)
2420 }
2421 }
2422 }
2423
2424 style
2425 }
2426}
2427
2428/// The per-frame state of an interactive element. Used for tracking stateful interactions like clicks
2429/// and scroll offsets.
2430#[derive(Default)]
2431pub struct InteractiveElementState {
2432 pub(crate) focus_handle: Option<FocusHandle>,
2433 pub(crate) clicked_state: Option<Rc<RefCell<ElementClickedState>>>,
2434 pub(crate) hover_state: Option<Rc<RefCell<bool>>>,
2435 pub(crate) pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
2436 pub(crate) scroll_offset: Option<Rc<RefCell<Point<Pixels>>>>,
2437 pub(crate) active_tooltip: Option<Rc<RefCell<Option<ActiveTooltip>>>>,
2438}
2439
2440/// Whether or not the element or a group that contains it is clicked by the mouse.
2441#[derive(Copy, Clone, Default, Eq, PartialEq)]
2442pub struct ElementClickedState {
2443 /// True if this element's group has been clicked, false otherwise
2444 pub group: bool,
2445
2446 /// True if this element has been clicked, false otherwise
2447 pub element: bool,
2448}
2449
2450impl ElementClickedState {
2451 fn is_clicked(&self) -> bool {
2452 self.group || self.element
2453 }
2454}
2455
2456pub(crate) enum ActiveTooltip {
2457 /// Currently delaying before showing the tooltip.
2458 WaitingForShow { _task: Task<()> },
2459 /// Tooltip is visible, element was hovered or for hoverable tooltips, the tooltip was hovered.
2460 Visible {
2461 tooltip: AnyTooltip,
2462 is_hoverable: bool,
2463 },
2464 /// Tooltip is visible and hoverable, but the mouse is no longer hovering. Currently delaying
2465 /// before hiding it.
2466 WaitingForHide {
2467 tooltip: AnyTooltip,
2468 _task: Task<()>,
2469 },
2470}
2471
2472pub(crate) fn clear_active_tooltip(
2473 active_tooltip: &Rc<RefCell<Option<ActiveTooltip>>>,
2474 window: &mut Window,
2475) {
2476 match active_tooltip.borrow_mut().take() {
2477 None => {}
2478 Some(ActiveTooltip::WaitingForShow { .. }) => {}
2479 Some(ActiveTooltip::Visible { .. }) => window.refresh(),
2480 Some(ActiveTooltip::WaitingForHide { .. }) => window.refresh(),
2481 }
2482}
2483
2484pub(crate) fn clear_active_tooltip_if_not_hoverable(
2485 active_tooltip: &Rc<RefCell<Option<ActiveTooltip>>>,
2486 window: &mut Window,
2487) {
2488 let should_clear = match active_tooltip.borrow().as_ref() {
2489 None => false,
2490 Some(ActiveTooltip::WaitingForShow { .. }) => false,
2491 Some(ActiveTooltip::Visible { is_hoverable, .. }) => !is_hoverable,
2492 Some(ActiveTooltip::WaitingForHide { .. }) => false,
2493 };
2494 if should_clear {
2495 active_tooltip.borrow_mut().take();
2496 window.refresh();
2497 }
2498}
2499
2500pub(crate) fn set_tooltip_on_window(
2501 active_tooltip: &Rc<RefCell<Option<ActiveTooltip>>>,
2502 window: &mut Window,
2503) -> Option<TooltipId> {
2504 let tooltip = match active_tooltip.borrow().as_ref() {
2505 None => return None,
2506 Some(ActiveTooltip::WaitingForShow { .. }) => return None,
2507 Some(ActiveTooltip::Visible { tooltip, .. }) => tooltip.clone(),
2508 Some(ActiveTooltip::WaitingForHide { tooltip, .. }) => tooltip.clone(),
2509 };
2510 Some(window.set_tooltip(tooltip))
2511}
2512
2513pub(crate) fn register_tooltip_mouse_handlers(
2514 active_tooltip: &Rc<RefCell<Option<ActiveTooltip>>>,
2515 tooltip_id: Option<TooltipId>,
2516 build_tooltip: Rc<dyn Fn(&mut Window, &mut App) -> Option<(AnyView, bool)>>,
2517 check_is_hovered: Rc<dyn Fn(&Window) -> bool>,
2518 check_is_hovered_during_prepaint: Rc<dyn Fn(&Window) -> bool>,
2519 window: &mut Window,
2520) {
2521 window.on_mouse_event({
2522 let active_tooltip = active_tooltip.clone();
2523 let build_tooltip = build_tooltip.clone();
2524 let check_is_hovered = check_is_hovered.clone();
2525 move |_: &MouseMoveEvent, phase, window, cx| {
2526 handle_tooltip_mouse_move(
2527 &active_tooltip,
2528 &build_tooltip,
2529 &check_is_hovered,
2530 &check_is_hovered_during_prepaint,
2531 phase,
2532 window,
2533 cx,
2534 )
2535 }
2536 });
2537
2538 window.on_mouse_event({
2539 let active_tooltip = active_tooltip.clone();
2540 move |_: &MouseDownEvent, _phase, window: &mut Window, _cx| {
2541 if !tooltip_id.map_or(false, |tooltip_id| tooltip_id.is_hovered(window)) {
2542 clear_active_tooltip_if_not_hoverable(&active_tooltip, window);
2543 }
2544 }
2545 });
2546
2547 window.on_mouse_event({
2548 let active_tooltip = active_tooltip.clone();
2549 move |_: &ScrollWheelEvent, _phase, window: &mut Window, _cx| {
2550 if !tooltip_id.map_or(false, |tooltip_id| tooltip_id.is_hovered(window)) {
2551 clear_active_tooltip_if_not_hoverable(&active_tooltip, window);
2552 }
2553 }
2554 });
2555}
2556
2557/// Handles displaying tooltips when an element is hovered.
2558///
2559/// The mouse hovering logic also relies on being called from window prepaint in order to handle the
2560/// case where the element the tooltip is on is not rendered - in that case its mouse listeners are
2561/// also not registered. During window prepaint, the hitbox information is not available, so
2562/// `check_is_hovered_during_prepaint` is used which bases the check off of the absolute bounds of
2563/// the element.
2564///
2565/// TODO: There's a minor bug due to the use of absolute bounds while checking during prepaint - it
2566/// does not know if the hitbox is occluded. In the case where a tooltip gets displayed and then
2567/// gets occluded after display, it will stick around until the mouse exits the hover bounds.
2568fn handle_tooltip_mouse_move(
2569 active_tooltip: &Rc<RefCell<Option<ActiveTooltip>>>,
2570 build_tooltip: &Rc<dyn Fn(&mut Window, &mut App) -> Option<(AnyView, bool)>>,
2571 check_is_hovered: &Rc<dyn Fn(&Window) -> bool>,
2572 check_is_hovered_during_prepaint: &Rc<dyn Fn(&Window) -> bool>,
2573 phase: DispatchPhase,
2574 window: &mut Window,
2575 cx: &mut App,
2576) {
2577 // Separates logic for what mutation should occur from applying it, to avoid overlapping
2578 // RefCell borrows.
2579 enum Action {
2580 None,
2581 CancelShow,
2582 ScheduleShow,
2583 }
2584
2585 let action = match active_tooltip.borrow().as_ref() {
2586 None => {
2587 let is_hovered = check_is_hovered(window);
2588 if is_hovered && phase.bubble() {
2589 Action::ScheduleShow
2590 } else {
2591 Action::None
2592 }
2593 }
2594 Some(ActiveTooltip::WaitingForShow { .. }) => {
2595 let is_hovered = check_is_hovered(window);
2596 if is_hovered {
2597 Action::None
2598 } else {
2599 Action::CancelShow
2600 }
2601 }
2602 // These are handled in check_visible_and_update.
2603 Some(ActiveTooltip::Visible { .. }) | Some(ActiveTooltip::WaitingForHide { .. }) => {
2604 Action::None
2605 }
2606 };
2607
2608 match action {
2609 Action::None => {}
2610 Action::CancelShow => {
2611 // Cancel waiting to show tooltip when it is no longer hovered.
2612 active_tooltip.borrow_mut().take();
2613 }
2614 Action::ScheduleShow => {
2615 let delayed_show_task = window.spawn(cx, {
2616 let active_tooltip = active_tooltip.clone();
2617 let build_tooltip = build_tooltip.clone();
2618 let check_is_hovered_during_prepaint = check_is_hovered_during_prepaint.clone();
2619 async move |cx| {
2620 cx.background_executor().timer(TOOLTIP_SHOW_DELAY).await;
2621 cx.update(|window, cx| {
2622 let new_tooltip =
2623 build_tooltip(window, cx).map(|(view, tooltip_is_hoverable)| {
2624 let active_tooltip = active_tooltip.clone();
2625 ActiveTooltip::Visible {
2626 tooltip: AnyTooltip {
2627 view,
2628 mouse_position: window.mouse_position(),
2629 check_visible_and_update: Rc::new(
2630 move |tooltip_bounds, window, cx| {
2631 handle_tooltip_check_visible_and_update(
2632 &active_tooltip,
2633 tooltip_is_hoverable,
2634 &check_is_hovered_during_prepaint,
2635 tooltip_bounds,
2636 window,
2637 cx,
2638 )
2639 },
2640 ),
2641 },
2642 is_hoverable: tooltip_is_hoverable,
2643 }
2644 });
2645 *active_tooltip.borrow_mut() = new_tooltip;
2646 window.refresh();
2647 })
2648 .ok();
2649 }
2650 });
2651 active_tooltip
2652 .borrow_mut()
2653 .replace(ActiveTooltip::WaitingForShow {
2654 _task: delayed_show_task,
2655 });
2656 }
2657 }
2658}
2659
2660/// Returns a callback which will be called by window prepaint to update tooltip visibility. The
2661/// purpose of doing this logic here instead of the mouse move handler is that the mouse move
2662/// handler won't get called when the element is not painted (e.g. via use of `visible_on_hover`).
2663fn handle_tooltip_check_visible_and_update(
2664 active_tooltip: &Rc<RefCell<Option<ActiveTooltip>>>,
2665 tooltip_is_hoverable: bool,
2666 check_is_hovered: &Rc<dyn Fn(&Window) -> bool>,
2667 tooltip_bounds: Bounds<Pixels>,
2668 window: &mut Window,
2669 cx: &mut App,
2670) -> bool {
2671 // Separates logic for what mutation should occur from applying it, to avoid overlapping RefCell
2672 // borrows.
2673 enum Action {
2674 None,
2675 Hide,
2676 ScheduleHide(AnyTooltip),
2677 CancelHide(AnyTooltip),
2678 }
2679
2680 let is_hovered = check_is_hovered(window)
2681 || (tooltip_is_hoverable && tooltip_bounds.contains(&window.mouse_position()));
2682 let action = match active_tooltip.borrow().as_ref() {
2683 Some(ActiveTooltip::Visible { tooltip, .. }) => {
2684 if is_hovered {
2685 Action::None
2686 } else {
2687 if tooltip_is_hoverable {
2688 Action::ScheduleHide(tooltip.clone())
2689 } else {
2690 Action::Hide
2691 }
2692 }
2693 }
2694 Some(ActiveTooltip::WaitingForHide { tooltip, .. }) => {
2695 if is_hovered {
2696 Action::CancelHide(tooltip.clone())
2697 } else {
2698 Action::None
2699 }
2700 }
2701 None | Some(ActiveTooltip::WaitingForShow { .. }) => Action::None,
2702 };
2703
2704 match action {
2705 Action::None => {}
2706 Action::Hide => clear_active_tooltip(&active_tooltip, window),
2707 Action::ScheduleHide(tooltip) => {
2708 let delayed_hide_task = window.spawn(cx, {
2709 let active_tooltip = active_tooltip.clone();
2710 async move |cx| {
2711 cx.background_executor()
2712 .timer(HOVERABLE_TOOLTIP_HIDE_DELAY)
2713 .await;
2714 if active_tooltip.borrow_mut().take().is_some() {
2715 cx.update(|window, _cx| window.refresh()).ok();
2716 }
2717 }
2718 });
2719 active_tooltip
2720 .borrow_mut()
2721 .replace(ActiveTooltip::WaitingForHide {
2722 tooltip,
2723 _task: delayed_hide_task,
2724 });
2725 }
2726 Action::CancelHide(tooltip) => {
2727 // Cancel waiting to hide tooltip when it becomes hovered.
2728 active_tooltip.borrow_mut().replace(ActiveTooltip::Visible {
2729 tooltip,
2730 is_hoverable: true,
2731 });
2732 }
2733 }
2734
2735 active_tooltip.borrow().is_some()
2736}
2737
2738#[derive(Default)]
2739pub(crate) struct GroupHitboxes(HashMap<SharedString, SmallVec<[HitboxId; 1]>>);
2740
2741impl Global for GroupHitboxes {}
2742
2743impl GroupHitboxes {
2744 pub fn get(name: &SharedString, cx: &mut App) -> Option<HitboxId> {
2745 cx.default_global::<Self>()
2746 .0
2747 .get(name)
2748 .and_then(|bounds_stack| bounds_stack.last())
2749 .cloned()
2750 }
2751
2752 pub fn push(name: SharedString, hitbox_id: HitboxId, cx: &mut App) {
2753 cx.default_global::<Self>()
2754 .0
2755 .entry(name)
2756 .or_default()
2757 .push(hitbox_id);
2758 }
2759
2760 pub fn pop(name: &SharedString, cx: &mut App) {
2761 cx.default_global::<Self>().0.get_mut(name).unwrap().pop();
2762 }
2763}
2764
2765/// A wrapper around an element that can be focused.
2766pub struct FocusableWrapper<E> {
2767 /// The element that is focusable
2768 pub element: E,
2769}
2770
2771impl<E: InteractiveElement> FocusableElement for FocusableWrapper<E> {}
2772
2773impl<E> InteractiveElement for FocusableWrapper<E>
2774where
2775 E: InteractiveElement,
2776{
2777 fn interactivity(&mut self) -> &mut Interactivity {
2778 self.element.interactivity()
2779 }
2780}
2781
2782impl<E: StatefulInteractiveElement> StatefulInteractiveElement for FocusableWrapper<E> {}
2783
2784impl<E> Styled for FocusableWrapper<E>
2785where
2786 E: Styled,
2787{
2788 fn style(&mut self) -> &mut StyleRefinement {
2789 self.element.style()
2790 }
2791}
2792
2793impl FocusableWrapper<Div> {
2794 /// Add a listener to be called when the children of this `Div` are prepainted.
2795 /// This allows you to store the [`Bounds`] of the children for later use.
2796 pub fn on_children_prepainted(
2797 mut self,
2798 listener: impl Fn(Vec<Bounds<Pixels>>, &mut Window, &mut App) + 'static,
2799 ) -> Self {
2800 self.element = self.element.on_children_prepainted(listener);
2801 self
2802 }
2803}
2804
2805impl<E> Element for FocusableWrapper<E>
2806where
2807 E: Element,
2808{
2809 type RequestLayoutState = E::RequestLayoutState;
2810 type PrepaintState = E::PrepaintState;
2811
2812 fn id(&self) -> Option<ElementId> {
2813 self.element.id()
2814 }
2815
2816 fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
2817 self.element.source_location()
2818 }
2819
2820 fn request_layout(
2821 &mut self,
2822 id: Option<&GlobalElementId>,
2823 inspector_id: Option<&InspectorElementId>,
2824 window: &mut Window,
2825 cx: &mut App,
2826 ) -> (LayoutId, Self::RequestLayoutState) {
2827 self.element.request_layout(id, inspector_id, window, cx)
2828 }
2829
2830 fn prepaint(
2831 &mut self,
2832 id: Option<&GlobalElementId>,
2833 inspector_id: Option<&InspectorElementId>,
2834 bounds: Bounds<Pixels>,
2835 state: &mut Self::RequestLayoutState,
2836 window: &mut Window,
2837 cx: &mut App,
2838 ) -> E::PrepaintState {
2839 self.element
2840 .prepaint(id, inspector_id, bounds, state, window, cx)
2841 }
2842
2843 fn paint(
2844 &mut self,
2845 id: Option<&GlobalElementId>,
2846 inspector_id: Option<&InspectorElementId>,
2847 bounds: Bounds<Pixels>,
2848 request_layout: &mut Self::RequestLayoutState,
2849 prepaint: &mut Self::PrepaintState,
2850 window: &mut Window,
2851 cx: &mut App,
2852 ) {
2853 self.element.paint(
2854 id,
2855 inspector_id,
2856 bounds,
2857 request_layout,
2858 prepaint,
2859 window,
2860 cx,
2861 )
2862 }
2863}
2864
2865impl<E> IntoElement for FocusableWrapper<E>
2866where
2867 E: IntoElement,
2868{
2869 type Element = E::Element;
2870
2871 fn into_element(self) -> Self::Element {
2872 self.element.into_element()
2873 }
2874}
2875
2876impl<E> ParentElement for FocusableWrapper<E>
2877where
2878 E: ParentElement,
2879{
2880 fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
2881 self.element.extend(elements)
2882 }
2883}
2884
2885/// A wrapper around an element that can store state, produced after assigning an ElementId.
2886pub struct Stateful<E> {
2887 pub(crate) element: E,
2888}
2889
2890impl<E> Styled for Stateful<E>
2891where
2892 E: Styled,
2893{
2894 fn style(&mut self) -> &mut StyleRefinement {
2895 self.element.style()
2896 }
2897}
2898
2899impl<E> StatefulInteractiveElement for Stateful<E>
2900where
2901 E: Element,
2902 Self: InteractiveElement,
2903{
2904}
2905
2906impl<E> InteractiveElement for Stateful<E>
2907where
2908 E: InteractiveElement,
2909{
2910 fn interactivity(&mut self) -> &mut Interactivity {
2911 self.element.interactivity()
2912 }
2913}
2914
2915impl<E: FocusableElement> FocusableElement for Stateful<E> {}
2916
2917impl<E> Element for Stateful<E>
2918where
2919 E: Element,
2920{
2921 type RequestLayoutState = E::RequestLayoutState;
2922 type PrepaintState = E::PrepaintState;
2923
2924 fn id(&self) -> Option<ElementId> {
2925 self.element.id()
2926 }
2927
2928 fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
2929 self.element.source_location()
2930 }
2931
2932 fn request_layout(
2933 &mut self,
2934 id: Option<&GlobalElementId>,
2935 inspector_id: Option<&InspectorElementId>,
2936 window: &mut Window,
2937 cx: &mut App,
2938 ) -> (LayoutId, Self::RequestLayoutState) {
2939 self.element.request_layout(id, inspector_id, window, cx)
2940 }
2941
2942 fn prepaint(
2943 &mut self,
2944 id: Option<&GlobalElementId>,
2945 inspector_id: Option<&InspectorElementId>,
2946 bounds: Bounds<Pixels>,
2947 state: &mut Self::RequestLayoutState,
2948 window: &mut Window,
2949 cx: &mut App,
2950 ) -> E::PrepaintState {
2951 self.element
2952 .prepaint(id, inspector_id, bounds, state, window, cx)
2953 }
2954
2955 fn paint(
2956 &mut self,
2957 id: Option<&GlobalElementId>,
2958 inspector_id: Option<&InspectorElementId>,
2959 bounds: Bounds<Pixels>,
2960 request_layout: &mut Self::RequestLayoutState,
2961 prepaint: &mut Self::PrepaintState,
2962 window: &mut Window,
2963 cx: &mut App,
2964 ) {
2965 self.element.paint(
2966 id,
2967 inspector_id,
2968 bounds,
2969 request_layout,
2970 prepaint,
2971 window,
2972 cx,
2973 );
2974 }
2975}
2976
2977impl<E> IntoElement for Stateful<E>
2978where
2979 E: Element,
2980{
2981 type Element = Self;
2982
2983 fn into_element(self) -> Self::Element {
2984 self
2985 }
2986}
2987
2988impl<E> ParentElement for Stateful<E>
2989where
2990 E: ParentElement,
2991{
2992 fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
2993 self.element.extend(elements)
2994 }
2995}
2996
2997/// Represents an element that can be scrolled *to* in its parent element.
2998///
2999/// Contrary to [ScrollHandle::scroll_to_item], an anchored element does not have to be an immediate child of the parent.
3000#[derive(Clone)]
3001pub struct ScrollAnchor {
3002 handle: ScrollHandle,
3003 last_origin: Rc<RefCell<Point<Pixels>>>,
3004}
3005
3006impl ScrollAnchor {
3007 /// Creates a [ScrollAnchor] associated with a given [ScrollHandle].
3008 pub fn for_handle(handle: ScrollHandle) -> Self {
3009 Self {
3010 handle,
3011 last_origin: Default::default(),
3012 }
3013 }
3014 /// Request scroll to this item on the next frame.
3015 pub fn scroll_to(&self, window: &mut Window, _cx: &mut App) {
3016 let this = self.clone();
3017
3018 window.on_next_frame(move |_, _| {
3019 let viewport_bounds = this.handle.bounds();
3020 let self_bounds = *this.last_origin.borrow();
3021 this.handle.set_offset(viewport_bounds.origin - self_bounds);
3022 });
3023 }
3024}
3025
3026#[derive(Default, Debug)]
3027struct ScrollHandleState {
3028 offset: Rc<RefCell<Point<Pixels>>>,
3029 bounds: Bounds<Pixels>,
3030 padded_content_size: Size<Pixels>,
3031 child_bounds: Vec<Bounds<Pixels>>,
3032 scroll_to_bottom: bool,
3033 overflow: Point<Overflow>,
3034}
3035
3036/// A handle to the scrollable aspects of an element.
3037/// Used for accessing scroll state, like the current scroll offset,
3038/// and for mutating the scroll state, like scrolling to a specific child.
3039#[derive(Clone, Debug)]
3040pub struct ScrollHandle(Rc<RefCell<ScrollHandleState>>);
3041
3042impl Default for ScrollHandle {
3043 fn default() -> Self {
3044 Self::new()
3045 }
3046}
3047
3048impl ScrollHandle {
3049 /// Construct a new scroll handle.
3050 pub fn new() -> Self {
3051 Self(Rc::default())
3052 }
3053
3054 /// Get the current scroll offset.
3055 pub fn offset(&self) -> Point<Pixels> {
3056 *self.0.borrow().offset.borrow()
3057 }
3058
3059 /// Get the top child that's scrolled into view.
3060 pub fn top_item(&self) -> usize {
3061 let state = self.0.borrow();
3062 let top = state.bounds.top() - state.offset.borrow().y;
3063
3064 match state.child_bounds.binary_search_by(|bounds| {
3065 if top < bounds.top() {
3066 Ordering::Greater
3067 } else if top > bounds.bottom() {
3068 Ordering::Less
3069 } else {
3070 Ordering::Equal
3071 }
3072 }) {
3073 Ok(ix) => ix,
3074 Err(ix) => ix.min(state.child_bounds.len().saturating_sub(1)),
3075 }
3076 }
3077
3078 /// Return the bounds into which this child is painted
3079 pub fn bounds(&self) -> Bounds<Pixels> {
3080 self.0.borrow().bounds
3081 }
3082
3083 /// Set the bounds into which this child is painted
3084 pub(super) fn set_bounds(&self, bounds: Bounds<Pixels>) {
3085 self.0.borrow_mut().bounds = bounds;
3086 }
3087
3088 /// Get the bounds for a specific child.
3089 pub fn bounds_for_item(&self, ix: usize) -> Option<Bounds<Pixels>> {
3090 self.0.borrow().child_bounds.get(ix).cloned()
3091 }
3092
3093 /// Get the size of the content with padding of the container.
3094 pub fn padded_content_size(&self) -> Size<Pixels> {
3095 self.0.borrow().padded_content_size
3096 }
3097
3098 /// scroll_to_item scrolls the minimal amount to ensure that the child is
3099 /// fully visible
3100 pub fn scroll_to_item(&self, ix: usize) {
3101 let state = self.0.borrow();
3102
3103 let Some(bounds) = state.child_bounds.get(ix) else {
3104 return;
3105 };
3106
3107 let mut scroll_offset = state.offset.borrow_mut();
3108
3109 if state.overflow.y == Overflow::Scroll {
3110 if bounds.top() + scroll_offset.y < state.bounds.top() {
3111 scroll_offset.y = state.bounds.top() - bounds.top();
3112 } else if bounds.bottom() + scroll_offset.y > state.bounds.bottom() {
3113 scroll_offset.y = state.bounds.bottom() - bounds.bottom();
3114 }
3115 }
3116
3117 if state.overflow.x == Overflow::Scroll {
3118 if bounds.left() + scroll_offset.x < state.bounds.left() {
3119 scroll_offset.x = state.bounds.left() - bounds.left();
3120 } else if bounds.right() + scroll_offset.x > state.bounds.right() {
3121 scroll_offset.x = state.bounds.right() - bounds.right();
3122 }
3123 }
3124 }
3125
3126 /// Scrolls to the bottom.
3127 pub fn scroll_to_bottom(&self) {
3128 let mut state = self.0.borrow_mut();
3129 state.scroll_to_bottom = true;
3130 }
3131
3132 /// Set the offset explicitly. The offset is the distance from the top left of the
3133 /// parent container to the top left of the first child.
3134 /// As you scroll further down the offset becomes more negative.
3135 pub fn set_offset(&self, mut position: Point<Pixels>) {
3136 let state = self.0.borrow();
3137 *state.offset.borrow_mut() = position;
3138 }
3139
3140 /// Get the logical scroll top, based on a child index and a pixel offset.
3141 pub fn logical_scroll_top(&self) -> (usize, Pixels) {
3142 let ix = self.top_item();
3143 let state = self.0.borrow();
3144
3145 if let Some(child_bounds) = state.child_bounds.get(ix) {
3146 (
3147 ix,
3148 child_bounds.top() + state.offset.borrow().y - state.bounds.top(),
3149 )
3150 } else {
3151 (ix, px(0.))
3152 }
3153 }
3154
3155 /// Get the count of children for scrollable item.
3156 pub fn children_count(&self) -> usize {
3157 self.0.borrow().child_bounds.len()
3158 }
3159}