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