From 569d99a5a1cbdb89c0a87c90940237b33fc91274 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 23 Aug 2023 09:08:05 -0600 Subject: [PATCH] Add hover styling support --- crates/gpui/playground/src/hoverable.rs | 33 ++++++++++++------------ crates/gpui/playground/src/playground.rs | 9 ++++--- crates/gpui/playground/src/style.rs | 8 ++++++ crates/gpui/src/app.rs | 9 ++++++- crates/gpui/src/app/window.rs | 3 ++- crates/gpui/src/platform.rs | 1 + crates/gpui/src/platform/mac/platform.rs | 8 +++++- crates/gpui/src/platform/test.rs | 4 +++ 8 files changed, 53 insertions(+), 22 deletions(-) diff --git a/crates/gpui/playground/src/hoverable.rs b/crates/gpui/playground/src/hoverable.rs index 5545155a60..d2c87506c4 100644 --- a/crates/gpui/playground/src/hoverable.rs +++ b/crates/gpui/playground/src/hoverable.rs @@ -2,40 +2,38 @@ use crate::{ element::{Element, Layout}, layout_context::LayoutContext, paint_context::PaintContext, - style::{StyleRefinement, Styleable}, + style::{Style, StyleHelpers, StyleRefinement, Styleable}, }; use anyhow::Result; use gpui::platform::MouseMovedEvent; use refineable::Refineable; -use std::{cell::Cell, marker::PhantomData}; +use std::cell::Cell; -pub struct Hoverable + Styleable> { +pub struct Hoverable { hovered: Cell, child_style: StyleRefinement, hovered_style: StyleRefinement, child: E, - view_type: PhantomData, } -pub fn hoverable + Styleable>(mut child: E) -> Hoverable { +pub fn hoverable(mut child: E) -> Hoverable { Hoverable { hovered: Cell::new(false), child_style: child.declared_style().clone(), hovered_style: Default::default(), child, - view_type: PhantomData, } } -impl + Styleable> Styleable for Hoverable { +impl Styleable for Hoverable { type Style = E::Style; fn declared_style(&mut self) -> &mut crate::style::StyleRefinement { - self.child.declared_style() + &mut self.hovered_style } } -impl + Styleable> Element for Hoverable { +impl + Styleable> Element for Hoverable { type Layout = E::Layout; fn layout(&mut self, view: &mut V, cx: &mut LayoutContext) -> Result> @@ -53,6 +51,10 @@ impl + Styleable> Element for Hoverable { ) where Self: Sized, { + let bounds = layout.bounds(cx); + let order = layout.order(cx); + + self.hovered.set(bounds.contains_point(cx.mouse_position())); if self.hovered.get() { // If hovered, refine the child's style with this element's style. self.child.declared_style().refine(&self.hovered_style); @@ -61,16 +63,15 @@ impl + Styleable> Element for Hoverable { *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 was_hovered = self.hovered.clone(); + let 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); + if bounds.contains_point(event.position) != hovered.get() { cx.repaint(); } }); + + self.child.paint(view, layout, cx); } } + +impl> StyleHelpers for Hoverable {} diff --git a/crates/gpui/playground/src/playground.rs b/crates/gpui/playground/src/playground.rs index 2462ac99f5..00f0ebdc80 100644 --- a/crates/gpui/playground/src/playground.rs +++ b/crates/gpui/playground/src/playground.rs @@ -1,5 +1,8 @@ #![allow(dead_code, unused_variables)] -use crate::{color::black, style::StyleHelpers}; +use crate::{ + color::black, + style::{StyleHelpers, Styleable}, +}; use element::Element; use gpui::{ geometry::{rect::RectF, vector::vec2f}, @@ -51,8 +54,8 @@ fn playground(theme: &ThemeColors) -> impl Element { .h_full() .w_1_2() .fill(theme.success(0.5)) - // .hover() - // .fill(theme.error(0.5)) + .hoverable() + .fill(theme.error(0.5)) // .child(button().label("Hello").click(|_, _, _| println!("click!"))) } diff --git a/crates/gpui/playground/src/style.rs b/crates/gpui/playground/src/style.rs index 9216702f7f..9d58c583f5 100644 --- a/crates/gpui/playground/src/style.rs +++ b/crates/gpui/playground/src/style.rs @@ -1,6 +1,7 @@ use crate::{ color::Hsla, element::{Element, Layout}, + hoverable::{hoverable, Hoverable}, paint_context::PaintContext, }; use gpui::{ @@ -255,6 +256,13 @@ pub trait Styleable { style.refine(self.declared_style()); style } + + fn hoverable(self) -> Hoverable + where + Self: Sized, + { + hoverable(self) + } } // Helpers methods that take and return mut self. This includes tailwind style methods for standard sizes etc. diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 076a3fdde3..55ed812f99 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -1363,7 +1363,14 @@ impl AppContext { window: handle, })); - let mut window = Window::new(handle, platform_window, self, build_root_view); + let mouse_position = self.platform.mouse_position(); + let mut window = Window::new( + handle, + platform_window, + mouse_position, + self, + build_root_view, + ); let mut cx = WindowContext::mutable(self, &mut window, handle); cx.layout(false).expect("initial layout should not error"); let scene = cx.paint().expect("initial paint should not error"); diff --git a/crates/gpui/src/app/window.rs b/crates/gpui/src/app/window.rs index 5638699565..e0d53401a8 100644 --- a/crates/gpui/src/app/window.rs +++ b/crates/gpui/src/app/window.rs @@ -70,6 +70,7 @@ impl Window { pub fn new( handle: AnyWindowHandle, platform_window: Box, + mouse_position: Vector2F, cx: &mut AppContext, build_view: F, ) -> Self @@ -97,7 +98,7 @@ impl Window { hovered_region_ids: Default::default(), clicked_region_ids: Default::default(), clicked_region: None, - mouse_position: vec2f(0., 0.), + mouse_position, titlebar_height, appearance, }; diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index 824e6b61e5..2ac7ec54c7 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -75,6 +75,7 @@ pub trait Platform: Send + Sync { fn read_credentials(&self, url: &str) -> Result)>>; fn delete_credentials(&self, url: &str) -> Result<()>; + fn mouse_position(&self) -> Vector2F; fn set_cursor_style(&self, style: CursorStyle); fn should_auto_hide_scrollbars(&self) -> bool; diff --git a/crates/gpui/src/platform/mac/platform.rs b/crates/gpui/src/platform/mac/platform.rs index 9a799c3a3a..af4eaa2a40 100644 --- a/crates/gpui/src/platform/mac/platform.rs +++ b/crates/gpui/src/platform/mac/platform.rs @@ -18,7 +18,7 @@ use cocoa::{ }, base::{id, nil, selector, BOOL, YES}, foundation::{ - NSArray, NSAutoreleasePool, NSBundle, NSData, NSInteger, NSProcessInfo, NSString, + NSArray, NSAutoreleasePool, NSBundle, NSData, NSInteger, NSPoint, NSProcessInfo, NSString, NSUInteger, NSURL, }, }; @@ -37,6 +37,7 @@ use objc::{ runtime::{Class, Object, Sel}, sel, sel_impl, }; +use pathfinder_geometry::vector::{vec2f, Vector2F}; use postage::oneshot; use ptr::null_mut; use std::{ @@ -784,6 +785,11 @@ impl platform::Platform for MacPlatform { Ok(()) } + fn mouse_position(&self) -> Vector2F { + let position: NSPoint = unsafe { msg_send![class!(NSEvent), mouseLocation] }; + vec2f(position.x as f32, position.y as f32) + } + fn set_cursor_style(&self, style: CursorStyle) { unsafe { let new_cursor: id = match style { diff --git a/crates/gpui/src/platform/test.rs b/crates/gpui/src/platform/test.rs index ee3a26c6fd..5b6f299f9c 100644 --- a/crates/gpui/src/platform/test.rs +++ b/crates/gpui/src/platform/test.rs @@ -195,6 +195,10 @@ impl super::Platform for Platform { Ok(()) } + fn mouse_position(&self) -> Vector2F { + Vector2F::zero() + } + fn set_cursor_style(&self, style: CursorStyle) { *self.cursor.lock() = style; }