diff --git a/assets/settings/default.json b/assets/settings/default.json index 9b8dce10d8..5cf5f59f76 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -128,9 +128,12 @@ // 4. Save when idle for a certain amount of time: // "autosave": { "after_delay": {"milliseconds": 500} }, "autosave": "off", - // Color tab titles based on the git status of the buffer. + // Settings related to the editor's tabs "tabs": { - "git_status": false + // Show git status colors in the editor tabs. + "git_status": false, + // Position of the close button on the editor tabs. + "close_position": "right" }, // Whether or not to remove any trailing whitespace from lines of a buffer // before saving it. diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 191fe6332a..b7a7408bef 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -723,12 +723,12 @@ pub struct Scrollbar { pub thumb: ContainerStyle, pub width: f32, pub min_height_factor: f32, - pub git: FileGitDiffColors, + pub git: BufferGitDiffColors, pub selections: Color, } #[derive(Clone, Deserialize, Default, JsonSchema)] -pub struct FileGitDiffColors { +pub struct BufferGitDiffColors { pub inserted: Color, pub modified: Color, pub deleted: Color, diff --git a/crates/workspace/src/item.rs b/crates/workspace/src/item.rs index c4b9d8e879..460698efb8 100644 --- a/crates/workspace/src/item.rs +++ b/crates/workspace/src/item.rs @@ -33,11 +33,30 @@ use theme::Theme; #[derive(Deserialize)] pub struct ItemSettings { pub git_status: bool, + pub close_position: ClosePosition, +} + +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "lowercase")] +pub enum ClosePosition { + Left, + #[default] + Right, +} + +impl ClosePosition { + pub fn right(&self) -> bool { + match self { + ClosePosition::Left => false, + ClosePosition::Right => true, + } + } } #[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] pub struct ItemSettingsContent { git_status: Option, + close_position: Option, } impl Setting for ItemSettings { diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 373c30fb52..f5b96fd421 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -1370,81 +1370,94 @@ impl Pane { container.border.left = false; } - Flex::row() - .with_child({ - let diameter = 7.0; - let icon_color = if item.has_conflict(cx) { - Some(tab_style.icon_conflict) - } else if item.is_dirty(cx) { - Some(tab_style.icon_dirty) - } else { - None - }; + let buffer_jewel_element = { + let diameter = 7.0; + let icon_color = if item.has_conflict(cx) { + Some(tab_style.icon_conflict) + } else if item.is_dirty(cx) { + Some(tab_style.icon_dirty) + } else { + None + }; - Canvas::new(move |scene, bounds, _, _, _| { - if let Some(color) = icon_color { - let square = RectF::new(bounds.origin(), vec2f(diameter, diameter)); - scene.push_quad(Quad { - bounds: square, - background: Some(color), - border: Default::default(), - corner_radius: diameter / 2., - }); - } - }) - .constrained() - .with_width(diameter) - .with_height(diameter) - .aligned() + Canvas::new(move |scene, bounds, _, _, _| { + if let Some(color) = icon_color { + let square = RectF::new(bounds.origin(), vec2f(diameter, diameter)); + scene.push_quad(Quad { + bounds: square, + background: Some(color), + border: Default::default(), + corner_radius: diameter / 2., + }); + } }) - .with_child(title.aligned().contained().with_style(ContainerStyle { - margin: Margin { - left: tab_style.spacing, - right: tab_style.spacing, - ..Default::default() - }, + .constrained() + .with_width(diameter) + .with_height(diameter) + .aligned() + }; + + let title_element = title.aligned().contained().with_style(ContainerStyle { + margin: Margin { + left: tab_style.spacing, + right: tab_style.spacing, ..Default::default() - })) - .with_child( - if hovered { - let item_id = item.id(); - enum TabCloseButton {} - let icon = Svg::new("icons/x_mark_8.svg"); - MouseEventHandler::::new(item_id, cx, |mouse_state, _| { - if mouse_state.hovered() { - icon.with_color(tab_style.icon_close_active) - } else { - icon.with_color(tab_style.icon_close) - } - }) - .with_padding(Padding::uniform(4.)) - .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, { - let pane = pane.clone(); - move |_, _, cx| { - let pane = pane.clone(); - cx.window_context().defer(move |cx| { - if let Some(pane) = pane.upgrade(cx) { - pane.update(cx, |pane, cx| { - pane.close_item_by_id(item_id, cx).detach_and_log_err(cx); - }); - } + }, + ..Default::default() + }); + + let close_element = if hovered { + let item_id = item.id(); + enum TabCloseButton {} + let icon = Svg::new("icons/x_mark_8.svg"); + MouseEventHandler::::new(item_id, cx, |mouse_state, _| { + if mouse_state.hovered() { + icon.with_color(tab_style.icon_close_active) + } else { + icon.with_color(tab_style.icon_close) + } + }) + .with_padding(Padding::uniform(4.)) + .with_cursor_style(CursorStyle::PointingHand) + .on_click(MouseButton::Left, { + let pane = pane.clone(); + move |_, _, cx| { + let pane = pane.clone(); + cx.window_context().defer(move |cx| { + if let Some(pane) = pane.upgrade(cx) { + pane.update(cx, |pane, cx| { + pane.close_item_by_id(item_id, cx).detach_and_log_err(cx); }); } - }) - .into_any_named("close-tab-icon") - .constrained() - } else { - Empty::new().constrained() + }); } - .with_width(tab_style.close_icon_width) - .aligned(), - ) - .contained() - .with_style(container) + }) + .into_any_named("close-tab-icon") .constrained() - .with_height(tab_style.height) - .into_any() + } else { + Empty::new().constrained() + } + .with_width(tab_style.close_icon_width) + .aligned(); + + let close_right = settings::get::(cx).close_position.right(); + + if close_right { + Flex::row() + .with_child(buffer_jewel_element) + .with_child(title_element) + .with_child(close_element) + } else { + Flex::row() + .with_child(close_element) + .with_child(title_element) + .with_child(buffer_jewel_element) + } + .contained() + .with_style(container) + .constrained() + .with_height(tab_style.height) + .into_any() } pub fn render_tab_bar_button<