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