From 733df38f9b3aaeffc74ee70eb655614d711dd3d3 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 22 Aug 2023 10:42:26 -0600 Subject: [PATCH] Checkpoint Co-Authored-By: Antonio Scandurra --- crates/gpui/playground/src/adapter.rs | 2 +- crates/gpui/playground/src/div.rs | 39 ++++----- crates/gpui/playground/src/element.rs | 94 ++++++++++----------- crates/gpui/playground/src/hoverable.rs | 26 +++--- crates/gpui/playground/src/interactive.rs | 34 ++++++++ crates/gpui/playground/src/paint_context.rs | 52 +++--------- crates/gpui/playground/src/playground.rs | 1 + crates/gpui/src/app.rs | 37 ++++++-- crates/gpui/src/app/window.rs | 39 +++------ crates/gpui/src/scene.rs | 32 +------ 10 files changed, 172 insertions(+), 184 deletions(-) create mode 100644 crates/gpui/playground/src/interactive.rs diff --git a/crates/gpui/playground/src/adapter.rs b/crates/gpui/playground/src/adapter.rs index 392186e295d53dfc5fac5795926681dbd2aa98fc..7a5a0399a81224ac2a9032f791b55764d9d76f68 100644 --- a/crates/gpui/playground/src/adapter.rs +++ b/crates/gpui/playground/src/adapter.rs @@ -47,7 +47,7 @@ impl gpui::Element for AdapterElement { 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()); } diff --git a/crates/gpui/playground/src/div.rs b/crates/gpui/playground/src/div.rs index 37bd69376de43e356af010677fa89a9d46b63776..8efe3590258a4249abd9bb69a359f95692b6a6a0 100644 --- a/crates/gpui/playground/src/div.rs +++ b/crates/gpui/playground/src/div.rs @@ -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 { +pub struct Div { style: StyleRefinement, + handlers: InteractionHandlers, children: SmallVec<[AnyElement; 2]>, } pub fn div() -> Div { Div { style: Default::default(), + handlers: Default::default(), children: Default::default(), } } @@ -44,45 +46,34 @@ impl Element for Div { let style = self.style(); style.paint_background::(layout, cx); + for child in &mut self.children { + child.paint(view, cx); + } } } impl Styleable for Div { type Style = Style; - fn declared_style(&mut self) -> &mut crate::style::StyleRefinement { + fn declared_style(&mut self) -> &mut StyleRefinement { &mut self.style } } impl StyleHelpers for Div {} -impl ParentElement for Div { - fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> { - &mut self.children +impl Interactive for Div { + fn interaction_handlers(&mut self) -> &mut InteractionHandlers { + &mut self.handlers } } -pub trait Interactive { - fn declared_interactions(&mut self) -> &mut Interactions; - - fn on_mouse_move(mut self, handler: H) -> Self - where - H: 'static + Fn(&mut V, &MouseMovedEvent, &mut EventContext), - Self: Sized, - { - self.declared_interactions().mouse_moved = Some(Rc::new(move |view, event, cx| { - handler(view, event, cx); - cx.bubble - })); - self +impl ParentElement for Div { + fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> { + &mut self.children } } -pub struct Interactions { - mouse_moved: Option) -> bool>>, -} - #[test] fn test() { // let elt = div().w_auto(); diff --git a/crates/gpui/playground/src/element.rs b/crates/gpui/playground/src/element.rs index 24ad7bcfdac04aac1250f60ea2bfcdcfc29a23c6..082f3b02b06522f3c12b435e6d02febd4a319390 100644 --- a/crates/gpui/playground/src/element.rs +++ b/crates/gpui/playground/src/element.rs @@ -10,48 +10,6 @@ pub use crate::paint_context::PaintContext; type LayoutId = gpui::LayoutId; -#[derive(Deref, DerefMut)] -pub struct Layout { - id: LayoutId, - engine_layout: Option, - #[deref] - #[deref_mut] - element_data: D, - view_type: PhantomData, -} - -impl Layout { - 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) -> RectF { - self.engine_layout(cx).bounds - } - - pub fn order(&mut self, cx: &mut PaintContext) -> 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 Layout>> { - pub fn paint(&mut self, view: &mut V, cx: &mut PaintContext) { - let mut element = self.element_data.take().unwrap(); - element.paint(view, self.id, cx); - self.element_data = Some(element); - } -} - pub trait Element: 'static { type Layout; @@ -85,7 +43,7 @@ pub trait Element: 'static { /// Used to make ElementState into a trait object, so we can wrap it in AnyElement. trait ElementStateObject { fn layout(&mut self, view: &mut V, cx: &mut LayoutContext) -> Result; - fn paint(&mut self, view: &mut V, layout_id: LayoutId, cx: &mut PaintContext); + fn paint(&mut self, view: &mut V, cx: &mut PaintContext); } /// A wrapper around an element that stores its layout state. @@ -103,10 +61,10 @@ impl> ElementStateObject for ElementState { Ok(layout_id) } - fn paint(&mut self, view: &mut V, layout_id: LayoutId, cx: &mut PaintContext) { + fn paint(&mut self, view: &mut V, cx: &mut PaintContext) { 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 AnyElement { self.0.layout(view, cx) } - pub fn paint(&mut self, view: &mut V, layout_id: LayoutId, cx: &mut PaintContext) { - self.0.paint(view, layout_id, cx) + pub fn paint(&mut self, view: &mut V, cx: &mut PaintContext) { + self.0.paint(view, cx) + } +} + +#[derive(Deref, DerefMut)] +pub struct Layout { + id: LayoutId, + engine_layout: Option, + #[deref] + #[deref_mut] + element_data: D, + view_type: PhantomData, +} + +impl Layout { + 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) -> RectF { + self.engine_layout(cx).bounds + } + + pub fn order(&mut self, cx: &mut PaintContext) -> 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 Layout>> { + pub fn paint(&mut self, view: &mut V, cx: &mut PaintContext) { + let mut element = self.element_data.take().unwrap(); + element.paint(view, cx); + self.element_data = Some(element); } } diff --git a/crates/gpui/playground/src/hoverable.rs b/crates/gpui/playground/src/hoverable.rs index 5335850870b7685ad6f42e026764e8f82f35f448..5545155a60c4e55e15bc534843c76a0e6f543d36 100644 --- a/crates/gpui/playground/src/hoverable.rs +++ b/crates/gpui/playground/src/hoverable.rs @@ -42,14 +42,6 @@ impl + Styleable> Element for Hoverable { 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 + Styleable> Element for Hoverable { ) 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(); + } + }); } } diff --git a/crates/gpui/playground/src/interactive.rs b/crates/gpui/playground/src/interactive.rs new file mode 100644 index 0000000000000000000000000000000000000000..8debcb1692cc5bcd42a8e65e82aa27ad6a02d4a0 --- /dev/null +++ b/crates/gpui/playground/src/interactive.rs @@ -0,0 +1,34 @@ +use gpui::{platform::MouseMovedEvent, EventContext}; +use smallvec::SmallVec; +use std::rc::Rc; + +pub trait Interactive { + fn interaction_handlers(&mut self) -> &mut InteractionHandlers; + + fn on_mouse_move(mut self, handler: H) -> Self + where + H: 'static + Fn(&mut V, &MouseMovedEvent, bool, &mut EventContext), + 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 { + mouse_moved: + SmallVec<[Rc) -> bool>; 2]>, +} + +impl Default for InteractionHandlers { + fn default() -> Self { + Self { + mouse_moved: Default::default(), + } + } +} diff --git a/crates/gpui/playground/src/paint_context.rs b/crates/gpui/playground/src/paint_context.rs index 59e728b6f7520226376074b4be4804e154e6453c..d853aff7f85ff4b8b434dffb6e99caca9f91d0d2 100644 --- a/crates/gpui/playground/src/paint_context.rs +++ b/crates/gpui/playground/src/paint_context.rs @@ -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) + '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::(), }) } - pub fn draw_interactive_region( - &mut self, - order: u32, - bounds: RectF, - outside_bounds: bool, - event_handler: impl Fn(&mut V, &E, &mut EventContext) + '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::(), - view_id: self.view_id(), - }); - } - pub(crate) fn computed_layout(&mut self, layout_id: LayoutId) -> Result { self.layout_engine() .ok_or_else(|| anyhow!("no layout engine present"))? diff --git a/crates/gpui/playground/src/playground.rs b/crates/gpui/playground/src/playground.rs index 9bb0b9a8a8f3b8c23cc5effbe88cbbd1cc002e67..2462ac99f59e412cdbd16d3b616503dcb7810660 100644 --- a/crates/gpui/playground/src/playground.rs +++ b/crates/gpui/playground/src/playground.rs @@ -16,6 +16,7 @@ mod components; mod div; mod element; mod hoverable; +mod interactive; mod layout_context; mod paint_context; mod style; diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 941e09c1d30f66a3ebeb17fadbfdff35455d34fe..d6a5367e40912ea29be959235da153fd54f00431 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -4680,18 +4680,37 @@ impl WeakViewHandle { }) } - pub fn update( + pub fn update( &self, - cx: &mut AsyncAppContext, + cx: &mut B, update: impl FnOnce(&mut V, &mut ViewContext) -> T, - ) -> Result { - 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 + where + B: BorrowWindowContext, + B::Result>: Flatten, + { + 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 { + fn flatten(self) -> Option; +} + +impl Flatten for Option> { + fn flatten(self) -> Option { + self.flatten() + } +} + +impl Flatten for Option { + fn flatten(self) -> Option { + self } } diff --git a/crates/gpui/src/app/window.rs b/crates/gpui/src/app/window.rs index b3b5017e00bf01cea9a54b7cc3faa089583f05c1..5b815acc899c743fbf98b429fbbfa52d2b9dd99c 100644 --- a/crates/gpui/src/app/window.rs +++ b/crates/gpui/src/app/window.rs @@ -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, mouse_regions: Vec<(MouseRegion, usize)>, - interactive_regions: Vec, + event_handlers: Vec, last_mouse_moved_event: Option, pub(crate) hovered_region_ids: Vec, pub(crate) clicked_region_ids: Vec, @@ -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 { - mem::take(&mut self.interactive_regions) + pub fn take_event_handlers(&mut self) -> Vec { + 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() { diff --git a/crates/gpui/src/scene.rs b/crates/gpui/src/scene.rs index 830ffa857e4fad3f3279cc3a8741f8ceff4f5010..9f326c170807a7661918575bb7b853cefbab9472 100644 --- a/crates/gpui/src/scene.rs +++ b/crates/gpui/src/scene.rs @@ -31,8 +31,6 @@ pub struct SceneBuilder { scale_factor: f32, stacking_contexts: Vec, active_stacking_context_stack: Vec, - /// Used by the playground crate. I hope to replace it with event_handlers. - pub interactive_regions: Vec, /// Used by the playground crate. pub event_handlers: Vec, #[cfg(debug_assertions)] @@ -42,7 +40,6 @@ pub struct SceneBuilder { pub struct Scene { scale_factor: f32, stacking_contexts: Vec, - interactive_regions: Vec, event_handlers: Vec, } @@ -285,15 +282,9 @@ impl Scene { .collect() } - /// TODO: Hoping to replace this with take_event_handlers - pub fn take_interactive_regions(&mut self) -> Vec { - 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 { - 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, - 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, + // The &dyn Any parameter below expects an event. + pub handler: Rc bool>, pub event_type: TypeId, }