Detailed changes
@@ -47,7 +47,7 @@ impl<V: 'static> gpui::Element<V> for AdapterElement<V> {
let (layout_engine, layout_id) = layout_data.take().unwrap();
legacy_cx.push_layout_engine(layout_engine);
let mut cx = PaintContext::new(legacy_cx, scene);
- self.0.paint(view, layout_id, &mut cx);
+ self.0.paint(view, &mut cx);
*layout_data = legacy_cx.pop_layout_engine().zip(Some(layout_id));
debug_assert!(layout_data.is_some());
}
@@ -1,22 +1,24 @@
use crate::{
element::{AnyElement, Element, Layout, ParentElement},
+ interactive::{InteractionHandlers, Interactive},
layout_context::LayoutContext,
paint_context::PaintContext,
style::{Style, StyleHelpers, StyleRefinement, Styleable},
};
use anyhow::Result;
-use gpui::{platform::MouseMovedEvent, EventContext, LayoutId};
+use gpui::LayoutId;
use smallvec::SmallVec;
-use std::rc::Rc;
-pub struct Div<V> {
+pub struct Div<V: 'static> {
style: StyleRefinement,
+ handlers: InteractionHandlers<V>,
children: SmallVec<[AnyElement<V>; 2]>,
}
pub fn div<V>() -> Div<V> {
Div {
style: Default::default(),
+ handlers: Default::default(),
children: Default::default(),
}
}
@@ -44,45 +46,34 @@ impl<V: 'static> Element<V> for Div<V> {
let style = self.style();
style.paint_background::<V, Self>(layout, cx);
+ for child in &mut self.children {
+ child.paint(view, cx);
+ }
}
}
impl<V> Styleable for Div<V> {
type Style = Style;
- fn declared_style(&mut self) -> &mut crate::style::StyleRefinement {
+ fn declared_style(&mut self) -> &mut StyleRefinement {
&mut self.style
}
}
impl<V> StyleHelpers for Div<V> {}
-impl<V: 'static> ParentElement<V> for Div<V> {
- fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
- &mut self.children
+impl<V> Interactive<V> for Div<V> {
+ fn interaction_handlers(&mut self) -> &mut InteractionHandlers<V> {
+ &mut self.handlers
}
}
-pub trait Interactive<V> {
- fn declared_interactions(&mut self) -> &mut Interactions<V>;
-
- fn on_mouse_move<H>(mut self, handler: H) -> Self
- where
- H: 'static + Fn(&mut V, &MouseMovedEvent, &mut EventContext<V>),
- Self: Sized,
- {
- self.declared_interactions().mouse_moved = Some(Rc::new(move |view, event, cx| {
- handler(view, event, cx);
- cx.bubble
- }));
- self
+impl<V: 'static> ParentElement<V> for Div<V> {
+ fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
+ &mut self.children
}
}
-pub struct Interactions<V> {
- mouse_moved: Option<Rc<dyn Fn(&mut V, &MouseMovedEvent, &mut EventContext<V>) -> bool>>,
-}
-
#[test]
fn test() {
// let elt = div().w_auto();
@@ -10,48 +10,6 @@ pub use crate::paint_context::PaintContext;
type LayoutId = gpui::LayoutId;
-#[derive(Deref, DerefMut)]
-pub struct Layout<V, D> {
- id: LayoutId,
- engine_layout: Option<EngineLayout>,
- #[deref]
- #[deref_mut]
- element_data: D,
- view_type: PhantomData<V>,
-}
-
-impl<V: 'static, D> Layout<V, D> {
- pub fn new(id: LayoutId, element_data: D) -> Self {
- Self {
- id,
- engine_layout: None,
- element_data: element_data,
- view_type: PhantomData,
- }
- }
-
- pub fn bounds(&mut self, cx: &mut PaintContext<V>) -> RectF {
- self.engine_layout(cx).bounds
- }
-
- pub fn order(&mut self, cx: &mut PaintContext<V>) -> u32 {
- self.engine_layout(cx).order
- }
-
- fn engine_layout(&mut self, cx: &mut PaintContext<'_, '_, '_, '_, V>) -> &mut EngineLayout {
- self.engine_layout
- .get_or_insert_with(|| cx.computed_layout(self.id).log_err().unwrap_or_default())
- }
-}
-
-impl<V: 'static> Layout<V, Option<AnyElement<V>>> {
- pub fn paint(&mut self, view: &mut V, cx: &mut PaintContext<V>) {
- let mut element = self.element_data.take().unwrap();
- element.paint(view, self.id, cx);
- self.element_data = Some(element);
- }
-}
-
pub trait Element<V: 'static>: 'static {
type Layout;
@@ -85,7 +43,7 @@ pub trait Element<V: 'static>: 'static {
/// Used to make ElementState<V, E> into a trait object, so we can wrap it in AnyElement<V>.
trait ElementStateObject<V> {
fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<LayoutId>;
- fn paint(&mut self, view: &mut V, layout_id: LayoutId, cx: &mut PaintContext<V>);
+ fn paint(&mut self, view: &mut V, cx: &mut PaintContext<V>);
}
/// A wrapper around an element that stores its layout state.
@@ -103,10 +61,10 @@ impl<V, E: Element<V>> ElementStateObject<V> for ElementState<V, E> {
Ok(layout_id)
}
- fn paint(&mut self, view: &mut V, layout_id: LayoutId, cx: &mut PaintContext<V>) {
+ fn paint(&mut self, view: &mut V, cx: &mut PaintContext<V>) {
let layout = self.layout.as_mut().expect("paint called before layout");
if layout.engine_layout.is_none() {
- layout.engine_layout = cx.computed_layout(layout_id).log_err()
+ layout.engine_layout = cx.computed_layout(layout.id).log_err()
}
self.element.paint(view, layout, cx)
}
@@ -120,8 +78,50 @@ impl<V> AnyElement<V> {
self.0.layout(view, cx)
}
- pub fn paint(&mut self, view: &mut V, layout_id: LayoutId, cx: &mut PaintContext<V>) {
- self.0.paint(view, layout_id, cx)
+ pub fn paint(&mut self, view: &mut V, cx: &mut PaintContext<V>) {
+ self.0.paint(view, cx)
+ }
+}
+
+#[derive(Deref, DerefMut)]
+pub struct Layout<V, D> {
+ id: LayoutId,
+ engine_layout: Option<EngineLayout>,
+ #[deref]
+ #[deref_mut]
+ element_data: D,
+ view_type: PhantomData<V>,
+}
+
+impl<V: 'static, D> Layout<V, D> {
+ pub fn new(id: LayoutId, element_data: D) -> Self {
+ Self {
+ id,
+ engine_layout: None,
+ element_data: element_data,
+ view_type: PhantomData,
+ }
+ }
+
+ pub fn bounds(&mut self, cx: &mut PaintContext<V>) -> RectF {
+ self.engine_layout(cx).bounds
+ }
+
+ pub fn order(&mut self, cx: &mut PaintContext<V>) -> u32 {
+ self.engine_layout(cx).order
+ }
+
+ fn engine_layout(&mut self, cx: &mut PaintContext<'_, '_, '_, '_, V>) -> &mut EngineLayout {
+ self.engine_layout
+ .get_or_insert_with(|| cx.computed_layout(self.id).log_err().unwrap_or_default())
+ }
+}
+
+impl<V: 'static> Layout<V, Option<AnyElement<V>>> {
+ pub fn paint(&mut self, view: &mut V, cx: &mut PaintContext<V>) {
+ let mut element = self.element_data.take().unwrap();
+ element.paint(view, cx);
+ self.element_data = Some(element);
}
}
@@ -42,14 +42,6 @@ impl<V: 'static, E: Element<V> + Styleable> Element<V> for Hoverable<V, E> {
where
Self: Sized,
{
- if self.hovered.get() {
- // If hovered, refine the child's style with this element's style.
- self.child.declared_style().refine(&self.hovered_style);
- } else {
- // Otherwise, set the child's style back to its original style.
- *self.child.declared_style() = self.child_style.clone();
- }
-
self.child.layout(view, cx)
}
@@ -61,10 +53,24 @@ impl<V: 'static, E: Element<V> + Styleable> Element<V> for Hoverable<V, E> {
) where
Self: Sized,
{
+ if self.hovered.get() {
+ // If hovered, refine the child's style with this element's style.
+ self.child.declared_style().refine(&self.hovered_style);
+ } else {
+ // Otherwise, set the child's style back to its original style.
+ *self.child.declared_style() = self.child_style.clone();
+ }
+
let bounds = layout.bounds(cx);
let order = layout.order(cx);
self.hovered.set(bounds.contains_point(cx.mouse_position()));
- let hovered = self.hovered.clone();
- cx.on_event(order, move |view, event: &MouseMovedEvent, cx| {});
+ let was_hovered = self.hovered.clone();
+ cx.on_event(order, move |view, event: &MouseMovedEvent, cx| {
+ let is_hovered = bounds.contains_point(event.position);
+ if is_hovered != was_hovered.get() {
+ was_hovered.set(is_hovered);
+ cx.repaint();
+ }
+ });
}
}
@@ -0,0 +1,34 @@
+use gpui::{platform::MouseMovedEvent, EventContext};
+use smallvec::SmallVec;
+use std::rc::Rc;
+
+pub trait Interactive<V: 'static> {
+ fn interaction_handlers(&mut self) -> &mut InteractionHandlers<V>;
+
+ fn on_mouse_move<H>(mut self, handler: H) -> Self
+ where
+ H: 'static + Fn(&mut V, &MouseMovedEvent, bool, &mut EventContext<V>),
+ Self: Sized,
+ {
+ self.interaction_handlers()
+ .mouse_moved
+ .push(Rc::new(move |view, event, hit_test, cx| {
+ handler(view, event, hit_test, cx);
+ cx.bubble
+ }));
+ self
+ }
+}
+
+pub struct InteractionHandlers<V: 'static> {
+ mouse_moved:
+ SmallVec<[Rc<dyn Fn(&mut V, &MouseMovedEvent, bool, &mut EventContext<V>) -> bool>; 2]>,
+}
+
+impl<V> Default for InteractionHandlers<V> {
+ fn default() -> Self {
+ Self {
+ mouse_moved: Default::default(),
+ }
+ }
+}
@@ -1,9 +1,6 @@
use anyhow::{anyhow, Result};
use derive_more::{Deref, DerefMut};
-use gpui::{
- geometry::rect::RectF, scene::EventHandler, EngineLayout, EventContext, LayoutId,
- RenderContext, ViewContext,
-};
+use gpui::{scene::EventHandler, EngineLayout, EventContext, LayoutId, RenderContext, ViewContext};
pub use gpui::{LayoutContext, PaintContext as LegacyPaintContext};
use std::{any::TypeId, rc::Rc};
pub use taffy::tree::NodeId;
@@ -47,48 +44,25 @@ impl<'a, 'b, 'c, 'd, V: 'static> PaintContext<'a, 'b, 'c, 'd, V> {
order: u32,
handler: impl Fn(&mut V, &E, &mut ViewContext<V>) + 'static,
) {
+ let view = self.weak_handle();
+
self.scene.event_handlers.push(EventHandler {
order,
- handler: Rc::new(move |view, event, window_cx, view_id| {
- let mut view_context = ViewContext::mutable(window_cx, view_id);
- handler(
- view.downcast_mut().unwrap(),
- event.downcast_ref().unwrap(),
- &mut view_context,
- );
+ handler: Rc::new(move |event, window_cx| {
+ if let Some(view) = view.upgrade(window_cx) {
+ view.update(window_cx, |view, view_cx| {
+ let mut event_cx = EventContext::new(view_cx);
+ handler(view, event.downcast_ref().unwrap(), &mut event_cx);
+ event_cx.bubble
+ })
+ } else {
+ true
+ }
}),
event_type: TypeId::of::<E>(),
})
}
- pub fn draw_interactive_region<E: 'static>(
- &mut self,
- order: u32,
- bounds: RectF,
- outside_bounds: bool,
- event_handler: impl Fn(&mut V, &E, &mut EventContext<V>) + 'static,
- ) {
- // We'll sort these later when `take_interactive_regions` is called.
- self.scene
- .interactive_regions
- .push(gpui::scene::InteractiveRegion {
- order,
- bounds,
- outside_bounds,
- event_handler: Rc::new(move |view, event, window_cx, view_id| {
- let mut view_context = ViewContext::mutable(window_cx, view_id);
- let mut event_context = EventContext::new(&mut view_context);
- event_handler(
- view.downcast_mut().unwrap(),
- event.downcast_ref().unwrap(),
- &mut event_context,
- );
- }),
- event_type: TypeId::of::<E>(),
- view_id: self.view_id(),
- });
- }
-
pub(crate) fn computed_layout(&mut self, layout_id: LayoutId) -> Result<EngineLayout> {
self.layout_engine()
.ok_or_else(|| anyhow!("no layout engine present"))?
@@ -16,6 +16,7 @@ mod components;
mod div;
mod element;
mod hoverable;
+mod interactive;
mod layout_context;
mod paint_context;
mod style;
@@ -4680,18 +4680,37 @@ impl<V: 'static> WeakViewHandle<V> {
})
}
- pub fn update<T>(
+ pub fn update<T, B>(
&self,
- cx: &mut AsyncAppContext,
+ cx: &mut B,
update: impl FnOnce(&mut V, &mut ViewContext<V>) -> T,
- ) -> Result<T> {
- cx.update(|cx| {
- let handle = cx
- .upgrade_view_handle(self)
- .ok_or_else(|| anyhow!("view was dropped"))?;
- cx.update_window(self.window, |cx| handle.update(cx, update))
- .ok_or_else(|| anyhow!("window was removed"))
+ ) -> Result<T>
+ where
+ B: BorrowWindowContext,
+ B::Result<Option<T>>: Flatten<T>,
+ {
+ cx.update_window(self.window(), |cx| {
+ cx.upgrade_view_handle(self)
+ .map(|handle| handle.update(cx, update))
})
+ .flatten()
+ .ok_or_else(|| anyhow!("window was removed"))
+ }
+}
+
+pub trait Flatten<T> {
+ fn flatten(self) -> Option<T>;
+}
+
+impl<T> Flatten<T> for Option<Option<T>> {
+ fn flatten(self) -> Option<T> {
+ self.flatten()
+ }
+}
+
+impl<T> Flatten<T> for Option<T> {
+ fn flatten(self) -> Option<T> {
+ self
}
}
@@ -8,9 +8,9 @@ use crate::{
MouseButton, MouseMovedEvent, PromptLevel, WindowBounds,
},
scene::{
- CursorRegion, InteractiveRegion, MouseClick, MouseClickOut, MouseDown, MouseDownOut,
- MouseDrag, MouseEvent, MouseHover, MouseMove, MouseMoveOut, MouseScrollWheel, MouseUp,
- MouseUpOut, Scene,
+ CursorRegion, EventHandler, MouseClick, MouseClickOut, MouseDown, MouseDownOut, MouseDrag,
+ MouseEvent, MouseHover, MouseMove, MouseMoveOut, MouseScrollWheel, MouseUp, MouseUpOut,
+ Scene,
},
text_layout::TextLayoutCache,
util::post_inc,
@@ -57,7 +57,7 @@ pub struct Window {
appearance: Appearance,
cursor_regions: Vec<CursorRegion>,
mouse_regions: Vec<(MouseRegion, usize)>,
- interactive_regions: Vec<InteractiveRegion>,
+ event_handlers: Vec<EventHandler>,
last_mouse_moved_event: Option<Event>,
pub(crate) hovered_region_ids: Vec<MouseRegionId>,
pub(crate) clicked_region_ids: Vec<MouseRegionId>,
@@ -91,7 +91,7 @@ impl Window {
rendered_views: Default::default(),
cursor_regions: Default::default(),
mouse_regions: Default::default(),
- interactive_regions: Vec::new(),
+ event_handlers: Default::default(),
text_layout_cache: TextLayoutCache::new(cx.font_system.clone()),
last_mouse_moved_event: None,
hovered_region_ids: Default::default(),
@@ -119,8 +119,8 @@ impl Window {
.expect("root_view called during window construction")
}
- pub fn take_interactive_regions(&mut self) -> Vec<InteractiveRegion> {
- mem::take(&mut self.interactive_regions)
+ pub fn take_event_handlers(&mut self) -> Vec<EventHandler> {
+ mem::take(&mut self.event_handlers)
}
}
@@ -889,26 +889,13 @@ impl<'a> WindowContext<'a> {
fn dispatch_to_interactive_regions(&mut self, event: &Event) {
if let Some(mouse_event) = event.mouse_event() {
let mouse_position = event.position().expect("mouse events must have a position");
- let interactive_regions = self.window.take_interactive_regions();
-
- for region in interactive_regions.iter().rev() {
- if region.event_type == mouse_event.type_id() {
- let in_bounds = region.bounds.contains_point(mouse_position);
-
- if in_bounds == !region.outside_bounds {
- self.update_any_view(region.view_id, |view, window_cx| {
- (region.event_handler)(
- view.as_any_mut(),
- mouse_event,
- window_cx,
- region.view_id,
- );
- });
- }
+ let event_handlers = self.window.take_event_handlers();
+ for event_handler in event_handlers.iter().rev() {
+ if event_handler.event_type == mouse_event.type_id() {
+ (event_handler.handler)(mouse_event, self);
}
}
-
- self.window.interactive_regions = interactive_regions;
+ self.window.event_handlers = event_handlers;
}
}
@@ -1066,7 +1053,7 @@ impl<'a> WindowContext<'a> {
let mut scene = scene_builder.build();
self.window.cursor_regions = scene.cursor_regions();
self.window.mouse_regions = scene.mouse_regions();
- self.window.interactive_regions = scene.take_interactive_regions();
+ self.window.event_handlers = scene.take_event_handlers();
if self.window_is_active() {
if let Some(event) = self.window.last_mouse_moved_event.clone() {
@@ -31,8 +31,6 @@ pub struct SceneBuilder {
scale_factor: f32,
stacking_contexts: Vec<StackingContext>,
active_stacking_context_stack: Vec<usize>,
- /// Used by the playground crate. I hope to replace it with event_handlers.
- pub interactive_regions: Vec<InteractiveRegion>,
/// Used by the playground crate.
pub event_handlers: Vec<EventHandler>,
#[cfg(debug_assertions)]
@@ -42,7 +40,6 @@ pub struct SceneBuilder {
pub struct Scene {
scale_factor: f32,
stacking_contexts: Vec<StackingContext>,
- interactive_regions: Vec<InteractiveRegion>,
event_handlers: Vec<EventHandler>,
}
@@ -285,15 +282,9 @@ impl Scene {
.collect()
}
- /// TODO: Hoping to replace this with take_event_handlers
- pub fn take_interactive_regions(&mut self) -> Vec<InteractiveRegion> {
- self.interactive_regions
- .sort_by(|a, b| a.order.cmp(&b.order));
- std::mem::take(&mut self.interactive_regions)
- }
-
pub fn take_event_handlers(&mut self) -> Vec<EventHandler> {
- self.event_handlers.sort_by(|a, b| a.order.cmp(&b.order));
+ self.event_handlers
+ .sort_by(|a, b| a.order.cmp(&b.order).reverse());
std::mem::take(&mut self.event_handlers)
}
}
@@ -307,7 +298,6 @@ impl SceneBuilder {
active_stacking_context_stack: vec![0],
#[cfg(debug_assertions)]
mouse_region_ids: Default::default(),
- interactive_regions: Vec::new(),
event_handlers: Vec::new(),
}
}
@@ -318,7 +308,6 @@ impl SceneBuilder {
Scene {
scale_factor: self.scale_factor,
stacking_contexts: self.stacking_contexts,
- interactive_regions: self.interactive_regions,
event_handlers: self.event_handlers,
}
}
@@ -716,23 +705,10 @@ impl MouseRegion {
}
}
-/// This is currently only used in the playground crate. It represents a region
-/// with which the user can interact via a pointing device. It aims to replace
-/// MouseRegion and CursorRegion.
-pub struct InteractiveRegion {
- pub order: u32,
- pub bounds: RectF,
- pub outside_bounds: bool,
- pub event_handler: Rc<dyn Fn(&mut dyn Any, &dyn Any, &mut WindowContext, usize)>,
- pub event_type: TypeId,
- pub view_id: usize,
-}
-
pub struct EventHandler {
pub order: u32,
- // First param is a dynamic view reference
- // Second param is a dynamic event reference
- pub handler: Rc<dyn Fn(&mut dyn Any, &dyn Any, &mut WindowContext, usize)>,
+ // The &dyn Any parameter below expects an event.
+ pub handler: Rc<dyn Fn(&dyn Any, &mut WindowContext) -> bool>,
pub event_type: TypeId,
}