Hide the mouse when the user is typing in the editor - take 2 (#27519)
Closes #4461 Take 2 on https://github.com/zed-industries/zed/pull/25040. Fixes panic caused due to using `setHiddenUntilMouseMoves` return type to `set` cursor on macOS. Release Notes: - Now cursor hides when the user is typing in editor. It will stay hidden until it is moved again. This behavior is `true` by default, and can be configured with `hide_mouse_while_typing` in settings. --------- Co-authored-by: Peter Tripp <peter@zed.dev> Co-authored-by: Thomas Mickley-Doyle <thomas@zed.dev> Co-authored-by: Agus <agus@zed.dev> Co-authored-by: Kirill Bulatov <kirill@zed.dev> Co-authored-by: Agus Zubiaga <hi@aguz.me> Co-authored-by: Angelk90 <angelo.k90@hotmail.it>
This commit is contained in:
parent
848a99c605
commit
77856bf017
25 changed files with 172 additions and 52 deletions
|
@ -1617,7 +1617,7 @@ impl Interactivity {
|
|||
|
||||
if !cx.has_active_drag() {
|
||||
if let Some(mouse_cursor) = style.mouse_cursor {
|
||||
window.set_cursor_style(mouse_cursor, hitbox);
|
||||
window.set_cursor_style(mouse_cursor, Some(hitbox));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -700,7 +700,7 @@ impl Element for InteractiveText {
|
|||
.iter()
|
||||
.any(|range| range.contains(&ix))
|
||||
{
|
||||
window.set_cursor_style(crate::CursorStyle::PointingHand, hitbox)
|
||||
window.set_cursor_style(crate::CursorStyle::PointingHand, Some(hitbox))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1228,6 +1228,9 @@ pub enum CursorStyle {
|
|||
/// A cursor indicating that the operation will result in a context menu
|
||||
/// corresponds to the CSS cursor value `context-menu`
|
||||
ContextualMenu,
|
||||
|
||||
/// Hide the cursor
|
||||
None,
|
||||
}
|
||||
|
||||
impl Default for CursorStyle {
|
||||
|
|
|
@ -666,6 +666,12 @@ impl CursorStyle {
|
|||
CursorStyle::DragLink => "alias",
|
||||
CursorStyle::DragCopy => "copy",
|
||||
CursorStyle::ContextualMenu => "context-menu",
|
||||
CursorStyle::None => {
|
||||
#[cfg(debug_assertions)]
|
||||
panic!("CursorStyle::None should be handled separately in the client");
|
||||
#[cfg(not(debug_assertions))]
|
||||
"default"
|
||||
}
|
||||
}
|
||||
.to_string()
|
||||
}
|
||||
|
|
|
@ -35,6 +35,12 @@ impl CursorStyle {
|
|||
CursorStyle::DragLink => Shape::Alias,
|
||||
CursorStyle::DragCopy => Shape::Copy,
|
||||
CursorStyle::ContextualMenu => Shape::ContextMenu,
|
||||
CursorStyle::None => {
|
||||
#[cfg(debug_assertions)]
|
||||
panic!("CursorStyle::None should be handled separately in the client");
|
||||
#[cfg(not(debug_assertions))]
|
||||
Shape::Default
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -667,7 +667,13 @@ impl LinuxClient for WaylandClient {
|
|||
let serial = state.serial_tracker.get(SerialKind::MouseEnter);
|
||||
state.cursor_style = Some(style);
|
||||
|
||||
if let Some(cursor_shape_device) = &state.cursor_shape_device {
|
||||
if let CursorStyle::None = style {
|
||||
let wl_pointer = state
|
||||
.wl_pointer
|
||||
.clone()
|
||||
.expect("window is focused by pointer");
|
||||
wl_pointer.set_cursor(serial, None, 0, 0);
|
||||
} else if let Some(cursor_shape_device) = &state.cursor_shape_device {
|
||||
cursor_shape_device.set_shape(serial, style.to_shape());
|
||||
} else if let Some(focused_window) = &state.mouse_focused_window {
|
||||
// cursor-shape-v1 isn't supported, set the cursor using a surface.
|
||||
|
|
|
@ -1438,13 +1438,16 @@ impl LinuxClient for X11Client {
|
|||
let cursor = match state.cursor_cache.get(&style) {
|
||||
Some(cursor) => *cursor,
|
||||
None => {
|
||||
let Some(cursor) = state
|
||||
.cursor_handle
|
||||
.load_cursor(&state.xcb_connection, &style.to_icon_name())
|
||||
.log_err()
|
||||
else {
|
||||
let Some(cursor) = (match style {
|
||||
CursorStyle::None => create_invisible_cursor(&state.xcb_connection).log_err(),
|
||||
_ => state
|
||||
.cursor_handle
|
||||
.load_cursor(&state.xcb_connection, &style.to_icon_name())
|
||||
.log_err(),
|
||||
}) else {
|
||||
return;
|
||||
};
|
||||
|
||||
state.cursor_cache.insert(style, cursor);
|
||||
cursor
|
||||
}
|
||||
|
@ -1938,3 +1941,19 @@ fn make_scroll_wheel_event(
|
|||
touch_phase: TouchPhase::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn create_invisible_cursor(
|
||||
connection: &XCBConnection,
|
||||
) -> anyhow::Result<crate::platform::linux::x11::client::xproto::Cursor> {
|
||||
let empty_pixmap = connection.generate_id()?;
|
||||
let root = connection.setup().roots[0].root;
|
||||
connection.create_pixmap(1, empty_pixmap, root, 1, 1)?;
|
||||
|
||||
let cursor = connection.generate_id()?;
|
||||
connection.create_cursor(cursor, empty_pixmap, empty_pixmap, 0, 0, 0, 0, 0, 0, 0, 0)?;
|
||||
|
||||
connection.free_pixmap(empty_pixmap)?;
|
||||
|
||||
connection.flush()?;
|
||||
Ok(cursor)
|
||||
}
|
||||
|
|
|
@ -891,6 +891,11 @@ impl Platform for MacPlatform {
|
|||
/// in macOS's [NSCursor](https://developer.apple.com/documentation/appkit/nscursor).
|
||||
fn set_cursor_style(&self, style: CursorStyle) {
|
||||
unsafe {
|
||||
if style == CursorStyle::None {
|
||||
let _: () = msg_send![class!(NSCursor), setHiddenUntilMouseMoves:YES];
|
||||
return;
|
||||
}
|
||||
|
||||
let new_cursor: id = match style {
|
||||
CursorStyle::Arrow => msg_send![class!(NSCursor), arrowCursor],
|
||||
CursorStyle::IBeam => msg_send![class!(NSCursor), IBeamCursor],
|
||||
|
@ -925,6 +930,7 @@ impl Platform for MacPlatform {
|
|||
CursorStyle::DragLink => msg_send![class!(NSCursor), dragLinkCursor],
|
||||
CursorStyle::DragCopy => msg_send![class!(NSCursor), dragCopyCursor],
|
||||
CursorStyle::ContextualMenu => msg_send![class!(NSCursor), contextualMenuCursor],
|
||||
CursorStyle::None => unreachable!(),
|
||||
};
|
||||
|
||||
let old_cursor: id = msg_send![class!(NSCursor), currentCursor];
|
||||
|
|
|
@ -1127,7 +1127,19 @@ fn handle_nc_mouse_up_msg(
|
|||
}
|
||||
|
||||
fn handle_cursor_changed(lparam: LPARAM, state_ptr: Rc<WindowsWindowStatePtr>) -> Option<isize> {
|
||||
state_ptr.state.borrow_mut().current_cursor = HCURSOR(lparam.0 as _);
|
||||
let mut state = state_ptr.state.borrow_mut();
|
||||
let had_cursor = state.current_cursor.is_some();
|
||||
|
||||
state.current_cursor = if lparam.0 == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(HCURSOR(lparam.0 as _))
|
||||
};
|
||||
|
||||
if had_cursor != state.current_cursor.is_some() {
|
||||
unsafe { SetCursor(state.current_cursor) };
|
||||
}
|
||||
|
||||
Some(0)
|
||||
}
|
||||
|
||||
|
@ -1138,7 +1150,9 @@ fn handle_set_cursor(lparam: LPARAM, state_ptr: Rc<WindowsWindowStatePtr>) -> Op
|
|||
) {
|
||||
return None;
|
||||
}
|
||||
unsafe { SetCursor(Some(state_ptr.state.borrow().current_cursor)) };
|
||||
unsafe {
|
||||
SetCursor(state_ptr.state.borrow().current_cursor);
|
||||
};
|
||||
Some(1)
|
||||
}
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ pub(crate) struct WindowsPlatformState {
|
|||
menus: Vec<OwnedMenu>,
|
||||
dock_menu_actions: Vec<Box<dyn Action>>,
|
||||
// NOTE: standard cursor handles don't need to close.
|
||||
pub(crate) current_cursor: HCURSOR,
|
||||
pub(crate) current_cursor: Option<HCURSOR>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
@ -558,11 +558,11 @@ impl Platform for WindowsPlatform {
|
|||
fn set_cursor_style(&self, style: CursorStyle) {
|
||||
let hcursor = load_cursor(style);
|
||||
let mut lock = self.state.borrow_mut();
|
||||
if lock.current_cursor.0 != hcursor.0 {
|
||||
if lock.current_cursor.map(|c| c.0) != hcursor.map(|c| c.0) {
|
||||
self.post_message(
|
||||
WM_GPUI_CURSOR_STYLE_CHANGED,
|
||||
WPARAM(0),
|
||||
LPARAM(hcursor.0 as isize),
|
||||
LPARAM(hcursor.map_or(0, |c| c.0 as isize)),
|
||||
);
|
||||
lock.current_cursor = hcursor;
|
||||
}
|
||||
|
@ -683,7 +683,7 @@ impl Drop for WindowsPlatform {
|
|||
pub(crate) struct WindowCreationInfo {
|
||||
pub(crate) icon: HICON,
|
||||
pub(crate) executor: ForegroundExecutor,
|
||||
pub(crate) current_cursor: HCURSOR,
|
||||
pub(crate) current_cursor: Option<HCURSOR>,
|
||||
pub(crate) windows_version: WindowsVersion,
|
||||
pub(crate) validation_number: usize,
|
||||
pub(crate) main_receiver: flume::Receiver<Runnable>,
|
||||
|
|
|
@ -106,7 +106,7 @@ pub(crate) fn windows_credentials_target_name(url: &str) -> String {
|
|||
format!("zed:url={}", url)
|
||||
}
|
||||
|
||||
pub(crate) fn load_cursor(style: CursorStyle) -> HCURSOR {
|
||||
pub(crate) fn load_cursor(style: CursorStyle) -> Option<HCURSOR> {
|
||||
static ARROW: OnceLock<SafeCursor> = OnceLock::new();
|
||||
static IBEAM: OnceLock<SafeCursor> = OnceLock::new();
|
||||
static CROSS: OnceLock<SafeCursor> = OnceLock::new();
|
||||
|
@ -127,17 +127,20 @@ pub(crate) fn load_cursor(style: CursorStyle) -> HCURSOR {
|
|||
| CursorStyle::ResizeUpDown
|
||||
| CursorStyle::ResizeRow => (&SIZENS, IDC_SIZENS),
|
||||
CursorStyle::OperationNotAllowed => (&NO, IDC_NO),
|
||||
CursorStyle::None => return None,
|
||||
_ => (&ARROW, IDC_ARROW),
|
||||
};
|
||||
*(*lock.get_or_init(|| {
|
||||
HCURSOR(
|
||||
unsafe { LoadImageW(None, name, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED) }
|
||||
.log_err()
|
||||
.unwrap_or_default()
|
||||
.0,
|
||||
)
|
||||
.into()
|
||||
}))
|
||||
Some(
|
||||
*(*lock.get_or_init(|| {
|
||||
HCURSOR(
|
||||
unsafe { LoadImageW(None, name, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED) }
|
||||
.log_err()
|
||||
.unwrap_or_default()
|
||||
.0,
|
||||
)
|
||||
.into()
|
||||
})),
|
||||
)
|
||||
}
|
||||
|
||||
/// This function is used to configure the dark mode for the window built-in title bar.
|
||||
|
|
|
@ -48,7 +48,7 @@ pub struct WindowsWindowState {
|
|||
|
||||
pub click_state: ClickState,
|
||||
pub system_settings: WindowsSystemSettings,
|
||||
pub current_cursor: HCURSOR,
|
||||
pub current_cursor: Option<HCURSOR>,
|
||||
pub nc_button_pressed: Option<u32>,
|
||||
|
||||
pub display: WindowsDisplay,
|
||||
|
@ -76,7 +76,7 @@ impl WindowsWindowState {
|
|||
hwnd: HWND,
|
||||
transparent: bool,
|
||||
cs: &CREATESTRUCTW,
|
||||
current_cursor: HCURSOR,
|
||||
current_cursor: Option<HCURSOR>,
|
||||
display: WindowsDisplay,
|
||||
gpu_context: &BladeContext,
|
||||
) -> Result<Self> {
|
||||
|
@ -351,7 +351,7 @@ struct WindowCreateContext<'a> {
|
|||
transparent: bool,
|
||||
is_movable: bool,
|
||||
executor: ForegroundExecutor,
|
||||
current_cursor: HCURSOR,
|
||||
current_cursor: Option<HCURSOR>,
|
||||
windows_version: WindowsVersion,
|
||||
validation_number: usize,
|
||||
main_receiver: flume::Receiver<Runnable>,
|
||||
|
|
|
@ -407,7 +407,7 @@ pub(crate) type AnyMouseListener =
|
|||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct CursorStyleRequest {
|
||||
pub(crate) hitbox_id: HitboxId,
|
||||
pub(crate) hitbox_id: Option<HitboxId>, // None represents whole window
|
||||
pub(crate) style: CursorStyle,
|
||||
}
|
||||
|
||||
|
@ -1928,10 +1928,10 @@ impl Window {
|
|||
|
||||
/// Updates the cursor style at the platform level. This method should only be called
|
||||
/// during the prepaint phase of element drawing.
|
||||
pub fn set_cursor_style(&mut self, style: CursorStyle, hitbox: &Hitbox) {
|
||||
pub fn set_cursor_style(&mut self, style: CursorStyle, hitbox: Option<&Hitbox>) {
|
||||
self.invalidator.debug_assert_paint();
|
||||
self.next_frame.cursor_styles.push(CursorStyleRequest {
|
||||
hitbox_id: hitbox.id,
|
||||
hitbox_id: hitbox.map(|hitbox| hitbox.id),
|
||||
style,
|
||||
});
|
||||
}
|
||||
|
@ -2984,7 +2984,11 @@ impl Window {
|
|||
.cursor_styles
|
||||
.iter()
|
||||
.rev()
|
||||
.find(|request| request.hitbox_id.is_hovered(self))
|
||||
.find(|request| {
|
||||
request
|
||||
.hitbox_id
|
||||
.map_or(true, |hitbox_id| hitbox_id.is_hovered(self))
|
||||
})
|
||||
.map(|request| request.style)
|
||||
.unwrap_or(CursorStyle::Arrow);
|
||||
cx.platform.set_cursor_style(style);
|
||||
|
@ -3241,6 +3245,7 @@ impl Window {
|
|||
keystroke,
|
||||
&dispatch_path,
|
||||
);
|
||||
|
||||
if !match_result.to_replay.is_empty() {
|
||||
self.replay_pending_input(match_result.to_replay, cx)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue