From 77856bf01758a0e51a52f65690f87f78b2ead290 Mon Sep 17 00:00:00 2001 From: Smit Barmase Date: Thu, 27 Mar 2025 01:58:26 +0530 Subject: [PATCH] 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 Co-authored-by: Thomas Mickley-Doyle Co-authored-by: Agus Co-authored-by: Kirill Bulatov Co-authored-by: Agus Zubiaga Co-authored-by: Angelk90 --- assets/settings/default.json | 2 ++ crates/editor/src/editor.rs | 24 +++++++++++++-- crates/editor/src/editor_settings.rs | 5 ++++ crates/editor/src/element.rs | 30 ++++++++++++------- crates/gpui/examples/window_shadow.rs | 2 +- crates/gpui/src/elements/div.rs | 2 +- crates/gpui/src/elements/text.rs | 2 +- crates/gpui/src/platform.rs | 3 ++ crates/gpui/src/platform/linux/platform.rs | 6 ++++ crates/gpui/src/platform/linux/wayland.rs | 6 ++++ .../gpui/src/platform/linux/wayland/client.rs | 8 ++++- crates/gpui/src/platform/linux/x11/client.rs | 29 ++++++++++++++---- crates/gpui/src/platform/mac/platform.rs | 6 ++++ crates/gpui/src/platform/windows/events.rs | 18 +++++++++-- crates/gpui/src/platform/windows/platform.rs | 8 ++--- crates/gpui/src/platform/windows/util.rs | 23 +++++++------- crates/gpui/src/platform/windows/window.rs | 6 ++-- crates/gpui/src/window.rs | 13 +++++--- crates/gpui_macros/src/styles.rs | 7 +++++ crates/markdown/src/markdown.rs | 4 +-- crates/terminal_view/src/terminal_element.rs | 4 +-- crates/ui/src/components/indent_guides.rs | 2 +- crates/workspace/src/pane_group.rs | 2 +- crates/workspace/src/workspace.rs | 2 +- docs/src/configuring-zed.md | 10 +++++++ 25 files changed, 172 insertions(+), 52 deletions(-) diff --git a/assets/settings/default.json b/assets/settings/default.json index 9eef75d0e6..dc113a72f1 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -155,6 +155,8 @@ // // Default: not set, defaults to "bar" "cursor_shape": null, + // Determines whether the mouse cursor is hidden when typing in an editor or input box. + "hide_mouse_while_typing": true, // How to highlight the current line in the editor. // // 1. Don't highlight the current line: diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 53a5713873..79e513c706 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -790,6 +790,8 @@ pub struct Editor { _scroll_cursor_center_top_bottom_task: Task<()>, serialize_selections: Task<()>, serialize_folds: Task<()>, + mouse_cursor_hidden: bool, + hide_mouse_while_typing: bool, } #[derive(Copy, Clone, Debug, PartialEq, Eq, Default)] @@ -1568,6 +1570,10 @@ impl Editor { serialize_folds: Task::ready(()), text_style_refinement: None, load_diff_task: load_uncommitted_diff, + mouse_cursor_hidden: false, + hide_mouse_while_typing: EditorSettings::get_global(cx) + .hide_mouse_while_typing + .unwrap_or(true), }; if let Some(breakpoints) = this.breakpoint_store.as_ref() { this._subscriptions @@ -2999,6 +3005,8 @@ impl Editor { return; } + self.mouse_cursor_hidden = self.hide_mouse_while_typing; + let selections = self.selections.all_adjusted(cx); let mut bracket_inserted = false; let mut edits = Vec::new(); @@ -3403,6 +3411,7 @@ impl Editor { } pub fn newline(&mut self, _: &Newline, window: &mut Window, cx: &mut Context) { + self.mouse_cursor_hidden = self.hide_mouse_while_typing; self.transact(window, cx, |this, window, cx| { let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = { let selections = this.selections.all::(cx); @@ -3518,6 +3527,8 @@ impl Editor { } pub fn newline_above(&mut self, _: &NewlineAbove, window: &mut Window, cx: &mut Context) { + self.mouse_cursor_hidden = self.hide_mouse_while_typing; + let buffer = self.buffer.read(cx); let snapshot = buffer.snapshot(cx); @@ -3575,6 +3586,8 @@ impl Editor { } pub fn newline_below(&mut self, _: &NewlineBelow, window: &mut Window, cx: &mut Context) { + self.mouse_cursor_hidden = self.hide_mouse_while_typing; + let buffer = self.buffer.read(cx); let snapshot = buffer.snapshot(cx); @@ -7765,6 +7778,7 @@ impl Editor { } pub fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context) { + self.mouse_cursor_hidden = self.hide_mouse_while_typing; self.transact(window, cx, |this, window, cx| { this.select_autoclose_pair(window, cx); let mut linked_ranges = HashMap::<_, Vec<_>>::default(); @@ -7863,6 +7877,7 @@ impl Editor { } pub fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context) { + self.mouse_cursor_hidden = self.hide_mouse_while_typing; self.transact(window, cx, |this, window, cx| { this.change_selections(Some(Autoscroll::fit()), window, cx, |s| { let line_mode = s.line_mode; @@ -7884,7 +7899,7 @@ impl Editor { if self.move_to_prev_snippet_tabstop(window, cx) { return; } - + self.mouse_cursor_hidden = self.hide_mouse_while_typing; self.outdent(&Outdent, window, cx); } @@ -7892,7 +7907,7 @@ impl Editor { if self.move_to_next_snippet_tabstop(window, cx) || self.read_only(cx) { return; } - + self.mouse_cursor_hidden = self.hide_mouse_while_typing; let mut selections = self.selections.all_adjusted(cx); let buffer = self.buffer.read(cx); let snapshot = buffer.snapshot(cx); @@ -16669,6 +16684,11 @@ impl Editor { self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin; self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs; self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default(); + self.hide_mouse_while_typing = editor_settings.hide_mouse_while_typing.unwrap_or(true); + + if !self.hide_mouse_while_typing { + self.mouse_cursor_hidden = false; + } } if old_cursor_shape != self.cursor_shape { diff --git a/crates/editor/src/editor_settings.rs b/crates/editor/src/editor_settings.rs index a3c033ce72..30c1eae5fb 100644 --- a/crates/editor/src/editor_settings.rs +++ b/crates/editor/src/editor_settings.rs @@ -39,6 +39,7 @@ pub struct EditorSettings { #[serde(default)] pub go_to_definition_fallback: GoToDefinitionFallback, pub jupyter: Jupyter, + pub hide_mouse_while_typing: Option, } #[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] @@ -235,6 +236,10 @@ pub struct EditorSettingsContent { /// /// Default: None pub cursor_shape: Option, + /// Determines whether the mouse cursor should be hidden while typing in an editor or input box. + /// + /// Default: true + pub hide_mouse_while_typing: Option, /// How to highlight the current line in the editor. /// /// Default: all diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index d053f5fca4..168078ac2d 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -894,6 +894,7 @@ impl EditorElement { let gutter_hovered = gutter_hitbox.is_hovered(window); editor.set_gutter_hovered(gutter_hovered, cx); editor.gutter_breakpoint_indicator = None; + editor.mouse_cursor_hidden = false; if gutter_hovered { let new_point = position_map @@ -4307,7 +4308,7 @@ impl EditorElement { let is_singleton = self.editor.read(cx).is_singleton(cx); let line_height = layout.position_map.line_height; - window.set_cursor_style(CursorStyle::Arrow, &layout.gutter_hitbox); + window.set_cursor_style(CursorStyle::Arrow, Some(&layout.gutter_hitbox)); for LineNumberLayout { shaped_line, @@ -4340,9 +4341,9 @@ impl EditorElement { // In singleton buffers, we select corresponding lines on the line number click, so use | -like cursor. // In multi buffers, we open file at the line number clicked, so use a pointing hand cursor. if is_singleton { - window.set_cursor_style(CursorStyle::IBeam, &hitbox); + window.set_cursor_style(CursorStyle::IBeam, Some(&hitbox)); } else { - window.set_cursor_style(CursorStyle::PointingHand, &hitbox); + window.set_cursor_style(CursorStyle::PointingHand, Some(&hitbox)); } } } @@ -4564,7 +4565,7 @@ impl EditorElement { .read(cx) .all_diff_hunks_expanded() { - window.set_cursor_style(CursorStyle::PointingHand, hunk_hitbox); + window.set_cursor_style(CursorStyle::PointingHand, Some(hunk_hitbox)); } } } @@ -4636,18 +4637,24 @@ impl EditorElement { bounds: layout.position_map.text_hitbox.bounds, }), |window| { - let cursor_style = if self - .editor - .read(cx) + let editor = self.editor.read(cx); + if editor.mouse_cursor_hidden { + window.set_cursor_style(CursorStyle::None, None); + } else if editor .hovered_link_state .as_ref() .is_some_and(|hovered_link_state| !hovered_link_state.links.is_empty()) { - CursorStyle::PointingHand + window.set_cursor_style( + CursorStyle::PointingHand, + Some(&layout.position_map.text_hitbox), + ); } else { - CursorStyle::IBeam + window.set_cursor_style( + CursorStyle::IBeam, + Some(&layout.position_map.text_hitbox), + ); }; - window.set_cursor_style(cursor_style, &layout.position_map.text_hitbox); self.paint_lines_background(layout, window, cx); let invisible_display_ranges = self.paint_highlights(layout, window); @@ -4842,7 +4849,7 @@ impl EditorElement { )); }) } - window.set_cursor_style(CursorStyle::Arrow, &hitbox); + window.set_cursor_style(CursorStyle::Arrow, Some(&hitbox)); } window.on_mouse_event({ @@ -6598,6 +6605,7 @@ impl Element for EditorElement { }, false, ); + // Offset the content_bounds from the text_bounds by the gutter margin (which // is roughly half a character wide) to make hit testing work more like how we want. let content_origin = diff --git a/crates/gpui/examples/window_shadow.rs b/crates/gpui/examples/window_shadow.rs index bbdab1a8ca..0d787680f3 100644 --- a/crates/gpui/examples/window_shadow.rs +++ b/crates/gpui/examples/window_shadow.rs @@ -61,7 +61,7 @@ impl Render for WindowShadow { CursorStyle::ResizeUpRightDownLeft } }, - &hitbox, + Some(&hitbox), ); }, ) diff --git a/crates/gpui/src/elements/div.rs b/crates/gpui/src/elements/div.rs index e868d3a39b..0ef76732fb 100644 --- a/crates/gpui/src/elements/div.rs +++ b/crates/gpui/src/elements/div.rs @@ -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)); } } diff --git a/crates/gpui/src/elements/text.rs b/crates/gpui/src/elements/text.rs index 7caa584c61..45a092edf0 100644 --- a/crates/gpui/src/elements/text.rs +++ b/crates/gpui/src/elements/text.rs @@ -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)) } } diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index ae89b5e172..985d43b4d7 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -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 { diff --git a/crates/gpui/src/platform/linux/platform.rs b/crates/gpui/src/platform/linux/platform.rs index 695e9798f2..a313bff2f1 100644 --- a/crates/gpui/src/platform/linux/platform.rs +++ b/crates/gpui/src/platform/linux/platform.rs @@ -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() } diff --git a/crates/gpui/src/platform/linux/wayland.rs b/crates/gpui/src/platform/linux/wayland.rs index f178e4b643..cf73832b11 100644 --- a/crates/gpui/src/platform/linux/wayland.rs +++ b/crates/gpui/src/platform/linux/wayland.rs @@ -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 + } } } } diff --git a/crates/gpui/src/platform/linux/wayland/client.rs b/crates/gpui/src/platform/linux/wayland/client.rs index 5c5ab5a392..075edac55c 100644 --- a/crates/gpui/src/platform/linux/wayland/client.rs +++ b/crates/gpui/src/platform/linux/wayland/client.rs @@ -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. diff --git a/crates/gpui/src/platform/linux/x11/client.rs b/crates/gpui/src/platform/linux/x11/client.rs index 4a4521b35d..4952843148 100644 --- a/crates/gpui/src/platform/linux/x11/client.rs +++ b/crates/gpui/src/platform/linux/x11/client.rs @@ -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 { + 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) +} diff --git a/crates/gpui/src/platform/mac/platform.rs b/crates/gpui/src/platform/mac/platform.rs index 4fe8383651..8c60369fd5 100644 --- a/crates/gpui/src/platform/mac/platform.rs +++ b/crates/gpui/src/platform/mac/platform.rs @@ -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]; diff --git a/crates/gpui/src/platform/windows/events.rs b/crates/gpui/src/platform/windows/events.rs index f61c14a834..ae88d224a5 100644 --- a/crates/gpui/src/platform/windows/events.rs +++ b/crates/gpui/src/platform/windows/events.rs @@ -1127,7 +1127,19 @@ fn handle_nc_mouse_up_msg( } fn handle_cursor_changed(lparam: LPARAM, state_ptr: Rc) -> Option { - 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) -> Op ) { return None; } - unsafe { SetCursor(Some(state_ptr.state.borrow().current_cursor)) }; + unsafe { + SetCursor(state_ptr.state.borrow().current_cursor); + }; Some(1) } diff --git a/crates/gpui/src/platform/windows/platform.rs b/crates/gpui/src/platform/windows/platform.rs index d4d0328a99..4644650425 100644 --- a/crates/gpui/src/platform/windows/platform.rs +++ b/crates/gpui/src/platform/windows/platform.rs @@ -54,7 +54,7 @@ pub(crate) struct WindowsPlatformState { menus: Vec, dock_menu_actions: Vec>, // NOTE: standard cursor handles don't need to close. - pub(crate) current_cursor: HCURSOR, + pub(crate) current_cursor: Option, } #[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, pub(crate) windows_version: WindowsVersion, pub(crate) validation_number: usize, pub(crate) main_receiver: flume::Receiver, diff --git a/crates/gpui/src/platform/windows/util.rs b/crates/gpui/src/platform/windows/util.rs index 1bf5924b99..6c3ef5e5ac 100644 --- a/crates/gpui/src/platform/windows/util.rs +++ b/crates/gpui/src/platform/windows/util.rs @@ -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 { static ARROW: OnceLock = OnceLock::new(); static IBEAM: OnceLock = OnceLock::new(); static CROSS: OnceLock = 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. diff --git a/crates/gpui/src/platform/windows/window.rs b/crates/gpui/src/platform/windows/window.rs index ff33b0dc18..7b99a445cf 100644 --- a/crates/gpui/src/platform/windows/window.rs +++ b/crates/gpui/src/platform/windows/window.rs @@ -48,7 +48,7 @@ pub struct WindowsWindowState { pub click_state: ClickState, pub system_settings: WindowsSystemSettings, - pub current_cursor: HCURSOR, + pub current_cursor: Option, pub nc_button_pressed: Option, pub display: WindowsDisplay, @@ -76,7 +76,7 @@ impl WindowsWindowState { hwnd: HWND, transparent: bool, cs: &CREATESTRUCTW, - current_cursor: HCURSOR, + current_cursor: Option, display: WindowsDisplay, gpu_context: &BladeContext, ) -> Result { @@ -351,7 +351,7 @@ struct WindowCreateContext<'a> { transparent: bool, is_movable: bool, executor: ForegroundExecutor, - current_cursor: HCURSOR, + current_cursor: Option, windows_version: WindowsVersion, validation_number: usize, main_receiver: flume::Receiver, diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index d10a8d65f6..4f2d9fb01e 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -407,7 +407,7 @@ pub(crate) type AnyMouseListener = #[derive(Clone)] pub(crate) struct CursorStyleRequest { - pub(crate) hitbox_id: HitboxId, + pub(crate) hitbox_id: Option, // 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) } diff --git a/crates/gpui_macros/src/styles.rs b/crates/gpui_macros/src/styles.rs index 39a93b0418..b4e28b8ff5 100644 --- a/crates/gpui_macros/src/styles.rs +++ b/crates/gpui_macros/src/styles.rs @@ -326,6 +326,13 @@ pub fn cursor_style_methods(input: TokenStream) -> TokenStream { self.style().mouse_cursor = Some(gpui::CursorStyle::ResizeLeft); self } + + /// Sets cursor style when hovering over an element to `none`. + /// [Docs](https://tailwindcss.com/docs/cursor) + #visibility fn cursor_none(mut self, cursor: CursorStyle) -> Self { + self.style().mouse_cursor = Some(gpui::CursorStyle::None); + self + } }; output.into() diff --git a/crates/markdown/src/markdown.rs b/crates/markdown/src/markdown.rs index 614e696176..b422817ac5 100644 --- a/crates/markdown/src/markdown.rs +++ b/crates/markdown/src/markdown.rs @@ -411,9 +411,9 @@ impl MarkdownElement { .is_some(); if is_hovering_link { - window.set_cursor_style(CursorStyle::PointingHand, hitbox); + window.set_cursor_style(CursorStyle::PointingHand, Some(hitbox)); } else { - window.set_cursor_style(CursorStyle::IBeam, hitbox); + window.set_cursor_style(CursorStyle::IBeam, Some(hitbox)); } self.on_mouse_event(window, cx, { diff --git a/crates/terminal_view/src/terminal_element.rs b/crates/terminal_view/src/terminal_element.rs index d5fc188c77..cbcc0a0f26 100644 --- a/crates/terminal_view/src/terminal_element.rs +++ b/crates/terminal_view/src/terminal_element.rs @@ -886,9 +886,9 @@ impl Element for TerminalElement { && bounds.contains(&window.mouse_position()) && self.terminal_view.read(cx).hover_target_tooltip.is_some() { - window.set_cursor_style(gpui::CursorStyle::PointingHand, &layout.hitbox); + window.set_cursor_style(gpui::CursorStyle::PointingHand, Some(&layout.hitbox)); } else { - window.set_cursor_style(gpui::CursorStyle::IBeam, &layout.hitbox); + window.set_cursor_style(gpui::CursorStyle::IBeam, Some(&layout.hitbox)); } let cursor = layout.cursor.take(); diff --git a/crates/ui/src/components/indent_guides.rs b/crates/ui/src/components/indent_guides.rs index fdee604055..8c99ba0d8e 100644 --- a/crates/ui/src/components/indent_guides.rs +++ b/crates/ui/src/components/indent_guides.rs @@ -311,7 +311,7 @@ mod uniform_list { }); let mut hovered_hitbox_id = None; for (i, hitbox) in hitboxes.iter().enumerate() { - window.set_cursor_style(gpui::CursorStyle::PointingHand, hitbox); + window.set_cursor_style(gpui::CursorStyle::PointingHand, Some(hitbox)); let indent_guide = &self.indent_guides[i]; let fill_color = if hitbox.is_hovered(window) { hovered_hitbox_id = Some(hitbox.id); diff --git a/crates/workspace/src/pane_group.rs b/crates/workspace/src/pane_group.rs index 34543cc310..ca8d12ebcc 100644 --- a/crates/workspace/src/pane_group.rs +++ b/crates/workspace/src/pane_group.rs @@ -1176,7 +1176,7 @@ mod element { Axis::Vertical => CursorStyle::ResizeRow, Axis::Horizontal => CursorStyle::ResizeColumn, }; - window.set_cursor_style(cursor_style, &handle.hitbox); + window.set_cursor_style(cursor_style, Some(&handle.hitbox)); window.paint_quad(gpui::fill( handle.divider_bounds, cx.theme().colors().pane_group_border, diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 5b439e5d24..80c76efed3 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -6702,7 +6702,7 @@ pub fn client_side_decorations( CursorStyle::ResizeUpRightDownLeft } }, - &hitbox, + Some(&hitbox), ); }, ) diff --git a/docs/src/configuring-zed.md b/docs/src/configuring-zed.md index 23f2826358..2e700b988d 100644 --- a/docs/src/configuring-zed.md +++ b/docs/src/configuring-zed.md @@ -553,6 +553,16 @@ List of `string` values "cursor_shape": "hollow" ``` +## Hide Mouse While Typing + +- Description: Determines whether the mouse cursor should be hidden while typing in an editor or input box. +- Setting: `hide_mouse_while_typing` +- Default: `true` + +**Options** + +`boolean` values + ## Editor Scrollbar - Description: Whether or not to show the editor scrollbar and various elements in it.