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