Add mouse_state method to RenderContext

Nathan Sobo created

We can use this to determine if a region is hovered or clicked.

Change summary

crates/gpui/src/app.rs       | 112 +++++++++++++++++++++++--------------
crates/gpui/src/presenter.rs |  24 ++++++-
2 files changed, 88 insertions(+), 48 deletions(-)

Detailed changes

crates/gpui/src/app.rs 🔗

@@ -7,7 +7,8 @@ use crate::{
     platform::{self, Platform, PromptLevel, WindowOptions},
     presenter::Presenter,
     util::post_inc,
-    AssetCache, AssetSource, ClipboardItem, FontCache, PathPromptOptions, TextLayoutCache,
+    AssetCache, AssetSource, ClipboardItem, FontCache, MouseRegionId, PathPromptOptions,
+    TextLayoutCache,
 };
 pub use action::*;
 use anyhow::{anyhow, Context, Result};
@@ -1040,19 +1041,15 @@ impl MutableAppContext {
             .map_or(false, |window| window.is_active)
     }
 
-    pub fn render_view(
-        &mut self,
-        window_id: usize,
-        view_id: usize,
-        titlebar_height: f32,
-        refreshing: bool,
-    ) -> Result<ElementBox> {
+    pub fn render_view(&mut self, params: RenderParams) -> Result<ElementBox> {
+        let window_id = params.window_id;
+        let view_id = params.view_id;
         let mut view = self
             .cx
             .views
-            .remove(&(window_id, view_id))
+            .remove(&(params.window_id, params.view_id))
             .ok_or(anyhow!("view not found"))?;
-        let element = view.render(window_id, view_id, titlebar_height, refreshing, self);
+        let element = view.render(params, self);
         self.cx.views.insert((window_id, view_id), view);
         Ok(element)
     }
@@ -1079,8 +1076,15 @@ impl MutableAppContext {
             .map(|view_id| {
                 (
                     view_id,
-                    self.render_view(window_id, view_id, titlebar_height, false)
-                        .unwrap(),
+                    self.render_view(RenderParams {
+                        window_id,
+                        view_id,
+                        titlebar_height,
+                        hovered_region_id: None,
+                        clicked_region_id: None,
+                        refreshing: false,
+                    })
+                    .unwrap(),
                 )
             })
             .collect()
@@ -1757,15 +1761,19 @@ impl MutableAppContext {
         window_id: usize,
         view_id: usize,
         titlebar_height: f32,
+        hovered_region_id: Option<MouseRegionId>,
+        clicked_region_id: Option<MouseRegionId>,
         refreshing: bool,
     ) -> RenderContext<V> {
         RenderContext {
             app: self,
-            titlebar_height,
-            refreshing,
             window_id,
             view_id,
             view_type: PhantomData,
+            titlebar_height,
+            hovered_region_id,
+            clicked_region_id,
+            refreshing,
         }
     }
 
@@ -2894,14 +2902,7 @@ pub trait AnyView {
         cx: &mut MutableAppContext,
     ) -> Option<Pin<Box<dyn 'static + Future<Output = ()>>>>;
     fn ui_name(&self) -> &'static str;
-    fn render<'a>(
-        &mut self,
-        window_id: usize,
-        view_id: usize,
-        titlebar_height: f32,
-        refreshing: bool,
-        cx: &mut MutableAppContext,
-    ) -> ElementBox;
+    fn render<'a>(&mut self, params: RenderParams, cx: &mut MutableAppContext) -> ElementBox;
     fn on_focus(&mut self, cx: &mut MutableAppContext, window_id: usize, view_id: usize);
     fn on_blur(&mut self, cx: &mut MutableAppContext, window_id: usize, view_id: usize);
     fn keymap_context(&self, cx: &AppContext) -> keymap::Context;
@@ -2935,25 +2936,8 @@ where
         T::ui_name()
     }
 
-    fn render<'a>(
-        &mut self,
-        window_id: usize,
-        view_id: usize,
-        titlebar_height: f32,
-        refreshing: bool,
-        cx: &mut MutableAppContext,
-    ) -> ElementBox {
-        View::render(
-            self,
-            &mut RenderContext {
-                window_id,
-                view_id,
-                app: cx,
-                view_type: PhantomData::<T>,
-                titlebar_height,
-                refreshing,
-            },
-        )
+    fn render<'a>(&mut self, params: RenderParams, cx: &mut MutableAppContext) -> ElementBox {
+        View::render(self, &mut RenderContext::new(params, cx))
     }
 
     fn on_focus(&mut self, cx: &mut MutableAppContext, window_id: usize, view_id: usize) {
@@ -3435,16 +3419,46 @@ impl<'a, T: View> ViewContext<'a, T> {
     }
 }
 
-pub struct RenderContext<'a, T: View> {
-    pub app: &'a mut MutableAppContext,
+pub struct RenderParams {
+    pub window_id: usize,
+    pub view_id: usize,
     pub titlebar_height: f32,
+    pub hovered_region_id: Option<MouseRegionId>,
+    pub clicked_region_id: Option<MouseRegionId>,
     pub refreshing: bool,
+}
+
+pub struct RenderContext<'a, T: View> {
+    pub app: &'a mut MutableAppContext,
     window_id: usize,
     view_id: usize,
     view_type: PhantomData<T>,
+    pub titlebar_height: f32,
+    hovered_region_id: Option<MouseRegionId>,
+    clicked_region_id: Option<MouseRegionId>,
+    pub refreshing: bool,
+}
+
+#[derive(Clone, Copy)]
+pub struct MouseState {
+    pub hovered: bool,
+    pub clicked: bool,
 }
 
 impl<'a, T: View> RenderContext<'a, T> {
+    fn new(params: RenderParams, app: &'a mut MutableAppContext) -> Self {
+        Self {
+            app,
+            window_id: params.window_id,
+            view_id: params.view_id,
+            view_type: PhantomData,
+            titlebar_height: params.titlebar_height,
+            hovered_region_id: params.hovered_region_id,
+            clicked_region_id: params.clicked_region_id,
+            refreshing: params.refreshing,
+        }
+    }
+
     pub fn handle(&self) -> WeakViewHandle<T> {
         WeakViewHandle::new(self.window_id, self.view_id)
     }
@@ -3452,6 +3466,18 @@ impl<'a, T: View> RenderContext<'a, T> {
     pub fn view_id(&self) -> usize {
         self.view_id
     }
+
+    pub fn mouse_state<Tag: 'static>(&self, region_id: usize) -> MouseState {
+        let region_id = Some(MouseRegionId {
+            view_id: self.view_id,
+            tag: TypeId::of::<Tag>(),
+            region_id,
+        });
+        MouseState {
+            hovered: self.hovered_region_id == region_id,
+            clicked: self.clicked_region_id == region_id,
+        }
+    }
 }
 
 impl AsRef<AppContext> for &AppContext {

crates/gpui/src/presenter.rs 🔗

@@ -9,8 +9,8 @@ use crate::{
     text_layout::TextLayoutCache,
     Action, AnyModelHandle, AnyViewHandle, AnyWeakModelHandle, AssetCache, ElementBox,
     ElementStateContext, Entity, FontSystem, ModelHandle, MouseRegion, MouseRegionId, ReadModel,
-    ReadView, Scene, UpgradeModelHandle, UpgradeViewHandle, View, ViewHandle, WeakModelHandle,
-    WeakViewHandle,
+    ReadView, RenderParams, Scene, UpgradeModelHandle, UpgradeViewHandle, View, ViewHandle,
+    WeakModelHandle, WeakViewHandle,
 };
 use pathfinder_geometry::vector::{vec2f, Vector2F};
 use serde_json::json;
@@ -93,8 +93,15 @@ impl Presenter {
         for view_id in &invalidation.updated {
             self.rendered_views.insert(
                 *view_id,
-                cx.render_view(self.window_id, *view_id, self.titlebar_height, false)
-                    .unwrap(),
+                cx.render_view(RenderParams {
+                    window_id: self.window_id,
+                    view_id: *view_id,
+                    titlebar_height: self.titlebar_height,
+                    hovered_region_id: self.hovered_region_id,
+                    clicked_region_id: self.clicked_region.as_ref().map(MouseRegion::id),
+                    refreshing: false,
+                })
+                .unwrap(),
             );
         }
     }
@@ -104,7 +111,14 @@ impl Presenter {
         for (view_id, view) in &mut self.rendered_views {
             if !invalidation.updated.contains(view_id) {
                 *view = cx
-                    .render_view(self.window_id, *view_id, self.titlebar_height, true)
+                    .render_view(RenderParams {
+                        window_id: self.window_id,
+                        view_id: *view_id,
+                        titlebar_height: self.titlebar_height,
+                        hovered_region_id: self.hovered_region_id,
+                        clicked_region_id: self.clicked_region.as_ref().map(MouseRegion::id),
+                        refreshing: true,
+                    })
                     .unwrap();
             }
         }