//! Div is the central, reusable element that most GPUI trees will be built from.//! It functions as a container for other elements, and provides a number of//! useful features for laying out and styling its children as well as binding//! mouse events and action handlers. It is meant to be similar to the HTML `<div>`//! element, but for GPUI.//!//! # Build your own div//!//! GPUI does not directly provide APIs for stateful, multi step events like `click`//! and `drag`. We want GPUI users to be able to build their own abstractions for//! their own needs. However, as a UI framework, we're also obliged to provide some//! building blocks to make the process of building your own elements easier.//! For this we have the [`Interactivity`] and the [`StyleRefinement`] structs, as well//! as several associated traits. Together, these provide the full suite of Dom-like events//! and Tailwind-like styling that you can use to build your own custom elements. Div is//! constructed by combining these two systems into an all-in-one element. use crate::{ AbsoluteLength, Action, AnyDrag, AnyElement, AnyTooltip, AnyView, App, Bounds, ClickEvent, DispatchPhase, Display, Element, ElementId, Entity, FocusHandle, Global, GlobalElementId, Hitbox, HitboxBehavior, HitboxId, InspectorElementId, IntoElement, IsZero, KeyContext, KeyDownEvent, KeyUpEvent, KeyboardButton, KeyboardClickEvent, LayoutId, ModifiersChangedEvent, MouseButton, MouseClickEvent, MouseDownEvent, MouseMoveEvent, MousePressureEvent, MouseUpEvent, Overflow, ParentElement, Pixels, Point, Render, ScrollWheelEvent, SharedString, Size, Style, StyleRefinement, Styled, Task, TooltipId, Visibility, Window, WindowControlArea, point, px, size,};use collections::HashMap;use refineable::Refineable;use smallvec::SmallVec;use stacksafe::{StackSafe, stacksafe};use std::{ any::{Any, TypeId}, cell::RefCell, cmp::Ordering, fmt::Debug, marker::PhantomData, mem, rc::Rc, sync::Arc, time::Duration,};use util::ResultExt; use super::ImageCacheProvider; const DRAG_THRESHOLD: f64 = 2.;const TOOLTIP_SHOW_DELAY: Duration = Duration::from_millis(500);const HOVERABLE_TOOLTIP_HIDE_DELAY: Duration = Duration::from_millis(500); /// The styling information for a given group.pub struct GroupStyle { /// The identifier for this group. pub group: SharedString, /// The specific style refinement that this group would apply /// to its children. pub style: Box<StyleRefinement>,} /// An event for when a drag is moving over this element, with the given state type.pub struct DragMoveEvent<T> { /// The mouse move event that triggered this drag move event. pub event: MouseMoveEvent, /// The bounds of this element. pub bounds: Bounds<Pixels>, drag: PhantomData<T>, dragged_item: Arc<dyn Any>,} impl<T: 'static> DragMoveEvent<T> { /// Returns the drag state for this event. pub fn drag<'b>(&self, cx: &'b App) -> &'b T { cx.active_drag .as_ref() .and_then(|drag| drag.value.downcast_ref::<T>()) .expect("DragMoveEvent is only valid when the stored active drag is of the same type.") } /// An item that is about to be dropped. pub fn dragged_item(&self) -> &dyn Any { self.dragged_item.as_ref() }} impl Interactivity { /// Create an `Interactivity`, capturing the caller location in debug mode. #[cfg(any(feature = "inspector", debug_assertions))] #[track_caller] pub fn new() -> Interactivity { Interactivity { source_location: Some(core::panic::Location::caller()), ..Default::default() } } /// Create an `Interactivity`, capturing the caller location in debug mode. #[cfg(not(any(feature = "inspector", debug_assertions)))] pub fn new() -> Interactivity { Interactivity::default() } /// Gets the source location of construction. Returns `None` when not in debug mode. pub fn source_location(&self) -> Option<&'static std::panic::Location<'static>> { #[cfg(any(feature = "inspector", debug_assertions))] { self.source_location } #[cfg(not(any(feature = "inspector", debug_assertions)))] { None } } /// Bind the given callback to the mouse down event for the given mouse button, during the bubble phase. /// The imperative API equivalent of [`InteractiveElement::on_mouse_down`]. /// /// See [`Context::listener`](crate::Context::listener) to get access to the view state from this callback. pub fn on_mouse_down( &mut self, button: MouseButton, listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static, ) { self.mouse_down_listeners .push(Box::new(move |event, phase, hitbox, window, cx| { if phase == DispatchPhase::Bubble && event.button == button && hitbox.is_hovered(window) { (listener)(event, window, cx) } })); } /// Bind the given callback to the mouse down event for any button, during the capture phase. /// The imperative API equivalent of [`InteractiveElement::capture_any_mouse_down`]. /// /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. pub fn capture_any_mouse_down( &mut self, listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static, ) { self.mouse_down_listeners .push(Box::new(move |event, phase, hitbox, window, cx| { if phase == DispatchPhase::Capture && hitbox.is_hovered(window) { (listener)(event, window, cx) } })); } /// Bind the given callback to the mouse down event for any button, during the bubble phase. /// The imperative API equivalent to [`InteractiveElement::on_any_mouse_down`]. /// /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. pub fn on_any_mouse_down( &mut self, listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static, ) { self.mouse_down_listeners .push(Box::new(move |event, phase, hitbox, window, cx| { if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) { (listener)(event, window, cx) } })); } /// Bind the given callback to the mouse pressure event, during the bubble phase /// the imperative API equivalent to [`InteractiveElement::on_mouse_pressure`]. /// /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. pub fn on_mouse_pressure( &mut self, listener: impl Fn(&MousePressureEvent, &mut Window, &mut App) + 'static, ) { self.mouse_pressure_listeners .push(Box::new(move |event, phase, hitbox, window, cx| { if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) { (listener)(event, window, cx) } })); } /// Bind the given callback to the mouse pressure event, during the capture phase /// the imperative API equivalent to [`InteractiveElement::on_mouse_pressure`]. /// /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. pub fn capture_mouse_pressure( &mut self, listener: impl Fn(&MousePressureEvent, &mut Window, &mut App) + 'static, ) { self.mouse_pressure_listeners .push(Box::new(move |event, phase, hitbox, window, cx| { if phase == DispatchPhase::Capture && hitbox.is_hovered(window) { (listener)(event, window, cx) } })); } /// Bind the given callback to the mouse up event for the given button, during the bubble phase. /// The imperative API equivalent to [`InteractiveElement::on_mouse_up`]. /// /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. pub fn on_mouse_up( &mut self, button: MouseButton, listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static, ) { self.mouse_up_listeners .push(Box::new(move |event, phase, hitbox, window, cx| { if phase == DispatchPhase::Bubble && event.button == button && hitbox.is_hovered(window) { (listener)(event, window, cx) } })); } /// Bind the given callback to the mouse up event for any button, during the capture phase. /// The imperative API equivalent to [`InteractiveElement::capture_any_mouse_up`]. /// /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. pub fn capture_any_mouse_up( &mut self, listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static, ) { self.mouse_up_listeners .push(Box::new(move |event, phase, hitbox, window, cx| { if phase == DispatchPhase::Capture && hitbox.is_hovered(window) { (listener)(event, window, cx) } })); } /// Bind the given callback to the mouse up event for any button, during the bubble phase. /// The imperative API equivalent to [`Interactivity::on_any_mouse_up`]. /// /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. pub fn on_any_mouse_up( &mut self, listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static, ) { self.mouse_up_listeners .push(Box::new(move |event, phase, hitbox, window, cx| { if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) { (listener)(event, window, cx) } })); } /// Bind the given callback to the mouse down event, on any button, during the capture phase, /// when the mouse is outside of the bounds of this element. /// The imperative API equivalent to [`InteractiveElement::on_mouse_down_out`]. /// /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. pub fn on_mouse_down_out( &mut self, listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static, ) { self.mouse_down_listeners .push(Box::new(move |event, phase, hitbox, window, cx| { if phase == DispatchPhase::Capture && !hitbox.contains(&window.mouse_position()) { (listener)(event, window, cx) } })); } /// Bind the given callback to the mouse up event, for the given button, during the capture phase, /// when the mouse is outside of the bounds of this element. /// The imperative API equivalent to [`InteractiveElement::on_mouse_up_out`]. /// /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. pub fn on_mouse_up_out( &mut self, button: MouseButton, listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static, ) { self.mouse_up_listeners .push(Box::new(move |event, phase, hitbox, window, cx| { if phase == DispatchPhase::Capture && event.button == button && !hitbox.is_hovered(window) { (listener)(event, window, cx); } })); } /// Bind the given callback to the mouse move event, during the bubble phase. /// The imperative API equivalent to [`InteractiveElement::on_mouse_move`]. /// /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. pub fn on_mouse_move( &mut self, listener: impl Fn(&MouseMoveEvent, &mut Window, &mut App) + 'static, ) { self.mouse_move_listeners .push(Box::new(move |event, phase, hitbox, window, cx| { if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) { (listener)(event, window, cx); } })); } /// Bind the given callback to the mouse drag event of the given type. Note that this /// will be called for all move events, inside or outside of this element, as long as the /// drag was started with this element under the mouse. Useful for implementing draggable /// UIs that don't conform to a drag and drop style interaction, like resizing. /// The imperative API equivalent to [`InteractiveElement::on_drag_move`]. /// /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. pub fn on_drag_move<T>( &mut self, listener: impl Fn(&DragMoveEvent<T>, &mut Window, &mut App) + 'static, ) where T: 'static, { self.mouse_move_listeners .push(Box::new(move |event, phase, hitbox, window, cx| { if phase == DispatchPhase::Capture && let Some(drag) = &cx.active_drag && drag.value.as_ref().type_id() == TypeId::of::<T>() { (listener)( &DragMoveEvent { event: event.clone(), bounds: hitbox.bounds, drag: PhantomData, dragged_item: Arc::clone(&drag.value), }, window, cx, ); } })); } /// Bind the given callback to scroll wheel events during the bubble phase. /// The imperative API equivalent to [`InteractiveElement::on_scroll_wheel`]. /// /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. pub fn on_scroll_wheel( &mut self, listener: impl Fn(&ScrollWheelEvent, &mut Window, &mut App) + 'static, ) { self.scroll_wheel_listeners .push(Box::new(move |event, phase, hitbox, window, cx| { if phase == DispatchPhase::Bubble && hitbox.should_handle_scroll(window) { (listener)(event, window, cx); } })); } /// Bind the given callback to an action dispatch during the capture phase. /// The imperative API equivalent to [`InteractiveElement::capture_action`]. /// /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. pub fn capture_action<A: Action>( &mut self, listener: impl Fn(&A, &mut Window, &mut App) + 'static, ) { self.action_listeners.push(( TypeId::of::<A>(), Box::new(move |action, phase, window, cx| { let action = action.downcast_ref().unwrap(); if phase == DispatchPhase::Capture { (listener)(action, window, cx) } else { cx.propagate(); } }), )); } /// Bind the given callback to an action dispatch during the bubble phase. /// The imperative API equivalent to [`InteractiveElement::on_action`]. /// /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. pub fn on_action<A: Action>(&mut self, listener: impl Fn(&A, &mut Window, &mut App) + 'static) { self.action_listeners.push(( TypeId::of::<A>(), Box::new(move |action, phase, window, cx| { let action = action.downcast_ref().unwrap(); if phase == DispatchPhase::Bubble { (listener)(action, window, cx) } }), )); } /// Bind the given callback to an action dispatch, based on a dynamic action parameter /// instead of a type parameter. Useful for component libraries that want to expose /// action bindings to their users. /// The imperative API equivalent to [`InteractiveElement::on_boxed_action`]. /// /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. pub fn on_boxed_action( &mut self, action: &dyn Action, listener: impl Fn(&dyn Action, &mut Window, &mut App) + 'static, ) { let action = action.boxed_clone(); self.action_listeners.push(( (*action).type_id(), Box::new(move |_, phase, window, cx| { if phase == DispatchPhase::Bubble { (listener)(&*action, window, cx) } }), )); } /// Bind the given callback to key down events during the bubble phase. /// The imperative API equivalent to [`InteractiveElement::on_key_down`]. /// /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. pub fn on_key_down( &mut self, listener: impl Fn(&KeyDownEvent, &mut Window, &mut App) + 'static, ) { self.key_down_listeners .push(Box::new(move |event, phase, window, cx| { if phase == DispatchPhase::Bubble { (listener)(event, window, cx) } })); } /// Bind the given callback to key down events during the capture phase. /// The imperative API equivalent to [`InteractiveElement::capture_key_down`]. /// /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. pub fn capture_key_down( &mut self, listener: impl Fn(&KeyDownEvent, &mut Window, &mut App) + 'static, ) { self.key_down_listeners .push(Box::new(move |event, phase, window, cx| { if phase == DispatchPhase::Capture { listener(event, window, cx) } })); } /// Bind the given callback to key up events during the bubble phase. /// The imperative API equivalent to [`InteractiveElement::on_key_up`]. /// /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. pub fn on_key_up(&mut self, listener: impl Fn(&KeyUpEvent, &mut Window, &mut App) + 'static) { self.key_up_listeners .push(Box::new(move |event, phase, window, cx| { if phase == DispatchPhase::Bubble { listener(event, window, cx) } })); } /// Bind the given callback to key up events during the capture phase. /// The imperative API equivalent to [`InteractiveElement::on_key_up`]. /// /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. pub fn capture_key_up( &mut self, listener: impl Fn(&KeyUpEvent, &mut Window, &mut App) + 'static, ) { self.key_up_listeners .push(Box::new(move |event, phase, window, cx| { if phase == DispatchPhase::Capture { listener(event, window, cx) } })); } /// Bind the given callback to modifiers changing events. /// The imperative API equivalent to [`InteractiveElement::on_modifiers_changed`]. /// /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. pub fn on_modifiers_changed( &mut self, listener: impl Fn(&ModifiersChangedEvent, &mut Window, &mut App) + 'static, ) { self.modifiers_changed_listeners .push(Box::new(move |event, window, cx| { listener(event, window, cx) })); } /// Bind the given callback to drop events of the given type, whether or not the drag started on this element. /// The imperative API equivalent to [`InteractiveElement::on_drop`]. /// /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. pub fn on_drop<T: 'static>(&mut self, listener: impl Fn(&T, &mut Window, &mut App) + 'static) { self.drop_listeners.push(( TypeId::of::<T>(), Box::new(move |dragged_value, window, cx| { listener(dragged_value.downcast_ref().unwrap(), window, cx); }), ));Lines 1–500 of 3545. Load more
//! Div is the central, reusable element that most GPUI trees will be built from. //! It functions as a container for other elements, and provides a number of //! useful features for laying out and styling its children as well as binding //! mouse events and action handlers. It is meant to be similar to the HTML `<div>` //! element, but for GPUI. //! //! # Build your own div //! //! GPUI does not directly provide APIs for stateful, multi step events like `click` //! and `drag`. We want GPUI users to be able to build their own abstractions for //! their own needs. However, as a UI framework, we're also obliged to provide some //! building blocks to make the process of building your own elements easier. //! For this we have the [`Interactivity`] and the [`StyleRefinement`] structs, as well //! as several associated traits. Together, these provide the full suite of Dom-like events //! and Tailwind-like styling that you can use to build your own custom elements. Div is //! constructed by combining these two systems into an all-in-one element. use crate::{ AbsoluteLength, Action, AnyDrag, AnyElement, AnyTooltip, AnyView, App, Bounds, ClickEvent, DispatchPhase, Display, Element, ElementId, Entity, FocusHandle, Global, GlobalElementId, Hitbox, HitboxBehavior, HitboxId, InspectorElementId, IntoElement, IsZero, KeyContext, KeyDownEvent, KeyUpEvent, KeyboardButton, KeyboardClickEvent, LayoutId, ModifiersChangedEvent, MouseButton, MouseClickEvent, MouseDownEvent, MouseMoveEvent, MousePressureEvent, MouseUpEvent, Overflow, ParentElement, Pixels, Point, Render, ScrollWheelEvent, SharedString, Size, Style, StyleRefinement, Styled, Task, TooltipId, Visibility, Window, WindowControlArea, point, px, size, }; use collections::HashMap; use refineable::Refineable; use smallvec::SmallVec; use stacksafe::{StackSafe, stacksafe}; use std::{ any::{Any, TypeId}, cell::RefCell, cmp::Ordering, fmt::Debug, marker::PhantomData, mem, rc::Rc, sync::Arc, time::Duration, }; use util::ResultExt; use super::ImageCacheProvider; const DRAG_THRESHOLD: f64 = 2.; const TOOLTIP_SHOW_DELAY: Duration = Duration::from_millis(500); const HOVERABLE_TOOLTIP_HIDE_DELAY: Duration = Duration::from_millis(500); /// The styling information for a given group. pub struct GroupStyle { /// The identifier for this group. pub group: SharedString, /// The specific style refinement that this group would apply /// to its children. pub style: Box<StyleRefinement>, } /// An event for when a drag is moving over this element, with the given state type. pub struct DragMoveEvent<T> { /// The mouse move event that triggered this drag move event. pub event: MouseMoveEvent, /// The bounds of this element. pub bounds: Bounds<Pixels>, drag: PhantomData<T>, dragged_item: Arc<dyn Any>, } impl<T: 'static> DragMoveEvent<T> { /// Returns the drag state for this event. pub fn drag<'b>(&self, cx: &'b App) -> &'b T { cx.active_drag .as_ref() .and_then(|drag| drag.value.downcast_ref::<T>()) .expect("DragMoveEvent is only valid when the stored active drag is of the same type.") } /// An item that is about to be dropped. pub fn dragged_item(&self) -> &dyn Any { self.dragged_item.as_ref() } } impl Interactivity { /// Create an `Interactivity`, capturing the caller location in debug mode. #[cfg(any(feature = "inspector", debug_assertions))] #[track_caller] pub fn new() -> Interactivity { Interactivity { source_location: Some(core::panic::Location::caller()), ..Default::default() } } /// Create an `Interactivity`, capturing the caller location in debug mode. #[cfg(not(any(feature = "inspector", debug_assertions)))] pub fn new() -> Interactivity { Interactivity::default() } /// Gets the source location of construction. Returns `None` when not in debug mode. pub fn source_location(&self) -> Option<&'static std::panic::Location<'static>> { #[cfg(any(feature = "inspector", debug_assertions))] { self.source_location } #[cfg(not(any(feature = "inspector", debug_assertions)))] { None } } /// Bind the given callback to the mouse down event for the given mouse button, during the bubble phase. /// The imperative API equivalent of [`InteractiveElement::on_mouse_down`]. /// /// See [`Context::listener`](crate::Context::listener) to get access to the view state from this callback. pub fn on_mouse_down( &mut self, button: MouseButton, listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static, ) { self.mouse_down_listeners .push(Box::new(move |event, phase, hitbox, window, cx| { if phase == DispatchPhase::Bubble && event.button == button && hitbox.is_hovered(window) { (listener)(event, window, cx) } })); } /// Bind the given callback to the mouse down event for any button, during the capture phase. /// The imperative API equivalent of [`InteractiveElement::capture_any_mouse_down`]. /// /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. pub fn capture_any_mouse_down( &mut self, listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static, ) { self.mouse_down_listeners .push(Box::new(move |event, phase, hitbox, window, cx| { if phase == DispatchPhase::Capture && hitbox.is_hovered(window) { (listener)(event, window, cx) } })); } /// Bind the given callback to the mouse down event for any button, during the bubble phase. /// The imperative API equivalent to [`InteractiveElement::on_any_mouse_down`]. /// /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. pub fn on_any_mouse_down( &mut self, listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static, ) { self.mouse_down_listeners .push(Box::new(move |event, phase, hitbox, window, cx| { if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) { (listener)(event, window, cx) } })); } /// Bind the given callback to the mouse pressure event, during the bubble phase /// the imperative API equivalent to [`InteractiveElement::on_mouse_pressure`]. /// /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. pub fn on_mouse_pressure( &mut self, listener: impl Fn(&MousePressureEvent, &mut Window, &mut App) + 'static, ) { self.mouse_pressure_listeners .push(Box::new(move |event, phase, hitbox, window, cx| { if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) { (listener)(event, window, cx) } })); } /// Bind the given callback to the mouse pressure event, during the capture phase /// the imperative API equivalent to [`InteractiveElement::on_mouse_pressure`]. /// /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. pub fn capture_mouse_pressure( &mut self, listener: impl Fn(&MousePressureEvent, &mut Window, &mut App) + 'static, ) { self.mouse_pressure_listeners .push(Box::new(move |event, phase, hitbox, window, cx| { if phase == DispatchPhase::Capture && hitbox.is_hovered(window) { (listener)(event, window, cx) } })); } /// Bind the given callback to the mouse up event for the given button, during the bubble phase. /// The imperative API equivalent to [`InteractiveElement::on_mouse_up`]. /// /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. pub fn on_mouse_up( &mut self, button: MouseButton, listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static, ) { self.mouse_up_listeners .push(Box::new(move |event, phase, hitbox, window, cx| { if phase == DispatchPhase::Bubble && event.button == button && hitbox.is_hovered(window) { (listener)(event, window, cx) } })); } /// Bind the given callback to the mouse up event for any button, during the capture phase. /// The imperative API equivalent to [`InteractiveElement::capture_any_mouse_up`]. /// /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. pub fn capture_any_mouse_up( &mut self, listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static, ) { self.mouse_up_listeners .push(Box::new(move |event, phase, hitbox, window, cx| { if phase == DispatchPhase::Capture && hitbox.is_hovered(window) { (listener)(event, window, cx) } })); } /// Bind the given callback to the mouse up event for any button, during the bubble phase. /// The imperative API equivalent to [`Interactivity::on_any_mouse_up`]. /// /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. pub fn on_any_mouse_up( &mut self, listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static, ) { self.mouse_up_listeners .push(Box::new(move |event, phase, hitbox, window, cx| { if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) { (listener)(event, window, cx) } })); } /// Bind the given callback to the mouse down event, on any button, during the capture phase, /// when the mouse is outside of the bounds of this element. /// The imperative API equivalent to [`InteractiveElement::on_mouse_down_out`]. /// /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. pub fn on_mouse_down_out( &mut self, listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static, ) { self.mouse_down_listeners .push(Box::new(move |event, phase, hitbox, window, cx| { if phase == DispatchPhase::Capture && !hitbox.contains(&window.mouse_position()) { (listener)(event, window, cx) } })); } /// Bind the given callback to the mouse up event, for the given button, during the capture phase, /// when the mouse is outside of the bounds of this element. /// The imperative API equivalent to [`InteractiveElement::on_mouse_up_out`]. /// /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. pub fn on_mouse_up_out( &mut self, button: MouseButton, listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static, ) { self.mouse_up_listeners .push(Box::new(move |event, phase, hitbox, window, cx| { if phase == DispatchPhase::Capture && event.button == button && !hitbox.is_hovered(window) { (listener)(event, window, cx); } })); } /// Bind the given callback to the mouse move event, during the bubble phase. /// The imperative API equivalent to [`InteractiveElement::on_mouse_move`]. /// /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. pub fn on_mouse_move( &mut self, listener: impl Fn(&MouseMoveEvent, &mut Window, &mut App) + 'static, ) { self.mouse_move_listeners .push(Box::new(move |event, phase, hitbox, window, cx| { if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) { (listener)(event, window, cx); } })); } /// Bind the given callback to the mouse drag event of the given type. Note that this /// will be called for all move events, inside or outside of this element, as long as the /// drag was started with this element under the mouse. Useful for implementing draggable /// UIs that don't conform to a drag and drop style interaction, like resizing. /// The imperative API equivalent to [`InteractiveElement::on_drag_move`]. /// /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. pub fn on_drag_move<T>( &mut self, listener: impl Fn(&DragMoveEvent<T>, &mut Window, &mut App) + 'static, ) where T: 'static, { self.mouse_move_listeners .push(Box::new(move |event, phase, hitbox, window, cx| { if phase == DispatchPhase::Capture && let Some(drag) = &cx.active_drag && drag.value.as_ref().type_id() == TypeId::of::<T>() { (listener)( &DragMoveEvent { event: event.clone(), bounds: hitbox.bounds, drag: PhantomData, dragged_item: Arc::clone(&drag.value), }, window, cx, ); } })); } /// Bind the given callback to scroll wheel events during the bubble phase. /// The imperative API equivalent to [`InteractiveElement::on_scroll_wheel`]. /// /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. pub fn on_scroll_wheel( &mut self, listener: impl Fn(&ScrollWheelEvent, &mut Window, &mut App) + 'static, ) { self.scroll_wheel_listeners .push(Box::new(move |event, phase, hitbox, window, cx| { if phase == DispatchPhase::Bubble && hitbox.should_handle_scroll(window) { (listener)(event, window, cx); } })); } /// Bind the given callback to an action dispatch during the capture phase. /// The imperative API equivalent to [`InteractiveElement::capture_action`]. /// /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. pub fn capture_action<A: Action>( &mut self, listener: impl Fn(&A, &mut Window, &mut App) + 'static, ) { self.action_listeners.push(( TypeId::of::<A>(), Box::new(move |action, phase, window, cx| { let action = action.downcast_ref().unwrap(); if phase == DispatchPhase::Capture { (listener)(action, window, cx) } else { cx.propagate(); } }), )); } /// Bind the given callback to an action dispatch during the bubble phase. /// The imperative API equivalent to [`InteractiveElement::on_action`]. /// /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. pub fn on_action<A: Action>(&mut self, listener: impl Fn(&A, &mut Window, &mut App) + 'static) { self.action_listeners.push(( TypeId::of::<A>(), Box::new(move |action, phase, window, cx| { let action = action.downcast_ref().unwrap(); if phase == DispatchPhase::Bubble { (listener)(action, window, cx) } }), )); } /// Bind the given callback to an action dispatch, based on a dynamic action parameter /// instead of a type parameter. Useful for component libraries that want to expose /// action bindings to their users. /// The imperative API equivalent to [`InteractiveElement::on_boxed_action`]. /// /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. pub fn on_boxed_action( &mut self, action: &dyn Action, listener: impl Fn(&dyn Action, &mut Window, &mut App) + 'static, ) { let action = action.boxed_clone(); self.action_listeners.push(( (*action).type_id(), Box::new(move |_, phase, window, cx| { if phase == DispatchPhase::Bubble { (listener)(&*action, window, cx) } }), )); } /// Bind the given callback to key down events during the bubble phase. /// The imperative API equivalent to [`InteractiveElement::on_key_down`]. /// /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. pub fn on_key_down( &mut self, listener: impl Fn(&KeyDownEvent, &mut Window, &mut App) + 'static, ) { self.key_down_listeners .push(Box::new(move |event, phase, window, cx| { if phase == DispatchPhase::Bubble { (listener)(event, window, cx) } })); } /// Bind the given callback to key down events during the capture phase. /// The imperative API equivalent to [`InteractiveElement::capture_key_down`]. /// /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. pub fn capture_key_down( &mut self, listener: impl Fn(&KeyDownEvent, &mut Window, &mut App) + 'static, ) { self.key_down_listeners .push(Box::new(move |event, phase, window, cx| { if phase == DispatchPhase::Capture { listener(event, window, cx) } })); } /// Bind the given callback to key up events during the bubble phase. /// The imperative API equivalent to [`InteractiveElement::on_key_up`]. /// /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. pub fn on_key_up(&mut self, listener: impl Fn(&KeyUpEvent, &mut Window, &mut App) + 'static) { self.key_up_listeners .push(Box::new(move |event, phase, window, cx| { if phase == DispatchPhase::Bubble { listener(event, window, cx) } })); } /// Bind the given callback to key up events during the capture phase. /// The imperative API equivalent to [`InteractiveElement::on_key_up`]. /// /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. pub fn capture_key_up( &mut self, listener: impl Fn(&KeyUpEvent, &mut Window, &mut App) + 'static, ) { self.key_up_listeners .push(Box::new(move |event, phase, window, cx| { if phase == DispatchPhase::Capture { listener(event, window, cx) } })); } /// Bind the given callback to modifiers changing events. /// The imperative API equivalent to [`InteractiveElement::on_modifiers_changed`]. /// /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. pub fn on_modifiers_changed( &mut self, listener: impl Fn(&ModifiersChangedEvent, &mut Window, &mut App) + 'static, ) { self.modifiers_changed_listeners .push(Box::new(move |event, window, cx| { listener(event, window, cx) })); } /// Bind the given callback to drop events of the given type, whether or not the drag started on this element. /// The imperative API equivalent to [`InteractiveElement::on_drop`]. /// /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. pub fn on_drop<T: 'static>(&mut self, listener: impl Fn(&T, &mut Window, &mut App) + 'static) { self.drop_listeners.push(( TypeId::of::<T>(), Box::new(move |dragged_value, window, cx| { listener(dragged_value.downcast_ref().unwrap(), window, cx); }), ));