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