Add action editor::OpenContextMenu
(#21494)
This addresses the editor context menu portion of #17819. Release Notes: - Added `editor::OpenContextMenu` action to open context menu at current cursor position.
This commit is contained in:
parent
0bde0f8e2f
commit
f0fac41ca4
5 changed files with 77 additions and 60 deletions
|
@ -108,7 +108,9 @@
|
||||||
"ctrl-'": "editor::ToggleHunkDiff",
|
"ctrl-'": "editor::ToggleHunkDiff",
|
||||||
"ctrl-\"": "editor::ExpandAllHunkDiffs",
|
"ctrl-\"": "editor::ExpandAllHunkDiffs",
|
||||||
"ctrl-i": "editor::ShowSignatureHelp",
|
"ctrl-i": "editor::ShowSignatureHelp",
|
||||||
"alt-g b": "editor::ToggleGitBlame"
|
"alt-g b": "editor::ToggleGitBlame",
|
||||||
|
"menu": "editor::OpenContextMenu",
|
||||||
|
"shift-f10": "editor::OpenContextMenu"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -296,6 +296,7 @@ gpui::actions!(
|
||||||
NewlineBelow,
|
NewlineBelow,
|
||||||
NextInlineCompletion,
|
NextInlineCompletion,
|
||||||
NextScreen,
|
NextScreen,
|
||||||
|
OpenContextMenu,
|
||||||
OpenExcerpts,
|
OpenExcerpts,
|
||||||
OpenExcerptsSplit,
|
OpenExcerptsSplit,
|
||||||
OpenProposedChangesEditor,
|
OpenProposedChangesEditor,
|
||||||
|
|
|
@ -13075,6 +13075,12 @@ impl Editor {
|
||||||
cx.write_to_clipboard(ClipboardItem::new_string(lines));
|
cx.write_to_clipboard(ClipboardItem::new_string(lines));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn open_context_menu(&mut self, _: &OpenContextMenu, cx: &mut ViewContext<Self>) {
|
||||||
|
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 {
|
pub fn inlay_hint_cache(&self) -> &InlayHintCache {
|
||||||
&self.inlay_hint_cache
|
&self.inlay_hint_cache
|
||||||
}
|
}
|
||||||
|
@ -13296,6 +13302,23 @@ impl Editor {
|
||||||
.get(&type_id)
|
.get(&type_id)
|
||||||
.and_then(|item| item.to_any().downcast_ref::<T>())
|
.and_then(|item| item.to_any().downcast_ref::<T>())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn character_size(&self, cx: &mut ViewContext<Self>) -> gpui::Point<Pixels> {
|
||||||
|
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 {
|
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<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> Option<gpui::Bounds<Pixels>> {
|
) -> Option<gpui::Bounds<Pixels>> {
|
||||||
let text_layout_details = self.text_layout_details(cx);
|
let text_layout_details = self.text_layout_details(cx);
|
||||||
let style = &text_layout_details.editor_style;
|
let gpui::Point {
|
||||||
let font_id = cx.text_system().resolve_font(&style.text.font());
|
x: em_width,
|
||||||
let font_size = style.text.font_size.to_pixels(cx.rem_size());
|
y: line_height,
|
||||||
let line_height = style.text.line_height_in_pixels(cx.rem_size());
|
} = self.character_size(cx);
|
||||||
|
|
||||||
let em_width = cx
|
|
||||||
.text_system()
|
|
||||||
.typographic_bounds(font_id, font_size, 'm')
|
|
||||||
.unwrap()
|
|
||||||
.size
|
|
||||||
.width;
|
|
||||||
|
|
||||||
let snapshot = self.snapshot(cx);
|
let snapshot = self.snapshot(cx);
|
||||||
let scroll_position = snapshot.scroll_position();
|
let scroll_position = snapshot.scroll_position();
|
||||||
|
|
|
@ -169,6 +169,7 @@ impl EditorElement {
|
||||||
|
|
||||||
crate::rust_analyzer_ext::apply_related_actions(view, cx);
|
crate::rust_analyzer_ext::apply_related_actions(view, cx);
|
||||||
crate::clangd_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_left);
|
||||||
register_action(view, cx, Editor::move_right);
|
register_action(view, cx, Editor::move_right);
|
||||||
register_action(view, cx, Editor::move_down);
|
register_action(view, cx, Editor::move_down);
|
||||||
|
@ -595,7 +596,7 @@ impl EditorElement {
|
||||||
position_map.point_for_position(text_hitbox.bounds, event.position);
|
position_map.point_for_position(text_hitbox.bounds, event.position);
|
||||||
mouse_context_menu::deploy_context_menu(
|
mouse_context_menu::deploy_context_menu(
|
||||||
editor,
|
editor,
|
||||||
event.position,
|
Some(event.position),
|
||||||
point_for_position.previous_valid,
|
point_for_position.previous_valid,
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
|
@ -2730,6 +2731,7 @@ impl EditorElement {
|
||||||
&self,
|
&self,
|
||||||
editor_snapshot: &EditorSnapshot,
|
editor_snapshot: &EditorSnapshot,
|
||||||
visible_range: Range<DisplayRow>,
|
visible_range: Range<DisplayRow>,
|
||||||
|
content_origin: gpui::Point<Pixels>,
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
) -> Option<AnyElement> {
|
) -> Option<AnyElement> {
|
||||||
let position = self.editor.update(cx, |editor, cx| {
|
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 mouse_context_menu = editor.mouse_context_menu.as_ref()?;
|
||||||
let (source_display_point, position) = match mouse_context_menu.position {
|
let (source_display_point, position) = match mouse_context_menu.position {
|
||||||
MenuPosition::PinnedToScreen(point) => (None, point),
|
MenuPosition::PinnedToScreen(point) => (None, point),
|
||||||
MenuPosition::PinnedToEditor {
|
MenuPosition::PinnedToEditor { source, offset } => {
|
||||||
source,
|
|
||||||
offset_x,
|
|
||||||
offset_y,
|
|
||||||
} => {
|
|
||||||
let source_display_point = source.to_display_point(editor_snapshot);
|
let source_display_point = source.to_display_point(editor_snapshot);
|
||||||
let mut source_point = editor.to_pixel_point(source, editor_snapshot, cx)?;
|
let source_point = editor.to_pixel_point(source, editor_snapshot, cx)?;
|
||||||
source_point.x += offset_x;
|
let position = content_origin + source_point + offset;
|
||||||
source_point.y += offset_y;
|
(Some(source_display_point), position)
|
||||||
(Some(source_display_point), source_point)
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -4325,8 +4322,8 @@ fn deploy_blame_entry_context_menu(
|
||||||
});
|
});
|
||||||
|
|
||||||
editor.update(cx, move |editor, cx| {
|
editor.update(cx, move |editor, cx| {
|
||||||
editor.mouse_context_menu = Some(MouseContextMenu::pinned_to_screen(
|
editor.mouse_context_menu = Some(MouseContextMenu::new(
|
||||||
position,
|
MenuPosition::PinnedToScreen(position),
|
||||||
context_menu,
|
context_menu,
|
||||||
cx,
|
cx,
|
||||||
));
|
));
|
||||||
|
@ -5578,8 +5575,12 @@ impl Element for EditorElement {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mouse_context_menu =
|
let mouse_context_menu = self.layout_mouse_context_menu(
|
||||||
self.layout_mouse_context_menu(&snapshot, start_row..end_row, cx);
|
&snapshot,
|
||||||
|
start_row..end_row,
|
||||||
|
content_origin,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
|
||||||
cx.with_element_namespace("crease_toggles", |cx| {
|
cx.with_element_namespace("crease_toggles", |cx| {
|
||||||
self.prepaint_crease_toggles(
|
self.prepaint_crease_toggles(
|
||||||
|
|
|
@ -20,8 +20,7 @@ pub enum MenuPosition {
|
||||||
/// Disappears when the position is no longer visible.
|
/// Disappears when the position is no longer visible.
|
||||||
PinnedToEditor {
|
PinnedToEditor {
|
||||||
source: multi_buffer::Anchor,
|
source: multi_buffer::Anchor,
|
||||||
offset_x: Pixels,
|
offset: Point<Pixels>,
|
||||||
offset_y: Pixels,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,36 +47,22 @@ impl MouseContextMenu {
|
||||||
context_menu: View<ui::ContextMenu>,
|
context_menu: View<ui::ContextMenu>,
|
||||||
cx: &mut ViewContext<Editor>,
|
cx: &mut ViewContext<Editor>,
|
||||||
) -> Option<Self> {
|
) -> Option<Self> {
|
||||||
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 editor_snapshot = editor.snapshot(cx);
|
||||||
let source_point = editor.to_pixel_point(source, &editor_snapshot, cx)?;
|
let content_origin = editor.last_bounds?.origin
|
||||||
let offset = position - source_point;
|
+ Point {
|
||||||
|
x: editor.gutter_dimensions.width,
|
||||||
Some(Self {
|
y: Pixels(0.0),
|
||||||
position: MenuPosition::PinnedToEditor {
|
};
|
||||||
source,
|
let source_position = editor.to_pixel_point(source, &editor_snapshot, cx)?;
|
||||||
offset_x: offset.x,
|
let menu_position = MenuPosition::PinnedToEditor {
|
||||||
offset_y: offset.y,
|
source,
|
||||||
},
|
offset: position - (source_position + content_origin),
|
||||||
context_menu,
|
};
|
||||||
_subscription,
|
return Some(MouseContextMenu::new(menu_position, context_menu, cx));
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn pinned_to_screen(
|
pub(crate) fn new(
|
||||||
position: Point<Pixels>,
|
position: MenuPosition,
|
||||||
context_menu: View<ui::ContextMenu>,
|
context_menu: View<ui::ContextMenu>,
|
||||||
cx: &mut ViewContext<Editor>,
|
cx: &mut ViewContext<Editor>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
@ -95,7 +80,7 @@ impl MouseContextMenu {
|
||||||
);
|
);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
position: MenuPosition::PinnedToScreen(position),
|
position,
|
||||||
context_menu,
|
context_menu,
|
||||||
_subscription,
|
_subscription,
|
||||||
}
|
}
|
||||||
|
@ -119,7 +104,7 @@ fn display_ranges<'a>(
|
||||||
|
|
||||||
pub fn deploy_context_menu(
|
pub fn deploy_context_menu(
|
||||||
editor: &mut Editor,
|
editor: &mut Editor,
|
||||||
position: Point<Pixels>,
|
position: Option<Point<Pixels>>,
|
||||||
point: DisplayPoint,
|
point: DisplayPoint,
|
||||||
cx: &mut ViewContext<Editor>,
|
cx: &mut ViewContext<Editor>,
|
||||||
) {
|
) {
|
||||||
|
@ -213,8 +198,18 @@ pub fn deploy_context_menu(
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
editor.mouse_context_menu =
|
editor.mouse_context_menu = match position {
|
||||||
MouseContextMenu::pinned_to_editor(editor, source_anchor, position, context_menu, cx);
|
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();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -248,7 +243,9 @@ mod tests {
|
||||||
}
|
}
|
||||||
"});
|
"});
|
||||||
cx.editor(|editor, _app| assert!(editor.mouse_context_menu.is_none()));
|
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! {"
|
cx.assert_editor_state(indoc! {"
|
||||||
fn test() {
|
fn test() {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue