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