improv(segmented_button): nav bar, tab, and segmented control theme improvements

This commit is contained in:
Michael Aaron Murphy 2025-08-12 17:15:18 +02:00
parent 562e885872
commit 8badf73383
No known key found for this signature in database
GPG key ID: B2732D4240C9212C
8 changed files with 279 additions and 256 deletions

View file

@ -5,7 +5,7 @@
use crate::widget::segmented_button::{Appearance, ItemAppearance, StyleSheet};
use crate::{theme::Theme, widget::segmented_button::ItemStatusAppearance};
use cosmic_theme::{Component, Container};
use iced::Border;
use iced_core::{Background, border::Radius};
use palette::WithAlpha;
@ -16,6 +16,8 @@ pub enum SegmentedButton {
TabBar,
/// A widget for multiple choice selection.
Control,
/// Navigation bar style
NavBar,
/// Or implement any custom theme of your liking.
Custom(Box<dyn Fn(&Theme) -> Appearance>),
}
@ -25,85 +27,54 @@ impl StyleSheet for Theme {
#[allow(clippy::too_many_lines)]
fn horizontal(&self, style: &Self::Style) -> Appearance {
let container = &self.current_container();
let cosmic = self.cosmic();
let container = self.current_container();
match style {
SegmentedButton::TabBar => {
let cosmic = self.cosmic();
let active = horizontal::tab_bar_active(cosmic);
let hc = cosmic.is_high_contrast;
let (border_end, border_start, border_top) = if hc {
(
Some((1., container.component.border.into())),
Some((1., container.component.border.into())),
Some((1., container.component.border.into())),
)
} else {
(None, None, None)
};
Appearance {
border_radius: cosmic.corner_radii.radius_0.into(),
inactive: ItemStatusAppearance {
background: None,
first: ItemAppearance {
border_radius: cosmic.corner_radii.radius_0.into(),
border_bottom: Some((1.0, cosmic.accent.base.into())),
border_end,
border_start,
border_top,
},
middle: ItemAppearance {
border_radius: cosmic.corner_radii.radius_0.into(),
border_bottom: Some((1.0, cosmic.accent.base.into())),
border_end,
border_start,
border_top,
},
last: ItemAppearance {
border_radius: cosmic.corner_radii.radius_0.into(),
border_bottom: Some((1.0, cosmic.accent.base.into())),
border_end,
border_start,
border_top,
},
text_color: container.component.on.into(),
},
hover: hover(cosmic, &container.component, &active),
focus: focus(cosmic, container, &active),
active,
..Default::default()
}
}
SegmentedButton::Control => {
let cosmic = self.cosmic();
let active = horizontal::selection_active(cosmic, &container.component);
let rad_m = cosmic.corner_radii.radius_m;
let rad_xl = cosmic.corner_radii.radius_xl;
let rad_0 = cosmic.corner_radii.radius_0;
let active = horizontal::selection_active(cosmic, &container.component);
Appearance {
background: Some(Background::Color(container.small_widget.into())),
border_radius: rad_m.into(),
background: Some(Background::Color(container.component.base.into())),
border: Border {
radius: rad_xl.into(),
..Default::default()
},
inactive: ItemStatusAppearance {
background: None,
first: ItemAppearance {
border_radius: Radius::from([rad_m[0], rad_0[1], rad_0[2], rad_m[3]]),
..Default::default()
border: Border {
radius: Radius::from([rad_xl[0], rad_0[1], rad_0[2], rad_xl[3]]),
..Default::default()
},
},
middle: ItemAppearance {
border_radius: cosmic.corner_radii.radius_0.into(),
..Default::default()
border: Border {
radius: cosmic.corner_radii.radius_0.into(),
..Default::default()
},
},
last: ItemAppearance {
border_radius: Radius::from([rad_0[0], rad_m[1], rad_m[2], rad_0[3]]),
..Default::default()
border: Border {
radius: Radius::from([rad_0[0], rad_xl[1], rad_xl[2], rad_0[3]]),
..Default::default()
},
},
text_color: container.component.on.into(),
},
hover: hover(cosmic, &container.component, &active),
focus: focus(cosmic, container, &active),
hover: hover(cosmic, &active, 0.2),
active,
..Default::default()
}
}
SegmentedButton::NavBar => Appearance {
active_width: 0.0,
..horizontal::tab_bar(cosmic, container)
},
SegmentedButton::TabBar => horizontal::tab_bar(cosmic, container),
SegmentedButton::Custom(func) => func(self),
}
}
@ -111,84 +82,126 @@ impl StyleSheet for Theme {
#[allow(clippy::too_many_lines)]
fn vertical(&self, style: &Self::Style) -> Appearance {
let cosmic = self.cosmic();
let rad_m = cosmic.corner_radii.radius_m;
let rad_0 = cosmic.corner_radii.radius_0;
let container = self.current_container();
match style {
SegmentedButton::TabBar => {
let container = &self.cosmic().primary;
let active = vertical::tab_bar_active(cosmic);
Appearance {
border_radius: cosmic.corner_radii.radius_0.into(),
inactive: ItemStatusAppearance {
background: None,
text_color: container.component.on.into(),
..active
},
hover: hover(cosmic, &container.component, &active),
focus: focus(cosmic, container, &active),
active,
..Default::default()
}
}
SegmentedButton::Control => {
let container = self.current_container();
let rad_xl = cosmic.corner_radii.radius_xl;
let rad_0 = cosmic.corner_radii.radius_0;
let active = vertical::selection_active(cosmic, &container.component);
Appearance {
background: Some(Background::Color(container.small_widget.into())),
border_radius: rad_m.into(),
background: Some(Background::Color(container.component.base.into())),
border: Border {
radius: rad_xl.into(),
..Default::default()
},
inactive: ItemStatusAppearance {
background: None,
first: ItemAppearance {
border_radius: Radius::from([rad_m[0], rad_m[1], rad_0[0], rad_0[0]]),
..Default::default()
border: Border {
radius: Radius::from([rad_xl[0], rad_xl[1], rad_0[0], rad_0[0]]),
..Default::default()
},
},
middle: ItemAppearance {
border_radius: cosmic.corner_radii.radius_0.into(),
..Default::default()
border: Border {
radius: cosmic.corner_radii.radius_0.into(),
..Default::default()
},
},
last: ItemAppearance {
border_radius: Radius::from([rad_0[0], rad_0[1], rad_m[2], rad_m[3]]),
..Default::default()
border: Border {
radius: Radius::from([rad_0[0], rad_0[1], rad_xl[2], rad_xl[3]]),
..Default::default()
},
},
text_color: container.component.on.into(),
},
hover: hover(cosmic, &container.component, &active),
focus: focus(cosmic, container, &active),
hover: hover(cosmic, &active, 0.2),
active,
..Default::default()
}
}
SegmentedButton::NavBar => Appearance {
active_width: 0.0,
..vertical::tab_bar(cosmic, container)
},
SegmentedButton::TabBar => vertical::tab_bar(cosmic, container),
SegmentedButton::Custom(func) => func(self),
}
}
}
mod horizontal {
use super::Appearance;
use crate::widget::segmented_button::{ItemAppearance, ItemStatusAppearance};
use cosmic_theme::Component;
use cosmic_theme::{Component, Container};
use iced::Border;
use iced_core::{Background, border::Radius};
use palette::WithAlpha;
pub fn tab_bar(cosmic: &cosmic_theme::Theme, container: &Container) -> Appearance {
let active = tab_bar_active(cosmic);
let hc = cosmic.is_high_contrast;
let border = if hc {
Border {
color: container.component.border.into(),
radius: cosmic.corner_radii.radius_0.into(),
width: 1.0,
}
} else {
Border::default()
};
Appearance {
active_width: 4.0,
border: Border {
radius: cosmic.corner_radii.radius_0.into(),
..Default::default()
},
inactive: ItemStatusAppearance {
background: None,
first: ItemAppearance { border },
middle: ItemAppearance { border },
last: ItemAppearance { border },
text_color: container.component.on.into(),
},
hover: super::hover(cosmic, &active, 0.3),
active,
..Default::default()
}
}
pub fn selection_active(
cosmic: &cosmic_theme::Theme,
component: &Component,
) -> ItemStatusAppearance {
let rad_m = cosmic.corner_radii.radius_m;
let rad_xl = cosmic.corner_radii.radius_xl;
let rad_0 = cosmic.corner_radii.radius_0;
ItemStatusAppearance {
background: Some(Background::Color(component.selected_state_color().into())),
background: Some(Background::Color(
cosmic.palette.neutral_5.with_alpha(0.1).into(),
)),
first: ItemAppearance {
border_radius: Radius::from([rad_m[0], rad_0[1], rad_0[2], rad_m[3]]),
..Default::default()
border: Border {
radius: Radius::from([rad_xl[0], rad_0[1], rad_0[2], rad_xl[3]]),
..Default::default()
},
},
middle: ItemAppearance {
border_radius: cosmic.corner_radii.radius_0.into(),
..Default::default()
border: Border {
radius: cosmic.corner_radii.radius_0.into(),
..Default::default()
},
},
last: ItemAppearance {
border_radius: Radius::from([rad_0[0], rad_m[1], rad_m[2], rad_0[3]]),
..Default::default()
border: Border {
radius: Radius::from([rad_0[0], rad_xl[1], rad_xl[2], rad_0[3]]),
..Default::default()
},
},
text_color: cosmic.accent_text_color().into(),
}
@ -202,78 +215,86 @@ mod horizontal {
cosmic.palette.neutral_5.with_alpha(0.2).into(),
)),
first: ItemAppearance {
border_radius: Radius::from([rad_s[0], rad_s[1], rad_0[2], rad_0[3]]),
border_bottom: Some((4.0, cosmic.accent.base.into())),
..Default::default()
border: Border {
color: cosmic.accent.base.into(),
radius: Radius::from([rad_s[0], rad_s[1], rad_0[2], rad_0[3]]),
width: 0.0,
},
},
middle: ItemAppearance {
border_radius: Radius::from([rad_s[0], rad_s[1], rad_0[2], rad_0[3]]),
border_bottom: Some((4.0, cosmic.accent.base.into())),
..Default::default()
border: Border {
color: cosmic.accent.base.into(),
radius: Radius::from([rad_s[0], rad_s[1], rad_0[2], rad_0[3]]),
width: 0.0,
},
},
last: ItemAppearance {
border_radius: Radius::from([rad_s[0], rad_s[1], rad_0[2], rad_0[3]]),
border_bottom: Some((4.0, cosmic.accent.base.into())),
..Default::default()
border: Border {
color: cosmic.accent.base.into(),
radius: Radius::from([rad_s[0], rad_s[1], rad_0[2], rad_0[3]]),
width: 0.0,
},
},
text_color: cosmic.accent_text_color().into(),
}
}
}
pub fn focus(
cosmic: &cosmic_theme::Theme,
container: &Container,
default: &ItemStatusAppearance,
) -> ItemStatusAppearance {
let color = container.small_widget;
ItemStatusAppearance {
background: Some(Background::Color(color.into())),
text_color: cosmic.accent_text_color().into(),
..*default
}
}
pub fn hover(
cosmic: &cosmic_theme::Theme,
component: &Component,
default: &ItemStatusAppearance,
) -> ItemStatusAppearance {
ItemStatusAppearance {
background: Some(Background::Color(component.hover.with_alpha(0.2).into())),
text_color: cosmic.accent_text_color().into(),
..*default
}
}
mod vertical {
use super::Appearance;
use crate::widget::segmented_button::{ItemAppearance, ItemStatusAppearance};
use cosmic_theme::Component;
use cosmic_theme::{Component, Container};
use iced::Border;
use iced_core::{Background, border::Radius};
use palette::WithAlpha;
pub fn tab_bar(cosmic: &cosmic_theme::Theme, container: &Container) -> Appearance {
let active = tab_bar_active(cosmic);
Appearance {
active_width: 4.0,
border: Border {
radius: cosmic.corner_radii.radius_0.into(),
..Default::default()
},
inactive: ItemStatusAppearance {
background: None,
text_color: container.component.on.into(),
..active
},
hover: super::hover(cosmic, &active, 0.3),
active,
..Default::default()
}
}
pub fn selection_active(
cosmic: &cosmic_theme::Theme,
component: &Component,
) -> ItemStatusAppearance {
let rad_0 = cosmic.corner_radii.radius_0;
let rad_m = cosmic.corner_radii.radius_m;
let rad_xl = cosmic.corner_radii.radius_xl;
ItemStatusAppearance {
background: Some(Background::Color(
component.selected_state_color().with_alpha(0.3).into(),
cosmic.palette.neutral_5.with_alpha(0.1).into(),
)),
first: ItemAppearance {
border_radius: Radius::from([rad_m[0], rad_m[1], rad_0[2], rad_0[3]]),
..Default::default()
border: Border {
radius: Radius::from([rad_xl[0], rad_xl[1], rad_0[2], rad_0[3]]),
..Default::default()
},
},
middle: ItemAppearance {
border_radius: cosmic.corner_radii.radius_0.into(),
..Default::default()
border: Border {
radius: cosmic.corner_radii.radius_0.into(),
..Default::default()
},
},
last: ItemAppearance {
border_radius: Radius::from([rad_0[0], rad_0[1], rad_m[2], rad_m[3]]),
..Default::default()
border: Border {
radius: Radius::from([rad_0[0], rad_0[1], rad_xl[2], rad_xl[3]]),
..Default::default()
},
},
text_color: cosmic.accent_text_color().into(),
}
@ -285,18 +306,41 @@ mod vertical {
cosmic.palette.neutral_5.with_alpha(0.2).into(),
)),
first: ItemAppearance {
border_radius: cosmic.corner_radii.radius_m.into(),
..Default::default()
border: Border {
radius: cosmic.corner_radii.radius_m.into(),
width: 0.0,
..Default::default()
},
},
middle: ItemAppearance {
border_radius: cosmic.corner_radii.radius_m.into(),
..Default::default()
border: Border {
radius: cosmic.corner_radii.radius_m.into(),
width: 0.0,
..Default::default()
},
},
last: ItemAppearance {
border_radius: cosmic.corner_radii.radius_m.into(),
..Default::default()
border: Border {
radius: cosmic.corner_radii.radius_m.into(),
width: 0.0,
..Default::default()
},
},
text_color: cosmic.accent_text_color().into(),
}
}
}
pub fn hover(
cosmic: &cosmic_theme::Theme,
default: &ItemStatusAppearance,
alpha: f32,
) -> ItemStatusAppearance {
ItemStatusAppearance {
background: Some(Background::Color(
cosmic.palette.neutral_5.with_alpha(alpha).into(),
)),
text_color: cosmic.accent_text_color().into(),
..*default
}
}