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