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