From caa6a7523813877617c73f23e70f6e819ad30879 Mon Sep 17 00:00:00 2001 From: Joseph Lyons Date: Fri, 3 Mar 2023 15:04:35 -0800 Subject: [PATCH 01/15] Show a pop up menu for terminals Co-Authored-By: Joseph T. Lyons <19867440+JosephTLyons@users.noreply.github.com> Co-Authored-By: Antonio Scandurra --- crates/project/src/terminals.rs | 4 + crates/workspace/src/pane.rs | 2 +- crates/workspace/src/terminal_button.rs | 157 +++++++++++++++--------- crates/workspace/src/workspace.rs | 8 +- 4 files changed, 110 insertions(+), 61 deletions(-) diff --git a/crates/project/src/terminals.rs b/crates/project/src/terminals.rs index b01546ff5c7f75ebfe880ff2095d50bdb9b5ca68..f7b4105dd2ac8f52a52e2a524ca7afced24ace0d 100644 --- a/crates/project/src/terminals.rs +++ b/crates/project/src/terminals.rs @@ -58,6 +58,10 @@ impl Project { terminal } } + + pub fn local_terminal_handles(&self) -> &Vec> { + &self.terminals.local_handles + } } // TODO: Add a few tests for adding and removing terminal tabs diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 98fcac664c069fa5c22c233e54a2f5b0a96a1c25..f9ba6ef5b4e452aff4670900c30a16c904f451ee 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -166,8 +166,8 @@ 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, _: &SplitDown, cx| pane.split(SplitDirection::Down, cx)); cx.add_action(Pane::deploy_split_menu); - cx.add_action(Pane::deploy_new_menu); cx.add_action(Pane::deploy_dock_menu); + cx.add_action(Pane::deploy_new_menu); cx.add_action(|workspace: &mut Workspace, _: &ReopenClosedItem, cx| { Pane::reopen_closed_item(workspace, cx).detach(); }); diff --git a/crates/workspace/src/terminal_button.rs b/crates/workspace/src/terminal_button.rs index 118df7cab15686085647427f40db65451cf99987..6573809359bc0d4623a75975c1ebb485272d85c6 100644 --- a/crates/workspace/src/terminal_button.rs +++ b/crates/workspace/src/terminal_button.rs @@ -1,26 +1,26 @@ +use context_menu::{ContextMenu, ContextMenuItem}; use gpui::{ - elements::{Empty, MouseEventHandler, Svg}, - CursorStyle, Element, ElementBox, Entity, MouseButton, RenderContext, View, ViewContext, - ViewHandle, WeakViewHandle, + actions, elements::*, geometry::vector::vec2f, CursorStyle, Element, ElementBox, Entity, + MouseButton, MutableAppContext, RenderContext, View, ViewContext, ViewHandle, WeakViewHandle, }; use settings::Settings; -use crate::{dock::FocusDock, item::ItemHandle, StatusItemView, Workspace}; +use crate::{dock::FocusDock, item::ItemHandle, NewTerminal, StatusItemView, Workspace}; -pub struct TerminalButton { - workspace: WeakViewHandle, -} +// #[derive(Clone, PartialEq)] +// pub struct DeployTerminalMenu { +// position: Vector2F, +// } -// TODO: Rename this to `DeployTerminalButton` -impl TerminalButton { - pub fn new(workspace: ViewHandle, cx: &mut ViewContext) -> Self { - // When terminal moves, redraw so that the icon and toggle status matches. - cx.subscribe(&workspace, |_, _, _, cx| cx.notify()).detach(); +actions!(terminal, [DeployTerminalMenu]); - Self { - workspace: workspace.downgrade(), - } - } +pub fn init(cx: &mut MutableAppContext) { + cx.add_action(TerminalButton::deploy_terminal_menu); +} + +pub struct TerminalButton { + workspace: WeakViewHandle, + popup_menu: ViewHandle, } impl Entity for TerminalButton { @@ -34,52 +34,93 @@ impl View for TerminalButton { fn render(&mut self, cx: &mut RenderContext<'_, Self>) -> ElementBox { let workspace = self.workspace.upgrade(cx); + let project = match workspace { + Some(workspace) => workspace.read(cx).project().read(cx), + None => return Empty::new().boxed(), + }; + let has_terminals = !project.local_terminal_handles().is_empty(); + let terminal_count = project.local_terminal_handles().iter().count(); + let theme = cx.global::().theme.clone(); - if workspace.is_none() { - return Empty::new().boxed(); - } + Stack::new() + .with_child( + MouseEventHandler::::new(0, cx, { + let theme = theme.clone(); + move |state, _| { + let style = theme + .workspace + .status_bar + .sidebar_buttons + .item + .style_for(state, true); - // let workspace = workspace.unwrap(); - let theme = cx.global::().theme.clone(); + Svg::new("icons/terminal_12.svg") + .with_color(style.icon_color) + .constrained() + .with_width(style.icon_size) + .with_height(style.icon_size) + .contained() + .with_style(style.container) + .boxed() + } + }) + .with_cursor_style(CursorStyle::PointingHand) + .on_up(MouseButton::Left, move |_, _| { + // TODO: Do we need this stuff? + // let dock_pane = workspace.read(cx.app).dock_pane(); + // let drop_index = dock_pane.read(cx.app).items_len() + 1; + // handle_dropped_item(event, &dock_pane.downgrade(), drop_index, false, None, cx); + }) + .on_click(MouseButton::Left, move |_, cx| { + if has_terminals { + cx.dispatch_action(DeployTerminalMenu); + println!("Yes, has_terminals {}", terminal_count); + } else { + cx.dispatch_action(FocusDock); + }; + }) + .with_tooltip::( + 0, + "Show Terminal".into(), + Some(Box::new(FocusDock)), + theme.tooltip.clone(), + cx, + ) + .boxed(), + ) + .with_child(ChildView::new(&self.popup_menu, cx).boxed()) + .boxed() + } +} - MouseEventHandler::::new(0, cx, { - let theme = theme.clone(); - move |state, _| { - let style = theme - .workspace - .status_bar - .sidebar_buttons - .item - .style_for(state, true); +// TODO: Rename this to `DeployTerminalButton` +impl TerminalButton { + pub fn new(workspace: ViewHandle, cx: &mut ViewContext) -> Self { + // When terminal moves, redraw so that the icon and toggle status matches. + cx.subscribe(&workspace, |_, _, _, cx| cx.notify()).detach(); + Self { + workspace: workspace.downgrade(), + popup_menu: cx.add_view(|cx| { + let mut menu = ContextMenu::new(cx); + menu.set_position_mode(OverlayPositionMode::Local); + menu + }), + } + } - Svg::new("icons/terminal_12.svg") - .with_color(style.icon_color) - .constrained() - .with_width(style.icon_size) - .with_height(style.icon_size) - .contained() - .with_style(style.container) - .boxed() - } - }) - .with_cursor_style(CursorStyle::PointingHand) - .on_up(MouseButton::Left, move |_, _| { - // TODO: Do we need this stuff? - // let dock_pane = workspace.read(cx.app).dock_pane(); - // let drop_index = dock_pane.read(cx.app).items_len() + 1; - // handle_dropped_item(event, &dock_pane.downgrade(), drop_index, false, None, cx); - }) - .on_click(MouseButton::Left, |_, cx| { - cx.dispatch_action(FocusDock); - }) - .with_tooltip::( - 0, - "Show Terminal".into(), - Some(Box::new(FocusDock)), - theme.tooltip.clone(), - cx, - ) - .boxed() + pub fn deploy_terminal_menu( + &mut self, + _action: &DeployTerminalMenu, + cx: &mut ViewContext, + ) { + self.popup_menu.update(cx, |menu, cx| { + menu.show( + vec2f(0., 0.), + AnchorCorner::TopLeft, + vec![ContextMenuItem::item("New Terminal", NewTerminal)], + cx, + ); + }); } } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index b9d80e7150fae064c2b34e80dee0507b7da22a2e..db15839ad0eb208ebc5d8d3e52ac980d9f5bc9a5 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -83,7 +83,7 @@ use status_bar::StatusBar; pub use status_bar::StatusItemView; use theme::{Theme, ThemeRegistry}; pub use toolbar::{ToolbarItemLocation, ToolbarItemView}; -use util::ResultExt; +use util::{ResultExt, StaffMode}; lazy_static! { static ref ZED_WINDOW_SIZE: Option = env::var("ZED_WINDOW_SIZE") @@ -185,6 +185,7 @@ impl_actions!(workspace, [ActivatePane]); pub fn init(app_state: Arc, cx: &mut MutableAppContext) { pane::init(cx); dock::init(cx); + terminal_button::init(cx); notifications::init(cx); cx.add_global_action(open); @@ -595,7 +596,10 @@ impl Workspace { status_bar.add_left_item(left_sidebar_buttons, cx); status_bar.add_right_item(right_sidebar_buttons, cx); status_bar.add_right_item(toggle_dock, cx); - status_bar.add_right_item(toggle_terminal, cx); + // TOOD: Remove this when things are done + if **cx.default_global::() { + status_bar.add_right_item(toggle_terminal, cx); + } status_bar }); From c80942ea00245ba65165848ef5852918a8553f77 Mon Sep 17 00:00:00 2001 From: Joseph Lyons Date: Tue, 7 Mar 2023 14:41:18 -0500 Subject: [PATCH 02/15] Begin work to dynamically add terminal names to menu --- crates/workspace/src/terminal_button.rs | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/crates/workspace/src/terminal_button.rs b/crates/workspace/src/terminal_button.rs index 6573809359bc0d4623a75975c1ebb485272d85c6..4789152327657ad9dc169d3801f9ee8658a0b735 100644 --- a/crates/workspace/src/terminal_button.rs +++ b/crates/workspace/src/terminal_button.rs @@ -113,13 +113,23 @@ impl TerminalButton { _action: &DeployTerminalMenu, cx: &mut ViewContext, ) { + let mut menu_options = vec![ContextMenuItem::item("New Terminal", NewTerminal)]; + + if let Some(workspace) = self.workspace.upgrade(cx) { + let project = workspace.read(cx).project().read(cx); + let local_terminal_handles = project.local_terminal_handles(); + + for local_terminal_handle in local_terminal_handles { + if let Some(_) = local_terminal_handle.upgrade(cx) { + // TODO: Obtain the actual terminal "name" and put it in the menu + // TODO: Replace the `NewTerminal` action with an action that instead focuses the selected terminal + menu_options.push(ContextMenuItem::item("Terminal", NewTerminal)) + } + } + } + self.popup_menu.update(cx, |menu, cx| { - menu.show( - vec2f(0., 0.), - AnchorCorner::TopLeft, - vec![ContextMenuItem::item("New Terminal", NewTerminal)], - cx, - ); + menu.show(vec2f(0., 0.), AnchorCorner::TopLeft, menu_options, cx); }); } } From 0a3f0c52523504e68815942e861c4245e123eb1e Mon Sep 17 00:00:00 2001 From: Joseph Lyons Date: Tue, 7 Mar 2023 15:04:12 -0500 Subject: [PATCH 03/15] Use terminal titles for buttons --- crates/terminal/src/terminal.rs | 33 +++++++++++++++++++++ crates/terminal_view/src/terminal_view.rs | 35 ++--------------------- crates/workspace/src/terminal_button.rs | 7 +++-- 3 files changed, 39 insertions(+), 36 deletions(-) diff --git a/crates/terminal/src/terminal.rs b/crates/terminal/src/terminal.rs index feed3d510f78c30a1b32fc57c3a6162b5f9c4816..0eefc8939a35682acae1c21487a95f42c9a0d13d 100644 --- a/crates/terminal/src/terminal.rs +++ b/crates/terminal/src/terminal.rs @@ -32,6 +32,7 @@ use mappings::mouse::{ use procinfo::LocalProcessInfo; use settings::{AlternateScroll, Settings, Shell, TerminalBlink}; +use util::truncate_and_trailoff; use std::{ cmp::min, @@ -1169,6 +1170,38 @@ impl Terminal { all_search_matches(&term, &searcher).collect() }) } + + pub fn title(&self) -> String { + self.foreground_process_info + .as_ref() + .map(|fpi| { + format!( + "{} — {}", + truncate_and_trailoff( + &fpi.cwd + .file_name() + .map(|name| name.to_string_lossy().to_string()) + .unwrap_or_default(), + 25 + ), + truncate_and_trailoff( + &{ + format!( + "{}{}", + fpi.name, + if fpi.argv.len() >= 1 { + format!(" {}", (&fpi.argv[1..]).join(" ")) + } else { + "".to_string() + } + ) + }, + 25 + ) + ) + }) + .unwrap_or_else(|| "Terminal".to_string()) + } } impl Drop for Terminal { diff --git a/crates/terminal_view/src/terminal_view.rs b/crates/terminal_view/src/terminal_view.rs index 3821185ec02e9d1981162863efa1a1708c9e3f97..302186d8c769433a3a56583a663da3c7de3d85f9 100644 --- a/crates/terminal_view/src/terminal_view.rs +++ b/crates/terminal_view/src/terminal_view.rs @@ -30,7 +30,7 @@ use terminal::{ }, Event, Terminal, }; -use util::{truncate_and_trailoff, ResultExt}; +use util::ResultExt; use workspace::{ item::{Item, ItemEvent}, notifications::NotifyResultExt, @@ -547,38 +547,7 @@ impl Item for TerminalView { tab_theme: &theme::Tab, cx: &gpui::AppContext, ) -> ElementBox { - let title = self - .terminal() - .read(cx) - .foreground_process_info - .as_ref() - .map(|fpi| { - format!( - "{} — {}", - truncate_and_trailoff( - &fpi.cwd - .file_name() - .map(|name| name.to_string_lossy().to_string()) - .unwrap_or_default(), - 25 - ), - truncate_and_trailoff( - &{ - format!( - "{}{}", - fpi.name, - if fpi.argv.len() >= 1 { - format!(" {}", (&fpi.argv[1..]).join(" ")) - } else { - "".to_string() - } - ) - }, - 25 - ) - ) - }) - .unwrap_or_else(|| "Terminal".to_string()); + let title = self.terminal().read(cx).title(); Flex::row() .with_child( diff --git a/crates/workspace/src/terminal_button.rs b/crates/workspace/src/terminal_button.rs index 4789152327657ad9dc169d3801f9ee8658a0b735..0e21f51341c5622fe5d7c806fabfa9591b2c373e 100644 --- a/crates/workspace/src/terminal_button.rs +++ b/crates/workspace/src/terminal_button.rs @@ -120,10 +120,11 @@ impl TerminalButton { let local_terminal_handles = project.local_terminal_handles(); for local_terminal_handle in local_terminal_handles { - if let Some(_) = local_terminal_handle.upgrade(cx) { - // TODO: Obtain the actual terminal "name" and put it in the menu + if let Some(terminal) = local_terminal_handle.upgrade(cx) { + let title = terminal.read(cx).title(); + // TODO: Replace the `NewTerminal` action with an action that instead focuses the selected terminal - menu_options.push(ContextMenuItem::item("Terminal", NewTerminal)) + menu_options.push(ContextMenuItem::item(title, NewTerminal)) } } } From ca03d871a6bb9d625408f77ddb3c7c7f1bc1593c Mon Sep 17 00:00:00 2001 From: Joseph Lyons Date: Tue, 7 Mar 2023 15:09:06 -0500 Subject: [PATCH 04/15] Add a separator between the New Terminal button and existing terminal buttons --- crates/workspace/src/terminal_button.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/crates/workspace/src/terminal_button.rs b/crates/workspace/src/terminal_button.rs index 0e21f51341c5622fe5d7c806fabfa9591b2c373e..69914fe6ceb58a925d512ac7d28834cda99c6f9b 100644 --- a/crates/workspace/src/terminal_button.rs +++ b/crates/workspace/src/terminal_button.rs @@ -119,12 +119,17 @@ impl TerminalButton { let project = workspace.read(cx).project().read(cx); let local_terminal_handles = project.local_terminal_handles(); + if !local_terminal_handles.is_empty() { + menu_options.push(ContextMenuItem::Separator) + } + for local_terminal_handle in local_terminal_handles { if let Some(terminal) = local_terminal_handle.upgrade(cx) { - let title = terminal.read(cx).title(); - // TODO: Replace the `NewTerminal` action with an action that instead focuses the selected terminal - menu_options.push(ContextMenuItem::item(title, NewTerminal)) + menu_options.push(ContextMenuItem::item( + terminal.read(cx).title(), + NewTerminal, + )) } } } From baa9e271d518944d80f4a534b1b7943041a62298 Mon Sep 17 00:00:00 2001 From: Petros Amoiridis Date: Wed, 8 Mar 2023 11:00:30 +0200 Subject: [PATCH 05/15] Make pop up open on the side of the button --- crates/workspace/src/terminal_button.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/workspace/src/terminal_button.rs b/crates/workspace/src/terminal_button.rs index 69914fe6ceb58a925d512ac7d28834cda99c6f9b..64bf1fa0ebae1ec60c196bad9ce3a9ca82ab666d 100644 --- a/crates/workspace/src/terminal_button.rs +++ b/crates/workspace/src/terminal_button.rs @@ -135,7 +135,7 @@ impl TerminalButton { } self.popup_menu.update(cx, |menu, cx| { - menu.show(vec2f(0., 0.), AnchorCorner::TopLeft, menu_options, cx); + menu.show(vec2f(0., 0.), AnchorCorner::TopRight, menu_options, cx); }); } } From ad7e49ed06e39729cf52e2993177d9d82dcb43c8 Mon Sep 17 00:00:00 2001 From: Petros Amoiridis Date: Wed, 8 Mar 2023 19:47:57 +0200 Subject: [PATCH 06/15] Give focus to the selected terminal Co-Authored-By: Max Brunsfeld --- Cargo.lock | 1 + .../src/terminal_button.rs | 39 ++++++++++++++----- crates/terminal_view/src/terminal_view.rs | 5 ++- crates/workspace/Cargo.toml | 1 + crates/workspace/src/workspace.rs | 10 +---- crates/zed/src/zed.rs | 7 ++++ 6 files changed, 43 insertions(+), 20 deletions(-) rename crates/{workspace => terminal_view}/src/terminal_button.rs (78%) diff --git a/Cargo.lock b/Cargo.lock index 2e7997458ed00185d5d1b48f2943eb0e657a1b41..693d27666967891fba73dff467a5fbb2760af4ed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8299,6 +8299,7 @@ dependencies = [ "serde_json", "settings", "smallvec", + "terminal", "theme", "util", "uuid 1.2.2", diff --git a/crates/workspace/src/terminal_button.rs b/crates/terminal_view/src/terminal_button.rs similarity index 78% rename from crates/workspace/src/terminal_button.rs rename to crates/terminal_view/src/terminal_button.rs index 64bf1fa0ebae1ec60c196bad9ce3a9ca82ab666d..c63712d524f5a29a9781596ab7752be8e081259b 100644 --- a/crates/workspace/src/terminal_button.rs +++ b/crates/terminal_view/src/terminal_button.rs @@ -1,21 +1,26 @@ use context_menu::{ContextMenu, ContextMenuItem}; use gpui::{ - actions, elements::*, geometry::vector::vec2f, CursorStyle, Element, ElementBox, Entity, - MouseButton, MutableAppContext, RenderContext, View, ViewContext, ViewHandle, WeakViewHandle, + actions, elements::*, geometry::vector::vec2f, impl_internal_actions, CursorStyle, Element, + ElementBox, Entity, MouseButton, MutableAppContext, RenderContext, View, ViewContext, + ViewHandle, WeakModelHandle, WeakViewHandle, }; use settings::Settings; +use terminal::Terminal; +use workspace::{dock::FocusDock, item::ItemHandle, NewTerminal, StatusItemView, Workspace}; -use crate::{dock::FocusDock, item::ItemHandle, NewTerminal, StatusItemView, Workspace}; +use crate::TerminalView; -// #[derive(Clone, PartialEq)] -// pub struct DeployTerminalMenu { -// position: Vector2F, -// } +#[derive(Clone, PartialEq)] +pub struct FocusTerminal { + terminal_handle: WeakModelHandle, +} actions!(terminal, [DeployTerminalMenu]); +impl_internal_actions!(terminal, [FocusTerminal]); pub fn init(cx: &mut MutableAppContext) { cx.add_action(TerminalButton::deploy_terminal_menu); + cx.add_action(TerminalButton::focus_terminal); } pub struct TerminalButton { @@ -125,10 +130,11 @@ impl TerminalButton { for local_terminal_handle in local_terminal_handles { if let Some(terminal) = local_terminal_handle.upgrade(cx) { - // TODO: Replace the `NewTerminal` action with an action that instead focuses the selected terminal menu_options.push(ContextMenuItem::item( terminal.read(cx).title(), - NewTerminal, + FocusTerminal { + terminal_handle: local_terminal_handle.clone(), + }, )) } } @@ -138,6 +144,21 @@ impl TerminalButton { menu.show(vec2f(0., 0.), AnchorCorner::TopRight, menu_options, cx); }); } + + pub fn focus_terminal(&mut self, action: &FocusTerminal, cx: &mut ViewContext) { + if let Some(workspace) = self.workspace.upgrade(cx) { + workspace.update(cx, |workspace, cx| { + let terminal = workspace + .items_of_type::(cx) + .find(|terminal| { + terminal.read(cx).model().downgrade() == action.terminal_handle + }); + if let Some(terminal) = terminal { + workspace.activate_item(&terminal, cx); + } + }); + } + } } impl StatusItemView for TerminalButton { diff --git a/crates/terminal_view/src/terminal_view.rs b/crates/terminal_view/src/terminal_view.rs index 302186d8c769433a3a56583a663da3c7de3d85f9..634fab938db32268719d83de5c7109cfbedd0709 100644 --- a/crates/terminal_view/src/terminal_view.rs +++ b/crates/terminal_view/src/terminal_view.rs @@ -1,4 +1,5 @@ mod persistence; +pub mod terminal_button; pub mod terminal_element; use std::{ @@ -177,8 +178,8 @@ impl TerminalView { } } - pub fn handle(&self) -> ModelHandle { - self.terminal.clone() + pub fn model(&self) -> &ModelHandle { + &self.terminal } pub fn has_new_content(&self) -> bool { diff --git a/crates/workspace/Cargo.toml b/crates/workspace/Cargo.toml index fc069fe6c8d7d481873a22ebc6b679b1aba85632..cdc1a799da08e30fdfdde63523e63467b6c32a70 100644 --- a/crates/workspace/Cargo.toml +++ b/crates/workspace/Cargo.toml @@ -31,6 +31,7 @@ language = { path = "../language" } menu = { path = "../menu" } project = { path = "../project" } settings = { path = "../settings" } +terminal = { path = "../terminal" } theme = { path = "../theme" } util = { path = "../util" } async-recursion = "1.0.0" diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index db15839ad0eb208ebc5d8d3e52ac980d9f5bc9a5..c134c7f68c9e04a7dd03a8ed86fc5959632745c7 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -12,7 +12,6 @@ pub mod searchable; pub mod shared_screen; pub mod sidebar; mod status_bar; -pub mod terminal_button; mod toolbar; pub use smallvec; @@ -57,7 +56,6 @@ use std::{ sync::Arc, time::Duration, }; -use terminal_button::TerminalButton; use crate::{ notifications::simple_message_notification::{MessageNotification, OsOpen}, @@ -83,7 +81,7 @@ use status_bar::StatusBar; pub use status_bar::StatusItemView; use theme::{Theme, ThemeRegistry}; pub use toolbar::{ToolbarItemLocation, ToolbarItemView}; -use util::{ResultExt, StaffMode}; +use util::ResultExt; lazy_static! { static ref ZED_WINDOW_SIZE: Option = env::var("ZED_WINDOW_SIZE") @@ -185,7 +183,6 @@ impl_actions!(workspace, [ActivatePane]); pub fn init(app_state: Arc, cx: &mut MutableAppContext) { pane::init(cx); dock::init(cx); - terminal_button::init(cx); notifications::init(cx); cx.add_global_action(open); @@ -587,7 +584,6 @@ impl Workspace { let left_sidebar = cx.add_view(|_| Sidebar::new(SidebarSide::Left)); let right_sidebar = cx.add_view(|_| Sidebar::new(SidebarSide::Right)); let left_sidebar_buttons = cx.add_view(|cx| SidebarButtons::new(left_sidebar.clone(), cx)); - let toggle_terminal = cx.add_view(|cx| TerminalButton::new(handle.clone(), cx)); let toggle_dock = cx.add_view(|cx| ToggleDockButton::new(handle, cx)); let right_sidebar_buttons = cx.add_view(|cx| SidebarButtons::new(right_sidebar.clone(), cx)); @@ -596,10 +592,6 @@ impl Workspace { status_bar.add_left_item(left_sidebar_buttons, cx); status_bar.add_right_item(right_sidebar_buttons, cx); status_bar.add_right_item(toggle_dock, cx); - // TOOD: Remove this when things are done - if **cx.default_global::() { - status_bar.add_right_item(toggle_terminal, cx); - } status_bar }); diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 2fbac3613ed3a6242c41db026e7b46b0d7f1aa50..0d9d094f871fa6d6d53c2ff1c474f4e203bdab91 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -31,6 +31,7 @@ use serde::Deserialize; use serde_json::to_string_pretty; use settings::{keymap_file_json_schema, settings_file_json_schema, Settings}; use std::{borrow::Cow, env, path::Path, str, sync::Arc}; +use terminal_view::terminal_button::{self, TerminalButton}; use util::{channel::ReleaseChannel, paths, ResultExt, StaffMode}; use uuid::Uuid; pub use workspace; @@ -73,6 +74,7 @@ actions!( const MIN_FONT_SIZE: f32 = 6.0; pub fn init(app_state: &Arc, cx: &mut gpui::MutableAppContext) { + terminal_button::init(cx); cx.add_action(about); cx.add_global_action(|_: &Hide, cx: &mut gpui::MutableAppContext| { cx.platform().hide(); @@ -330,6 +332,7 @@ pub fn initialize_workspace( ) }); + let toggle_terminal = cx.add_view(|cx| TerminalButton::new(workspace_handle.clone(), cx)); let diagnostic_summary = cx.add_view(|cx| diagnostics::items::DiagnosticIndicator::new(workspace.project(), cx)); let activity_indicator = @@ -340,6 +343,10 @@ pub fn initialize_workspace( workspace.status_bar().update(cx, |status_bar, cx| { status_bar.add_left_item(diagnostic_summary, cx); status_bar.add_left_item(activity_indicator, cx); + // TODO: Remove this when things are done + if **cx.default_global::() { + status_bar.add_right_item(toggle_terminal, cx); + } status_bar.add_right_item(cursor_position, cx); status_bar.add_right_item(feedback_button, cx); }); From e2bdd261a1eb51c70405c9d1367fdbf9677c7021 Mon Sep 17 00:00:00 2001 From: Petros Amoiridis Date: Thu, 9 Mar 2023 16:53:34 +0200 Subject: [PATCH 07/15] Remove debugging statement --- crates/terminal_view/src/terminal_button.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/terminal_view/src/terminal_button.rs b/crates/terminal_view/src/terminal_button.rs index c63712d524f5a29a9781596ab7752be8e081259b..36517964421c18457da4a82921586ff53774ee12 100644 --- a/crates/terminal_view/src/terminal_button.rs +++ b/crates/terminal_view/src/terminal_button.rs @@ -79,7 +79,6 @@ impl View for TerminalButton { .on_click(MouseButton::Left, move |_, cx| { if has_terminals { cx.dispatch_action(DeployTerminalMenu); - println!("Yes, has_terminals {}", terminal_count); } else { cx.dispatch_action(FocusDock); }; From 5b7d0ee6fe5f7df429bb22edbe3504044f8e9068 Mon Sep 17 00:00:00 2001 From: Petros Amoiridis Date: Thu, 9 Mar 2023 16:54:47 +0200 Subject: [PATCH 08/15] Show button in a normal state --- crates/terminal_view/src/terminal_button.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/terminal_view/src/terminal_button.rs b/crates/terminal_view/src/terminal_button.rs index 36517964421c18457da4a82921586ff53774ee12..d006fc6edbd5bfcd882bae7403fc741aaa5ce382 100644 --- a/crates/terminal_view/src/terminal_button.rs +++ b/crates/terminal_view/src/terminal_button.rs @@ -57,7 +57,7 @@ impl View for TerminalButton { .status_bar .sidebar_buttons .item - .style_for(state, true); + .style_for(state, false); Svg::new("icons/terminal_12.svg") .with_color(style.icon_color) From d53c18cc57182b4869ed4cc9297a9a9a86702217 Mon Sep 17 00:00:00 2001 From: Petros Amoiridis Date: Thu, 9 Mar 2023 16:57:38 +0200 Subject: [PATCH 09/15] Open menu relative to the mouse cursor --- crates/terminal_view/src/terminal_button.rs | 23 +++++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/crates/terminal_view/src/terminal_button.rs b/crates/terminal_view/src/terminal_button.rs index d006fc6edbd5bfcd882bae7403fc741aaa5ce382..a669b5e1c90cd5969ee7a8ff6b3edf953d0c73cc 100644 --- a/crates/terminal_view/src/terminal_button.rs +++ b/crates/terminal_view/src/terminal_button.rs @@ -1,6 +1,6 @@ use context_menu::{ContextMenu, ContextMenuItem}; use gpui::{ - actions, elements::*, geometry::vector::vec2f, impl_internal_actions, CursorStyle, Element, + elements::*, geometry::vector::Vector2F, impl_internal_actions, CursorStyle, Element, ElementBox, Entity, MouseButton, MutableAppContext, RenderContext, View, ViewContext, ViewHandle, WeakModelHandle, WeakViewHandle, }; @@ -15,8 +15,12 @@ pub struct FocusTerminal { terminal_handle: WeakModelHandle, } -actions!(terminal, [DeployTerminalMenu]); -impl_internal_actions!(terminal, [FocusTerminal]); +#[derive(Clone, PartialEq)] +pub struct DeployTerminalMenu { + position: Vector2F, +} + +impl_internal_actions!(terminal, [FocusTerminal, DeployTerminalMenu]); pub fn init(cx: &mut MutableAppContext) { cx.add_action(TerminalButton::deploy_terminal_menu); @@ -44,7 +48,6 @@ impl View for TerminalButton { None => return Empty::new().boxed(), }; let has_terminals = !project.local_terminal_handles().is_empty(); - let terminal_count = project.local_terminal_handles().iter().count(); let theme = cx.global::().theme.clone(); Stack::new() @@ -76,9 +79,11 @@ impl View for TerminalButton { // let drop_index = dock_pane.read(cx.app).items_len() + 1; // handle_dropped_item(event, &dock_pane.downgrade(), drop_index, false, None, cx); }) - .on_click(MouseButton::Left, move |_, cx| { + .on_click(MouseButton::Left, move |e, cx| { if has_terminals { - cx.dispatch_action(DeployTerminalMenu); + cx.dispatch_action(DeployTerminalMenu { + position: e.region.upper_right(), + }); } else { cx.dispatch_action(FocusDock); }; @@ -106,7 +111,7 @@ impl TerminalButton { workspace: workspace.downgrade(), popup_menu: cx.add_view(|cx| { let mut menu = ContextMenu::new(cx); - menu.set_position_mode(OverlayPositionMode::Local); + menu.set_position_mode(OverlayPositionMode::Window); menu }), } @@ -114,7 +119,7 @@ impl TerminalButton { pub fn deploy_terminal_menu( &mut self, - _action: &DeployTerminalMenu, + action: &DeployTerminalMenu, cx: &mut ViewContext, ) { let mut menu_options = vec![ContextMenuItem::item("New Terminal", NewTerminal)]; @@ -140,7 +145,7 @@ impl TerminalButton { } self.popup_menu.update(cx, |menu, cx| { - menu.show(vec2f(0., 0.), AnchorCorner::TopRight, menu_options, cx); + menu.show(action.position, AnchorCorner::BottomRight, menu_options, cx); }); } From 6c68a3e709fb05eb53c0e34e58e4cdc29d495b76 Mon Sep 17 00:00:00 2001 From: Petros Amoiridis Date: Thu, 9 Mar 2023 20:02:40 +0200 Subject: [PATCH 10/15] Remove unneeded code --- crates/terminal_view/src/terminal_button.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/crates/terminal_view/src/terminal_button.rs b/crates/terminal_view/src/terminal_button.rs index a669b5e1c90cd5969ee7a8ff6b3edf953d0c73cc..1de46bfaf11d0b192c146879a9c581463df81ff0 100644 --- a/crates/terminal_view/src/terminal_button.rs +++ b/crates/terminal_view/src/terminal_button.rs @@ -73,12 +73,6 @@ impl View for TerminalButton { } }) .with_cursor_style(CursorStyle::PointingHand) - .on_up(MouseButton::Left, move |_, _| { - // TODO: Do we need this stuff? - // let dock_pane = workspace.read(cx.app).dock_pane(); - // let drop_index = dock_pane.read(cx.app).items_len() + 1; - // handle_dropped_item(event, &dock_pane.downgrade(), drop_index, false, None, cx); - }) .on_click(MouseButton::Left, move |e, cx| { if has_terminals { cx.dispatch_action(DeployTerminalMenu { From 8440a988505444ec90e00fe0b8d544b637cf37f2 Mon Sep 17 00:00:00 2001 From: Petros Amoiridis Date: Sat, 11 Mar 2023 16:45:28 +0200 Subject: [PATCH 11/15] Activate terminal button when a terminal is focus This is code I brought over from https://github.com/zed-industries/zed/pull/2267 by @mikayla-maki after fixing the conflicts. Co-Authored-By: Mikayla Maki --- crates/terminal_view/src/terminal_button.rs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/crates/terminal_view/src/terminal_button.rs b/crates/terminal_view/src/terminal_button.rs index 1de46bfaf11d0b192c146879a9c581463df81ff0..7dac256f0c73ae3c1b2f5e2be788eeeb24ac9631 100644 --- a/crates/terminal_view/src/terminal_button.rs +++ b/crates/terminal_view/src/terminal_button.rs @@ -47,6 +47,13 @@ impl View for TerminalButton { Some(workspace) => workspace.read(cx).project().read(cx), None => return Empty::new().boxed(), }; + + let focused_view = cx.focused_view_id(cx.window_id()); + // FIXME: Don't hardcode "Terminal" in here + let active = focused_view + .map(|view| cx.view_ui_name(cx.window_id(), view) == Some("Terminal")) + .unwrap_or(false); + let has_terminals = !project.local_terminal_handles().is_empty(); let theme = cx.global::().theme.clone(); @@ -60,7 +67,7 @@ impl View for TerminalButton { .status_bar .sidebar_buttons .item - .style_for(state, false); + .style_for(state, active); Svg::new("icons/terminal_12.svg") .with_color(style.icon_color) @@ -79,7 +86,9 @@ impl View for TerminalButton { position: e.region.upper_right(), }); } else { - cx.dispatch_action(FocusDock); + if !active { + cx.dispatch_action(FocusDock); + } }; }) .with_tooltip::( @@ -160,5 +169,7 @@ impl TerminalButton { } impl StatusItemView for TerminalButton { - fn set_active_pane_item(&mut self, _: Option<&dyn ItemHandle>, _: &mut ViewContext) {} + fn set_active_pane_item(&mut self, _: Option<&dyn ItemHandle>, cx: &mut ViewContext) { + cx.notify(); + } } From 432aeeac56f714f3f27300533bd7f17eea8f42f3 Mon Sep 17 00:00:00 2001 From: Petros Amoiridis Date: Sat, 11 Mar 2023 17:22:21 +0200 Subject: [PATCH 12/15] Remove comment I know @JosephTLyons you had added that, but I am not entirely sure this is how the view should be called. Let's discuss this further if you fill strong about it. --- crates/terminal_view/src/terminal_button.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/terminal_view/src/terminal_button.rs b/crates/terminal_view/src/terminal_button.rs index 7dac256f0c73ae3c1b2f5e2be788eeeb24ac9631..d3b5d627e373260b242463b4d8463568a773b919 100644 --- a/crates/terminal_view/src/terminal_button.rs +++ b/crates/terminal_view/src/terminal_button.rs @@ -105,7 +105,6 @@ impl View for TerminalButton { } } -// TODO: Rename this to `DeployTerminalButton` impl TerminalButton { pub fn new(workspace: ViewHandle, cx: &mut ViewContext) -> Self { // When terminal moves, redraw so that the icon and toggle status matches. From 29f0078084aa34f182f66e0ec97811fb75b6144f Mon Sep 17 00:00:00 2001 From: Petros Amoiridis Date: Sat, 11 Mar 2023 17:40:47 +0200 Subject: [PATCH 13/15] Show tooltip for Give Feedback icon --- crates/feedback/src/deploy_feedback_button.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/crates/feedback/src/deploy_feedback_button.rs b/crates/feedback/src/deploy_feedback_button.rs index 222c542eed4dba57e31a1b61d1099b4a72f02e24..52d1ab9d92d45817fd83c016efc3939489b71842 100644 --- a/crates/feedback/src/deploy_feedback_button.rs +++ b/crates/feedback/src/deploy_feedback_button.rs @@ -25,10 +25,10 @@ impl View for DeployFeedbackButton { fn render(&mut self, cx: &mut RenderContext<'_, Self>) -> ElementBox { let active = self.active; + let theme = cx.global::().theme.clone(); Stack::new() .with_child( - MouseEventHandler::::new(0, cx, |state, cx| { - let theme = &cx.global::().theme; + MouseEventHandler::::new(0, cx, |state, _| { let style = &theme .workspace .status_bar @@ -54,6 +54,13 @@ impl View for DeployFeedbackButton { cx.dispatch_action(GiveFeedback) } }) + .with_tooltip::( + 0, + "Give Feedback".into(), + Some(Box::new(GiveFeedback)), + theme.tooltip.clone(), + cx, + ) .boxed(), ) .boxed() From 46efb844af3e9d7ab59504514858aec803f52b32 Mon Sep 17 00:00:00 2001 From: Joseph Lyons Date: Sat, 11 Mar 2023 16:27:00 -0500 Subject: [PATCH 14/15] Remove hardcoding of "Terminal" string --- crates/terminal_view/src/terminal_button.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/terminal_view/src/terminal_button.rs b/crates/terminal_view/src/terminal_button.rs index d3b5d627e373260b242463b4d8463568a773b919..c3efb43fb4f2e3e2ac9340257997052147187c77 100644 --- a/crates/terminal_view/src/terminal_button.rs +++ b/crates/terminal_view/src/terminal_button.rs @@ -49,9 +49,8 @@ impl View for TerminalButton { }; let focused_view = cx.focused_view_id(cx.window_id()); - // FIXME: Don't hardcode "Terminal" in here let active = focused_view - .map(|view| cx.view_ui_name(cx.window_id(), view) == Some("Terminal")) + .map(|view| cx.view_ui_name(cx.window_id(), view) == Some(TerminalView::ui_name())) .unwrap_or(false); let has_terminals = !project.local_terminal_handles().is_empty(); From 726c8eb43f6ba0a58aac182fa311e0bce7fda49b Mon Sep 17 00:00:00 2001 From: Petros Amoiridis Date: Mon, 13 Mar 2023 11:42:40 +0200 Subject: [PATCH 15/15] Use type_id to determine what has the focus Co-Authored-By: Antonio Scandurra --- crates/gpui/src/app.rs | 6 ++++++ crates/terminal_view/src/terminal_button.rs | 5 ++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 7fc1de83fdc69257e8a0384b41db839dcfc21d13..51208f3930a1fd722f5186015fd72a2f94ee483c 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -2757,6 +2757,12 @@ impl AppContext { Some(self.views.get(&(window_id, view_id))?.ui_name()) } + pub fn view_type_id(&self, window_id: usize, view_id: usize) -> Option { + self.views + .get(&(window_id, view_id)) + .map(|view| view.as_any().type_id()) + } + pub fn background(&self) -> &Arc { &self.background } diff --git a/crates/terminal_view/src/terminal_button.rs b/crates/terminal_view/src/terminal_button.rs index c3efb43fb4f2e3e2ac9340257997052147187c77..c4f4c0571f10e3d6a772c7bd7518d8e787b718ab 100644 --- a/crates/terminal_view/src/terminal_button.rs +++ b/crates/terminal_view/src/terminal_button.rs @@ -5,6 +5,7 @@ use gpui::{ ViewHandle, WeakModelHandle, WeakViewHandle, }; use settings::Settings; +use std::any::TypeId; use terminal::Terminal; use workspace::{dock::FocusDock, item::ItemHandle, NewTerminal, StatusItemView, Workspace}; @@ -50,7 +51,9 @@ impl View for TerminalButton { let focused_view = cx.focused_view_id(cx.window_id()); let active = focused_view - .map(|view| cx.view_ui_name(cx.window_id(), view) == Some(TerminalView::ui_name())) + .map(|view_id| { + cx.view_type_id(cx.window_id(), view_id) == Some(TypeId::of::()) + }) .unwrap_or(false); let has_terminals = !project.local_terminal_handles().is_empty();