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