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