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},
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<V: 'static, E: Element<V> + Styleable> {
pub struct Hoverable<E: Styleable> {
hovered: Cell<bool>,
child_style: StyleRefinement,
hovered_style: StyleRefinement,
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 {
hovered: Cell::new(false),
child_style: child.declared_style().clone(),
hovered_style: Default::default(),
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;
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;
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
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<V: 'static, E: Element<V> + Styleable> Element<V> for Hoverable<V, E> {
*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<E: Styleable<Style = Style>> StyleHelpers for Hoverable<E> {}

View file

@ -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<V: 'static>(theme: &ThemeColors) -> impl Element<V> {
.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!")))
}

View file

@ -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<Self>
where
Self: Sized,
{
hoverable(self)
}
}
// 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,
}));
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");

View file

@ -70,6 +70,7 @@ impl Window {
pub fn new<V, F>(
handle: AnyWindowHandle,
platform_window: Box<dyn platform::Window>,
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,
};

View file

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

View file

@ -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 {

View file

@ -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;
}