Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 44 additions & 27 deletions app/src/tab.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,20 @@ pub fn uses_vertical_tabs(ctx: &AppContext) -> bool {

const WARP_2_TAB_COLOR_OPACITY: Opacity = 25;
const WARP_2_HOVERED_TAB_COLOR_OPACITY: Opacity = 50;
/// Opacity applied to the colored fill of the active tab in the legacy
/// (non-`NewTabStyling`) tab bar. Higher than the hover/inactive opacities
/// so the active colored tab is clearly distinguishable.
const WARP_2_ACTIVE_TAB_COLOR_OPACITY: Opacity = 80;
/// Opacity used for the active tab's colored background in the
/// `NewTabStyling` tab bar. Bumped relative to hover/inactive so the active
/// colored tab is clearly brighter than its neighbours.
const NEW_TAB_ACTIVE_COLOR_OPACITY: u8 = 85;
const NEW_TAB_HOVERED_COLOR_OPACITY: u8 = 40;
const NEW_TAB_INACTIVE_COLOR_OPACITY: u8 = 20;
/// Opacity (0..=100) for the saturated colored border drawn around the
/// active tab when the tab has a custom color. Used by both legacy and new
/// tab styling.
const ACTIVE_TAB_COLOR_BORDER_OPACITY: Opacity = 95;
const TAB_CLOSE_BUTTON_OPACITY: Opacity = 60;
const TAB_CLOSE_BUTTON_WIDTH: f32 = 20.0;
const MAX_TOOLTIP_LENGTH: usize = 80;
Expand Down Expand Up @@ -1195,26 +1209,25 @@ impl<'a> TabComponent<'a> {
let theme = self.appearance.theme();
let is_active = self.is_active_tab();

let custom_background_solid = self.styles.background.map(|fill| match fill {
ThemeFill::Solid(color) => color,
ThemeFill::VerticalGradient(gradient) => gradient.get_most_opaque(),
ThemeFill::HorizontalGradient(gradient) => gradient.get_most_opaque(),
});

let (background_color, border_fill) = if FeatureFlag::NewTabStyling.is_enabled() {
// If there is a custom tab background, we overlay it with varying opacities.
let bg = if let Some(custom_background) = self.styles.background {
// If there is a custom tab background, we overlay it with varying opacities so the
// active tab pops while inactive colored tabs remain a subtle tint.
let bg = if let Some(color) = custom_background_solid {
let base_opacity = if is_active {
60
NEW_TAB_ACTIVE_COLOR_OPACITY
} else if is_hovered {
40
NEW_TAB_HOVERED_COLOR_OPACITY
} else {
20
NEW_TAB_INACTIVE_COLOR_OPACITY
};
let opacity = (base_opacity as f32 * self.background_opacity as f32 / 100.) as u8;
match custom_background {
ThemeFill::Solid(color) => coloru_with_opacity(color, opacity).into(),
ThemeFill::VerticalGradient(gradient) => {
coloru_with_opacity(gradient.get_most_opaque(), opacity).into()
}
ThemeFill::HorizontalGradient(gradient) => {
coloru_with_opacity(gradient.get_most_opaque(), opacity).into()
}
}
coloru_with_opacity(color, opacity).into()
} else if is_active {
internal_colors::fg_overlay_2(theme).into()
} else if is_hovered {
Expand All @@ -1224,34 +1237,38 @@ impl<'a> TabComponent<'a> {
};

let border = if is_active {
internal_colors::fg_overlay_2(theme)
if let Some(color) = custom_background_solid {
ThemeFill::Solid(coloru_with_opacity(color, ACTIVE_TAB_COLOR_BORDER_OPACITY))
} else {
internal_colors::fg_overlay_2(theme)
}
} else {
internal_colors::fg_overlay_1(theme)
};

(bg, border)
} else {
let tab_opacity = if is_active || is_hovered {
let tab_opacity = if is_active {
WARP_2_ACTIVE_TAB_COLOR_OPACITY
} else if is_hovered {
WARP_2_HOVERED_TAB_COLOR_OPACITY
} else {
WARP_2_TAB_COLOR_OPACITY
};

let bg = if let Some(custom_background) = self.styles.background {
match custom_background {
ThemeFill::Solid(color) => coloru_with_opacity(color, tab_opacity).into(),
ThemeFill::VerticalGradient(gradient) => {
coloru_with_opacity(gradient.get_most_opaque(), tab_opacity).into()
}
ThemeFill::HorizontalGradient(gradient) => {
coloru_with_opacity(gradient.get_most_opaque(), tab_opacity).into()
}
}
let bg = if let Some(color) = custom_background_solid {
coloru_with_opacity(color, tab_opacity).into()
} else {
coloru_with_opacity(theme.surface_3().into(), tab_opacity).into()
};

let border = if is_active || is_hovered {
let border = if is_active {
if let Some(color) = custom_background_solid {
ThemeFill::Solid(coloru_with_opacity(color, ACTIVE_TAB_COLOR_BORDER_OPACITY))
} else {
internal_colors::fg_overlay_2(theme)
}
} else if is_hovered {
internal_colors::fg_overlay_2(theme)
} else {
internal_colors::fg_overlay_1(theme)
Expand Down
41 changes: 34 additions & 7 deletions app/src/workspace/view/vertical_tabs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,12 @@ const DETAIL_SIDECAR_CORNER_RADIUS: f32 = 4.;
/// so the row doesn't resize when badges are toggled.
const METADATA_ROW_HEIGHT: f32 = BADGE_ICON_SIZE + 2.;
const TAB_COLOR_OPACITY: Opacity = 15;
const TAB_COLOR_HOVER_OPACITY: Opacity = 50;
const TAB_COLOR_HOVER_OPACITY: Opacity = 35;
const TAB_COLOR_SELECTED_OPACITY: Opacity = 75;
/// Opacity applied to the tab color when it is used as the active row's
/// border. Boosts saturation so the colored border reads as a clear active
/// indicator on top of the same color used (more faintly) as the row fill.
const TAB_COLOR_ACTIVE_BORDER_OPACITY: Opacity = 90;

// Circular icon constants
const ICON_WITH_STATUS_GAP: f32 = 8.;
Expand Down Expand Up @@ -289,7 +294,11 @@ fn pane_row_background(
theme: &WarpTheme,
) -> Option<ThemeFill> {
if let Some(color) = pane_color {
let opacity = if is_selected || is_hovered {
// Distinct opacities for selected vs. hovered so the active colored
// tab is clearly brighter than a merely-hovered one.
let opacity = if is_selected {
TAB_COLOR_SELECTED_OPACITY
} else if is_hovered {
TAB_COLOR_HOVER_OPACITY
} else {
TAB_COLOR_OPACITY
Expand All @@ -304,6 +313,24 @@ fn pane_row_background(
}
}

/// Border fill for a vertical tab pane row. When the row is selected and
/// colored, render a saturated border in the same ANSI color so the active
/// colored tab stands out from inactive colored tabs.
fn pane_row_border_fill(
pane_color: Option<ThemeFill>,
is_selected: bool,
theme: &WarpTheme,
) -> ElementFill {
if !is_selected {
return ElementFill::None;
}
if let Some(color) = pane_color {
color.with_opacity(TAB_COLOR_ACTIVE_BORDER_OPACITY).into()
} else {
internal_colors::fg_overlay_3(theme).into()
}
}

fn render_pane_row_element(
props: PaneProps<'_>,
padding: Padding,
Expand Down Expand Up @@ -360,11 +387,11 @@ fn render_pane_row_element(
}

container
.with_border(Border::all(1.).with_border_fill(if is_selected {
internal_colors::fg_overlay_3(theme).into()
} else {
ElementFill::None
}))
.with_border(Border::all(1.).with_border_fill(pane_row_border_fill(
pane_color,
is_selected,
theme,
)))
.finish()
})
.on_click(move |ctx, _, _| {
Expand Down
Loading