Allow styling the cursor in MouseEventHandler

Co-Authored-By: Max Brunsfeld <max@zed.dev>
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
Antonio Scandurra
2021-08-27 19:01:49 +02:00
co-authored by Max Brunsfeld Nathan Sobo
parent 53dc08dfc5
commit 5e6e0c68cd
6 changed files with 92 additions and 7 deletions
+31 -2
View File
@@ -2,7 +2,7 @@ use crate::{
elements::ElementBox,
executor,
keymap::{self, Keystroke},
platform::{self, Platform, PromptLevel, WindowOptions},
platform::{self, CursorStyle, Platform, PromptLevel, WindowOptions},
presenter::Presenter,
util::{post_inc, timeout},
AssetCache, AssetSource, ClipboardItem, FontCache, PathPromptOptions, TextLayoutCache,
@@ -25,7 +25,10 @@ use std::{
ops::{Deref, DerefMut},
path::{Path, PathBuf},
rc::{self, Rc},
sync::{Arc, Weak},
sync::{
atomic::{AtomicUsize, Ordering::SeqCst},
Arc, Weak,
},
time::Duration,
};
@@ -661,6 +664,7 @@ pub struct MutableAppContext {
pending_effects: VecDeque<Effect>,
pending_flushes: usize,
flushing_effects: bool,
next_cursor_style_handle_id: Arc<AtomicUsize>,
}
impl MutableAppContext {
@@ -700,6 +704,7 @@ impl MutableAppContext {
pending_effects: VecDeque::new(),
pending_flushes: 0,
flushing_effects: false,
next_cursor_style_handle_id: Default::default(),
}
}
@@ -1456,6 +1461,16 @@ impl MutableAppContext {
self.presenters_and_platform_windows = presenters;
}
pub fn set_cursor_style(&mut self, style: CursorStyle) -> CursorStyleHandle {
self.platform.set_cursor_style(style);
let id = self.next_cursor_style_handle_id.fetch_add(1, SeqCst);
CursorStyleHandle {
id,
next_cursor_style_handle_id: self.next_cursor_style_handle_id.clone(),
platform: self.platform(),
}
}
fn emit_event(&mut self, entity_id: usize, payload: Box<dyn Any>) {
let callbacks = self.subscriptions.lock().remove(&entity_id);
if let Some(callbacks) = callbacks {
@@ -3029,6 +3044,20 @@ impl<T> Drop for ElementStateHandle<T> {
}
}
pub struct CursorStyleHandle {
id: usize,
next_cursor_style_handle_id: Arc<AtomicUsize>,
platform: Arc<dyn Platform>,
}
impl Drop for CursorStyleHandle {
fn drop(&mut self) {
if self.id + 1 == self.next_cursor_style_handle_id.load(SeqCst) {
self.platform.set_cursor_style(CursorStyle::Arrow);
}
}
}
#[must_use]
pub enum Subscription {
Subscription {
+25 -3
View File
@@ -2,23 +2,26 @@ use std::ops::DerefMut;
use crate::{
geometry::{rect::RectF, vector::Vector2F},
DebugContext, Element, ElementBox, ElementStateHandle, Event, EventContext, LayoutContext,
MutableAppContext, PaintContext, SizeConstraint,
platform::CursorStyle,
CursorStyleHandle, DebugContext, Element, ElementBox, ElementStateHandle, Event, EventContext,
LayoutContext, MutableAppContext, PaintContext, SizeConstraint,
};
use serde_json::json;
pub struct MouseEventHandler {
state: ElementStateHandle<MouseState>,
child: ElementBox,
cursor_style: Option<CursorStyle>,
click_handler: Option<Box<dyn FnMut(&mut EventContext)>>,
drag_handler: Option<Box<dyn FnMut(Vector2F, &mut EventContext)>>,
}
#[derive(Clone, Copy, Debug, Default)]
#[derive(Default)]
pub struct MouseState {
pub hovered: bool,
pub clicked: bool,
prev_drag_position: Option<Vector2F>,
cursor_style_handle: Option<CursorStyleHandle>,
}
impl MouseEventHandler {
@@ -33,11 +36,17 @@ impl MouseEventHandler {
Self {
state: state_handle,
child,
cursor_style: None,
click_handler: None,
drag_handler: None,
}
}
pub fn with_cursor_style(mut self, cursor: CursorStyle) -> Self {
self.cursor_style = Some(cursor);
self
}
pub fn on_click(mut self, handler: impl FnMut(&mut EventContext) + 'static) -> Self {
self.click_handler = Some(Box::new(handler));
self
@@ -78,6 +87,7 @@ impl Element for MouseEventHandler {
_: &mut Self::PaintState,
cx: &mut EventContext,
) -> bool {
let cursor_style = self.cursor_style;
let click_handler = self.click_handler.as_mut();
let drag_handler = self.drag_handler.as_mut();
@@ -88,6 +98,15 @@ impl Element for MouseEventHandler {
let mouse_in = bounds.contains_point(*position);
if state.hovered != mouse_in {
state.hovered = mouse_in;
if let Some(cursor_style) = cursor_style {
if !state.clicked {
if state.hovered {
state.cursor_style_handle = Some(cx.set_cursor_style(cursor_style));
} else {
state.cursor_style_handle = None;
}
}
}
cx.notify();
true
} else {
@@ -108,6 +127,9 @@ impl Element for MouseEventHandler {
state.prev_drag_position = None;
if !handled_in_child && state.clicked {
state.clicked = false;
if !state.hovered {
state.cursor_style_handle = None;
}
cx.notify();
if let Some(handler) = click_handler {
if bounds.contains_point(*position) {
+9
View File
@@ -47,6 +47,8 @@ pub trait Platform: Send + Sync {
fn write_credentials(&self, url: &str, username: &str, password: &[u8]);
fn read_credentials(&self, url: &str) -> Option<(String, Vec<u8>)>;
fn set_cursor_style(&self, style: CursorStyle);
}
pub(crate) trait ForegroundPlatform {
@@ -114,6 +116,13 @@ pub enum PromptLevel {
Critical,
}
#[derive(Copy, Clone, Debug)]
pub enum CursorStyle {
Arrow,
ResizeLeftRight,
PointingHand,
}
pub trait FontSystem: Send + Sync {
fn load_family(&self, name: &str) -> anyhow::Result<Vec<FontId>>;
fn select_font(
+15 -1
View File
@@ -1,6 +1,9 @@
use super::{BoolExt as _, Dispatcher, FontSystem, Window};
use crate::{
executor, keymap::Keystroke, platform, AnyAction, ClipboardItem, Event, Menu, MenuItem,
executor,
keymap::Keystroke,
platform::{self, CursorStyle},
AnyAction, ClipboardItem, Event, Menu, MenuItem,
};
use block::ConcreteBlock;
use cocoa::{
@@ -544,6 +547,17 @@ impl platform::Platform for MacPlatform {
Some((username.to_string(), password.bytes().to_vec()))
}
}
fn set_cursor_style(&self, style: CursorStyle) {
unsafe {
let cursor: id = match style {
CursorStyle::Arrow => msg_send![class!(NSCursor), arrowCursor],
CursorStyle::ResizeLeftRight => msg_send![class!(NSCursor), resizeLeftRightCursor],
CursorStyle::PointingHand => msg_send![class!(NSCursor), pointingHandCursor],
};
let _: () = msg_send![cursor, set];
}
}
}
unsafe fn get_foreground_platform(object: &mut Object) -> &MacForegroundPlatform {
+7
View File
@@ -1,3 +1,4 @@
use super::CursorStyle;
use crate::{AnyAction, ClipboardItem};
use parking_lot::Mutex;
use pathfinder_geometry::vector::Vector2F;
@@ -13,6 +14,7 @@ pub struct Platform {
dispatcher: Arc<dyn super::Dispatcher>,
fonts: Arc<dyn super::FontSystem>,
current_clipboard_item: Mutex<Option<ClipboardItem>>,
cursor: Mutex<CursorStyle>,
}
#[derive(Default)]
@@ -84,6 +86,7 @@ impl Platform {
dispatcher: Arc::new(Dispatcher),
fonts: Arc::new(super::current::FontSystem::new()),
current_clipboard_item: Default::default(),
cursor: Mutex::new(CursorStyle::Arrow),
}
}
}
@@ -129,6 +132,10 @@ impl super::Platform for Platform {
fn read_credentials(&self, _: &str) -> Option<(String, Vec<u8>)> {
None
}
fn set_cursor_style(&self, style: CursorStyle) {
*self.cursor.lock() = style;
}
}
impl Window {
+5 -1
View File
@@ -1,6 +1,8 @@
use super::Workspace;
use crate::Settings;
use gpui::{action, elements::*, AnyViewHandle, MutableAppContext, RenderContext};
use gpui::{
action, elements::*, platform::CursorStyle, AnyViewHandle, MutableAppContext, RenderContext,
};
use std::{cell::RefCell, rc::Rc};
pub struct Sidebar {
@@ -88,6 +90,7 @@ impl Sidebar {
.with_height(line_height + 16.0)
.boxed()
})
.with_cursor_style(CursorStyle::PointingHand)
.on_click(move |cx| {
cx.dispatch_action(ToggleSidebarItem(ToggleArg { side, item_index }))
})
@@ -135,6 +138,7 @@ impl Sidebar {
.with_style(&settings.theme.workspace.sidebar.resize_handle)
.boxed()
})
.with_cursor_style(CursorStyle::ResizeLeftRight)
.on_drag(move |delta, cx| {
let prev_width = *width.borrow();
match side {