+//! mouse events and action handlers. It is meant to be similar to the HTML `
`
//! element, but for GPUI.
//!
//! # Build your own div
@@ -14,13 +14,6 @@
//! 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.
-//!
-//! # Capturing and bubbling
-//!
-//! Note that while event dispatch in GPUI uses similar names and concepts to the web
-//! even API, the details are very different. See the documentation in [TODO!(docs)
-//! DOCUMENT EVENT DISPATCH SOMEWHERE IN WINDOW CONTEXT] for more details
-//!
use crate::{
point, px, size, Action, AnyDrag, AnyElement, AnyTooltip, AnyView, AppContext, Bounds,
@@ -85,7 +78,7 @@ impl Interactivity {
/// 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 [`ViewContext::listener()`] to get access to the view state from this callback
+ /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to the view state from this callback.
pub fn on_mouse_down(
&mut self,
button: MouseButton,
@@ -105,7 +98,7 @@ impl Interactivity {
/// 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 [`ViewContext::listener()`] to get access to a view's state from this callback
+ /// See [`ViewContext::listener`](crate::ViewContext::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 WindowContext) + 'static,
@@ -119,9 +112,9 @@ impl Interactivity {
}
/// 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()`]
+ /// the imperative API equivalent to [`InteractiveElement::on_any_mouse_down`]
///
- /// See [`ViewContext::listener()`] to get access to a view's state from this callback
+ /// See [`ViewContext::listener`](crate::ViewContext::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 WindowContext) + 'static,
@@ -135,9 +128,9 @@ impl Interactivity {
}
/// 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()`]
+ /// the imperative API equivalent to [`InteractiveElement::on_mouse_up`]
///
- /// See [`ViewContext::listener()`] to get access to a view's state from this callback
+ /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
pub fn on_mouse_up(
&mut self,
button: MouseButton,
@@ -155,9 +148,9 @@ impl Interactivity {
}
/// 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()`]
+ /// the imperative API equivalent to [`InteractiveElement::capture_any_mouse_up`]
///
- /// See [`ViewContext::listener()`] to get access to a view's state from this callback
+ /// See [`ViewContext::listener`](crate::ViewContext::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 WindowContext) + 'static,
@@ -171,9 +164,9 @@ impl Interactivity {
}
/// Bind the given callback to the mouse up event for any button, during the bubble phase
- /// the imperative API equivalent to [`InteractiveElement::on_any_mouse_up()`]
+ /// the imperative API equivalent to [`Interactivity::on_any_mouse_up`]
///
- /// See [`ViewContext::listener()`] to get access to a view's state from this callback
+ /// See [`ViewContext::listener`](crate::ViewContext::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 WindowContext) + 'static,
@@ -188,9 +181,9 @@ impl Interactivity {
/// 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()`]
+ /// The imperative API equivalent to [`InteractiveElement::on_mouse_down_out`]
///
- /// See [`ViewContext::listener()`] to get access to a view's state from this callback
+ /// See [`ViewContext::listener`](crate::ViewContext::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 WindowContext) + 'static,
@@ -206,9 +199,9 @@ impl Interactivity {
/// 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()`]
+ /// The imperative API equivalent to [`InteractiveElement::on_mouse_up_out`]
///
- /// See [`ViewContext::listener()`] to get access to a view's state from this callback
+ /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
pub fn on_mouse_up_out(
&mut self,
button: MouseButton,
@@ -226,9 +219,9 @@ impl Interactivity {
}
/// Bind the given callback to the mouse move event, during the bubble phase
- /// The imperative API equivalent to [`InteractiveElement::on_mouse_move()`]
+ /// The imperative API equivalent to [`InteractiveElement::on_mouse_move`]
///
- /// See [`ViewContext::listener()`] to get access to a view's state from this callback
+ /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
pub fn on_mouse_move(
&mut self,
listener: impl Fn(&MouseMoveEvent, &mut WindowContext) + 'static,
@@ -245,9 +238,9 @@ impl Interactivity {
/// 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()`]
+ /// The imperative API equivalent to [`InteractiveElement::on_drag_move`]
///
- /// See [`ViewContext::listener()`] to get access to a view's state from this callback
+ /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
pub fn on_drag_move
(
&mut self,
listener: impl Fn(&DragMoveEvent, &mut WindowContext) + 'static,
@@ -275,9 +268,9 @@ impl Interactivity {
}
/// Bind the given callback to scroll wheel events during the bubble phase
- /// The imperative API equivalent to [`InteractiveElement::on_scroll_wheel()`]
+ /// The imperative API equivalent to [`InteractiveElement::on_scroll_wheel`]
///
- /// See [`ViewContext::listener()`] to get access to a view's state from this callback
+ /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
pub fn on_scroll_wheel(
&mut self,
listener: impl Fn(&ScrollWheelEvent, &mut WindowContext) + 'static,
@@ -291,9 +284,9 @@ impl Interactivity {
}
/// Bind the given callback to an action dispatch during the capture phase
- /// The imperative API equivalent to [`InteractiveElement::capture_action()`]
+ /// The imperative API equivalent to [`InteractiveElement::capture_action`]
///
- /// See [`ViewContext::listener()`] to get access to a view's state from this callback
+ /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
pub fn capture_action(
&mut self,
listener: impl Fn(&A, &mut WindowContext) + 'static,
@@ -310,9 +303,9 @@ impl Interactivity {
}
/// Bind the given callback to an action dispatch during the bubble phase
- /// The imperative API equivalent to [`InteractiveElement::on_action()`]
+ /// The imperative API equivalent to [`InteractiveElement::on_action`]
///
- /// See [`ViewContext::listener()`] to get access to a view's state from this callback
+ /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
pub fn on_action(&mut self, listener: impl Fn(&A, &mut WindowContext) + 'static) {
self.action_listeners.push((
TypeId::of::(),
@@ -328,9 +321,9 @@ impl Interactivity {
/// 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()`]
+ /// The imperative API equivalent to [`InteractiveElement::on_boxed_action`]
///
- /// See [`ViewContext::listener()`] to get access to a view's state from this callback
+ /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
pub fn on_boxed_action(
&mut self,
action: &dyn Action,
@@ -348,9 +341,9 @@ impl Interactivity {
}
/// Bind the given callback to key down events during the bubble phase
- /// The imperative API equivalent to [`InteractiveElement::on_key_down()`]
+ /// The imperative API equivalent to [`InteractiveElement::on_key_down`]
///
- /// See [`ViewContext::listener()`] to get access to a view's state from this callback
+ /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
pub fn on_key_down(&mut self, listener: impl Fn(&KeyDownEvent, &mut WindowContext) + 'static) {
self.key_down_listeners
.push(Box::new(move |event, phase, cx| {
@@ -361,9 +354,9 @@ impl Interactivity {
}
/// Bind the given callback to key down events during the capture phase
- /// The imperative API equivalent to [`InteractiveElement::capture_key_down()`]
+ /// The imperative API equivalent to [`InteractiveElement::capture_key_down`]
///
- /// See [`ViewContext::listener()`] to get access to a view's state from this callback
+ /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
pub fn capture_key_down(
&mut self,
listener: impl Fn(&KeyDownEvent, &mut WindowContext) + 'static,
@@ -377,9 +370,9 @@ impl Interactivity {
}
/// Bind the given callback to key up events during the bubble phase
- /// The imperative API equivalent to [`InteractiveElement::on_key_up()`]
+ /// The imperative API equivalent to [`InteractiveElement::on_key_up`]
///
- /// See [`ViewContext::listener()`] to get access to a view's state from this callback
+ /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
pub fn on_key_up(&mut self, listener: impl Fn(&KeyUpEvent, &mut WindowContext) + 'static) {
self.key_up_listeners
.push(Box::new(move |event, phase, cx| {
@@ -390,9 +383,9 @@ impl Interactivity {
}
/// Bind the given callback to key up events during the capture phase
- /// The imperative API equivalent to [`InteractiveElement::on_key_up()`]
+ /// The imperative API equivalent to [`InteractiveElement::on_key_up`]
///
- /// See [`ViewContext::listener()`] to get access to a view's state from this callback
+ /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
pub fn capture_key_up(&mut self, listener: impl Fn(&KeyUpEvent, &mut WindowContext) + 'static) {
self.key_up_listeners
.push(Box::new(move |event, phase, cx| {
@@ -403,9 +396,9 @@ impl Interactivity {
}
/// 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()`]
+ /// The imperative API equivalent to [`InteractiveElement::on_drop`]
///
- /// See [`ViewContext::listener()`] to get access to a view's state from this callback
+ /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
pub fn on_drop(&mut self, listener: impl Fn(&T, &mut WindowContext) + 'static) {
self.drop_listeners.push((
TypeId::of::(),
@@ -416,15 +409,15 @@ impl Interactivity {
}
/// Use the given predicate to determine whether or not a drop event should be dispatched to this element
- /// The imperative API equivalent to [`InteractiveElement::can_drop()`]
+ /// The imperative API equivalent to [`InteractiveElement::can_drop`]
pub fn can_drop(&mut self, predicate: impl Fn(&dyn Any, &mut WindowContext) -> bool + 'static) {
self.can_drop_predicate = Some(Box::new(predicate));
}
/// Bind the given callback to click events of this element
- /// The imperative API equivalent to [`InteractiveElement::on_click()`]
+ /// The imperative API equivalent to [`StatefulInteractiveElement::on_click`]
///
- /// See [`ViewContext::listener()`] to get access to a view's state from this callback
+ /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
pub fn on_click(&mut self, listener: impl Fn(&ClickEvent, &mut WindowContext) + 'static)
where
Self: Sized,
@@ -435,10 +428,10 @@ impl Interactivity {
/// On drag initiation, this callback will be used to create a new view to render the dragged value for a
/// drag and drop operation. This API should also be used as the equivalent of 'on drag start' with
- /// the [`Self::on_drag_move()`] API
- /// The imperative API equivalent to [`InteractiveElement::on_drag()`]
+ /// the [`Self::on_drag_move`] API
+ /// The imperative API equivalent to [`StatefulInteractiveElement::on_drag`]
///
- /// See [`ViewContext::listener()`] to get access to a view's state from this callback
+ /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
pub fn on_drag(
&mut self,
value: T,
@@ -460,9 +453,9 @@ impl Interactivity {
/// Bind the given callback on the hover start and end events of this element. Note that the boolean
/// passed to the callback is true when the hover starts and false when it ends.
- /// The imperative API equivalent to [`InteractiveElement::on_drag()`]
+ /// The imperative API equivalent to [`StatefulInteractiveElement::on_drag`]
///
- /// See [`ViewContext::listener()`] to get access to a view's state from this callback
+ /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
pub fn on_hover(&mut self, listener: impl Fn(&bool, &mut WindowContext) + 'static)
where
Self: Sized,
@@ -475,7 +468,7 @@ impl Interactivity {
}
/// Use the given callback to construct a new tooltip view when the mouse hovers over this element.
- /// The imperative API equivalent to [`InteractiveElement::tooltip()`]
+ /// The imperative API equivalent to [`InteractiveElement::tooltip`]
pub fn tooltip(&mut self, build_tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static)
where
Self: Sized,
@@ -488,7 +481,7 @@ impl Interactivity {
}
/// Block the mouse from interacting with this element or any of it's children
- /// The imperative API equivalent to [`InteractiveElement::block_mouse()`]
+ /// The imperative API equivalent to [`InteractiveElement::block_mouse`]
pub fn block_mouse(&mut self) {
self.block_mouse = true;
}
@@ -559,9 +552,9 @@ pub trait InteractiveElement: Sized {
}
/// Bind the given callback to the mouse down event for the given mouse button,
- /// the fluent API equivalent to [`Interactivity::on_mouse_down()`]
+ /// the fluent API equivalent to [`Interactivity::on_mouse_down`]
///
- /// See [`ViewContext::listener()`] to get access to the view state from this callback
+ /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to the view state from this callback.
fn on_mouse_down(
mut self,
button: MouseButton,
@@ -573,7 +566,7 @@ pub trait InteractiveElement: Sized {
#[cfg(any(test, feature = "test-support"))]
/// Set a key that can be used to look up this element's bounds
- /// in the [`VisualTestContext::debug_bounds()`] map
+ /// in the [`VisualTestContext::debug_bounds`] map
/// This is a noop in release builds
fn debug_selector(mut self, f: impl FnOnce() -> String) -> Self {
self.interactivity().debug_selector = Some(f());
@@ -582,7 +575,7 @@ pub trait InteractiveElement: Sized {
#[cfg(not(any(test, feature = "test-support")))]
/// Set a key that can be used to look up this element's bounds
- /// in the [`VisualTestContext::debug_bounds()`] map
+ /// in the [`VisualTestContext::debug_bounds`] map
/// This is a noop in release builds
#[inline]
fn debug_selector(self, _: impl FnOnce() -> String) -> Self {
@@ -590,9 +583,9 @@ pub trait InteractiveElement: Sized {
}
/// Bind the given callback to the mouse down event for any button, during the capture phase
- /// the fluent API equivalent to [`Interactivity::capture_any_mouse_down()`]
+ /// the fluent API equivalent to [`Interactivity::capture_any_mouse_down`]
///
- /// See [`ViewContext::listener()`] to get access to a view's state from this callback
+ /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
fn capture_any_mouse_down(
mut self,
listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
@@ -602,9 +595,9 @@ pub trait InteractiveElement: Sized {
}
/// Bind the given callback to the mouse down event for any button, during the capture phase
- /// the fluent API equivalent to [`Interactivity::on_any_mouse_down()`]
+ /// the fluent API equivalent to [`Interactivity::on_any_mouse_down`]
///
- /// See [`ViewContext::listener()`] to get access to a view's state from this callback
+ /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
fn on_any_mouse_down(
mut self,
listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
@@ -614,9 +607,9 @@ pub trait InteractiveElement: Sized {
}
/// Bind the given callback to the mouse up event for the given button, during the bubble phase
- /// the fluent API equivalent to [`Interactivity::on_mouse_up()`]
+ /// the fluent API equivalent to [`Interactivity::on_mouse_up`]
///
- /// See [`ViewContext::listener()`] to get access to a view's state from this callback
+ /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
fn on_mouse_up(
mut self,
button: MouseButton,
@@ -627,9 +620,9 @@ pub trait InteractiveElement: Sized {
}
/// Bind the given callback to the mouse up event for any button, during the capture phase
- /// the fluent API equivalent to [`Interactivity::capture_any_mouse_up()`]
+ /// the fluent API equivalent to [`Interactivity::capture_any_mouse_up`]
///
- /// See [`ViewContext::listener()`] to get access to a view's state from this callback
+ /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
fn capture_any_mouse_up(
mut self,
listener: impl Fn(&MouseUpEvent, &mut WindowContext) + 'static,
@@ -640,9 +633,9 @@ pub trait InteractiveElement: Sized {
/// 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 fluent API equivalent to [`Interactivity::on_mouse_down_out()`]
+ /// The fluent API equivalent to [`Interactivity::on_mouse_down_out`]
///
- /// See [`ViewContext::listener()`] to get access to a view's state from this callback
+ /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
fn on_mouse_down_out(
mut self,
listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
@@ -653,9 +646,9 @@ pub trait InteractiveElement: Sized {
/// 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 fluent API equivalent to [`Interactivity::on_mouse_up_out()`]
+ /// The fluent API equivalent to [`Interactivity::on_mouse_up_out`]
///
- /// See [`ViewContext::listener()`] to get access to a view's state from this callback
+ /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
fn on_mouse_up_out(
mut self,
button: MouseButton,
@@ -666,9 +659,9 @@ pub trait InteractiveElement: Sized {
}
/// Bind the given callback to the mouse move event, during the bubble phase
- /// The fluent API equivalent to [`Interactivity::on_mouse_move()`]
+ /// The fluent API equivalent to [`Interactivity::on_mouse_move`]
///
- /// See [`ViewContext::listener()`] to get access to a view's state from this callback
+ /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
fn on_mouse_move(
mut self,
listener: impl Fn(&MouseMoveEvent, &mut WindowContext) + 'static,
@@ -681,9 +674,9 @@ pub trait InteractiveElement: Sized {
/// 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 fluent API equivalent to [`Interactivity::on_drag_move()`]
+ /// The fluent API equivalent to [`Interactivity::on_drag_move`]
///
- /// See [`ViewContext::listener()`] to get access to a view's state from this callback
+ /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
fn on_drag_move(
mut self,
listener: impl Fn(&DragMoveEvent, &mut WindowContext) + 'static,
@@ -696,9 +689,9 @@ pub trait InteractiveElement: Sized {
}
/// Bind the given callback to scroll wheel events during the bubble phase
- /// The fluent API equivalent to [`Interactivity::on_scroll_wheel()`]
+ /// The fluent API equivalent to [`Interactivity::on_scroll_wheel`]
///
- /// See [`ViewContext::listener()`] to get access to a view's state from this callback
+ /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
fn on_scroll_wheel(
mut self,
listener: impl Fn(&ScrollWheelEvent, &mut WindowContext) + 'static,
@@ -708,9 +701,9 @@ pub trait InteractiveElement: Sized {
}
/// Capture the given action, before normal action dispatch can fire
- /// The fluent API equivalent to [`Interactivity::on_scroll_wheel()`]
+ /// The fluent API equivalent to [`Interactivity::on_scroll_wheel`]
///
- /// See [`ViewContext::listener()`] to get access to a view's state from this callback
+ /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
fn capture_action(
mut self,
listener: impl Fn(&A, &mut WindowContext) + 'static,
@@ -720,9 +713,9 @@ pub trait InteractiveElement: Sized {
}
/// Bind the given callback to an action dispatch during the bubble phase
- /// The fluent API equivalent to [`Interactivity::on_action()`]
+ /// The fluent API equivalent to [`Interactivity::on_action`]
///
- /// See [`ViewContext::listener()`] to get access to a view's state from this callback
+ /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
fn on_action(mut self, listener: impl Fn(&A, &mut WindowContext) + 'static) -> Self {
self.interactivity().on_action(listener);
self
@@ -731,9 +724,9 @@ pub trait InteractiveElement: Sized {
/// 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 fluent API equivalent to [`Interactivity::on_boxed_action()`]
+ /// The fluent API equivalent to [`Interactivity::on_boxed_action`]
///
- /// See [`ViewContext::listener()`] to get access to a view's state from this callback
+ /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
fn on_boxed_action(
mut self,
action: &dyn Action,
@@ -744,9 +737,9 @@ pub trait InteractiveElement: Sized {
}
/// Bind the given callback to key down events during the bubble phase
- /// The fluent API equivalent to [`Interactivity::on_key_down()`]
+ /// The fluent API equivalent to [`Interactivity::on_key_down`]
///
- /// See [`ViewContext::listener()`] to get access to a view's state from this callback
+ /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
fn on_key_down(
mut self,
listener: impl Fn(&KeyDownEvent, &mut WindowContext) + 'static,
@@ -756,9 +749,9 @@ pub trait InteractiveElement: Sized {
}
/// Bind the given callback to key down events during the capture phase
- /// The fluent API equivalent to [`Interactivity::capture_key_down()`]
+ /// The fluent API equivalent to [`Interactivity::capture_key_down`]
///
- /// See [`ViewContext::listener()`] to get access to a view's state from this callback
+ /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
fn capture_key_down(
mut self,
listener: impl Fn(&KeyDownEvent, &mut WindowContext) + 'static,
@@ -768,18 +761,18 @@ pub trait InteractiveElement: Sized {
}
/// Bind the given callback to key up events during the bubble phase
- /// The fluent API equivalent to [`Interactivity::on_key_up()`]
+ /// The fluent API equivalent to [`Interactivity::on_key_up`]
///
- /// See [`ViewContext::listener()`] to get access to a view's state from this callback
+ /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
fn on_key_up(mut self, listener: impl Fn(&KeyUpEvent, &mut WindowContext) + 'static) -> Self {
self.interactivity().on_key_up(listener);
self
}
/// Bind the given callback to key up events during the capture phase
- /// The fluent API equivalent to [`Interactivity::capture_key_up()`]
+ /// The fluent API equivalent to [`Interactivity::capture_key_up`]
///
- /// See [`ViewContext::listener()`] to get access to a view's state from this callback
+ /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
fn capture_key_up(
mut self,
listener: impl Fn(&KeyUpEvent, &mut WindowContext) + 'static,
@@ -813,16 +806,16 @@ pub trait InteractiveElement: Sized {
}
/// Bind the given callback to drop events of the given type, whether or not the drag started on this element
- /// The fluent API equivalent to [`Interactivity::on_drop()`]
+ /// The fluent API equivalent to [`Interactivity::on_drop`]
///
- /// See [`ViewContext::listener()`] to get access to a view's state from this callback
+ /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
fn on_drop(mut self, listener: impl Fn(&T, &mut WindowContext) + 'static) -> Self {
self.interactivity().on_drop(listener);
self
}
/// Use the given predicate to determine whether or not a drop event should be dispatched to this element
- /// The fluent API equivalent to [`Interactivity::can_drop()`]
+ /// The fluent API equivalent to [`Interactivity::can_drop`]
fn can_drop(
mut self,
predicate: impl Fn(&dyn Any, &mut WindowContext) -> bool + 'static,
@@ -832,7 +825,7 @@ pub trait InteractiveElement: Sized {
}
/// Block the mouse from interacting with this element or any of it's children
- /// The fluent API equivalent to [`Interactivity::block_mouse()`]
+ /// The fluent API equivalent to [`Interactivity::block_mouse`]
fn block_mouse(mut self) -> Self {
self.interactivity().block_mouse();
self
@@ -899,9 +892,9 @@ pub trait StatefulInteractiveElement: InteractiveElement {
}
/// Bind the given callback to click events of this element
- /// The fluent API equivalent to [`Interactivity::on_click()`]
+ /// The fluent API equivalent to [`Interactivity::on_click`]
///
- /// See [`ViewContext::listener()`] to get access to a view's state from this callback
+ /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
fn on_click(mut self, listener: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self
where
Self: Sized,
@@ -912,10 +905,10 @@ pub trait StatefulInteractiveElement: InteractiveElement {
/// On drag initiation, this callback will be used to create a new view to render the dragged value for a
/// drag and drop operation. This API should also be used as the equivalent of 'on drag start' with
- /// the [`Self::on_drag_move()`] API
- /// The fluent API equivalent to [`Interactivity::on_drag()`]
+ /// the [`Self::on_drag_move`] API
+ /// The fluent API equivalent to [`Interactivity::on_drag`]
///
- /// See [`ViewContext::listener()`] to get access to a view's state from this callback
+ /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
fn on_drag(
mut self,
value: T,
@@ -932,9 +925,9 @@ pub trait StatefulInteractiveElement: InteractiveElement {
/// Bind the given callback on the hover start and end events of this element. Note that the boolean
/// passed to the callback is true when the hover starts and false when it ends.
- /// The fluent API equivalent to [`Interactivity::on_hover()`]
+ /// The fluent API equivalent to [`Interactivity::on_hover`]
///
- /// See [`ViewContext::listener()`] to get access to a view's state from this callback
+ /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
fn on_hover(mut self, listener: impl Fn(&bool, &mut WindowContext) + 'static) -> Self
where
Self: Sized,
@@ -944,7 +937,7 @@ pub trait StatefulInteractiveElement: InteractiveElement {
}
/// Use the given callback to construct a new tooltip view when the mouse hovers over this element.
- /// The fluent API equivalent to [`Interactivity::tooltip()`]
+ /// The fluent API equivalent to [`Interactivity::tooltip`]
fn tooltip(mut self, build_tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self
where
Self: Sized,
@@ -1008,10 +1001,9 @@ pub(crate) type ActionListener = Box Div {
#[cfg(debug_assertions)]
- let interactivity = {
- let mut interactivity = Interactivity::default();
- interactivity.location = Some(*core::panic::Location::caller());
- interactivity
+ let interactivity = Interactivity {
+ location: Some(*core::panic::Location::caller()),
+ ..Default::default()
};
#[cfg(not(debug_assertions))]
diff --git a/crates/gpui/src/elements/img.rs b/crates/gpui/src/elements/img.rs
index 191cadf2939834eb68c4b70abf18b92c8bdc6ff8..e7377373fe18cd7d6d26a610c2d5b776e02874fc 100644
--- a/crates/gpui/src/elements/img.rs
+++ b/crates/gpui/src/elements/img.rs
@@ -104,7 +104,7 @@ impl Element for Img {
cx.with_z_index(1, |cx| {
match source {
ImageSource::Uri(uri) => {
- let image_future = cx.image_cache.get(uri.clone());
+ let image_future = cx.image_cache.get(uri.clone(), cx);
if let Some(data) = image_future
.clone()
.now_or_never()
diff --git a/crates/gpui/src/elements/overlay.rs b/crates/gpui/src/elements/overlay.rs
index 61c34bd9385f6580feb3930dd3b3ea4b1494e919..9db75b75ba0d8138d75fd4a9aa90071e15689a10 100644
--- a/crates/gpui/src/elements/overlay.rs
+++ b/crates/gpui/src/elements/overlay.rs
@@ -222,7 +222,7 @@ impl OverlayPositionMode {
) -> (Point, Bounds) {
match self {
OverlayPositionMode::Window => {
- let anchor_position = anchor_position.unwrap_or_else(|| bounds.origin);
+ let anchor_position = anchor_position.unwrap_or(bounds.origin);
let bounds = anchor_corner.get_bounds(anchor_position, size);
(anchor_position, bounds)
}
diff --git a/crates/gpui/src/elements/text.rs b/crates/gpui/src/elements/text.rs
index 13fc6200766edc068af5f23defc3d9c2e73c4214..2b5bf9166ef2182dca7cae92f93a3e7a91af5925 100644
--- a/crates/gpui/src/elements/text.rs
+++ b/crates/gpui/src/elements/text.rs
@@ -46,6 +46,18 @@ impl IntoElement for &'static str {
}
}
+impl IntoElement for String {
+ type Element = SharedString;
+
+ fn element_id(&self) -> Option {
+ None
+ }
+
+ fn into_element(self) -> Self::Element {
+ self.into()
+ }
+}
+
impl Element for SharedString {
type State = TextState;
diff --git a/crates/gpui/src/elements/uniform_list.rs b/crates/gpui/src/elements/uniform_list.rs
index 108e669f7550a2e6387641aaa4c1dae6e59f6313..ce32b993a5592732f763712b84fa33f24e2738f4 100644
--- a/crates/gpui/src/elements/uniform_list.rs
+++ b/crates/gpui/src/elements/uniform_list.rs
@@ -181,7 +181,7 @@ impl Element for UniformList {
let shared_scroll_offset = element_state
.interactive
.scroll_offset
- .get_or_insert_with(|| Rc::default())
+ .get_or_insert_with(Rc::default)
.clone();
let item_height = self.measure_item(Some(padded_bounds.size.width), cx).height;
diff --git a/crates/gpui/src/geometry.rs b/crates/gpui/src/geometry.rs
index d986f26be4475a72b0da8b75de8b3847e3fce588..dd826b68f541c98c88d364156dafc641bdbc8afa 100644
--- a/crates/gpui/src/geometry.rs
+++ b/crates/gpui/src/geometry.rs
@@ -1,3 +1,7 @@
+//! The GPUI geometry module is a collection of types and traits that
+//! can be used to describe common units, concepts, and the relationships
+//! between them.
+
use core::fmt::Debug;
use derive_more::{Add, AddAssign, Div, DivAssign, Mul, Neg, Sub, SubAssign};
use refineable::Refineable;
@@ -8,13 +12,17 @@ use std::{
ops::{Add, Div, Mul, MulAssign, Sub},
};
+/// An axis along which a measurement can be made.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Axis {
+ /// The y axis, or up and down
Vertical,
+ /// The x axis, or left and right
Horizontal,
}
impl Axis {
+ /// Swap this axis to the opposite axis.
pub fn invert(&self) -> Self {
match self {
Axis::Vertical => Axis::Horizontal,
@@ -23,11 +31,15 @@ impl Axis {
}
}
+/// A trait for accessing the given unit along a certain axis.
pub trait Along {
+ /// The unit associated with this type
type Unit;
+ /// Returns the unit along the given axis.
fn along(&self, axis: Axis) -> Self::Unit;
+ /// Applies the given function to the unit along the given axis and returns a new value.
fn apply_along(&self, axis: Axis, f: impl FnOnce(Self::Unit) -> Self::Unit) -> Self;
}
@@ -47,7 +59,9 @@ pub trait Along {
#[refineable(Debug)]
#[repr(C)]
pub struct Point {
+ /// The x coordinate of the point.
pub x: T,
+ /// The y coordinate of the point.
pub y: T,
}
@@ -334,7 +348,9 @@ impl Clone for Point {
#[refineable(Debug)]
#[repr(C)]
pub struct Size {
+ /// The width component of the size.
pub width: T,
+ /// The height component of the size.
pub height: T,
}
@@ -640,7 +656,9 @@ impl Size {
#[refineable(Debug)]
#[repr(C)]
pub struct Bounds {
+ /// The origin point of this area.
pub origin: Point,
+ /// The size of the rectangle.
pub size: Size,
}
@@ -1192,9 +1210,13 @@ impl Copy for Bounds {}
#[refineable(Debug)]
#[repr(C)]
pub struct Edges {
+ /// The size of the top edge.
pub top: T,
+ /// The size of the right edge.
pub right: T,
+ /// The size of the bottom edge.
pub bottom: T,
+ /// The size of the left edge.
pub left: T,
}
@@ -1600,9 +1622,13 @@ impl From for Edges {
#[refineable(Debug)]
#[repr(C)]
pub struct Corners {
+ /// The value associated with the top left corner.
pub top_left: T,
+ /// The value associated with the top right corner.
pub top_right: T,
+ /// The value associated with the bottom right corner.
pub bottom_right: T,
+ /// The value associated with the bottom left corner.
pub bottom_left: T,
}
@@ -2020,13 +2046,13 @@ impl Eq for Pixels {}
impl PartialOrd for Pixels {
fn partial_cmp(&self, other: &Self) -> Option {
- self.0.partial_cmp(&other.0)
+ Some(self.cmp(other))
}
}
impl Ord for Pixels {
fn cmp(&self, other: &Self) -> cmp::Ordering {
- self.partial_cmp(other).unwrap()
+ self.0.total_cmp(&other.0)
}
}
diff --git a/crates/gpui/src/gpui.rs b/crates/gpui/src/gpui.rs
index 929e9ebfb4f262088e90e7ab5b99477a9933d998..638e94de3969360f94b5594f06e45710cace1495 100644
--- a/crates/gpui/src/gpui.rs
+++ b/crates/gpui/src/gpui.rs
@@ -1,30 +1,67 @@
//! # Welcome to GPUI!
//!
//! GPUI is a hybrid immediate and retained mode, GPU accelerated, UI framework
-//! for Rust, designed to support a wide variety of applications. GPUI is currently
-//! being actively developed and improved for the [Zed code editor](https://zed.dev/), and new versions
-//! will have breaking changes. You'll probably need to use the latest stable version
-//! of rust to use GPUI.
+//! for Rust, designed to support a wide variety of applications.
//!
-//! # Getting started with GPUI
+//! ## Getting Started
//!
-//! TODO!(docs): Write a code sample showing how to create a window and render a simple
-//! div
+//! GPUI is still in active development as we work on the Zed code editor and isn't yet on crates.io.
+//! You'll also need to use the latest version of stable rust and be on macOS. Add the following to your
+//! Cargo.toml:
//!
-//! # Drawing interesting things
+//! ```
+//! gpui = { git = "https://github.com/zed-industries/zed" }
+//! ```
//!
-//! TODO!(docs): Expand demo to show how to draw a more interesting scene, with
-//! a counter to store state and a button to increment it.
+//! Everything in GPUI starts with an [`App`]. You can create one with [`App::new`], and
+//! kick off your application by passing a callback to [`App::run`]. Inside this callback,
+//! you can create a new window with [`AppContext::open_window`], and register your first root
+//! view. See [gpui.rs](https://www.gpui.rs/) for a complete example.
//!
-//! # Interacting with your application state
+//! ## The Big Picture
//!
-//! TODO!(docs): Expand demo to show GPUI entity interactions, like subscriptions and entities
-//! maybe make a network request to show async stuff?
+//! GPUI offers three different [registers](https://en.wikipedia.org/wiki/Register_(sociolinguistics)) depending on your needs:
//!
-//! # Conclusion
+//! - State management and communication with Models. Whenever you need to store application state
+//! that communicates between different parts of your application, you'll want to use GPUI's
+//! models. Models are owned by GPUI and are only accessible through an owned smart pointer
+//! similar to an [`Rc`]. See the [`app::model_context`] module for more information.
//!
-//! TODO!(docs): Wrap up with a conclusion and links to other places? Zed / GPUI website?
-//! Discord for chatting about it? Other tutorials or references?
+//! - High level, declarative UI with Views. All UI in GPUI starts with a View. A view is simply
+//! a model that can be rendered, via the [`Render`] trait. At the start of each frame, GPUI
+//! will call this render method on the root view of a given window. Views build a tree of
+//! `elements`, lay them out and style them with a tailwind-style API, and then give them to
+//! GPUI to turn into pixels. See the [`elements::Div`] element for an all purpose swiss-army
+//! knife for UI.
+//!
+//! - Low level, imperative UI with Elements. Elements are the building blocks of UI in GPUI, and they
+//! provide a nice wrapper around an imperative API that provides as much flexibility and control as
+//! you need. Elements have total control over how they and their child elements are rendered and and
+//! can be used for making efficient views into large lists, implement custom layouting for a code editor,
+//! and anything else you can think of. See the [`element`] module for more information.
+//!
+//! Each of these registers has one or more corresponding contexts that can be accessed from all GPUI services.
+//! This context is your main interface to GPUI, and is used extensively throughout the framework.
+//!
+//! ## Other Resources
+//!
+//! In addition to the systems above, GPUI provides a range of smaller services that are useful for building
+//! complex applications:
+//!
+//! - Actions are user-defined structs that are used for converting keystrokes into logical operations in your UI.
+//! Use this for implementing keyboard shortcuts, such as cmd-q. See the [`action`] module for more information.
+//! - Platform services, such as `quit the app` or `open a URL` are available as methods on the [`app::AppContext`].
+//! - An async executor that is integrated with the platform's event loop. See the [`executor`] module for more information.,
+//! - The [gpui::test] macro provides a convenient way to write tests for your GPUI applications. Tests also have their
+//! own kind of context, a [`TestAppContext`] which provides ways of simulating common platform input. See [`app::test_context`]
+//! and [`test`] modules for more details.
+//!
+//! Currently, the best way to learn about these APIs is to read the Zed source code, ask us about it at a fireside hack, or drop
+//! a question in the [Zed Discord](https://discord.gg/U4qhCEhMXP). We're working on improving the documentation, creating more examples,
+//! and will be publishing more guides to GPUI on our [blog](https://zed.dev/blog).
+
+#![deny(missing_docs)]
+#![allow(clippy::type_complexity)]
#[macro_use]
mod action;
diff --git a/crates/gpui/src/image_cache.rs b/crates/gpui/src/image_cache.rs
index dd7b7b571e43c20609e6920f0da59093b5aa010f..95b41c3b2c0619c24deb4207e41b01d23e615b21 100644
--- a/crates/gpui/src/image_cache.rs
+++ b/crates/gpui/src/image_cache.rs
@@ -1,9 +1,6 @@
-use crate::{ImageData, ImageId, SharedUrl};
+use crate::{AppContext, ImageData, ImageId, SharedUrl, Task};
use collections::HashMap;
-use futures::{
- future::{BoxFuture, Shared},
- AsyncReadExt, FutureExt, TryFutureExt,
-};
+use futures::{future::Shared, AsyncReadExt, FutureExt, TryFutureExt};
use image::ImageError;
use parking_lot::Mutex;
use std::sync::Arc;
@@ -44,10 +41,10 @@ impl From for Error {
pub(crate) struct ImageCache {
client: Arc,
- images: Arc>>,
+ images: Arc>>,
}
-type FetchImageFuture = Shared, Error>>>;
+type FetchImageTask = Shared, Error>>>;
impl ImageCache {
pub fn new(client: Arc) -> Self {
@@ -57,10 +54,7 @@ impl ImageCache {
}
}
- pub fn get(
- &self,
- uri: impl Into,
- ) -> Shared, Error>>> {
+ pub fn get(&self, uri: impl Into, cx: &AppContext) -> FetchImageTask {
let uri = uri.into();
let mut images = self.images.lock();
@@ -68,36 +62,39 @@ impl ImageCache {
Some(future) => future.clone(),
None => {
let client = self.client.clone();
- let future = {
- let uri = uri.clone();
- async move {
- let mut response = client.get(uri.as_ref(), ().into(), true).await?;
- let mut body = Vec::new();
- response.body_mut().read_to_end(&mut body).await?;
+ let future = cx
+ .background_executor()
+ .spawn(
+ {
+ let uri = uri.clone();
+ async move {
+ let mut response =
+ client.get(uri.as_ref(), ().into(), true).await?;
+ let mut body = Vec::new();
+ response.body_mut().read_to_end(&mut body).await?;
- if !response.status().is_success() {
- return Err(Error::BadStatus {
- status: response.status(),
- body: String::from_utf8_lossy(&body).into_owned(),
- });
- }
-
- let format = image::guess_format(&body)?;
- let image =
- image::load_from_memory_with_format(&body, format)?.into_bgra8();
- Ok(Arc::new(ImageData::new(image)))
- }
- }
- .map_err({
- let uri = uri.clone();
+ if !response.status().is_success() {
+ return Err(Error::BadStatus {
+ status: response.status(),
+ body: String::from_utf8_lossy(&body).into_owned(),
+ });
+ }
- move |error| {
- log::log!(log::Level::Error, "{:?} {:?}", &uri, &error);
- error
- }
- })
- .boxed()
- .shared();
+ let format = image::guess_format(&body)?;
+ let image = image::load_from_memory_with_format(&body, format)?
+ .into_bgra8();
+ Ok(Arc::new(ImageData::new(image)))
+ }
+ }
+ .map_err({
+ let uri = uri.clone();
+ move |error| {
+ log::log!(log::Level::Error, "{:?} {:?}", &uri, &error);
+ error
+ }
+ }),
+ )
+ .shared();
images.insert(uri, future.clone());
future
diff --git a/crates/gpui/src/interactive.rs b/crates/gpui/src/interactive.rs
index 4faa92ce043ae32e6e795b296daeb9ad281475f1..1bc32717c4b7702ca37ee413092d13e12fc6114f 100644
--- a/crates/gpui/src/interactive.rs
+++ b/crates/gpui/src/interactive.rs
@@ -343,7 +343,7 @@ impl ExternalPaths {
impl Render for ExternalPaths {
fn render(&mut self, _: &mut ViewContext) -> impl IntoElement {
- () // Intentionally left empty because the platform will render icons for the dragged files
+ // Intentionally left empty because the platform will render icons for the dragged files
}
}
diff --git a/crates/gpui/src/key_dispatch.rs b/crates/gpui/src/key_dispatch.rs
index dd5a7ab84e25e267012de5e9dfb216404b8dfa7d..c6a2e1788453cc1a5c654f0e7c2f4fef70cfabb4 100644
--- a/crates/gpui/src/key_dispatch.rs
+++ b/crates/gpui/src/key_dispatch.rs
@@ -54,15 +54,24 @@ use crate::{
KeyContext, Keymap, KeymatchResult, Keystroke, KeystrokeMatcher, WindowContext,
};
use collections::FxHashMap;
-use parking_lot::Mutex;
use smallvec::{smallvec, SmallVec};
use std::{
any::{Any, TypeId},
+ cell::RefCell,
mem,
rc::Rc,
- sync::Arc,
};
+/// KeymatchMode controls how keybindings are resolved in the case of conflicting pending keystrokes.
+/// When `Sequenced`, gpui will wait for 1s for sequences to complete.
+/// When `Immediate`, gpui will immediately resolve the keybinding.
+#[derive(Default, PartialEq)]
+pub enum KeymatchMode {
+ #[default]
+ Sequenced,
+ Immediate,
+}
+
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub(crate) struct DispatchNodeId(usize);
@@ -73,8 +82,9 @@ pub(crate) struct DispatchTree {
focusable_node_ids: FxHashMap,
view_node_ids: FxHashMap,
keystroke_matchers: FxHashMap, KeystrokeMatcher>,
- keymap: Arc>,
+ keymap: Rc>,
action_registry: Rc,
+ pub(crate) keymatch_mode: KeymatchMode,
}
#[derive(Default)]
@@ -96,7 +106,7 @@ pub(crate) struct DispatchActionListener {
}
impl DispatchTree {
- pub fn new(keymap: Arc