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:
co-authored by
Max Brunsfeld
Nathan Sobo
parent
53dc08dfc5
commit
5e6e0c68cd
+31
-2
@@ -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 {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user