diff --git a/assets/keymaps/default.json b/assets/keymaps/default.json index 0f1e005891..7f1c38d0b9 100644 --- a/assets/keymaps/default.json +++ b/assets/keymaps/default.json @@ -13,6 +13,8 @@ "ctrl-c": "menu::Cancel", "shift-cmd-{": "pane::ActivatePrevItem", "shift-cmd-}": "pane::ActivateNextItem", + "alt-cmd-left": "pane::ActivatePrevItem", + "alt-cmd-right": "pane::ActivateNextItem", "cmd-w": "pane::CloseActiveItem", "cmd-shift-W": "workspace::CloseWindow", "alt-cmd-t": "pane::CloseInactiveItems", @@ -210,6 +212,43 @@ { "context": "Pane", "bindings": { + "ctrl-1": [ + "pane::ActivateItem", + 0 + ], + "ctrl-2": [ + "pane::ActivateItem", + 1 + ], + "ctrl-3": [ + "pane::ActivateItem", + 2 + ], + "ctrl-4": [ + "pane::ActivateItem", + 3 + ], + "ctrl-5": [ + "pane::ActivateItem", + 4 + ], + "ctrl-6": [ + "pane::ActivateItem", + 5 + ], + "ctrl-7": [ + "pane::ActivateItem", + 6 + ], + "ctrl-8": [ + "pane::ActivateItem", + 7 + ], + "ctrl-9": [ + "pane::ActivateItem", + 8 + ], + "ctrl-0": "pane::ActivateLastItem", "ctrl--": "pane::GoBack", "shift-ctrl-_": "pane::GoForward", "cmd-shift-T": "pane::ReopenClosedItem", @@ -219,6 +258,43 @@ { "context": "Workspace", "bindings": { + "cmd-1": [ + "workspace::ActivatePane", + 0 + ], + "cmd-2": [ + "workspace::ActivatePane", + 1 + ], + "cmd-3": [ + "workspace::ActivatePane", + 2 + ], + "cmd-4": [ + "workspace::ActivatePane", + 3 + ], + "cmd-5": [ + "workspace::ActivatePane", + 4 + ], + "cmd-6": [ + "workspace::ActivatePane", + 5 + ], + "cmd-7": [ + "workspace::ActivatePane", + 6 + ], + "cmd-8": [ + "workspace::ActivatePane", + 7 + ], + "cmd-9": [ + "workspace::ActivatePane", + 8 + ], + "cmd-b": "workspace::ToggleLeftSidebar", "cmd-shift-F": "project_search::Deploy", "cmd-k cmd-t": "theme_selector::Toggle", "cmd-k cmd-s": "zed::OpenKeymap", @@ -226,6 +302,7 @@ "cmd-p": "file_finder::Toggle", "cmd-shift-P": "command_palette::Toggle", "cmd-shift-M": "diagnostics::Deploy", + "cmd-shift-E": "project_panel::Toggle", "cmd-alt-s": "workspace::SaveAll" } }, @@ -310,34 +387,8 @@ { "context": "Workspace", "bindings": { - "cmd-1": [ - "workspace::ToggleSidebarItemFocus", - { - "side": "Left", - "item_index": 0 - } - ], - "cmd-shift-!": [ - "workspace::ToggleSidebarItem", - { - "side": "Left", - "item_index": 0 - } - ], - "cmd-9": [ - "workspace::ToggleSidebarItemFocus", - { - "side": "Right", - "item_index": 0 - } - ], - "cmd-shift-(": [ - "workspace::ToggleSidebarItem", - { - "side": "Right", - "item_index": 0 - } - ] + "cmd-shift-C": "contacts_panel::Toggle", + "cmd-shift-B": "workspace::ToggleRightSidebar" } }, { diff --git a/crates/contacts_panel/src/contacts_panel.rs b/crates/contacts_panel/src/contacts_panel.rs index 1384774cc0..8ffbd17a1d 100644 --- a/crates/contacts_panel/src/contacts_panel.rs +++ b/crates/contacts_panel/src/contacts_panel.rs @@ -8,6 +8,7 @@ use contact_notification::ContactNotification; use editor::{Cancel, Editor}; use fuzzy::{match_strings, StringMatchCandidate}; use gpui::{ + actions, elements::*, geometry::{rect::RectF, vector::vec2f}, impl_actions, impl_internal_actions, @@ -24,6 +25,8 @@ use std::{ops::DerefMut, sync::Arc}; use theme::IconButton; use workspace::{sidebar::SidebarItem, JoinProject, ToggleProjectOnline, Workspace}; +actions!(contacts_panel, [Toggle]); + impl_actions!( contacts_panel, [RequestContact, RemoveContact, RespondToContactRequest] diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index 78aed09dc6..eaa747f2ac 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -108,7 +108,8 @@ actions!( Cut, Paste, Delete, - Rename + Rename, + Toggle ] ); impl_internal_actions!(project_panel, [Open, ToggleExpanded, DeployContextMenu]); diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 29770dc772..5e039b8cd0 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -18,11 +18,15 @@ use settings::Settings; use std::{any::Any, cell::RefCell, mem, path::Path, rc::Rc}; use util::ResultExt; +#[derive(Clone, Deserialize, PartialEq)] +pub struct ActivateItem(pub usize); + actions!( pane, [ ActivatePrevItem, ActivateNextItem, + ActivateLastItem, CloseActiveItem, CloseInactiveItems, ReopenClosedItem, @@ -39,9 +43,6 @@ pub struct CloseItem { pub pane: WeakViewHandle, } -#[derive(Clone, Deserialize, PartialEq)] -pub struct ActivateItem(pub usize); - #[derive(Clone, Deserialize, PartialEq)] pub struct GoBack { #[serde(skip_deserializing)] @@ -54,8 +55,8 @@ pub struct GoForward { pub pane: Option>, } -impl_actions!(pane, [GoBack, GoForward]); -impl_internal_actions!(pane, [CloseItem, ActivateItem]); +impl_actions!(pane, [GoBack, GoForward, ActivateItem]); +impl_internal_actions!(pane, [CloseItem]); const MAX_NAVIGATION_HISTORY_LEN: usize = 1024; @@ -63,6 +64,9 @@ pub fn init(cx: &mut MutableAppContext) { cx.add_action(|pane: &mut Pane, action: &ActivateItem, cx| { pane.activate_item(action.0, true, true, cx); }); + cx.add_action(|pane: &mut Pane, _: &ActivateLastItem, cx| { + pane.activate_item(pane.items.len() - 1, true, true, cx); + }); cx.add_action(|pane: &mut Pane, _: &ActivatePrevItem, cx| { pane.activate_prev_item(cx); }); diff --git a/crates/workspace/src/sidebar.rs b/crates/workspace/src/sidebar.rs index 0cfb7f7865..341026aecf 100644 --- a/crates/workspace/src/sidebar.rs +++ b/crates/workspace/src/sidebar.rs @@ -55,7 +55,8 @@ impl Into for &dyn SidebarItemHandle { pub struct Sidebar { side: Side, items: Vec, - active_item_ix: Option, + is_open: bool, + active_item_ix: usize, actual_width: Rc>, custom_width: Rc>, } @@ -83,25 +84,41 @@ pub struct ToggleSidebarItem { pub item_index: usize, } -#[derive(Clone, Debug, Deserialize, PartialEq)] -pub struct ToggleSidebarItemFocus { - pub side: Side, - pub item_index: usize, -} - -impl_actions!(workspace, [ToggleSidebarItem, ToggleSidebarItemFocus]); +impl_actions!(workspace, [ToggleSidebarItem]); impl Sidebar { pub fn new(side: Side) -> Self { Self { side, items: Default::default(), - active_item_ix: None, + active_item_ix: 0, + is_open: false, actual_width: Rc::new(RefCell::new(260.)), custom_width: Rc::new(RefCell::new(260.)), } } + pub fn is_open(&self) -> bool { + self.is_open + } + + pub fn active_item_ix(&self) -> usize { + self.active_item_ix + } + + pub fn set_open(&mut self, open: bool, cx: &mut ViewContext) { + if open != self.is_open { + self.is_open = open; + cx.notify(); + } + } + + pub fn toggle_open(&mut self, cx: &mut ViewContext) { + if self.is_open {} + self.is_open = !self.is_open; + cx.notify(); + } + pub fn add_item( &mut self, icon_path: &'static str, @@ -133,23 +150,25 @@ impl Sidebar { } pub fn activate_item(&mut self, item_ix: usize, cx: &mut ViewContext) { - self.active_item_ix = Some(item_ix); + self.active_item_ix = item_ix; cx.notify(); } pub fn toggle_item(&mut self, item_ix: usize, cx: &mut ViewContext) { - if self.active_item_ix == Some(item_ix) { - self.active_item_ix = None; + if self.active_item_ix == item_ix { + self.is_open = false; } else { - self.active_item_ix = Some(item_ix); + self.active_item_ix = item_ix; } cx.notify(); } pub fn active_item(&self) -> Option<&Rc> { - self.active_item_ix - .and_then(|ix| self.items.get(ix)) - .map(|item| &item.view) + if self.is_open { + self.items.get(self.active_item_ix).map(|item| &item.view) + } else { + None + } } fn render_resize_handle(&self, theme: &Theme, cx: &mut RenderContext) -> ElementBox { @@ -249,6 +268,7 @@ impl View for SidebarButtons { let item_style = theme.item; let badge_style = theme.badge; let active_ix = sidebar.active_item_ix; + let is_open = sidebar.is_open; let side = sidebar.side; let group_style = match side { Side::Left => theme.group_left, @@ -267,7 +287,7 @@ impl View for SidebarButtons { item_index: ix, }; MouseEventHandler::new::(ix, cx, move |state, cx| { - let is_active = Some(ix) == active_ix; + let is_active = is_open && ix == active_ix; let style = item_style.style_for(state, is_active); Stack::new() .with_child(Svg::new(icon_path).with_color(style.icon_color).boxed()) diff --git a/crates/workspace/src/waiting_room.rs b/crates/workspace/src/waiting_room.rs index 5052afcf50..e5d765d4d5 100644 --- a/crates/workspace/src/waiting_room.rs +++ b/crates/workspace/src/waiting_room.rs @@ -1,7 +1,4 @@ -use crate::{ - sidebar::{Side, ToggleSidebarItem}, - AppState, ToggleFollow, Workspace, -}; +use crate::{sidebar::Side, AppState, ToggleFollow, Workspace}; use anyhow::Result; use client::{proto, Client, Contact}; use gpui::{ @@ -104,13 +101,7 @@ impl WaitingRoom { &app_state, cx, ); - workspace.toggle_sidebar_item( - &ToggleSidebarItem { - side: Side::Left, - item_index: 0, - }, - cx, - ); + workspace.toggle_sidebar(Side::Left, cx); if let Some((host_peer_id, _)) = workspace.project.read(cx).collaborators().iter().find( |(_, collaborator)| collaborator.replica_id == 0, diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index b0e4373ca4..68e5b3cb26 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -31,7 +31,7 @@ use postage::prelude::Stream; use project::{fs, Fs, Project, ProjectEntryId, ProjectPath, ProjectStore, Worktree, WorktreeId}; use serde::Deserialize; use settings::Settings; -use sidebar::{Side, Sidebar, SidebarButtons, ToggleSidebarItem, ToggleSidebarItemFocus}; +use sidebar::{Side, Sidebar, SidebarButtons, ToggleSidebarItem}; use smallvec::SmallVec; use status_bar::StatusBar; pub use status_bar::StatusItemView; @@ -90,6 +90,8 @@ actions!( ActivatePreviousPane, ActivateNextPane, FollowNextCollaborator, + ToggleLeftSidebar, + ToggleRightSidebar, ] ); @@ -104,6 +106,9 @@ pub struct ToggleProjectOnline { pub project: Option>, } +#[derive(Clone, Deserialize, PartialEq)] +pub struct ActivatePane(pub usize); + #[derive(Clone, PartialEq)] pub struct ToggleFollow(pub PeerId); @@ -122,7 +127,7 @@ impl_internal_actions!( RemoveWorktreeFromProject ] ); -impl_actions!(workspace, [ToggleProjectOnline]); +impl_actions!(workspace, [ToggleProjectOnline, ActivatePane]); pub fn init(app_state: Arc, cx: &mut MutableAppContext) { pane::init(cx); @@ -185,7 +190,6 @@ pub fn init(app_state: Arc, cx: &mut MutableAppContext) { }, ); cx.add_action(Workspace::toggle_sidebar_item); - cx.add_action(Workspace::toggle_sidebar_item_focus); cx.add_action(Workspace::focus_center); cx.add_action(|workspace: &mut Workspace, _: &ActivatePreviousPane, cx| { workspace.activate_previous_pane(cx) @@ -193,6 +197,13 @@ pub fn init(app_state: Arc, cx: &mut MutableAppContext) { cx.add_action(|workspace: &mut Workspace, _: &ActivateNextPane, cx| { workspace.activate_next_pane(cx) }); + cx.add_action(|workspace: &mut Workspace, _: &ToggleLeftSidebar, cx| { + workspace.toggle_sidebar(Side::Left, cx); + }); + cx.add_action(|workspace: &mut Workspace, _: &ToggleRightSidebar, cx| { + workspace.toggle_sidebar(Side::Right, cx); + }); + cx.add_action(Workspace::activate_pane_at_index); let client = &app_state.client; client.add_view_request_handler(Workspace::handle_follow); @@ -1248,17 +1259,39 @@ impl Workspace { } } + pub fn toggle_sidebar(&mut self, side: Side, cx: &mut ViewContext) { + let sidebar = match side { + Side::Left => &mut self.left_sidebar, + Side::Right => &mut self.right_sidebar, + }; + sidebar.update(cx, |sidebar, cx| { + sidebar.set_open(!sidebar.is_open(), cx); + }); + cx.focus_self(); + cx.notify(); + } + pub fn toggle_sidebar_item(&mut self, action: &ToggleSidebarItem, cx: &mut ViewContext) { let sidebar = match action.side { Side::Left => &mut self.left_sidebar, Side::Right => &mut self.right_sidebar, }; let active_item = sidebar.update(cx, |sidebar, cx| { - sidebar.toggle_item(action.item_index, cx); - sidebar.active_item().map(|item| item.to_any()) + if sidebar.is_open() && sidebar.active_item_ix() == action.item_index { + sidebar.set_open(false, cx); + None + } else { + sidebar.set_open(true, cx); + sidebar.activate_item(action.item_index, cx); + sidebar.active_item().cloned() + } }); if let Some(active_item) = active_item { - cx.focus(active_item); + if active_item.is_focused(cx) { + cx.focus_self(); + } else { + cx.focus(active_item.to_any()); + } } else { cx.focus_self(); } @@ -1267,15 +1300,17 @@ impl Workspace { pub fn toggle_sidebar_item_focus( &mut self, - action: &ToggleSidebarItemFocus, + side: Side, + item_index: usize, cx: &mut ViewContext, ) { - let sidebar = match action.side { + let sidebar = match side { Side::Left => &mut self.left_sidebar, Side::Right => &mut self.right_sidebar, }; let active_item = sidebar.update(cx, |sidebar, cx| { - sidebar.activate_item(action.item_index, cx); + sidebar.set_open(true, cx); + sidebar.activate_item(item_index, cx); sidebar.active_item().cloned() }); if let Some(active_item) = active_item { @@ -1405,6 +1440,15 @@ impl Workspace { } } + fn activate_pane_at_index(&mut self, action: &ActivatePane, cx: &mut ViewContext) { + let panes = self.center.panes(); + if let Some(pane) = panes.get(action.0).map(|p| (*p).clone()) { + self.activate_pane(pane, cx); + } else { + self.split_pane(self.active_pane.clone(), SplitDirection::Right, cx); + } + } + pub fn activate_next_pane(&mut self, cx: &mut ViewContext) { let next_pane = { let panes = self.center.panes(); @@ -2481,13 +2525,7 @@ pub fn open_paths( let mut workspace = Workspace::new(project, cx); (app_state.initialize_workspace)(&mut workspace, &app_state, cx); if contains_directory { - workspace.toggle_sidebar_item( - &ToggleSidebarItem { - side: Side::Left, - item_index: 0, - }, - cx, - ); + workspace.toggle_sidebar(Side::Left, cx); } workspace }) diff --git a/crates/zed/src/menus.rs b/crates/zed/src/menus.rs index dfc3556604..cadedc118b 100644 --- a/crates/zed/src/menus.rs +++ b/crates/zed/src/menus.rs @@ -187,11 +187,42 @@ pub fn menus() -> Vec> { }, MenuItem::Separator, MenuItem::Action { - name: "Project Browser", - action: Box::new(workspace::sidebar::ToggleSidebarItemFocus { - side: workspace::sidebar::Side::Left, - item_index: 0, - }), + name: "Toggle Left Sidebar", + action: Box::new(workspace::ToggleLeftSidebar), + }, + MenuItem::Action { + name: "Toggle Right Sidebar", + action: Box::new(workspace::ToggleRightSidebar), + }, + MenuItem::Submenu(Menu { + name: "Editor Layout", + items: vec![ + MenuItem::Action { + name: "Split Up", + action: Box::new(workspace::SplitUp), + }, + MenuItem::Action { + name: "Split Down", + action: Box::new(workspace::SplitDown), + }, + MenuItem::Action { + name: "Split Left", + action: Box::new(workspace::SplitLeft), + }, + MenuItem::Action { + name: "Split Right", + action: Box::new(workspace::SplitRight), + }, + ], + }), + MenuItem::Separator, + MenuItem::Action { + name: "Project Panel", + action: Box::new(project_panel::Toggle), + }, + MenuItem::Action { + name: "Contacts Panel", + action: Box::new(contacts_panel::Toggle), }, MenuItem::Action { name: "Command Palette", diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 68a713e9af..8341f3639e 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -34,7 +34,7 @@ use std::{ }; use util::ResultExt; pub use workspace; -use workspace::{AppState, Workspace}; +use workspace::{sidebar::Side, AppState, Workspace}; #[derive(Deserialize, Clone, PartialEq)] struct OpenBrowser { @@ -128,6 +128,16 @@ pub fn init(app_state: &Arc, cx: &mut gpui::MutableAppContext) { } }, ); + cx.add_action( + |workspace: &mut Workspace, _: &project_panel::Toggle, cx: &mut ViewContext| { + workspace.toggle_sidebar_item_focus(Side::Left, 0, cx); + }, + ); + cx.add_action( + |workspace: &mut Workspace, _: &contacts_panel::Toggle, cx: &mut ViewContext| { + workspace.toggle_sidebar_item_focus(Side::Right, 0, cx); + }, + ); lsp_status::init(cx); settings::KeymapFileContent::load_defaults(cx); @@ -429,7 +439,7 @@ mod tests { let workspace_1 = cx.root_view::(cx.window_ids()[0]).unwrap(); workspace_1.update(cx, |workspace, cx| { assert_eq!(workspace.worktrees(cx).count(), 2); - assert!(workspace.left_sidebar().read(cx).active_item().is_some()); + assert!(workspace.left_sidebar().read(cx).is_open()); assert!(workspace.active_pane().is_focused(cx)); });