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 if let Some(scroll_handle) = &self.tracked_scroll_handle {
1563 let mut state = scroll_handle.0.borrow_mut();
1564 state.overflow = style.overflow;
1565 scroll_to_bottom = mem::take(&mut state.scroll_to_bottom);
1566 }
1567
1568 let rem_size = window.rem_size();
1569 let padding_size = size(
1570 style
1571 .padding
1572 .left
1573 .to_pixels(bounds.size.width.into(), rem_size)
1574 + style
1575 .padding
1576 .right
1577 .to_pixels(bounds.size.width.into(), rem_size),
1578 style
1579 .padding
1580 .top
1581 .to_pixels(bounds.size.height.into(), rem_size)
1582 + style
1583 .padding
1584 .bottom
1585 .to_pixels(bounds.size.height.into(), rem_size),
1586 );
1587 let scroll_max = (self.content_size + padding_size - bounds.size).max(&Size::default());
1588 // Clamp scroll offset in case scroll max is smaller now (e.g., if children
1589 // were removed or the bounds became larger).
1590 let mut scroll_offset = scroll_offset.borrow_mut();
1591
1592 scroll_offset.x = scroll_offset.x.clamp(-scroll_max.width, px(0.));
1593 if scroll_to_bottom {
1594 scroll_offset.y = -scroll_max.height;
1595 } else {
1596 scroll_offset.y = scroll_offset.y.clamp(-scroll_max.height, px(0.));
1597 }
1598
1599 *scroll_offset
1600 } else {
1601 Point::default()
1602 }
1603 }
1604
1605 /// Paint this element according to this interactivity state's configured styles
1606 /// and bind the element's mouse and keyboard events.
1607 ///
1608 /// content_size is the size of the content of the element, which may be larger than the
1609 /// element's bounds if the element is scrollable.
1610 ///
1611 /// the final computed style will be passed to the provided function, along
1612 /// with the current scroll offset
1613 pub fn paint(
1614 &mut self,
1615 global_id: Option<&GlobalElementId>,
1616 bounds: Bounds<Pixels>,
1617 hitbox: Option<&Hitbox>,
1618 window: &mut Window,
1619 cx: &mut App,
1620 f: impl FnOnce(&Style, &mut Window, &mut App),
1621 ) {
1622 self.hovered = hitbox.map(|hitbox| hitbox.is_hovered(window));
1623 window.with_optional_element_state::<InteractiveElementState, _>(
1624 global_id,
1625 |element_state, window| {
1626 let mut element_state =
1627 element_state.map(|element_state| element_state.unwrap_or_default());
1628
1629 let style = self.compute_style_internal(hitbox, element_state.as_mut(), window, cx);
1630
1631 #[cfg(any(feature = "test-support", test))]
1632 if let Some(debug_selector) = &self.debug_selector {
1633 window
1634 .next_frame
1635 .debug_bounds
1636 .insert(debug_selector.clone(), bounds);
1637 }
1638
1639 self.paint_hover_group_handler(window, cx);
1640
1641 if style.visibility == Visibility::Hidden {
1642 return ((), element_state);
1643 }
1644
1645 window.with_element_opacity(style.opacity, |window| {
1646 style.paint(bounds, window, cx, |window: &mut Window, cx: &mut App| {
1647 window.with_text_style(style.text_style().cloned(), |window| {
1648 window.with_content_mask(
1649 style.overflow_mask(bounds, window.rem_size()),
1650 |window| {
1651 if let Some(hitbox) = hitbox {
1652 #[cfg(debug_assertions)]
1653 self.paint_debug_info(
1654 global_id, hitbox, &style, window, cx,
1655 );
1656
1657 if let Some(drag) = cx.active_drag.as_ref() {
1658 if let Some(mouse_cursor) = drag.cursor_style {
1659 window.set_cursor_style(mouse_cursor, None);
1660 }
1661 } else {
1662 if let Some(mouse_cursor) = style.mouse_cursor {
1663 window.set_cursor_style(mouse_cursor, Some(hitbox));
1664 }
1665 }
1666
1667 if let Some(group) = self.group.clone() {
1668 GroupHitboxes::push(group, hitbox.id, cx);
1669 }
1670
1671 self.paint_mouse_listeners(
1672 hitbox,
1673 element_state.as_mut(),
1674 window,
1675 cx,
1676 );
1677 self.paint_scroll_listener(hitbox, &style, window, cx);
1678 }
1679
1680 self.paint_keyboard_listeners(window, cx);
1681 f(&style, window, cx);
1682
1683 if hitbox.is_some() {
1684 if let Some(group) = self.group.as_ref() {
1685 GroupHitboxes::pop(group, cx);
1686 }
1687 }
1688 },
1689 );
1690 });
1691 });
1692 });
1693
1694 ((), element_state)
1695 },
1696 );
1697 }
1698
1699 #[cfg(debug_assertions)]
1700 fn paint_debug_info(
1701 &self,
1702 global_id: Option<&GlobalElementId>,
1703 hitbox: &Hitbox,
1704 style: &Style,
1705 window: &mut Window,
1706 cx: &mut App,
1707 ) {
1708 use crate::{BorderStyle, TextAlign};
1709
1710 if global_id.is_some()
1711 && (style.debug || style.debug_below || cx.has_global::<crate::DebugBelow>())
1712 && hitbox.is_hovered(window)
1713 {
1714 const FONT_SIZE: crate::Pixels = crate::Pixels(10.);
1715 let element_id = format!("{:?}", global_id.unwrap());
1716 let str_len = element_id.len();
1717
1718 let render_debug_text = |window: &mut Window| {
1719 if let Some(text) = window
1720 .text_system()
1721 .shape_text(
1722 element_id.into(),
1723 FONT_SIZE,
1724 &[window.text_style().to_run(str_len)],
1725 None,
1726 None,
1727 )
1728 .ok()
1729 .and_then(|mut text| text.pop())
1730 {
1731 text.paint(hitbox.origin, FONT_SIZE, TextAlign::Left, None, window, cx)
1732 .ok();
1733
1734 let text_bounds = crate::Bounds {
1735 origin: hitbox.origin,
1736 size: text.size(FONT_SIZE),
1737 };
1738 if self.location.is_some()
1739 && text_bounds.contains(&window.mouse_position())
1740 && window.modifiers().secondary()
1741 {
1742 let secondary_held = window.modifiers().secondary();
1743 window.on_key_event({
1744 move |e: &crate::ModifiersChangedEvent, _phase, window, _cx| {
1745 if e.modifiers.secondary() != secondary_held
1746 && text_bounds.contains(&window.mouse_position())
1747 {
1748 window.refresh();
1749 }
1750 }
1751 });
1752
1753 let was_hovered = hitbox.is_hovered(window);
1754 let current_view = window.current_view();
1755 window.on_mouse_event({
1756 let hitbox = hitbox.clone();
1757 move |_: &MouseMoveEvent, phase, window, cx| {
1758 if phase == DispatchPhase::Capture {
1759 let hovered = hitbox.is_hovered(window);
1760 if hovered != was_hovered {
1761 cx.notify(current_view)
1762 }
1763 }
1764 }
1765 });
1766
1767 window.on_mouse_event({
1768 let hitbox = hitbox.clone();
1769 let location = self.location.unwrap();
1770 move |e: &crate::MouseDownEvent, phase, window, cx| {
1771 if text_bounds.contains(&e.position)
1772 && phase.capture()
1773 && hitbox.is_hovered(window)
1774 {
1775 cx.stop_propagation();
1776 let Ok(dir) = std::env::current_dir() else {
1777 return;
1778 };
1779
1780 eprintln!(
1781 "This element was created at:\n{}:{}:{}",
1782 dir.join(location.file()).to_string_lossy(),
1783 location.line(),
1784 location.column()
1785 );
1786 }
1787 }
1788 });
1789 window.paint_quad(crate::outline(
1790 crate::Bounds {
1791 origin: hitbox.origin
1792 + crate::point(crate::px(0.), FONT_SIZE - px(2.)),
1793 size: crate::Size {
1794 width: text_bounds.size.width,
1795 height: crate::px(1.),
1796 },
1797 },
1798 crate::red(),
1799 BorderStyle::default(),
1800 ))
1801 }
1802 }
1803 };
1804
1805 window.with_text_style(
1806 Some(crate::TextStyleRefinement {
1807 color: Some(crate::red()),
1808 line_height: Some(FONT_SIZE.into()),
1809 background_color: Some(crate::white()),
1810 ..Default::default()
1811 }),
1812 render_debug_text,
1813 )
1814 }
1815 }
1816
1817 fn paint_mouse_listeners(
1818 &mut self,
1819 hitbox: &Hitbox,
1820 element_state: Option<&mut InteractiveElementState>,
1821 window: &mut Window,
1822 cx: &mut App,
1823 ) {
1824 // If this element can be focused, register a mouse down listener
1825 // that will automatically transfer focus when hitting the element.
1826 // This behavior can be suppressed by using `cx.prevent_default()`.
1827 if let Some(focus_handle) = self.tracked_focus_handle.clone() {
1828 let hitbox = hitbox.clone();
1829 window.on_mouse_event(move |_: &MouseDownEvent, phase, window, _| {
1830 if phase == DispatchPhase::Bubble
1831 && hitbox.is_hovered(window)
1832 && !window.default_prevented()
1833 {
1834 window.focus(&focus_handle);
1835 // If there is a parent that is also focusable, prevent it
1836 // from transferring focus because we already did so.
1837 window.prevent_default();
1838 }
1839 });
1840 }
1841
1842 for listener in self.mouse_down_listeners.drain(..) {
1843 let hitbox = hitbox.clone();
1844 window.on_mouse_event(move |event: &MouseDownEvent, phase, window, cx| {
1845 listener(event, phase, &hitbox, window, cx);
1846 })
1847 }
1848
1849 for listener in self.mouse_up_listeners.drain(..) {
1850 let hitbox = hitbox.clone();
1851 window.on_mouse_event(move |event: &MouseUpEvent, phase, window, cx| {
1852 listener(event, phase, &hitbox, window, cx);
1853 })
1854 }
1855
1856 for listener in self.mouse_move_listeners.drain(..) {
1857 let hitbox = hitbox.clone();
1858 window.on_mouse_event(move |event: &MouseMoveEvent, phase, window, cx| {
1859 listener(event, phase, &hitbox, window, cx);
1860 })
1861 }
1862
1863 for listener in self.scroll_wheel_listeners.drain(..) {
1864 let hitbox = hitbox.clone();
1865 window.on_mouse_event(move |event: &ScrollWheelEvent, phase, window, cx| {
1866 listener(event, phase, &hitbox, window, cx);
1867 })
1868 }
1869
1870 if self.hover_style.is_some()
1871 || self.base_style.mouse_cursor.is_some()
1872 || cx.active_drag.is_some() && !self.drag_over_styles.is_empty()
1873 {
1874 let hitbox = hitbox.clone();
1875 let was_hovered = hitbox.is_hovered(window);
1876 let current_view = window.current_view();
1877 window.on_mouse_event(move |_: &MouseMoveEvent, phase, window, cx| {
1878 let hovered = hitbox.is_hovered(window);
1879 if phase == DispatchPhase::Capture && hovered != was_hovered {
1880 cx.notify(current_view);
1881 }
1882 });
1883 }
1884 let drag_cursor_style = self.base_style.as_ref().mouse_cursor;
1885
1886 let mut drag_listener = mem::take(&mut self.drag_listener);
1887 let drop_listeners = mem::take(&mut self.drop_listeners);
1888 let click_listeners = mem::take(&mut self.click_listeners);
1889 let can_drop_predicate = mem::take(&mut self.can_drop_predicate);
1890
1891 if !drop_listeners.is_empty() {
1892 let hitbox = hitbox.clone();
1893 window.on_mouse_event({
1894 move |_: &MouseUpEvent, phase, window, cx| {
1895 if let Some(drag) = &cx.active_drag {
1896 if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
1897 let drag_state_type = drag.value.as_ref().type_id();
1898 for (drop_state_type, listener) in &drop_listeners {
1899 if *drop_state_type == drag_state_type {
1900 let drag = cx
1901 .active_drag
1902 .take()
1903 .expect("checked for type drag state type above");
1904
1905 let mut can_drop = true;
1906 if let Some(predicate) = &can_drop_predicate {
1907 can_drop = predicate(drag.value.as_ref(), window, cx);
1908 }
1909
1910 if can_drop {
1911 listener(drag.value.as_ref(), window, cx);
1912 window.refresh();
1913 cx.stop_propagation();
1914 }
1915 }
1916 }
1917 }
1918 }
1919 }
1920 });
1921 }
1922
1923 if let Some(element_state) = element_state {
1924 if !click_listeners.is_empty() || drag_listener.is_some() {
1925 let pending_mouse_down = element_state
1926 .pending_mouse_down
1927 .get_or_insert_with(Default::default)
1928 .clone();
1929
1930 let clicked_state = element_state
1931 .clicked_state
1932 .get_or_insert_with(Default::default)
1933 .clone();
1934
1935 window.on_mouse_event({
1936 let pending_mouse_down = pending_mouse_down.clone();
1937 let hitbox = hitbox.clone();
1938 move |event: &MouseDownEvent, phase, window, _cx| {
1939 if phase == DispatchPhase::Bubble
1940 && event.button == MouseButton::Left
1941 && hitbox.is_hovered(window)
1942 {
1943 *pending_mouse_down.borrow_mut() = Some(event.clone());
1944 window.refresh();
1945 }
1946 }
1947 });
1948
1949 window.on_mouse_event({
1950 let pending_mouse_down = pending_mouse_down.clone();
1951 let hitbox = hitbox.clone();
1952 move |event: &MouseMoveEvent, phase, window, cx| {
1953 if phase == DispatchPhase::Capture {
1954 return;
1955 }
1956
1957 let mut pending_mouse_down = pending_mouse_down.borrow_mut();
1958 if let Some(mouse_down) = pending_mouse_down.clone() {
1959 if !cx.has_active_drag()
1960 && (event.position - mouse_down.position).magnitude()
1961 > DRAG_THRESHOLD
1962 {
1963 if let Some((drag_value, drag_listener)) = drag_listener.take() {
1964 *clicked_state.borrow_mut() = ElementClickedState::default();
1965 let cursor_offset = event.position - hitbox.origin;
1966 let drag = (drag_listener)(
1967 drag_value.as_ref(),
1968 cursor_offset,
1969 window,
1970 cx,
1971 );
1972 cx.active_drag = Some(AnyDrag {
1973 view: drag,
1974 value: drag_value,
1975 cursor_offset,
1976 cursor_style: drag_cursor_style,
1977 });
1978 pending_mouse_down.take();
1979 window.refresh();
1980 cx.stop_propagation();
1981 }
1982 }
1983 }
1984 }
1985 });
1986
1987 window.on_mouse_event({
1988 let mut captured_mouse_down = None;
1989 let hitbox = hitbox.clone();
1990 move |event: &MouseUpEvent, phase, window, cx| match phase {
1991 // Clear the pending mouse down during the capture phase,
1992 // so that it happens even if another event handler stops
1993 // propagation.
1994 DispatchPhase::Capture => {
1995 let mut pending_mouse_down = pending_mouse_down.borrow_mut();
1996 if pending_mouse_down.is_some() && hitbox.is_hovered(window) {
1997 captured_mouse_down = pending_mouse_down.take();
1998 window.refresh();
1999 } else if pending_mouse_down.is_some() {
2000 // Clear the pending mouse down event (without firing click handlers)
2001 // if the hitbox is not being hovered.
2002 // This avoids dragging elements that changed their position
2003 // immediately after being clicked.
2004 // See https://github.com/zed-industries/zed/issues/24600 for more details
2005 pending_mouse_down.take();
2006 window.refresh();
2007 }
2008 }
2009 // Fire click handlers during the bubble phase.
2010 DispatchPhase::Bubble => {
2011 if let Some(mouse_down) = captured_mouse_down.take() {
2012 let mouse_click = ClickEvent {
2013 down: mouse_down,
2014 up: event.clone(),
2015 };
2016 for listener in &click_listeners {
2017 listener(&mouse_click, window, cx);
2018 }
2019 }
2020 }
2021 }
2022 });
2023 }
2024
2025 if let Some(hover_listener) = self.hover_listener.take() {
2026 let hitbox = hitbox.clone();
2027 let was_hovered = element_state
2028 .hover_state
2029 .get_or_insert_with(Default::default)
2030 .clone();
2031 let has_mouse_down = element_state
2032 .pending_mouse_down
2033 .get_or_insert_with(Default::default)
2034 .clone();
2035
2036 window.on_mouse_event(move |_: &MouseMoveEvent, phase, window, cx| {
2037 if phase != DispatchPhase::Bubble {
2038 return;
2039 }
2040 let is_hovered = has_mouse_down.borrow().is_none()
2041 && !cx.has_active_drag()
2042 && hitbox.is_hovered(window);
2043 let mut was_hovered = was_hovered.borrow_mut();
2044
2045 if is_hovered != *was_hovered {
2046 *was_hovered = is_hovered;
2047 drop(was_hovered);
2048
2049 hover_listener(&is_hovered, window, cx);
2050 }
2051 });
2052 }
2053
2054 if let Some(tooltip_builder) = self.tooltip_builder.take() {
2055 let active_tooltip = element_state
2056 .active_tooltip
2057 .get_or_insert_with(Default::default)
2058 .clone();
2059 let pending_mouse_down = element_state
2060 .pending_mouse_down
2061 .get_or_insert_with(Default::default)
2062 .clone();
2063
2064 let tooltip_is_hoverable = tooltip_builder.hoverable;
2065 let build_tooltip = Rc::new(move |window: &mut Window, cx: &mut App| {
2066 Some(((tooltip_builder.build)(window, cx), tooltip_is_hoverable))
2067 });
2068 // Use bounds instead of testing hitbox since this is called during prepaint.
2069 let check_is_hovered_during_prepaint = Rc::new({
2070 let pending_mouse_down = pending_mouse_down.clone();
2071 let source_bounds = hitbox.bounds;
2072 move |window: &Window| {
2073 pending_mouse_down.borrow().is_none()
2074 && source_bounds.contains(&window.mouse_position())
2075 }
2076 });
2077 let check_is_hovered = Rc::new({
2078 let hitbox = hitbox.clone();
2079 move |window: &Window| {
2080 pending_mouse_down.borrow().is_none() && hitbox.is_hovered(window)
2081 }
2082 });
2083 register_tooltip_mouse_handlers(
2084 &active_tooltip,
2085 self.tooltip_id,
2086 build_tooltip,
2087 check_is_hovered,
2088 check_is_hovered_during_prepaint,
2089 window,
2090 );
2091 }
2092
2093 let active_state = element_state
2094 .clicked_state
2095 .get_or_insert_with(Default::default)
2096 .clone();
2097 if active_state.borrow().is_clicked() {
2098 window.on_mouse_event(move |_: &MouseUpEvent, phase, window, _cx| {
2099 if phase == DispatchPhase::Capture {
2100 *active_state.borrow_mut() = ElementClickedState::default();
2101 window.refresh();
2102 }
2103 });
2104 } else {
2105 let active_group_hitbox = self
2106 .group_active_style
2107 .as_ref()
2108 .and_then(|group_active| GroupHitboxes::get(&group_active.group, cx));
2109 let hitbox = hitbox.clone();
2110 window.on_mouse_event(move |_: &MouseDownEvent, phase, window, _cx| {
2111 if phase == DispatchPhase::Bubble && !window.default_prevented() {
2112 let group_hovered = active_group_hitbox
2113 .map_or(false, |group_hitbox_id| group_hitbox_id.is_hovered(window));
2114 let element_hovered = hitbox.is_hovered(window);
2115 if group_hovered || element_hovered {
2116 *active_state.borrow_mut() = ElementClickedState {
2117 group: group_hovered,
2118 element: element_hovered,
2119 };
2120 window.refresh();
2121 }
2122 }
2123 });
2124 }
2125 }
2126 }
2127
2128 fn paint_keyboard_listeners(&mut self, window: &mut Window, _cx: &mut App) {
2129 let key_down_listeners = mem::take(&mut self.key_down_listeners);
2130 let key_up_listeners = mem::take(&mut self.key_up_listeners);
2131 let modifiers_changed_listeners = mem::take(&mut self.modifiers_changed_listeners);
2132 let action_listeners = mem::take(&mut self.action_listeners);
2133 if let Some(context) = self.key_context.clone() {
2134 window.set_key_context(context);
2135 }
2136
2137 for listener in key_down_listeners {
2138 window.on_key_event(move |event: &KeyDownEvent, phase, window, cx| {
2139 listener(event, phase, window, cx);
2140 })
2141 }
2142
2143 for listener in key_up_listeners {
2144 window.on_key_event(move |event: &KeyUpEvent, phase, window, cx| {
2145 listener(event, phase, window, cx);
2146 })
2147 }
2148
2149 for listener in modifiers_changed_listeners {
2150 window.on_modifiers_changed(move |event: &ModifiersChangedEvent, window, cx| {
2151 listener(event, window, cx);
2152 })
2153 }
2154
2155 for (action_type, listener) in action_listeners {
2156 window.on_action(action_type, listener)
2157 }
2158 }
2159
2160 fn paint_hover_group_handler(&self, window: &mut Window, cx: &mut App) {
2161 let group_hitbox = self
2162 .group_hover_style
2163 .as_ref()
2164 .and_then(|group_hover| GroupHitboxes::get(&group_hover.group, cx));
2165
2166 if let Some(group_hitbox) = group_hitbox {
2167 let was_hovered = group_hitbox.is_hovered(window);
2168 let current_view = window.current_view();
2169 window.on_mouse_event(move |_: &MouseMoveEvent, phase, window, cx| {
2170 let hovered = group_hitbox.is_hovered(window);
2171 if phase == DispatchPhase::Capture && hovered != was_hovered {
2172 cx.notify(current_view);
2173 }
2174 });
2175 }
2176 }
2177
2178 fn paint_scroll_listener(
2179 &self,
2180 hitbox: &Hitbox,
2181 style: &Style,
2182 window: &mut Window,
2183 _cx: &mut App,
2184 ) {
2185 if let Some(scroll_offset) = self.scroll_offset.clone() {
2186 let overflow = style.overflow;
2187 let allow_concurrent_scroll = style.allow_concurrent_scroll;
2188 let restrict_scroll_to_axis = style.restrict_scroll_to_axis;
2189 let line_height = window.line_height();
2190 let hitbox = hitbox.clone();
2191 let current_view = window.current_view();
2192 window.on_mouse_event(move |event: &ScrollWheelEvent, phase, window, cx| {
2193 if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
2194 let mut scroll_offset = scroll_offset.borrow_mut();
2195 let old_scroll_offset = *scroll_offset;
2196 let delta = event.delta.pixel_delta(line_height);
2197
2198 let mut delta_x = Pixels::ZERO;
2199 if overflow.x == Overflow::Scroll {
2200 if !delta.x.is_zero() {
2201 delta_x = delta.x;
2202 } else if !restrict_scroll_to_axis && overflow.y != Overflow::Scroll {
2203 delta_x = delta.y;
2204 }
2205 }
2206 let mut delta_y = Pixels::ZERO;
2207 if overflow.y == Overflow::Scroll {
2208 if !delta.y.is_zero() {
2209 delta_y = delta.y;
2210 } else if !restrict_scroll_to_axis && overflow.x != Overflow::Scroll {
2211 delta_y = delta.x;
2212 }
2213 }
2214 if !allow_concurrent_scroll && !delta_x.is_zero() && !delta_y.is_zero() {
2215 if delta_x.abs() > delta_y.abs() {
2216 delta_y = Pixels::ZERO;
2217 } else {
2218 delta_x = Pixels::ZERO;
2219 }
2220 }
2221 scroll_offset.y += delta_y;
2222 scroll_offset.x += delta_x;
2223 cx.stop_propagation();
2224 if *scroll_offset != old_scroll_offset {
2225 cx.notify(current_view);
2226 }
2227 }
2228 });
2229 }
2230 }
2231
2232 /// Compute the visual style for this element, based on the current bounds and the element's state.
2233 pub fn compute_style(
2234 &self,
2235 global_id: Option<&GlobalElementId>,
2236 hitbox: Option<&Hitbox>,
2237 window: &mut Window,
2238 cx: &mut App,
2239 ) -> Style {
2240 window.with_optional_element_state(global_id, |element_state, window| {
2241 let mut element_state =
2242 element_state.map(|element_state| element_state.unwrap_or_default());
2243 let style = self.compute_style_internal(hitbox, element_state.as_mut(), window, cx);
2244 (style, element_state)
2245 })
2246 }
2247
2248 /// Called from internal methods that have already called with_element_state.
2249 fn compute_style_internal(
2250 &self,
2251 hitbox: Option<&Hitbox>,
2252 element_state: Option<&mut InteractiveElementState>,
2253 window: &mut Window,
2254 cx: &mut App,
2255 ) -> Style {
2256 let mut style = Style::default();
2257 style.refine(&self.base_style);
2258
2259 if let Some(focus_handle) = self.tracked_focus_handle.as_ref() {
2260 if let Some(in_focus_style) = self.in_focus_style.as_ref() {
2261 if focus_handle.within_focused(window, cx) {
2262 style.refine(in_focus_style);
2263 }
2264 }
2265
2266 if let Some(focus_style) = self.focus_style.as_ref() {
2267 if focus_handle.is_focused(window) {
2268 style.refine(focus_style);
2269 }
2270 }
2271 }
2272
2273 if let Some(hitbox) = hitbox {
2274 if !cx.has_active_drag() {
2275 if let Some(group_hover) = self.group_hover_style.as_ref() {
2276 if let Some(group_hitbox_id) = GroupHitboxes::get(&group_hover.group, cx) {
2277 if group_hitbox_id.is_hovered(window) {
2278 style.refine(&group_hover.style);
2279 }
2280 }
2281 }
2282
2283 if let Some(hover_style) = self.hover_style.as_ref() {
2284 if hitbox.is_hovered(window) {
2285 style.refine(hover_style);
2286 }
2287 }
2288 }
2289
2290 if let Some(drag) = cx.active_drag.take() {
2291 let mut can_drop = true;
2292 if let Some(can_drop_predicate) = &self.can_drop_predicate {
2293 can_drop = can_drop_predicate(drag.value.as_ref(), window, cx);
2294 }
2295
2296 if can_drop {
2297 for (state_type, group_drag_style) in &self.group_drag_over_styles {
2298 if let Some(group_hitbox_id) =
2299 GroupHitboxes::get(&group_drag_style.group, cx)
2300 {
2301 if *state_type == drag.value.as_ref().type_id()
2302 && group_hitbox_id.is_hovered(window)
2303 {
2304 style.refine(&group_drag_style.style);
2305 }
2306 }
2307 }
2308
2309 for (state_type, build_drag_over_style) in &self.drag_over_styles {
2310 if *state_type == drag.value.as_ref().type_id() && hitbox.is_hovered(window)
2311 {
2312 style.refine(&build_drag_over_style(drag.value.as_ref(), window, cx));
2313 }
2314 }
2315 }
2316
2317 style.mouse_cursor = drag.cursor_style;
2318 cx.active_drag = Some(drag);
2319 }
2320 }
2321
2322 if let Some(element_state) = element_state {
2323 let clicked_state = element_state
2324 .clicked_state
2325 .get_or_insert_with(Default::default)
2326 .borrow();
2327 if clicked_state.group {
2328 if let Some(group) = self.group_active_style.as_ref() {
2329 style.refine(&group.style)
2330 }
2331 }
2332
2333 if let Some(active_style) = self.active_style.as_ref() {
2334 if clicked_state.element {
2335 style.refine(active_style)
2336 }
2337 }
2338 }
2339
2340 style
2341 }
2342}
2343
2344/// The per-frame state of an interactive element. Used for tracking stateful interactions like clicks
2345/// and scroll offsets.
2346#[derive(Default)]
2347pub struct InteractiveElementState {
2348 pub(crate) focus_handle: Option<FocusHandle>,
2349 pub(crate) clicked_state: Option<Rc<RefCell<ElementClickedState>>>,
2350 pub(crate) hover_state: Option<Rc<RefCell<bool>>>,
2351 pub(crate) pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
2352 pub(crate) scroll_offset: Option<Rc<RefCell<Point<Pixels>>>>,
2353 pub(crate) active_tooltip: Option<Rc<RefCell<Option<ActiveTooltip>>>>,
2354}
2355
2356/// Whether or not the element or a group that contains it is clicked by the mouse.
2357#[derive(Copy, Clone, Default, Eq, PartialEq)]
2358pub struct ElementClickedState {
2359 /// True if this element's group has been clicked, false otherwise
2360 pub group: bool,
2361
2362 /// True if this element has been clicked, false otherwise
2363 pub element: bool,
2364}
2365
2366impl ElementClickedState {
2367 fn is_clicked(&self) -> bool {
2368 self.group || self.element
2369 }
2370}
2371
2372pub(crate) enum ActiveTooltip {
2373 /// Currently delaying before showing the tooltip.
2374 WaitingForShow { _task: Task<()> },
2375 /// Tooltip is visible, element was hovered or for hoverable tooltips, the tooltip was hovered.
2376 Visible {
2377 tooltip: AnyTooltip,
2378 is_hoverable: bool,
2379 },
2380 /// Tooltip is visible and hoverable, but the mouse is no longer hovering. Currently delaying
2381 /// before hiding it.
2382 WaitingForHide {
2383 tooltip: AnyTooltip,
2384 _task: Task<()>,
2385 },
2386}
2387
2388pub(crate) fn clear_active_tooltip(
2389 active_tooltip: &Rc<RefCell<Option<ActiveTooltip>>>,
2390 window: &mut Window,
2391) {
2392 match active_tooltip.borrow_mut().take() {
2393 None => {}
2394 Some(ActiveTooltip::WaitingForShow { .. }) => {}
2395 Some(ActiveTooltip::Visible { .. }) => window.refresh(),
2396 Some(ActiveTooltip::WaitingForHide { .. }) => window.refresh(),
2397 }
2398}
2399
2400pub(crate) fn clear_active_tooltip_if_not_hoverable(
2401 active_tooltip: &Rc<RefCell<Option<ActiveTooltip>>>,
2402 window: &mut Window,
2403) {
2404 let should_clear = match active_tooltip.borrow().as_ref() {
2405 None => false,
2406 Some(ActiveTooltip::WaitingForShow { .. }) => false,
2407 Some(ActiveTooltip::Visible { is_hoverable, .. }) => !is_hoverable,
2408 Some(ActiveTooltip::WaitingForHide { .. }) => false,
2409 };
2410 if should_clear {
2411 active_tooltip.borrow_mut().take();
2412 window.refresh();
2413 }
2414}
2415
2416pub(crate) fn set_tooltip_on_window(
2417 active_tooltip: &Rc<RefCell<Option<ActiveTooltip>>>,
2418 window: &mut Window,
2419) -> Option<TooltipId> {
2420 let tooltip = match active_tooltip.borrow().as_ref() {
2421 None => return None,
2422 Some(ActiveTooltip::WaitingForShow { .. }) => return None,
2423 Some(ActiveTooltip::Visible { tooltip, .. }) => tooltip.clone(),
2424 Some(ActiveTooltip::WaitingForHide { tooltip, .. }) => tooltip.clone(),
2425 };
2426 Some(window.set_tooltip(tooltip))
2427}
2428
2429pub(crate) fn register_tooltip_mouse_handlers(
2430 active_tooltip: &Rc<RefCell<Option<ActiveTooltip>>>,
2431 tooltip_id: Option<TooltipId>,
2432 build_tooltip: Rc<dyn Fn(&mut Window, &mut App) -> Option<(AnyView, bool)>>,
2433 check_is_hovered: Rc<dyn Fn(&Window) -> bool>,
2434 check_is_hovered_during_prepaint: Rc<dyn Fn(&Window) -> bool>,
2435 window: &mut Window,
2436) {
2437 window.on_mouse_event({
2438 let active_tooltip = active_tooltip.clone();
2439 let build_tooltip = build_tooltip.clone();
2440 let check_is_hovered = check_is_hovered.clone();
2441 move |_: &MouseMoveEvent, phase, window, cx| {
2442 handle_tooltip_mouse_move(
2443 &active_tooltip,
2444 &build_tooltip,
2445 &check_is_hovered,
2446 &check_is_hovered_during_prepaint,
2447 phase,
2448 window,
2449 cx,
2450 )
2451 }
2452 });
2453
2454 window.on_mouse_event({
2455 let active_tooltip = active_tooltip.clone();
2456 move |_: &MouseDownEvent, _phase, window: &mut Window, _cx| {
2457 if !tooltip_id.map_or(false, |tooltip_id| tooltip_id.is_hovered(window)) {
2458 clear_active_tooltip_if_not_hoverable(&active_tooltip, window);
2459 }
2460 }
2461 });
2462
2463 window.on_mouse_event({
2464 let active_tooltip = active_tooltip.clone();
2465 move |_: &ScrollWheelEvent, _phase, window: &mut Window, _cx| {
2466 if !tooltip_id.map_or(false, |tooltip_id| tooltip_id.is_hovered(window)) {
2467 clear_active_tooltip_if_not_hoverable(&active_tooltip, window);
2468 }
2469 }
2470 });
2471}
2472
2473/// Handles displaying tooltips when an element is hovered.
2474///
2475/// The mouse hovering logic also relies on being called from window prepaint in order to handle the
2476/// case where the element the tooltip is on is not rendered - in that case its mouse listeners are
2477/// also not registered. During window prepaint, the hitbox information is not available, so
2478/// `check_is_hovered_during_prepaint` is used which bases the check off of the absolute bounds of
2479/// the element.
2480///
2481/// TODO: There's a minor bug due to the use of absolute bounds while checking during prepaint - it
2482/// does not know if the hitbox is occluded. In the case where a tooltip gets displayed and then
2483/// gets occluded after display, it will stick around until the mouse exits the hover bounds.
2484fn handle_tooltip_mouse_move(
2485 active_tooltip: &Rc<RefCell<Option<ActiveTooltip>>>,
2486 build_tooltip: &Rc<dyn Fn(&mut Window, &mut App) -> Option<(AnyView, bool)>>,
2487 check_is_hovered: &Rc<dyn Fn(&Window) -> bool>,
2488 check_is_hovered_during_prepaint: &Rc<dyn Fn(&Window) -> bool>,
2489 phase: DispatchPhase,
2490 window: &mut Window,
2491 cx: &mut App,
2492) {
2493 // Separates logic for what mutation should occur from applying it, to avoid overlapping
2494 // RefCell borrows.
2495 enum Action {
2496 None,
2497 CancelShow,
2498 ScheduleShow,
2499 }
2500
2501 let action = match active_tooltip.borrow().as_ref() {
2502 None => {
2503 let is_hovered = check_is_hovered(window);
2504 if is_hovered && phase.bubble() {
2505 Action::ScheduleShow
2506 } else {
2507 Action::None
2508 }
2509 }
2510 Some(ActiveTooltip::WaitingForShow { .. }) => {
2511 let is_hovered = check_is_hovered(window);
2512 if is_hovered {
2513 Action::None
2514 } else {
2515 Action::CancelShow
2516 }
2517 }
2518 // These are handled in check_visible_and_update.
2519 Some(ActiveTooltip::Visible { .. }) | Some(ActiveTooltip::WaitingForHide { .. }) => {
2520 Action::None
2521 }
2522 };
2523
2524 match action {
2525 Action::None => {}
2526 Action::CancelShow => {
2527 // Cancel waiting to show tooltip when it is no longer hovered.
2528 active_tooltip.borrow_mut().take();
2529 }
2530 Action::ScheduleShow => {
2531 let delayed_show_task = window.spawn(cx, {
2532 let active_tooltip = active_tooltip.clone();
2533 let build_tooltip = build_tooltip.clone();
2534 let check_is_hovered_during_prepaint = check_is_hovered_during_prepaint.clone();
2535 async move |cx| {
2536 cx.background_executor().timer(TOOLTIP_SHOW_DELAY).await;
2537 cx.update(|window, cx| {
2538 let new_tooltip =
2539 build_tooltip(window, cx).map(|(view, tooltip_is_hoverable)| {
2540 let active_tooltip = active_tooltip.clone();
2541 ActiveTooltip::Visible {
2542 tooltip: AnyTooltip {
2543 view,
2544 mouse_position: window.mouse_position(),
2545 check_visible_and_update: Rc::new(
2546 move |tooltip_bounds, window, cx| {
2547 handle_tooltip_check_visible_and_update(
2548 &active_tooltip,
2549 tooltip_is_hoverable,
2550 &check_is_hovered_during_prepaint,
2551 tooltip_bounds,
2552 window,
2553 cx,
2554 )
2555 },
2556 ),
2557 },
2558 is_hoverable: tooltip_is_hoverable,
2559 }
2560 });
2561 *active_tooltip.borrow_mut() = new_tooltip;
2562 window.refresh();
2563 })
2564 .ok();
2565 }
2566 });
2567 active_tooltip
2568 .borrow_mut()
2569 .replace(ActiveTooltip::WaitingForShow {
2570 _task: delayed_show_task,
2571 });
2572 }
2573 }
2574}
2575
2576/// Returns a callback which will be called by window prepaint to update tooltip visibility. The
2577/// purpose of doing this logic here instead of the mouse move handler is that the mouse move
2578/// handler won't get called when the element is not painted (e.g. via use of `visible_on_hover`).
2579fn handle_tooltip_check_visible_and_update(
2580 active_tooltip: &Rc<RefCell<Option<ActiveTooltip>>>,
2581 tooltip_is_hoverable: bool,
2582 check_is_hovered: &Rc<dyn Fn(&Window) -> bool>,
2583 tooltip_bounds: Bounds<Pixels>,
2584 window: &mut Window,
2585 cx: &mut App,
2586) -> bool {
2587 // Separates logic for what mutation should occur from applying it, to avoid overlapping RefCell
2588 // borrows.
2589 enum Action {
2590 None,
2591 Hide,
2592 ScheduleHide(AnyTooltip),
2593 CancelHide(AnyTooltip),
2594 }
2595
2596 let is_hovered = check_is_hovered(window)
2597 || (tooltip_is_hoverable && tooltip_bounds.contains(&window.mouse_position()));
2598 let action = match active_tooltip.borrow().as_ref() {
2599 Some(ActiveTooltip::Visible { tooltip, .. }) => {
2600 if is_hovered {
2601 Action::None
2602 } else {
2603 if tooltip_is_hoverable {
2604 Action::ScheduleHide(tooltip.clone())
2605 } else {
2606 Action::Hide
2607 }
2608 }
2609 }
2610 Some(ActiveTooltip::WaitingForHide { tooltip, .. }) => {
2611 if is_hovered {
2612 Action::CancelHide(tooltip.clone())
2613 } else {
2614 Action::None
2615 }
2616 }
2617 None | Some(ActiveTooltip::WaitingForShow { .. }) => Action::None,
2618 };
2619
2620 match action {
2621 Action::None => {}
2622 Action::Hide => clear_active_tooltip(&active_tooltip, window),
2623 Action::ScheduleHide(tooltip) => {
2624 let delayed_hide_task = window.spawn(cx, {
2625 let active_tooltip = active_tooltip.clone();
2626 async move |cx| {
2627 cx.background_executor()
2628 .timer(HOVERABLE_TOOLTIP_HIDE_DELAY)
2629 .await;
2630 if active_tooltip.borrow_mut().take().is_some() {
2631 cx.update(|window, _cx| window.refresh()).ok();
2632 }
2633 }
2634 });
2635 active_tooltip
2636 .borrow_mut()
2637 .replace(ActiveTooltip::WaitingForHide {
2638 tooltip,
2639 _task: delayed_hide_task,
2640 });
2641 }
2642 Action::CancelHide(tooltip) => {
2643 // Cancel waiting to hide tooltip when it becomes hovered.
2644 active_tooltip.borrow_mut().replace(ActiveTooltip::Visible {
2645 tooltip,
2646 is_hoverable: true,
2647 });
2648 }
2649 }
2650
2651 active_tooltip.borrow().is_some()
2652}
2653
2654#[derive(Default)]
2655pub(crate) struct GroupHitboxes(HashMap<SharedString, SmallVec<[HitboxId; 1]>>);
2656
2657impl Global for GroupHitboxes {}
2658
2659impl GroupHitboxes {
2660 pub fn get(name: &SharedString, cx: &mut App) -> Option<HitboxId> {
2661 cx.default_global::<Self>()
2662 .0
2663 .get(name)
2664 .and_then(|bounds_stack| bounds_stack.last())
2665 .cloned()
2666 }
2667
2668 pub fn push(name: SharedString, hitbox_id: HitboxId, cx: &mut App) {
2669 cx.default_global::<Self>()
2670 .0
2671 .entry(name)
2672 .or_default()
2673 .push(hitbox_id);
2674 }
2675
2676 pub fn pop(name: &SharedString, cx: &mut App) {
2677 cx.default_global::<Self>().0.get_mut(name).unwrap().pop();
2678 }
2679}
2680
2681/// A wrapper around an element that can be focused.
2682pub struct FocusableWrapper<E> {
2683 /// The element that is focusable
2684 pub element: E,
2685}
2686
2687impl<E: InteractiveElement> FocusableElement for FocusableWrapper<E> {}
2688
2689impl<E> InteractiveElement for FocusableWrapper<E>
2690where
2691 E: InteractiveElement,
2692{
2693 fn interactivity(&mut self) -> &mut Interactivity {
2694 self.element.interactivity()
2695 }
2696}
2697
2698impl<E: StatefulInteractiveElement> StatefulInteractiveElement for FocusableWrapper<E> {}
2699
2700impl<E> Styled for FocusableWrapper<E>
2701where
2702 E: Styled,
2703{
2704 fn style(&mut self) -> &mut StyleRefinement {
2705 self.element.style()
2706 }
2707}
2708
2709impl FocusableWrapper<Div> {
2710 /// Add a listener to be called when the children of this `Div` are prepainted.
2711 /// This allows you to store the [`Bounds`] of the children for later use.
2712 pub fn on_children_prepainted(
2713 mut self,
2714 listener: impl Fn(Vec<Bounds<Pixels>>, &mut Window, &mut App) + 'static,
2715 ) -> Self {
2716 self.element = self.element.on_children_prepainted(listener);
2717 self
2718 }
2719}
2720
2721impl<E> Element for FocusableWrapper<E>
2722where
2723 E: Element,
2724{
2725 type RequestLayoutState = E::RequestLayoutState;
2726 type PrepaintState = E::PrepaintState;
2727
2728 fn id(&self) -> Option<ElementId> {
2729 self.element.id()
2730 }
2731
2732 fn request_layout(
2733 &mut self,
2734 id: Option<&GlobalElementId>,
2735 window: &mut Window,
2736 cx: &mut App,
2737 ) -> (LayoutId, Self::RequestLayoutState) {
2738 self.element.request_layout(id, window, cx)
2739 }
2740
2741 fn prepaint(
2742 &mut self,
2743 id: Option<&GlobalElementId>,
2744 bounds: Bounds<Pixels>,
2745 state: &mut Self::RequestLayoutState,
2746 window: &mut Window,
2747 cx: &mut App,
2748 ) -> E::PrepaintState {
2749 self.element.prepaint(id, bounds, state, window, cx)
2750 }
2751
2752 fn paint(
2753 &mut self,
2754 id: Option<&GlobalElementId>,
2755 bounds: Bounds<Pixels>,
2756 request_layout: &mut Self::RequestLayoutState,
2757 prepaint: &mut Self::PrepaintState,
2758 window: &mut Window,
2759 cx: &mut App,
2760 ) {
2761 self.element
2762 .paint(id, bounds, request_layout, prepaint, window, cx)
2763 }
2764}
2765
2766impl<E> IntoElement for FocusableWrapper<E>
2767where
2768 E: IntoElement,
2769{
2770 type Element = E::Element;
2771
2772 fn into_element(self) -> Self::Element {
2773 self.element.into_element()
2774 }
2775}
2776
2777impl<E> ParentElement for FocusableWrapper<E>
2778where
2779 E: ParentElement,
2780{
2781 fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
2782 self.element.extend(elements)
2783 }
2784}
2785
2786/// A wrapper around an element that can store state, produced after assigning an ElementId.
2787pub struct Stateful<E> {
2788 pub(crate) element: E,
2789}
2790
2791impl<E> Styled for Stateful<E>
2792where
2793 E: Styled,
2794{
2795 fn style(&mut self) -> &mut StyleRefinement {
2796 self.element.style()
2797 }
2798}
2799
2800impl<E> StatefulInteractiveElement for Stateful<E>
2801where
2802 E: Element,
2803 Self: InteractiveElement,
2804{
2805}
2806
2807impl<E> InteractiveElement for Stateful<E>
2808where
2809 E: InteractiveElement,
2810{
2811 fn interactivity(&mut self) -> &mut Interactivity {
2812 self.element.interactivity()
2813 }
2814}
2815
2816impl<E: FocusableElement> FocusableElement for Stateful<E> {}
2817
2818impl<E> Element for Stateful<E>
2819where
2820 E: Element,
2821{
2822 type RequestLayoutState = E::RequestLayoutState;
2823 type PrepaintState = E::PrepaintState;
2824
2825 fn id(&self) -> Option<ElementId> {
2826 self.element.id()
2827 }
2828
2829 fn request_layout(
2830 &mut self,
2831 id: Option<&GlobalElementId>,
2832 window: &mut Window,
2833 cx: &mut App,
2834 ) -> (LayoutId, Self::RequestLayoutState) {
2835 self.element.request_layout(id, window, cx)
2836 }
2837
2838 fn prepaint(
2839 &mut self,
2840 id: Option<&GlobalElementId>,
2841 bounds: Bounds<Pixels>,
2842 state: &mut Self::RequestLayoutState,
2843 window: &mut Window,
2844 cx: &mut App,
2845 ) -> E::PrepaintState {
2846 self.element.prepaint(id, bounds, state, window, cx)
2847 }
2848
2849 fn paint(
2850 &mut self,
2851 id: Option<&GlobalElementId>,
2852 bounds: Bounds<Pixels>,
2853 request_layout: &mut Self::RequestLayoutState,
2854 prepaint: &mut Self::PrepaintState,
2855 window: &mut Window,
2856 cx: &mut App,
2857 ) {
2858 self.element
2859 .paint(id, bounds, request_layout, prepaint, window, cx);
2860 }
2861}
2862
2863impl<E> IntoElement for Stateful<E>
2864where
2865 E: Element,
2866{
2867 type Element = Self;
2868
2869 fn into_element(self) -> Self::Element {
2870 self
2871 }
2872}
2873
2874impl<E> ParentElement for Stateful<E>
2875where
2876 E: ParentElement,
2877{
2878 fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
2879 self.element.extend(elements)
2880 }
2881}
2882
2883/// Represents an element that can be scrolled *to* in its parent element.
2884///
2885/// Contrary to [ScrollHandle::scroll_to_item], an anchored element does not have to be an immediate child of the parent.
2886#[derive(Clone)]
2887pub struct ScrollAnchor {
2888 handle: ScrollHandle,
2889 last_origin: Rc<RefCell<Point<Pixels>>>,
2890}
2891
2892impl ScrollAnchor {
2893 /// Creates a [ScrollAnchor] associated with a given [ScrollHandle].
2894 pub fn for_handle(handle: ScrollHandle) -> Self {
2895 Self {
2896 handle,
2897 last_origin: Default::default(),
2898 }
2899 }
2900 /// Request scroll to this item on the next frame.
2901 pub fn scroll_to(&self, window: &mut Window, _cx: &mut App) {
2902 let this = self.clone();
2903
2904 window.on_next_frame(move |_, _| {
2905 let viewport_bounds = this.handle.bounds();
2906 let self_bounds = *this.last_origin.borrow();
2907 this.handle.set_offset(viewport_bounds.origin - self_bounds);
2908 });
2909 }
2910}
2911
2912#[derive(Default, Debug)]
2913struct ScrollHandleState {
2914 offset: Rc<RefCell<Point<Pixels>>>,
2915 bounds: Bounds<Pixels>,
2916 child_bounds: Vec<Bounds<Pixels>>,
2917 scroll_to_bottom: bool,
2918 overflow: Point<Overflow>,
2919}
2920
2921/// A handle to the scrollable aspects of an element.
2922/// Used for accessing scroll state, like the current scroll offset,
2923/// and for mutating the scroll state, like scrolling to a specific child.
2924#[derive(Clone, Debug)]
2925pub struct ScrollHandle(Rc<RefCell<ScrollHandleState>>);
2926
2927impl Default for ScrollHandle {
2928 fn default() -> Self {
2929 Self::new()
2930 }
2931}
2932
2933impl ScrollHandle {
2934 /// Construct a new scroll handle.
2935 pub fn new() -> Self {
2936 Self(Rc::default())
2937 }
2938
2939 /// Get the current scroll offset.
2940 pub fn offset(&self) -> Point<Pixels> {
2941 *self.0.borrow().offset.borrow()
2942 }
2943
2944 /// Get the top child that's scrolled into view.
2945 pub fn top_item(&self) -> usize {
2946 let state = self.0.borrow();
2947 let top = state.bounds.top() - state.offset.borrow().y;
2948
2949 match state.child_bounds.binary_search_by(|bounds| {
2950 if top < bounds.top() {
2951 Ordering::Greater
2952 } else if top > bounds.bottom() {
2953 Ordering::Less
2954 } else {
2955 Ordering::Equal
2956 }
2957 }) {
2958 Ok(ix) => ix,
2959 Err(ix) => ix.min(state.child_bounds.len().saturating_sub(1)),
2960 }
2961 }
2962
2963 /// Return the bounds into which this child is painted
2964 pub fn bounds(&self) -> Bounds<Pixels> {
2965 self.0.borrow().bounds
2966 }
2967
2968 /// Set the bounds into which this child is painted
2969 pub(super) fn set_bounds(&self, bounds: Bounds<Pixels>) {
2970 self.0.borrow_mut().bounds = bounds;
2971 }
2972
2973 /// Get the bounds for a specific child.
2974 pub fn bounds_for_item(&self, ix: usize) -> Option<Bounds<Pixels>> {
2975 self.0.borrow().child_bounds.get(ix).cloned()
2976 }
2977
2978 /// scroll_to_item scrolls the minimal amount to ensure that the child is
2979 /// fully visible
2980 pub fn scroll_to_item(&self, ix: usize) {
2981 let state = self.0.borrow();
2982
2983 let Some(bounds) = state.child_bounds.get(ix) else {
2984 return;
2985 };
2986
2987 let mut scroll_offset = state.offset.borrow_mut();
2988
2989 if state.overflow.y == Overflow::Scroll {
2990 if bounds.top() + scroll_offset.y < state.bounds.top() {
2991 scroll_offset.y = state.bounds.top() - bounds.top();
2992 } else if bounds.bottom() + scroll_offset.y > state.bounds.bottom() {
2993 scroll_offset.y = state.bounds.bottom() - bounds.bottom();
2994 }
2995 }
2996
2997 if state.overflow.x == Overflow::Scroll {
2998 if bounds.left() + scroll_offset.x < state.bounds.left() {
2999 scroll_offset.x = state.bounds.left() - bounds.left();
3000 } else if bounds.right() + scroll_offset.x > state.bounds.right() {
3001 scroll_offset.x = state.bounds.right() - bounds.right();
3002 }
3003 }
3004 }
3005
3006 /// Scrolls to the bottom.
3007 pub fn scroll_to_bottom(&self) {
3008 let mut state = self.0.borrow_mut();
3009 state.scroll_to_bottom = true;
3010 }
3011
3012 /// Set the offset explicitly. The offset is the distance from the top left of the
3013 /// parent container to the top left of the first child.
3014 /// As you scroll further down the offset becomes more negative.
3015 pub fn set_offset(&self, mut position: Point<Pixels>) {
3016 let state = self.0.borrow();
3017 *state.offset.borrow_mut() = position;
3018 }
3019
3020 /// Get the logical scroll top, based on a child index and a pixel offset.
3021 pub fn logical_scroll_top(&self) -> (usize, Pixels) {
3022 let ix = self.top_item();
3023 let state = self.0.borrow();
3024
3025 if let Some(child_bounds) = state.child_bounds.get(ix) {
3026 (
3027 ix,
3028 child_bounds.top() + state.offset.borrow().y - state.bounds.top(),
3029 )
3030 } else {
3031 (ix, px(0.))
3032 }
3033 }
3034
3035 /// Get the count of children for scrollable item.
3036 pub fn children_count(&self) -> usize {
3037 self.0.borrow().child_bounds.len()
3038 }
3039}