diff --git a/crates/story/src/tabs_story.rs b/crates/story/src/tabs_story.rs
index 2a364b32..a7046fd4 100644
--- a/crates/story/src/tabs_story.rs
+++ b/crates/story/src/tabs_story.rs
@@ -100,7 +100,7 @@ impl Render for TabsStory {
)
.child(
section("Tabs", cx).child(
- TabBar::new()
+ TabBar::new("tabs")
.w_full()
.with_size(self.size)
.selected_index(self.active_tab_ix)
@@ -123,12 +123,14 @@ impl Render for TabsStory {
.icon(IconName::ArrowRight),
),
)
- .child(Tab::new("tab-account", "Account"))
- .child(Tab::new("tab-profile", "Profile").disabled(true))
- .child(Tab::new("tab-documents", "Documents"))
- .child(Tab::new("tab-mail", "Mail"))
- .child(Tab::new("tab-appearance", "Appearance"))
- .child(Tab::new("tab-settings", "Settings"))
+ .child(Tab::new("Account"))
+ .child(Tab::new("Profile").disabled(true))
+ .child(Tab::new("Documents"))
+ .child(Tab::new("Mail"))
+ .child(Tab::new("Appearance"))
+ .child(Tab::new("Settings"))
+ .child(Tab::new("About"))
+ .child(Tab::new("License"))
.suffix(
h_flex()
.mx_1()
@@ -144,7 +146,7 @@ impl Render for TabsStory {
)
.child(
section("Pill Tabs", cx).child(
- TabBar::new()
+ TabBar::new("pill")
.w_full()
.pill()
.with_size(self.size)
@@ -152,17 +154,19 @@ impl Render for TabsStory {
.on_click(cx.listener(|this, ix: &usize, window, cx| {
this.set_active_tab(*ix, window, cx);
}))
- .child(Tab::new("tab-account", "Account"))
- .child(Tab::new("tab-profile", "Profile").disabled(true))
- .child(Tab::new("tab-documents", "Documents"))
- .child(Tab::new("tab-mail", "Mail"))
- .child(Tab::new("tab-appearance", "Appearance"))
- .child(Tab::new("tab-settings", "Settings")),
+ .child(Tab::new("Account"))
+ .child(Tab::new("Profile").disabled(true))
+ .child(Tab::new("Documents"))
+ .child(Tab::new("Mail"))
+ .child(Tab::new("Appearance"))
+ .child(Tab::new("Settings"))
+ .child(Tab::new("About"))
+ .child(Tab::new("License")),
),
)
.child(
section("Segmented Tabs", cx).child(
- TabBar::new()
+ TabBar::new("segmented")
.w_full()
.segmented()
.with_size(self.size)
@@ -177,13 +181,17 @@ impl Render for TabsStory {
"Mail",
"Appearance",
"Settings",
+ "About",
+ "License",
]),
),
)
.child(
section("Underline Tabs", cx).child(
- TabBar::new()
+ TabBar::new("underline")
.w_full()
+ .px_2()
+ .mx_3()
.underline()
.with_size(self.size)
.selected_index(self.active_tab_ix)
@@ -195,7 +203,9 @@ impl Render for TabsStory {
.child("Documents")
.child("Mail")
.child("Appearance")
- .child("Settings"),
+ .child("Settings")
+ .child("About")
+ .child("License"),
),
)
}
diff --git a/crates/ui/src/dock/tab_panel.rs b/crates/ui/src/dock/tab_panel.rs
index a56c4cfd..6c24a4c1 100644
--- a/crates/ui/src/dock/tab_panel.rs
+++ b/crates/ui/src/dock/tab_panel.rs
@@ -650,7 +650,7 @@ impl TabPanel {
let tabs_count = self.panels.len();
- TabBar::new()
+ TabBar::new("tab-bar")
.mt(-px(1.))
.track_scroll(self.tab_bar_scroll_handle.clone())
.when(
@@ -673,9 +673,6 @@ impl TabPanel {
)
},
)
- .on_click(cx.listener(move |view, ix: &usize, window, cx| {
- view.set_active_ix(*ix, window, cx);
- }))
.children(self.panels.iter().enumerate().filter_map(|(ix, panel)| {
let mut active = state.active_panel.as_ref() == Some(panel);
let disabled = self.collapsed;
@@ -690,12 +687,16 @@ impl TabPanel {
}
Some(
- Tab::new(("tab", ix), panel.title(window, cx))
+ Tab::new("")
+ .child(panel.title(window, cx))
.py_2()
.selected(active)
.disabled(disabled)
.when(!disabled, |this| {
- this.when(state.draggable, |this| {
+ this.on_click(cx.listener(move |view, _, window, cx| {
+ view.set_active_ix(ix, window, cx);
+ }))
+ .when(state.draggable, |this| {
this.on_drag(
DragPanel::new(panel.clone(), view.clone()),
|drag, _, _, cx| {
diff --git a/crates/ui/src/tab/tab.rs b/crates/ui/src/tab/tab.rs
index 366aad18..6e1a0217 100644
--- a/crates/ui/src/tab/tab.rs
+++ b/crates/ui/src/tab/tab.rs
@@ -4,8 +4,8 @@ use crate::{ActiveTheme, Selectable, Sizable, Size, StyledExt};
use gpui::prelude::FluentBuilder as _;
use gpui::{
div, px, AnyElement, App, ClickEvent, Div, Edges, ElementId, Hsla, InteractiveElement,
- IntoElement, ParentElement as _, Pixels, RenderOnce, SharedString, Stateful,
- StatefulInteractiveElement, Styled, Window,
+ IntoElement, ParentElement, Pixels, RenderOnce, SharedString, StatefulInteractiveElement,
+ Styled, Window,
};
#[derive(Debug, Clone, Default, Copy, PartialEq, Eq, Hash)]
@@ -45,6 +45,23 @@ impl Default for TabStyle {
}
impl TabVariant {
+ fn height(&self, size: Size) -> Pixels {
+ match size {
+ Size::Small | Size::XSmall => match self {
+ TabVariant::Tab => px(24.),
+ TabVariant::Pill => px(24.),
+ TabVariant::Segmented => px(24.),
+ TabVariant::Underline => px(30.),
+ },
+ _ => match self {
+ TabVariant::Tab => px(30.),
+ TabVariant::Pill => px(31.),
+ TabVariant::Segmented => px(30.),
+ TabVariant::Underline => px(36.),
+ },
+ }
+ }
+
fn inner_height(&self, size: Size) -> Pixels {
match size {
Size::Small | Size::XSmall => match self {
@@ -57,7 +74,7 @@ impl TabVariant {
TabVariant::Tab => px(30.),
TabVariant::Pill => px(31.),
TabVariant::Segmented => px(30.),
- TabVariant::Underline => px(25.),
+ TabVariant::Underline => px(24.),
},
}
}
@@ -122,7 +139,8 @@ impl TabVariant {
},
_ => match self {
TabVariant::Underline => Edges {
- bottom: px(4.),
+ top: px(5.),
+ bottom: px(3.),
..Default::default()
},
_ => Edges::all(px(0.)),
@@ -233,7 +251,7 @@ impl TabVariant {
..Default::default()
},
TabVariant::Pill => TabStyle {
- fg: cx.theme().accent_foreground,
+ fg: cx.theme().primary,
bg: cx.theme().transparent,
borders: Edges::all(px(1.)),
border_color: cx.theme().primary,
@@ -322,10 +340,11 @@ impl TabVariant {
#[derive(IntoElement)]
pub struct Tab {
id: ElementId,
- base: Stateful
,
- label: AnyElement,
+ base: Div,
+ label: SharedString,
prefix: Option
,
suffix: Option,
+ children: Vec,
variant: TabVariant,
size: Size,
disabled: bool,
@@ -336,31 +355,30 @@ pub struct Tab {
impl From<&'static str> for Tab {
fn from(label: &'static str) -> Self {
let label = SharedString::from(label);
- Self::new(label.clone(), label)
+ Self::new(label)
}
}
impl From for Tab {
fn from(label: String) -> Self {
let label = SharedString::from(label);
- Self::new(label.clone(), label)
+ Self::new(label)
}
}
impl From for Tab {
fn from(label: SharedString) -> Self {
- let label = SharedString::from(label);
- Self::new(label.clone(), label)
+ Self::new(label)
}
}
impl Tab {
- pub fn new(id: impl Into, label: impl IntoElement) -> Self {
- let id: ElementId = id.into();
+ pub fn new(label: impl Into) -> Self {
Self {
- id: id.clone(),
- base: div().id(id).gap_1(),
- label: label.into_any_element(),
+ id: ElementId::Integer(0),
+ base: div().gap_1(),
+ label: label.into(),
+ children: Vec::new(),
disabled: false,
selected: false,
prefix: None,
@@ -371,6 +389,12 @@ impl Tab {
}
}
+ /// Set id to the tab.
+ pub fn id(mut self, id: impl Into) -> Self {
+ self.id = id.into();
+ self
+ }
+
/// Set Tab Variant.
pub fn variant(mut self, variant: TabVariant) -> Self {
self.variant = variant;
@@ -423,6 +447,12 @@ impl Tab {
}
}
+impl ParentElement for Tab {
+ fn extend(&mut self, elements: impl IntoIterator- ) {
+ self.children.extend(elements);
+ }
+}
+
impl Selectable for Tab {
fn element_id(&self) -> &ElementId {
&self.id
@@ -470,13 +500,11 @@ impl RenderOnce for Tab {
let inner_paddings = self.variant.inner_paddings(self.size);
let inner_margins = self.variant.inner_margins(self.size);
let inner_height = self.variant.inner_height(self.size);
-
- let height = match self.size {
- Size::Small | Size::XSmall => px(24.),
- _ => px(31.),
- };
+ let height = self.variant.height(self.size);
+ let has_label = !self.label.is_empty();
self.base
+ .id(self.id)
.flex()
.items_center()
.flex_shrink_0()
@@ -516,7 +544,8 @@ impl RenderOnce for Tab {
.margins(inner_margins)
.text_ellipsis()
.flex_shrink_0()
- .child(self.label)
+ .when(has_label, |this| this.child(self.label))
+ .when(!has_label, |this| this.children(self.children))
.bg(tab_style.inner_bg)
.rounded(tab_style.inner_radius)
.hover(|this| {
diff --git a/crates/ui/src/tab/tab_bar.rs b/crates/ui/src/tab/tab_bar.rs
index e6aee863..40c582e3 100644
--- a/crates/ui/src/tab/tab_bar.rs
+++ b/crates/ui/src/tab/tab_bar.rs
@@ -3,8 +3,8 @@ use std::sync::Arc;
use crate::{h_flex, ActiveTheme, Selectable, Sizable, Size, StyledExt};
use gpui::prelude::FluentBuilder as _;
use gpui::{
- div, rems, AbsoluteLength, AnyElement, App, Div, Edges, IntoElement, ParentElement, RenderOnce,
- ScrollHandle, StatefulInteractiveElement as _, Styled, Window,
+ div, AnyElement, App, Div, Edges, ElementId, IntoElement, ParentElement, RenderOnce,
+ ScrollHandle, Stateful, StatefulInteractiveElement as _, Styled, Window,
};
use gpui::{px, InteractiveElement};
use smallvec::SmallVec;
@@ -13,12 +13,12 @@ use super::{Tab, TabVariant};
#[derive(IntoElement)]
pub struct TabBar {
- base: Div,
- scroll_handle: ScrollHandle,
+ base: Stateful
,
+ scroll_handle: Option
,
prefix: Option,
suffix: Option,
children: SmallVec<[Tab; 2]>,
- last_empty_space: Option,
+ last_empty_space: AnyElement,
selected_index: Option,
variant: TabVariant,
size: Size,
@@ -27,16 +27,16 @@ pub struct TabBar {
impl TabBar {
/// Create a new TabBar.
- pub fn new() -> Self {
+ pub fn new(id: impl Into) -> Self {
Self {
- base: div().px(px(-1.)),
+ base: div().id(id).px(px(-1.)),
children: SmallVec::new(),
- scroll_handle: ScrollHandle::new(),
+ scroll_handle: None,
prefix: None,
suffix: None,
variant: TabVariant::default(),
size: Size::default(),
- last_empty_space: None,
+ last_empty_space: div().w_3().into_any_element(),
selected_index: None,
on_click: None,
}
@@ -68,7 +68,7 @@ impl TabBar {
/// Track the scroll of the TabBar
pub fn track_scroll(mut self, scroll_handle: ScrollHandle) -> Self {
- self.scroll_handle = scroll_handle;
+ self.scroll_handle = Some(scroll_handle);
self
}
@@ -105,7 +105,7 @@ impl TabBar {
/// Set the last empty space element of the TabBar
pub fn last_empty_space(mut self, last_empty_space: impl IntoElement) -> Self {
- self.last_empty_space = Some(last_empty_space.into_any_element());
+ self.last_empty_space = last_empty_space.into_any_element();
self
}
@@ -139,31 +139,32 @@ impl RenderOnce for TabBar {
};
let (bg, paddings, gap) = match self.variant {
TabVariant::Tab => {
- let padding = Edges::all(AbsoluteLength::Pixels(px(0.)));
+ let padding = Edges::all(px(0.));
(cx.theme().tab_bar, padding, px(0.))
}
TabVariant::Pill => {
- let padding = Edges::all(AbsoluteLength::Rems(rems(0.25)));
+ let padding = Edges::all(px(0.));
(cx.theme().transparent, padding, default_gap)
}
TabVariant::Segmented => {
- let padding = Edges::all(AbsoluteLength::Rems(rems(0.25)));
+ let padding = Edges::all(px(4.));
(cx.theme().accent, padding, default_gap / 2.)
}
TabVariant::Underline => {
- let padding = Edges::all(AbsoluteLength::Pixels(px(0.)));
+ let padding = Edges::all(px(0.));
(cx.theme().transparent, padding, default_gap / 2.)
}
};
self.base
.group("tab-bar")
+ .w_full()
.relative()
.flex()
.flex_none()
+ .flex_nowrap()
.items_center()
.bg(bg)
- .paddings(paddings)
.text_color(cx.theme().tab_foreground)
.when(
self.variant == TabVariant::Underline || self.variant == TabVariant::Tab,
@@ -172,6 +173,7 @@ impl RenderOnce for TabBar {
div()
.id("border-b")
.absolute()
+ .left_0()
.bottom_0()
.size_full()
.border_b_1()
@@ -187,16 +189,20 @@ impl RenderOnce for TabBar {
.child(
h_flex()
.id("tabs")
- .flex_grow()
+ .flex_1()
.overflow_x_scroll()
- .track_scroll(&self.scroll_handle)
+ .when_some(self.scroll_handle, |this, scroll_handle| {
+ this.track_scroll(&scroll_handle)
+ })
.gap(gap)
+ .paddings(paddings)
.children(
self.children
.into_iter()
.enumerate()
.map(move |(ix, child)| {
child
+ .id(ix)
.variant(self.variant)
.with_size(self.size)
.when_some(self.selected_index, |this, selected_ix| {
@@ -209,7 +215,7 @@ impl RenderOnce for TabBar {
})
}),
)
- .children(self.last_empty_space),
+ .child(self.last_empty_space),
)
.when_some(self.suffix, |this, suffix| this.child(suffix))
}