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