From fc4b7e2a2a35b856f240bdb632d673a30d2aa76b Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 26 Apr 2021 21:52:18 -0600 Subject: [PATCH] Introduce MouseEventHandler Still need to give elements the ability to re-render their parent view. Once that is in place, I think we can implement hoverable close tab buttons. --- gpui/src/app.rs | 13 ++- gpui/src/elements/mod.rs | 1 + gpui/src/elements/mouse_event_handler.rs | 118 +++++++++++++++++++++++ gpui/src/platform/event.rs | 3 + gpui/src/platform/mac/event.rs | 6 ++ 5 files changed, 138 insertions(+), 3 deletions(-) create mode 100644 gpui/src/elements/mouse_event_handler.rs diff --git a/gpui/src/app.rs b/gpui/src/app.rs index 5352b94c0772cab859c60d6664a7544104dfa51d..425f940e6ab3380283a7371a27fca5985af0e25a 100644 --- a/gpui/src/app.rs +++ b/gpui/src/app.rs @@ -866,7 +866,8 @@ impl MutableAppContext { fn remove_dropped_entities(&mut self) { loop { - let (dropped_models, dropped_views, dropped_values) = self.ctx.ref_counts.lock().take_dropped(); + let (dropped_models, dropped_views, dropped_values) = + self.ctx.ref_counts.lock().take_dropped(); if dropped_models.is_empty() && dropped_views.is_empty() && dropped_values.is_empty() { break; } @@ -1361,7 +1362,7 @@ impl AppContext { &self.thread_pool } - pub fn value(&self, id: usize) -> ValueHandle { + pub fn value(&self, id: usize) -> ValueHandle { let key = (TypeId::of::(), id); let mut values = self.values.lock(); values.entry(key).or_insert_with(|| Box::new(T::default())); @@ -2439,7 +2440,13 @@ impl RefCounts { } } - fn take_dropped(&mut self) -> (HashSet, HashSet<(usize, usize)>, HashSet<(TypeId, usize)>) { + fn take_dropped( + &mut self, + ) -> ( + HashSet, + HashSet<(usize, usize)>, + HashSet<(TypeId, usize)>, + ) { let mut dropped_models = HashSet::new(); let mut dropped_views = HashSet::new(); let mut dropped_values = HashSet::new(); diff --git a/gpui/src/elements/mod.rs b/gpui/src/elements/mod.rs index 1bcfa7f6fa58a02e62c63beac0ad5c9d696e2e9e..4564acaed3f009fb15860138b6ca96a91cb331db 100644 --- a/gpui/src/elements/mod.rs +++ b/gpui/src/elements/mod.rs @@ -7,6 +7,7 @@ mod event_handler; mod flex; mod label; mod line_box; +mod mouse_event_handler; mod new; mod stack; mod svg; diff --git a/gpui/src/elements/mouse_event_handler.rs b/gpui/src/elements/mouse_event_handler.rs new file mode 100644 index 0000000000000000000000000000000000000000..6991d9eea31d55d5a0b97d473ac0161daa90a018 --- /dev/null +++ b/gpui/src/elements/mouse_event_handler.rs @@ -0,0 +1,118 @@ +use crate::{ + geometry::{rect::RectF, vector::Vector2F}, + AfterLayoutContext, AppContext, DebugContext, Element, ElementBox, Event, EventContext, + LayoutContext, PaintContext, SizeConstraint, ValueHandle, +}; +use serde_json::json; + +pub struct MouseEventHandler { + state: ValueHandle, + child: ElementBox, +} + +#[derive(Clone, Copy, Default)] +pub struct MouseState { + hovered: bool, + clicked: bool, +} + +impl MouseEventHandler { + pub fn new( + id: usize, + ctx: &AppContext, + render_child: impl FnOnce(MouseState) -> ElementBox, + ) -> Self { + let state = ctx.value::(id); + let child = state.map(ctx, |state| render_child(*state)); + Self { state, child } + } +} + +impl Element for MouseEventHandler { + type LayoutState = (); + + type PaintState = (); + + fn layout( + &mut self, + constraint: SizeConstraint, + ctx: &mut LayoutContext, + ) -> (Vector2F, Self::LayoutState) { + (self.child.layout(constraint, ctx), ()) + } + + fn after_layout( + &mut self, + _: Vector2F, + _: &mut Self::LayoutState, + ctx: &mut AfterLayoutContext, + ) { + self.child.after_layout(ctx); + } + + fn paint( + &mut self, + bounds: RectF, + _: &mut Self::LayoutState, + ctx: &mut PaintContext, + ) -> Self::PaintState { + self.child.paint(bounds.origin(), ctx); + } + + fn dispatch_event( + &mut self, + event: &Event, + bounds: RectF, + _: &mut Self::LayoutState, + _: &mut Self::PaintState, + ctx: &mut EventContext, + ) -> bool { + self.state.map(ctx.app, |state| match event { + Event::MouseMoved { position } => { + let mouse_in = bounds.contains_point(*position); + if state.hovered != mouse_in { + state.hovered = mouse_in; + log::info!("hovered {}", state.hovered); + // ctx.notify(); + true + } else { + false + } + } + Event::LeftMouseDown { position, .. } => { + if bounds.contains_point(*position) { + log::info!("clicked"); + state.clicked = true; + // ctx.notify(); + true + } else { + false + } + } + Event::LeftMouseUp { .. } => { + if state.clicked { + log::info!("unclicked"); + state.clicked = false; + // ctx.notify(); + true + } else { + false + } + } + _ => false, + }) + } + + fn debug( + &self, + _: RectF, + _: &Self::LayoutState, + _: &Self::PaintState, + ctx: &DebugContext, + ) -> serde_json::Value { + json!({ + "type": "MouseEventHandler", + "child": self.child.debug(ctx), + }) + } +} diff --git a/gpui/src/platform/event.rs b/gpui/src/platform/event.rs index 6a60bd31203004c8b30b95839a9889423c323701..2020a92aec3eb7b415522cc6cfc2666d3c88d551 100644 --- a/gpui/src/platform/event.rs +++ b/gpui/src/platform/event.rs @@ -21,4 +21,7 @@ pub enum Event { LeftMouseDragged { position: Vector2F, }, + MouseMoved { + position: Vector2F, + }, } diff --git a/gpui/src/platform/mac/event.rs b/gpui/src/platform/mac/event.rs index 694c160784ad7d9fece3b4c4575e396074b854c9..b6c27960f0305684a602b432d6c5e3fed69bd5e9 100644 --- a/gpui/src/platform/mac/event.rs +++ b/gpui/src/platform/mac/event.rs @@ -108,6 +108,12 @@ impl Event { ), precise: native_event.hasPreciseScrollingDeltas() == YES, }), + NSEventType::NSMouseMoved => window_height.map(|window_height| Self::MouseMoved { + position: vec2f( + native_event.locationInWindow().x as f32, + window_height - native_event.locationInWindow().y as f32, + ), + }), _ => None, } }