diff --git a/app/src/tab.rs b/app/src/tab.rs index b2bc28b2..150b251d 100644 --- a/app/src/tab.rs +++ b/app/src/tab.rs @@ -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; @@ -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 { @@ -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) diff --git a/app/src/workspace/view/vertical_tabs.rs b/app/src/workspace/view/vertical_tabs.rs index 8b52af66..611a9342 100644 --- a/app/src/workspace/view/vertical_tabs.rs +++ b/app/src/workspace/view/vertical_tabs.rs @@ -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.; @@ -289,7 +294,11 @@ fn pane_row_background( theme: &WarpTheme, ) -> Option { 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 @@ -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, + 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, @@ -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, _, _| {