Add hover-reveal close glyph to every regular tab row

This is the actual root cause of "vertical tabs not closeable" —
`render_tab_row` (the rich title+URL rows used in the TABS section)
never rendered a close glyph. Only `render_launcher_row` (favorites
and pinned) had one. So every regular tab in the sidebar was
permanently un-closeable from the sidebar UI; the only path to close
was Cmd+W or the menu.

Restructure render_tab_row from a flex_col (title above URL) to a
flex row: title + URL stack on the left under flex_1+min_w_0, close
glyph on the right with the same group_hover opacity-0 → 1 pattern
the launcher rows use, plus stop_propagation so the row's own
on_click doesn't re-select the just-closed tab.

The two text rows now use `truncate` instead of `overflow_hidden` so
long titles ellipsize cleanly inside the constrained width.
This commit is contained in:
2026-05-09 21:57:15 -04:00
parent 4a29e2d23c
commit 546c3f616d
+37 -5
View File
@@ -318,17 +318,21 @@ impl ElyShell {
cx: &mut Context<Self>, cx: &mut Context<Self>,
) -> AnyElement { ) -> AnyElement {
let tab_id = tab.id().clone(); let tab_id = tab.id().clone();
let close_tab_id = tab.id().clone();
let bg_color = if active { ACTIVE_NAV_BG } else { 0x00000000 }; let bg_color = if active { ACTIVE_NAV_BG } else { 0x00000000 };
let text_color = if active { colors::INK } else { colors::INK_2 }; let text_color = if active { colors::INK } else { colors::INK_2 };
let group_name = SharedString::from(format!("tab-{}", tab.id().as_str()));
let close_id = SharedString::from(format!("tab-close-{}", tab.id().as_str()));
div() div()
.id(SharedString::from(tab.id().as_str().to_string())) .id(SharedString::from(tab.id().as_str().to_string()))
.group(group_name.clone())
.rounded(px(spacing::RADIUS_NAV)) .rounded(px(spacing::RADIUS_NAV))
.px(px(10.0)) .px(px(10.0))
.py(px(7.0)) .py(px(7.0))
.gap(px(6.0)) .gap(px(8.0))
.flex() .flex()
.flex_col() .items_center()
.cursor_pointer() .cursor_pointer()
.hover(|style| style.bg(rgba(ACTIVE_NAV_BG))) .hover(|style| style.bg(rgba(ACTIVE_NAV_BG)))
.active(|style| style.opacity(0.82)) .active(|style| style.opacity(0.82))
@@ -337,24 +341,52 @@ impl ElyShell {
.on_click(cx.listener(move |shell, _, window, cx| { .on_click(cx.listener(move |shell, _, window, cx| {
shell.select_tab(&tab_id, window, cx); shell.select_tab(&tab_id, window, cx);
})) }))
.child(
div()
.flex_1()
.min_w_0()
.flex()
.flex_col()
.gap(px(2.0))
.child( .child(
div() div()
.text_size(px(13.0)) .text_size(px(13.0))
.font_weight(gpui::FontWeight(500.0)) .font_weight(gpui::FontWeight(500.0))
.text_color(rgb(text_color)) .text_color(rgb(text_color))
.overflow_hidden() .truncate()
.child(tab.title().to_string()), .child(tab.title().to_string()),
) )
.child( .child(
div() div()
.text_size(px(11.0)) .text_size(px(11.0))
.text_color(rgb(colors::INK_4)) .text_color(rgb(colors::INK_4))
.overflow_hidden() .truncate()
.child(tab.display_url()), .child(tab.display_url()),
),
)
.child(
div()
.id(close_id)
.size(px(16.0))
.rounded(px(4.0))
.flex_shrink_0()
.flex()
.items_center()
.justify_center()
.text_color(rgb(colors::INK_4))
.opacity(0.0)
.group_hover(group_name, |style| style.opacity(1.0))
.hover(|style| style.bg(rgba(0x281e1414)).text_color(rgb(colors::INK)))
.cursor_pointer()
.on_click(cx.listener(move |shell, _, window, cx| {
shell.select_tab(&close_tab_id, window, cx);
shell.close_active_tab(window, cx);
cx.stop_propagation();
}))
.child(IconName::Close),
) )
.into_any_element() .into_any_element()
} }
} }
fn profile_initial(name: &str) -> String { fn profile_initial(name: &str) -> String {