diff --git a/assets/settings/default.json b/assets/settings/default.json index 94d4854ff4..f041b362df 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -118,6 +118,19 @@ // Share your project when you are the first to join a channel "share_on_join": true }, + // The editor tab bar related settings + "tab_bar": { + // Where to show the tab bar in the editor. + // This setting can take three values: + // + // 1. Don't show the tab bar: + // "no" + // 2. Show tab bar at the top of the editor (default): + // "top" + // 3. Show tab bar at the bottom of the editor: + // "bottom" + "placement": "top" + }, // Toolbar related settings "toolbar": { // Whether to show breadcrumbs. diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 05582f4441..097d04dc5a 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -49,7 +49,7 @@ use copilot::Copilot; use debounced_delay::DebouncedDelay; pub use display_map::DisplayPoint; use display_map::*; -pub use editor_settings::EditorSettings; +pub use editor_settings::{EditorSettings, TabBar}; use element::LineWithInvisibles; pub use element::{ CursorLayout, EditorElement, HighlightedRange, HighlightedRangeLine, PointForPosition, @@ -387,6 +387,7 @@ pub struct Editor { hovered_cursors: HashMap>, pub show_local_selections: bool, mode: EditorMode, + tab_bar_settings: TabBar, show_breadcrumbs: bool, show_gutter: bool, show_wrap_guides: Option, @@ -1528,6 +1529,7 @@ impl Editor { blink_manager: blink_manager.clone(), show_local_selections: true, mode, + tab_bar_settings: EditorSettings::get_global(cx).tab_bar, show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs, show_gutter: mode == EditorMode::Full, show_wrap_guides: None, @@ -9534,6 +9536,7 @@ impl Editor { ); let editor_settings = EditorSettings::get_global(cx); self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin; + self.tab_bar_settings = editor_settings.tab_bar; self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs; cx.notify(); } diff --git a/crates/editor/src/editor_settings.rs b/crates/editor/src/editor_settings.rs index d3a122954f..8b093e6c83 100644 --- a/crates/editor/src/editor_settings.rs +++ b/crates/editor/src/editor_settings.rs @@ -1,6 +1,6 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::Settings; +use settings::{Settings, TabBarPlacement}; #[derive(Deserialize, Clone)] pub struct EditorSettings { @@ -10,6 +10,7 @@ pub struct EditorSettings { pub show_completion_documentation: bool, pub completion_documentation_secondary_query_debounce: u64, pub use_on_type_format: bool, + pub tab_bar: TabBar, pub toolbar: Toolbar, pub scrollbar: Scrollbar, pub gutter: Gutter, @@ -46,6 +47,11 @@ pub enum DoubleClickInMultibuffer { Open, } +#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +pub struct TabBar { + pub placement: TabBarPlacement, +} + #[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] pub struct Toolbar { pub breadcrumbs: bool, @@ -126,6 +132,8 @@ pub struct EditorSettingsContent { /// /// Default: true pub use_on_type_format: Option, + /// Tab bar related settings + pub tab_bar: Option, /// Toolbar related settings pub toolbar: Option, /// Scrollbar related settings @@ -162,6 +170,15 @@ pub struct EditorSettingsContent { pub double_click_in_multibuffer: Option, } +// Tab bar related settings +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +pub struct TabBarContent { + /// Where to place tab bar in the editor. + /// + /// Default: top + pub placement: Option, +} + // Toolbar related settings #[derive(Clone, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] pub struct ToolbarContent { diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index 944f63133d..3c3cd88630 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -18,7 +18,7 @@ use language::{ use project::repository::GitFileStatus; use project::{search::SearchQuery, FormatTrigger, Item as _, Project, ProjectPath}; use rpc::proto::{self, update_view, PeerId}; -use settings::Settings; +use settings::{Settings, TabBarPlacement}; use workspace::item::ItemSettings; use std::{ @@ -795,6 +795,10 @@ impl Item for Editor { self.pixel_position_of_newest_cursor } + fn tab_bar_placement(&self) -> TabBarPlacement { + self.tab_bar_settings.placement + } + fn breadcrumb_location(&self) -> ToolbarItemLocation { if self.show_breadcrumbs { ToolbarItemLocation::PrimaryLeft diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index 4febec6542..4b3561cc2c 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -1,6 +1,7 @@ mod keymap_file; mod settings_file; mod settings_store; +mod tab_bar; use rust_embed::RustEmbed; use std::{borrow::Cow, str}; @@ -9,6 +10,7 @@ use util::asset_str; pub use keymap_file::KeymapFile; pub use settings_file::*; pub use settings_store::{Settings, SettingsJsonSchemaParams, SettingsLocation, SettingsStore}; +pub use tab_bar::TabBarPlacement; #[derive(RustEmbed)] #[folder = "../../assets"] diff --git a/crates/settings/src/tab_bar.rs b/crates/settings/src/tab_bar.rs new file mode 100644 index 0000000000..9312d308fd --- /dev/null +++ b/crates/settings/src/tab_bar.rs @@ -0,0 +1,16 @@ +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +/// The tab bar placement in a pane. +/// +/// Default: top +#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +pub enum TabBarPlacement { + /// Don't show tab bar. + No, + /// Place tab bar on top of the pane. + Top, + /// Place tab bar at the bottom of the pane. + Bottom, +} diff --git a/crates/workspace/src/item.rs b/crates/workspace/src/item.rs index 8cbe042cbc..f15d1f4903 100644 --- a/crates/workspace/src/item.rs +++ b/crates/workspace/src/item.rs @@ -20,7 +20,7 @@ use gpui::{ use project::{Project, ProjectEntryId, ProjectPath}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::Settings; +use settings::{Settings, TabBarPlacement}; use smallvec::SmallVec; use std::{ any::{Any, TypeId}, @@ -198,6 +198,10 @@ pub trait Item: FocusableView + EventEmitter { None } + fn tab_bar_placement(&self) -> TabBarPlacement { + TabBarPlacement::Bottom + } + fn breadcrumb_location(&self) -> ToolbarItemLocation { ToolbarItemLocation::Hidden } @@ -293,6 +297,7 @@ pub trait ItemHandle: 'static + Send { callback: Box, ) -> gpui::Subscription; fn to_searchable_item_handle(&self, cx: &AppContext) -> Option>; + fn tab_bar_placement(&self, cx: &AppContext) -> TabBarPlacement; fn breadcrumb_location(&self, cx: &AppContext) -> ToolbarItemLocation; fn breadcrumbs(&self, theme: &Theme, cx: &AppContext) -> Option>; fn serialized_item_kind(&self) -> Option<&'static str>; @@ -644,6 +649,10 @@ impl ItemHandle for View { self.read(cx).as_searchable(self) } + fn tab_bar_placement(&self, cx: &AppContext) -> TabBarPlacement { + self.read(cx).tab_bar_placement() + } + fn breadcrumb_location(&self, cx: &AppContext) -> ToolbarItemLocation { self.read(cx).breadcrumb_location() } diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index ff6063b4e8..c0a73c191a 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -17,7 +17,7 @@ use gpui::{ use parking_lot::Mutex; use project::{Project, ProjectEntryId, ProjectPath}; use serde::Deserialize; -use settings::Settings; +use settings::{Settings, TabBarPlacement}; use std::{ any::Any, cmp, fmt, mem, @@ -1486,6 +1486,13 @@ impl Pane { }) } + fn need_tab_bar_at(&self, placement: TabBarPlacement, cx: &mut ViewContext<'_, Pane>) -> bool { + let Some(item) = self.active_item() else { + return false; + }; + item.tab_bar_placement(cx) == placement + } + fn render_tab_bar(&mut self, cx: &mut ViewContext<'_, Pane>) -> impl IntoElement { TabBar::new("tab_bar") .track_scroll(self.tab_bar_scroll_handle.clone()) @@ -1857,7 +1864,7 @@ impl Render for Pane { } }), ) - .when(self.active_item().is_some(), |pane| { + .when(self.need_tab_bar_at(TabBarPlacement::Top, cx), |pane| { pane.child(self.render_tab_bar(cx)) }) .child({ @@ -1926,6 +1933,9 @@ impl Render for Pane { }), ) }) + .when(self.need_tab_bar_at(TabBarPlacement::Bottom, cx), |pane| { + pane.child(self.render_tab_bar(cx)) + }) .on_mouse_down( MouseButton::Navigate(NavigationDirection::Back), cx.listener(|pane, _, cx| {