Add Editor Controls Menu to Tool Bar (#10655)
This PR adds an editor controls menu to the tool bar. This menu will be used to contain controls that toggle visual features in the editor, like toggling inlay hints, showing git status or blame, hiding the gutter, hiding or showing elements in the tool bar, etc. For the moment, this consolidates the new Inline Git Blame toggle and the old Inlay Hints toggle. In the future it will contain additional controls. Before:  After:  --- Release Notes: - Added an editor controls menu to the tool bar. This will contain visual, editor-specific options like toggling inlay hints, showing git status or blame, etc. - Removed the top level inlay hint toggle from the tool bar due to the above change. - Added the ability to toggle inline git blame from the new editor controls menu. --------- Co-authored-by: Marshall Bowers <1486634+maxdeviant@users.noreply.github.com>
This commit is contained in:
parent
775539b3fa
commit
e93d554725
5 changed files with 141 additions and 31 deletions
3
assets/icons/sliders.svg
Normal file
3
assets/icons/sliders.svg
Normal file
|
@ -0,0 +1,3 @@
|
|||
<svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.36667 3.79167C5.53364 3.79167 4.85833 4.46697 4.85833 5.3C4.85833 6.13303 5.53364 6.80833 6.36667 6.80833C7.1997 6.80833 7.875 6.13303 7.875 5.3C7.875 4.46697 7.1997 3.79167 6.36667 3.79167ZM2.1 5.925H3.67944C3.9626 7.14732 5.05824 8.05833 6.36667 8.05833C7.67509 8.05833 8.77073 7.14732 9.05389 5.925H14.9C15.2452 5.925 15.525 5.64518 15.525 5.3C15.525 4.95482 15.2452 4.675 14.9 4.675H9.05389C8.77073 3.45268 7.67509 2.54167 6.36667 2.54167C5.05824 2.54167 3.9626 3.45268 3.67944 4.675H2.1C1.75482 4.675 1.475 4.95482 1.475 5.3C1.475 5.64518 1.75482 5.925 2.1 5.925ZM13.3206 12.325C13.0374 13.5473 11.9418 14.4583 10.6333 14.4583C9.32491 14.4583 8.22927 13.5473 7.94611 12.325H2.1C1.75482 12.325 1.475 12.0452 1.475 11.7C1.475 11.3548 1.75482 11.075 2.1 11.075H7.94611C8.22927 9.85268 9.32491 8.94167 10.6333 8.94167C11.9418 8.94167 13.0374 9.85268 13.3206 11.075H14.9C15.2452 11.075 15.525 11.3548 15.525 11.7C15.525 12.0452 15.2452 12.325 14.9 12.325H13.3206ZM9.125 11.7C9.125 10.867 9.8003 10.1917 10.6333 10.1917C11.4664 10.1917 12.1417 10.867 12.1417 11.7C12.1417 12.533 11.4664 13.2083 10.6333 13.2083C9.8003 13.2083 9.125 12.533 9.125 11.7Z" fill="black"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
|
@ -8889,6 +8889,10 @@ impl Editor {
|
|||
cx.notify();
|
||||
}
|
||||
|
||||
pub fn git_blame_inline_enabled(&self) -> bool {
|
||||
self.git_blame_inline_enabled
|
||||
}
|
||||
|
||||
fn start_git_blame(&mut self, user_triggered: bool, cx: &mut ViewContext<Self>) {
|
||||
if let Some(project) = self.project.as_ref() {
|
||||
let Some(buffer) = self.buffer().read(cx).as_singleton() else {
|
||||
|
|
|
@ -3,18 +3,21 @@ use assistant::{AssistantPanel, InlineAssist};
|
|||
use editor::{Editor, EditorSettings};
|
||||
|
||||
use gpui::{
|
||||
Action, ClickEvent, ElementId, EventEmitter, InteractiveElement, ParentElement, Render, Styled,
|
||||
Subscription, View, ViewContext, WeakView,
|
||||
anchored, deferred, Action, AnchorCorner, ClickEvent, DismissEvent, ElementId, EventEmitter,
|
||||
InteractiveElement, ParentElement, Render, Styled, Subscription, View, ViewContext, WeakView,
|
||||
};
|
||||
use search::{buffer_search, BufferSearchBar};
|
||||
use settings::{Settings, SettingsStore};
|
||||
use ui::{prelude::*, ButtonSize, ButtonStyle, IconButton, IconName, IconSize, Tooltip};
|
||||
use ui::{
|
||||
prelude::*, ButtonSize, ButtonStyle, ContextMenu, IconButton, IconName, IconSize, Tooltip,
|
||||
};
|
||||
use workspace::{
|
||||
item::ItemHandle, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, Workspace,
|
||||
};
|
||||
|
||||
pub struct QuickActionBar {
|
||||
buffer_search_bar: View<BufferSearchBar>,
|
||||
toggle_settings_menu: Option<View<ContextMenu>>,
|
||||
active_item: Option<Box<dyn ItemHandle>>,
|
||||
_inlay_hints_enabled_subscription: Option<Subscription>,
|
||||
workspace: WeakView<Workspace>,
|
||||
|
@ -29,6 +32,7 @@ impl QuickActionBar {
|
|||
) -> Self {
|
||||
let mut this = Self {
|
||||
buffer_search_bar,
|
||||
toggle_settings_menu: None,
|
||||
active_item: None,
|
||||
_inlay_hints_enabled_subscription: None,
|
||||
workspace: workspace.weak_handle(),
|
||||
|
@ -63,6 +67,17 @@ impl QuickActionBar {
|
|||
ToolbarItemLocation::Hidden
|
||||
}
|
||||
}
|
||||
|
||||
fn render_menu_overlay(menu: &View<ContextMenu>) -> Div {
|
||||
div().absolute().bottom_0().right_0().size_0().child(
|
||||
deferred(
|
||||
anchored()
|
||||
.anchor(AnchorCorner::TopRight)
|
||||
.child(menu.clone()),
|
||||
)
|
||||
.with_priority(1),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for QuickActionBar {
|
||||
|
@ -70,22 +85,6 @@ impl Render for QuickActionBar {
|
|||
let Some(editor) = self.active_editor() else {
|
||||
return div().id("empty quick action bar");
|
||||
};
|
||||
let inlay_hints_button = Some(QuickActionBarButton::new(
|
||||
"toggle inlay hints",
|
||||
IconName::InlayHint,
|
||||
editor.read(cx).inlay_hints_enabled(),
|
||||
Box::new(editor::actions::ToggleInlayHints),
|
||||
"Toggle Inlay Hints",
|
||||
{
|
||||
let editor = editor.clone();
|
||||
move |_, cx| {
|
||||
editor.update(cx, |editor, cx| {
|
||||
editor.toggle_inlay_hints(&editor::actions::ToggleInlayHints, cx);
|
||||
});
|
||||
}
|
||||
},
|
||||
))
|
||||
.filter(|_| editor.read(cx).supports_inlay_hints(cx));
|
||||
|
||||
let search_button = Some(QuickActionBarButton::new(
|
||||
"toggle buffer search",
|
||||
|
@ -122,14 +121,85 @@ impl Render for QuickActionBar {
|
|||
},
|
||||
);
|
||||
|
||||
let editor_settings_dropdown =
|
||||
IconButton::new("toggle_editor_settings_icon", IconName::Sliders)
|
||||
.size(ButtonSize::Compact)
|
||||
.icon_size(IconSize::Small)
|
||||
.style(ButtonStyle::Subtle)
|
||||
.selected(self.toggle_settings_menu.is_some())
|
||||
.on_click({
|
||||
let editor = editor.clone();
|
||||
cx.listener(move |quick_action_bar, _, cx| {
|
||||
let inlay_hints_enabled = editor.read(cx).inlay_hints_enabled();
|
||||
let supports_inlay_hints = editor.read(cx).supports_inlay_hints(cx);
|
||||
let git_blame_inline_enabled = editor.read(cx).git_blame_inline_enabled();
|
||||
|
||||
let menu = ContextMenu::build(cx, |mut menu, _| {
|
||||
if supports_inlay_hints {
|
||||
menu = menu.toggleable_entry(
|
||||
"Show Inlay Hints",
|
||||
inlay_hints_enabled,
|
||||
Some(editor::actions::ToggleInlayHints.boxed_clone()),
|
||||
{
|
||||
let editor = editor.clone();
|
||||
move |cx| {
|
||||
editor.update(cx, |editor, cx| {
|
||||
editor.toggle_inlay_hints(
|
||||
&editor::actions::ToggleInlayHints,
|
||||
cx,
|
||||
);
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
menu = menu.toggleable_entry(
|
||||
"Show Git Blame",
|
||||
git_blame_inline_enabled,
|
||||
Some(editor::actions::ToggleGitBlameInline.boxed_clone()),
|
||||
{
|
||||
let editor = editor.clone();
|
||||
move |cx| {
|
||||
editor.update(cx, |editor, cx| {
|
||||
editor.toggle_git_blame_inline(
|
||||
&editor::actions::ToggleGitBlameInline,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
menu
|
||||
});
|
||||
cx.subscribe(&menu, |quick_action_bar, _, _: &DismissEvent, _cx| {
|
||||
quick_action_bar.toggle_settings_menu = None;
|
||||
})
|
||||
.detach();
|
||||
quick_action_bar.toggle_settings_menu = Some(menu);
|
||||
})
|
||||
})
|
||||
.tooltip(|cx| Tooltip::text("Editor Controls", cx));
|
||||
|
||||
h_flex()
|
||||
.id("quick action bar")
|
||||
.gap_2()
|
||||
.children(inlay_hints_button)
|
||||
.children(search_button)
|
||||
.when(AssistantSettings::get_global(cx).button, |bar| {
|
||||
bar.child(assistant_button)
|
||||
})
|
||||
.gap_3()
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_1p5()
|
||||
.children(search_button)
|
||||
.when(AssistantSettings::get_global(cx).button, |bar| {
|
||||
bar.child(assistant_button)
|
||||
}),
|
||||
)
|
||||
.child(editor_settings_dropdown)
|
||||
.when_some(
|
||||
self.toggle_settings_menu.as_ref(),
|
||||
|el, toggle_settings_menu| {
|
||||
el.child(Self::render_menu_overlay(toggle_settings_menu))
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ enum ContextMenuItem {
|
|||
Separator,
|
||||
Header(SharedString),
|
||||
Entry {
|
||||
toggled: Option<bool>,
|
||||
label: SharedString,
|
||||
icon: Option<IconName>,
|
||||
handler: Rc<dyn Fn(&mut WindowContext)>,
|
||||
|
@ -92,6 +93,24 @@ impl ContextMenu {
|
|||
handler: impl Fn(&mut WindowContext) + 'static,
|
||||
) -> Self {
|
||||
self.items.push(ContextMenuItem::Entry {
|
||||
toggled: None,
|
||||
label: label.into(),
|
||||
handler: Rc::new(handler),
|
||||
icon: None,
|
||||
action,
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
pub fn toggleable_entry(
|
||||
mut self,
|
||||
label: impl Into<SharedString>,
|
||||
toggled: bool,
|
||||
action: Option<Box<dyn Action>>,
|
||||
handler: impl Fn(&mut WindowContext) + 'static,
|
||||
) -> Self {
|
||||
self.items.push(ContextMenuItem::Entry {
|
||||
toggled: Some(toggled),
|
||||
label: label.into(),
|
||||
handler: Rc::new(handler),
|
||||
icon: None,
|
||||
|
@ -114,6 +133,7 @@ impl ContextMenu {
|
|||
|
||||
pub fn action(mut self, label: impl Into<SharedString>, action: Box<dyn Action>) -> Self {
|
||||
self.items.push(ContextMenuItem::Entry {
|
||||
toggled: None,
|
||||
label: label.into(),
|
||||
action: Some(action.boxed_clone()),
|
||||
handler: Rc::new(move |cx| cx.dispatch_action(action.boxed_clone())),
|
||||
|
@ -124,6 +144,7 @@ impl ContextMenu {
|
|||
|
||||
pub fn link(mut self, label: impl Into<SharedString>, action: Box<dyn Action>) -> Self {
|
||||
self.items.push(ContextMenuItem::Entry {
|
||||
toggled: None,
|
||||
label: label.into(),
|
||||
action: Some(action.boxed_clone()),
|
||||
handler: Rc::new(move |cx| cx.dispatch_action(action.boxed_clone())),
|
||||
|
@ -279,6 +300,7 @@ impl Render for ContextMenu {
|
|||
.inset(true)
|
||||
.into_any_element(),
|
||||
ContextMenuItem::Entry {
|
||||
toggled,
|
||||
label,
|
||||
handler,
|
||||
icon,
|
||||
|
@ -300,13 +322,14 @@ impl Render for ContextMenu {
|
|||
ListItem::new(ix)
|
||||
.inset(true)
|
||||
.selected(Some(ix) == self.selected_index)
|
||||
.on_click(move |_, cx| {
|
||||
handler(cx);
|
||||
menu.update(cx, |menu, cx| {
|
||||
menu.clicked = true;
|
||||
cx.emit(DismissEvent);
|
||||
.when_some(*toggled, |list_item, toggled| {
|
||||
list_item.start_slot(if toggled {
|
||||
v_flex().flex_none().child(
|
||||
Icon::new(IconName::Check).color(Color::Accent),
|
||||
)
|
||||
} else {
|
||||
v_flex().flex_none().size(IconSize::default().rems())
|
||||
})
|
||||
.ok();
|
||||
})
|
||||
.child(
|
||||
h_flex()
|
||||
|
@ -328,6 +351,14 @@ impl Render for ContextMenu {
|
|||
.map(|binding| div().ml_1().child(binding))
|
||||
})),
|
||||
)
|
||||
.on_click(move |_, cx| {
|
||||
handler(cx);
|
||||
menu.update(cx, |menu, cx| {
|
||||
menu.clicked = true;
|
||||
cx.emit(DismissEvent);
|
||||
})
|
||||
.ok();
|
||||
})
|
||||
.into_any_element()
|
||||
}
|
||||
ContextMenuItem::CustomEntry {
|
||||
|
|
|
@ -105,6 +105,7 @@ pub enum IconName {
|
|||
Return,
|
||||
ReplyArrowRight,
|
||||
Settings,
|
||||
Sliders,
|
||||
Screen,
|
||||
SelectAll,
|
||||
Server,
|
||||
|
@ -204,6 +205,7 @@ impl IconName {
|
|||
IconName::Return => "icons/return.svg",
|
||||
IconName::ReplyArrowRight => "icons/reply_arrow_right.svg",
|
||||
IconName::Settings => "icons/file_icons/settings.svg",
|
||||
IconName::Sliders => "icons/sliders.svg",
|
||||
IconName::Screen => "icons/desktop.svg",
|
||||
IconName::SelectAll => "icons/select_all.svg",
|
||||
IconName::Server => "icons/server.svg",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue