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