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