Add more menus to Zed (#12940)
### TODO - [x] Make sure keybinding shows up in pane + menu - [x] Selection tool in the editor toolbar - [x] Application Menu - [x] Add more options to pane + menu - Go to File... - Go to Symbol in Project... - [x] Add go items to the selection tool in the editor: - Go to Symbol in Editor... - Go to Line/Column... - Next Problem - Previous Problem - [x] Fix a bug where modals opened from a context menu aren't focused correclty - [x] Determine if or what needs to be done with project actions: - Difficulty is that these are exposed in the UI via clicking the project name in the titlebar or by right clicking the root entry in the project panel. But they require reading and are two clicks away. Is that sufficient? - Add Folder to Project - Open a new project - Open recent - [x] Get a style pass - [x] Implement style pass - [x] Fix the wrong actions in the selection menu - [x] Show selection tool toggle in the 'editor settings' thing - [x] Put preferences section from the app menu onto the right hand user menu - [x] Add Project menu into app menu to replace 'preferences' section, and put the rest of the actions there - [ ] ~~Adopt `...` convention for opening a surface~~ uncertain what this convention is. - [x] Adopt link styling for webview actions - [x] Set lucide hamburger for menu icon - [x] Gate application menu to only show on Linux and Windows Release Notes: - Added a 'selection and movement' tool to the Editor's toolbar, as well as controls to toggle it and a setting to remove it (`"toolbar": {"selections_menu": true/false }`) - Changed the behavior of the `+` menu in the tab bar to use standard actions and keybindings. Replaced 'New Center Terminal' with 'New Terminal', and 'New Search', with the usual 'Deploy Search'. Also added item-creating actions to this menu. - Added an 'application' menu to the titlebar to Linux and Windows builds of Zed
This commit is contained in:
parent
8af8493da6
commit
6b9ddbfef2
33 changed files with 712 additions and 198 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -2475,11 +2475,13 @@ dependencies = [
|
||||||
"channel",
|
"channel",
|
||||||
"client",
|
"client",
|
||||||
"collections",
|
"collections",
|
||||||
|
"command_palette",
|
||||||
"db",
|
"db",
|
||||||
"dev_server_projects",
|
"dev_server_projects",
|
||||||
"editor",
|
"editor",
|
||||||
"emojis",
|
"emojis",
|
||||||
"extensions_ui",
|
"extensions_ui",
|
||||||
|
"feedback",
|
||||||
"futures 0.3.28",
|
"futures 0.3.28",
|
||||||
"fuzzy",
|
"fuzzy",
|
||||||
"gpui",
|
"gpui",
|
||||||
|
|
1
assets/icons/rotate_ccw.svg
Normal file
1
assets/icons/rotate_ccw.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-rotate-ccw"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/></svg>
|
After Width: | Height: | Size: 302 B |
1
assets/icons/text-cursor.svg
Normal file
1
assets/icons/text-cursor.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-text-cursor"><path d="M17 22h-1a4 4 0 0 1-4-4V6a4 4 0 0 1 4-4h1"/><path d="M7 22h1a4 4 0 0 0 4-4v-1"/><path d="M7 2h1a4 4 0 0 1 4 4v1"/></svg>
|
After Width: | Height: | Size: 345 B |
|
@ -183,7 +183,9 @@
|
||||||
// Whether to show breadcrumbs.
|
// Whether to show breadcrumbs.
|
||||||
"breadcrumbs": true,
|
"breadcrumbs": true,
|
||||||
// Whether to show quick action buttons.
|
// Whether to show quick action buttons.
|
||||||
"quick_actions": true
|
"quick_actions": true,
|
||||||
|
// Whether to show the Selections menu in the editor toolbar
|
||||||
|
"selections_menu": true
|
||||||
},
|
},
|
||||||
// Scrollbar related settings
|
// Scrollbar related settings
|
||||||
"scrollbar": {
|
"scrollbar": {
|
||||||
|
|
|
@ -832,13 +832,8 @@ impl PromptLibrary {
|
||||||
|
|
||||||
impl Render for PromptLibrary {
|
impl Render for PromptLibrary {
|
||||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||||
let (ui_font, ui_font_size) = {
|
let ui_font = theme::setup_ui_font(cx);
|
||||||
let theme_settings = ThemeSettings::get_global(cx);
|
|
||||||
(theme_settings.ui_font.clone(), theme_settings.ui_font_size)
|
|
||||||
};
|
|
||||||
|
|
||||||
let theme = cx.theme().clone();
|
let theme = cx.theme().clone();
|
||||||
cx.set_rem_size(ui_font_size);
|
|
||||||
|
|
||||||
h_flex()
|
h_flex()
|
||||||
.id("prompt-manager")
|
.id("prompt-manager")
|
||||||
|
|
|
@ -86,10 +86,16 @@ impl Render for Breadcrumbs {
|
||||||
.style(ButtonStyle::Subtle)
|
.style(ButtonStyle::Subtle)
|
||||||
.on_click(move |_, cx| {
|
.on_click(move |_, cx| {
|
||||||
if let Some(editor) = editor.upgrade() {
|
if let Some(editor) = editor.upgrade() {
|
||||||
outline::toggle(editor, &outline::Toggle, cx)
|
outline::toggle(editor, &editor::actions::ToggleOutline, cx)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.tooltip(|cx| Tooltip::for_action("Show symbol outline", &outline::Toggle, cx)),
|
.tooltip(|cx| {
|
||||||
|
Tooltip::for_action(
|
||||||
|
"Show symbol outline",
|
||||||
|
&editor::actions::ToggleOutline,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
}),
|
||||||
),
|
),
|
||||||
None => element
|
None => element
|
||||||
// Match the height of the `ButtonLike` in the other arm.
|
// Match the height of the `ButtonLike` in the other arm.
|
||||||
|
|
|
@ -35,10 +35,12 @@ call.workspace = true
|
||||||
channel.workspace = true
|
channel.workspace = true
|
||||||
client.workspace = true
|
client.workspace = true
|
||||||
collections.workspace = true
|
collections.workspace = true
|
||||||
|
command_palette.workspace = true
|
||||||
db.workspace = true
|
db.workspace = true
|
||||||
editor.workspace = true
|
editor.workspace = true
|
||||||
emojis.workspace = true
|
emojis.workspace = true
|
||||||
extensions_ui.workspace = true
|
extensions_ui.workspace = true
|
||||||
|
feedback.workspace = true
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
fuzzy.workspace = true
|
fuzzy.workspace = true
|
||||||
gpui.workspace = true
|
gpui.workspace = true
|
||||||
|
|
|
@ -10,8 +10,9 @@ use gpui::{
|
||||||
use project::{Project, RepositoryEntry};
|
use project::{Project, RepositoryEntry};
|
||||||
use recent_projects::RecentProjects;
|
use recent_projects::RecentProjects;
|
||||||
use rpc::proto::{self, DevServerStatus};
|
use rpc::proto::{self, DevServerStatus};
|
||||||
|
use settings::Settings;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use theme::ActiveTheme;
|
use theme::{ActiveTheme, ThemeSettings};
|
||||||
use ui::{
|
use ui::{
|
||||||
h_flex, prelude::*, Avatar, AvatarAudioStatusIndicator, Button, ButtonLike, ButtonStyle,
|
h_flex, prelude::*, Avatar, AvatarAudioStatusIndicator, Button, ButtonLike, ButtonStyle,
|
||||||
ContextMenu, Icon, IconButton, IconName, Indicator, PopoverMenu, TintColor, TitleBar, Tooltip,
|
ContextMenu, Icon, IconButton, IconName, Indicator, PopoverMenu, TintColor, TitleBar, Tooltip,
|
||||||
|
@ -73,6 +74,7 @@ impl Render for CollabTitlebarItem {
|
||||||
.child(
|
.child(
|
||||||
h_flex()
|
h_flex()
|
||||||
.gap_1()
|
.gap_1()
|
||||||
|
.children(self.render_application_menu(cx))
|
||||||
.children(self.render_project_host(cx))
|
.children(self.render_project_host(cx))
|
||||||
.child(self.render_project_name(cx))
|
.child(self.render_project_name(cx))
|
||||||
.children(self.render_project_branch(cx))
|
.children(self.render_project_branch(cx))
|
||||||
|
@ -386,8 +388,173 @@ impl CollabTitlebarItem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// resolve if you are in a room -> render_project_owner
|
pub fn render_application_menu(&self, cx: &mut ViewContext<Self>) -> Option<AnyElement> {
|
||||||
// render_project_owner -> resolve if you are in a room -> Option<foo>
|
cfg!(not(target_os = "macos")).then(|| {
|
||||||
|
let ui_font_size = ThemeSettings::get_global(cx).ui_font_size;
|
||||||
|
let font = cx.text_style().font();
|
||||||
|
let font_id = cx.text_system().resolve_font(&font);
|
||||||
|
let width = cx
|
||||||
|
.text_system()
|
||||||
|
.typographic_bounds(font_id, ui_font_size, 'm')
|
||||||
|
.unwrap()
|
||||||
|
.size
|
||||||
|
.width
|
||||||
|
* 3.0;
|
||||||
|
|
||||||
|
PopoverMenu::new("application-menu")
|
||||||
|
.menu(move |cx| {
|
||||||
|
let width = width;
|
||||||
|
ContextMenu::build(cx, move |menu, _cx| {
|
||||||
|
let width = width;
|
||||||
|
menu.header("Workspace")
|
||||||
|
.action("Open Command Palette", Box::new(command_palette::Toggle))
|
||||||
|
.custom_row(move |cx| {
|
||||||
|
div()
|
||||||
|
.w_full()
|
||||||
|
.flex()
|
||||||
|
.flex_row()
|
||||||
|
.justify_between()
|
||||||
|
.cursor(gpui::CursorStyle::Arrow)
|
||||||
|
.child(Label::new("Buffer Font Size"))
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.flex()
|
||||||
|
.flex_row()
|
||||||
|
.child(div().w(px(16.0)))
|
||||||
|
.child(
|
||||||
|
IconButton::new(
|
||||||
|
"reset-buffer-zoom",
|
||||||
|
IconName::RotateCcw,
|
||||||
|
)
|
||||||
|
.on_click(|_, cx| {
|
||||||
|
cx.dispatch_action(Box::new(
|
||||||
|
zed_actions::ResetBufferFontSize,
|
||||||
|
))
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
IconButton::new("--buffer-zoom", IconName::Dash)
|
||||||
|
.on_click(|_, cx| {
|
||||||
|
cx.dispatch_action(Box::new(
|
||||||
|
zed_actions::DecreaseBufferFontSize,
|
||||||
|
))
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.w(width)
|
||||||
|
.flex()
|
||||||
|
.flex_row()
|
||||||
|
.justify_around()
|
||||||
|
.child(Label::new(
|
||||||
|
theme::get_buffer_font_size(cx).to_string(),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
IconButton::new("+-buffer-zoom", IconName::Plus)
|
||||||
|
.on_click(|_, cx| {
|
||||||
|
cx.dispatch_action(Box::new(
|
||||||
|
zed_actions::IncreaseBufferFontSize,
|
||||||
|
))
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.into_any_element()
|
||||||
|
})
|
||||||
|
.custom_row(move |cx| {
|
||||||
|
div()
|
||||||
|
.w_full()
|
||||||
|
.flex()
|
||||||
|
.flex_row()
|
||||||
|
.justify_between()
|
||||||
|
.cursor(gpui::CursorStyle::Arrow)
|
||||||
|
.child(Label::new("UI Font Size"))
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.flex()
|
||||||
|
.flex_row()
|
||||||
|
.child(
|
||||||
|
IconButton::new(
|
||||||
|
"reset-ui-zoom",
|
||||||
|
IconName::RotateCcw,
|
||||||
|
)
|
||||||
|
.on_click(|_, cx| {
|
||||||
|
cx.dispatch_action(Box::new(
|
||||||
|
zed_actions::ResetUiFontSize,
|
||||||
|
))
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
IconButton::new("--ui-zoom", IconName::Dash)
|
||||||
|
.on_click(|_, cx| {
|
||||||
|
cx.dispatch_action(Box::new(
|
||||||
|
zed_actions::DecreaseUiFontSize,
|
||||||
|
))
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.w(width)
|
||||||
|
.flex()
|
||||||
|
.flex_row()
|
||||||
|
.justify_around()
|
||||||
|
.child(Label::new(
|
||||||
|
theme::get_ui_font_size(cx).to_string(),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
IconButton::new("+-ui-zoom", IconName::Plus)
|
||||||
|
.on_click(|_, cx| {
|
||||||
|
cx.dispatch_action(Box::new(
|
||||||
|
zed_actions::IncreaseUiFontSize,
|
||||||
|
))
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.into_any_element()
|
||||||
|
})
|
||||||
|
.header("Project")
|
||||||
|
.action(
|
||||||
|
"Add Folder to Project...",
|
||||||
|
Box::new(workspace::AddFolderToProject),
|
||||||
|
)
|
||||||
|
.action("Open a new Project...", Box::new(workspace::Open))
|
||||||
|
.action(
|
||||||
|
"Open Recent Projects...",
|
||||||
|
Box::new(recent_projects::OpenRecent {
|
||||||
|
create_new_window: false,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.header("Help")
|
||||||
|
.action("About Zed", Box::new(zed_actions::About))
|
||||||
|
.action("Welcome", Box::new(workspace::Welcome))
|
||||||
|
.link(
|
||||||
|
"Documentation",
|
||||||
|
Box::new(zed_actions::OpenBrowser {
|
||||||
|
url: "https://zed.dev/docs".into(),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.action("Give Feedback", Box::new(feedback::GiveFeedback))
|
||||||
|
.action("Check for Updates", Box::new(auto_update::Check))
|
||||||
|
.action("View Telemetry", Box::new(zed_actions::OpenTelemetryLog))
|
||||||
|
.action(
|
||||||
|
"View Dependency Licenses",
|
||||||
|
Box::new(zed_actions::OpenLicenses),
|
||||||
|
)
|
||||||
|
.separator()
|
||||||
|
.action("Quit", Box::new(zed_actions::Quit))
|
||||||
|
})
|
||||||
|
.into()
|
||||||
|
})
|
||||||
|
.trigger(
|
||||||
|
IconButton::new("application-menu", ui::IconName::Menu)
|
||||||
|
.style(ButtonStyle::Subtle)
|
||||||
|
.tooltip(|cx| Tooltip::text("Open Application Menu", cx))
|
||||||
|
.icon_size(IconSize::Small),
|
||||||
|
)
|
||||||
|
.into_any_element()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn render_project_host(&self, cx: &mut ViewContext<Self>) -> Option<AnyElement> {
|
pub fn render_project_host(&self, cx: &mut ViewContext<Self>) -> Option<AnyElement> {
|
||||||
if let Some(dev_server) =
|
if let Some(dev_server) =
|
||||||
|
@ -743,8 +910,9 @@ impl CollabTitlebarItem {
|
||||||
.menu(|cx| {
|
.menu(|cx| {
|
||||||
ContextMenu::build(cx, |menu, _| {
|
ContextMenu::build(cx, |menu, _| {
|
||||||
menu.action("Settings", zed_actions::OpenSettings.boxed_clone())
|
menu.action("Settings", zed_actions::OpenSettings.boxed_clone())
|
||||||
.action("Extensions", extensions_ui::Extensions.boxed_clone())
|
.action("Key Bindings", Box::new(zed_actions::OpenKeymap))
|
||||||
.action("Themes…", theme_selector::Toggle::default().boxed_clone())
|
.action("Themes", theme_selector::Toggle::default().boxed_clone())
|
||||||
|
.action("Extensions...", extensions_ui::Extensions.boxed_clone())
|
||||||
.separator()
|
.separator()
|
||||||
.action("Sign Out", client::SignOut.boxed_clone())
|
.action("Sign Out", client::SignOut.boxed_clone())
|
||||||
})
|
})
|
||||||
|
@ -771,8 +939,9 @@ impl CollabTitlebarItem {
|
||||||
.menu(|cx| {
|
.menu(|cx| {
|
||||||
ContextMenu::build(cx, |menu, _| {
|
ContextMenu::build(cx, |menu, _| {
|
||||||
menu.action("Settings", zed_actions::OpenSettings.boxed_clone())
|
menu.action("Settings", zed_actions::OpenSettings.boxed_clone())
|
||||||
.action("Extensions", extensions_ui::Extensions.boxed_clone())
|
.action("Key Bindings", Box::new(zed_actions::OpenKeymap))
|
||||||
.action("Themes…", theme_selector::Toggle::default().boxed_clone())
|
.action("Themes", theme_selector::Toggle::default().boxed_clone())
|
||||||
|
.action("Extensions...", extensions_ui::Extensions.boxed_clone())
|
||||||
})
|
})
|
||||||
.into()
|
.into()
|
||||||
})
|
})
|
||||||
|
|
|
@ -3,9 +3,8 @@ use crate::notifications::collab_notification::CollabNotification;
|
||||||
use call::{ActiveCall, IncomingCall};
|
use call::{ActiveCall, IncomingCall};
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use gpui::{prelude::*, AppContext, WindowHandle};
|
use gpui::{prelude::*, AppContext, WindowHandle};
|
||||||
use settings::Settings;
|
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
use theme::ThemeSettings;
|
|
||||||
use ui::{prelude::*, Button, Label};
|
use ui::{prelude::*, Button, Label};
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
use workspace::AppState;
|
use workspace::AppState;
|
||||||
|
@ -113,13 +112,7 @@ impl IncomingCallNotification {
|
||||||
|
|
||||||
impl Render for IncomingCallNotification {
|
impl Render for IncomingCallNotification {
|
||||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||||
// TODO: Is there a better place for us to initialize the font?
|
let ui_font = theme::setup_ui_font(cx);
|
||||||
let (ui_font, ui_font_size) = {
|
|
||||||
let theme_settings = ThemeSettings::get_global(cx);
|
|
||||||
(theme_settings.ui_font.clone(), theme_settings.ui_font_size)
|
|
||||||
};
|
|
||||||
|
|
||||||
cx.set_rem_size(ui_font_size);
|
|
||||||
|
|
||||||
div().size_full().font(ui_font).child(
|
div().size_full().font(ui_font).child(
|
||||||
CollabNotification::new(
|
CollabNotification::new(
|
||||||
|
|
|
@ -4,9 +4,8 @@ use call::{room, ActiveCall};
|
||||||
use client::User;
|
use client::User;
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use gpui::{AppContext, Size};
|
use gpui::{AppContext, Size};
|
||||||
use settings::Settings;
|
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
use theme::ThemeSettings;
|
|
||||||
use ui::{prelude::*, Button, Label};
|
use ui::{prelude::*, Button, Label};
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
use workspace::AppState;
|
use workspace::AppState;
|
||||||
|
@ -124,13 +123,7 @@ impl ProjectSharedNotification {
|
||||||
|
|
||||||
impl Render for ProjectSharedNotification {
|
impl Render for ProjectSharedNotification {
|
||||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||||
// TODO: Is there a better place for us to initialize the font?
|
let ui_font = theme::setup_ui_font(cx);
|
||||||
let (ui_font, ui_font_size) = {
|
|
||||||
let theme_settings = ThemeSettings::get_global(cx);
|
|
||||||
(theme_settings.ui_font.clone(), theme_settings.ui_font_size)
|
|
||||||
};
|
|
||||||
|
|
||||||
cx.set_rem_size(ui_font_size);
|
|
||||||
|
|
||||||
div().size_full().font(ui_font).child(
|
div().size_full().font(ui_font).child(
|
||||||
CollabNotification::new(
|
CollabNotification::new(
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
//! This module contains all actions supported by [`Editor`].
|
//! This module contains all actions supported by [`Editor`].
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use gpui::action_as;
|
||||||
use util::serde::default_true;
|
use util::serde::default_true;
|
||||||
|
|
||||||
#[derive(PartialEq, Clone, Deserialize, Default)]
|
#[derive(PartialEq, Clone, Deserialize, Default)]
|
||||||
|
@ -290,6 +291,7 @@ gpui::actions!(
|
||||||
TabPrev,
|
TabPrev,
|
||||||
ToggleGitBlame,
|
ToggleGitBlame,
|
||||||
ToggleGitBlameInline,
|
ToggleGitBlameInline,
|
||||||
|
ToggleSelectionMenu,
|
||||||
ToggleHunkDiff,
|
ToggleHunkDiff,
|
||||||
ToggleInlayHints,
|
ToggleInlayHints,
|
||||||
ToggleLineNumbers,
|
ToggleLineNumbers,
|
||||||
|
@ -304,3 +306,7 @@ gpui::actions!(
|
||||||
UniqueLinesCaseSensitive,
|
UniqueLinesCaseSensitive,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
action_as!(outline, ToggleOutline as Toggle);
|
||||||
|
|
||||||
|
action_as!(go_to_line, ToggleGoToLine as Toggle);
|
||||||
|
|
|
@ -537,6 +537,7 @@ pub struct Editor {
|
||||||
show_git_blame_inline: bool,
|
show_git_blame_inline: bool,
|
||||||
show_git_blame_inline_delay_task: Option<Task<()>>,
|
show_git_blame_inline_delay_task: Option<Task<()>>,
|
||||||
git_blame_inline_enabled: bool,
|
git_blame_inline_enabled: bool,
|
||||||
|
show_selection_menu: Option<bool>,
|
||||||
blame: Option<Model<GitBlame>>,
|
blame: Option<Model<GitBlame>>,
|
||||||
blame_subscription: Option<Subscription>,
|
blame_subscription: Option<Subscription>,
|
||||||
custom_context_menu: Option<
|
custom_context_menu: Option<
|
||||||
|
@ -1833,6 +1834,7 @@ impl Editor {
|
||||||
custom_context_menu: None,
|
custom_context_menu: None,
|
||||||
show_git_blame_gutter: false,
|
show_git_blame_gutter: false,
|
||||||
show_git_blame_inline: false,
|
show_git_blame_inline: false,
|
||||||
|
show_selection_menu: None,
|
||||||
show_git_blame_inline_delay_task: None,
|
show_git_blame_inline_delay_task: None,
|
||||||
git_blame_inline_enabled: ProjectSettings::get_global(cx).git.inline_blame_enabled(),
|
git_blame_inline_enabled: ProjectSettings::get_global(cx).git.inline_blame_enabled(),
|
||||||
blame: None,
|
blame: None,
|
||||||
|
@ -10182,6 +10184,20 @@ impl Editor {
|
||||||
self.git_blame_inline_enabled
|
self.git_blame_inline_enabled
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn toggle_selection_menu(&mut self, _: &ToggleSelectionMenu, cx: &mut ViewContext<Self>) {
|
||||||
|
self.show_selection_menu = self
|
||||||
|
.show_selection_menu
|
||||||
|
.map(|show_selections_menu| !show_selections_menu)
|
||||||
|
.or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
|
||||||
|
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn selection_menu_enabled(&self, cx: &AppContext) -> bool {
|
||||||
|
self.show_selection_menu
|
||||||
|
.unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
|
||||||
|
}
|
||||||
|
|
||||||
fn start_git_blame(&mut self, user_triggered: bool, cx: &mut ViewContext<Self>) {
|
fn start_git_blame(&mut self, user_triggered: bool, cx: &mut ViewContext<Self>) {
|
||||||
if let Some(project) = self.project.as_ref() {
|
if let Some(project) = self.project.as_ref() {
|
||||||
let Some(buffer) = self.buffer().read(cx).as_singleton() else {
|
let Some(buffer) = self.buffer().read(cx).as_singleton() else {
|
||||||
|
|
|
@ -67,6 +67,7 @@ pub enum DoubleClickInMultibuffer {
|
||||||
pub struct Toolbar {
|
pub struct Toolbar {
|
||||||
pub breadcrumbs: bool,
|
pub breadcrumbs: bool,
|
||||||
pub quick_actions: bool,
|
pub quick_actions: bool,
|
||||||
|
pub selections_menu: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||||
|
@ -129,6 +130,7 @@ pub struct EditorSettingsContent {
|
||||||
///
|
///
|
||||||
/// Default: true
|
/// Default: true
|
||||||
pub hover_popover_enabled: Option<bool>,
|
pub hover_popover_enabled: Option<bool>,
|
||||||
|
|
||||||
/// Whether to pop the completions menu while typing in an editor without
|
/// Whether to pop the completions menu while typing in an editor without
|
||||||
/// explicitly requesting it.
|
/// explicitly requesting it.
|
||||||
///
|
///
|
||||||
|
@ -202,10 +204,15 @@ pub struct ToolbarContent {
|
||||||
///
|
///
|
||||||
/// Default: true
|
/// Default: true
|
||||||
pub breadcrumbs: Option<bool>,
|
pub breadcrumbs: Option<bool>,
|
||||||
/// Whether to display quik action buttons in the editor toolbar.
|
/// Whether to display quick action buttons in the editor toolbar.
|
||||||
///
|
///
|
||||||
/// Default: true
|
/// Default: true
|
||||||
pub quick_actions: Option<bool>,
|
pub quick_actions: Option<bool>,
|
||||||
|
|
||||||
|
/// Whether to show the selections menu in the editor toolbar
|
||||||
|
///
|
||||||
|
/// Default: true
|
||||||
|
pub selections_menu: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Scrollbar related settings
|
/// Scrollbar related settings
|
||||||
|
|
|
@ -7,9 +7,9 @@ use collections::{BTreeSet, HashMap};
|
||||||
use editor::{scroll::Autoscroll, Bias, Editor};
|
use editor::{scroll::Autoscroll, Bias, Editor};
|
||||||
use fuzzy::{CharBag, PathMatch, PathMatchCandidate};
|
use fuzzy::{CharBag, PathMatch, PathMatchCandidate};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions, impl_actions, rems, Action, AnyElement, AppContext, DismissEvent, EventEmitter,
|
actions, rems, Action, AnyElement, AppContext, DismissEvent, EventEmitter, FocusHandle,
|
||||||
FocusHandle, FocusableView, Model, Modifiers, ModifiersChangedEvent, ParentElement, Render,
|
FocusableView, Model, Modifiers, ModifiersChangedEvent, ParentElement, Render, Styled, Task,
|
||||||
Styled, Task, View, ViewContext, VisualContext, WeakView,
|
View, ViewContext, VisualContext, WeakView,
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use new_path_prompt::NewPathPrompt;
|
use new_path_prompt::NewPathPrompt;
|
||||||
|
@ -30,13 +30,6 @@ use util::{paths::PathLikeWithPosition, post_inc, ResultExt};
|
||||||
use workspace::{item::PreviewTabsSettings, ModalView, Workspace};
|
use workspace::{item::PreviewTabsSettings, ModalView, Workspace};
|
||||||
|
|
||||||
actions!(file_finder, [SelectPrev]);
|
actions!(file_finder, [SelectPrev]);
|
||||||
impl_actions!(file_finder, [Toggle]);
|
|
||||||
|
|
||||||
#[derive(Default, PartialEq, Eq, Clone, serde::Deserialize)]
|
|
||||||
pub struct Toggle {
|
|
||||||
#[serde(default)]
|
|
||||||
pub separate_history: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ModalView for FileFinder {}
|
impl ModalView for FileFinder {}
|
||||||
|
|
||||||
|
@ -52,7 +45,7 @@ pub fn init(cx: &mut AppContext) {
|
||||||
|
|
||||||
impl FileFinder {
|
impl FileFinder {
|
||||||
fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
|
fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
|
||||||
workspace.register_action(|workspace, action: &Toggle, cx| {
|
workspace.register_action(|workspace, action: &workspace::ToggleFileFinder, cx| {
|
||||||
let Some(file_finder) = workspace.active_modal::<Self>(cx) else {
|
let Some(file_finder) = workspace.active_modal::<Self>(cx) else {
|
||||||
Self::open(workspace, action.separate_history, cx);
|
Self::open(workspace, action.separate_history, cx);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -6,7 +6,7 @@ use gpui::{Entity, TestAppContext, VisualTestContext};
|
||||||
use menu::{Confirm, SelectNext, SelectPrev};
|
use menu::{Confirm, SelectNext, SelectPrev};
|
||||||
use project::FS_WATCH_LATENCY;
|
use project::FS_WATCH_LATENCY;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use workspace::{AppState, Workspace};
|
use workspace::{AppState, ToggleFileFinder, Workspace};
|
||||||
|
|
||||||
#[ctor::ctor]
|
#[ctor::ctor]
|
||||||
fn init_logger() {
|
fn init_logger() {
|
||||||
|
@ -872,7 +872,7 @@ async fn test_toggle_panel_new_selections(cx: &mut gpui::TestAppContext) {
|
||||||
let current_history = open_close_queried_buffer("sec", 1, "second.rs", &workspace, cx).await;
|
let current_history = open_close_queried_buffer("sec", 1, "second.rs", &workspace, cx).await;
|
||||||
|
|
||||||
for expected_selected_index in 0..current_history.len() {
|
for expected_selected_index in 0..current_history.len() {
|
||||||
cx.dispatch_action(Toggle::default());
|
cx.dispatch_action(ToggleFileFinder::default());
|
||||||
let picker = active_file_picker(&workspace, cx);
|
let picker = active_file_picker(&workspace, cx);
|
||||||
let selected_index = picker.update(cx, |picker, _| picker.delegate.selected_index());
|
let selected_index = picker.update(cx, |picker, _| picker.delegate.selected_index());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -881,7 +881,7 @@ async fn test_toggle_panel_new_selections(cx: &mut gpui::TestAppContext) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
cx.dispatch_action(Toggle::default());
|
cx.dispatch_action(ToggleFileFinder::default());
|
||||||
let selected_index = workspace.update(cx, |workspace, cx| {
|
let selected_index = workspace.update(cx, |workspace, cx| {
|
||||||
workspace
|
workspace
|
||||||
.active_modal::<FileFinder>(cx)
|
.active_modal::<FileFinder>(cx)
|
||||||
|
@ -1201,7 +1201,7 @@ async fn test_non_separate_history_items(cx: &mut TestAppContext) {
|
||||||
open_close_queried_buffer("lib", 1, "lib.rs", &workspace, cx).await;
|
open_close_queried_buffer("lib", 1, "lib.rs", &workspace, cx).await;
|
||||||
open_queried_buffer("main", 1, "main.rs", &workspace, cx).await;
|
open_queried_buffer("main", 1, "main.rs", &workspace, cx).await;
|
||||||
|
|
||||||
cx.dispatch_action(Toggle::default());
|
cx.dispatch_action(ToggleFileFinder::default());
|
||||||
let picker = active_file_picker(&workspace, cx);
|
let picker = active_file_picker(&workspace, cx);
|
||||||
// main.rs is on top, previously used is selected
|
// main.rs is on top, previously used is selected
|
||||||
picker.update(cx, |finder, _| {
|
picker.update(cx, |finder, _| {
|
||||||
|
@ -1653,7 +1653,7 @@ async fn test_switches_between_release_norelease_modes_on_forward_nav(
|
||||||
// Back to navigation with initial shortcut
|
// Back to navigation with initial shortcut
|
||||||
// Open file on modifiers release
|
// Open file on modifiers release
|
||||||
cx.simulate_modifiers_change(Modifiers::secondary_key());
|
cx.simulate_modifiers_change(Modifiers::secondary_key());
|
||||||
cx.dispatch_action(Toggle::default());
|
cx.dispatch_action(ToggleFileFinder::default());
|
||||||
cx.simulate_modifiers_change(Modifiers::none());
|
cx.simulate_modifiers_change(Modifiers::none());
|
||||||
cx.read(|cx| {
|
cx.read(|cx| {
|
||||||
let active_editor = workspace.read(cx).active_item_as::<Editor>(cx).unwrap();
|
let active_editor = workspace.read(cx).active_item_as::<Editor>(cx).unwrap();
|
||||||
|
@ -1769,7 +1769,7 @@ async fn test_repeat_toggle_action(cx: &mut gpui::TestAppContext) {
|
||||||
let project = Project::test(app_state.fs.clone(), ["/test".as_ref()], cx).await;
|
let project = Project::test(app_state.fs.clone(), ["/test".as_ref()], cx).await;
|
||||||
let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx));
|
let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx));
|
||||||
|
|
||||||
cx.dispatch_action(Toggle::default());
|
cx.dispatch_action(ToggleFileFinder::default());
|
||||||
let picker = active_file_picker(&workspace, cx);
|
let picker = active_file_picker(&workspace, cx);
|
||||||
picker.update(cx, |picker, _| {
|
picker.update(cx, |picker, _| {
|
||||||
assert_eq!(picker.delegate.selected_index, 0);
|
assert_eq!(picker.delegate.selected_index, 0);
|
||||||
|
@ -1777,9 +1777,9 @@ async fn test_repeat_toggle_action(cx: &mut gpui::TestAppContext) {
|
||||||
});
|
});
|
||||||
|
|
||||||
// When toggling repeatedly, the picker scrolls to reveal the selected item.
|
// When toggling repeatedly, the picker scrolls to reveal the selected item.
|
||||||
cx.dispatch_action(Toggle::default());
|
cx.dispatch_action(ToggleFileFinder::default());
|
||||||
cx.dispatch_action(Toggle::default());
|
cx.dispatch_action(ToggleFileFinder::default());
|
||||||
cx.dispatch_action(Toggle::default());
|
cx.dispatch_action(ToggleFileFinder::default());
|
||||||
picker.update(cx, |picker, _| {
|
picker.update(cx, |picker, _| {
|
||||||
assert_eq!(picker.delegate.selected_index, 3);
|
assert_eq!(picker.delegate.selected_index, 3);
|
||||||
assert_eq!(picker.logical_scroll_top_index(), 3);
|
assert_eq!(picker.logical_scroll_top_index(), 3);
|
||||||
|
@ -1886,7 +1886,7 @@ fn open_file_picker(
|
||||||
workspace: &View<Workspace>,
|
workspace: &View<Workspace>,
|
||||||
cx: &mut VisualTestContext,
|
cx: &mut VisualTestContext,
|
||||||
) -> View<Picker<FileFinderDelegate>> {
|
) -> View<Picker<FileFinderDelegate>> {
|
||||||
cx.dispatch_action(Toggle {
|
cx.dispatch_action(ToggleFileFinder {
|
||||||
separate_history: true,
|
separate_history: true,
|
||||||
});
|
});
|
||||||
active_file_picker(workspace, cx)
|
active_file_picker(workspace, cx)
|
||||||
|
|
|
@ -134,7 +134,13 @@ impl Render for CursorPosition {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
.tooltip(|cx| Tooltip::for_action("Go to Line/Column", &crate::Toggle, cx)),
|
.tooltip(|cx| {
|
||||||
|
Tooltip::for_action(
|
||||||
|
"Go to Line/Column",
|
||||||
|
&editor::actions::ToggleGoToLine,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ pub mod cursor_position;
|
||||||
use cursor_position::LineIndicatorFormat;
|
use cursor_position::LineIndicatorFormat;
|
||||||
use editor::{scroll::Autoscroll, Editor};
|
use editor::{scroll::Autoscroll, Editor};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions, div, prelude::*, AnyWindowHandle, AppContext, DismissEvent, EventEmitter, FocusHandle,
|
div, prelude::*, AnyWindowHandle, AppContext, DismissEvent, EventEmitter, FocusHandle,
|
||||||
FocusableView, Render, SharedString, Styled, Subscription, View, ViewContext, VisualContext,
|
FocusableView, Render, SharedString, Styled, Subscription, View, ViewContext, VisualContext,
|
||||||
};
|
};
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
|
@ -13,8 +13,6 @@ use ui::{h_flex, prelude::*, v_flex, Label};
|
||||||
use util::paths::FILE_ROW_COLUMN_DELIMITER;
|
use util::paths::FILE_ROW_COLUMN_DELIMITER;
|
||||||
use workspace::ModalView;
|
use workspace::ModalView;
|
||||||
|
|
||||||
actions!(go_to_line, [Toggle]);
|
|
||||||
|
|
||||||
pub fn init(cx: &mut AppContext) {
|
pub fn init(cx: &mut AppContext) {
|
||||||
LineIndicatorFormat::register(cx);
|
LineIndicatorFormat::register(cx);
|
||||||
cx.observe_new_views(GoToLine::register).detach();
|
cx.observe_new_views(GoToLine::register).detach();
|
||||||
|
@ -43,7 +41,7 @@ impl GoToLine {
|
||||||
fn register(editor: &mut Editor, cx: &mut ViewContext<Editor>) {
|
fn register(editor: &mut Editor, cx: &mut ViewContext<Editor>) {
|
||||||
let handle = cx.view().downgrade();
|
let handle = cx.view().downgrade();
|
||||||
editor
|
editor
|
||||||
.register_action(move |_: &Toggle, cx| {
|
.register_action(move |_: &editor::actions::ToggleGoToLine, cx| {
|
||||||
let Some(editor) = handle.upgrade() else {
|
let Some(editor) = handle.upgrade() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
@ -341,7 +339,7 @@ mod tests {
|
||||||
workspace: &View<Workspace>,
|
workspace: &View<Workspace>,
|
||||||
cx: &mut VisualTestContext,
|
cx: &mut VisualTestContext,
|
||||||
) -> View<GoToLine> {
|
) -> View<GoToLine> {
|
||||||
cx.dispatch_action(Toggle);
|
cx.dispatch_action(editor::actions::ToggleGoToLine);
|
||||||
workspace.update(cx, |workspace, cx| {
|
workspace.update(cx, |workspace, cx| {
|
||||||
workspace.active_modal::<GoToLine>(cx).unwrap().clone()
|
workspace.active_modal::<GoToLine>(cx).unwrap().clone()
|
||||||
})
|
})
|
||||||
|
|
|
@ -189,7 +189,7 @@ macro_rules! actions {
|
||||||
#[serde(crate = "gpui::private::serde")]
|
#[serde(crate = "gpui::private::serde")]
|
||||||
pub struct $name;
|
pub struct $name;
|
||||||
|
|
||||||
gpui::__impl_action!($namespace, $name,
|
gpui::__impl_action!($namespace, $name, $name,
|
||||||
fn build(_: gpui::private::serde_json::Value) -> gpui::Result<::std::boxed::Box<dyn gpui::Action>> {
|
fn build(_: gpui::private::serde_json::Value) -> gpui::Result<::std::boxed::Box<dyn gpui::Action>> {
|
||||||
Ok(Box::new(Self))
|
Ok(Box::new(Self))
|
||||||
}
|
}
|
||||||
|
@ -200,12 +200,48 @@ macro_rules! actions {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Defines a unit struct that can be used as an actions, with a name
|
||||||
|
/// that differs from it's type name.
|
||||||
|
///
|
||||||
|
/// To use more complex data types as actions, and rename them use
|
||||||
|
/// `impl_action_as!`
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! action_as {
|
||||||
|
($namespace:path, $name:ident as $visual_name:tt) => {
|
||||||
|
#[doc = "The `"]
|
||||||
|
#[doc = stringify!($name)]
|
||||||
|
#[doc = "` action, see [`gpui::actions!`]"]
|
||||||
|
#[derive(
|
||||||
|
::std::cmp::PartialEq,
|
||||||
|
::std::clone::Clone,
|
||||||
|
::std::default::Default,
|
||||||
|
::std::fmt::Debug,
|
||||||
|
gpui::private::serde_derive::Deserialize,
|
||||||
|
)]
|
||||||
|
#[serde(crate = "gpui::private::serde")]
|
||||||
|
pub struct $name;
|
||||||
|
|
||||||
|
gpui::__impl_action!(
|
||||||
|
$namespace,
|
||||||
|
$name,
|
||||||
|
$visual_name,
|
||||||
|
fn build(
|
||||||
|
_: gpui::private::serde_json::Value,
|
||||||
|
) -> gpui::Result<::std::boxed::Box<dyn gpui::Action>> {
|
||||||
|
Ok(Box::new(Self))
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
gpui::register_action!($name);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/// Implements the Action trait for any struct that implements Clone, Default, PartialEq, and serde_deserialize::Deserialize
|
/// Implements the Action trait for any struct that implements Clone, Default, PartialEq, and serde_deserialize::Deserialize
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! impl_actions {
|
macro_rules! impl_actions {
|
||||||
($namespace:path, [ $($name:ident),* $(,)? ]) => {
|
($namespace:path, [ $($name:ident),* $(,)? ]) => {
|
||||||
$(
|
$(
|
||||||
gpui::__impl_action!($namespace, $name,
|
gpui::__impl_action!($namespace, $name, $name,
|
||||||
fn build(value: gpui::private::serde_json::Value) -> gpui::Result<::std::boxed::Box<dyn gpui::Action>> {
|
fn build(value: gpui::private::serde_json::Value) -> gpui::Result<::std::boxed::Box<dyn gpui::Action>> {
|
||||||
Ok(std::boxed::Box::new(gpui::private::serde_json::from_value::<Self>(value)?))
|
Ok(std::boxed::Box::new(gpui::private::serde_json::from_value::<Self>(value)?))
|
||||||
}
|
}
|
||||||
|
@ -216,17 +252,39 @@ macro_rules! impl_actions {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Implements the Action trait for a struct that implements Clone, Default, PartialEq, and serde_deserialize::Deserialize
|
||||||
|
/// Allows you to rename the action visually, without changing the struct's name
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! impl_action_as {
|
||||||
|
($namespace:path, $name:ident as $visual_name:tt ) => {
|
||||||
|
gpui::__impl_action!(
|
||||||
|
$namespace,
|
||||||
|
$name,
|
||||||
|
$visual_name,
|
||||||
|
fn build(
|
||||||
|
value: gpui::private::serde_json::Value,
|
||||||
|
) -> gpui::Result<::std::boxed::Box<dyn gpui::Action>> {
|
||||||
|
Ok(std::boxed::Box::new(
|
||||||
|
gpui::private::serde_json::from_value::<Self>(value)?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
gpui::register_action!($name);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! __impl_action {
|
macro_rules! __impl_action {
|
||||||
($namespace:path, $name:ident, $build:item) => {
|
($namespace:path, $name:ident, $visual_name:tt, $build:item) => {
|
||||||
impl gpui::Action for $name {
|
impl gpui::Action for $name {
|
||||||
fn name(&self) -> &'static str
|
fn name(&self) -> &'static str
|
||||||
{
|
{
|
||||||
concat!(
|
concat!(
|
||||||
stringify!($namespace),
|
stringify!($namespace),
|
||||||
"::",
|
"::",
|
||||||
stringify!($name),
|
stringify!($visual_name),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,7 +295,7 @@ macro_rules! __impl_action {
|
||||||
concat!(
|
concat!(
|
||||||
stringify!($namespace),
|
stringify!($namespace),
|
||||||
"::",
|
"::",
|
||||||
stringify!($name),
|
stringify!($visual_name),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2157,6 +2157,12 @@ impl From<Percentage> for Radians {
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct Pixels(pub f32);
|
pub struct Pixels(pub f32);
|
||||||
|
|
||||||
|
impl std::fmt::Display for Pixels {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.write_fmt(format_args!("{}px", self.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl std::ops::Div for Pixels {
|
impl std::ops::Div for Pixels {
|
||||||
type Output = f32;
|
type Output = f32;
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
use editor::{scroll::Autoscroll, Anchor, AnchorRangeExt, Editor, EditorMode};
|
use editor::{
|
||||||
|
actions::ToggleOutline, scroll::Autoscroll, Anchor, AnchorRangeExt, Editor, EditorMode,
|
||||||
|
};
|
||||||
use fuzzy::StringMatch;
|
use fuzzy::StringMatch;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions, div, rems, AppContext, DismissEvent, EventEmitter, FocusHandle, FocusableView,
|
div, rems, AppContext, DismissEvent, EventEmitter, FocusHandle, FocusableView, HighlightStyle,
|
||||||
HighlightStyle, ParentElement, Point, Render, Styled, Task, View, ViewContext, VisualContext,
|
ParentElement, Point, Render, Styled, Task, View, ViewContext, VisualContext, WeakView,
|
||||||
WeakView, WindowContext,
|
WindowContext,
|
||||||
};
|
};
|
||||||
use language::Outline;
|
use language::Outline;
|
||||||
use ordered_float::OrderedFloat;
|
use ordered_float::OrderedFloat;
|
||||||
|
@ -18,13 +20,11 @@ use ui::{prelude::*, ListItem, ListItemSpacing};
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
use workspace::{DismissDecision, ModalView};
|
use workspace::{DismissDecision, ModalView};
|
||||||
|
|
||||||
actions!(outline, [Toggle]);
|
|
||||||
|
|
||||||
pub fn init(cx: &mut AppContext) {
|
pub fn init(cx: &mut AppContext) {
|
||||||
cx.observe_new_views(OutlineView::register).detach();
|
cx.observe_new_views(OutlineView::register).detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn toggle(editor: View<Editor>, _: &Toggle, cx: &mut WindowContext) {
|
pub fn toggle(editor: View<Editor>, _: &ToggleOutline, cx: &mut WindowContext) {
|
||||||
let outline = editor
|
let outline = editor
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.buffer()
|
.buffer()
|
||||||
|
@ -423,7 +423,7 @@ mod tests {
|
||||||
workspace: &View<Workspace>,
|
workspace: &View<Workspace>,
|
||||||
cx: &mut VisualTestContext,
|
cx: &mut VisualTestContext,
|
||||||
) -> View<Picker<OutlineViewDelegate>> {
|
) -> View<Picker<OutlineViewDelegate>> {
|
||||||
cx.dispatch_action(Toggle);
|
cx.dispatch_action(ToggleOutline);
|
||||||
workspace.update(cx, |workspace, cx| {
|
workspace.update(cx, |workspace, cx| {
|
||||||
workspace
|
workspace
|
||||||
.active_modal::<OutlineView>(cx)
|
.active_modal::<OutlineView>(cx)
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use editor::{scroll::Autoscroll, styled_runs_for_code_label, Bias, Editor};
|
use editor::{scroll::Autoscroll, styled_runs_for_code_label, Bias, Editor};
|
||||||
use fuzzy::{StringMatch, StringMatchCandidate};
|
use fuzzy::{StringMatch, StringMatchCandidate};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions, rems, AppContext, DismissEvent, FontWeight, Model, ParentElement, StyledText, Task,
|
rems, AppContext, DismissEvent, FontWeight, Model, ParentElement, StyledText, Task, View,
|
||||||
View, ViewContext, WeakView, WindowContext,
|
ViewContext, WeakView, WindowContext,
|
||||||
};
|
};
|
||||||
use ordered_float::OrderedFloat;
|
use ordered_float::OrderedFloat;
|
||||||
use picker::{Picker, PickerDelegate};
|
use picker::{Picker, PickerDelegate};
|
||||||
|
@ -15,12 +15,10 @@ use workspace::{
|
||||||
Workspace,
|
Workspace,
|
||||||
};
|
};
|
||||||
|
|
||||||
actions!(project_symbols, [Toggle]);
|
|
||||||
|
|
||||||
pub fn init(cx: &mut AppContext) {
|
pub fn init(cx: &mut AppContext) {
|
||||||
cx.observe_new_views(
|
cx.observe_new_views(
|
||||||
|workspace: &mut Workspace, _: &mut ViewContext<Workspace>| {
|
|workspace: &mut Workspace, _: &mut ViewContext<Workspace>| {
|
||||||
workspace.register_action(|workspace, _: &Toggle, cx| {
|
workspace.register_action(|workspace, _: &workspace::ToggleProjectSymbols, cx| {
|
||||||
let project = workspace.project().clone();
|
let project = workspace.project().clone();
|
||||||
let handle = cx.view().downgrade();
|
let handle = cx.view().downgrade();
|
||||||
workspace.toggle_modal(cx, move |cx| {
|
workspace.toggle_modal(cx, move |cx| {
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
use assistant::assistant_settings::AssistantSettings;
|
use assistant::assistant_settings::AssistantSettings;
|
||||||
use assistant::{AssistantPanel, InlineAssist};
|
use assistant::{AssistantPanel, InlineAssist};
|
||||||
|
use editor::actions::{
|
||||||
|
AddSelectionAbove, AddSelectionBelow, DuplicateLineDown, GoToDiagnostic, GoToHunk,
|
||||||
|
GoToPrevDiagnostic, GoToPrevHunk, MoveLineDown, MoveLineUp, SelectAll, SelectLargerSyntaxNode,
|
||||||
|
SelectNext, SelectSmallerSyntaxNode, ToggleGoToLine, ToggleOutline,
|
||||||
|
};
|
||||||
use editor::{Editor, EditorSettings};
|
use editor::{Editor, EditorSettings};
|
||||||
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
|
@ -18,6 +23,7 @@ use workspace::{
|
||||||
pub struct QuickActionBar {
|
pub struct QuickActionBar {
|
||||||
buffer_search_bar: View<BufferSearchBar>,
|
buffer_search_bar: View<BufferSearchBar>,
|
||||||
toggle_settings_menu: Option<View<ContextMenu>>,
|
toggle_settings_menu: Option<View<ContextMenu>>,
|
||||||
|
toggle_selections_menu: Option<View<ContextMenu>>,
|
||||||
active_item: Option<Box<dyn ItemHandle>>,
|
active_item: Option<Box<dyn ItemHandle>>,
|
||||||
_inlay_hints_enabled_subscription: Option<Subscription>,
|
_inlay_hints_enabled_subscription: Option<Subscription>,
|
||||||
workspace: WeakView<Workspace>,
|
workspace: WeakView<Workspace>,
|
||||||
|
@ -33,6 +39,7 @@ impl QuickActionBar {
|
||||||
let mut this = Self {
|
let mut this = Self {
|
||||||
buffer_search_bar,
|
buffer_search_bar,
|
||||||
toggle_settings_menu: None,
|
toggle_settings_menu: None,
|
||||||
|
toggle_selections_menu: None,
|
||||||
active_item: None,
|
active_item: None,
|
||||||
_inlay_hints_enabled_subscription: None,
|
_inlay_hints_enabled_subscription: None,
|
||||||
workspace: workspace.weak_handle(),
|
workspace: workspace.weak_handle(),
|
||||||
|
@ -86,22 +93,43 @@ impl Render for QuickActionBar {
|
||||||
return div().id("empty quick action bar");
|
return div().id("empty quick action bar");
|
||||||
};
|
};
|
||||||
|
|
||||||
let search_button = Some(QuickActionBarButton::new(
|
let (
|
||||||
"toggle buffer search",
|
selection_menu_enabled,
|
||||||
IconName::MagnifyingGlass,
|
inlay_hints_enabled,
|
||||||
!self.buffer_search_bar.read(cx).is_dismissed(),
|
supports_inlay_hints,
|
||||||
Box::new(buffer_search::Deploy::find()),
|
git_blame_inline_enabled,
|
||||||
"Buffer Search",
|
) = {
|
||||||
{
|
let editor = editor.read(cx);
|
||||||
let buffer_search_bar = self.buffer_search_bar.clone();
|
let selection_menu_enabled = editor.selection_menu_enabled(cx);
|
||||||
move |_, cx| {
|
let inlay_hints_enabled = editor.inlay_hints_enabled();
|
||||||
buffer_search_bar.update(cx, |search_bar, cx| {
|
let supports_inlay_hints = editor.supports_inlay_hints(cx);
|
||||||
search_bar.toggle(&buffer_search::Deploy::find(), cx)
|
let git_blame_inline_enabled = editor.git_blame_inline_enabled();
|
||||||
});
|
|
||||||
}
|
(
|
||||||
},
|
selection_menu_enabled,
|
||||||
))
|
inlay_hints_enabled,
|
||||||
.filter(|_| editor.is_singleton(cx));
|
supports_inlay_hints,
|
||||||
|
git_blame_inline_enabled,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let search_button = editor.is_singleton(cx).then(|| {
|
||||||
|
QuickActionBarButton::new(
|
||||||
|
"toggle buffer search",
|
||||||
|
IconName::MagnifyingGlass,
|
||||||
|
!self.buffer_search_bar.read(cx).is_dismissed(),
|
||||||
|
Box::new(buffer_search::Deploy::find()),
|
||||||
|
"Buffer Search",
|
||||||
|
{
|
||||||
|
let buffer_search_bar = self.buffer_search_bar.clone();
|
||||||
|
move |_, cx| {
|
||||||
|
buffer_search_bar.update(cx, |search_bar, cx| {
|
||||||
|
search_bar.toggle(&buffer_search::Deploy::find(), cx)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
let assistant_button = QuickActionBarButton::new(
|
let assistant_button = QuickActionBarButton::new(
|
||||||
"toggle inline assistant",
|
"toggle inline assistant",
|
||||||
|
@ -121,6 +149,55 @@ impl Render for QuickActionBar {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let editor_selections_dropdown = selection_menu_enabled.then(|| {
|
||||||
|
IconButton::new("toggle_editor_selections_icon", IconName::TextCursor)
|
||||||
|
.size(ButtonSize::Compact)
|
||||||
|
.icon_size(IconSize::Small)
|
||||||
|
.style(ButtonStyle::Subtle)
|
||||||
|
.selected(self.toggle_selections_menu.is_some())
|
||||||
|
.on_click({
|
||||||
|
let focus = editor.focus_handle(cx);
|
||||||
|
cx.listener(move |quick_action_bar, _, cx| {
|
||||||
|
let focus = focus.clone();
|
||||||
|
let menu = ContextMenu::build(cx, move |menu, _| {
|
||||||
|
menu.context(focus.clone())
|
||||||
|
.action("Select All", Box::new(SelectAll))
|
||||||
|
.action(
|
||||||
|
"Select Next Occurrence",
|
||||||
|
Box::new(SelectNext {
|
||||||
|
replace_newest: false,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.action("Expand Selection", Box::new(SelectLargerSyntaxNode))
|
||||||
|
.action("Shrink Selection", Box::new(SelectSmallerSyntaxNode))
|
||||||
|
.action("Add Cursor Above", Box::new(AddSelectionAbove))
|
||||||
|
.action("Add Cursor Below", Box::new(AddSelectionBelow))
|
||||||
|
.separator()
|
||||||
|
.action("Go to Symbol", Box::new(ToggleOutline))
|
||||||
|
.action("Go to Line/Column", Box::new(ToggleGoToLine))
|
||||||
|
.separator()
|
||||||
|
.action("Next Problem", Box::new(GoToDiagnostic))
|
||||||
|
.action("Previous Problem", Box::new(GoToPrevDiagnostic))
|
||||||
|
.separator()
|
||||||
|
.action("Next Hunk", Box::new(GoToHunk))
|
||||||
|
.action("Previous Hunk", Box::new(GoToPrevHunk))
|
||||||
|
.separator()
|
||||||
|
.action("Move Line Up", Box::new(MoveLineUp))
|
||||||
|
.action("Move Line Down", Box::new(MoveLineDown))
|
||||||
|
.action("Duplicate Selection", Box::new(DuplicateLineDown))
|
||||||
|
});
|
||||||
|
cx.subscribe(&menu, |quick_action_bar, _, _: &DismissEvent, _cx| {
|
||||||
|
quick_action_bar.toggle_selections_menu = None;
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
quick_action_bar.toggle_selections_menu = Some(menu);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.when(self.toggle_selections_menu.is_none(), |this| {
|
||||||
|
this.tooltip(|cx| Tooltip::text("Selection Controls", cx))
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
let editor_settings_dropdown =
|
let editor_settings_dropdown =
|
||||||
IconButton::new("toggle_editor_settings_icon", IconName::Sliders)
|
IconButton::new("toggle_editor_settings_icon", IconName::Sliders)
|
||||||
.size(ButtonSize::Compact)
|
.size(ButtonSize::Compact)
|
||||||
|
@ -130,10 +207,6 @@ impl Render for QuickActionBar {
|
||||||
.on_click({
|
.on_click({
|
||||||
let editor = editor.clone();
|
let editor = editor.clone();
|
||||||
cx.listener(move |quick_action_bar, _, cx| {
|
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, _| {
|
let menu = ContextMenu::build(cx, |mut menu, _| {
|
||||||
if supports_inlay_hints {
|
if supports_inlay_hints {
|
||||||
menu = menu.toggleable_entry(
|
menu = menu.toggleable_entry(
|
||||||
|
@ -171,6 +244,23 @@ impl Render for QuickActionBar {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
menu = menu.toggleable_entry(
|
||||||
|
"Show Selection Menu",
|
||||||
|
selection_menu_enabled,
|
||||||
|
Some(editor::actions::ToggleSelectionMenu.boxed_clone()),
|
||||||
|
{
|
||||||
|
let editor = editor.clone();
|
||||||
|
move |cx| {
|
||||||
|
editor.update(cx, |editor, cx| {
|
||||||
|
editor.toggle_selection_menu(
|
||||||
|
&editor::actions::ToggleSelectionMenu,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
menu
|
menu
|
||||||
});
|
});
|
||||||
cx.subscribe(&menu, |quick_action_bar, _, _: &DismissEvent, _cx| {
|
cx.subscribe(&menu, |quick_action_bar, _, _: &DismissEvent, _cx| {
|
||||||
|
@ -191,6 +281,7 @@ impl Render for QuickActionBar {
|
||||||
h_flex()
|
h_flex()
|
||||||
.gap_1p5()
|
.gap_1p5()
|
||||||
.children(search_button)
|
.children(search_button)
|
||||||
|
.children(editor_selections_dropdown)
|
||||||
.when(AssistantSettings::get_global(cx).button, |bar| {
|
.when(AssistantSettings::get_global(cx).button, |bar| {
|
||||||
bar.child(assistant_button)
|
bar.child(assistant_button)
|
||||||
}),
|
}),
|
||||||
|
@ -202,6 +293,12 @@ impl Render for QuickActionBar {
|
||||||
el.child(Self::render_menu_overlay(toggle_settings_menu))
|
el.child(Self::render_menu_overlay(toggle_settings_menu))
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
.when_some(
|
||||||
|
self.toggle_selections_menu.as_ref(),
|
||||||
|
|el, toggle_selections_menu| {
|
||||||
|
el.child(Self::render_menu_overlay(toggle_selections_menu))
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ use anyhow::Result;
|
||||||
use derive_more::{Deref, DerefMut};
|
use derive_more::{Deref, DerefMut};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
px, AppContext, Font, FontFeatures, FontStyle, FontWeight, Global, Pixels, Subscription,
|
px, AppContext, Font, FontFeatures, FontStyle, FontWeight, Global, Pixels, Subscription,
|
||||||
ViewContext,
|
ViewContext, WindowContext,
|
||||||
};
|
};
|
||||||
use refineable::Refineable;
|
use refineable::Refineable;
|
||||||
use schemars::{
|
use schemars::{
|
||||||
|
@ -167,6 +167,11 @@ pub(crate) struct AdjustedBufferFontSize(Pixels);
|
||||||
|
|
||||||
impl Global for AdjustedBufferFontSize {}
|
impl Global for AdjustedBufferFontSize {}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub(crate) struct AdjustedUiFontSize(Pixels);
|
||||||
|
|
||||||
|
impl Global for AdjustedUiFontSize {}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
pub enum ThemeSelection {
|
pub enum ThemeSelection {
|
||||||
|
@ -358,7 +363,13 @@ pub fn adjusted_font_size(size: Pixels, cx: &mut AppContext) -> Pixels {
|
||||||
.max(MIN_FONT_SIZE)
|
.max(MIN_FONT_SIZE)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn adjust_font_size(cx: &mut AppContext, f: fn(&mut Pixels)) {
|
pub fn get_buffer_font_size(cx: &AppContext) -> Pixels {
|
||||||
|
let buffer_font_size = ThemeSettings::get_global(cx).buffer_font_size;
|
||||||
|
cx.try_global::<AdjustedBufferFontSize>()
|
||||||
|
.map_or(buffer_font_size, |adjusted_size| adjusted_size.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn adjust_buffer_font_size(cx: &mut AppContext, f: fn(&mut Pixels)) {
|
||||||
let buffer_font_size = ThemeSettings::get_global(cx).buffer_font_size;
|
let buffer_font_size = ThemeSettings::get_global(cx).buffer_font_size;
|
||||||
let mut adjusted_size = cx
|
let mut adjusted_size = cx
|
||||||
.try_global::<AdjustedBufferFontSize>()
|
.try_global::<AdjustedBufferFontSize>()
|
||||||
|
@ -370,13 +381,49 @@ pub fn adjust_font_size(cx: &mut AppContext, f: fn(&mut Pixels)) {
|
||||||
cx.refresh();
|
cx.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset_font_size(cx: &mut AppContext) {
|
pub fn reset_buffer_font_size(cx: &mut AppContext) {
|
||||||
if cx.has_global::<AdjustedBufferFontSize>() {
|
if cx.has_global::<AdjustedBufferFontSize>() {
|
||||||
cx.remove_global::<AdjustedBufferFontSize>();
|
cx.remove_global::<AdjustedBufferFontSize>();
|
||||||
cx.refresh();
|
cx.refresh();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn setup_ui_font(cx: &mut WindowContext) -> gpui::Font {
|
||||||
|
let (ui_font, ui_font_size) = {
|
||||||
|
let theme_settings = ThemeSettings::get_global(cx);
|
||||||
|
let font = theme_settings.ui_font.clone();
|
||||||
|
(font, get_ui_font_size(cx))
|
||||||
|
};
|
||||||
|
|
||||||
|
cx.set_rem_size(ui_font_size);
|
||||||
|
ui_font
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_ui_font_size(cx: &WindowContext) -> Pixels {
|
||||||
|
let ui_font_size = ThemeSettings::get_global(cx).ui_font_size;
|
||||||
|
cx.try_global::<AdjustedUiFontSize>()
|
||||||
|
.map_or(ui_font_size, |adjusted_size| adjusted_size.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn adjust_ui_font_size(cx: &mut WindowContext, f: fn(&mut Pixels)) {
|
||||||
|
let ui_font_size = ThemeSettings::get_global(cx).ui_font_size;
|
||||||
|
let mut adjusted_size = cx
|
||||||
|
.try_global::<AdjustedUiFontSize>()
|
||||||
|
.map_or(ui_font_size, |adjusted_size| adjusted_size.0);
|
||||||
|
|
||||||
|
f(&mut adjusted_size);
|
||||||
|
adjusted_size = adjusted_size.max(MIN_FONT_SIZE);
|
||||||
|
cx.set_global(AdjustedUiFontSize(adjusted_size));
|
||||||
|
cx.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reset_ui_font_size(cx: &mut WindowContext) {
|
||||||
|
if cx.has_global::<AdjustedUiFontSize>() {
|
||||||
|
cx.remove_global::<AdjustedUiFontSize>();
|
||||||
|
cx.refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl settings::Settings for ThemeSettings {
|
impl settings::Settings for ThemeSettings {
|
||||||
const KEY: Option<&'static str> = None;
|
const KEY: Option<&'static str> = None;
|
||||||
|
|
||||||
|
|
|
@ -84,7 +84,7 @@ pub fn init(themes_to_load: LoadThemes, cx: &mut AppContext) {
|
||||||
let buffer_font_size = ThemeSettings::get_global(cx).buffer_font_size;
|
let buffer_font_size = ThemeSettings::get_global(cx).buffer_font_size;
|
||||||
if buffer_font_size != prev_buffer_font_size {
|
if buffer_font_size != prev_buffer_font_size {
|
||||||
prev_buffer_font_size = buffer_font_size;
|
prev_buffer_font_size = buffer_font_size;
|
||||||
reset_font_size(cx);
|
reset_buffer_font_size(cx);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
|
|
|
@ -18,12 +18,13 @@ enum ContextMenuItem {
|
||||||
toggled: Option<bool>,
|
toggled: Option<bool>,
|
||||||
label: SharedString,
|
label: SharedString,
|
||||||
icon: Option<IconName>,
|
icon: Option<IconName>,
|
||||||
handler: Rc<dyn Fn(&mut WindowContext)>,
|
handler: Rc<dyn Fn(Option<&FocusHandle>, &mut WindowContext)>,
|
||||||
action: Option<Box<dyn Action>>,
|
action: Option<Box<dyn Action>>,
|
||||||
},
|
},
|
||||||
CustomEntry {
|
CustomEntry {
|
||||||
entry_render: Box<dyn Fn(&mut WindowContext) -> AnyElement>,
|
entry_render: Box<dyn Fn(&mut WindowContext) -> AnyElement>,
|
||||||
handler: Rc<dyn Fn(&mut WindowContext)>,
|
handler: Rc<dyn Fn(Option<&FocusHandle>, &mut WindowContext)>,
|
||||||
|
selectable: bool,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,7 +98,7 @@ impl ContextMenu {
|
||||||
self.items.push(ContextMenuItem::Entry {
|
self.items.push(ContextMenuItem::Entry {
|
||||||
toggled: None,
|
toggled: None,
|
||||||
label: label.into(),
|
label: label.into(),
|
||||||
handler: Rc::new(handler),
|
handler: Rc::new(move |_, cx| handler(cx)),
|
||||||
icon: None,
|
icon: None,
|
||||||
action,
|
action,
|
||||||
});
|
});
|
||||||
|
@ -114,13 +115,25 @@ impl ContextMenu {
|
||||||
self.items.push(ContextMenuItem::Entry {
|
self.items.push(ContextMenuItem::Entry {
|
||||||
toggled: Some(toggled),
|
toggled: Some(toggled),
|
||||||
label: label.into(),
|
label: label.into(),
|
||||||
handler: Rc::new(handler),
|
handler: Rc::new(move |_, cx| handler(cx)),
|
||||||
icon: None,
|
icon: None,
|
||||||
action,
|
action,
|
||||||
});
|
});
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn custom_row(
|
||||||
|
mut self,
|
||||||
|
entry_render: impl Fn(&mut WindowContext) -> AnyElement + 'static,
|
||||||
|
) -> Self {
|
||||||
|
self.items.push(ContextMenuItem::CustomEntry {
|
||||||
|
entry_render: Box::new(entry_render),
|
||||||
|
handler: Rc::new(|_, _| {}),
|
||||||
|
selectable: false,
|
||||||
|
});
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn custom_entry(
|
pub fn custom_entry(
|
||||||
mut self,
|
mut self,
|
||||||
entry_render: impl Fn(&mut WindowContext) -> AnyElement + 'static,
|
entry_render: impl Fn(&mut WindowContext) -> AnyElement + 'static,
|
||||||
|
@ -128,7 +141,8 @@ impl ContextMenu {
|
||||||
) -> Self {
|
) -> Self {
|
||||||
self.items.push(ContextMenuItem::CustomEntry {
|
self.items.push(ContextMenuItem::CustomEntry {
|
||||||
entry_render: Box::new(entry_render),
|
entry_render: Box::new(entry_render),
|
||||||
handler: Rc::new(handler),
|
handler: Rc::new(move |_, cx| handler(cx)),
|
||||||
|
selectable: true,
|
||||||
});
|
});
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -138,7 +152,13 @@ impl ContextMenu {
|
||||||
toggled: None,
|
toggled: None,
|
||||||
label: label.into(),
|
label: label.into(),
|
||||||
action: Some(action.boxed_clone()),
|
action: Some(action.boxed_clone()),
|
||||||
handler: Rc::new(move |cx| cx.dispatch_action(action.boxed_clone())),
|
|
||||||
|
handler: Rc::new(move |context, cx| {
|
||||||
|
if let Some(context) = &context {
|
||||||
|
cx.focus(context);
|
||||||
|
}
|
||||||
|
cx.dispatch_action(action.boxed_clone());
|
||||||
|
}),
|
||||||
icon: None,
|
icon: None,
|
||||||
});
|
});
|
||||||
self
|
self
|
||||||
|
@ -148,19 +168,21 @@ impl ContextMenu {
|
||||||
self.items.push(ContextMenuItem::Entry {
|
self.items.push(ContextMenuItem::Entry {
|
||||||
toggled: None,
|
toggled: None,
|
||||||
label: label.into(),
|
label: label.into(),
|
||||||
|
|
||||||
action: Some(action.boxed_clone()),
|
action: Some(action.boxed_clone()),
|
||||||
handler: Rc::new(move |cx| cx.dispatch_action(action.boxed_clone())),
|
handler: Rc::new(move |_, cx| cx.dispatch_action(action.boxed_clone())),
|
||||||
icon: Some(IconName::Link),
|
icon: Some(IconName::Link),
|
||||||
});
|
});
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn confirm(&mut self, _: &menu::Confirm, cx: &mut ViewContext<Self>) {
|
pub fn confirm(&mut self, _: &menu::Confirm, cx: &mut ViewContext<Self>) {
|
||||||
|
let context = self.action_context.as_ref();
|
||||||
match self.selected_index.and_then(|ix| self.items.get(ix)) {
|
match self.selected_index.and_then(|ix| self.items.get(ix)) {
|
||||||
Some(
|
Some(
|
||||||
ContextMenuItem::Entry { handler, .. }
|
ContextMenuItem::Entry { handler, .. }
|
||||||
| ContextMenuItem::CustomEntry { handler, .. },
|
| ContextMenuItem::CustomEntry { handler, .. },
|
||||||
) => (handler)(cx),
|
) => (handler)(context, cx),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -260,7 +282,12 @@ impl ContextMenu {
|
||||||
|
|
||||||
impl ContextMenuItem {
|
impl ContextMenuItem {
|
||||||
fn is_selectable(&self) -> bool {
|
fn is_selectable(&self) -> bool {
|
||||||
matches!(self, Self::Entry { .. } | Self::CustomEntry { .. })
|
match self {
|
||||||
|
ContextMenuItem::Separator => false,
|
||||||
|
ContextMenuItem::Header(_) => false,
|
||||||
|
ContextMenuItem::Entry { .. } => true,
|
||||||
|
ContextMenuItem::CustomEntry { selectable, .. } => *selectable,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -360,32 +387,47 @@ impl Render for ContextMenu {
|
||||||
.map(|binding| div().ml_4().child(binding))
|
.map(|binding| div().ml_4().child(binding))
|
||||||
})),
|
})),
|
||||||
)
|
)
|
||||||
.on_click(move |_, cx| {
|
.on_click({
|
||||||
handler(cx);
|
let context = self.action_context.clone();
|
||||||
menu.update(cx, |menu, cx| {
|
move |_, cx| {
|
||||||
menu.clicked = true;
|
handler(context.as_ref(), cx);
|
||||||
cx.emit(DismissEvent);
|
menu.update(cx, |menu, cx| {
|
||||||
})
|
menu.clicked = true;
|
||||||
.ok();
|
cx.emit(DismissEvent);
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.into_any_element()
|
.into_any_element()
|
||||||
}
|
}
|
||||||
ContextMenuItem::CustomEntry {
|
ContextMenuItem::CustomEntry {
|
||||||
entry_render,
|
entry_render,
|
||||||
handler,
|
handler,
|
||||||
|
selectable,
|
||||||
} => {
|
} => {
|
||||||
let handler = handler.clone();
|
let handler = handler.clone();
|
||||||
let menu = cx.view().downgrade();
|
let menu = cx.view().downgrade();
|
||||||
ListItem::new(ix)
|
ListItem::new(ix)
|
||||||
.inset(true)
|
.inset(true)
|
||||||
.selected(Some(ix) == self.selected_index)
|
.selected(if *selectable {
|
||||||
.on_click(move |_, cx| {
|
Some(ix) == self.selected_index
|
||||||
handler(cx);
|
} else {
|
||||||
menu.update(cx, |menu, cx| {
|
false
|
||||||
menu.clicked = true;
|
})
|
||||||
cx.emit(DismissEvent);
|
.selectable(*selectable)
|
||||||
})
|
.on_click({
|
||||||
.ok();
|
let context = self.action_context.clone();
|
||||||
|
let selectable = *selectable;
|
||||||
|
move |_, cx| {
|
||||||
|
if selectable {
|
||||||
|
handler(context.as_ref(), cx);
|
||||||
|
menu.update(cx, |menu, cx| {
|
||||||
|
menu.clicked = true;
|
||||||
|
cx.emit(DismissEvent);
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.child(entry_render(cx))
|
.child(entry_render(cx))
|
||||||
.into_any_element()
|
.into_any_element()
|
||||||
|
|
|
@ -174,6 +174,7 @@ pub enum IconName {
|
||||||
Rerun,
|
Rerun,
|
||||||
Return,
|
Return,
|
||||||
Reveal,
|
Reveal,
|
||||||
|
RotateCcw,
|
||||||
RotateCw,
|
RotateCw,
|
||||||
Save,
|
Save,
|
||||||
Screen,
|
Screen,
|
||||||
|
@ -199,6 +200,7 @@ pub enum IconName {
|
||||||
SupermavenInit,
|
SupermavenInit,
|
||||||
Tab,
|
Tab,
|
||||||
Terminal,
|
Terminal,
|
||||||
|
TextCursor,
|
||||||
Trash,
|
Trash,
|
||||||
TriangleRight,
|
TriangleRight,
|
||||||
Update,
|
Update,
|
||||||
|
@ -307,6 +309,7 @@ impl IconName {
|
||||||
IconName::Rerun => "icons/rerun.svg",
|
IconName::Rerun => "icons/rerun.svg",
|
||||||
IconName::Return => "icons/return.svg",
|
IconName::Return => "icons/return.svg",
|
||||||
IconName::RotateCw => "icons/rotate_cw.svg",
|
IconName::RotateCw => "icons/rotate_cw.svg",
|
||||||
|
IconName::RotateCcw => "icons/rotate_ccw.svg",
|
||||||
IconName::Save => "icons/save.svg",
|
IconName::Save => "icons/save.svg",
|
||||||
IconName::Screen => "icons/desktop.svg",
|
IconName::Screen => "icons/desktop.svg",
|
||||||
IconName::SelectAll => "icons/select_all.svg",
|
IconName::SelectAll => "icons/select_all.svg",
|
||||||
|
@ -331,6 +334,7 @@ impl IconName {
|
||||||
IconName::SupermavenInit => "icons/supermaven_init.svg",
|
IconName::SupermavenInit => "icons/supermaven_init.svg",
|
||||||
IconName::Tab => "icons/tab.svg",
|
IconName::Tab => "icons/tab.svg",
|
||||||
IconName::Terminal => "icons/terminal.svg",
|
IconName::Terminal => "icons/terminal.svg",
|
||||||
|
IconName::TextCursor => "icons/text-cursor.svg",
|
||||||
IconName::Trash => "icons/trash.svg",
|
IconName::Trash => "icons/trash.svg",
|
||||||
IconName::TriangleRight => "icons/triangle_right.svg",
|
IconName::TriangleRight => "icons/triangle_right.svg",
|
||||||
IconName::Update => "icons/update.svg",
|
IconName::Update => "icons/update.svg",
|
||||||
|
|
|
@ -35,6 +35,7 @@ pub struct ListItem {
|
||||||
tooltip: Option<Box<dyn Fn(&mut WindowContext) -> AnyView + 'static>>,
|
tooltip: Option<Box<dyn Fn(&mut WindowContext) -> AnyView + 'static>>,
|
||||||
on_secondary_mouse_down: Option<Box<dyn Fn(&MouseDownEvent, &mut WindowContext) + 'static>>,
|
on_secondary_mouse_down: Option<Box<dyn Fn(&MouseDownEvent, &mut WindowContext) + 'static>>,
|
||||||
children: SmallVec<[AnyElement; 2]>,
|
children: SmallVec<[AnyElement; 2]>,
|
||||||
|
selectable: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ListItem {
|
impl ListItem {
|
||||||
|
@ -56,6 +57,7 @@ impl ListItem {
|
||||||
on_toggle: None,
|
on_toggle: None,
|
||||||
tooltip: None,
|
tooltip: None,
|
||||||
children: SmallVec::new(),
|
children: SmallVec::new(),
|
||||||
|
selectable: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,6 +66,11 @@ impl ListItem {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn selectable(mut self, has_hover: bool) -> Self {
|
||||||
|
self.selectable = has_hover;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self {
|
pub fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self {
|
||||||
self.on_click = Some(Box::new(handler));
|
self.on_click = Some(Box::new(handler));
|
||||||
self
|
self
|
||||||
|
@ -164,10 +171,12 @@ impl RenderOnce for ListItem {
|
||||||
// this.border_1()
|
// this.border_1()
|
||||||
// .border_color(cx.theme().colors().border_focused)
|
// .border_color(cx.theme().colors().border_focused)
|
||||||
// })
|
// })
|
||||||
.hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
|
.when(self.selectable, |this| {
|
||||||
.active(|style| style.bg(cx.theme().colors().ghost_element_active))
|
this.hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
|
||||||
.when(self.selected, |this| {
|
.active(|style| style.bg(cx.theme().colors().ghost_element_active))
|
||||||
this.bg(cx.theme().colors().ghost_element_selected)
|
.when(self.selected, |this| {
|
||||||
|
this.bg(cx.theme().colors().ghost_element_selected)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.child(
|
.child(
|
||||||
|
@ -189,10 +198,14 @@ impl RenderOnce for ListItem {
|
||||||
// this.border_1()
|
// this.border_1()
|
||||||
// .border_color(cx.theme().colors().border_focused)
|
// .border_color(cx.theme().colors().border_focused)
|
||||||
// })
|
// })
|
||||||
.hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
|
.when(self.selectable, |this| {
|
||||||
.active(|style| style.bg(cx.theme().colors().ghost_element_active))
|
this.hover(|style| {
|
||||||
.when(self.selected, |this| {
|
style.bg(cx.theme().colors().ghost_element_hover)
|
||||||
this.bg(cx.theme().colors().ghost_element_selected)
|
})
|
||||||
|
.active(|style| style.bg(cx.theme().colors().ghost_element_active))
|
||||||
|
.when(self.selected, |this| {
|
||||||
|
this.bg(cx.theme().colors().ghost_element_selected)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.when_some(self.on_click, |this, on_click| {
|
.when_some(self.on_click, |this, on_click| {
|
||||||
|
|
|
@ -96,7 +96,9 @@ impl ModalLayer {
|
||||||
previous_focus_handle: cx.focused(),
|
previous_focus_handle: cx.focused(),
|
||||||
focus_handle,
|
focus_handle,
|
||||||
});
|
});
|
||||||
cx.focus_view(&new_modal);
|
cx.defer(move |_, cx| {
|
||||||
|
cx.focus_view(&new_modal);
|
||||||
|
});
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,8 +5,8 @@ use crate::{
|
||||||
},
|
},
|
||||||
toolbar::Toolbar,
|
toolbar::Toolbar,
|
||||||
workspace_settings::{AutosaveSetting, TabBarSettings, WorkspaceSettings},
|
workspace_settings::{AutosaveSetting, TabBarSettings, WorkspaceSettings},
|
||||||
CloseWindow, NewCenterTerminal, NewFile, NewSearch, OpenInTerminal, OpenTerminal, OpenVisible,
|
CloseWindow, NewFile, NewTerminal, OpenInTerminal, OpenTerminal, OpenVisible, SplitDirection,
|
||||||
SplitDirection, ToggleZoom, Workspace,
|
ToggleFileFinder, ToggleProjectSymbols, ToggleZoom, Workspace,
|
||||||
};
|
};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use collections::{BTreeSet, HashMap, HashSet, VecDeque};
|
use collections::{BTreeSet, HashMap, HashSet, VecDeque};
|
||||||
|
@ -366,8 +366,24 @@ impl Pane {
|
||||||
.on_click(cx.listener(|pane, _, cx| {
|
.on_click(cx.listener(|pane, _, cx| {
|
||||||
let menu = ContextMenu::build(cx, |menu, _| {
|
let menu = ContextMenu::build(cx, |menu, _| {
|
||||||
menu.action("New File", NewFile.boxed_clone())
|
menu.action("New File", NewFile.boxed_clone())
|
||||||
.action("New Terminal", NewCenterTerminal.boxed_clone())
|
.action(
|
||||||
.action("New Search", NewSearch.boxed_clone())
|
"Open File",
|
||||||
|
ToggleFileFinder::default().boxed_clone(),
|
||||||
|
)
|
||||||
|
.separator()
|
||||||
|
.action(
|
||||||
|
"Search Project",
|
||||||
|
DeploySearch {
|
||||||
|
replace_enabled: false,
|
||||||
|
}
|
||||||
|
.boxed_clone(),
|
||||||
|
)
|
||||||
|
.action(
|
||||||
|
"Search Symbols",
|
||||||
|
ToggleProjectSymbols.boxed_clone(),
|
||||||
|
)
|
||||||
|
.separator()
|
||||||
|
.action("New Terminal", NewTerminal.boxed_clone())
|
||||||
});
|
});
|
||||||
cx.subscribe(&menu, |pane, _, _: &DismissEvent, cx| {
|
cx.subscribe(&menu, |pane, _, _: &DismissEvent, cx| {
|
||||||
pane.focus(cx);
|
pane.focus(cx);
|
||||||
|
@ -1818,7 +1834,11 @@ impl Pane {
|
||||||
.track_scroll(self.tab_bar_scroll_handle.clone())
|
.track_scroll(self.tab_bar_scroll_handle.clone())
|
||||||
.when(
|
.when(
|
||||||
self.display_nav_history_buttons.unwrap_or_default(),
|
self.display_nav_history_buttons.unwrap_or_default(),
|
||||||
|tab_bar| tab_bar.start_children(vec![navigate_backward, navigate_forward]),
|
|tab_bar| {
|
||||||
|
tab_bar
|
||||||
|
.start_child(navigate_backward)
|
||||||
|
.start_child(navigate_forward)
|
||||||
|
},
|
||||||
)
|
)
|
||||||
.when(self.has_focus(cx), |tab_bar| {
|
.when(self.has_focus(cx), |tab_bar| {
|
||||||
tab_bar.end_child({
|
tab_bar.end_child({
|
||||||
|
|
|
@ -27,11 +27,11 @@ use futures::{
|
||||||
Future, FutureExt, StreamExt,
|
Future, FutureExt, StreamExt,
|
||||||
};
|
};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions, canvas, impl_actions, point, relative, size, Action, AnyElement, AnyView, AnyWeakView,
|
action_as, actions, canvas, impl_action_as, impl_actions, point, relative, size, Action,
|
||||||
AppContext, AsyncAppContext, AsyncWindowContext, Bounds, DragMoveEvent, Entity as _, EntityId,
|
AnyElement, AnyView, AnyWeakView, AppContext, AsyncAppContext, AsyncWindowContext, Bounds,
|
||||||
EventEmitter, FocusHandle, FocusableView, Global, KeyContext, Keystroke, ManagedView, Model,
|
DragMoveEvent, Entity as _, EntityId, EventEmitter, FocusHandle, FocusableView, Global,
|
||||||
ModelContext, PathPromptOptions, Point, PromptLevel, Render, Size, Subscription, Task, View,
|
KeyContext, Keystroke, ManagedView, Model, ModelContext, PathPromptOptions, Point, PromptLevel,
|
||||||
WeakView, WindowBounds, WindowHandle, WindowOptions,
|
Render, Size, Subscription, Task, View, WeakView, WindowBounds, WindowHandle, WindowOptions,
|
||||||
};
|
};
|
||||||
use item::{
|
use item::{
|
||||||
FollowableItem, FollowableItemHandle, Item, ItemHandle, ItemSettings, PreviewTabsSettings,
|
FollowableItem, FollowableItemHandle, Item, ItemHandle, ItemSettings, PreviewTabsSettings,
|
||||||
|
@ -112,30 +112,30 @@ pub struct RemoveWorktreeFromProject(pub WorktreeId);
|
||||||
actions!(
|
actions!(
|
||||||
workspace,
|
workspace,
|
||||||
[
|
[
|
||||||
|
ActivateNextPane,
|
||||||
|
ActivatePreviousPane,
|
||||||
|
AddFolderToProject,
|
||||||
|
CloseAllDocks,
|
||||||
|
CloseWindow,
|
||||||
|
Feedback,
|
||||||
|
FollowNextCollaborator,
|
||||||
|
NewCenterTerminal,
|
||||||
|
NewFile,
|
||||||
|
NewSearch,
|
||||||
|
NewTerminal,
|
||||||
|
NewWindow,
|
||||||
Open,
|
Open,
|
||||||
OpenInTerminal,
|
OpenInTerminal,
|
||||||
NewFile,
|
ReloadActiveItem,
|
||||||
NewWindow,
|
|
||||||
CloseWindow,
|
|
||||||
AddFolderToProject,
|
|
||||||
Unfollow,
|
|
||||||
SaveAs,
|
SaveAs,
|
||||||
SaveWithoutFormat,
|
SaveWithoutFormat,
|
||||||
ReloadActiveItem,
|
|
||||||
ActivatePreviousPane,
|
|
||||||
ActivateNextPane,
|
|
||||||
FollowNextCollaborator,
|
|
||||||
NewTerminal,
|
|
||||||
NewCenterTerminal,
|
|
||||||
NewSearch,
|
|
||||||
Feedback,
|
|
||||||
Welcome,
|
|
||||||
ToggleZoom,
|
|
||||||
ToggleLeftDock,
|
|
||||||
ToggleRightDock,
|
|
||||||
ToggleBottomDock,
|
ToggleBottomDock,
|
||||||
ToggleCenteredLayout,
|
ToggleCenteredLayout,
|
||||||
CloseAllDocks,
|
ToggleLeftDock,
|
||||||
|
ToggleRightDock,
|
||||||
|
ToggleZoom,
|
||||||
|
Unfollow,
|
||||||
|
Welcome,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -188,6 +188,16 @@ pub struct Reload {
|
||||||
pub binary_path: Option<PathBuf>,
|
pub binary_path: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
action_as!(project_symbols, ToggleProjectSymbols as Toggle);
|
||||||
|
|
||||||
|
#[derive(Default, PartialEq, Eq, Clone, serde::Deserialize)]
|
||||||
|
pub struct ToggleFileFinder {
|
||||||
|
#[serde(default)]
|
||||||
|
pub separate_history: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_action_as!(file_finder, ToggleFileFinder as Toggle);
|
||||||
|
|
||||||
impl_actions!(
|
impl_actions!(
|
||||||
workspace,
|
workspace,
|
||||||
[
|
[
|
||||||
|
@ -4144,14 +4154,10 @@ impl Render for Workspace {
|
||||||
} else {
|
} else {
|
||||||
(None, None)
|
(None, None)
|
||||||
};
|
};
|
||||||
let (ui_font, ui_font_size) = {
|
let ui_font = theme::setup_ui_font(cx);
|
||||||
let theme_settings = ThemeSettings::get_global(cx);
|
|
||||||
(theme_settings.ui_font.clone(), theme_settings.ui_font_size)
|
|
||||||
};
|
|
||||||
|
|
||||||
let theme = cx.theme().clone();
|
let theme = cx.theme().clone();
|
||||||
let colors = theme.colors();
|
let colors = theme.colors();
|
||||||
cx.set_rem_size(ui_font_size);
|
|
||||||
|
|
||||||
self.actions(div(), cx)
|
self.actions(div(), cx)
|
||||||
.key_context(context)
|
.key_context(context)
|
||||||
|
|
|
@ -52,22 +52,15 @@ use zed_actions::{OpenBrowser, OpenSettings, OpenZedUrl, Quit};
|
||||||
actions!(
|
actions!(
|
||||||
zed,
|
zed,
|
||||||
[
|
[
|
||||||
About,
|
|
||||||
DebugElements,
|
DebugElements,
|
||||||
DecreaseBufferFontSize,
|
|
||||||
Hide,
|
Hide,
|
||||||
HideOthers,
|
HideOthers,
|
||||||
IncreaseBufferFontSize,
|
|
||||||
Minimize,
|
Minimize,
|
||||||
OpenDefaultKeymap,
|
OpenDefaultKeymap,
|
||||||
OpenDefaultSettings,
|
OpenDefaultSettings,
|
||||||
OpenKeymap,
|
|
||||||
OpenLicenses,
|
|
||||||
OpenLocalSettings,
|
OpenLocalSettings,
|
||||||
OpenLocalTasks,
|
OpenLocalTasks,
|
||||||
OpenTasks,
|
OpenTasks,
|
||||||
OpenTelemetryLog,
|
|
||||||
ResetBufferFontSize,
|
|
||||||
ResetDatabase,
|
ResetDatabase,
|
||||||
ShowAll,
|
ShowAll,
|
||||||
ToggleFullScreen,
|
ToggleFullScreen,
|
||||||
|
@ -252,13 +245,33 @@ pub fn initialize_workspace(app_state: Arc<AppState>, cx: &mut AppContext) {
|
||||||
OpenListener::global(cx).open_urls(vec![action.url.clone()])
|
OpenListener::global(cx).open_urls(vec![action.url.clone()])
|
||||||
})
|
})
|
||||||
.register_action(|_, action: &OpenBrowser, cx| cx.open_url(&action.url))
|
.register_action(|_, action: &OpenBrowser, cx| cx.open_url(&action.url))
|
||||||
.register_action(move |_, _: &IncreaseBufferFontSize, cx| {
|
.register_action(move |_, _: &zed_actions::IncreaseBufferFontSize, cx| {
|
||||||
theme::adjust_font_size(cx, |size| *size += px(1.0))
|
theme::adjust_buffer_font_size(cx, |size| *size += px(1.0))
|
||||||
})
|
})
|
||||||
.register_action(move |_, _: &DecreaseBufferFontSize, cx| {
|
.register_action(move |_, _: &zed_actions::DecreaseBufferFontSize, cx| {
|
||||||
theme::adjust_font_size(cx, |size| *size -= px(1.0))
|
theme::adjust_buffer_font_size(cx, |size| *size -= px(1.0))
|
||||||
|
})
|
||||||
|
.register_action(move |_, _: &zed_actions::ResetBufferFontSize, cx| {
|
||||||
|
theme::reset_buffer_font_size(cx)
|
||||||
|
})
|
||||||
|
.register_action(move |_, _: &zed_actions::IncreaseUiFontSize, cx| {
|
||||||
|
theme::adjust_ui_font_size(cx, |size| *size += px(1.0))
|
||||||
|
})
|
||||||
|
.register_action(move |_, _: &zed_actions::DecreaseUiFontSize, cx| {
|
||||||
|
theme::adjust_ui_font_size(cx, |size| *size -= px(1.0))
|
||||||
|
})
|
||||||
|
.register_action(move |_, _: &zed_actions::ResetUiFontSize, cx| {
|
||||||
|
theme::reset_ui_font_size(cx)
|
||||||
|
})
|
||||||
|
.register_action(move |_, _: &zed_actions::IncreaseBufferFontSize, cx| {
|
||||||
|
theme::adjust_buffer_font_size(cx, |size| *size += px(1.0))
|
||||||
|
})
|
||||||
|
.register_action(move |_, _: &zed_actions::DecreaseBufferFontSize, cx| {
|
||||||
|
theme::adjust_buffer_font_size(cx, |size| *size -= px(1.0))
|
||||||
|
})
|
||||||
|
.register_action(move |_, _: &zed_actions::ResetBufferFontSize, cx| {
|
||||||
|
theme::reset_buffer_font_size(cx)
|
||||||
})
|
})
|
||||||
.register_action(move |_, _: &ResetBufferFontSize, cx| theme::reset_font_size(cx))
|
|
||||||
.register_action(|_, _: &install_cli::Install, cx| {
|
.register_action(|_, _: &install_cli::Install, cx| {
|
||||||
cx.spawn(|workspace, mut cx| async move {
|
cx.spawn(|workspace, mut cx| async move {
|
||||||
if cfg!(target_os = "linux") {
|
if cfg!(target_os = "linux") {
|
||||||
|
@ -323,7 +336,7 @@ pub fn initialize_workspace(app_state: Arc<AppState>, cx: &mut AppContext) {
|
||||||
.register_action(|workspace, _: &OpenLog, cx| {
|
.register_action(|workspace, _: &OpenLog, cx| {
|
||||||
open_log_file(workspace, cx);
|
open_log_file(workspace, cx);
|
||||||
})
|
})
|
||||||
.register_action(|workspace, _: &OpenLicenses, cx| {
|
.register_action(|workspace, _: &zed_actions::OpenLicenses, cx| {
|
||||||
open_bundled_file(
|
open_bundled_file(
|
||||||
workspace,
|
workspace,
|
||||||
asset_str::<Assets>("licenses.md"),
|
asset_str::<Assets>("licenses.md"),
|
||||||
|
@ -334,14 +347,16 @@ pub fn initialize_workspace(app_state: Arc<AppState>, cx: &mut AppContext) {
|
||||||
})
|
})
|
||||||
.register_action(
|
.register_action(
|
||||||
move |workspace: &mut Workspace,
|
move |workspace: &mut Workspace,
|
||||||
_: &OpenTelemetryLog,
|
_: &zed_actions::OpenTelemetryLog,
|
||||||
cx: &mut ViewContext<Workspace>| {
|
cx: &mut ViewContext<Workspace>| {
|
||||||
open_telemetry_log_file(workspace, cx);
|
open_telemetry_log_file(workspace, cx);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.register_action(
|
.register_action(
|
||||||
move |_: &mut Workspace, _: &OpenKeymap, cx: &mut ViewContext<Workspace>| {
|
move |_: &mut Workspace,
|
||||||
open_settings_file(paths::keymap_file(), Rope::default, cx);
|
_: &zed_actions::OpenKeymap,
|
||||||
|
cx: &mut ViewContext<Workspace>| {
|
||||||
|
open_settings_file(&paths::keymap_file(), Rope::default, cx);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.register_action(
|
.register_action(
|
||||||
|
@ -485,7 +500,7 @@ fn initialize_pane(workspace: &mut Workspace, pane: &View<Pane>, cx: &mut ViewCo
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn about(_: &mut Workspace, _: &About, cx: &mut gpui::ViewContext<Workspace>) {
|
fn about(_: &mut Workspace, _: &zed_actions::About, cx: &mut gpui::ViewContext<Workspace>) {
|
||||||
let release_channel = ReleaseChannel::global(cx).display_name();
|
let release_channel = ReleaseChannel::global(cx).display_name();
|
||||||
let version = env!("CARGO_PKG_VERSION");
|
let version = env!("CARGO_PKG_VERSION");
|
||||||
let message = format!("{release_channel} {version}");
|
let message = format!("{release_channel} {version}");
|
||||||
|
|
|
@ -9,14 +9,14 @@ pub fn app_menus() -> Vec<Menu<'static>> {
|
||||||
Menu {
|
Menu {
|
||||||
name: "Zed",
|
name: "Zed",
|
||||||
items: vec![
|
items: vec![
|
||||||
MenuItem::action("About Zed…", super::About),
|
MenuItem::action("About Zed…", zed_actions::About),
|
||||||
MenuItem::action("Check for Updates", auto_update::Check),
|
MenuItem::action("Check for Updates", auto_update::Check),
|
||||||
MenuItem::separator(),
|
MenuItem::separator(),
|
||||||
MenuItem::submenu(Menu {
|
MenuItem::submenu(Menu {
|
||||||
name: "Preferences",
|
name: "Preferences",
|
||||||
items: vec![
|
items: vec![
|
||||||
MenuItem::action("Open Settings", super::OpenSettings),
|
MenuItem::action("Open Settings", super::OpenSettings),
|
||||||
MenuItem::action("Open Key Bindings", super::OpenKeymap),
|
MenuItem::action("Open Key Bindings", zed_actions::OpenKeymap),
|
||||||
MenuItem::action("Open Default Settings", super::OpenDefaultSettings),
|
MenuItem::action("Open Default Settings", super::OpenDefaultSettings),
|
||||||
MenuItem::action("Open Default Key Bindings", super::OpenDefaultKeymap),
|
MenuItem::action("Open Default Key Bindings", super::OpenDefaultKeymap),
|
||||||
MenuItem::action("Open Local Settings", super::OpenLocalSettings),
|
MenuItem::action("Open Local Settings", super::OpenLocalSettings),
|
||||||
|
@ -104,9 +104,9 @@ pub fn app_menus() -> Vec<Menu<'static>> {
|
||||||
Menu {
|
Menu {
|
||||||
name: "View",
|
name: "View",
|
||||||
items: vec![
|
items: vec![
|
||||||
MenuItem::action("Zoom In", super::IncreaseBufferFontSize),
|
MenuItem::action("Zoom In", zed_actions::IncreaseBufferFontSize),
|
||||||
MenuItem::action("Zoom Out", super::DecreaseBufferFontSize),
|
MenuItem::action("Zoom Out", zed_actions::DecreaseBufferFontSize),
|
||||||
MenuItem::action("Reset Zoom", super::ResetBufferFontSize),
|
MenuItem::action("Reset Zoom", zed_actions::ResetBufferFontSize),
|
||||||
MenuItem::separator(),
|
MenuItem::separator(),
|
||||||
MenuItem::action("Toggle Left Dock", workspace::ToggleLeftDock),
|
MenuItem::action("Toggle Left Dock", workspace::ToggleLeftDock),
|
||||||
MenuItem::action("Toggle Right Dock", workspace::ToggleRightDock),
|
MenuItem::action("Toggle Right Dock", workspace::ToggleRightDock),
|
||||||
|
@ -139,10 +139,10 @@ pub fn app_menus() -> Vec<Menu<'static>> {
|
||||||
MenuItem::separator(),
|
MenuItem::separator(),
|
||||||
MenuItem::action("Command Palette...", command_palette::Toggle),
|
MenuItem::action("Command Palette...", command_palette::Toggle),
|
||||||
MenuItem::separator(),
|
MenuItem::separator(),
|
||||||
MenuItem::action("Go to File...", file_finder::Toggle::default()),
|
MenuItem::action("Go to File...", workspace::ToggleFileFinder::default()),
|
||||||
// MenuItem::action("Go to Symbol in Project", project_symbols::Toggle),
|
// MenuItem::action("Go to Symbol in Project", project_symbols::Toggle),
|
||||||
MenuItem::action("Go to Symbol in Editor...", outline::Toggle),
|
MenuItem::action("Go to Symbol in Editor...", editor::actions::ToggleOutline),
|
||||||
MenuItem::action("Go to Line/Column...", go_to_line::Toggle),
|
MenuItem::action("Go to Line/Column...", editor::actions::ToggleGoToLine),
|
||||||
MenuItem::separator(),
|
MenuItem::separator(),
|
||||||
MenuItem::action("Go to Definition", editor::actions::GoToDefinition),
|
MenuItem::action("Go to Definition", editor::actions::GoToDefinition),
|
||||||
MenuItem::action("Go to Type Definition", editor::actions::GoToTypeDefinition),
|
MenuItem::action("Go to Type Definition", editor::actions::GoToTypeDefinition),
|
||||||
|
@ -163,8 +163,8 @@ pub fn app_menus() -> Vec<Menu<'static>> {
|
||||||
Menu {
|
Menu {
|
||||||
name: "Help",
|
name: "Help",
|
||||||
items: vec![
|
items: vec![
|
||||||
MenuItem::action("View Telemetry", super::OpenTelemetryLog),
|
MenuItem::action("View Telemetry", zed_actions::OpenTelemetryLog),
|
||||||
MenuItem::action("View Dependency Licenses", super::OpenLicenses),
|
MenuItem::action("View Dependency Licenses", zed_actions::OpenLicenses),
|
||||||
MenuItem::action("Show Welcome", workspace::Welcome),
|
MenuItem::action("Show Welcome", workspace::Welcome),
|
||||||
MenuItem::action("Give Feedback...", feedback::GiveFeedback),
|
MenuItem::action("Give Feedback...", feedback::GiveFeedback),
|
||||||
MenuItem::separator(),
|
MenuItem::separator(),
|
||||||
|
|
|
@ -22,4 +22,20 @@ pub struct OpenZedUrl {
|
||||||
|
|
||||||
impl_actions!(zed, [OpenBrowser, OpenZedUrl]);
|
impl_actions!(zed, [OpenBrowser, OpenZedUrl]);
|
||||||
|
|
||||||
actions!(zed, [OpenSettings, Quit]);
|
actions!(
|
||||||
|
zed,
|
||||||
|
[
|
||||||
|
OpenSettings,
|
||||||
|
Quit,
|
||||||
|
OpenKeymap,
|
||||||
|
About,
|
||||||
|
OpenLicenses,
|
||||||
|
OpenTelemetryLog,
|
||||||
|
DecreaseBufferFontSize,
|
||||||
|
IncreaseBufferFontSize,
|
||||||
|
ResetBufferFontSize,
|
||||||
|
DecreaseUiFontSize,
|
||||||
|
IncreaseUiFontSize,
|
||||||
|
ResetUiFontSize
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue