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
|
@ -155,6 +155,8 @@
|
||||||
//
|
//
|
||||||
// Default: not set, defaults to "bar"
|
// Default: not set, defaults to "bar"
|
||||||
"cursor_shape": null,
|
"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.
|
// How to highlight the current line in the editor.
|
||||||
//
|
//
|
||||||
// 1. Don't highlight the current line:
|
// 1. Don't highlight the current line:
|
||||||
|
|
|
@ -790,6 +790,8 @@ pub struct Editor {
|
||||||
_scroll_cursor_center_top_bottom_task: Task<()>,
|
_scroll_cursor_center_top_bottom_task: Task<()>,
|
||||||
serialize_selections: Task<()>,
|
serialize_selections: Task<()>,
|
||||||
serialize_folds: Task<()>,
|
serialize_folds: Task<()>,
|
||||||
|
mouse_cursor_hidden: bool,
|
||||||
|
hide_mouse_while_typing: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
|
||||||
|
@ -1568,6 +1570,10 @@ impl Editor {
|
||||||
serialize_folds: Task::ready(()),
|
serialize_folds: Task::ready(()),
|
||||||
text_style_refinement: None,
|
text_style_refinement: None,
|
||||||
load_diff_task: load_uncommitted_diff,
|
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() {
|
if let Some(breakpoints) = this.breakpoint_store.as_ref() {
|
||||||
this._subscriptions
|
this._subscriptions
|
||||||
|
@ -2999,6 +3005,8 @@ impl Editor {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.mouse_cursor_hidden = self.hide_mouse_while_typing;
|
||||||
|
|
||||||
let selections = self.selections.all_adjusted(cx);
|
let selections = self.selections.all_adjusted(cx);
|
||||||
let mut bracket_inserted = false;
|
let mut bracket_inserted = false;
|
||||||
let mut edits = Vec::new();
|
let mut edits = Vec::new();
|
||||||
|
@ -3403,6 +3411,7 @@ impl Editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn newline(&mut self, _: &Newline, window: &mut Window, cx: &mut Context<Self>) {
|
pub fn newline(&mut self, _: &Newline, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
|
self.mouse_cursor_hidden = self.hide_mouse_while_typing;
|
||||||
self.transact(window, cx, |this, window, cx| {
|
self.transact(window, cx, |this, window, cx| {
|
||||||
let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
|
let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
|
||||||
let selections = this.selections.all::<usize>(cx);
|
let selections = this.selections.all::<usize>(cx);
|
||||||
|
@ -3518,6 +3527,8 @@ impl Editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn newline_above(&mut self, _: &NewlineAbove, window: &mut Window, cx: &mut Context<Self>) {
|
pub fn newline_above(&mut self, _: &NewlineAbove, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
|
self.mouse_cursor_hidden = self.hide_mouse_while_typing;
|
||||||
|
|
||||||
let buffer = self.buffer.read(cx);
|
let buffer = self.buffer.read(cx);
|
||||||
let snapshot = buffer.snapshot(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>) {
|
pub fn newline_below(&mut self, _: &NewlineBelow, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
|
self.mouse_cursor_hidden = self.hide_mouse_while_typing;
|
||||||
|
|
||||||
let buffer = self.buffer.read(cx);
|
let buffer = self.buffer.read(cx);
|
||||||
let snapshot = buffer.snapshot(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>) {
|
pub fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
|
self.mouse_cursor_hidden = self.hide_mouse_while_typing;
|
||||||
self.transact(window, cx, |this, window, cx| {
|
self.transact(window, cx, |this, window, cx| {
|
||||||
this.select_autoclose_pair(window, cx);
|
this.select_autoclose_pair(window, cx);
|
||||||
let mut linked_ranges = HashMap::<_, Vec<_>>::default();
|
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>) {
|
pub fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
|
self.mouse_cursor_hidden = self.hide_mouse_while_typing;
|
||||||
self.transact(window, cx, |this, window, cx| {
|
self.transact(window, cx, |this, window, cx| {
|
||||||
this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
|
this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
|
||||||
let line_mode = s.line_mode;
|
let line_mode = s.line_mode;
|
||||||
|
@ -7884,7 +7899,7 @@ impl Editor {
|
||||||
if self.move_to_prev_snippet_tabstop(window, cx) {
|
if self.move_to_prev_snippet_tabstop(window, cx) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
self.mouse_cursor_hidden = self.hide_mouse_while_typing;
|
||||||
self.outdent(&Outdent, window, cx);
|
self.outdent(&Outdent, window, cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7892,7 +7907,7 @@ impl Editor {
|
||||||
if self.move_to_next_snippet_tabstop(window, cx) || self.read_only(cx) {
|
if self.move_to_next_snippet_tabstop(window, cx) || self.read_only(cx) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
self.mouse_cursor_hidden = self.hide_mouse_while_typing;
|
||||||
let mut selections = self.selections.all_adjusted(cx);
|
let mut selections = self.selections.all_adjusted(cx);
|
||||||
let buffer = self.buffer.read(cx);
|
let buffer = self.buffer.read(cx);
|
||||||
let snapshot = buffer.snapshot(cx);
|
let snapshot = buffer.snapshot(cx);
|
||||||
|
@ -16669,6 +16684,11 @@ impl Editor {
|
||||||
self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
|
self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
|
||||||
self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
|
self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
|
||||||
self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
|
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 {
|
if old_cursor_shape != self.cursor_shape {
|
||||||
|
|
|
@ -39,6 +39,7 @@ pub struct EditorSettings {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub go_to_definition_fallback: GoToDefinitionFallback,
|
pub go_to_definition_fallback: GoToDefinitionFallback,
|
||||||
pub jupyter: Jupyter,
|
pub jupyter: Jupyter,
|
||||||
|
pub hide_mouse_while_typing: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||||
|
@ -235,6 +236,10 @@ pub struct EditorSettingsContent {
|
||||||
///
|
///
|
||||||
/// Default: None
|
/// Default: None
|
||||||
pub cursor_shape: Option<CursorShape>,
|
pub cursor_shape: Option<CursorShape>,
|
||||||
|
/// Determines whether the mouse cursor should be hidden while typing in an editor or input box.
|
||||||
|
///
|
||||||
|
/// Default: true
|
||||||
|
pub hide_mouse_while_typing: Option<bool>,
|
||||||
/// How to highlight the current line in the editor.
|
/// How to highlight the current line in the editor.
|
||||||
///
|
///
|
||||||
/// Default: all
|
/// Default: all
|
||||||
|
|
|
@ -894,6 +894,7 @@ impl EditorElement {
|
||||||
let gutter_hovered = gutter_hitbox.is_hovered(window);
|
let gutter_hovered = gutter_hitbox.is_hovered(window);
|
||||||
editor.set_gutter_hovered(gutter_hovered, cx);
|
editor.set_gutter_hovered(gutter_hovered, cx);
|
||||||
editor.gutter_breakpoint_indicator = None;
|
editor.gutter_breakpoint_indicator = None;
|
||||||
|
editor.mouse_cursor_hidden = false;
|
||||||
|
|
||||||
if gutter_hovered {
|
if gutter_hovered {
|
||||||
let new_point = position_map
|
let new_point = position_map
|
||||||
|
@ -4307,7 +4308,7 @@ impl EditorElement {
|
||||||
let is_singleton = self.editor.read(cx).is_singleton(cx);
|
let is_singleton = self.editor.read(cx).is_singleton(cx);
|
||||||
|
|
||||||
let line_height = layout.position_map.line_height;
|
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 {
|
for LineNumberLayout {
|
||||||
shaped_line,
|
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 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.
|
// In multi buffers, we open file at the line number clicked, so use a pointing hand cursor.
|
||||||
if is_singleton {
|
if is_singleton {
|
||||||
window.set_cursor_style(CursorStyle::IBeam, &hitbox);
|
window.set_cursor_style(CursorStyle::IBeam, Some(&hitbox));
|
||||||
} else {
|
} else {
|
||||||
window.set_cursor_style(CursorStyle::PointingHand, &hitbox);
|
window.set_cursor_style(CursorStyle::PointingHand, Some(&hitbox));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4564,7 +4565,7 @@ impl EditorElement {
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.all_diff_hunks_expanded()
|
.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,
|
bounds: layout.position_map.text_hitbox.bounds,
|
||||||
}),
|
}),
|
||||||
|window| {
|
|window| {
|
||||||
let cursor_style = if self
|
let editor = self.editor.read(cx);
|
||||||
.editor
|
if editor.mouse_cursor_hidden {
|
||||||
.read(cx)
|
window.set_cursor_style(CursorStyle::None, None);
|
||||||
|
} else if editor
|
||||||
.hovered_link_state
|
.hovered_link_state
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.is_some_and(|hovered_link_state| !hovered_link_state.links.is_empty())
|
.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 {
|
} 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);
|
self.paint_lines_background(layout, window, cx);
|
||||||
let invisible_display_ranges = self.paint_highlights(layout, window);
|
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({
|
window.on_mouse_event({
|
||||||
|
@ -6598,6 +6605,7 @@ impl Element for EditorElement {
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Offset the content_bounds from the text_bounds by the gutter margin (which
|
// 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.
|
// is roughly half a character wide) to make hit testing work more like how we want.
|
||||||
let content_origin =
|
let content_origin =
|
||||||
|
|
|
@ -61,7 +61,7 @@ impl Render for WindowShadow {
|
||||||
CursorStyle::ResizeUpRightDownLeft
|
CursorStyle::ResizeUpRightDownLeft
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
&hitbox,
|
Some(&hitbox),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
@ -1617,7 +1617,7 @@ impl Interactivity {
|
||||||
|
|
||||||
if !cx.has_active_drag() {
|
if !cx.has_active_drag() {
|
||||||
if let Some(mouse_cursor) = style.mouse_cursor {
|
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()
|
.iter()
|
||||||
.any(|range| range.contains(&ix))
|
.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
|
/// A cursor indicating that the operation will result in a context menu
|
||||||
/// corresponds to the CSS cursor value `context-menu`
|
/// corresponds to the CSS cursor value `context-menu`
|
||||||
ContextualMenu,
|
ContextualMenu,
|
||||||
|
|
||||||
|
/// Hide the cursor
|
||||||
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for CursorStyle {
|
impl Default for CursorStyle {
|
||||||
|
|
|
@ -666,6 +666,12 @@ impl CursorStyle {
|
||||||
CursorStyle::DragLink => "alias",
|
CursorStyle::DragLink => "alias",
|
||||||
CursorStyle::DragCopy => "copy",
|
CursorStyle::DragCopy => "copy",
|
||||||
CursorStyle::ContextualMenu => "context-menu",
|
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()
|
.to_string()
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,12 @@ impl CursorStyle {
|
||||||
CursorStyle::DragLink => Shape::Alias,
|
CursorStyle::DragLink => Shape::Alias,
|
||||||
CursorStyle::DragCopy => Shape::Copy,
|
CursorStyle::DragCopy => Shape::Copy,
|
||||||
CursorStyle::ContextualMenu => Shape::ContextMenu,
|
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);
|
let serial = state.serial_tracker.get(SerialKind::MouseEnter);
|
||||||
state.cursor_style = Some(style);
|
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());
|
cursor_shape_device.set_shape(serial, style.to_shape());
|
||||||
} else if let Some(focused_window) = &state.mouse_focused_window {
|
} else if let Some(focused_window) = &state.mouse_focused_window {
|
||||||
// cursor-shape-v1 isn't supported, set the cursor using a surface.
|
// 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) {
|
let cursor = match state.cursor_cache.get(&style) {
|
||||||
Some(cursor) => *cursor,
|
Some(cursor) => *cursor,
|
||||||
None => {
|
None => {
|
||||||
let Some(cursor) = state
|
let Some(cursor) = (match style {
|
||||||
.cursor_handle
|
CursorStyle::None => create_invisible_cursor(&state.xcb_connection).log_err(),
|
||||||
.load_cursor(&state.xcb_connection, &style.to_icon_name())
|
_ => state
|
||||||
.log_err()
|
.cursor_handle
|
||||||
else {
|
.load_cursor(&state.xcb_connection, &style.to_icon_name())
|
||||||
|
.log_err(),
|
||||||
|
}) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
state.cursor_cache.insert(style, cursor);
|
state.cursor_cache.insert(style, cursor);
|
||||||
cursor
|
cursor
|
||||||
}
|
}
|
||||||
|
@ -1938,3 +1941,19 @@ fn make_scroll_wheel_event(
|
||||||
touch_phase: TouchPhase::default(),
|
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).
|
/// in macOS's [NSCursor](https://developer.apple.com/documentation/appkit/nscursor).
|
||||||
fn set_cursor_style(&self, style: CursorStyle) {
|
fn set_cursor_style(&self, style: CursorStyle) {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
if style == CursorStyle::None {
|
||||||
|
let _: () = msg_send![class!(NSCursor), setHiddenUntilMouseMoves:YES];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let new_cursor: id = match style {
|
let new_cursor: id = match style {
|
||||||
CursorStyle::Arrow => msg_send![class!(NSCursor), arrowCursor],
|
CursorStyle::Arrow => msg_send![class!(NSCursor), arrowCursor],
|
||||||
CursorStyle::IBeam => msg_send![class!(NSCursor), IBeamCursor],
|
CursorStyle::IBeam => msg_send![class!(NSCursor), IBeamCursor],
|
||||||
|
@ -925,6 +930,7 @@ impl Platform for MacPlatform {
|
||||||
CursorStyle::DragLink => msg_send![class!(NSCursor), dragLinkCursor],
|
CursorStyle::DragLink => msg_send![class!(NSCursor), dragLinkCursor],
|
||||||
CursorStyle::DragCopy => msg_send![class!(NSCursor), dragCopyCursor],
|
CursorStyle::DragCopy => msg_send![class!(NSCursor), dragCopyCursor],
|
||||||
CursorStyle::ContextualMenu => msg_send![class!(NSCursor), contextualMenuCursor],
|
CursorStyle::ContextualMenu => msg_send![class!(NSCursor), contextualMenuCursor],
|
||||||
|
CursorStyle::None => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let old_cursor: id = msg_send![class!(NSCursor), currentCursor];
|
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> {
|
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)
|
Some(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1138,7 +1150,9 @@ fn handle_set_cursor(lparam: LPARAM, state_ptr: Rc<WindowsWindowStatePtr>) -> Op
|
||||||
) {
|
) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
unsafe { SetCursor(Some(state_ptr.state.borrow().current_cursor)) };
|
unsafe {
|
||||||
|
SetCursor(state_ptr.state.borrow().current_cursor);
|
||||||
|
};
|
||||||
Some(1)
|
Some(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,7 +54,7 @@ pub(crate) struct WindowsPlatformState {
|
||||||
menus: Vec<OwnedMenu>,
|
menus: Vec<OwnedMenu>,
|
||||||
dock_menu_actions: Vec<Box<dyn Action>>,
|
dock_menu_actions: Vec<Box<dyn Action>>,
|
||||||
// NOTE: standard cursor handles don't need to close.
|
// NOTE: standard cursor handles don't need to close.
|
||||||
pub(crate) current_cursor: HCURSOR,
|
pub(crate) current_cursor: Option<HCURSOR>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -558,11 +558,11 @@ impl Platform for WindowsPlatform {
|
||||||
fn set_cursor_style(&self, style: CursorStyle) {
|
fn set_cursor_style(&self, style: CursorStyle) {
|
||||||
let hcursor = load_cursor(style);
|
let hcursor = load_cursor(style);
|
||||||
let mut lock = self.state.borrow_mut();
|
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(
|
self.post_message(
|
||||||
WM_GPUI_CURSOR_STYLE_CHANGED,
|
WM_GPUI_CURSOR_STYLE_CHANGED,
|
||||||
WPARAM(0),
|
WPARAM(0),
|
||||||
LPARAM(hcursor.0 as isize),
|
LPARAM(hcursor.map_or(0, |c| c.0 as isize)),
|
||||||
);
|
);
|
||||||
lock.current_cursor = hcursor;
|
lock.current_cursor = hcursor;
|
||||||
}
|
}
|
||||||
|
@ -683,7 +683,7 @@ impl Drop for WindowsPlatform {
|
||||||
pub(crate) struct WindowCreationInfo {
|
pub(crate) struct WindowCreationInfo {
|
||||||
pub(crate) icon: HICON,
|
pub(crate) icon: HICON,
|
||||||
pub(crate) executor: ForegroundExecutor,
|
pub(crate) executor: ForegroundExecutor,
|
||||||
pub(crate) current_cursor: HCURSOR,
|
pub(crate) current_cursor: Option<HCURSOR>,
|
||||||
pub(crate) windows_version: WindowsVersion,
|
pub(crate) windows_version: WindowsVersion,
|
||||||
pub(crate) validation_number: usize,
|
pub(crate) validation_number: usize,
|
||||||
pub(crate) main_receiver: flume::Receiver<Runnable>,
|
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)
|
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 ARROW: OnceLock<SafeCursor> = OnceLock::new();
|
||||||
static IBEAM: OnceLock<SafeCursor> = OnceLock::new();
|
static IBEAM: OnceLock<SafeCursor> = OnceLock::new();
|
||||||
static CROSS: 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::ResizeUpDown
|
||||||
| CursorStyle::ResizeRow => (&SIZENS, IDC_SIZENS),
|
| CursorStyle::ResizeRow => (&SIZENS, IDC_SIZENS),
|
||||||
CursorStyle::OperationNotAllowed => (&NO, IDC_NO),
|
CursorStyle::OperationNotAllowed => (&NO, IDC_NO),
|
||||||
|
CursorStyle::None => return None,
|
||||||
_ => (&ARROW, IDC_ARROW),
|
_ => (&ARROW, IDC_ARROW),
|
||||||
};
|
};
|
||||||
*(*lock.get_or_init(|| {
|
Some(
|
||||||
HCURSOR(
|
*(*lock.get_or_init(|| {
|
||||||
unsafe { LoadImageW(None, name, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED) }
|
HCURSOR(
|
||||||
.log_err()
|
unsafe { LoadImageW(None, name, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED) }
|
||||||
.unwrap_or_default()
|
.log_err()
|
||||||
.0,
|
.unwrap_or_default()
|
||||||
)
|
.0,
|
||||||
.into()
|
)
|
||||||
}))
|
.into()
|
||||||
|
})),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This function is used to configure the dark mode for the window built-in title bar.
|
/// 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 click_state: ClickState,
|
||||||
pub system_settings: WindowsSystemSettings,
|
pub system_settings: WindowsSystemSettings,
|
||||||
pub current_cursor: HCURSOR,
|
pub current_cursor: Option<HCURSOR>,
|
||||||
pub nc_button_pressed: Option<u32>,
|
pub nc_button_pressed: Option<u32>,
|
||||||
|
|
||||||
pub display: WindowsDisplay,
|
pub display: WindowsDisplay,
|
||||||
|
@ -76,7 +76,7 @@ impl WindowsWindowState {
|
||||||
hwnd: HWND,
|
hwnd: HWND,
|
||||||
transparent: bool,
|
transparent: bool,
|
||||||
cs: &CREATESTRUCTW,
|
cs: &CREATESTRUCTW,
|
||||||
current_cursor: HCURSOR,
|
current_cursor: Option<HCURSOR>,
|
||||||
display: WindowsDisplay,
|
display: WindowsDisplay,
|
||||||
gpu_context: &BladeContext,
|
gpu_context: &BladeContext,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
|
@ -351,7 +351,7 @@ struct WindowCreateContext<'a> {
|
||||||
transparent: bool,
|
transparent: bool,
|
||||||
is_movable: bool,
|
is_movable: bool,
|
||||||
executor: ForegroundExecutor,
|
executor: ForegroundExecutor,
|
||||||
current_cursor: HCURSOR,
|
current_cursor: Option<HCURSOR>,
|
||||||
windows_version: WindowsVersion,
|
windows_version: WindowsVersion,
|
||||||
validation_number: usize,
|
validation_number: usize,
|
||||||
main_receiver: flume::Receiver<Runnable>,
|
main_receiver: flume::Receiver<Runnable>,
|
||||||
|
|
|
@ -407,7 +407,7 @@ pub(crate) type AnyMouseListener =
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub(crate) struct CursorStyleRequest {
|
pub(crate) struct CursorStyleRequest {
|
||||||
pub(crate) hitbox_id: HitboxId,
|
pub(crate) hitbox_id: Option<HitboxId>, // None represents whole window
|
||||||
pub(crate) style: CursorStyle,
|
pub(crate) style: CursorStyle,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1928,10 +1928,10 @@ impl Window {
|
||||||
|
|
||||||
/// Updates the cursor style at the platform level. This method should only be called
|
/// Updates the cursor style at the platform level. This method should only be called
|
||||||
/// during the prepaint phase of element drawing.
|
/// 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.invalidator.debug_assert_paint();
|
||||||
self.next_frame.cursor_styles.push(CursorStyleRequest {
|
self.next_frame.cursor_styles.push(CursorStyleRequest {
|
||||||
hitbox_id: hitbox.id,
|
hitbox_id: hitbox.map(|hitbox| hitbox.id),
|
||||||
style,
|
style,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -2984,7 +2984,11 @@ impl Window {
|
||||||
.cursor_styles
|
.cursor_styles
|
||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.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)
|
.map(|request| request.style)
|
||||||
.unwrap_or(CursorStyle::Arrow);
|
.unwrap_or(CursorStyle::Arrow);
|
||||||
cx.platform.set_cursor_style(style);
|
cx.platform.set_cursor_style(style);
|
||||||
|
@ -3241,6 +3245,7 @@ impl Window {
|
||||||
keystroke,
|
keystroke,
|
||||||
&dispatch_path,
|
&dispatch_path,
|
||||||
);
|
);
|
||||||
|
|
||||||
if !match_result.to_replay.is_empty() {
|
if !match_result.to_replay.is_empty() {
|
||||||
self.replay_pending_input(match_result.to_replay, cx)
|
self.replay_pending_input(match_result.to_replay, cx)
|
||||||
}
|
}
|
||||||
|
|
|
@ -326,6 +326,13 @@ pub fn cursor_style_methods(input: TokenStream) -> TokenStream {
|
||||||
self.style().mouse_cursor = Some(gpui::CursorStyle::ResizeLeft);
|
self.style().mouse_cursor = Some(gpui::CursorStyle::ResizeLeft);
|
||||||
self
|
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()
|
output.into()
|
||||||
|
|
|
@ -411,9 +411,9 @@ impl MarkdownElement {
|
||||||
.is_some();
|
.is_some();
|
||||||
|
|
||||||
if is_hovering_link {
|
if is_hovering_link {
|
||||||
window.set_cursor_style(CursorStyle::PointingHand, hitbox);
|
window.set_cursor_style(CursorStyle::PointingHand, Some(hitbox));
|
||||||
} else {
|
} else {
|
||||||
window.set_cursor_style(CursorStyle::IBeam, hitbox);
|
window.set_cursor_style(CursorStyle::IBeam, Some(hitbox));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.on_mouse_event(window, cx, {
|
self.on_mouse_event(window, cx, {
|
||||||
|
|
|
@ -886,9 +886,9 @@ impl Element for TerminalElement {
|
||||||
&& bounds.contains(&window.mouse_position())
|
&& bounds.contains(&window.mouse_position())
|
||||||
&& self.terminal_view.read(cx).hover_target_tooltip.is_some()
|
&& 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 {
|
} 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();
|
let cursor = layout.cursor.take();
|
||||||
|
|
|
@ -311,7 +311,7 @@ mod uniform_list {
|
||||||
});
|
});
|
||||||
let mut hovered_hitbox_id = None;
|
let mut hovered_hitbox_id = None;
|
||||||
for (i, hitbox) in hitboxes.iter().enumerate() {
|
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 indent_guide = &self.indent_guides[i];
|
||||||
let fill_color = if hitbox.is_hovered(window) {
|
let fill_color = if hitbox.is_hovered(window) {
|
||||||
hovered_hitbox_id = Some(hitbox.id);
|
hovered_hitbox_id = Some(hitbox.id);
|
||||||
|
|
|
@ -1176,7 +1176,7 @@ mod element {
|
||||||
Axis::Vertical => CursorStyle::ResizeRow,
|
Axis::Vertical => CursorStyle::ResizeRow,
|
||||||
Axis::Horizontal => CursorStyle::ResizeColumn,
|
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(
|
window.paint_quad(gpui::fill(
|
||||||
handle.divider_bounds,
|
handle.divider_bounds,
|
||||||
cx.theme().colors().pane_group_border,
|
cx.theme().colors().pane_group_border,
|
||||||
|
|
|
@ -6702,7 +6702,7 @@ pub fn client_side_decorations(
|
||||||
CursorStyle::ResizeUpRightDownLeft
|
CursorStyle::ResizeUpRightDownLeft
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
&hitbox,
|
Some(&hitbox),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
@ -553,6 +553,16 @@ List of `string` values
|
||||||
"cursor_shape": "hollow"
|
"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
|
## Editor Scrollbar
|
||||||
|
|
||||||
- Description: Whether or not to show the editor scrollbar and various elements in it.
|
- Description: Whether or not to show the editor scrollbar and various elements in it.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue