refactor(theme): make Theme Copy

This commit is contained in:
Ashley Wulber 2023-02-27 19:56:53 -05:00 committed by Jeremy Soller
parent 3245ff1b0e
commit 844aeba379
4 changed files with 97 additions and 83 deletions

View file

@ -25,7 +25,7 @@ mod bluetooth;
mod demo; mod demo;
use self::{demo::ThemeMode, desktop::DesktopPage}; use self::desktop::DesktopPage;
mod desktop; mod desktop;
mod editor; mod editor;
@ -176,7 +176,7 @@ impl Window {
} }
#[allow(dead_code)] #[allow(dead_code)]
#[derive(Clone, Debug)] #[derive(Clone, Copy, Debug)]
pub enum Message { pub enum Message {
Bluetooth(bluetooth::Message), Bluetooth(bluetooth::Message),
Close, Close,
@ -390,10 +390,9 @@ impl Application for Window {
Message::Demo(message) => match self.demo.update(message) { Message::Demo(message) => match self.demo.update(message) {
Some(demo::Output::Debug(debug)) => self.debug = debug, Some(demo::Output::Debug(debug)) => self.debug = debug,
Some(demo::Output::ScalingFactor(factor)) => self.set_scale_factor(factor), Some(demo::Output::ScalingFactor(factor)) => self.set_scale_factor(factor),
Some(demo::Output::ThemeChanged(theme)) => match theme { Some(demo::Output::ThemeChanged(theme)) => {
ThemeMode::Light => self.theme = Theme::light(), self.theme = theme;
ThemeMode::Dark => self.theme = Theme::dark(), }
},
Some(demo::Output::ToggleWarning) => self.toggle_warning(), Some(demo::Output::ToggleWarning) => self.toggle_warning(),
None => (), None => (),
}, },
@ -568,6 +567,6 @@ impl Application for Window {
} }
fn theme(&self) -> Theme { fn theme(&self) -> Theme {
self.theme.clone() self.theme
} }
} }

View file

@ -29,13 +29,7 @@ pub enum MultiOption {
OptionE, OptionE,
} }
#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug)]
pub enum ThemeMode {
Light,
Dark,
}
#[derive(Clone, Debug)]
pub enum Message { pub enum Message {
ButtonPressed, ButtonPressed,
CheckboxToggled(bool), CheckboxToggled(bool),
@ -48,7 +42,7 @@ pub enum Message {
Selection(segmented_button::Entity), Selection(segmented_button::Entity),
SliderChanged(f32), SliderChanged(f32),
SpinButton(spin_button::Message), SpinButton(spin_button::Message),
ThemeChanged(ThemeMode), ThemeChanged(Theme),
ToggleWarning, ToggleWarning,
TogglerToggled(bool), TogglerToggled(bool),
ViewSwitcher(segmented_button::Entity), ViewSwitcher(segmented_button::Entity),
@ -57,7 +51,7 @@ pub enum Message {
pub enum Output { pub enum Output {
Debug(bool), Debug(bool),
ScalingFactor(f32), ScalingFactor(f32),
ThemeChanged(ThemeMode), ThemeChanged(Theme),
ToggleWarning, ToggleWarning,
} }
@ -148,15 +142,20 @@ impl State {
} }
pub(super) fn view<'a>(&'a self, window: &'a Window) -> Element<'a, Message> { pub(super) fn view<'a>(&'a self, window: &'a Window) -> Element<'a, Message> {
let choose_theme = [ThemeMode::Light, ThemeMode::Dark].iter().fold( let choose_theme = [
Theme::light(),
Theme::dark(),
Theme::light_hc(),
Theme::dark_hc(),
]
.iter()
.fold(
row![].spacing(10).align_items(Alignment::Center), row![].spacing(10).align_items(Alignment::Center),
|row, theme| { |row, theme| {
row.push(radio( row.push(radio(
format!("{:?}", theme), format!("{:?}", theme.theme_type),
*theme, *theme,
if window.theme.cosmic().is_dark && matches!(theme, ThemeMode::Dark) if window.theme == *theme {
|| !window.theme.cosmic().is_dark && matches!(theme, ThemeMode::Light)
{
Some(*theme) Some(*theme)
} else { } else {
None None

View file

@ -39,7 +39,9 @@ type CosmicThemeCss = cosmic_theme::Theme<cosmic_theme::util::CssColor>;
lazy_static::lazy_static! { lazy_static::lazy_static! {
pub static ref COSMIC_DARK: CosmicTheme = CosmicThemeCss::dark_default().into_srgba(); pub static ref COSMIC_DARK: CosmicTheme = CosmicThemeCss::dark_default().into_srgba();
pub static ref COSMIC_HC_DARK: CosmicTheme = CosmicThemeCss::high_contrast_dark_default().into_srgba();
pub static ref COSMIC_LIGHT: CosmicTheme = CosmicThemeCss::light_default().into_srgba(); pub static ref COSMIC_LIGHT: CosmicTheme = CosmicThemeCss::light_default().into_srgba();
pub static ref COSMIC_HC_LIGHT: CosmicTheme = CosmicThemeCss::high_contrast_light_default().into_srgba();
pub static ref TRANSPARENT_COMPONENT: Component<CosmicColor> = Component { pub static ref TRANSPARENT_COMPONENT: Component<CosmicColor> = Component {
base: CosmicColor::new(0.0, 0.0, 0.0, 0.0), base: CosmicColor::new(0.0, 0.0, 0.0, 0.0),
hover: CosmicColor::new(0.0, 0.0, 0.0, 0.0), hover: CosmicColor::new(0.0, 0.0, 0.0, 0.0),
@ -54,54 +56,79 @@ lazy_static::lazy_static! {
}; };
} }
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
pub struct Theme { pub enum ThemeType {
pub cosmic: cosmic_theme::Theme<Srgba>, #[default]
Dark,
Light,
HighContrastDark,
HighContrastLight,
} }
impl Default for Theme { #[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
fn default() -> Self { pub struct Theme {
Self::dark() pub theme_type: ThemeType,
} pub layer: cosmic_theme::Layer,
} }
impl Theme { impl Theme {
#[must_use]
pub fn new(cosmic: cosmic_theme::Theme<Srgba>) -> Self {
Self { cosmic }
}
#[must_use] #[must_use]
pub fn cosmic(&self) -> &cosmic_theme::Theme<Srgba> { pub fn cosmic(&self) -> &cosmic_theme::Theme<Srgba> {
&self.cosmic match self.theme_type {
} ThemeType::Dark => &COSMIC_DARK,
ThemeType::Light => &COSMIC_LIGHT,
pub fn cosmic_mut(&mut self) -> &mut cosmic_theme::Theme<Srgba> { ThemeType::HighContrastDark => &COSMIC_HC_DARK,
&mut self.cosmic ThemeType::HighContrastLight => &COSMIC_HC_LIGHT,
} }
pub fn set_cosmic(&mut self, cosmic: cosmic_theme::Theme<Srgba>) {
self.cosmic = cosmic;
} }
#[must_use] #[must_use]
pub fn dark() -> Self { pub fn dark() -> Self {
Self { Self {
cosmic: COSMIC_DARK.clone(), theme_type: ThemeType::Dark,
..Default::default()
} }
} }
#[must_use] #[must_use]
pub fn light() -> Self { pub fn light() -> Self {
Self { Self {
cosmic: COSMIC_LIGHT.clone(), theme_type: ThemeType::Light,
..Default::default()
}
}
#[must_use]
pub fn dark_hc() -> Self {
Self {
theme_type: ThemeType::HighContrastDark,
..Default::default()
}
}
#[must_use]
pub fn light_hc() -> Self {
Self {
theme_type: ThemeType::HighContrastLight,
..Default::default()
}
}
/// get current container
/// can be used in a component that is intended to be a child of a `CosmicContainer`
#[must_use]
pub fn current_container(&self) -> &cosmic_theme::Container<Srgba> {
match self.layer {
cosmic_theme::Layer::Background => &self.cosmic().background,
cosmic_theme::Layer::Primary => &self.cosmic().primary,
cosmic_theme::Layer::Secondary => &self.cosmic().secondary,
} }
} }
} }
impl LayeredTheme for Theme { impl LayeredTheme for Theme {
fn set_layer(&mut self, layer: cosmic_theme::Layer) { fn set_layer(&mut self, layer: cosmic_theme::Layer) {
self.cosmic.layer = layer; self.layer = layer;
} }
} }
@ -166,14 +193,14 @@ impl Button {
let cosmic = theme.cosmic(); let cosmic = theme.cosmic();
match self { match self {
Button::Primary => &cosmic.accent, Button::Primary => &cosmic.accent,
Button::Secondary => &cosmic.current_container().component, Button::Secondary => &theme.current_container().component,
Button::Positive => &cosmic.success, Button::Positive => &cosmic.success,
Button::Destructive => &cosmic.destructive, Button::Destructive => &cosmic.destructive,
Button::Text => &cosmic.current_container().component, Button::Text => &theme.current_container().component,
Button::Link => &cosmic.accent, Button::Link => &cosmic.accent,
Button::LinkActive => &cosmic.accent, Button::LinkActive => &cosmic.accent,
Button::Transparent => &TRANSPARENT_COMPONENT, Button::Transparent => &TRANSPARENT_COMPONENT,
Button::Deactivated => &cosmic.current_container().component, Button::Deactivated => &theme.current_container().component,
Button::Custom { .. } => &TRANSPARENT_COMPONENT, Button::Custom { .. } => &TRANSPARENT_COMPONENT,
} }
} }
@ -359,15 +386,15 @@ impl checkbox::StyleSheet for Theme {
}, },
Checkbox::Secondary => checkbox::Appearance { Checkbox::Secondary => checkbox::Appearance {
background: Background::Color(if is_checked { background: Background::Color(if is_checked {
palette.current_container().base.into() self.current_container().base.into()
} else { } else {
neutral_10.into() neutral_10.into()
}), }),
checkmark_color: palette.current_container().on.into(), checkmark_color: self.current_container().on.into(),
border_radius: 4.0, border_radius: 4.0,
border_width: if is_checked { 0.0 } else { 1.0 }, border_width: if is_checked { 0.0 } else { 1.0 },
border_color: if is_checked { border_color: if is_checked {
palette.current_container().base self.current_container().base
} else { } else {
neutral_7 neutral_7
} }
@ -538,7 +565,7 @@ impl slider::StyleSheet for Theme {
let mut style = self.active(style); let mut style = self.active(style);
style.handle.shape = slider::HandleShape::Circle { radius: 16.0 }; style.handle.shape = slider::HandleShape::Circle { radius: 16.0 };
style.handle.border_width = 6.0; style.handle.border_width = 6.0;
let mut border_color = self.cosmic.palette.neutral_10; let mut border_color = self.cosmic().palette.neutral_10;
border_color.alpha = 0.1; border_color.alpha = 0.1;
style.handle.border_color = border_color.into(); style.handle.border_color = border_color.into();
style style
@ -546,7 +573,7 @@ impl slider::StyleSheet for Theme {
fn dragging(&self, style: &Self::Style) -> slider::Appearance { fn dragging(&self, style: &Self::Style) -> slider::Appearance {
let mut style = self.hovered(style); let mut style = self.hovered(style);
let mut border_color = self.cosmic.palette.neutral_10; let mut border_color = self.cosmic().palette.neutral_10;
border_color.alpha = 0.2; border_color.alpha = 0.2;
style.handle.border_color = border_color.into(); style.handle.border_color = border_color.into();
@ -789,33 +816,25 @@ impl rule::StyleSheet for Theme {
type Style = Rule; type Style = Rule;
fn appearance(&self, style: &Self::Style) -> rule::Appearance { fn appearance(&self, style: &Self::Style) -> rule::Appearance {
let palette = self.cosmic();
match style { match style {
Rule::Default => rule::Appearance { Rule::Default => rule::Appearance {
color: palette.current_container().divider.into(), color: self.current_container().divider.into(),
width: 1, width: 1,
radius: 0.0, radius: 0.0,
fill_mode: rule::FillMode::Full, fill_mode: rule::FillMode::Full,
}, },
Rule::LightDivider => { Rule::LightDivider => rule::Appearance {
let cosmic = &self.cosmic(); color: self.current_container().divider.into(),
rule::Appearance { width: 1,
color: cosmic.current_container().divider.into(), radius: 0.0,
width: 1, fill_mode: rule::FillMode::Padded(10),
radius: 0.0, },
fill_mode: rule::FillMode::Padded(10), Rule::HeavyDivider => rule::Appearance {
} color: self.current_container().divider.into(),
} width: 4,
Rule::HeavyDivider => { radius: 4.0,
let cosmic = &self.cosmic(); fill_mode: rule::FillMode::Full,
rule::Appearance { },
color: cosmic.current_container().divider.into(),
width: 4,
radius: 4.0,
fill_mode: rule::FillMode::Full,
}
}
Rule::Custom(f) => f(self), Rule::Custom(f) => f(self),
} }
} }
@ -828,17 +847,15 @@ impl scrollable::StyleSheet for Theme {
type Style = (); type Style = ();
fn active(&self, _style: &Self::Style) -> scrollable::Scrollbar { fn active(&self, _style: &Self::Style) -> scrollable::Scrollbar {
let theme = self.cosmic();
scrollable::Scrollbar { scrollable::Scrollbar {
background: Some(Background::Color( background: Some(Background::Color(
theme.current_container().component.base.into(), self.current_container().component.base.into(),
)), )),
border_radius: 4.0, border_radius: 4.0,
border_width: 0.0, border_width: 0.0,
border_color: Color::TRANSPARENT, border_color: Color::TRANSPARENT,
scroller: scrollable::Scroller { scroller: scrollable::Scroller {
color: theme.current_container().component.divider.into(), color: self.current_container().component.divider.into(),
border_radius: 4.0, border_radius: 4.0,
border_width: 0.0, border_width: 0.0,
border_color: Color::TRANSPARENT, border_color: Color::TRANSPARENT,
@ -851,7 +868,7 @@ impl scrollable::StyleSheet for Theme {
scrollable::Scrollbar { scrollable::Scrollbar {
background: Some(Background::Color( background: Some(Background::Color(
theme.current_container().component.hover.into(), self.current_container().component.hover.into(),
)), )),
border_radius: 4.0, border_radius: 4.0,
border_width: 0.0, border_width: 0.0,
@ -907,7 +924,7 @@ impl svg::StyleSheet for Theme {
Svg::Default => svg::Appearance::default(), Svg::Default => svg::Appearance::default(),
Svg::Custom(appearance) => appearance(self), Svg::Custom(appearance) => appearance(self),
Svg::Symbolic => svg::Appearance { Svg::Symbolic => svg::Appearance {
color: Some(self.cosmic().current_container().on.into()), color: Some(self.current_container().on.into()),
}, },
Svg::SymbolicActive => svg::Appearance { Svg::SymbolicActive => svg::Appearance {
color: Some(self.cosmic().accent.base.into()), color: Some(self.cosmic().accent.base.into()),
@ -977,7 +994,7 @@ impl text_input::StyleSheet for Theme {
background: Color::from(bg).into(), background: Color::from(bg).into(),
border_radius: 2.0, border_radius: 2.0,
border_width: 1.0, border_width: 1.0,
border_color: palette.current_container().component.divider.into(), border_color: self.current_container().component.divider.into(),
}, },
TextInput::Search => text_input::Appearance { TextInput::Search => text_input::Appearance {
background: Color::from(bg).into(), background: Color::from(bg).into(),

View file

@ -59,8 +59,7 @@ impl<'a, Message: 'static> From<ListColumn<'a, Message>> for Element<'a, Message
#[must_use] #[must_use]
#[allow(clippy::trivially_copy_pass_by_ref)] #[allow(clippy::trivially_copy_pass_by_ref)]
pub fn style(theme: &crate::Theme) -> iced::widget::container::Appearance { pub fn style(theme: &crate::Theme) -> iced::widget::container::Appearance {
let cosmic = &theme.cosmic(); let container = &theme.current_container().component;
let container = &cosmic.current_container().component;
iced::widget::container::Appearance { iced::widget::container::Appearance {
text_color: Some(container.on.into()), text_color: Some(container.on.into()),
background: Some(Background::Color(container.base.into())), background: Some(Background::Color(container.base.into())),