From 5e6e0c68cd097bee55751b60b857f33312bcf2f7 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 27 Aug 2021 19:01:49 +0200 Subject: [PATCH] Allow styling the cursor in `MouseEventHandler` Co-Authored-By: Max Brunsfeld Co-Authored-By: Nathan Sobo --- gpui/src/app.rs | 33 ++++++++++++++++++++++-- gpui/src/elements/mouse_event_handler.rs | 28 +++++++++++++++++--- gpui/src/platform.rs | 9 +++++++ gpui/src/platform/mac/platform.rs | 16 +++++++++++- gpui/src/platform/test.rs | 7 +++++ zed/src/workspace/sidebar.rs | 6 ++++- 6 files changed, 92 insertions(+), 7 deletions(-) diff --git a/gpui/src/app.rs b/gpui/src/app.rs index 4582dc0727e12acdd87e9c67052d5b7394e24881..d9caf3e6d2b9b516c09a427dffacaf48be66daaf 100644 --- a/gpui/src/app.rs +++ b/gpui/src/app.rs @@ -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, pending_flushes: usize, flushing_effects: bool, + next_cursor_style_handle_id: Arc, } 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) { let callbacks = self.subscriptions.lock().remove(&entity_id); if let Some(callbacks) = callbacks { @@ -3029,6 +3044,20 @@ impl Drop for ElementStateHandle { } } +pub struct CursorStyleHandle { + id: usize, + next_cursor_style_handle_id: Arc, + platform: Arc, +} + +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 { diff --git a/gpui/src/elements/mouse_event_handler.rs b/gpui/src/elements/mouse_event_handler.rs index 7512fe43b4e5b8afa16ce36db5565d54ed7f4f84..997331545436b53f6b33a32211f5033f2f57e62f 100644 --- a/gpui/src/elements/mouse_event_handler.rs +++ b/gpui/src/elements/mouse_event_handler.rs @@ -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, child: ElementBox, + cursor_style: Option, click_handler: Option>, drag_handler: Option>, } -#[derive(Clone, Copy, Debug, Default)] +#[derive(Default)] pub struct MouseState { pub hovered: bool, pub clicked: bool, prev_drag_position: Option, + cursor_style_handle: Option, } 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) { diff --git a/gpui/src/platform.rs b/gpui/src/platform.rs index 449f6bb962ea696e6a0b0b455febb4426b0ec38c..9262d99ec7004a63d119ed2b9b431ecf1daef7f1 100644 --- a/gpui/src/platform.rs +++ b/gpui/src/platform.rs @@ -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)>; + + 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>; fn select_font( diff --git a/gpui/src/platform/mac/platform.rs b/gpui/src/platform/mac/platform.rs index 6593f9e3efa27db8dcd63fb59ea19601040f6ab6..861984a24761ccc1dbe2fe92601383d3c44d8e88 100644 --- a/gpui/src/platform/mac/platform.rs +++ b/gpui/src/platform/mac/platform.rs @@ -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 { diff --git a/gpui/src/platform/test.rs b/gpui/src/platform/test.rs index 40c9304a7aa1ebe4ed2cc6e2521ee849d6fd3b80..b66a8d5043b3e4c04756064bcf3bc4aef9c2fc3d 100644 --- a/gpui/src/platform/test.rs +++ b/gpui/src/platform/test.rs @@ -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, fonts: Arc, current_clipboard_item: Mutex>, + cursor: Mutex, } #[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)> { None } + + fn set_cursor_style(&self, style: CursorStyle) { + *self.cursor.lock() = style; + } } impl Window { diff --git a/zed/src/workspace/sidebar.rs b/zed/src/workspace/sidebar.rs index 3c02907067f6e2cb4aa4b04496d9e2c08ca8ab05..739aecf46c249f559b6d4f51b11fbaf9bca452e6 100644 --- a/zed/src/workspace/sidebar.rs +++ b/zed/src/workspace/sidebar.rs @@ -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 {