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 }
1108}
1109
1110/// A [`Div`] element, the all-in-one element for building complex UIs in GPUI
1111pub struct Div {
1112 interactivity: Interactivity,
1113 children: SmallVec<[AnyElement; 2]>,
1114}
1115
1116/// A frame state for a `Div` element, which contains layout IDs for its children.
1117///
1118/// This struct is used internally by the `Div` element to manage the layout state of its children
1119/// during the UI update cycle. It holds a small vector of `LayoutId` values, each corresponding to
1120/// a child element of the `Div`. These IDs are used to query the layout engine for the computed
1121/// bounds of the children after the layout phase is complete.
1122pub struct DivFrameState {
1123 child_layout_ids: SmallVec<[LayoutId; 2]>,
1124}
1125
1126impl Styled for Div {
1127 fn style(&mut self) -> &mut StyleRefinement {
1128 &mut self.interactivity.base_style
1129 }
1130}
1131
1132impl InteractiveElement for Div {
1133 fn interactivity(&mut self) -> &mut Interactivity {
1134 &mut self.interactivity
1135 }
1136}
1137
1138impl ParentElement for Div {
1139 fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
1140 self.children.extend(elements)
1141 }
1142}
1143
1144impl Element for Div {
1145 type RequestLayoutState = DivFrameState;
1146 type PrepaintState = Option<Hitbox>;
1147
1148 fn id(&self) -> Option<ElementId> {
1149 self.interactivity.element_id.clone()
1150 }
1151
1152 fn request_layout(
1153 &mut self,
1154 global_id: Option<&GlobalElementId>,
1155 cx: &mut WindowContext,
1156 ) -> (LayoutId, Self::RequestLayoutState) {
1157 let mut child_layout_ids = SmallVec::new();
1158 let layout_id = self
1159 .interactivity
1160 .request_layout(global_id, cx, |style, cx| {
1161 cx.with_text_style(style.text_style().cloned(), |cx| {
1162 child_layout_ids = self
1163 .children
1164 .iter_mut()
1165 .map(|child| child.request_layout(cx))
1166 .collect::<SmallVec<_>>();
1167 cx.request_layout(style, child_layout_ids.iter().copied())
1168 })
1169 });
1170 (layout_id, DivFrameState { child_layout_ids })
1171 }
1172
1173 fn prepaint(
1174 &mut self,
1175 global_id: Option<&GlobalElementId>,
1176 bounds: Bounds<Pixels>,
1177 request_layout: &mut Self::RequestLayoutState,
1178 cx: &mut WindowContext,
1179 ) -> Option<Hitbox> {
1180 let mut child_min = point(Pixels::MAX, Pixels::MAX);
1181 let mut child_max = Point::default();
1182 if let Some(handle) = self.interactivity.scroll_anchor.as_ref() {
1183 *handle.last_origin.borrow_mut() = bounds.origin - cx.element_offset();
1184 }
1185 let content_size = if request_layout.child_layout_ids.is_empty() {
1186 bounds.size
1187 } else if let Some(scroll_handle) = self.interactivity.tracked_scroll_handle.as_ref() {
1188 let mut state = scroll_handle.0.borrow_mut();
1189 state.child_bounds = Vec::with_capacity(request_layout.child_layout_ids.len());
1190 state.bounds = bounds;
1191 let requested = state.requested_scroll_top.take();
1192
1193 for (ix, child_layout_id) in request_layout.child_layout_ids.iter().enumerate() {
1194 let child_bounds = cx.layout_bounds(*child_layout_id);
1195 child_min = child_min.min(&child_bounds.origin);
1196 child_max = child_max.max(&child_bounds.bottom_right());
1197 state.child_bounds.push(child_bounds);
1198
1199 if let Some(requested) = requested.as_ref() {
1200 if requested.0 == ix {
1201 *state.offset.borrow_mut() =
1202 bounds.origin - (child_bounds.origin - point(px(0.), requested.1));
1203 }
1204 }
1205 }
1206 (child_max - child_min).into()
1207 } else {
1208 for child_layout_id in &request_layout.child_layout_ids {
1209 let child_bounds = cx.layout_bounds(*child_layout_id);
1210 child_min = child_min.min(&child_bounds.origin);
1211 child_max = child_max.max(&child_bounds.bottom_right());
1212 }
1213 (child_max - child_min).into()
1214 };
1215
1216 self.interactivity.prepaint(
1217 global_id,
1218 bounds,
1219 content_size,
1220 cx,
1221 |_style, scroll_offset, hitbox, cx| {
1222 cx.with_element_offset(scroll_offset, |cx| {
1223 for child in &mut self.children {
1224 child.prepaint(cx);
1225 }
1226 });
1227 hitbox
1228 },
1229 )
1230 }
1231
1232 fn paint(
1233 &mut self,
1234 global_id: Option<&GlobalElementId>,
1235 bounds: Bounds<Pixels>,
1236 _request_layout: &mut Self::RequestLayoutState,
1237 hitbox: &mut Option<Hitbox>,
1238 cx: &mut WindowContext,
1239 ) {
1240 self.interactivity
1241 .paint(global_id, bounds, hitbox.as_ref(), cx, |_style, cx| {
1242 for child in &mut self.children {
1243 child.paint(cx);
1244 }
1245 });
1246 }
1247}
1248
1249impl IntoElement for Div {
1250 type Element = Self;
1251
1252 fn into_element(self) -> Self::Element {
1253 self
1254 }
1255}
1256
1257/// The interactivity struct. Powers all of the general-purpose
1258/// interactivity in the `Div` element.
1259#[derive(Default)]
1260pub struct Interactivity {
1261 /// The element ID of the element. In id is required to support a stateful subset of the interactivity such as on_click.
1262 pub element_id: Option<ElementId>,
1263 /// Whether the element was clicked. This will only be present after layout.
1264 pub active: Option<bool>,
1265 /// Whether the element was hovered. This will only be present after paint if an hitbox
1266 /// was created for the interactive element.
1267 pub hovered: Option<bool>,
1268 pub(crate) tooltip_id: Option<TooltipId>,
1269 pub(crate) content_size: Size<Pixels>,
1270 pub(crate) key_context: Option<KeyContext>,
1271 pub(crate) focusable: bool,
1272 pub(crate) tracked_focus_handle: Option<FocusHandle>,
1273 pub(crate) tracked_scroll_handle: Option<ScrollHandle>,
1274 pub(crate) scroll_anchor: Option<ScrollAnchor>,
1275 pub(crate) scroll_offset: Option<Rc<RefCell<Point<Pixels>>>>,
1276 pub(crate) group: Option<SharedString>,
1277 /// The base style of the element, before any modifications are applied
1278 /// by focus, active, etc.
1279 pub base_style: Box<StyleRefinement>,
1280 pub(crate) focus_style: Option<Box<StyleRefinement>>,
1281 pub(crate) in_focus_style: Option<Box<StyleRefinement>>,
1282 pub(crate) hover_style: Option<Box<StyleRefinement>>,
1283 pub(crate) group_hover_style: Option<GroupStyle>,
1284 pub(crate) active_style: Option<Box<StyleRefinement>>,
1285 pub(crate) group_active_style: Option<GroupStyle>,
1286 pub(crate) drag_over_styles: Vec<(
1287 TypeId,
1288 Box<dyn Fn(&dyn Any, &mut WindowContext) -> StyleRefinement>,
1289 )>,
1290 pub(crate) group_drag_over_styles: Vec<(TypeId, GroupStyle)>,
1291 pub(crate) mouse_down_listeners: Vec<MouseDownListener>,
1292 pub(crate) mouse_up_listeners: Vec<MouseUpListener>,
1293 pub(crate) mouse_move_listeners: Vec<MouseMoveListener>,
1294 pub(crate) scroll_wheel_listeners: Vec<ScrollWheelListener>,
1295 pub(crate) key_down_listeners: Vec<KeyDownListener>,
1296 pub(crate) key_up_listeners: Vec<KeyUpListener>,
1297 pub(crate) modifiers_changed_listeners: Vec<ModifiersChangedListener>,
1298 pub(crate) action_listeners: Vec<(TypeId, ActionListener)>,
1299 pub(crate) drop_listeners: Vec<(TypeId, DropListener)>,
1300 pub(crate) can_drop_predicate: Option<CanDropPredicate>,
1301 pub(crate) click_listeners: Vec<ClickListener>,
1302 pub(crate) drag_listener: Option<(Arc<dyn Any>, DragListener)>,
1303 pub(crate) hover_listener: Option<Box<dyn Fn(&bool, &mut WindowContext)>>,
1304 pub(crate) tooltip_builder: Option<TooltipBuilder>,
1305 pub(crate) occlude_mouse: bool,
1306
1307 #[cfg(debug_assertions)]
1308 pub(crate) location: Option<core::panic::Location<'static>>,
1309
1310 #[cfg(any(test, feature = "test-support"))]
1311 pub(crate) debug_selector: Option<String>,
1312}
1313
1314impl Interactivity {
1315 /// Layout this element according to this interactivity state's configured styles
1316 pub fn request_layout(
1317 &mut self,
1318 global_id: Option<&GlobalElementId>,
1319 cx: &mut WindowContext,
1320 f: impl FnOnce(Style, &mut WindowContext) -> LayoutId,
1321 ) -> LayoutId {
1322 cx.with_optional_element_state::<InteractiveElementState, _>(
1323 global_id,
1324 |element_state, cx| {
1325 let mut element_state =
1326 element_state.map(|element_state| element_state.unwrap_or_default());
1327
1328 if let Some(element_state) = element_state.as_ref() {
1329 if cx.has_active_drag() {
1330 if let Some(pending_mouse_down) = element_state.pending_mouse_down.as_ref()
1331 {
1332 *pending_mouse_down.borrow_mut() = None;
1333 }
1334 if let Some(clicked_state) = element_state.clicked_state.as_ref() {
1335 *clicked_state.borrow_mut() = ElementClickedState::default();
1336 }
1337 }
1338 }
1339
1340 // Ensure we store a focus handle in our element state if we're focusable.
1341 // If there's an explicit focus handle we're tracking, use that. Otherwise
1342 // create a new handle and store it in the element state, which lives for as
1343 // as frames contain an element with this id.
1344 if self.focusable && self.tracked_focus_handle.is_none() {
1345 if let Some(element_state) = element_state.as_mut() {
1346 self.tracked_focus_handle = Some(
1347 element_state
1348 .focus_handle
1349 .get_or_insert_with(|| cx.focus_handle())
1350 .clone(),
1351 );
1352 }
1353 }
1354
1355 if let Some(scroll_handle) = self.tracked_scroll_handle.as_ref() {
1356 self.scroll_offset = Some(scroll_handle.0.borrow().offset.clone());
1357 } else if self.base_style.overflow.x == Some(Overflow::Scroll)
1358 || self.base_style.overflow.y == Some(Overflow::Scroll)
1359 {
1360 if let Some(element_state) = element_state.as_mut() {
1361 self.scroll_offset = Some(
1362 element_state
1363 .scroll_offset
1364 .get_or_insert_with(Rc::default)
1365 .clone(),
1366 );
1367 }
1368 }
1369
1370 let style = self.compute_style_internal(None, element_state.as_mut(), cx);
1371 let layout_id = f(style, cx);
1372 (layout_id, element_state)
1373 },
1374 )
1375 }
1376
1377 /// Commit the bounds of this element according to this interactivity state's configured styles.
1378 pub fn prepaint<R>(
1379 &mut self,
1380 global_id: Option<&GlobalElementId>,
1381 bounds: Bounds<Pixels>,
1382 content_size: Size<Pixels>,
1383 cx: &mut WindowContext,
1384 f: impl FnOnce(&Style, Point<Pixels>, Option<Hitbox>, &mut WindowContext) -> R,
1385 ) -> R {
1386 self.content_size = content_size;
1387 if let Some(focus_handle) = self.tracked_focus_handle.as_ref() {
1388 cx.set_focus_handle(focus_handle);
1389 }
1390 cx.with_optional_element_state::<InteractiveElementState, _>(
1391 global_id,
1392 |element_state, cx| {
1393 let mut element_state =
1394 element_state.map(|element_state| element_state.unwrap_or_default());
1395 let style = self.compute_style_internal(None, element_state.as_mut(), cx);
1396
1397 if let Some(element_state) = element_state.as_ref() {
1398 if let Some(clicked_state) = element_state.clicked_state.as_ref() {
1399 let clicked_state = clicked_state.borrow();
1400 self.active = Some(clicked_state.element);
1401 }
1402
1403 if let Some(active_tooltip) = element_state.active_tooltip.as_ref() {
1404 if let Some(active_tooltip) = active_tooltip.borrow().as_ref() {
1405 if let Some(tooltip) = active_tooltip.tooltip.clone() {
1406 self.tooltip_id = Some(cx.set_tooltip(tooltip));
1407 }
1408 }
1409 }
1410 }
1411
1412 cx.with_text_style(style.text_style().cloned(), |cx| {
1413 cx.with_content_mask(style.overflow_mask(bounds, cx.rem_size()), |cx| {
1414 let hitbox = if self.should_insert_hitbox(&style) {
1415 Some(cx.insert_hitbox(bounds, self.occlude_mouse))
1416 } else {
1417 None
1418 };
1419
1420 let invalidate_tooltip = hitbox
1421 .as_ref()
1422 .map_or(true, |hitbox| !hitbox.bounds.contains(&cx.mouse_position()));
1423 if invalidate_tooltip {
1424 if let Some(active_tooltip) = element_state
1425 .as_ref()
1426 .and_then(|state| state.active_tooltip.as_ref())
1427 {
1428 *active_tooltip.borrow_mut() = None;
1429 self.tooltip_id = None;
1430 }
1431 }
1432
1433 let scroll_offset = self.clamp_scroll_position(bounds, &style, cx);
1434 let result = f(&style, scroll_offset, hitbox, cx);
1435 (result, element_state)
1436 })
1437 })
1438 },
1439 )
1440 }
1441
1442 fn should_insert_hitbox(&self, style: &Style) -> bool {
1443 self.occlude_mouse
1444 || style.mouse_cursor.is_some()
1445 || self.group.is_some()
1446 || self.scroll_offset.is_some()
1447 || self.tracked_focus_handle.is_some()
1448 || self.hover_style.is_some()
1449 || self.group_hover_style.is_some()
1450 || !self.mouse_up_listeners.is_empty()
1451 || !self.mouse_down_listeners.is_empty()
1452 || !self.mouse_move_listeners.is_empty()
1453 || !self.click_listeners.is_empty()
1454 || !self.scroll_wheel_listeners.is_empty()
1455 || self.drag_listener.is_some()
1456 || !self.drop_listeners.is_empty()
1457 || self.tooltip_builder.is_some()
1458 }
1459
1460 fn clamp_scroll_position(
1461 &self,
1462 bounds: Bounds<Pixels>,
1463 style: &Style,
1464 cx: &mut WindowContext,
1465 ) -> Point<Pixels> {
1466 if let Some(scroll_offset) = self.scroll_offset.as_ref() {
1467 if let Some(scroll_handle) = &self.tracked_scroll_handle {
1468 scroll_handle.0.borrow_mut().overflow = style.overflow;
1469 }
1470
1471 let rem_size = cx.rem_size();
1472 let padding_size = size(
1473 style
1474 .padding
1475 .left
1476 .to_pixels(bounds.size.width.into(), rem_size)
1477 + style
1478 .padding
1479 .right
1480 .to_pixels(bounds.size.width.into(), rem_size),
1481 style
1482 .padding
1483 .top
1484 .to_pixels(bounds.size.height.into(), rem_size)
1485 + style
1486 .padding
1487 .bottom
1488 .to_pixels(bounds.size.height.into(), rem_size),
1489 );
1490 let scroll_max = (self.content_size + padding_size - bounds.size).max(&Size::default());
1491 // Clamp scroll offset in case scroll max is smaller now (e.g., if children
1492 // were removed or the bounds became larger).
1493 let mut scroll_offset = scroll_offset.borrow_mut();
1494 scroll_offset.x = scroll_offset.x.clamp(-scroll_max.width, px(0.));
1495 scroll_offset.y = scroll_offset.y.clamp(-scroll_max.height, px(0.));
1496 *scroll_offset
1497 } else {
1498 Point::default()
1499 }
1500 }
1501
1502 /// Paint this element according to this interactivity state's configured styles
1503 /// and bind the element's mouse and keyboard events.
1504 ///
1505 /// content_size is the size of the content of the element, which may be larger than the
1506 /// element's bounds if the element is scrollable.
1507 ///
1508 /// the final computed style will be passed to the provided function, along
1509 /// with the current scroll offset
1510 pub fn paint(
1511 &mut self,
1512 global_id: Option<&GlobalElementId>,
1513 bounds: Bounds<Pixels>,
1514 hitbox: Option<&Hitbox>,
1515 cx: &mut WindowContext,
1516 f: impl FnOnce(&Style, &mut WindowContext),
1517 ) {
1518 self.hovered = hitbox.map(|hitbox| hitbox.is_hovered(cx));
1519 cx.with_optional_element_state::<InteractiveElementState, _>(
1520 global_id,
1521 |element_state, cx| {
1522 let mut element_state =
1523 element_state.map(|element_state| element_state.unwrap_or_default());
1524
1525 let style = self.compute_style_internal(hitbox, element_state.as_mut(), cx);
1526
1527 #[cfg(any(feature = "test-support", test))]
1528 if let Some(debug_selector) = &self.debug_selector {
1529 cx.window
1530 .next_frame
1531 .debug_bounds
1532 .insert(debug_selector.clone(), bounds);
1533 }
1534
1535 self.paint_hover_group_handler(cx);
1536
1537 if style.visibility == Visibility::Hidden {
1538 return ((), element_state);
1539 }
1540
1541 cx.with_element_opacity(style.opacity, |cx| {
1542 style.paint(bounds, cx, |cx: &mut WindowContext| {
1543 cx.with_text_style(style.text_style().cloned(), |cx| {
1544 cx.with_content_mask(
1545 style.overflow_mask(bounds, cx.rem_size()),
1546 |cx| {
1547 if let Some(hitbox) = hitbox {
1548 #[cfg(debug_assertions)]
1549 self.paint_debug_info(global_id, hitbox, &style, cx);
1550
1551 if !cx.has_active_drag() {
1552 if let Some(mouse_cursor) = style.mouse_cursor {
1553 cx.set_cursor_style(mouse_cursor, hitbox);
1554 }
1555 }
1556
1557 if let Some(group) = self.group.clone() {
1558 GroupHitboxes::push(group, hitbox.id, cx);
1559 }
1560
1561 self.paint_mouse_listeners(
1562 hitbox,
1563 element_state.as_mut(),
1564 cx,
1565 );
1566 self.paint_scroll_listener(hitbox, &style, cx);
1567 }
1568
1569 self.paint_keyboard_listeners(cx);
1570 f(&style, cx);
1571
1572 if hitbox.is_some() {
1573 if let Some(group) = self.group.as_ref() {
1574 GroupHitboxes::pop(group, cx);
1575 }
1576 }
1577 },
1578 );
1579 });
1580 });
1581 });
1582
1583 ((), element_state)
1584 },
1585 );
1586 }
1587
1588 #[cfg(debug_assertions)]
1589 fn paint_debug_info(
1590 &self,
1591 global_id: Option<&GlobalElementId>,
1592 hitbox: &Hitbox,
1593 style: &Style,
1594 cx: &mut WindowContext,
1595 ) {
1596 if global_id.is_some()
1597 && (style.debug || style.debug_below || cx.has_global::<crate::DebugBelow>())
1598 && hitbox.is_hovered(cx)
1599 {
1600 const FONT_SIZE: crate::Pixels = crate::Pixels(10.);
1601 let element_id = format!("{:?}", global_id.unwrap());
1602 let str_len = element_id.len();
1603
1604 let render_debug_text = |cx: &mut WindowContext| {
1605 if let Some(text) = cx
1606 .text_system()
1607 .shape_text(
1608 element_id.into(),
1609 FONT_SIZE,
1610 &[cx.text_style().to_run(str_len)],
1611 None,
1612 )
1613 .ok()
1614 .and_then(|mut text| text.pop())
1615 {
1616 text.paint(hitbox.origin, FONT_SIZE, cx).ok();
1617
1618 let text_bounds = crate::Bounds {
1619 origin: hitbox.origin,
1620 size: text.size(FONT_SIZE),
1621 };
1622 if self.location.is_some()
1623 && text_bounds.contains(&cx.mouse_position())
1624 && cx.modifiers().secondary()
1625 {
1626 let secondary_held = cx.modifiers().secondary();
1627 cx.on_key_event({
1628 move |e: &crate::ModifiersChangedEvent, _phase, cx| {
1629 if e.modifiers.secondary() != secondary_held
1630 && text_bounds.contains(&cx.mouse_position())
1631 {
1632 cx.refresh();
1633 }
1634 }
1635 });
1636
1637 let was_hovered = hitbox.is_hovered(cx);
1638 cx.on_mouse_event({
1639 let hitbox = hitbox.clone();
1640 move |_: &MouseMoveEvent, phase, cx| {
1641 if phase == DispatchPhase::Capture {
1642 let hovered = hitbox.is_hovered(cx);
1643 if hovered != was_hovered {
1644 cx.refresh();
1645 }
1646 }
1647 }
1648 });
1649
1650 cx.on_mouse_event({
1651 let hitbox = hitbox.clone();
1652 let location = self.location.unwrap();
1653 move |e: &crate::MouseDownEvent, phase, cx| {
1654 if text_bounds.contains(&e.position)
1655 && phase.capture()
1656 && hitbox.is_hovered(cx)
1657 {
1658 cx.stop_propagation();
1659 let Ok(dir) = std::env::current_dir() else {
1660 return;
1661 };
1662
1663 eprintln!(
1664 "This element was created at:\n{}:{}:{}",
1665 dir.join(location.file()).to_string_lossy(),
1666 location.line(),
1667 location.column()
1668 );
1669 }
1670 }
1671 });
1672 cx.paint_quad(crate::outline(
1673 crate::Bounds {
1674 origin: hitbox.origin
1675 + crate::point(crate::px(0.), FONT_SIZE - px(2.)),
1676 size: crate::Size {
1677 width: text_bounds.size.width,
1678 height: crate::px(1.),
1679 },
1680 },
1681 crate::red(),
1682 ))
1683 }
1684 }
1685 };
1686
1687 cx.with_text_style(
1688 Some(crate::TextStyleRefinement {
1689 color: Some(crate::red()),
1690 line_height: Some(FONT_SIZE.into()),
1691 background_color: Some(crate::white()),
1692 ..Default::default()
1693 }),
1694 render_debug_text,
1695 )
1696 }
1697 }
1698
1699 fn paint_mouse_listeners(
1700 &mut self,
1701 hitbox: &Hitbox,
1702 element_state: Option<&mut InteractiveElementState>,
1703 cx: &mut WindowContext,
1704 ) {
1705 // If this element can be focused, register a mouse down listener
1706 // that will automatically transfer focus when hitting the element.
1707 // This behavior can be suppressed by using `cx.prevent_default()`.
1708 if let Some(focus_handle) = self.tracked_focus_handle.clone() {
1709 let hitbox = hitbox.clone();
1710 cx.on_mouse_event(move |_: &MouseDownEvent, phase, cx| {
1711 if phase == DispatchPhase::Bubble
1712 && hitbox.is_hovered(cx)
1713 && !cx.default_prevented()
1714 {
1715 cx.focus(&focus_handle);
1716 // If there is a parent that is also focusable, prevent it
1717 // from transferring focus because we already did so.
1718 cx.prevent_default();
1719 }
1720 });
1721 }
1722
1723 for listener in self.mouse_down_listeners.drain(..) {
1724 let hitbox = hitbox.clone();
1725 cx.on_mouse_event(move |event: &MouseDownEvent, phase, cx| {
1726 listener(event, phase, &hitbox, cx);
1727 })
1728 }
1729
1730 for listener in self.mouse_up_listeners.drain(..) {
1731 let hitbox = hitbox.clone();
1732 cx.on_mouse_event(move |event: &MouseUpEvent, phase, cx| {
1733 listener(event, phase, &hitbox, cx);
1734 })
1735 }
1736
1737 for listener in self.mouse_move_listeners.drain(..) {
1738 let hitbox = hitbox.clone();
1739 cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
1740 listener(event, phase, &hitbox, cx);
1741 })
1742 }
1743
1744 for listener in self.scroll_wheel_listeners.drain(..) {
1745 let hitbox = hitbox.clone();
1746 cx.on_mouse_event(move |event: &ScrollWheelEvent, phase, cx| {
1747 listener(event, phase, &hitbox, cx);
1748 })
1749 }
1750
1751 if self.hover_style.is_some()
1752 || self.base_style.mouse_cursor.is_some()
1753 || cx.active_drag.is_some() && !self.drag_over_styles.is_empty()
1754 {
1755 let hitbox = hitbox.clone();
1756 let was_hovered = hitbox.is_hovered(cx);
1757 cx.on_mouse_event(move |_: &MouseMoveEvent, phase, cx| {
1758 let hovered = hitbox.is_hovered(cx);
1759 if phase == DispatchPhase::Capture && hovered != was_hovered {
1760 cx.refresh();
1761 }
1762 });
1763 }
1764
1765 let mut drag_listener = mem::take(&mut self.drag_listener);
1766 let drop_listeners = mem::take(&mut self.drop_listeners);
1767 let click_listeners = mem::take(&mut self.click_listeners);
1768 let can_drop_predicate = mem::take(&mut self.can_drop_predicate);
1769
1770 if !drop_listeners.is_empty() {
1771 let hitbox = hitbox.clone();
1772 cx.on_mouse_event({
1773 move |_: &MouseUpEvent, phase, cx| {
1774 if let Some(drag) = &cx.active_drag {
1775 if phase == DispatchPhase::Bubble && hitbox.is_hovered(cx) {
1776 let drag_state_type = drag.value.as_ref().type_id();
1777 for (drop_state_type, listener) in &drop_listeners {
1778 if *drop_state_type == drag_state_type {
1779 let drag = cx
1780 .active_drag
1781 .take()
1782 .expect("checked for type drag state type above");
1783
1784 let mut can_drop = true;
1785 if let Some(predicate) = &can_drop_predicate {
1786 can_drop = predicate(drag.value.as_ref(), cx);
1787 }
1788
1789 if can_drop {
1790 listener(drag.value.as_ref(), cx);
1791 cx.refresh();
1792 cx.stop_propagation();
1793 }
1794 }
1795 }
1796 }
1797 }
1798 }
1799 });
1800 }
1801
1802 if let Some(element_state) = element_state {
1803 if !click_listeners.is_empty() || drag_listener.is_some() {
1804 let pending_mouse_down = element_state
1805 .pending_mouse_down
1806 .get_or_insert_with(Default::default)
1807 .clone();
1808
1809 let clicked_state = element_state
1810 .clicked_state
1811 .get_or_insert_with(Default::default)
1812 .clone();
1813
1814 cx.on_mouse_event({
1815 let pending_mouse_down = pending_mouse_down.clone();
1816 let hitbox = hitbox.clone();
1817 move |event: &MouseDownEvent, phase, cx| {
1818 if phase == DispatchPhase::Bubble
1819 && event.button == MouseButton::Left
1820 && hitbox.is_hovered(cx)
1821 {
1822 *pending_mouse_down.borrow_mut() = Some(event.clone());
1823 cx.refresh();
1824 }
1825 }
1826 });
1827
1828 cx.on_mouse_event({
1829 let pending_mouse_down = pending_mouse_down.clone();
1830 let hitbox = hitbox.clone();
1831 move |event: &MouseMoveEvent, phase, cx| {
1832 if phase == DispatchPhase::Capture {
1833 return;
1834 }
1835
1836 let mut pending_mouse_down = pending_mouse_down.borrow_mut();
1837 if let Some(mouse_down) = pending_mouse_down.clone() {
1838 if !cx.has_active_drag()
1839 && (event.position - mouse_down.position).magnitude()
1840 > DRAG_THRESHOLD
1841 {
1842 if let Some((drag_value, drag_listener)) = drag_listener.take() {
1843 *clicked_state.borrow_mut() = ElementClickedState::default();
1844 let cursor_offset = event.position - hitbox.origin;
1845 let drag =
1846 (drag_listener)(drag_value.as_ref(), cursor_offset, cx);
1847 cx.active_drag = Some(AnyDrag {
1848 view: drag,
1849 value: drag_value,
1850 cursor_offset,
1851 });
1852 pending_mouse_down.take();
1853 cx.refresh();
1854 cx.stop_propagation();
1855 }
1856 }
1857 }
1858 }
1859 });
1860
1861 cx.on_mouse_event({
1862 let mut captured_mouse_down = None;
1863 let hitbox = hitbox.clone();
1864 move |event: &MouseUpEvent, phase, cx| match phase {
1865 // Clear the pending mouse down during the capture phase,
1866 // so that it happens even if another event handler stops
1867 // propagation.
1868 DispatchPhase::Capture => {
1869 let mut pending_mouse_down = pending_mouse_down.borrow_mut();
1870 if pending_mouse_down.is_some() && hitbox.is_hovered(cx) {
1871 captured_mouse_down = pending_mouse_down.take();
1872 cx.refresh();
1873 }
1874 }
1875 // Fire click handlers during the bubble phase.
1876 DispatchPhase::Bubble => {
1877 if let Some(mouse_down) = captured_mouse_down.take() {
1878 let mouse_click = ClickEvent {
1879 down: mouse_down,
1880 up: event.clone(),
1881 };
1882 for listener in &click_listeners {
1883 listener(&mouse_click, cx);
1884 }
1885 }
1886 }
1887 }
1888 });
1889 }
1890
1891 if let Some(hover_listener) = self.hover_listener.take() {
1892 let hitbox = hitbox.clone();
1893 let was_hovered = element_state
1894 .hover_state
1895 .get_or_insert_with(Default::default)
1896 .clone();
1897 let has_mouse_down = element_state
1898 .pending_mouse_down
1899 .get_or_insert_with(Default::default)
1900 .clone();
1901
1902 cx.on_mouse_event(move |_: &MouseMoveEvent, phase, cx| {
1903 if phase != DispatchPhase::Bubble {
1904 return;
1905 }
1906 let is_hovered = has_mouse_down.borrow().is_none()
1907 && !cx.has_active_drag()
1908 && hitbox.is_hovered(cx);
1909 let mut was_hovered = was_hovered.borrow_mut();
1910
1911 if is_hovered != *was_hovered {
1912 *was_hovered = is_hovered;
1913 drop(was_hovered);
1914
1915 hover_listener(&is_hovered, cx);
1916 }
1917 });
1918 }
1919
1920 // Ensure to remove active tooltip if tooltip builder is none
1921 if self.tooltip_builder.is_none() {
1922 element_state.active_tooltip.take();
1923 }
1924
1925 if let Some(tooltip_builder) = self.tooltip_builder.take() {
1926 let tooltip_is_hoverable = tooltip_builder.hoverable;
1927 let active_tooltip = element_state
1928 .active_tooltip
1929 .get_or_insert_with(Default::default)
1930 .clone();
1931 let pending_mouse_down = element_state
1932 .pending_mouse_down
1933 .get_or_insert_with(Default::default)
1934 .clone();
1935
1936 cx.on_mouse_event({
1937 let active_tooltip = active_tooltip.clone();
1938 let hitbox = hitbox.clone();
1939 let tooltip_id = self.tooltip_id;
1940 move |_: &MouseMoveEvent, phase, cx| {
1941 let is_hovered =
1942 pending_mouse_down.borrow().is_none() && hitbox.is_hovered(cx);
1943 let tooltip_is_hovered =
1944 tooltip_id.map_or(false, |tooltip_id| tooltip_id.is_hovered(cx));
1945 if !is_hovered && (!tooltip_is_hoverable || !tooltip_is_hovered) {
1946 if active_tooltip.borrow_mut().take().is_some() {
1947 cx.refresh();
1948 }
1949
1950 return;
1951 }
1952
1953 if phase != DispatchPhase::Bubble {
1954 return;
1955 }
1956
1957 if active_tooltip.borrow().is_none() {
1958 let task = cx.spawn({
1959 let active_tooltip = active_tooltip.clone();
1960 let build_tooltip = tooltip_builder.build.clone();
1961 move |mut cx| async move {
1962 cx.background_executor().timer(TOOLTIP_DELAY).await;
1963 cx.update(|cx| {
1964 active_tooltip.borrow_mut().replace(ActiveTooltip {
1965 tooltip: Some(AnyTooltip {
1966 view: build_tooltip(cx),
1967 mouse_position: cx.mouse_position(),
1968 }),
1969 _task: None,
1970 });
1971 cx.refresh();
1972 })
1973 .ok();
1974 }
1975 });
1976 active_tooltip.borrow_mut().replace(ActiveTooltip {
1977 tooltip: None,
1978 _task: Some(task),
1979 });
1980 }
1981 }
1982 });
1983
1984 cx.on_mouse_event({
1985 let active_tooltip = active_tooltip.clone();
1986 let tooltip_id = self.tooltip_id;
1987 move |_: &MouseDownEvent, _, cx| {
1988 let tooltip_is_hovered =
1989 tooltip_id.map_or(false, |tooltip_id| tooltip_id.is_hovered(cx));
1990
1991 if (!tooltip_is_hoverable || !tooltip_is_hovered)
1992 && active_tooltip.borrow_mut().take().is_some()
1993 {
1994 cx.refresh();
1995 }
1996 }
1997 });
1998
1999 cx.on_mouse_event({
2000 let active_tooltip = active_tooltip.clone();
2001 let tooltip_id = self.tooltip_id;
2002 move |_: &ScrollWheelEvent, _, cx| {
2003 let tooltip_is_hovered =
2004 tooltip_id.map_or(false, |tooltip_id| tooltip_id.is_hovered(cx));
2005 if (!tooltip_is_hoverable || !tooltip_is_hovered)
2006 && active_tooltip.borrow_mut().take().is_some()
2007 {
2008 cx.refresh();
2009 }
2010 }
2011 })
2012 }
2013
2014 let active_state = element_state
2015 .clicked_state
2016 .get_or_insert_with(Default::default)
2017 .clone();
2018 if active_state.borrow().is_clicked() {
2019 cx.on_mouse_event(move |_: &MouseUpEvent, phase, cx| {
2020 if phase == DispatchPhase::Capture {
2021 *active_state.borrow_mut() = ElementClickedState::default();
2022 cx.refresh();
2023 }
2024 });
2025 } else {
2026 let active_group_hitbox = self
2027 .group_active_style
2028 .as_ref()
2029 .and_then(|group_active| GroupHitboxes::get(&group_active.group, cx));
2030 let hitbox = hitbox.clone();
2031 cx.on_mouse_event(move |_: &MouseDownEvent, phase, cx| {
2032 if phase == DispatchPhase::Bubble && !cx.default_prevented() {
2033 let group_hovered = active_group_hitbox
2034 .map_or(false, |group_hitbox_id| group_hitbox_id.is_hovered(cx));
2035 let element_hovered = hitbox.is_hovered(cx);
2036 if group_hovered || element_hovered {
2037 *active_state.borrow_mut() = ElementClickedState {
2038 group: group_hovered,
2039 element: element_hovered,
2040 };
2041 cx.refresh();
2042 }
2043 }
2044 });
2045 }
2046 }
2047 }
2048
2049 fn paint_keyboard_listeners(&mut self, cx: &mut WindowContext) {
2050 let key_down_listeners = mem::take(&mut self.key_down_listeners);
2051 let key_up_listeners = mem::take(&mut self.key_up_listeners);
2052 let modifiers_changed_listeners = mem::take(&mut self.modifiers_changed_listeners);
2053 let action_listeners = mem::take(&mut self.action_listeners);
2054 if let Some(context) = self.key_context.clone() {
2055 cx.set_key_context(context);
2056 }
2057
2058 for listener in key_down_listeners {
2059 cx.on_key_event(move |event: &KeyDownEvent, phase, cx| {
2060 listener(event, phase, cx);
2061 })
2062 }
2063
2064 for listener in key_up_listeners {
2065 cx.on_key_event(move |event: &KeyUpEvent, phase, cx| {
2066 listener(event, phase, cx);
2067 })
2068 }
2069
2070 for listener in modifiers_changed_listeners {
2071 cx.on_modifiers_changed(move |event: &ModifiersChangedEvent, cx| {
2072 listener(event, cx);
2073 })
2074 }
2075
2076 for (action_type, listener) in action_listeners {
2077 cx.on_action(action_type, listener)
2078 }
2079 }
2080
2081 fn paint_hover_group_handler(&self, cx: &mut WindowContext) {
2082 let group_hitbox = self
2083 .group_hover_style
2084 .as_ref()
2085 .and_then(|group_hover| GroupHitboxes::get(&group_hover.group, cx));
2086
2087 if let Some(group_hitbox) = group_hitbox {
2088 let was_hovered = group_hitbox.is_hovered(cx);
2089 cx.on_mouse_event(move |_: &MouseMoveEvent, phase, cx| {
2090 let hovered = group_hitbox.is_hovered(cx);
2091 if phase == DispatchPhase::Capture && hovered != was_hovered {
2092 cx.refresh();
2093 }
2094 });
2095 }
2096 }
2097
2098 fn paint_scroll_listener(&self, hitbox: &Hitbox, style: &Style, cx: &mut WindowContext) {
2099 if let Some(scroll_offset) = self.scroll_offset.clone() {
2100 let overflow = style.overflow;
2101 let allow_concurrent_scroll = style.allow_concurrent_scroll;
2102 let line_height = cx.line_height();
2103 let hitbox = hitbox.clone();
2104 cx.on_mouse_event(move |event: &ScrollWheelEvent, phase, cx| {
2105 if phase == DispatchPhase::Bubble && hitbox.is_hovered(cx) {
2106 let mut scroll_offset = scroll_offset.borrow_mut();
2107 let old_scroll_offset = *scroll_offset;
2108 let delta = event.delta.pixel_delta(line_height);
2109
2110 let mut delta_x = Pixels::ZERO;
2111 if overflow.x == Overflow::Scroll {
2112 if !delta.x.is_zero() {
2113 delta_x = delta.x;
2114 } else if overflow.y != Overflow::Scroll {
2115 delta_x = delta.y;
2116 }
2117 }
2118 let mut delta_y = Pixels::ZERO;
2119 if overflow.y == Overflow::Scroll {
2120 if !delta.y.is_zero() {
2121 delta_y = delta.y;
2122 } else if overflow.x != Overflow::Scroll {
2123 delta_y = delta.x;
2124 }
2125 }
2126 if !allow_concurrent_scroll && !delta_x.is_zero() && !delta_y.is_zero() {
2127 if delta_x.abs() > delta_y.abs() {
2128 delta_y = Pixels::ZERO;
2129 } else {
2130 delta_x = Pixels::ZERO;
2131 }
2132 }
2133 scroll_offset.y += delta_y;
2134 scroll_offset.x += delta_x;
2135 cx.stop_propagation();
2136 if *scroll_offset != old_scroll_offset {
2137 cx.refresh();
2138 }
2139 }
2140 });
2141 }
2142 }
2143
2144 /// Compute the visual style for this element, based on the current bounds and the element's state.
2145 pub fn compute_style(
2146 &self,
2147 global_id: Option<&GlobalElementId>,
2148 hitbox: Option<&Hitbox>,
2149 cx: &mut WindowContext,
2150 ) -> Style {
2151 cx.with_optional_element_state(global_id, |element_state, cx| {
2152 let mut element_state =
2153 element_state.map(|element_state| element_state.unwrap_or_default());
2154 let style = self.compute_style_internal(hitbox, element_state.as_mut(), cx);
2155 (style, element_state)
2156 })
2157 }
2158
2159 /// Called from internal methods that have already called with_element_state.
2160 fn compute_style_internal(
2161 &self,
2162 hitbox: Option<&Hitbox>,
2163 element_state: Option<&mut InteractiveElementState>,
2164 cx: &mut WindowContext,
2165 ) -> Style {
2166 let mut style = Style::default();
2167 style.refine(&self.base_style);
2168
2169 if let Some(focus_handle) = self.tracked_focus_handle.as_ref() {
2170 if let Some(in_focus_style) = self.in_focus_style.as_ref() {
2171 if focus_handle.within_focused(cx) {
2172 style.refine(in_focus_style);
2173 }
2174 }
2175
2176 if let Some(focus_style) = self.focus_style.as_ref() {
2177 if focus_handle.is_focused(cx) {
2178 style.refine(focus_style);
2179 }
2180 }
2181 }
2182
2183 if let Some(hitbox) = hitbox {
2184 if !cx.has_active_drag() {
2185 if let Some(group_hover) = self.group_hover_style.as_ref() {
2186 if let Some(group_hitbox_id) =
2187 GroupHitboxes::get(&group_hover.group, cx.deref_mut())
2188 {
2189 if group_hitbox_id.is_hovered(cx) {
2190 style.refine(&group_hover.style);
2191 }
2192 }
2193 }
2194
2195 if let Some(hover_style) = self.hover_style.as_ref() {
2196 if hitbox.is_hovered(cx) {
2197 style.refine(hover_style);
2198 }
2199 }
2200 }
2201
2202 if let Some(drag) = cx.active_drag.take() {
2203 let mut can_drop = true;
2204 if let Some(can_drop_predicate) = &self.can_drop_predicate {
2205 can_drop = can_drop_predicate(drag.value.as_ref(), cx);
2206 }
2207
2208 if can_drop {
2209 for (state_type, group_drag_style) in &self.group_drag_over_styles {
2210 if let Some(group_hitbox_id) =
2211 GroupHitboxes::get(&group_drag_style.group, cx.deref_mut())
2212 {
2213 if *state_type == drag.value.as_ref().type_id()
2214 && group_hitbox_id.is_hovered(cx)
2215 {
2216 style.refine(&group_drag_style.style);
2217 }
2218 }
2219 }
2220
2221 for (state_type, build_drag_over_style) in &self.drag_over_styles {
2222 if *state_type == drag.value.as_ref().type_id() && hitbox.is_hovered(cx) {
2223 style.refine(&build_drag_over_style(drag.value.as_ref(), cx));
2224 }
2225 }
2226 }
2227
2228 cx.active_drag = Some(drag);
2229 }
2230 }
2231
2232 if let Some(element_state) = element_state {
2233 let clicked_state = element_state
2234 .clicked_state
2235 .get_or_insert_with(Default::default)
2236 .borrow();
2237 if clicked_state.group {
2238 if let Some(group) = self.group_active_style.as_ref() {
2239 style.refine(&group.style)
2240 }
2241 }
2242
2243 if let Some(active_style) = self.active_style.as_ref() {
2244 if clicked_state.element {
2245 style.refine(active_style)
2246 }
2247 }
2248 }
2249
2250 style
2251 }
2252}
2253
2254/// The per-frame state of an interactive element. Used for tracking stateful interactions like clicks
2255/// and scroll offsets.
2256#[derive(Default)]
2257pub struct InteractiveElementState {
2258 pub(crate) focus_handle: Option<FocusHandle>,
2259 pub(crate) clicked_state: Option<Rc<RefCell<ElementClickedState>>>,
2260 pub(crate) hover_state: Option<Rc<RefCell<bool>>>,
2261 pub(crate) pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
2262 pub(crate) scroll_offset: Option<Rc<RefCell<Point<Pixels>>>>,
2263 pub(crate) active_tooltip: Option<Rc<RefCell<Option<ActiveTooltip>>>>,
2264}
2265
2266/// The current active tooltip
2267pub struct ActiveTooltip {
2268 pub(crate) tooltip: Option<AnyTooltip>,
2269 pub(crate) _task: Option<Task<()>>,
2270}
2271
2272/// Whether or not the element or a group that contains it is clicked by the mouse.
2273#[derive(Copy, Clone, Default, Eq, PartialEq)]
2274pub struct ElementClickedState {
2275 /// True if this element's group has been clicked, false otherwise
2276 pub group: bool,
2277
2278 /// True if this element has been clicked, false otherwise
2279 pub element: bool,
2280}
2281
2282impl ElementClickedState {
2283 fn is_clicked(&self) -> bool {
2284 self.group || self.element
2285 }
2286}
2287
2288#[derive(Default)]
2289pub(crate) struct GroupHitboxes(HashMap<SharedString, SmallVec<[HitboxId; 1]>>);
2290
2291impl Global for GroupHitboxes {}
2292
2293impl GroupHitboxes {
2294 pub fn get(name: &SharedString, cx: &mut AppContext) -> Option<HitboxId> {
2295 cx.default_global::<Self>()
2296 .0
2297 .get(name)
2298 .and_then(|bounds_stack| bounds_stack.last())
2299 .cloned()
2300 }
2301
2302 pub fn push(name: SharedString, hitbox_id: HitboxId, cx: &mut AppContext) {
2303 cx.default_global::<Self>()
2304 .0
2305 .entry(name)
2306 .or_default()
2307 .push(hitbox_id);
2308 }
2309
2310 pub fn pop(name: &SharedString, cx: &mut AppContext) {
2311 cx.default_global::<Self>().0.get_mut(name).unwrap().pop();
2312 }
2313}
2314
2315/// A wrapper around an element that can be focused.
2316pub struct Focusable<E> {
2317 /// The element that is focusable
2318 pub element: E,
2319}
2320
2321impl<E: InteractiveElement> FocusableElement for Focusable<E> {}
2322
2323impl<E> InteractiveElement for Focusable<E>
2324where
2325 E: InteractiveElement,
2326{
2327 fn interactivity(&mut self) -> &mut Interactivity {
2328 self.element.interactivity()
2329 }
2330}
2331
2332impl<E: StatefulInteractiveElement> StatefulInteractiveElement for Focusable<E> {}
2333
2334impl<E> Styled for Focusable<E>
2335where
2336 E: Styled,
2337{
2338 fn style(&mut self) -> &mut StyleRefinement {
2339 self.element.style()
2340 }
2341}
2342
2343impl<E> Element for Focusable<E>
2344where
2345 E: Element,
2346{
2347 type RequestLayoutState = E::RequestLayoutState;
2348 type PrepaintState = E::PrepaintState;
2349
2350 fn id(&self) -> Option<ElementId> {
2351 self.element.id()
2352 }
2353
2354 fn request_layout(
2355 &mut self,
2356 id: Option<&GlobalElementId>,
2357 cx: &mut WindowContext,
2358 ) -> (LayoutId, Self::RequestLayoutState) {
2359 self.element.request_layout(id, cx)
2360 }
2361
2362 fn prepaint(
2363 &mut self,
2364 id: Option<&GlobalElementId>,
2365 bounds: Bounds<Pixels>,
2366 state: &mut Self::RequestLayoutState,
2367 cx: &mut WindowContext,
2368 ) -> E::PrepaintState {
2369 self.element.prepaint(id, bounds, state, cx)
2370 }
2371
2372 fn paint(
2373 &mut self,
2374 id: Option<&GlobalElementId>,
2375 bounds: Bounds<Pixels>,
2376 request_layout: &mut Self::RequestLayoutState,
2377 prepaint: &mut Self::PrepaintState,
2378 cx: &mut WindowContext,
2379 ) {
2380 self.element.paint(id, bounds, request_layout, prepaint, cx)
2381 }
2382}
2383
2384impl<E> IntoElement for Focusable<E>
2385where
2386 E: IntoElement,
2387{
2388 type Element = E::Element;
2389
2390 fn into_element(self) -> Self::Element {
2391 self.element.into_element()
2392 }
2393}
2394
2395impl<E> ParentElement for Focusable<E>
2396where
2397 E: ParentElement,
2398{
2399 fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
2400 self.element.extend(elements)
2401 }
2402}
2403
2404/// A wrapper around an element that can store state, produced after assigning an ElementId.
2405pub struct Stateful<E> {
2406 pub(crate) element: E,
2407}
2408
2409impl<E> Styled for Stateful<E>
2410where
2411 E: Styled,
2412{
2413 fn style(&mut self) -> &mut StyleRefinement {
2414 self.element.style()
2415 }
2416}
2417
2418impl<E> StatefulInteractiveElement for Stateful<E>
2419where
2420 E: Element,
2421 Self: InteractiveElement,
2422{
2423}
2424
2425impl<E> InteractiveElement for Stateful<E>
2426where
2427 E: InteractiveElement,
2428{
2429 fn interactivity(&mut self) -> &mut Interactivity {
2430 self.element.interactivity()
2431 }
2432}
2433
2434impl<E: FocusableElement> FocusableElement for Stateful<E> {}
2435
2436impl<E> Element for Stateful<E>
2437where
2438 E: Element,
2439{
2440 type RequestLayoutState = E::RequestLayoutState;
2441 type PrepaintState = E::PrepaintState;
2442
2443 fn id(&self) -> Option<ElementId> {
2444 self.element.id()
2445 }
2446
2447 fn request_layout(
2448 &mut self,
2449 id: Option<&GlobalElementId>,
2450 cx: &mut WindowContext,
2451 ) -> (LayoutId, Self::RequestLayoutState) {
2452 self.element.request_layout(id, cx)
2453 }
2454
2455 fn prepaint(
2456 &mut self,
2457 id: Option<&GlobalElementId>,
2458 bounds: Bounds<Pixels>,
2459 state: &mut Self::RequestLayoutState,
2460 cx: &mut WindowContext,
2461 ) -> E::PrepaintState {
2462 self.element.prepaint(id, bounds, state, cx)
2463 }
2464
2465 fn paint(
2466 &mut self,
2467 id: Option<&GlobalElementId>,
2468 bounds: Bounds<Pixels>,
2469 request_layout: &mut Self::RequestLayoutState,
2470 prepaint: &mut Self::PrepaintState,
2471 cx: &mut WindowContext,
2472 ) {
2473 self.element.paint(id, bounds, request_layout, prepaint, cx);
2474 }
2475}
2476
2477impl<E> IntoElement for Stateful<E>
2478where
2479 E: Element,
2480{
2481 type Element = Self;
2482
2483 fn into_element(self) -> Self::Element {
2484 self
2485 }
2486}
2487
2488impl<E> ParentElement for Stateful<E>
2489where
2490 E: ParentElement,
2491{
2492 fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
2493 self.element.extend(elements)
2494 }
2495}
2496
2497/// Represents an element that can be scrolled *to* in its parent element.
2498///
2499/// Contrary to [ScrollHandle::scroll_to_item], an anchored element does not have to be an immediate child of the parent.
2500#[derive(Clone)]
2501pub struct ScrollAnchor {
2502 handle: ScrollHandle,
2503 last_origin: Rc<RefCell<Point<Pixels>>>,
2504}
2505
2506impl ScrollAnchor {
2507 /// Creates a [ScrollAnchor] associated with a given [ScrollHandle].
2508 pub fn for_handle(handle: ScrollHandle) -> Self {
2509 Self {
2510 handle,
2511 last_origin: Default::default(),
2512 }
2513 }
2514 /// Request scroll to this item on the next frame.
2515 pub fn scroll_to(&self, cx: &mut WindowContext) {
2516 let this = self.clone();
2517
2518 cx.on_next_frame(move |_| {
2519 let viewport_bounds = this.handle.bounds();
2520 let self_bounds = *this.last_origin.borrow();
2521 this.handle.set_offset(viewport_bounds.origin - self_bounds);
2522 });
2523 }
2524}
2525#[derive(Default, Debug)]
2526struct ScrollHandleState {
2527 offset: Rc<RefCell<Point<Pixels>>>,
2528 bounds: Bounds<Pixels>,
2529 child_bounds: Vec<Bounds<Pixels>>,
2530 requested_scroll_top: Option<(usize, Pixels)>,
2531 overflow: Point<Overflow>,
2532}
2533
2534/// A handle to the scrollable aspects of an element.
2535/// Used for accessing scroll state, like the current scroll offset,
2536/// and for mutating the scroll state, like scrolling to a specific child.
2537#[derive(Clone, Debug)]
2538pub struct ScrollHandle(Rc<RefCell<ScrollHandleState>>);
2539
2540impl Default for ScrollHandle {
2541 fn default() -> Self {
2542 Self::new()
2543 }
2544}
2545
2546impl ScrollHandle {
2547 /// Construct a new scroll handle.
2548 pub fn new() -> Self {
2549 Self(Rc::default())
2550 }
2551
2552 /// Get the current scroll offset.
2553 pub fn offset(&self) -> Point<Pixels> {
2554 *self.0.borrow().offset.borrow()
2555 }
2556
2557 /// Get the top child that's scrolled into view.
2558 pub fn top_item(&self) -> usize {
2559 let state = self.0.borrow();
2560 let top = state.bounds.top() - state.offset.borrow().y;
2561
2562 match state.child_bounds.binary_search_by(|bounds| {
2563 if top < bounds.top() {
2564 Ordering::Greater
2565 } else if top > bounds.bottom() {
2566 Ordering::Less
2567 } else {
2568 Ordering::Equal
2569 }
2570 }) {
2571 Ok(ix) => ix,
2572 Err(ix) => ix.min(state.child_bounds.len().saturating_sub(1)),
2573 }
2574 }
2575
2576 /// Return the bounds into which this child is painted
2577 pub fn bounds(&self) -> Bounds<Pixels> {
2578 self.0.borrow().bounds
2579 }
2580
2581 /// Set the bounds into which this child is painted
2582 pub(super) fn set_bounds(&self, bounds: Bounds<Pixels>) {
2583 self.0.borrow_mut().bounds = bounds;
2584 }
2585
2586 /// Get the bounds for a specific child.
2587 pub fn bounds_for_item(&self, ix: usize) -> Option<Bounds<Pixels>> {
2588 self.0.borrow().child_bounds.get(ix).cloned()
2589 }
2590
2591 /// scroll_to_item scrolls the minimal amount to ensure that the child is
2592 /// fully visible
2593 pub fn scroll_to_item(&self, ix: usize) {
2594 let state = self.0.borrow();
2595
2596 let Some(bounds) = state.child_bounds.get(ix) else {
2597 return;
2598 };
2599
2600 let mut scroll_offset = state.offset.borrow_mut();
2601
2602 if state.overflow.y == Overflow::Scroll {
2603 if bounds.top() + scroll_offset.y < state.bounds.top() {
2604 scroll_offset.y = state.bounds.top() - bounds.top();
2605 } else if bounds.bottom() + scroll_offset.y > state.bounds.bottom() {
2606 scroll_offset.y = state.bounds.bottom() - bounds.bottom();
2607 }
2608 }
2609
2610 if state.overflow.x == Overflow::Scroll {
2611 if bounds.left() + scroll_offset.x < state.bounds.left() {
2612 scroll_offset.x = state.bounds.left() - bounds.left();
2613 } else if bounds.right() + scroll_offset.x > state.bounds.right() {
2614 scroll_offset.x = state.bounds.right() - bounds.right();
2615 }
2616 }
2617 }
2618
2619 /// Set the offset explicitly. The offset is the distance from the top left of the
2620 /// parent container to the top left of the first child.
2621 /// As you scroll further down the offset becomes more negative.
2622 pub fn set_offset(&self, mut position: Point<Pixels>) {
2623 let state = self.0.borrow();
2624 *state.offset.borrow_mut() = position;
2625 }
2626
2627 /// Get the logical scroll top, based on a child index and a pixel offset.
2628 pub fn logical_scroll_top(&self) -> (usize, Pixels) {
2629 let ix = self.top_item();
2630 let state = self.0.borrow();
2631
2632 if let Some(child_bounds) = state.child_bounds.get(ix) {
2633 (
2634 ix,
2635 child_bounds.top() + state.offset.borrow().y - state.bounds.top(),
2636 )
2637 } else {
2638 (ix, px(0.))
2639 }
2640 }
2641
2642 /// Set the logical scroll top, based on a child index and a pixel offset.
2643 pub fn set_logical_scroll_top(&self, ix: usize, px: Pixels) {
2644 self.0.borrow_mut().requested_scroll_top = Some((ix, px));
2645 }
2646
2647 /// Get the count of children for scrollable item.
2648 pub fn children_count(&self) -> usize {
2649 self.0.borrow().child_bounds.len()
2650 }
2651}