diff --git a/assets/keymaps/default-linux.json b/assets/keymaps/default-linux.json index 2b792f353fc1e7..3787f97a8d69bd 100644 --- a/assets/keymaps/default-linux.json +++ b/assets/keymaps/default-linux.json @@ -108,7 +108,9 @@ "ctrl-'": "editor::ToggleHunkDiff", "ctrl-\"": "editor::ExpandAllHunkDiffs", "ctrl-i": "editor::ShowSignatureHelp", - "alt-g b": "editor::ToggleGitBlame" + "alt-g b": "editor::ToggleGitBlame", + "menu": "editor::OpenContextMenu", + "shift-f10": "editor::OpenContextMenu" } }, { diff --git a/crates/editor/src/actions.rs b/crates/editor/src/actions.rs index 9a00f1efca0593..99e7c6cd0be7fe 100644 --- a/crates/editor/src/actions.rs +++ b/crates/editor/src/actions.rs @@ -296,6 +296,7 @@ gpui::actions!( NewlineBelow, NextInlineCompletion, NextScreen, + OpenContextMenu, OpenExcerpts, OpenExcerptsSplit, OpenProposedChangesEditor, diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 2464ce8427161c..883ea570a92895 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -13075,6 +13075,12 @@ impl Editor { cx.write_to_clipboard(ClipboardItem::new_string(lines)); } + pub fn open_context_menu(&mut self, _: &OpenContextMenu, cx: &mut ViewContext) { + self.request_autoscroll(Autoscroll::newest(), cx); + let position = self.selections.newest_display(cx).start; + mouse_context_menu::deploy_context_menu(self, None, position, cx); + } + pub fn inlay_hint_cache(&self) -> &InlayHintCache { &self.inlay_hint_cache } @@ -13296,6 +13302,23 @@ impl Editor { .get(&type_id) .and_then(|item| item.to_any().downcast_ref::()) } + + fn character_size(&self, cx: &mut ViewContext) -> gpui::Point { + let text_layout_details = self.text_layout_details(cx); + let style = &text_layout_details.editor_style; + let font_id = cx.text_system().resolve_font(&style.text.font()); + let font_size = style.text.font_size.to_pixels(cx.rem_size()); + let line_height = style.text.line_height_in_pixels(cx.rem_size()); + + let em_width = cx + .text_system() + .typographic_bounds(font_id, font_size, 'm') + .unwrap() + .size + .width; + + gpui::Point::new(em_width, line_height) + } } fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize { @@ -14725,17 +14748,10 @@ impl ViewInputHandler for Editor { cx: &mut ViewContext, ) -> Option> { let text_layout_details = self.text_layout_details(cx); - let style = &text_layout_details.editor_style; - let font_id = cx.text_system().resolve_font(&style.text.font()); - let font_size = style.text.font_size.to_pixels(cx.rem_size()); - let line_height = style.text.line_height_in_pixels(cx.rem_size()); - - let em_width = cx - .text_system() - .typographic_bounds(font_id, font_size, 'm') - .unwrap() - .size - .width; + let gpui::Point { + x: em_width, + y: line_height, + } = self.character_size(cx); let snapshot = self.snapshot(cx); let scroll_position = snapshot.scroll_position(); diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 2bb40c460207bd..47de2609f7f5b3 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -169,6 +169,7 @@ impl EditorElement { crate::rust_analyzer_ext::apply_related_actions(view, cx); crate::clangd_ext::apply_related_actions(view, cx); + register_action(view, cx, Editor::open_context_menu); register_action(view, cx, Editor::move_left); register_action(view, cx, Editor::move_right); register_action(view, cx, Editor::move_down); @@ -595,7 +596,7 @@ impl EditorElement { position_map.point_for_position(text_hitbox.bounds, event.position); mouse_context_menu::deploy_context_menu( editor, - event.position, + Some(event.position), point_for_position.previous_valid, cx, ); @@ -2730,6 +2731,7 @@ impl EditorElement { &self, editor_snapshot: &EditorSnapshot, visible_range: Range, + content_origin: gpui::Point, cx: &mut WindowContext, ) -> Option { let position = self.editor.update(cx, |editor, cx| { @@ -2747,16 +2749,11 @@ impl EditorElement { let mouse_context_menu = editor.mouse_context_menu.as_ref()?; let (source_display_point, position) = match mouse_context_menu.position { MenuPosition::PinnedToScreen(point) => (None, point), - MenuPosition::PinnedToEditor { - source, - offset_x, - offset_y, - } => { + MenuPosition::PinnedToEditor { source, offset } => { let source_display_point = source.to_display_point(editor_snapshot); - let mut source_point = editor.to_pixel_point(source, editor_snapshot, cx)?; - source_point.x += offset_x; - source_point.y += offset_y; - (Some(source_display_point), source_point) + let source_point = editor.to_pixel_point(source, editor_snapshot, cx)?; + let position = content_origin + source_point + offset; + (Some(source_display_point), position) } }; @@ -4325,8 +4322,8 @@ fn deploy_blame_entry_context_menu( }); editor.update(cx, move |editor, cx| { - editor.mouse_context_menu = Some(MouseContextMenu::pinned_to_screen( - position, + editor.mouse_context_menu = Some(MouseContextMenu::new( + MenuPosition::PinnedToScreen(position), context_menu, cx, )); @@ -5578,8 +5575,12 @@ impl Element for EditorElement { ); } - let mouse_context_menu = - self.layout_mouse_context_menu(&snapshot, start_row..end_row, cx); + let mouse_context_menu = self.layout_mouse_context_menu( + &snapshot, + start_row..end_row, + content_origin, + cx, + ); cx.with_element_namespace("crease_toggles", |cx| { self.prepaint_crease_toggles( diff --git a/crates/editor/src/mouse_context_menu.rs b/crates/editor/src/mouse_context_menu.rs index 9abf4d990cd7a0..6861d424ec47c8 100644 --- a/crates/editor/src/mouse_context_menu.rs +++ b/crates/editor/src/mouse_context_menu.rs @@ -20,8 +20,7 @@ pub enum MenuPosition { /// Disappears when the position is no longer visible. PinnedToEditor { source: multi_buffer::Anchor, - offset_x: Pixels, - offset_y: Pixels, + offset: Point, }, } @@ -48,36 +47,22 @@ impl MouseContextMenu { context_menu: View, cx: &mut ViewContext, ) -> Option { - let context_menu_focus = context_menu.focus_handle(cx); - cx.focus(&context_menu_focus); - - let _subscription = cx.subscribe( - &context_menu, - move |editor, _, _event: &DismissEvent, cx| { - editor.mouse_context_menu.take(); - if context_menu_focus.contains_focused(cx) { - editor.focus(cx); - } - }, - ); - let editor_snapshot = editor.snapshot(cx); - let source_point = editor.to_pixel_point(source, &editor_snapshot, cx)?; - let offset = position - source_point; - - Some(Self { - position: MenuPosition::PinnedToEditor { - source, - offset_x: offset.x, - offset_y: offset.y, - }, - context_menu, - _subscription, - }) + let content_origin = editor.last_bounds?.origin + + Point { + x: editor.gutter_dimensions.width, + y: Pixels(0.0), + }; + let source_position = editor.to_pixel_point(source, &editor_snapshot, cx)?; + let menu_position = MenuPosition::PinnedToEditor { + source, + offset: position - (source_position + content_origin), + }; + return Some(MouseContextMenu::new(menu_position, context_menu, cx)); } - pub(crate) fn pinned_to_screen( - position: Point, + pub(crate) fn new( + position: MenuPosition, context_menu: View, cx: &mut ViewContext, ) -> Self { @@ -95,7 +80,7 @@ impl MouseContextMenu { ); Self { - position: MenuPosition::PinnedToScreen(position), + position, context_menu, _subscription, } @@ -119,7 +104,7 @@ fn display_ranges<'a>( pub fn deploy_context_menu( editor: &mut Editor, - position: Point, + position: Option>, point: DisplayPoint, cx: &mut ViewContext, ) { @@ -213,8 +198,18 @@ pub fn deploy_context_menu( }) }; - editor.mouse_context_menu = - MouseContextMenu::pinned_to_editor(editor, source_anchor, position, context_menu, cx); + editor.mouse_context_menu = match position { + Some(position) => { + MouseContextMenu::pinned_to_editor(editor, source_anchor, position, context_menu, cx) + } + None => { + let menu_position = MenuPosition::PinnedToEditor { + source: source_anchor, + offset: editor.character_size(cx), + }; + Some(MouseContextMenu::new(menu_position, context_menu, cx)) + } + }; cx.notify(); } @@ -248,7 +243,9 @@ mod tests { } "}); cx.editor(|editor, _app| assert!(editor.mouse_context_menu.is_none())); - cx.update_editor(|editor, cx| deploy_context_menu(editor, Default::default(), point, cx)); + cx.update_editor(|editor, cx| { + deploy_context_menu(editor, Some(Default::default()), point, cx) + }); cx.assert_editor_state(indoc! {" fn test() {