tab: Add setting to hide the close button entirely (#23880)

Closes #23744

Release Notes:

- Changed the `always_show_close_button` key to `show_close_button` and
introduced a new `hidden` value, that allows never displaying the close
button.

---------

Co-authored-by: Peter Tripp <peter@zed.dev>
Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
Co-authored-by: smit <0xtimsb@gmail.com>
This commit is contained in:
Morgan Metz 2025-03-02 19:01:48 -08:00 committed by GitHub
parent ae6d350334
commit 0a4ff2f475
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 149 additions and 28 deletions

View file

@ -648,11 +648,19 @@
// Show git status colors in the editor tabs. // Show git status colors in the editor tabs.
"git_status": false, "git_status": false,
// Position of the close button on the editor tabs. // Position of the close button on the editor tabs.
// One of: ["right", "left", "hidden"]
"close_position": "right", "close_position": "right",
// Whether to show the file icon for a tab. // Whether to show the file icon for a tab.
"file_icons": false, "file_icons": false,
// Whether to always show the close button on tabs. // Controls the appearance behavior of the tab's close button.
"always_show_close_button": false, //
// 1. Show it just upon hovering the tab. (default)
// "hover"
// 2. Show it persistently.
// "always"
// 3. Never show it, even if hovering it.
// "hidden"
"show_close_button": "hover",
// What to do after closing the current tab. // What to do after closing the current tab.
// //
// 1. Activate the tab that was open previously (default) // 1. Activate the tab that was open previously (default)

View file

@ -72,7 +72,7 @@ pub fn migrate_edit_prediction_provider_settings(text: &str) -> Result<Option<St
migrate( migrate(
&text, &text,
&[( &[(
SETTINGS_REPLACE_NESTED_KEY, SETTINGS_NESTED_KEY_VALUE_PATTERN,
replace_edit_prediction_provider_setting, replace_edit_prediction_provider_setting,
)], )],
&EDIT_PREDICTION_SETTINGS_MIGRATION_QUERY, &EDIT_PREDICTION_SETTINGS_MIGRATION_QUERY,
@ -571,9 +571,17 @@ pub static ACTION_ARGUMENT_SNAKE_CASE_REPLACE: LazyLock<HashMap<&str, &str>> =
const SETTINGS_MIGRATION_PATTERNS: MigrationPatterns = &[ const SETTINGS_MIGRATION_PATTERNS: MigrationPatterns = &[
(SETTINGS_STRING_REPLACE_QUERY, replace_setting_name), (SETTINGS_STRING_REPLACE_QUERY, replace_setting_name),
( (
SETTINGS_REPLACE_NESTED_KEY, SETTINGS_NESTED_KEY_VALUE_PATTERN,
replace_edit_prediction_provider_setting, replace_edit_prediction_provider_setting,
), ),
(
SETTINGS_NESTED_KEY_VALUE_PATTERN,
replace_tab_close_button_setting_key,
),
(
SETTINGS_NESTED_KEY_VALUE_PATTERN,
replace_tab_close_button_setting_value,
),
( (
SETTINGS_REPLACE_IN_LANGUAGES_QUERY, SETTINGS_REPLACE_IN_LANGUAGES_QUERY,
replace_setting_in_languages, replace_setting_in_languages,
@ -594,7 +602,7 @@ static SETTINGS_MIGRATION_QUERY: LazyLock<Query> = LazyLock::new(|| {
static EDIT_PREDICTION_SETTINGS_MIGRATION_QUERY: LazyLock<Query> = LazyLock::new(|| { static EDIT_PREDICTION_SETTINGS_MIGRATION_QUERY: LazyLock<Query> = LazyLock::new(|| {
Query::new( Query::new(
&tree_sitter_json::LANGUAGE.into(), &tree_sitter_json::LANGUAGE.into(),
SETTINGS_REPLACE_NESTED_KEY, SETTINGS_NESTED_KEY_VALUE_PATTERN,
) )
.unwrap() .unwrap()
}); });
@ -639,14 +647,14 @@ pub static SETTINGS_STRING_REPLACE: LazyLock<HashMap<&'static str, &'static str>
]) ])
}); });
const SETTINGS_REPLACE_NESTED_KEY: &str = r#" const SETTINGS_NESTED_KEY_VALUE_PATTERN: &str = r#"
(object (object
(pair (pair
key: (string (string_content) @parent_key) key: (string (string_content) @parent_key)
value: (object value: (object
(pair (pair
key: (string (string_content) @setting_name) key: (string (string_content) @setting_name)
value: (_) @value value: (_) @setting_value
) )
) )
) )
@ -679,6 +687,73 @@ fn replace_edit_prediction_provider_setting(
None None
} }
fn replace_tab_close_button_setting_key(
contents: &str,
mat: &QueryMatch,
query: &Query,
) -> Option<(Range<usize>, String)> {
let parent_object_capture_ix = query.capture_index_for_name("parent_key")?;
let parent_object_range = mat
.nodes_for_capture_index(parent_object_capture_ix)
.next()?
.byte_range();
let parent_object_name = contents.get(parent_object_range.clone())?;
let setting_name_ix = query.capture_index_for_name("setting_name")?;
let setting_range = mat
.nodes_for_capture_index(setting_name_ix)
.next()?
.byte_range();
let setting_name = contents.get(setting_range.clone())?;
if parent_object_name == "tabs" && setting_name == "always_show_close_button" {
return Some((setting_range, "show_close_button".into()));
}
None
}
fn replace_tab_close_button_setting_value(
contents: &str,
mat: &QueryMatch,
query: &Query,
) -> Option<(Range<usize>, String)> {
let parent_object_capture_ix = query.capture_index_for_name("parent_key")?;
let parent_object_range = mat
.nodes_for_capture_index(parent_object_capture_ix)
.next()?
.byte_range();
let parent_object_name = contents.get(parent_object_range.clone())?;
let setting_name_ix = query.capture_index_for_name("setting_name")?;
let setting_name_range = mat
.nodes_for_capture_index(setting_name_ix)
.next()?
.byte_range();
let setting_name = contents.get(setting_name_range.clone())?;
let setting_value_ix = query.capture_index_for_name("setting_value")?;
let setting_value_range = mat
.nodes_for_capture_index(setting_value_ix)
.next()?
.byte_range();
let setting_value = contents.get(setting_value_range.clone())?;
if parent_object_name == "tabs" && setting_name == "always_show_close_button" {
match setting_value {
"true" => {
return Some((setting_value_range, "\"always\"".to_string()));
}
"false" => {
return Some((setting_value_range, "\"hover\"".to_string()));
}
_ => {}
}
}
None
}
const SETTINGS_REPLACE_IN_LANGUAGES_QUERY: &str = r#" const SETTINGS_REPLACE_IN_LANGUAGES_QUERY: &str = r#"
(object (object
(pair (pair

View file

@ -42,7 +42,7 @@ pub struct ItemSettings {
pub activate_on_close: ActivateOnClose, pub activate_on_close: ActivateOnClose,
pub file_icons: bool, pub file_icons: bool,
pub show_diagnostics: ShowDiagnostics, pub show_diagnostics: ShowDiagnostics,
pub always_show_close_button: bool, pub show_close_button: ShowCloseButton,
} }
#[derive(Deserialize)] #[derive(Deserialize)]
@ -60,6 +60,15 @@ pub enum ClosePosition {
Right, Right,
} }
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "lowercase")]
pub enum ShowCloseButton {
Always,
#[default]
Hover,
Hidden,
}
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] #[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
pub enum ShowDiagnostics { pub enum ShowDiagnostics {
@ -104,7 +113,7 @@ pub struct ItemSettingsContent {
/// Whether to always show the close button on tabs. /// Whether to always show the close button on tabs.
/// ///
/// Default: false /// Default: false
always_show_close_button: Option<bool>, show_close_button: Option<ShowCloseButton>,
} }
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] #[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]

View file

@ -1,7 +1,7 @@
use crate::{ use crate::{
item::{ item::{
ActivateOnClose, ClosePosition, Item, ItemHandle, ItemSettings, PreviewTabsSettings, ActivateOnClose, ClosePosition, Item, ItemHandle, ItemSettings, PreviewTabsSettings,
ShowDiagnostics, TabContentParams, TabTooltipContent, WeakItemHandle, ShowCloseButton, ShowDiagnostics, TabContentParams, TabTooltipContent, WeakItemHandle,
}, },
move_item, move_item,
notifications::NotifyResultExt, notifications::NotifyResultExt,
@ -2269,7 +2269,7 @@ impl Pane {
let settings = ItemSettings::get_global(cx); let settings = ItemSettings::get_global(cx);
let close_side = &settings.close_position; let close_side = &settings.close_position;
let always_show_close_button = settings.always_show_close_button; let show_close_button = &settings.show_close_button;
let indicator = render_item_indicator(item.boxed_clone(), cx); let indicator = render_item_indicator(item.boxed_clone(), cx);
let item_id = item.item_id(); let item_id = item.item_id();
let is_first_item = ix == 0; let is_first_item = ix == 0;
@ -2373,18 +2373,21 @@ impl Pane {
close_pinned: false, close_pinned: false,
}; };
end_slot_tooltip_text = "Close Tab"; end_slot_tooltip_text = "Close Tab";
IconButton::new("close tab", IconName::Close) match show_close_button {
.when(!always_show_close_button, |button| { ShowCloseButton::Always => IconButton::new("close tab", IconName::Close),
button.visible_on_hover("") ShowCloseButton::Hover => {
}) IconButton::new("close tab", IconName::Close).visible_on_hover("")
.shape(IconButtonShape::Square) }
.icon_color(Color::Muted) ShowCloseButton::Hidden => return this,
.size(ButtonSize::None) }
.icon_size(IconSize::XSmall) .shape(IconButtonShape::Square)
.on_click(cx.listener(move |pane, _, window, cx| { .icon_color(Color::Muted)
pane.close_item_by_id(item_id, SaveIntent::Close, window, cx) .size(ButtonSize::None)
.detach_and_log_err(cx); .icon_size(IconSize::XSmall)
})) .on_click(cx.listener(move |pane, _, window, cx| {
pane.close_item_by_id(item_id, SaveIntent::Close, window, cx)
.detach_and_log_err(cx);
}))
} }
.map(|this| { .map(|this| {
if is_active { if is_active {

View file

@ -784,7 +784,7 @@ List of `string` values
"file_icons": false, "file_icons": false,
"git_status": false, "git_status": false,
"activate_on_close": "history", "activate_on_close": "history",
"always_show_close_button": false "show_close_button": "hover"
}, },
``` ```
@ -856,11 +856,37 @@ List of `string` values
} }
``` ```
### Always show the close button ### Show close button
- Description: Whether to always show the close button on tabs. - Description: Controls the appearance behavior of the tab's close button.
- Setting: `always_show_close_button` - Setting: `show_close_button`
- Default: `false` - Default: `hover`
**Options**
1. Show it just upon hovering the tab:
```json
{
"show_close_button": "hover"
}
```
2. Show it persistently:
```json
{
"show_close_button": "always"
}
```
3. Never show it, even if hovering it:
```json
{
"show_close_button": "hidden"
}
```
## Editor Toolbar ## Editor Toolbar