Add hover styling support

This commit is contained in:
Nathan Sobo 2023-08-23 09:08:05 -06:00
parent 1bc4f22373
commit 569d99a5a1
8 changed files with 53 additions and 22 deletions

View file

@ -2,40 +2,38 @@ use crate::{
element::{Element, Layout}, element::{Element, Layout},
layout_context::LayoutContext, layout_context::LayoutContext,
paint_context::PaintContext, paint_context::PaintContext,
style::{StyleRefinement, Styleable}, style::{Style, StyleHelpers, StyleRefinement, Styleable},
}; };
use anyhow::Result; use anyhow::Result;
use gpui::platform::MouseMovedEvent; use gpui::platform::MouseMovedEvent;
use refineable::Refineable; use refineable::Refineable;
use std::{cell::Cell, marker::PhantomData}; use std::cell::Cell;
pub struct Hoverable<V: 'static, E: Element<V> + Styleable> { pub struct Hoverable<E: Styleable> {
hovered: Cell<bool>, hovered: Cell<bool>,
child_style: StyleRefinement, child_style: StyleRefinement,
hovered_style: StyleRefinement, hovered_style: StyleRefinement,
child: E, child: E,
view_type: PhantomData<V>,
} }
pub fn hoverable<V, E: Element<V> + Styleable>(mut child: E) -> Hoverable<V, E> { pub fn hoverable<E: Styleable>(mut child: E) -> Hoverable<E> {
Hoverable { Hoverable {
hovered: Cell::new(false), hovered: Cell::new(false),
child_style: child.declared_style().clone(), child_style: child.declared_style().clone(),
hovered_style: Default::default(), hovered_style: Default::default(),
child, child,
view_type: PhantomData,
} }
} }
impl<V, E: Element<V> + Styleable> Styleable for Hoverable<V, E> { impl<E: Styleable> Styleable for Hoverable<E> {
type Style = E::Style; type Style = E::Style;
fn declared_style(&mut self) -> &mut crate::style::StyleRefinement { fn declared_style(&mut self) -> &mut crate::style::StyleRefinement {
self.child.declared_style() &mut self.hovered_style
} }
} }
impl<V: 'static, E: Element<V> + Styleable> Element<V> for Hoverable<V, E> { impl<V: 'static, E: Element<V> + Styleable> Element<V> for Hoverable<E> {
type Layout = E::Layout; type Layout = E::Layout;
fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<Layout<V, Self::Layout>> fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<Layout<V, Self::Layout>>
@ -53,6 +51,10 @@ impl<V: 'static, E: Element<V> + Styleable> Element<V> for Hoverable<V, E> {
) where ) where
Self: Sized, 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 self.hovered.get() {
// If hovered, refine the child's style with this element's style. // If hovered, refine the child's style with this element's style.
self.child.declared_style().refine(&self.hovered_style); self.child.declared_style().refine(&self.hovered_style);
@ -61,16 +63,15 @@ impl<V: 'static, E: Element<V> + Styleable> Element<V> for Hoverable<V, E> {
*self.child.declared_style() = self.child_style.clone(); *self.child.declared_style() = self.child_style.clone();
} }
let bounds = layout.bounds(cx); let hovered = self.hovered.clone();
let order = layout.order(cx);
self.hovered.set(bounds.contains_point(cx.mouse_position()));
let was_hovered = self.hovered.clone();
cx.on_event(order, move |view, event: &MouseMovedEvent, cx| { cx.on_event(order, move |view, event: &MouseMovedEvent, cx| {
let is_hovered = bounds.contains_point(event.position); if bounds.contains_point(event.position) != hovered.get() {
if is_hovered != was_hovered.get() {
was_hovered.set(is_hovered);
cx.repaint(); cx.repaint();
} }
}); });
self.child.paint(view, layout, cx);
} }
} }
impl<E: Styleable<Style = Style>> StyleHelpers for Hoverable<E> {}

View file

@ -1,5 +1,8 @@
#![allow(dead_code, unused_variables)] #![allow(dead_code, unused_variables)]
use crate::{color::black, style::StyleHelpers}; use crate::{
color::black,
style::{StyleHelpers, Styleable},
};
use element::Element; use element::Element;
use gpui::{ use gpui::{
geometry::{rect::RectF, vector::vec2f}, geometry::{rect::RectF, vector::vec2f},
@ -51,8 +54,8 @@ fn playground<V: 'static>(theme: &ThemeColors) -> impl Element<V> {
.h_full() .h_full()
.w_1_2() .w_1_2()
.fill(theme.success(0.5)) .fill(theme.success(0.5))
// .hover() .hoverable()
// .fill(theme.error(0.5)) .fill(theme.error(0.5))
// .child(button().label("Hello").click(|_, _, _| println!("click!"))) // .child(button().label("Hello").click(|_, _, _| println!("click!")))
} }

View file

@ -1,6 +1,7 @@
use crate::{ use crate::{
color::Hsla, color::Hsla,
element::{Element, Layout}, element::{Element, Layout},
hoverable::{hoverable, Hoverable},
paint_context::PaintContext, paint_context::PaintContext,
}; };
use gpui::{ use gpui::{
@ -255,6 +256,13 @@ pub trait Styleable {
style.refine(self.declared_style()); style.refine(self.declared_style());
style style
} }
fn hoverable(self) -> Hoverable<Self>
where
Self: Sized,
{
hoverable(self)
}
} }
// Helpers methods that take and return mut self. This includes tailwind style methods for standard sizes etc. // Helpers methods that take and return mut self. This includes tailwind style methods for standard sizes etc.

View file

@ -1363,7 +1363,14 @@ impl AppContext {
window: handle, 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); let mut cx = WindowContext::mutable(self, &mut window, handle);
cx.layout(false).expect("initial layout should not error"); cx.layout(false).expect("initial layout should not error");
let scene = cx.paint().expect("initial paint should not error"); let scene = cx.paint().expect("initial paint should not error");

View file

@ -70,6 +70,7 @@ impl Window {
pub fn new<V, F>( pub fn new<V, F>(
handle: AnyWindowHandle, handle: AnyWindowHandle,
platform_window: Box<dyn platform::Window>, platform_window: Box<dyn platform::Window>,
mouse_position: Vector2F,
cx: &mut AppContext, cx: &mut AppContext,
build_view: F, build_view: F,
) -> Self ) -> Self
@ -97,7 +98,7 @@ impl Window {
hovered_region_ids: Default::default(), hovered_region_ids: Default::default(),
clicked_region_ids: Default::default(), clicked_region_ids: Default::default(),
clicked_region: None, clicked_region: None,
mouse_position: vec2f(0., 0.), mouse_position,
titlebar_height, titlebar_height,
appearance, appearance,
}; };

View file

@ -75,6 +75,7 @@ pub trait Platform: Send + Sync {
fn read_credentials(&self, url: &str) -> Result<Option<(String, Vec<u8>)>>; fn read_credentials(&self, url: &str) -> Result<Option<(String, Vec<u8>)>>;
fn delete_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 set_cursor_style(&self, style: CursorStyle);
fn should_auto_hide_scrollbars(&self) -> bool; fn should_auto_hide_scrollbars(&self) -> bool;

View file

@ -18,7 +18,7 @@ use cocoa::{
}, },
base::{id, nil, selector, BOOL, YES}, base::{id, nil, selector, BOOL, YES},
foundation::{ foundation::{
NSArray, NSAutoreleasePool, NSBundle, NSData, NSInteger, NSProcessInfo, NSString, NSArray, NSAutoreleasePool, NSBundle, NSData, NSInteger, NSPoint, NSProcessInfo, NSString,
NSUInteger, NSURL, NSUInteger, NSURL,
}, },
}; };
@ -37,6 +37,7 @@ use objc::{
runtime::{Class, Object, Sel}, runtime::{Class, Object, Sel},
sel, sel_impl, sel, sel_impl,
}; };
use pathfinder_geometry::vector::{vec2f, Vector2F};
use postage::oneshot; use postage::oneshot;
use ptr::null_mut; use ptr::null_mut;
use std::{ use std::{
@ -784,6 +785,11 @@ impl platform::Platform for MacPlatform {
Ok(()) 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) { fn set_cursor_style(&self, style: CursorStyle) {
unsafe { unsafe {
let new_cursor: id = match style { let new_cursor: id = match style {

View file

@ -195,6 +195,10 @@ impl super::Platform for Platform {
Ok(()) Ok(())
} }
fn mouse_position(&self) -> Vector2F {
Vector2F::zero()
}
fn set_cursor_style(&self, style: CursorStyle) { fn set_cursor_style(&self, style: CursorStyle) {
*self.cursor.lock() = style; *self.cursor.lock() = style;
} }