diff --git a/cosmic-theme/src/model/derivation.rs b/cosmic-theme/src/model/derivation.rs index 7d857c31..994ee35a 100644 --- a/cosmic-theme/src/model/derivation.rs +++ b/cosmic-theme/src/model/derivation.rs @@ -15,18 +15,28 @@ pub struct Container { pub divider: Srgba, /// the color of text in the container pub on: Srgba, + /// the color of @small_widget_container + pub small_widget: Srgba, } impl Container { - pub(crate) fn new(component: Component, base: Srgba, on: Srgba) -> Self { + pub(crate) fn new( + component: Component, + base: Srgba, + on: Srgba, + mut small_widget: Srgba, + ) -> Self { let mut divider_c = on; divider_c.alpha = 0.2; + small_widget.alpha = 0.25; + Self { base, component, divider: over(divider_c, base), on, + small_widget, } } } diff --git a/cosmic-theme/src/model/theme.rs b/cosmic-theme/src/model/theme.rs index 62ed20a9..93c3bde3 100644 --- a/cosmic-theme/src/model/theme.rs +++ b/cosmic-theme/src/model/theme.rs @@ -5,7 +5,7 @@ use crate::{ DARK_PALETTE, LIGHT_PALETTE, NAME, }; use cosmic_config::{Config, CosmicConfigEntry}; -use palette::{IntoColor, Oklcha, Srgb, Srgba}; +use palette::{rgb::Rgb, IntoColor, Oklcha, Srgb, Srgba}; use serde::{Deserialize, Serialize}; use std::num::NonZeroUsize; @@ -182,13 +182,6 @@ impl Theme { self.warning.base } - #[must_use] - #[allow(clippy::doc_markdown)] - /// get @small_container_widget - pub fn small_container_widget(&self) -> Srgba { - self.palette.gray_2 - } - #[must_use] #[allow(clippy::doc_markdown)] /// get @small_widget_divider @@ -774,6 +767,11 @@ impl ThemeBuilder { let p_ref = palette.as_ref(); + let neutral_steps = steps( + neutral_tint.unwrap_or(Rgb::new(0.0, 0.0, 0.0)), + NonZeroUsize::new(100).unwrap(), + ); + let bg = if let Some(bg_color) = bg_color { bg_color } else { @@ -783,10 +781,14 @@ impl ThemeBuilder { let step_array = steps(bg, NonZeroUsize::new(100).unwrap()); let bg_index = color_index(bg, step_array.len()); - let mut component_hovered_overlay = p_ref.neutral_0; + let mut component_hovered_overlay = if bg_index < 91 { + p_ref.neutral_10 + } else { + p_ref.neutral_0 + }; component_hovered_overlay.alpha = 0.1; - let mut component_pressed_overlay = p_ref.neutral_0; + let mut component_pressed_overlay = component_hovered_overlay; component_pressed_overlay.alpha = 0.2; // Standard button background is neutral 7 with 25% opacity @@ -805,7 +807,6 @@ impl ThemeBuilder { let on_bg_component = get_text( color_index(bg_component, step_array.len()), &step_array, - is_dark, &p_ref.neutral_8, text_steps_array.as_ref(), ); @@ -831,10 +832,16 @@ impl ThemeBuilder { get_text( bg_index, &step_array, - is_dark, &p_ref.neutral_8, text_steps_array.as_ref(), ), + get_surface_color( + bg_index, + 5, + &neutral_steps, + bg_index <= 65, + &p_ref.neutral_6, + ), ), primary: { let container_bg = if let Some(primary_container_bg_color) = primary_container_bg { @@ -843,10 +850,20 @@ impl ThemeBuilder { get_surface_color(bg_index, 5, &step_array, is_dark, &p_ref.neutral_1) }; - let base_index = color_index(container_bg, step_array.len()); + let base_index: usize = color_index(container_bg, step_array.len()); let component_base = get_surface_color(base_index, 6, &step_array, is_dark, &p_ref.neutral_3); + component_hovered_overlay = if base_index < 91 { + p_ref.neutral_10 + } else { + p_ref.neutral_0 + }; + component_hovered_overlay.alpha = 0.1; + + component_pressed_overlay = component_hovered_overlay; + component_pressed_overlay.alpha = 0.2; + let container = Container::new( Component::component( component_base, @@ -854,7 +871,6 @@ impl ThemeBuilder { get_text( color_index(component_base, step_array.len()), &step_array, - is_dark, &p_ref.neutral_8, text_steps_array.as_ref(), ), @@ -867,10 +883,16 @@ impl ThemeBuilder { get_text( base_index, &step_array, - is_dark, &p_ref.neutral_8, text_steps_array.as_ref(), ), + get_surface_color( + base_index, + 5, + &neutral_steps, + base_index <= 65, + &p_ref.neutral_6, + ), ); container @@ -886,6 +908,16 @@ impl ThemeBuilder { let secondary_component = get_surface_color(base_index, 3, &step_array, is_dark, &p_ref.neutral_4); + component_hovered_overlay = if base_index < 91 { + p_ref.neutral_10 + } else { + p_ref.neutral_0 + }; + component_hovered_overlay.alpha = 0.1; + + component_pressed_overlay = component_hovered_overlay; + component_pressed_overlay.alpha = 0.2; + Container::new( Component::component( secondary_component, @@ -893,7 +925,6 @@ impl ThemeBuilder { get_text( color_index(secondary_component, step_array.len()), &step_array, - is_dark, &p_ref.neutral_8, text_steps_array.as_ref(), ), @@ -906,10 +937,16 @@ impl ThemeBuilder { get_text( base_index, &step_array, - is_dark, &p_ref.neutral_8, text_steps_array.as_ref(), ), + get_surface_color( + base_index, + 5, + &neutral_steps, + base_index <= 65, + &p_ref.neutral_6, + ), ) }, accent: Component::colored_component( diff --git a/cosmic-theme/src/steps.rs b/cosmic-theme/src/steps.rs index ef4d39ad..2c306e3c 100644 --- a/cosmic-theme/src/steps.rs +++ b/cosmic-theme/src/steps.rs @@ -43,16 +43,15 @@ pub fn get_surface_color( is_dark = is_dark || base_index < 91; - get_index(base_index, steps, step_array.len(), is_dark) - .and_then(|i| step_array.get(i).cloned()) - .unwrap_or_else(|| fallback.to_owned()) + *get_index(base_index, steps, step_array.len(), is_dark) + .and_then(|i| step_array.get(i)) + .unwrap_or(fallback) } /// get text color given a base background color pub fn get_text( base_index: usize, step_array: &Vec, - is_dark: bool, fallback: &Srgba, tint_array: Option<&Vec>, ) -> Srgba { @@ -63,16 +62,14 @@ pub fn get_text( } else { step_array }; - let Some(index) = get_index(base_index, 70, step_array.len(), is_dark) - .or_else(|| get_index(base_index, 50, step_array.len(), is_dark)) - else { - return fallback.to_owned(); - }; - step_array - .get(index) - .cloned() - .unwrap_or_else(|| fallback.to_owned()) + let is_dark = base_index < 60; + + let index = get_index(base_index, 70, step_array.len(), is_dark) + .or_else(|| get_index(base_index, 50, step_array.len(), is_dark)) + .unwrap_or_else(|| if is_dark { 99 } else { 0 }); + + *step_array.get(index).unwrap_or(fallback) } /// get the index into the steps array for a given color diff --git a/src/theme/style/button.rs b/src/theme/style/button.rs index b2e33689..d3fdcb88 100644 --- a/src/theme/style/button.rs +++ b/src/theme/style/button.rs @@ -64,8 +64,10 @@ pub fn appearance( let (background, text, icon) = color(style_component); appearance.background = Some(Background::Color(background)); - appearance.text_color = text; - appearance.icon_color = icon; + if !matches!(style, Button::Standard | Button::Text) { + appearance.text_color = text; + appearance.icon_color = icon; + } } Button::Icon | Button::IconVertical | Button::HeaderBar => { @@ -152,15 +154,14 @@ impl StyleSheet for crate::Theme { if let Button::Custom { active, .. } = style { return active(focused, self); } - let accent = self.cosmic().accent_color(); - appearance(self, focused, selected, style, |component| { + appearance(self, focused, selected, style, move |component| { let text_color = if matches!( style, Button::Icon | Button::IconVertical | Button::HeaderBar ) && selected { - Some(accent.into()) + Some(self.cosmic().accent_color().into()) } else if matches!(style, Button::HeaderBar) && !selected { let mut c = Color::from(component.on); c.a = 0.8; @@ -197,7 +198,6 @@ impl StyleSheet for crate::Theme { if let Button::Custom { hovered, .. } = style { return hovered(focused, self); } - let accent = self.cosmic().accent_button.hover; appearance( self, @@ -210,7 +210,7 @@ impl StyleSheet for crate::Theme { Button::Icon | Button::IconVertical | Button::HeaderBar ) && selected { - Some(accent.into()) + Some(self.cosmic().accent_color().into()) } else if matches!(style, Button::HeaderBar) && !selected { let mut c = Color::from(component.on); c.a = 0.8; @@ -228,7 +228,6 @@ impl StyleSheet for crate::Theme { if let Button::Custom { pressed, .. } = style { return pressed(focused, self); } - let accent = self.cosmic().accent_button.pressed; appearance(self, focused, selected, style, |component| { let text_color = if matches!( @@ -236,7 +235,7 @@ impl StyleSheet for crate::Theme { Button::Icon | Button::IconVertical | Button::HeaderBar ) && selected { - Some(accent.into()) + Some(self.cosmic().accent_color().into()) } else if matches!(style, Button::HeaderBar) && !selected { let mut c = Color::from(component.on); c.a = 0.8; diff --git a/src/theme/style/iced.rs b/src/theme/style/iced.rs index 31fd74f9..af6ee0ef 100644 --- a/src/theme/style/iced.rs +++ b/src/theme/style/iced.rs @@ -436,6 +436,7 @@ impl container::StyleSheet for Theme { #[allow(clippy::too_many_lines)] fn appearance(&self, style: &Self::Style) -> container::Appearance { let cosmic = self.cosmic(); + match style { Container::Transparent => container::Appearance::default(), @@ -459,13 +460,7 @@ impl container::StyleSheet for Theme { }, Container::List => { - // TODO: The primary component has the wrong color on the light theme. - let component = if cosmic.is_dark { - &self.current_container().component - } else { - &cosmic.background.component - }; - + let component = &self.current_container().component; container::Appearance { icon_color: Some(component.on.into()), text_color: Some(component.on.into()), @@ -479,7 +474,7 @@ impl container::StyleSheet for Theme { } Container::HeaderBar => container::Appearance { - icon_color: Some(Color::from(cosmic.accent.base)), + icon_color: Some(Color::from(cosmic.background.on)), text_color: Some(Color::from(cosmic.background.on)), background: Some(iced::Background::Color(cosmic.background.base.into())), border: Border { diff --git a/src/theme/style/segmented_button.rs b/src/theme/style/segmented_button.rs index 170db8fc..3fc4ae1e 100644 --- a/src/theme/style/segmented_button.rs +++ b/src/theme/style/segmented_button.rs @@ -5,6 +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_core::{border::Radius, Background}; #[derive(Default)] @@ -23,6 +24,8 @@ impl StyleSheet for Theme { #[allow(clippy::too_many_lines)] fn horizontal(&self, style: &Self::Style) -> Appearance { + let container = &self.current_container(); + match style { SegmentedButton::TabBar => { let cosmic = self.cosmic(); @@ -46,25 +49,24 @@ impl StyleSheet for Theme { border_bottom: Some((1.0, cosmic.accent.base.into())), ..Default::default() }, - text_color: cosmic.on_bg_color().into(), + text_color: container.component.on.into(), }, - hover: hover(cosmic, &active), - focus: focus(cosmic, &active), + 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); - let mut neutral_5 = cosmic.palette.neutral_5; - neutral_5.alpha = 0.2; + let active = horizontal::selection_active(cosmic, &container.component); let rad_m = cosmic.corner_radii.radius_m; let rad_0 = cosmic.corner_radii.radius_0; Appearance { - border_radius: cosmic.corner_radii.radius_0.into(), + background: Some(Background::Color(container.small_widget.into())), + border_radius: rad_m.into(), inactive: ItemStatusAppearance { - background: Some(Background::Color(cosmic.small_container_widget().into())), + background: None, first: ItemAppearance { border_radius: Radius::from([rad_m[0], rad_0[1], rad_0[2], rad_m[3]]), ..Default::default() @@ -77,10 +79,10 @@ impl StyleSheet for Theme { border_radius: Radius::from([rad_0[0], rad_m[1], rad_m[2], rad_0[3]]), ..Default::default() }, - text_color: cosmic.on_bg_color().into(), + text_color: container.component.on.into(), }, - hover: hover(cosmic, &active), - focus: focus(cosmic, &active), + hover: hover(cosmic, &container.component, &active), + focus: focus(cosmic, container, &active), active, ..Default::default() } @@ -96,28 +98,29 @@ impl StyleSheet for Theme { let rad_0 = cosmic.corner_radii.radius_0; 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: cosmic.on_bg_color().into(), + text_color: container.component.on.into(), ..active }, - hover: hover(cosmic, &active), - focus: focus(cosmic, &active), + hover: hover(cosmic, &container.component, &active), + focus: focus(cosmic, container, &active), active, ..Default::default() } } SegmentedButton::Control => { - let active = vertical::selection_active(cosmic); - let mut neutral_5 = cosmic.palette.neutral_5; - neutral_5.alpha = 0.2; + let container = self.current_container(); + let active = vertical::selection_active(cosmic, &container.component); Appearance { - border_radius: cosmic.corner_radii.radius_0.into(), + background: Some(Background::Color(container.small_widget.into())), + border_radius: rad_m.into(), inactive: ItemStatusAppearance { - background: Some(Background::Color(neutral_5.into())), + background: None, first: ItemAppearance { border_radius: Radius::from([rad_m[0], rad_m[1], rad_0[0], rad_0[0]]), ..Default::default() @@ -130,10 +133,10 @@ impl StyleSheet for Theme { border_radius: Radius::from([rad_0[0], rad_0[1], rad_m[2], rad_m[3]]), ..Default::default() }, - text_color: cosmic.on_bg_color().into(), + text_color: container.component.on.into(), }, - hover: hover(cosmic, &active), - focus: focus(cosmic, &active), + hover: hover(cosmic, &container.component, &active), + focus: focus(cosmic, container, &active), active, ..Default::default() } @@ -145,15 +148,21 @@ impl StyleSheet for Theme { mod horizontal { use crate::widget::segmented_button::{ItemAppearance, ItemStatusAppearance}; + use cosmic_theme::Component; use iced_core::{border::Radius, Background}; - pub fn selection_active(cosmic: &cosmic_theme::Theme) -> ItemStatusAppearance { - let mut neutral_5 = cosmic.palette.neutral_5; - neutral_5.alpha = 0.2; + pub fn selection_active( + cosmic: &cosmic_theme::Theme, + component: &Component, + ) -> ItemStatusAppearance { + let mut color = cosmic.palette.neutral_5; + color.alpha = 0.2; + let rad_m = cosmic.corner_radii.radius_m; let rad_0 = cosmic.corner_radii.radius_0; + ItemStatusAppearance { - background: Some(Background::Color(neutral_5.into())), + background: Some(Background::Color(color.into())), first: ItemAppearance { border_radius: Radius::from([rad_m[0], rad_0[1], rad_0[2], rad_m[3]]), ..Default::default() @@ -197,23 +206,28 @@ mod horizontal { } } -pub fn focus(cosmic: &cosmic_theme::Theme, default: &ItemStatusAppearance) -> ItemStatusAppearance { - // TODO: This is a hack to make the hover color lighter than the selected color - // I'm not sure why the alpha is being applied differently here than in figma - let mut neutral_5 = cosmic.palette.neutral_5; - neutral_5.alpha = 0.2; +pub fn focus( + cosmic: &cosmic_theme::Theme, + container: &Container, + default: &ItemStatusAppearance, +) -> ItemStatusAppearance { + let color = container.small_widget; ItemStatusAppearance { - background: Some(Background::Color(neutral_5.into())), + background: Some(Background::Color(color.into())), text_color: cosmic.accent.base.into(), ..*default } } -pub fn hover(cosmic: &cosmic_theme::Theme, default: &ItemStatusAppearance) -> ItemStatusAppearance { - let mut neutral_10 = cosmic.palette.neutral_10; - neutral_10.alpha = 0.1; +pub fn hover( + cosmic: &cosmic_theme::Theme, + component: &Component, + default: &ItemStatusAppearance, +) -> ItemStatusAppearance { + let mut color = cosmic.palette.neutral_8; + color.alpha = 0.2; ItemStatusAppearance { - background: Some(Background::Color(neutral_10.into())), + background: Some(Background::Color(color.into())), text_color: cosmic.accent.base.into(), ..*default } @@ -221,15 +235,21 @@ pub fn hover(cosmic: &cosmic_theme::Theme, default: &ItemStatusAppearance) -> It mod vertical { use crate::widget::segmented_button::{ItemAppearance, ItemStatusAppearance}; + use cosmic_theme::Component; use iced_core::{border::Radius, Background}; - pub fn selection_active(cosmic: &cosmic_theme::Theme) -> ItemStatusAppearance { - let mut neutral_5 = cosmic.palette.neutral_5; - neutral_5.alpha = 0.2; + pub fn selection_active( + cosmic: &cosmic_theme::Theme, + component: &Component, + ) -> ItemStatusAppearance { + let mut color = component.selected_state_color(); + color.alpha = 0.3; + let rad_0 = cosmic.corner_radii.radius_0; let rad_m = cosmic.corner_radii.radius_m; + ItemStatusAppearance { - background: Some(Background::Color(neutral_5.into())), + background: Some(Background::Color(color.into())), first: ItemAppearance { border_radius: Radius::from([rad_m[0], rad_m[1], rad_0[2], rad_0[3]]), ..Default::default()