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.
"git_status": false,
// Position of the close button on the editor tabs.
// One of: ["right", "left", "hidden"]
"close_position": "right",
// Whether to show the file icon for a tab.
"file_icons": false,
// Whether to always show the close button on tabs.
"always_show_close_button": false,
// Controls the appearance behavior of the tab's close button.
//
// 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.
//
// 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(
&text,
&[(
SETTINGS_REPLACE_NESTED_KEY,
SETTINGS_NESTED_KEY_VALUE_PATTERN,
replace_edit_prediction_provider_setting,
)],
&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 = &[
(SETTINGS_STRING_REPLACE_QUERY, replace_setting_name),
(
SETTINGS_REPLACE_NESTED_KEY,
SETTINGS_NESTED_KEY_VALUE_PATTERN,
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,
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(|| {
Query::new(
&tree_sitter_json::LANGUAGE.into(),
SETTINGS_REPLACE_NESTED_KEY,
SETTINGS_NESTED_KEY_VALUE_PATTERN,
)
.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
(pair
key: (string (string_content) @parent_key)
value: (object
(pair
key: (string (string_content) @setting_name)
value: (_) @value
value: (_) @setting_value
)
)
)
@ -679,6 +687,73 @@ fn replace_edit_prediction_provider_setting(
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#"
(object
(pair

View file

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

View file

@ -1,7 +1,7 @@
use crate::{
item::{
ActivateOnClose, ClosePosition, Item, ItemHandle, ItemSettings, PreviewTabsSettings,
ShowDiagnostics, TabContentParams, TabTooltipContent, WeakItemHandle,
ShowCloseButton, ShowDiagnostics, TabContentParams, TabTooltipContent, WeakItemHandle,
},
move_item,
notifications::NotifyResultExt,
@ -2269,7 +2269,7 @@ impl Pane {
let settings = ItemSettings::get_global(cx);
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 item_id = item.item_id();
let is_first_item = ix == 0;
@ -2373,18 +2373,21 @@ impl Pane {
close_pinned: false,
};
end_slot_tooltip_text = "Close Tab";
IconButton::new("close tab", IconName::Close)
.when(!always_show_close_button, |button| {
button.visible_on_hover("")
})
.shape(IconButtonShape::Square)
.icon_color(Color::Muted)
.size(ButtonSize::None)
.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);
}))
match show_close_button {
ShowCloseButton::Always => IconButton::new("close tab", IconName::Close),
ShowCloseButton::Hover => {
IconButton::new("close tab", IconName::Close).visible_on_hover("")
}
ShowCloseButton::Hidden => return this,
}
.shape(IconButtonShape::Square)
.icon_color(Color::Muted)
.size(ButtonSize::None)
.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| {
if is_active {

View file

@ -784,7 +784,7 @@ List of `string` values
"file_icons": false,
"git_status": false,
"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.
- Setting: `always_show_close_button`
- Default: `false`
- Description: Controls the appearance behavior of the tab's close button.
- Setting: `show_close_button`
- 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