Merge pull request #1467 from zed-industries/new-button

Add discoverable 'New' button
This commit is contained in:
Mikayla Maki 2022-08-04 12:02:57 -07:00 committed by GitHub
commit 5a372cecaa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 87 additions and 18 deletions

View file

@ -31,7 +31,7 @@
"cmd-n": "workspace::NewFile", "cmd-n": "workspace::NewFile",
"cmd-shift-n": "workspace::NewWindow", "cmd-shift-n": "workspace::NewWindow",
"cmd-o": "workspace::Open", "cmd-o": "workspace::Open",
"ctrl-`": "terminal::Deploy" "ctrl-`": "workspace::NewTerminal"
} }
}, },
{ {
@ -300,7 +300,7 @@
8 8
], ],
"cmd-b": "workspace::ToggleLeftSidebar", "cmd-b": "workspace::ToggleLeftSidebar",
"cmd-shift-f": "project_search::Deploy", "cmd-shift-f": "workspace::NewSearch",
"cmd-k cmd-t": "theme_selector::Toggle", "cmd-k cmd-t": "theme_selector::Toggle",
"cmd-k cmd-s": "zed::OpenKeymap", "cmd-k cmd-s": "zed::OpenKeymap",
"cmd-t": "project_symbols::Toggle", "cmd-t": "project_symbols::Toggle",

View file

@ -22,6 +22,7 @@ pub fn init(cx: &mut MutableAppContext) {
cx.add_action(ContextMenu::cancel); cx.add_action(ContextMenu::cancel);
} }
//
pub enum ContextMenuItem { pub enum ContextMenuItem {
Item { Item {
label: String, label: String,
@ -258,6 +259,7 @@ impl ContextMenu {
let style = style let style = style
.item .item
.style_for(Default::default(), Some(ix) == self.selected_index); .style_for(Default::default(), Some(ix) == self.selected_index);
Label::new(label.to_string(), style.label.clone()) Label::new(label.to_string(), style.label.clone())
.contained() .contained()
.with_style(style.container) .with_style(style.container)
@ -319,9 +321,12 @@ impl ContextMenu {
MouseEventHandler::new::<MenuItem, _, _>(ix, cx, |state, _| { MouseEventHandler::new::<MenuItem, _, _>(ix, cx, |state, _| {
let style = let style =
style.item.style_for(state, Some(ix) == self.selected_index); style.item.style_for(state, Some(ix) == self.selected_index);
Flex::row() Flex::row()
.with_child( .with_child(
Label::new(label.to_string(), style.label.clone()).boxed(), Label::new(label.to_string(), style.label.clone())
.contained()
.boxed(),
) )
.with_child({ .with_child({
KeystrokeLabel::new( KeystrokeLabel::new(

View file

@ -24,7 +24,7 @@ use workspace::{
Item, ItemHandle, ItemNavHistory, Pane, ToolbarItemLocation, ToolbarItemView, Workspace, Item, ItemHandle, ItemNavHistory, Pane, ToolbarItemLocation, ToolbarItemView, Workspace,
}; };
actions!(project_search, [Deploy, SearchInNew, ToggleFocus]); actions!(project_search, [SearchInNew, ToggleFocus]);
#[derive(Default)] #[derive(Default)]
struct ActiveSearches(HashMap<WeakModelHandle<Project>, WeakViewHandle<ProjectSearchView>>); struct ActiveSearches(HashMap<WeakModelHandle<Project>, WeakViewHandle<ProjectSearchView>>);
@ -431,7 +431,11 @@ impl ProjectSearchView {
// Re-activate the most recently activated search or the most recent if it has been closed. // Re-activate the most recently activated search or the most recent if it has been closed.
// If no search exists in the workspace, create a new one. // If no search exists in the workspace, create a new one.
fn deploy(workspace: &mut Workspace, _: &Deploy, cx: &mut ViewContext<Workspace>) { fn deploy(
workspace: &mut Workspace,
_: &workspace::NewSearch,
cx: &mut ViewContext<Workspace>,
) {
// Clean up entries for dropped projects // Clean up entries for dropped projects
cx.update_global(|state: &mut ActiveSearches, cx| { cx.update_global(|state: &mut ActiveSearches, cx| {
state.0.retain(|project, _| project.is_upgradable(cx)) state.0.retain(|project, _| project.is_upgradable(cx))

View file

@ -5,17 +5,17 @@ use gpui::{
actions, elements::*, AnyViewHandle, AppContext, Entity, ModelHandle, View, ViewContext, actions, elements::*, AnyViewHandle, AppContext, Entity, ModelHandle, View, ViewContext,
ViewHandle, ViewHandle,
}; };
use workspace::{Item, Workspace};
use crate::TerminalSize; use crate::TerminalSize;
use project::{LocalWorktree, Project, ProjectPath}; use project::{LocalWorktree, Project, ProjectPath};
use settings::{Settings, WorkingDirectory}; use settings::{Settings, WorkingDirectory};
use smallvec::SmallVec; use smallvec::SmallVec;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use workspace::{Item, Workspace};
use crate::connected_el::TerminalEl; use crate::connected_el::TerminalEl;
actions!(terminal, [Deploy, DeployModal]); actions!(terminal, [DeployModal]);
//Make terminal view an enum, that can give you views for the error and non-error states //Make terminal view an enum, that can give you views for the error and non-error states
//Take away all the result unwrapping in the current TerminalView by making it 'infallible' //Take away all the result unwrapping in the current TerminalView by making it 'infallible'
@ -59,7 +59,11 @@ impl Entity for ErrorView {
impl TerminalView { impl TerminalView {
///Create a new Terminal in the current working directory or the user's home directory ///Create a new Terminal in the current working directory or the user's home directory
pub fn deploy(workspace: &mut Workspace, _: &Deploy, cx: &mut ViewContext<Workspace>) { pub fn deploy(
workspace: &mut Workspace,
_: &workspace::NewTerminal,
cx: &mut ViewContext<Workspace>,
) {
let strategy = cx let strategy = cx
.global::<Settings>() .global::<Settings>()
.terminal_overrides .terminal_overrides

View file

@ -267,6 +267,8 @@ pub struct ContextMenuItem {
pub container: ContainerStyle, pub container: ContainerStyle,
pub label: TextStyle, pub label: TextStyle,
pub keystroke: ContainedText, pub keystroke: ContainedText,
pub icon_width: f32,
pub icon_spacing: f32,
} }
#[derive(Debug, Deserialize, Default)] #[derive(Debug, Deserialize, Default)]

View file

@ -1,5 +1,5 @@
use super::{ItemHandle, SplitDirection}; use super::{ItemHandle, SplitDirection};
use crate::{toolbar::Toolbar, Item, WeakItemHandle, Workspace}; use crate::{toolbar::Toolbar, Item, NewFile, NewSearch, NewTerminal, WeakItemHandle, Workspace};
use anyhow::Result; use anyhow::Result;
use collections::{HashMap, HashSet, VecDeque}; use collections::{HashMap, HashSet, VecDeque};
use context_menu::{ContextMenu, ContextMenuItem}; use context_menu::{ContextMenu, ContextMenuItem};
@ -65,8 +65,13 @@ pub struct DeploySplitMenu {
position: Vector2F, position: Vector2F,
} }
#[derive(Clone, PartialEq)]
pub struct DeployNewMenu {
position: Vector2F,
}
impl_actions!(pane, [GoBack, GoForward, ActivateItem]); impl_actions!(pane, [GoBack, GoForward, ActivateItem]);
impl_internal_actions!(pane, [CloseItem, DeploySplitMenu]); impl_internal_actions!(pane, [CloseItem, DeploySplitMenu, DeployNewMenu]);
const MAX_NAVIGATION_HISTORY_LEN: usize = 1024; const MAX_NAVIGATION_HISTORY_LEN: usize = 1024;
@ -98,6 +103,7 @@ pub fn init(cx: &mut MutableAppContext) {
cx.add_action(|pane: &mut Pane, _: &SplitRight, cx| pane.split(SplitDirection::Right, cx)); cx.add_action(|pane: &mut Pane, _: &SplitRight, cx| pane.split(SplitDirection::Right, cx));
cx.add_action(|pane: &mut Pane, _: &SplitDown, cx| pane.split(SplitDirection::Down, cx)); cx.add_action(|pane: &mut Pane, _: &SplitDown, cx| pane.split(SplitDirection::Down, cx));
cx.add_action(Pane::deploy_split_menu); cx.add_action(Pane::deploy_split_menu);
cx.add_action(Pane::deploy_new_menu);
cx.add_action(|workspace: &mut Workspace, _: &ReopenClosedItem, cx| { cx.add_action(|workspace: &mut Workspace, _: &ReopenClosedItem, cx| {
Pane::reopen_closed_item(workspace, cx).detach(); Pane::reopen_closed_item(workspace, cx).detach();
}); });
@ -141,7 +147,7 @@ pub struct Pane {
autoscroll: bool, autoscroll: bool,
nav_history: Rc<RefCell<NavHistory>>, nav_history: Rc<RefCell<NavHistory>>,
toolbar: ViewHandle<Toolbar>, toolbar: ViewHandle<Toolbar>,
split_menu: ViewHandle<ContextMenu>, context_menu: ViewHandle<ContextMenu>,
} }
pub struct ItemNavHistory { pub struct ItemNavHistory {
@ -182,7 +188,7 @@ pub struct NavigationEntry {
impl Pane { impl Pane {
pub fn new(cx: &mut ViewContext<Self>) -> Self { pub fn new(cx: &mut ViewContext<Self>) -> Self {
let handle = cx.weak_handle(); let handle = cx.weak_handle();
let split_menu = cx.add_view(|cx| ContextMenu::new(cx)); let context_menu = cx.add_view(|cx| ContextMenu::new(cx));
Self { Self {
items: Vec::new(), items: Vec::new(),
is_active: true, is_active: true,
@ -197,7 +203,7 @@ impl Pane {
pane: handle.clone(), pane: handle.clone(),
})), })),
toolbar: cx.add_view(|_| Toolbar::new(handle)), toolbar: cx.add_view(|_| Toolbar::new(handle)),
split_menu, context_menu,
} }
} }
@ -831,7 +837,7 @@ impl Pane {
} }
fn deploy_split_menu(&mut self, action: &DeploySplitMenu, cx: &mut ViewContext<Self>) { fn deploy_split_menu(&mut self, action: &DeploySplitMenu, cx: &mut ViewContext<Self>) {
self.split_menu.update(cx, |menu, cx| { self.context_menu.update(cx, |menu, cx| {
menu.show( menu.show(
action.position, action.position,
vec![ vec![
@ -845,6 +851,20 @@ impl Pane {
}); });
} }
fn deploy_new_menu(&mut self, action: &DeployNewMenu, cx: &mut ViewContext<Self>) {
self.context_menu.update(cx, |menu, cx| {
menu.show(
action.position,
vec![
ContextMenuItem::item("New File", NewFile),
ContextMenuItem::item("New Terminal", NewTerminal),
ContextMenuItem::item("New Search", NewSearch),
],
cx,
);
});
}
pub fn toolbar(&self) -> &ViewHandle<Toolbar> { pub fn toolbar(&self) -> &ViewHandle<Toolbar> {
&self.toolbar &self.toolbar
} }
@ -1083,10 +1103,40 @@ impl View for Pane {
.with_child(self.render_tabs(cx).flex(1., true).named("tabs")); .with_child(self.render_tabs(cx).flex(1., true).named("tabs"));
if self.is_active { if self.is_active {
tab_row.add_child( tab_row.add_children([
MouseEventHandler::new::<SplitIcon, _, _>( MouseEventHandler::new::<SplitIcon, _, _>(
0, 0,
cx, cx,
|mouse_state, cx| {
let theme =
&cx.global::<Settings>().theme.workspace.tab_bar;
let style =
theme.pane_button.style_for(mouse_state, false);
Svg::new("icons/plus_12.svg")
.with_color(style.color)
.constrained()
.with_width(style.icon_width)
.aligned()
.contained()
.with_style(style.container)
.constrained()
.with_width(style.button_width)
.with_height(style.button_width)
.aligned()
.boxed()
},
)
.with_cursor_style(CursorStyle::PointingHand)
.on_down(
MouseButton::Left,
|MouseButtonEvent { position, .. }, cx| {
cx.dispatch_action(DeployNewMenu { position });
},
)
.boxed(),
MouseEventHandler::new::<SplitIcon, _, _>(
1,
cx,
|mouse_state, cx| { |mouse_state, cx| {
let theme = let theme =
&cx.global::<Settings>().theme.workspace.tab_bar; &cx.global::<Settings>().theme.workspace.tab_bar;
@ -1114,7 +1164,7 @@ impl View for Pane {
}, },
) )
.boxed(), .boxed(),
) ])
} }
tab_row tab_row
@ -1155,7 +1205,7 @@ impl View for Pane {
}) })
.boxed(), .boxed(),
) )
.with_child(ChildView::new(&self.split_menu).boxed()) .with_child(ChildView::new(&self.context_menu).boxed())
.named("pane") .named("pane")
} }

View file

@ -96,6 +96,8 @@ actions!(
FollowNextCollaborator, FollowNextCollaborator,
ToggleLeftSidebar, ToggleLeftSidebar,
ToggleRightSidebar, ToggleRightSidebar,
NewTerminal,
NewSearch
] ]
); );

View file

@ -136,7 +136,7 @@ pub fn menus() -> Vec<Menu<'static>> {
}, },
MenuItem::Action { MenuItem::Action {
name: "Find In Project", name: "Find In Project",
action: Box::new(search::project_search::Deploy), action: Box::new(workspace::NewSearch),
}, },
MenuItem::Separator, MenuItem::Separator,
MenuItem::Action { MenuItem::Action {

View file

@ -16,6 +16,8 @@ export default function contextMenu(theme: Theme) {
border: border(theme, "primary"), border: border(theme, "primary"),
keystrokeMargin: 30, keystrokeMargin: 30,
item: { item: {
iconSpacing: 8,
iconWidth: 14,
padding: { left: 4, right: 4, top: 2, bottom: 2 }, padding: { left: 4, right: 4, top: 2, bottom: 2 },
cornerRadius: 6, cornerRadius: 6,
label: text(theme, "sans", "primary", { size: "sm" }), label: text(theme, "sans", "primary", { size: "sm" }),