From 1da2441e7b2a06df1bde1d8460f36aa91b13b5c3 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Mon, 1 Apr 2024 17:22:59 -0700 Subject: [PATCH] Fix assorted linux issues (#10061) - Fix a bug where modifiers would be dispatched before they changed - Add a secondary modifier - Improve keybindings Release Notes: - N/A --- assets/keymaps/default-linux.json | 25 ++-- assets/settings/default.json | 3 +- crates/editor/src/editor_settings.rs | 3 +- crates/editor/src/element.rs | 9 +- crates/editor/src/hover_links.rs | 52 +++---- crates/file_finder/src/file_finder_tests.rs | 14 +- crates/gpui/src/elements/div.rs | 6 +- crates/gpui/src/platform/keystroke.rs | 87 ++++++++++-- .../gpui/src/platform/linux/wayland/client.rs | 129 ++++++++---------- crates/gpui/src/platform/linux/x11/event.rs | 2 +- crates/gpui/src/platform/mac/events.rs | 4 +- crates/gpui/src/platform/mac/platform.rs | 2 +- crates/gpui/src/platform/mac/window.rs | 2 +- crates/gpui/src/platform/windows/window.rs | 2 +- crates/picker/src/picker.rs | 6 +- crates/project_panel/src/project_panel.rs | 2 +- crates/terminal/src/mappings/keys.rs | 4 +- crates/terminal/src/terminal.rs | 16 +-- crates/ui/src/components/keybinding.rs | 2 +- crates/vim/src/test/neovim_connection.rs | 4 +- 20 files changed, 220 insertions(+), 154 deletions(-) diff --git a/assets/keymaps/default-linux.json b/assets/keymaps/default-linux.json index db730c98a0..8f7eb5b2c8 100644 --- a/assets/keymaps/default-linux.json +++ b/assets/keymaps/default-linux.json @@ -219,7 +219,7 @@ "context": "BufferSearchBar && in_replace", "bindings": { "enter": "search::ReplaceNext", - "cmd-enter": "search::ReplaceAll" + "ctrl-enter": "search::ReplaceAll" } }, { @@ -258,7 +258,7 @@ "bindings": { "escape": "project_search::ToggleFocus", "alt-tab": "search::CycleMode", - "cmd-shift-h": "search::ToggleReplace", + "ctrl-shift-h": "search::ToggleReplace", "alt-ctrl-g": "search::ActivateRegexMode", "alt-ctrl-x": "search::ActivateTextMode" } @@ -304,8 +304,10 @@ } ], "ctrl-alt-shift-down": "editor::DuplicateLine", - "ctrl-shift-right": "editor::SelectLargerSyntaxNode", - "ctrl-shift-left": "editor::SelectSmallerSyntaxNode", + "ctrl-shift-left": "editor::SelectToPreviousWordStart", + "ctrl-shift-right": "editor::SelectToNextWordEnd", + "ctrl-shift-up": "editor::SelectLargerSyntaxNode", //todo(linux) tmp keybinding + "ctrl-shift-down": "editor::SelectSmallerSyntaxNode", //todo(linux) tmp keybinding "ctrl-d": [ "editor::SelectNext", { @@ -354,14 +356,14 @@ "ctrl-shift-]": "editor::UnfoldLines", "ctrl-space": "editor::ShowCompletions", "ctrl-.": "editor::ToggleCodeActions", - "alt-cmd-r": "editor::RevealInFinder", + "alt-ctrl-r": "editor::RevealInFinder", "ctrl-alt-shift-c": "editor::DisplayCursorNames" } }, { "context": "Editor && mode == full", "bindings": { - "cmd-shift-o": "outline::Toggle", + "ctrl-shift-o": "outline::Toggle", "ctrl-g": "go_to_line::Toggle" } }, @@ -419,7 +421,7 @@ "ctrl-shift-f": "pane::DeploySearch", "ctrl-k ctrl-s": "zed::OpenKeymap", "ctrl-k ctrl-t": "theme_selector::Toggle", - "ctrl-t": "project_symbols::Toggle", + "ctrl-shift-t": "project_symbols::Toggle", "ctrl-p": "file_finder::Toggle", "ctrl-tab": "tab_switcher::Toggle", "ctrl-shift-tab": ["tab_switcher::Toggle", { "select_last": true }], @@ -549,7 +551,7 @@ "delete": "project_panel::Delete", "ctrl-backspace": ["project_panel::Delete", { "skip_prompt": true }], "ctrl-delete": ["project_panel::Delete", { "skip_prompt": true }], - "alt-cmd-r": "project_panel::RevealInFinder", + "alt-ctrl-r": "project_panel::RevealInFinder", "alt-shift-f": "project_panel::NewSearchInDirectory" } }, @@ -610,7 +612,12 @@ "pagedown": ["terminal::SendKeystroke", "pagedown"], "escape": ["terminal::SendKeystroke", "escape"], "enter": ["terminal::SendKeystroke", "enter"], - "ctrl-c": ["terminal::SendKeystroke", "ctrl-c"] + "ctrl-c": ["terminal::SendKeystroke", "ctrl-c"], + + // Some nice conveniences + "ctrl-backspace": ["terminal::SendText", "\u0015"], + "ctrl-right": ["terminal::SendText", "\u0005"], + "ctrl-left": ["terminal::SendText", "\u0001"] } } ] diff --git a/assets/settings/default.json b/assets/settings/default.json index d5cdb1238c..8b39ac11b9 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -48,7 +48,8 @@ // which gives the same size as all other panes. "active_pane_magnification": 1.0, // The key to use for adding multiple cursors - // Currently "alt" or "cmd" are supported. + // Currently "alt" or "cmd_or_ctrl" (also aliased as + // "cmd" and "ctrl") are supported. "multi_cursor_modifier": "alt", // Whether to enable vim modes and key bindings "vim_mode": false, diff --git a/crates/editor/src/editor_settings.rs b/crates/editor/src/editor_settings.rs index d3a122954f..bf960dc751 100644 --- a/crates/editor/src/editor_settings.rs +++ b/crates/editor/src/editor_settings.rs @@ -92,7 +92,8 @@ pub enum ShowScrollbar { #[serde(rename_all = "snake_case")] pub enum MultiCursorModifier { Alt, - Cmd, + #[serde(alias = "cmd", alias = "ctrl")] + CmdOrCtrl, } #[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 787e70ff88..1abea7c471 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -449,7 +449,8 @@ impl EditorElement { }, cx, ); - } else if modifiers.shift && !modifiers.control && !modifiers.alt && !modifiers.command { + } else if modifiers.shift && !modifiers.control && !modifiers.alt && !modifiers.secondary() + { editor.select( SelectPhase::Extend { position, @@ -461,7 +462,7 @@ impl EditorElement { let multi_cursor_setting = EditorSettings::get_global(cx).multi_cursor_modifier; let multi_cursor_modifier = match multi_cursor_setting { MultiCursorModifier::Alt => modifiers.alt, - MultiCursorModifier::Cmd => modifiers.command, + MultiCursorModifier::CmdOrCtrl => modifiers.secondary(), }; editor.select( SelectPhase::Begin { @@ -513,8 +514,8 @@ impl EditorElement { let multi_cursor_setting = EditorSettings::get_global(cx).multi_cursor_modifier; let multi_cursor_modifier = match multi_cursor_setting { - MultiCursorModifier::Alt => event.modifiers.command, - MultiCursorModifier::Cmd => event.modifiers.alt, + MultiCursorModifier::Alt => event.modifiers.secondary(), + MultiCursorModifier::CmdOrCtrl => event.modifiers.alt, }; if !pending_nonempty_selections && multi_cursor_modifier && text_hitbox.is_hovered(cx) { diff --git a/crates/editor/src/hover_links.rs b/crates/editor/src/hover_links.rs index 9d2b02385e..7475b8d09f 100644 --- a/crates/editor/src/hover_links.rs +++ b/crates/editor/src/hover_links.rs @@ -93,7 +93,7 @@ impl Editor { modifiers: Modifiers, cx: &mut ViewContext, ) { - if !modifiers.command || self.has_pending_selection() { + if !modifiers.secondary() || self.has_pending_selection() { self.hide_hovered_link(cx); return; } @@ -113,7 +113,7 @@ impl Editor { &snapshot, point_for_position, self, - modifiers.command, + modifiers.secondary(), modifiers.shift, cx, ); @@ -256,7 +256,7 @@ pub fn update_inlay_link_and_hover_points( snapshot: &EditorSnapshot, point_for_position: PointForPosition, editor: &mut Editor, - cmd_held: bool, + secondary_held: bool, shift_held: bool, cx: &mut ViewContext<'_, Editor>, ) { @@ -394,7 +394,9 @@ pub fn update_inlay_link_and_hover_points( if let Some((language_server_id, location)) = hovered_hint_part.location { - if cmd_held && !editor.has_pending_nonempty_selection() { + if secondary_held + && !editor.has_pending_nonempty_selection() + { go_to_definition_updated = true; show_link_definition( shift_held, @@ -762,7 +764,7 @@ mod tests { let «variable» = A; "}); - cx.simulate_modifiers_change(Modifiers::command()); + cx.simulate_modifiers_change(Modifiers::secondary_key()); cx.run_until_parked(); // Assert no link highlights cx.assert_editor_text_highlights::(indoc! {" @@ -823,7 +825,7 @@ mod tests { ]))) }); - cx.simulate_mouse_move(hover_point, Modifiers::command()); + cx.simulate_mouse_move(hover_point, Modifiers::secondary_key()); requests.next().await; cx.background_executor.run_until_parked(); cx.assert_editor_text_highlights::(indoc! {" @@ -849,7 +851,7 @@ mod tests { ]))) }); - cx.simulate_mouse_move(hover_point, Modifiers::command()); + cx.simulate_mouse_move(hover_point, Modifiers::secondary_key()); requests.next().await; cx.background_executor.run_until_parked(); cx.assert_editor_text_highlights::(indoc! {" @@ -868,7 +870,7 @@ mod tests { // No definitions returned Ok(Some(lsp::GotoDefinitionResponse::Link(vec![]))) }); - cx.simulate_mouse_move(hover_point, Modifiers::command()); + cx.simulate_mouse_move(hover_point, Modifiers::secondary_key()); requests.next().await; cx.background_executor.run_until_parked(); @@ -912,7 +914,7 @@ mod tests { ]))) }); - cx.simulate_modifiers_change(Modifiers::command()); + cx.simulate_modifiers_change(Modifiers::secondary_key()); requests.next().await; cx.background_executor.run_until_parked(); @@ -928,7 +930,7 @@ mod tests { fn do_work() { test(); } "}); - cx.simulate_mouse_move(hover_point, Modifiers::command()); + cx.simulate_mouse_move(hover_point, Modifiers::secondary_key()); cx.background_executor.run_until_parked(); cx.assert_editor_text_highlights::(indoc! {" fn test() { do_work(); } @@ -940,7 +942,7 @@ mod tests { fn test() { do_work(); } fn do_work() { tesˇt(); } "}); - cx.simulate_mouse_move(hover_point, Modifiers::command()); + cx.simulate_mouse_move(hover_point, Modifiers::secondary_key()); cx.background_executor.run_until_parked(); cx.assert_editor_text_highlights::(indoc! {" fn test() { do_work(); } @@ -948,7 +950,7 @@ mod tests { "}); // Cmd click with existing definition doesn't re-request and dismisses highlight - cx.simulate_click(hover_point, Modifiers::command()); + cx.simulate_click(hover_point, Modifiers::secondary_key()); cx.lsp .handle_request::(move |_, _| async move { // Empty definition response to make sure we aren't hitting the lsp and using @@ -987,7 +989,7 @@ mod tests { }, ]))) }); - cx.simulate_click(hover_point, Modifiers::command()); + cx.simulate_click(hover_point, Modifiers::secondary_key()); requests.next().await; cx.background_executor.run_until_parked(); cx.assert_editor_state(indoc! {" @@ -1030,7 +1032,7 @@ mod tests { s.set_pending_anchor_range(anchor_range, crate::SelectMode::Character) }); }); - cx.simulate_mouse_move(hover_point, Modifiers::command()); + cx.simulate_mouse_move(hover_point, Modifiers::secondary_key()); cx.background_executor.run_until_parked(); assert!(requests.try_next().is_err()); cx.assert_editor_text_highlights::(indoc! {" @@ -1144,7 +1146,7 @@ mod tests { }); // Press cmd to trigger highlight let hover_point = cx.pixel_position_for(midpoint); - cx.simulate_mouse_move(hover_point, Modifiers::command()); + cx.simulate_mouse_move(hover_point, Modifiers::secondary_key()); cx.background_executor.run_until_parked(); cx.update_editor(|editor, cx| { let snapshot = editor.snapshot(cx); @@ -1175,9 +1177,9 @@ mod tests { assert!(actual_ranges.is_empty(), "When no cmd is pressed, should have no hint label selected, but got: {actual_ranges:?}"); }); - cx.simulate_modifiers_change(Modifiers::command()); + cx.simulate_modifiers_change(Modifiers::secondary_key()); cx.background_executor.run_until_parked(); - cx.simulate_click(hover_point, Modifiers::command()); + cx.simulate_click(hover_point, Modifiers::secondary_key()); cx.background_executor.run_until_parked(); cx.assert_editor_state(indoc! {" struct «TestStructˇ»; @@ -1207,12 +1209,12 @@ mod tests { Let's test a [complex](https://zed.dev/channel/had-(ˇoops)) case. "}); - cx.simulate_mouse_move(screen_coord, Modifiers::command()); + cx.simulate_mouse_move(screen_coord, Modifiers::secondary_key()); cx.assert_editor_text_highlights::(indoc! {" Let's test a [complex](«https://zed.dev/channel/had-(oops)ˇ») case. "}); - cx.simulate_click(screen_coord, Modifiers::command()); + cx.simulate_click(screen_coord, Modifiers::secondary_key()); assert_eq!( cx.opened_url(), Some("https://zed.dev/channel/had-(oops)".into()) @@ -1235,12 +1237,12 @@ mod tests { let screen_coord = cx.pixel_position(indoc! {"https://zed.dev/relˇeases is a cool webpage."}); - cx.simulate_mouse_move(screen_coord, Modifiers::command()); + cx.simulate_mouse_move(screen_coord, Modifiers::secondary_key()); cx.assert_editor_text_highlights::( indoc! {"«https://zed.dev/releasesˇ» is a cool webpage."}, ); - cx.simulate_click(screen_coord, Modifiers::command()); + cx.simulate_click(screen_coord, Modifiers::secondary_key()); assert_eq!(cx.opened_url(), Some("https://zed.dev/releases".into())); } @@ -1260,12 +1262,12 @@ mod tests { let screen_coord = cx.pixel_position(indoc! {"A cool webpage is https://zed.dev/releˇases"}); - cx.simulate_mouse_move(screen_coord, Modifiers::command()); + cx.simulate_mouse_move(screen_coord, Modifiers::secondary_key()); cx.assert_editor_text_highlights::( indoc! {"A cool webpage is «https://zed.dev/releasesˇ»"}, ); - cx.simulate_click(screen_coord, Modifiers::command()); + cx.simulate_click(screen_coord, Modifiers::secondary_key()); assert_eq!(cx.opened_url(), Some("https://zed.dev/releases".into())); } @@ -1386,7 +1388,7 @@ mod tests { }); for _ in 0..5 { - cx.simulate_click(definition_hover_point, Modifiers::command()); + cx.simulate_click(definition_hover_point, Modifiers::secondary_key()); cx.background_executor.run_until_parked(); cx.assert_editor_state(indoc! {" fn test() { @@ -1398,7 +1400,7 @@ mod tests { } "}); - cx.simulate_click(reference_hover_point, Modifiers::command()); + cx.simulate_click(reference_hover_point, Modifiers::secondary_key()); cx.background_executor.run_until_parked(); cx.assert_editor_state(indoc! {" fn «testˇ»() { diff --git a/crates/file_finder/src/file_finder_tests.rs b/crates/file_finder/src/file_finder_tests.rs index d387f79028..5d03db8820 100644 --- a/crates/file_finder/src/file_finder_tests.rs +++ b/crates/file_finder/src/file_finder_tests.rs @@ -1490,7 +1490,7 @@ async fn test_keeps_file_finder_open_after_modifier_keys_release(cx: &mut gpui:: open_queried_buffer("1", 1, "1.txt", &workspace, cx).await; - cx.simulate_modifiers_change(Modifiers::command()); + cx.simulate_modifiers_change(Modifiers::secondary_key()); open_file_picker(&workspace, cx); cx.simulate_modifiers_change(Modifiers::none()); @@ -1519,7 +1519,7 @@ async fn test_opens_file_on_modifier_keys_release(cx: &mut gpui::TestAppContext) open_queried_buffer("1", 1, "1.txt", &workspace, cx).await; open_queried_buffer("2", 1, "2.txt", &workspace, cx).await; - cx.simulate_modifiers_change(Modifiers::command()); + cx.simulate_modifiers_change(Modifiers::secondary_key()); let picker = open_file_picker(&workspace, cx); picker.update(cx, |finder, _| { assert_eq!(finder.delegate.matches.len(), 2); @@ -1560,7 +1560,7 @@ async fn test_switches_between_release_norelease_modes_on_forward_nav( open_queried_buffer("2", 1, "2.txt", &workspace, cx).await; // Open with a shortcut - cx.simulate_modifiers_change(Modifiers::command()); + cx.simulate_modifiers_change(Modifiers::secondary_key()); let picker = open_file_picker(&workspace, cx); picker.update(cx, |finder, _| { assert_eq!(finder.delegate.matches.len(), 2); @@ -1581,7 +1581,7 @@ async fn test_switches_between_release_norelease_modes_on_forward_nav( // Back to navigation with initial shortcut // Open file on modifiers release - cx.simulate_modifiers_change(Modifiers::command()); + cx.simulate_modifiers_change(Modifiers::secondary_key()); cx.dispatch_action(Toggle); cx.simulate_modifiers_change(Modifiers::none()); cx.read(|cx| { @@ -1617,7 +1617,7 @@ async fn test_switches_between_release_norelease_modes_on_backward_nav( open_queried_buffer("3", 1, "3.txt", &workspace, cx).await; // Open with a shortcut - cx.simulate_modifiers_change(Modifiers::command()); + cx.simulate_modifiers_change(Modifiers::secondary_key()); let picker = open_file_picker(&workspace, cx); picker.update(cx, |finder, _| { assert_eq!(finder.delegate.matches.len(), 3); @@ -1640,7 +1640,7 @@ async fn test_switches_between_release_norelease_modes_on_backward_nav( // Back to navigation with initial shortcut // Open file on modifiers release - cx.simulate_modifiers_change(Modifiers::command()); + cx.simulate_modifiers_change(Modifiers::secondary_key()); cx.dispatch_action(SelectPrev); // <-- File Finder's SelectPrev, not menu's cx.simulate_modifiers_change(Modifiers::none()); cx.read(|cx| { @@ -1669,7 +1669,7 @@ async fn test_extending_modifiers_does_not_confirm_selection(cx: &mut gpui::Test open_queried_buffer("1", 1, "1.txt", &workspace, cx).await; - cx.simulate_modifiers_change(Modifiers::command()); + cx.simulate_modifiers_change(Modifiers::secondary_key()); open_file_picker(&workspace, cx); cx.simulate_modifiers_change(Modifiers::command_shift()); diff --git a/crates/gpui/src/elements/div.rs b/crates/gpui/src/elements/div.rs index 1eae5931a0..204c6a024e 100644 --- a/crates/gpui/src/elements/div.rs +++ b/crates/gpui/src/elements/div.rs @@ -1510,12 +1510,12 @@ impl Interactivity { }; if self.location.is_some() && text_bounds.contains(&cx.mouse_position()) - && cx.modifiers().command + && cx.modifiers().secondary() { - let command_held = cx.modifiers().command; + let secondary_held = cx.modifiers().secondary(); cx.on_key_event({ move |e: &crate::ModifiersChangedEvent, _phase, cx| { - if e.modifiers.command != command_held + if e.modifiers.secondary() != secondary_held && text_bounds.contains(&cx.mouse_position()) { cx.refresh(); diff --git a/crates/gpui/src/platform/keystroke.rs b/crates/gpui/src/platform/keystroke.rs index acb0d336b0..5f3d75f72e 100644 --- a/crates/gpui/src/platform/keystroke.rs +++ b/crates/gpui/src/platform/keystroke.rs @@ -37,7 +37,7 @@ impl Keystroke { control: self.modifiers.control, alt: false, shift: false, - command: false, + platform: false, function: false, }, key: ime_key.to_string(), @@ -62,7 +62,7 @@ impl Keystroke { let mut control = false; let mut alt = false; let mut shift = false; - let mut command = false; + let mut platform = false; let mut function = false; let mut key = None; let mut ime_key = None; @@ -73,8 +73,13 @@ impl Keystroke { "ctrl" => control = true, "alt" => alt = true, "shift" => shift = true, - "cmd" => command = true, "fn" => function = true, + #[cfg(target_os = "macos")] + "cmd" => platform = true, + #[cfg(target_os = "linux")] + "super" => platform = true, + #[cfg(target_os = "windows")] + "win" => platform = true, _ => { if let Some(next) = components.peek() { if next.is_empty() && source.ends_with('-') { @@ -101,7 +106,7 @@ impl Keystroke { control, alt, shift, - command, + platform, function, }, key, @@ -114,7 +119,7 @@ impl Keystroke { /// be able to simulate typing "space", etc. pub fn with_simulated_ime(mut self) -> Self { if self.ime_key.is_none() - && !self.modifiers.command + && !self.modifiers.platform && !self.modifiers.control && !self.modifiers.function && !self.modifiers.alt @@ -147,8 +152,15 @@ impl std::fmt::Display for Keystroke { if self.modifiers.alt { f.write_char('⌥')?; } - if self.modifiers.command { + if self.modifiers.platform { + #[cfg(target_os = "macos")] f.write_char('⌘')?; + + #[cfg(target_os = "linux")] + f.write_char('❖')?; + + #[cfg(target_os = "windows")] + f.write_char('⊞')?; } if self.modifiers.shift { f.write_char('⇧')?; @@ -188,7 +200,8 @@ pub struct Modifiers { /// The command key, on macos /// the windows key, on windows - pub command: bool, + /// the super key, on linux + pub platform: bool, /// The function key pub function: bool, @@ -197,7 +210,22 @@ pub struct Modifiers { impl Modifiers { /// Returns true if any modifier key is pressed pub fn modified(&self) -> bool { - self.control || self.alt || self.shift || self.command || self.function + self.control || self.alt || self.shift || self.platform || self.function + } + + /// Whether the semantically 'secondary' modifier key is pressed + /// On macos, this is the command key + /// On windows and linux, this is the control key + pub fn secondary(&self) -> bool { + #[cfg(target_os = "macos")] + { + return self.platform; + } + + #[cfg(not(target_os = "macos"))] + { + return self.control; + } } /// helper method for Modifiers with no modifiers @@ -205,10 +233,45 @@ impl Modifiers { Default::default() } - /// helper method for Modifiers with just command + /// helper method for Modifiers with just the command key pub fn command() -> Modifiers { Modifiers { - command: true, + platform: true, + ..Default::default() + } + } + + /// A helper method for Modifiers with just the secondary key pressed + pub fn secondary_key() -> Modifiers { + #[cfg(target_os = "macos")] + { + Modifiers { + platform: true, + ..Default::default() + } + } + + #[cfg(not(target_os = "macos"))] + { + Modifiers { + control: true, + ..Default::default() + } + } + } + + /// helper method for Modifiers with just the windows key + pub fn windows() -> Modifiers { + Modifiers { + platform: true, + ..Default::default() + } + } + + /// helper method for Modifiers with just the super key + pub fn super_key() -> Modifiers { + Modifiers { + platform: true, ..Default::default() } } @@ -233,7 +296,7 @@ impl Modifiers { pub fn command_shift() -> Modifiers { Modifiers { shift: true, - command: true, + platform: true, ..Default::default() } } @@ -243,7 +306,7 @@ impl Modifiers { (other.control || !self.control) && (other.alt || !self.alt) && (other.shift || !self.shift) - && (other.command || !self.command) + && (other.platform || !self.platform) && (other.function || !self.function) } } diff --git a/crates/gpui/src/platform/linux/wayland/client.rs b/crates/gpui/src/platform/linux/wayland/client.rs index c3fbe3ea25..95ab6b9342 100644 --- a/crates/gpui/src/platform/linux/wayland/client.rs +++ b/crates/gpui/src/platform/linux/wayland/client.rs @@ -190,7 +190,7 @@ impl WaylandClient { control: false, alt: false, function: false, - command: false, + platform: false, }, scroll_direction: -1.0, axis_source: AxisSource::Wheel, @@ -692,6 +692,11 @@ impl Dispatch for WaylandClientState { group, .. } => { + let focused_window = state.keyboard_focused_window.clone(); + let Some(focused_window) = focused_window else { + return; + }; + let keymap_state = state.keymap_state.as_mut().unwrap(); keymap_state.update_mask(mods_depressed, mods_latched, mods_locked, 0, 0, group); @@ -707,14 +712,22 @@ impl Dispatch for WaylandClientState { state.modifiers.shift = shift; state.modifiers.alt = alt; state.modifiers.control = control; - state.modifiers.command = command; + state.modifiers.platform = command; + + let input = PlatformInput::ModifiersChanged(ModifiersChangedEvent { + modifiers: state.modifiers, + }); + + drop(state); + + focused_window.handle_input(input); } wl_keyboard::Event::Key { key, state: WEnum::Value(key_state), .. } => { - let focused_window = &state.keyboard_focused_window; + let focused_window = state.keyboard_focused_window.clone(); let Some(focused_window) = focused_window else { return; }; @@ -725,80 +738,56 @@ impl Dispatch for WaylandClientState { let keysym = keymap_state.key_get_one_sym(keycode); match key_state { - wl_keyboard::KeyState::Pressed => { - let input = if keysym.is_modifier_key() { - PlatformInput::ModifiersChanged(ModifiersChangedEvent { - modifiers: state.modifiers, + wl_keyboard::KeyState::Pressed if !keysym.is_modifier_key() => { + let input = PlatformInput::KeyDown(KeyDownEvent { + keystroke: Keystroke::from_xkb(keymap_state, state.modifiers, keycode), + is_held: false, // todo(linux) + }); + + state.repeat.current_id += 1; + state.repeat.current_keysym = Some(keysym); + + let rate = state.repeat.characters_per_second; + let delay = state.repeat.delay; + let id = state.repeat.current_id; + let this = this.clone(); + + let timer = Timer::from_duration(delay); + let state_ = Rc::clone(&this.client_state_inner); + let input_ = input.clone(); + state + .loop_handle + .insert_source(timer, move |event, _metadata, shared_data| { + let state_ = state_.borrow_mut(); + let is_repeating = id == state_.repeat.current_id + && state_.repeat.current_keysym.is_some() + && state_.keyboard_focused_window.is_some(); + + if !is_repeating { + return TimeoutAction::Drop; + } + + let focused_window = + state_.keyboard_focused_window.as_ref().unwrap().clone(); + + drop(state_); + + focused_window.handle_input(input_.clone()); + + TimeoutAction::ToDuration(Duration::from_secs(1) / rate) }) - } else { - PlatformInput::KeyDown(KeyDownEvent { - keystroke: Keystroke::from_xkb( - keymap_state, - state.modifiers, - keycode, - ), - is_held: false, // todo(linux) - }) - }; - - if !keysym.is_modifier_key() { - state.repeat.current_id += 1; - state.repeat.current_keysym = Some(keysym); - - let rate = state.repeat.characters_per_second; - let delay = state.repeat.delay; - let id = state.repeat.current_id; - let this = this.clone(); - - let timer = Timer::from_duration(delay); - let state_ = Rc::clone(&this.client_state_inner); - let input_ = input.clone(); - state - .loop_handle - .insert_source(timer, move |event, _metadata, shared_data| { - let state_ = state_.borrow_mut(); - let is_repeating = id == state_.repeat.current_id - && state_.repeat.current_keysym.is_some() - && state_.keyboard_focused_window.is_some(); - - if !is_repeating { - return TimeoutAction::Drop; - } - - let focused_window = - state_.keyboard_focused_window.as_ref().unwrap().clone(); - - drop(state_); - - focused_window.handle_input(input_.clone()); - - TimeoutAction::ToDuration(Duration::from_secs(1) / rate) - }) - .unwrap(); - } + .unwrap(); drop(state); focused_window.handle_input(input); } - wl_keyboard::KeyState::Released => { - let input = if keysym.is_modifier_key() { - PlatformInput::ModifiersChanged(ModifiersChangedEvent { - modifiers: state.modifiers, - }) - } else { - PlatformInput::KeyUp(KeyUpEvent { - keystroke: Keystroke::from_xkb( - keymap_state, - state.modifiers, - keycode, - ), - }) - }; + wl_keyboard::KeyState::Released if !keysym.is_modifier_key() => { + let input = PlatformInput::KeyUp(KeyUpEvent { + keystroke: Keystroke::from_xkb(keymap_state, state.modifiers, keycode), + }); - if !keysym.is_modifier_key() { - state.repeat.current_keysym = None; - } + state.repeat.current_keysym = None; drop(state); diff --git a/crates/gpui/src/platform/linux/x11/event.rs b/crates/gpui/src/platform/linux/x11/event.rs index a63e1c68cb..3c173b3393 100644 --- a/crates/gpui/src/platform/linux/x11/event.rs +++ b/crates/gpui/src/platform/linux/x11/event.rs @@ -18,7 +18,7 @@ pub(crate) fn modifiers_from_state(state: xproto::KeyButMask) -> Modifiers { control: state.contains(xproto::KeyButMask::CONTROL), alt: state.contains(xproto::KeyButMask::MOD1), shift: state.contains(xproto::KeyButMask::SHIFT), - command: state.contains(xproto::KeyButMask::MOD4), + platform: state.contains(xproto::KeyButMask::MOD4), function: false, } } diff --git a/crates/gpui/src/platform/mac/events.rs b/crates/gpui/src/platform/mac/events.rs index 9319dd039d..e500acc0a2 100644 --- a/crates/gpui/src/platform/mac/events.rs +++ b/crates/gpui/src/platform/mac/events.rs @@ -77,7 +77,7 @@ unsafe fn read_modifiers(native_event: id) -> Modifiers { control, alt, shift, - command, + platform: command, function, } } @@ -323,7 +323,7 @@ unsafe fn parse_keystroke(native_event: id) -> Keystroke { control, alt, shift, - command, + platform: command, function, }, key, diff --git a/crates/gpui/src/platform/mac/platform.rs b/crates/gpui/src/platform/mac/platform.rs index 7e4a06c8f8..91e89fced8 100644 --- a/crates/gpui/src/platform/mac/platform.rs +++ b/crates/gpui/src/platform/mac/platform.rs @@ -279,7 +279,7 @@ impl MacPlatform { let mut mask = NSEventModifierFlags::empty(); for (modifier, flag) in &[ ( - keystroke.modifiers.command, + keystroke.modifiers.platform, NSEventModifierFlags::NSCommandKeyMask, ), ( diff --git a/crates/gpui/src/platform/mac/window.rs b/crates/gpui/src/platform/mac/window.rs index 9f167ceb51..edd400d9fc 100644 --- a/crates/gpui/src/platform/mac/window.rs +++ b/crates/gpui/src/platform/mac/window.rs @@ -853,7 +853,7 @@ impl PlatformWindow for MacWindow { control, alt, shift, - command, + platform: command, function, } } diff --git a/crates/gpui/src/platform/windows/window.rs b/crates/gpui/src/platform/windows/window.rs index 4f54a74253..ce9e4c6a1a 100644 --- a/crates/gpui/src/platform/windows/window.rs +++ b/crates/gpui/src/platform/windows/window.rs @@ -239,7 +239,7 @@ impl WindowsWindowInner { control: self.is_virtual_key_pressed(VK_CONTROL), alt: self.is_virtual_key_pressed(VK_MENU), shift: self.is_virtual_key_pressed(VK_SHIFT), - command: self.is_virtual_key_pressed(VK_LWIN) || self.is_virtual_key_pressed(VK_RWIN), + platform: self.is_virtual_key_pressed(VK_LWIN) || self.is_virtual_key_pressed(VK_RWIN), function: false, } } diff --git a/crates/picker/src/picker.rs b/crates/picker/src/picker.rs index 5b1d2c43f3..2679f21e1b 100644 --- a/crates/picker/src/picker.rs +++ b/crates/picker/src/picker.rs @@ -418,7 +418,7 @@ impl Picker { .id(("item", ix)) .cursor_pointer() .on_click(cx.listener(move |this, event: &ClickEvent, cx| { - this.handle_click(ix, event.down.modifiers.command, cx) + this.handle_click(ix, event.down.modifiers.secondary(), cx) })) // As of this writing, GPUI intercepts `ctrl-[mouse-event]`s on macOS // and produces right mouse button events. This matches platforms norms @@ -427,7 +427,9 @@ impl Picker { .on_mouse_up( MouseButton::Right, cx.listener(move |this, event: &MouseUpEvent, cx| { - this.handle_click(ix, event.modifiers.command, cx) + // We specficially want to use the platform key here, as + // ctrl will already be held down for the tab switcher. + this.handle_click(ix, event.modifiers.platform, cx) }), ) .children( diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index 467358215b..a3786b578f 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -1456,7 +1456,7 @@ impl ProjectPanel { if kind.is_dir() { this.toggle_expanded(entry_id, cx); } else { - if event.down.modifiers.command { + if event.down.modifiers.secondary() { this.split_entry(entry_id, cx); } else { this.open_entry(entry_id, event.up.click_count > 1, cx); diff --git a/crates/terminal/src/mappings/keys.rs b/crates/terminal/src/mappings/keys.rs index f8a26fbe2b..ca5defd23f 100644 --- a/crates/terminal/src/mappings/keys.rs +++ b/crates/terminal/src/mappings/keys.rs @@ -18,7 +18,7 @@ impl AlacModifiers { ks.modifiers.alt, ks.modifiers.control, ks.modifiers.shift, - ks.modifiers.command, + ks.modifiers.platform, ) { (false, false, false, false) => AlacModifiers::None, (true, false, false, false) => AlacModifiers::Alt, @@ -336,7 +336,7 @@ mod test { control: false, alt: false, shift: false, - command: false, + platform: false, function: false, }, key: "🖖🏻".to_string(), //2 char string diff --git a/crates/terminal/src/terminal.rs b/crates/terminal/src/terminal.rs index 287cbd8085..174bf16a5f 100644 --- a/crates/terminal/src/terminal.rs +++ b/crates/terminal/src/terminal.rs @@ -432,7 +432,7 @@ impl TerminalBuilder { last_mouse_position: None, next_link_id: 0, selection_phase: SelectionPhase::Ended, - cmd_pressed: false, + secondary_pressed: false, hovered_word: false, url_regex, word_regex, @@ -585,7 +585,7 @@ pub struct Terminal { scroll_px: Pixels, next_link_id: usize, selection_phase: SelectionPhase, - cmd_pressed: bool, + secondary_pressed: bool, hovered_word: bool, url_regex: RegexSearch, word_regex: RegexSearch, @@ -1029,11 +1029,11 @@ impl Terminal { } pub fn try_modifiers_change(&mut self, modifiers: &Modifiers) -> bool { - let changed = self.cmd_pressed != modifiers.command; - if !self.cmd_pressed && modifiers.command { + let changed = self.secondary_pressed != modifiers.secondary(); + if !self.secondary_pressed && modifiers.secondary() { self.refresh_hovered_word(); } - self.cmd_pressed = modifiers.command; + self.secondary_pressed = modifiers.secondary(); changed } @@ -1136,7 +1136,7 @@ impl Terminal { self.pty_tx.notify(bytes); } } - } else if self.cmd_pressed { + } else if self.secondary_pressed { self.word_from_position(Some(position)); } } @@ -1266,7 +1266,7 @@ impl Terminal { let mouse_cell_index = content_index_for_mouse(position, &self.last_content.size); if let Some(link) = self.last_content.cells[mouse_cell_index].hyperlink() { cx.open_url(link.uri()); - } else if self.cmd_pressed { + } else if self.secondary_pressed { self.events .push_back(InternalEvent::FindHyperlink(position, true)); } @@ -1402,7 +1402,7 @@ impl Terminal { } pub fn can_navigate_to_selected_word(&self) -> bool { - self.cmd_pressed && self.hovered_word + self.secondary_pressed && self.hovered_word } pub fn task(&self) -> Option<&TaskState> { diff --git a/crates/ui/src/components/keybinding.rs b/crates/ui/src/components/keybinding.rs index 5212705edc..94173304fc 100644 --- a/crates/ui/src/components/keybinding.rs +++ b/crates/ui/src/components/keybinding.rs @@ -113,7 +113,7 @@ impl RenderOnce for KeyBinding { el.child(Key::new("Alt")).child(Key::new("+")) } }) - .when(keystroke.modifiers.command, |el| { + .when(keystroke.modifiers.platform, |el| { match self.platform_style { PlatformStyle::Mac => el.child(KeyIcon::new(IconName::Command)), PlatformStyle::Linux => { diff --git a/crates/vim/src/test/neovim_connection.rs b/crates/vim/src/test/neovim_connection.rs index 616959160f..8e5948330f 100644 --- a/crates/vim/src/test/neovim_connection.rs +++ b/crates/vim/src/test/neovim_connection.rs @@ -120,7 +120,7 @@ impl NeovimConnection { let special = keystroke.modifiers.shift || keystroke.modifiers.control || keystroke.modifiers.alt - || keystroke.modifiers.command + || keystroke.modifiers.platform || keystroke.key.len() > 1; let start = if special { "<" } else { "" }; let shift = if keystroke.modifiers.shift { "S-" } else { "" }; @@ -130,7 +130,7 @@ impl NeovimConnection { "" }; let alt = if keystroke.modifiers.alt { "M-" } else { "" }; - let cmd = if keystroke.modifiers.command { + let cmd = if keystroke.modifiers.platform { "D-" } else { ""