// Copyright 2022 System76 // SPDX-License-Identifier: MPL-2.0 //! Contains stylesheet implementations for widgets native to iced. use crate::theme::{CosmicComponent, Theme, TRANSPARENT_COMPONENT}; use cosmic_theme::composite::over; use iced_core::gradient::Linear; use iced_core::BorderRadius; use iced_core::Radians; use iced_core::{Background, Color}; use iced_style::application; use iced_style::button as iced_button; use iced_style::checkbox; use iced_style::container; use iced_style::menu; use iced_style::pane_grid; use iced_style::pick_list; use iced_style::progress_bar; use iced_style::radio; use iced_style::rule; use iced_style::scrollable; use iced_style::slider; use iced_style::slider::Rail; use iced_style::svg; use iced_style::text_input; use iced_style::toggler; use std::f32::consts::PI; use std::rc::Rc; #[derive(Default)] pub enum Application { #[default] Default, Custom(Box application::Appearance>), } impl Application { pub fn custom application::Appearance + 'static>(f: F) -> Self { Self::Custom(Box::new(f)) } } impl application::StyleSheet for Theme { type Style = Application; fn appearance(&self, style: &Self::Style) -> application::Appearance { let cosmic = self.cosmic(); match style { Application::Default => application::Appearance { icon_color: cosmic.bg_color().into(), background_color: cosmic.bg_color().into(), text_color: cosmic.on_bg_color().into(), }, Application::Custom(f) => f(self), } } } /// Styles for the button widget from iced-rs. #[derive(Default)] pub enum Button { Deactivated, Destructive, Positive, #[default] Primary, Secondary, Text, Link, LinkActive, Transparent, Card, Custom { active: Box iced_button::Appearance>, hover: Box iced_button::Appearance>, }, } impl Button { #[allow(clippy::trivially_copy_pass_by_ref)] #[allow(clippy::match_same_arms)] fn cosmic<'a>(&'a self, theme: &'a Theme) -> &CosmicComponent { let cosmic = theme.cosmic(); match self { Self::Primary => &cosmic.accent_button, Self::Secondary => &theme.current_container().component, Self::Positive => &cosmic.success_button, Self::Destructive => &cosmic.destructive_button, Self::Text => &cosmic.text_button, Self::Link => &cosmic.accent_button, Self::LinkActive => &cosmic.accent_button, Self::Transparent => &TRANSPARENT_COMPONENT, Self::Deactivated => &theme.current_container().component, Self::Card => &theme.current_container().component, Self::Custom { .. } => &TRANSPARENT_COMPONENT, } } } impl iced_button::StyleSheet for Theme { type Style = Button; fn active(&self, style: &Self::Style) -> iced_button::Appearance { if let Button::Custom { active, .. } = style { return active(self); } let corner_radii = &self.cosmic().corner_radii; let component = style.cosmic(self); iced_button::Appearance { border_radius: match style { Button::Link => corner_radii.radius_0.into(), Button::Card => corner_radii.radius_xs.into(), _ => corner_radii.radius_xl.into(), }, background: match style { Button::Link | Button::Text => None, Button::LinkActive => Some(Background::Color(component.divider.into())), _ => Some(Background::Color(component.base.into())), }, text_color: match style { Button::Link | Button::LinkActive => component.base.into(), _ => component.on.into(), }, ..iced_button::Appearance::default() } } fn hovered(&self, style: &Self::Style) -> iced_button::Appearance { if let Button::Custom { hover, .. } = style { return hover(self); } let active = self.active(style); let component = style.cosmic(self); iced_button::Appearance { background: match style { Button::Link => None, Button::LinkActive => Some(Background::Color(component.divider.into())), _ => Some(Background::Color(component.hover.into())), }, ..active } } fn disabled(&self, style: &Self::Style) -> iced_button::Appearance { let active = self.active(style); if matches!(style, Button::Card) { return active; } iced_button::Appearance { shadow_offset: iced_core::Vector::default(), background: active.background.map(|background| match background { Background::Color(color) => Background::Color(Color { a: color.a * 0.5, ..color }), Background::Gradient(gradient) => Background::Gradient(gradient.mul_alpha(0.5)), }), text_color: Color { a: active.text_color.a * 0.5, ..active.text_color }, ..active } } } /* * TODO: Checkbox */ #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Checkbox { Primary, Secondary, Success, Danger, } impl Default for Checkbox { fn default() -> Self { Self::Primary } } impl checkbox::StyleSheet for Theme { type Style = Checkbox; fn active(&self, style: &Self::Style, is_checked: bool) -> checkbox::Appearance { let cosmic = self.cosmic(); let corners = &cosmic.corner_radii; match style { Checkbox::Primary => checkbox::Appearance { background: Background::Color(if is_checked { cosmic.accent.base.into() } else { cosmic.button.base.into() }), icon_color: cosmic.accent.on.into(), border_radius: corners.radius_xs.into(), border_width: if is_checked { 0.0 } else { 1.0 }, border_color: if is_checked { cosmic.accent.base } else { cosmic.button.border } .into(), text_color: None, }, Checkbox::Secondary => checkbox::Appearance { background: Background::Color(if is_checked { cosmic.background.component.base.into() } else { cosmic.background.base.into() }), icon_color: cosmic.background.on.into(), border_radius: corners.radius_xs.into(), border_width: if is_checked { 0.0 } else { 1.0 }, border_color: cosmic.button.border.into(), text_color: None, }, Checkbox::Success => checkbox::Appearance { background: Background::Color(if is_checked { cosmic.success.base.into() } else { cosmic.button.base.into() }), icon_color: cosmic.success.on.into(), border_radius: corners.radius_xs.into(), border_width: if is_checked { 0.0 } else { 1.0 }, border_color: if is_checked { cosmic.success.base } else { cosmic.button.border } .into(), text_color: None, }, Checkbox::Danger => checkbox::Appearance { background: Background::Color(if is_checked { cosmic.destructive.base.into() } else { cosmic.button.base.into() }), icon_color: cosmic.destructive.on.into(), border_radius: corners.radius_xs.into(), border_width: if is_checked { 0.0 } else { 1.0 }, border_color: if is_checked { cosmic.destructive.base } else { cosmic.button.border } .into(), text_color: None, }, } } fn hovered(&self, style: &Self::Style, is_checked: bool) -> checkbox::Appearance { let cosmic = self.cosmic(); let corners = &cosmic.corner_radii; match style { Checkbox::Primary => checkbox::Appearance { background: Background::Color(if is_checked { cosmic.accent.base.into() } else { cosmic.button.base.into() }), icon_color: cosmic.accent.on.into(), border_radius: corners.radius_xs.into(), border_width: if is_checked { 0.0 } else { 1.0 }, border_color: if is_checked { cosmic.accent.base } else { cosmic.button.border } .into(), text_color: None, }, Checkbox::Secondary => checkbox::Appearance { background: Background::Color(if is_checked { self.current_container().base.into() } else { cosmic.button.base.into() }), icon_color: self.current_container().on.into(), border_radius: corners.radius_xs.into(), border_width: if is_checked { 0.0 } else { 1.0 }, border_color: if is_checked { self.current_container().base } else { cosmic.button.border } .into(), text_color: None, }, Checkbox::Success => checkbox::Appearance { background: Background::Color(if is_checked { cosmic.success.base.into() } else { cosmic.button.base.into() }), icon_color: cosmic.success.on.into(), border_radius: corners.radius_xs.into(), border_width: if is_checked { 0.0 } else { 1.0 }, border_color: if is_checked { cosmic.success.base } else { cosmic.button.border } .into(), text_color: None, }, Checkbox::Danger => checkbox::Appearance { background: Background::Color(if is_checked { cosmic.destructive.base.into() } else { cosmic.button.base.into() }), icon_color: cosmic.destructive.on.into(), border_radius: corners.radius_xs.into(), border_width: if is_checked { 0.0 } else { 1.0 }, border_color: if is_checked { cosmic.destructive.base } else { cosmic.button.border } .into(), text_color: None, }, } } } /* * TODO: Container */ #[derive(Default)] pub enum Container { Background, Card, Custom(Box container::Appearance>), Dropdown, HeaderBar, Primary, Secondary, Tooltip, #[default] Transparent, } impl Container { pub fn custom container::Appearance + 'static>(f: F) -> Self { Self::Custom(Box::new(f)) } } impl container::StyleSheet for Theme { type Style = Container; #[allow(clippy::too_many_lines)] fn appearance(&self, style: &Self::Style) -> container::Appearance { let cosmic = self.cosmic(); match style { Container::Transparent => container::Appearance::default(), Container::Custom(f) => f(self), Container::Background => { let palette = self.cosmic(); container::Appearance { icon_color: Some(Color::from(palette.background.on)), text_color: Some(Color::from(palette.background.on)), background: Some(iced::Background::Color(palette.background.base.into())), border_radius: cosmic.corner_radii.radius_xs.into(), border_width: 0.0, border_color: Color::TRANSPARENT, } } Container::HeaderBar => { let palette = self.cosmic(); let mut header_top = palette.background.base; let header_bottom = palette.background.base; header_top.alpha = 0.8; container::Appearance { icon_color: Some(Color::from(palette.accent.base)), text_color: Some(Color::from(palette.background.on)), background: Some(iced::Background::Gradient(iced_core::Gradient::Linear( Linear::new(Radians(PI)) .add_stop(0.0, header_top.into()) .add_stop(1.0, header_bottom.into()), ))), border_radius: BorderRadius::from([ palette.corner_radii.radius_xs[0], palette.corner_radii.radius_xs[3], 0.0, 0.0, ]), border_width: 0.0, border_color: Color::TRANSPARENT, } } Container::Primary => { let palette = self.cosmic(); container::Appearance { icon_color: Some(Color::from(palette.primary.on)), text_color: Some(Color::from(palette.primary.on)), background: Some(iced::Background::Color(palette.primary.base.into())), border_radius: cosmic.corner_radii.radius_xs.into(), border_width: 0.0, border_color: Color::TRANSPARENT, } } Container::Secondary => { let palette = self.cosmic(); container::Appearance { icon_color: Some(Color::from(palette.secondary.on)), text_color: Some(Color::from(palette.secondary.on)), background: Some(iced::Background::Color(palette.secondary.base.into())), border_radius: cosmic.corner_radii.radius_xs.into(), border_width: 0.0, border_color: Color::TRANSPARENT, } } Container::Dropdown => { let theme = self.cosmic(); container::Appearance { icon_color: None, text_color: None, background: Some(iced::Background::Color(theme.primary.base.into())), border_radius: theme.corner_radii.radius_xs.into(), border_width: 1.0, border_color: theme.bg_divider().into(), } } Container::Tooltip => { let theme = self.cosmic(); container::Appearance { icon_color: None, text_color: None, background: Some(iced::Background::Color(theme.palette.neutral_2.into())), border_radius: theme.corner_radii.radius_l.into(), border_width: 0.0, border_color: Color::TRANSPARENT, } } Container::Card => { let palette = self.cosmic(); match self.layer { cosmic_theme::Layer::Background => container::Appearance { icon_color: Some(Color::from(palette.background.component.on)), text_color: Some(Color::from(palette.background.component.on)), background: Some(iced::Background::Color( palette.background.component.base.into(), )), border_radius: cosmic.corner_radii.radius_s.into(), border_width: 0.0, border_color: Color::TRANSPARENT, }, cosmic_theme::Layer::Primary => container::Appearance { icon_color: Some(Color::from(palette.primary.component.on)), text_color: Some(Color::from(palette.primary.component.on)), background: Some(iced::Background::Color( palette.primary.component.base.into(), )), border_radius: cosmic.corner_radii.radius_s.into(), border_width: 0.0, border_color: Color::TRANSPARENT, }, cosmic_theme::Layer::Secondary => container::Appearance { icon_color: Some(Color::from(palette.secondary.component.on)), text_color: Some(Color::from(palette.secondary.component.on)), background: Some(iced::Background::Color( palette.secondary.component.base.into(), )), border_radius: cosmic.corner_radii.radius_s.into(), border_width: 0.0, border_color: Color::TRANSPARENT, }, } } } } } #[derive(Default)] pub enum Slider { #[default] Standard, Custom { active: Rc slider::Appearance>, hovered: Rc slider::Appearance>, dragging: Rc slider::Appearance>, }, } /* * Slider */ impl slider::StyleSheet for Theme { type Style = Slider; fn active(&self, style: &Self::Style) -> slider::Appearance { match style { Slider::Standard => //TODO: no way to set rail thickness { let cosmic: &cosmic_theme::Theme> = self.cosmic(); slider::Appearance { rail: Rail { colors: slider::RailBackground::Pair( cosmic.accent.base.into(), //TODO: no way to set color before/after slider Color::TRANSPARENT, ), width: 4.0, border_radius: cosmic.corner_radii.radius_xs.into(), }, handle: slider::Handle { shape: slider::HandleShape::Circle { radius: 10.0 }, color: cosmic.accent.base.into(), border_color: Color::TRANSPARENT, border_width: 0.0, }, } } Slider::Custom { active, .. } => active(self), } } fn hovered(&self, style: &Self::Style) -> slider::Appearance { match style { Slider::Standard => { let mut style = self.active(style); style.handle.shape = slider::HandleShape::Circle { radius: 16.0 }; style.handle.border_width = 6.0; let mut border_color = self.cosmic().palette.neutral_10; border_color.alpha = 0.1; style.handle.border_color = border_color.into(); style } Slider::Custom { hovered, .. } => hovered(self), } } fn dragging(&self, style: &Self::Style) -> slider::Appearance { match style { Slider::Standard => { let mut style = self.hovered(style); let mut border_color = self.cosmic().palette.neutral_10; border_color.alpha = 0.2; style.handle.border_color = border_color.into(); style } Slider::Custom { dragging, .. } => dragging(self), } } } /* * TODO: Menu */ impl menu::StyleSheet for Theme { type Style = (); fn appearance(&self, _style: &Self::Style) -> menu::Appearance { let cosmic = self.cosmic(); menu::Appearance { text_color: cosmic.on_bg_color().into(), background: Background::Color(cosmic.background.base.into()), border_width: 0.0, border_radius: cosmic.corner_radii.radius_m.into(), border_color: Color::TRANSPARENT, selected_text_color: cosmic.accent.base.into(), selected_background: Background::Color(cosmic.background.component.hover.into()), } } } /* * TODO: Pick List */ impl pick_list::StyleSheet for Theme { type Style = (); fn active(&self, _style: &()) -> pick_list::Appearance { let cosmic = &self.cosmic(); pick_list::Appearance { text_color: cosmic.on_bg_color().into(), background: Color::TRANSPARENT.into(), placeholder_color: cosmic.on_bg_color().into(), border_radius: cosmic.corner_radii.radius_m.into(), border_width: 0.0, border_color: Color::TRANSPARENT, // icon_size: 0.7, // TODO: how to replace handle_color: cosmic.on_bg_color().into(), } } fn hovered(&self, style: &()) -> pick_list::Appearance { let cosmic = &self.cosmic(); pick_list::Appearance { background: Background::Color(cosmic.background.base.into()), ..self.active(style) } } } /* * TODO: Radio */ impl radio::StyleSheet for Theme { type Style = (); fn active(&self, _style: &Self::Style, is_selected: bool) -> radio::Appearance { let theme = self.cosmic(); radio::Appearance { background: if is_selected { Color::from(theme.accent.base).into() } else { // TODO: this seems to be defined weirdly in FIGMA Color::from(theme.background.base).into() }, dot_color: theme.accent.on.into(), border_width: 1.0, border_color: if is_selected { Color::from(theme.accent.base) } else { // TODO: this seems to be defined weirdly in FIGMA Color::from(theme.palette.neutral_7) }, text_color: None, } } fn hovered(&self, _style: &Self::Style, is_selected: bool) -> radio::Appearance { let theme = self.cosmic(); let mut neutral_10 = theme.palette.neutral_10; neutral_10.alpha = 0.1; radio::Appearance { background: if is_selected { Color::from(theme.accent.base).into() } else { // TODO: this seems to be defined weirdly in FIGMA Color::from(neutral_10).into() }, dot_color: theme.accent.on.into(), border_width: 1.0, border_color: if is_selected { Color::from(theme.accent.base) } else { // TODO: this seems to be defined weirdly in FIGMA Color::from(theme.palette.neutral_7) }, text_color: None, } } } /* * Toggler */ impl toggler::StyleSheet for Theme { type Style = (); fn active(&self, _style: &Self::Style, is_active: bool) -> toggler::Appearance { let theme = self.cosmic(); toggler::Appearance { background: if is_active { theme.accent.base.into() } else { theme.palette.neutral_5.into() }, background_border: None, foreground: theme.palette.neutral_2.into(), foreground_border: None, } } fn hovered(&self, style: &Self::Style, is_active: bool) -> toggler::Appearance { let cosmic = self.cosmic(); //TODO: grab colors from palette let mut neutral_10 = cosmic.palette.neutral_10; neutral_10.alpha = 0.1; toggler::Appearance { background: if is_active { over(neutral_10, cosmic.accent_color()) } else { over(neutral_10, cosmic.palette.neutral_5) } .into(), ..self.active(style, is_active) } } } /* * TODO: Pane Grid */ impl pane_grid::StyleSheet for Theme { type Style = (); fn picked_split(&self, _style: &Self::Style) -> Option { let theme = self.cosmic(); Some(pane_grid::Line { color: theme.accent.base.into(), width: 2.0, }) } fn hovered_split(&self, _style: &Self::Style) -> Option { let theme = self.cosmic(); Some(pane_grid::Line { color: theme.accent.hover.into(), width: 2.0, }) } fn hovered_region(&self, _style: &Self::Style) -> pane_grid::Appearance { let theme = self.cosmic(); pane_grid::Appearance { background: Background::Color(theme.bg_color().into()), border_width: 2.0, border_color: theme.bg_divider().into(), border_radius: theme.corner_radii.radius_0.into(), } } } /* * TODO: Progress Bar */ #[derive(Default)] pub enum ProgressBar { #[default] Primary, Success, Danger, Custom(Box progress_bar::Appearance>), } impl ProgressBar { pub fn custom progress_bar::Appearance + 'static>(f: F) -> Self { Self::Custom(Box::new(f)) } } impl progress_bar::StyleSheet for Theme { type Style = ProgressBar; fn appearance(&self, style: &Self::Style) -> progress_bar::Appearance { let theme = self.cosmic(); match style { ProgressBar::Primary => progress_bar::Appearance { background: Color::from(theme.background.divider).into(), bar: Color::from(theme.accent.base).into(), border_radius: theme.corner_radii.radius_xs.into(), }, ProgressBar::Success => progress_bar::Appearance { background: Color::from(theme.background.divider).into(), bar: Color::from(theme.success.base).into(), border_radius: theme.corner_radii.radius_xs.into(), }, ProgressBar::Danger => progress_bar::Appearance { background: Color::from(theme.background.divider).into(), bar: Color::from(theme.destructive.base).into(), border_radius: theme.corner_radii.radius_xs.into(), }, ProgressBar::Custom(f) => f(self), } } } /* * TODO: Rule */ #[derive(Default)] pub enum Rule { #[default] Default, LightDivider, HeavyDivider, Custom(Box rule::Appearance>), } impl Rule { pub fn custom rule::Appearance + 'static>(f: F) -> Self { Self::Custom(Box::new(f)) } } impl rule::StyleSheet for Theme { type Style = Rule; fn appearance(&self, style: &Self::Style) -> rule::Appearance { match style { Rule::Default => rule::Appearance { color: self.current_container().divider.into(), width: 1, radius: 0.0.into(), fill_mode: rule::FillMode::Full, }, Rule::LightDivider => rule::Appearance { color: self.current_container().divider.into(), width: 1, radius: 0.0.into(), fill_mode: rule::FillMode::Padded(10), }, Rule::HeavyDivider => rule::Appearance { color: self.current_container().divider.into(), width: 4, radius: 4.0.into(), fill_mode: rule::FillMode::Full, }, Rule::Custom(f) => f(self), } } } /* * TODO: Scrollable */ impl scrollable::StyleSheet for Theme { type Style = (); fn active(&self, _style: &Self::Style) -> scrollable::Scrollbar { let cosmic = self.cosmic(); scrollable::Scrollbar { background: Some(Background::Color( self.current_container().component.base.into(), )), border_radius: cosmic.corner_radii.radius_s.into(), border_width: 0.0, border_color: Color::TRANSPARENT, scroller: scrollable::Scroller { color: self.current_container().component.divider.into(), border_radius: cosmic.corner_radii.radius_s.into(), border_width: 0.0, border_color: Color::TRANSPARENT, }, } } fn hovered( &self, _style: &Self::Style, _is_mouse_over_scrollbar: bool, ) -> scrollable::Scrollbar { let theme = self.cosmic(); scrollable::Scrollbar { background: Some(Background::Color( self.current_container().component.hover.into(), )), border_radius: theme.corner_radii.radius_s.into(), border_width: 0.0, border_color: Color::TRANSPARENT, scroller: scrollable::Scroller { color: theme.accent.base.into(), border_radius: theme.corner_radii.radius_s.into(), border_width: 0.0, border_color: Color::TRANSPARENT, }, } } } #[derive(Clone, Default)] pub enum Svg { /// Apply a custom appearance filter Custom(Rc svg::Appearance>), /// No filtering is applied #[default] Default, } impl Svg { pub fn custom svg::Appearance + 'static>(f: F) -> Self { Self::Custom(Rc::new(f)) } } impl svg::StyleSheet for Theme { type Style = Svg; fn appearance(&self, style: &Self::Style) -> svg::Appearance { #[allow(clippy::match_same_arms)] match style { Svg::Default => svg::Appearance::default(), Svg::Custom(appearance) => appearance(self), } } } /* * TODO: Text */ #[derive(Clone, Copy, Default)] pub enum Text { Accent, #[default] Default, Color(Color), // TODO: Can't use dyn Fn since this must be copy Custom(fn(&Theme) -> iced_widget::text::Appearance), } impl From for Text { fn from(color: Color) -> Self { Self::Color(color) } } impl iced_widget::text::StyleSheet for Theme { type Style = Text; fn appearance(&self, style: Self::Style) -> iced_widget::text::Appearance { match style { Text::Accent => iced_widget::text::Appearance { color: Some(self.cosmic().accent.base.into()), }, Text::Default => iced_widget::text::Appearance { color: None }, Text::Color(c) => iced_widget::text::Appearance { color: Some(c) }, Text::Custom(f) => f(self), } } } #[derive(Copy, Clone, Default)] pub enum TextInput { #[default] Default, Search, } /* * TODO: Text Input */ impl text_input::StyleSheet for Theme { type Style = TextInput; fn active(&self, style: &Self::Style) -> text_input::Appearance { let palette = self.cosmic(); let mut bg = palette.palette.neutral_7; bg.alpha = 0.25; match style { TextInput::Default => text_input::Appearance { background: Color::from(bg).into(), border_radius: palette.corner_radii.radius_s.into(), border_width: 1.0, border_color: self.current_container().component.divider.into(), icon_color: self.current_container().on.into(), }, TextInput::Search => text_input::Appearance { background: Color::from(bg).into(), border_radius: palette.corner_radii.radius_m.into(), border_width: 0.0, border_color: Color::TRANSPARENT, icon_color: self.current_container().on.into(), }, } } fn hovered(&self, style: &Self::Style) -> text_input::Appearance { let palette = self.cosmic(); let mut bg = palette.palette.neutral_7; bg.alpha = 0.25; match style { TextInput::Default => text_input::Appearance { background: Color::from(bg).into(), border_radius: palette.corner_radii.radius_s.into(), border_width: 1.0, border_color: palette.accent.base.into(), icon_color: self.current_container().on.into(), }, TextInput::Search => text_input::Appearance { background: Color::from(bg).into(), border_radius: palette.corner_radii.radius_m.into(), border_width: 0.0, border_color: Color::TRANSPARENT, icon_color: self.current_container().on.into(), }, } } fn focused(&self, style: &Self::Style) -> text_input::Appearance { let palette = self.cosmic(); let mut bg = palette.palette.neutral_7; bg.alpha = 0.25; match style { TextInput::Default => text_input::Appearance { background: Color::from(bg).into(), border_radius: palette.corner_radii.radius_s.into(), border_width: 1.0, border_color: palette.accent.base.into(), icon_color: self.current_container().on.into(), }, TextInput::Search => text_input::Appearance { background: Color::from(bg).into(), border_radius: palette.corner_radii.radius_m.into(), border_width: 0.0, border_color: Color::TRANSPARENT, icon_color: self.current_container().on.into(), }, } } fn placeholder_color(&self, _style: &Self::Style) -> Color { let palette = self.cosmic(); let mut neutral_9 = palette.palette.neutral_9; neutral_9.alpha = 0.7; neutral_9.into() } fn value_color(&self, _style: &Self::Style) -> Color { let palette = self.cosmic(); palette.palette.neutral_9.into() } fn selection_color(&self, _style: &Self::Style) -> Color { let palette = self.cosmic(); palette.accent.base.into() } fn disabled_color(&self, _style: &Self::Style) -> Color { let palette = self.cosmic(); let mut neutral_9 = palette.palette.neutral_9; neutral_9.alpha = 0.5; neutral_9.into() } fn disabled(&self, style: &Self::Style) -> text_input::Appearance { self.active(style) } } impl crate::widget::card::style::StyleSheet for Theme { fn default(&self) -> crate::widget::card::style::Appearance { let cosmic = self.cosmic(); match self.layer { cosmic_theme::Layer::Background => crate::widget::card::style::Appearance { card_1: Background::Color(cosmic.background.component.hover.into()), card_2: Background::Color(cosmic.background.component.pressed.into()), }, cosmic_theme::Layer::Primary => crate::widget::card::style::Appearance { card_1: Background::Color(cosmic.primary.component.hover.into()), card_2: Background::Color(cosmic.primary.component.pressed.into()), }, cosmic_theme::Layer::Secondary => crate::widget::card::style::Appearance { card_1: Background::Color(cosmic.secondary.component.hover.into()), card_2: Background::Color(cosmic.secondary.component.pressed.into()), }, } } } #[derive(Default)] pub enum TextEditor { #[default] Default, Custom(Box>), } impl iced_style::text_editor::StyleSheet for Theme { type Style = TextEditor; fn active(&self, style: &Self::Style) -> iced_style::text_editor::Appearance { if let TextEditor::Custom(style) = style { return style.active(self); } let cosmic = self.cosmic(); iced_style::text_editor::Appearance { background: iced::Color::from(cosmic.bg_color()).into(), border_radius: cosmic.corner_radii.radius_0.into(), border_width: f32::from(cosmic.space_xxxs()), border_color: iced::Color::from(cosmic.bg_divider()), } } fn focused(&self, style: &Self::Style) -> iced_style::text_editor::Appearance { if let TextEditor::Custom(style) = style { return style.focused(self); } let cosmic = self.cosmic(); iced_style::text_editor::Appearance { background: iced::Color::from(cosmic.bg_color()).into(), border_radius: cosmic.corner_radii.radius_0.into(), border_width: f32::from(cosmic.space_xxxs()), border_color: iced::Color::from(cosmic.accent.base), } } fn placeholder_color(&self, style: &Self::Style) -> Color { if let TextEditor::Custom(style) = style { return style.placeholder_color(self); } let palette = self.cosmic(); let mut neutral_9 = palette.palette.neutral_9; neutral_9.alpha = 0.7; neutral_9.into() } fn value_color(&self, style: &Self::Style) -> Color { if let TextEditor::Custom(style) = style { return style.value_color(self); } let palette = self.cosmic(); palette.palette.neutral_9.into() } fn disabled_color(&self, style: &Self::Style) -> Color { if let TextEditor::Custom(style) = style { return style.disabled_color(self); } let palette = self.cosmic(); let mut neutral_9 = palette.palette.neutral_9; neutral_9.alpha = 0.5; neutral_9.into() } fn selection_color(&self, style: &Self::Style) -> Color { if let TextEditor::Custom(style) = style { return style.selection_color(self); } let cosmic = self.cosmic(); cosmic.accent.base.into() } fn disabled(&self, style: &Self::Style) -> iced_style::text_editor::Appearance { if let TextEditor::Custom(style) = style { return style.disabled(self); } self.active(style) } }