use iced::{Border, Color, Shadow, Theme, Background, Vector};
use iced::overlay::menu;
fn text_input_style(theme: &Theme, status: TextInputStatus) -> text_input::Style {
let colors = get_theme_colors(theme);
let base_style = text_input::Style {
background: Background::Color(colors.input_bg),
border: Border {
radius: SMALL_CORNER_RADIUS.into(),
width: 1.0,
color: colors.input_border,
},
icon: colors.text,
placeholder: colors.placeholder,
value: colors.text,
selection: colors.blue.scale_alpha(0.3),
};
match status {
TextInputStatus::Active => base_style,
TextInputStatus::Hovered => text_input::Style {
border: Border {
color: colors.placeholder,
..base_style.border
},
..base_style
},
TextInputStatus::Focused => text_input::Style {
border: Border {
color: colors.blue,
width: 2.0,
..base_style.border
},
..base_style
},
TextInputStatus::Disabled => text_input::Style {
background: Background::Color(colors.input_bg.scale_alpha(0.7)),
border: Border {
color: colors.input_border.scale_alpha(0.5),
..base_style.border
},
value: colors.text.scale_alpha(0.5),
..base_style
},
}
}
fn pick_list_style(theme: &Theme, status: pick_list::Status) -> pick_list::Style {
let colors = get_theme_colors(theme);
let base_style = pick_list::Style {
text_color: colors.text,
placeholder_color: colors.placeholder,
background: Background::Color(colors.input_bg),
border: Border {
radius: SMALL_CORNER_RADIUS.into(),
width: 1.0,
color: colors.input_border,
},
handle_color: colors.placeholder,
};
match status {
pick_list::Status::Active => base_style,
pick_list::Status::Hovered => pick_list::Style {
border: Border {
color: colors.placeholder,
..base_style.border
},
..base_style
},
pick_list::Status::Opened => pick_list::Style {
border: Border {
color: colors.blue,
width: 1.5,
..base_style.border
},
handle_color: colors.blue,
..base_style
},
}
}
fn combo_box_style(theme: &Theme, status: TextInputStatus) -> text_input::Style {
text_input_style(theme, status)
}
fn create_modern_theme(dark_mode: bool) -> Theme {
let name = if dark_mode { "Modern Dark" } else { "Modern Light" };
let (background, text) = if dark_mode {
(Color::from_rgb(0.11, 0.11, 0.12), Color::WHITE) } else {
(Color::from_rgb(0.95, 0.95, 0.97), Color::BLACK) };
let primary = if dark_mode { MODERN_BLUE_DARK } else { MODERN_BLUE_LIGHT };
let success = if dark_mode { MODERN_GREEN_DARK } else { MODERN_GREEN_LIGHT };
let danger = if dark_mode { MODERN_RED_DARK } else { MODERN_RED_LIGHT };
Theme::custom(
String::from(name),
iced::theme::Palette {
background,
text,
primary,
success,
danger,
}
)
}
fn radio_style(theme: &Theme, status: radio::Status) -> radio::Style {
let colors = get_theme_colors(theme);
let style = radio::Style {
background: Background::Color(Color::TRANSPARENT),
dot_color: colors.blue,
border_width: 2.0,
border_color: match status {
radio::Status::Active { is_selected } if is_selected => colors.blue,
radio::Status::Hovered { is_selected } if is_selected => colors.blue,
_ => colors.inactive_border,
},
text_color: Some(colors.text),
};
match status {
radio::Status::Hovered { is_selected: true } => style,
radio::Status::Hovered { is_selected: false } => radio::Style {
border_color: colors.blue.scale_alpha(0.5),
..style
},
_ => style,
}
}
fn checkbox_style(theme: &Theme, status: checkbox::Status) -> checkbox::Style {
let colors = get_theme_colors(theme);
match status {
checkbox::Status::Active { is_checked } => {
if is_checked {
checkbox::Style {
background: Background::Color(colors.blue),
icon_color: Color::WHITE,
border: Border {
radius: TINY_CORNER_RADIUS.into(),
width: 0.0,
color: Color::TRANSPARENT,
},
text_color: Some(colors.text),
}
} else {
checkbox::Style {
background: Background::Color(Color::TRANSPARENT),
icon_color: Color::TRANSPARENT,
border: Border {
radius: TINY_CORNER_RADIUS.into(),
width: 2.0,
color: colors.inactive_border,
},
text_color: Some(colors.text),
}
}
},
checkbox::Status::Hovered { is_checked } => {
if is_checked {
checkbox::Style {
background: Background::Color(colors.blue.scale_alpha(0.9)),
icon_color: Color::WHITE,
border: Border {
radius: TINY_CORNER_RADIUS.into(),
width: 0.0,
color: Color::TRANSPARENT,
},
text_color: Some(colors.text),
}
} else {
checkbox::Style {
background: Background::Color(Color::TRANSPARENT),
icon_color: Color::TRANSPARENT,
border: Border {
radius: TINY_CORNER_RADIUS.into(),
width: 2.0,
color: colors.blue.scale_alpha(0.5),
},
text_color: Some(colors.text),
}
}
},
checkbox::Status::Disabled { is_checked } => {
if is_checked {
checkbox::Style {
background: Background::Color(colors.blue.scale_alpha(0.5)),
icon_color: Color::WHITE.scale_alpha(0.5),
border: Border {
radius: TINY_CORNER_RADIUS.into(),
width: 0.0,
color: Color::TRANSPARENT,
},
text_color: Some(colors.text.scale_alpha(0.5)),
}
} else {
checkbox::Style {
background: Background::Color(Color::TRANSPARENT),
icon_color: Color::TRANSPARENT,
border: Border {
radius: TINY_CORNER_RADIUS.into(),
width: 2.0,
color: colors.inactive_border.scale_alpha(0.5),
},
text_color: Some(colors.text.scale_alpha(0.5)),
}
}
},
}
}
fn container_style(theme: &Theme, class: &style::Container) -> container::Style {
let colors = get_theme_colors(theme);
match class {
style::Container::Transparent => container::Style {
text_color: Some(colors.text),
background: None,
border: Border::default(),
shadow: Shadow::default(),
},
style::Container::Card => {
container::Style {
text_color: Some(colors.text),
background: Some(Background::Color(colors.card_bg)),
border: Border {
radius: 10.0.into(), width: 0.0,
color: Color::TRANSPARENT,
},
shadow: Shadow {
color: Color { a: 0.1, ..Color::BLACK },
offset: Vector::new(0.0, 2.0),
blur_radius: 8.0,
},
}
},
style::Container::Sheet => {
let sheet_bg = if is_dark_mode(theme) {
Color::from_rgb(0.22, 0.22, 0.23) } else {
Color::from_rgb(0.95, 0.95, 0.97) };
container::Style {
text_color: Some(colors.text),
background: Some(Background::Color(sheet_bg)),
border: Border {
radius: 12.0.into(), width: 0.0,
color: Color::TRANSPARENT,
},
shadow: Shadow {
color: Color { a: 0.2, ..Color::BLACK },
offset: Vector::new(0.0, 4.0),
blur_radius: 16.0,
},
}
},
style::Container::Group => {
let group_bg = if is_dark_mode(theme) {
Color::from_rgb(0.17, 0.17, 0.18) } else {
Color::from_rgb(0.95, 0.95, 0.97) };
container::Style {
text_color: Some(colors.text),
background: Some(Background::Color(group_bg)),
border: Border {
radius: 10.0.into(),
width: 0.0,
color: Color::TRANSPARENT,
},
shadow: Shadow::default(), }
},
style::Container::Sidebar => {
let sidebar_bg = if is_dark_mode(theme) {
Color::from_rgb(0.15, 0.15, 0.16) } else {
Color::from_rgb(0.92, 0.92, 0.93) };
container::Style {
text_color: Some(colors.text),
background: Some(Background::Color(sidebar_bg)),
border: Border::default(),
shadow: Shadow {
color: Color { a: 0.05, ..Color::BLACK },
offset: Vector::new(1.0, 0.0),
blur_radius: 3.0,
},
}
},
}
}
fn button_hover_style(base_style: button::Style, is_dark: bool) -> button::Style {
let adjust_color = |color: Color| -> Color {
if is_dark {
Color {
r: (color.r + 0.05).min(1.0),
g: (color.g + 0.05).min(1.0),
b: (color.b + 0.05).min(1.0),
a: color.a,
}
} else {
Color {
r: (color.r - 0.05).max(0.0),
g: (color.g - 0.05).max(0.0),
b: (color.b - 0.05).max(0.0),
a: color.a,
}
}
};
if let Some(Background::Color(color)) = base_style.background {
button::Style {
background: Some(Background::Color(adjust_color(color))),
..base_style
}
} else {
base_style
}
}
fn button_pressed_style(base_style: button::Style, is_dark: bool) -> button::Style {
let adjust_color = |color: Color| -> Color {
if is_dark {
Color {
r: (color.r + 0.1).min(1.0),
g: (color.g + 0.1).min(1.0),
b: (color.b + 0.1).min(1.0),
a: color.a,
}
} else {
Color {
r: (color.r - 0.1).max(0.0),
g: (color.g - 0.1).max(0.0),
b: (color.b - 0.1).max(0.0),
a: color.a,
}
}
};
let mut pressed_style = base_style;
pressed_style.shadow = Shadow::default();
if let Some(Background::Color(color)) = base_style.background {
pressed_style.background = Some(Background::Color(adjust_color(color)));
}
pressed_style
}
fn button_disabled_style(base_style: button::Style) -> button::Style {
button::Style {
background: base_style.background.map(|bg| match bg {
Background::Color(color) => Background::Color(color.scale_alpha(0.5)),
_ => bg,
}),
text_color: base_style.text_color.scale_alpha(0.5),
border: Border {
color: base_style.border.color.scale_alpha(0.5),
..base_style.border
},
shadow: Shadow::default(), }
}
use iced::widget::{button, text, text_input, container, radio, checkbox, pick_list, combo_box};
use iced::widget::button::Status as ButtonStatus;
use iced::widget::text_input::Status as TextInputStatus;
use crate::colors::*;
use crate::styles::*;
pub struct Modern;
impl Modern {
pub fn button<'a>(style: style::Button) -> impl Fn(&Theme, ButtonStatus) -> button::Style + 'a {
move |theme, status| button_style(theme, &style, status)
}
pub fn primary_button<'a>() -> impl Fn(&Theme, ButtonStatus) -> button::Style + 'a {
Self::button(style::Button::Primary)
}
pub fn secondary_button<'a>() -> impl Fn(&Theme, ButtonStatus) -> button::Style + 'a {
Self::button(style::Button::Secondary)
}
pub fn success_button<'a>() -> impl Fn(&Theme, ButtonStatus) -> button::Style + 'a {
Self::button(style::Button::Success)
}
pub fn warning_button<'a>() -> impl Fn(&Theme, ButtonStatus) -> button::Style + 'a {
Self::button(style::Button::Warning)
}
pub fn danger_button<'a>() -> impl Fn(&Theme, ButtonStatus) -> button::Style + 'a {
Self::button(style::Button::Danger)
}
pub fn link_button<'a>() -> impl Fn(&Theme, ButtonStatus) -> button::Style + 'a {
Self::button(style::Button::Link)
}
pub fn system_button<'a>() -> impl Fn(&Theme, ButtonStatus) -> button::Style + 'a {
Self::button(style::Button::System)
}
pub fn plain_button<'a>() -> impl Fn(&Theme, ButtonStatus) -> button::Style + 'a {
Self::button(style::Button::Plain)
}
pub fn text_input<'a>() -> impl Fn(&Theme, TextInputStatus) -> text_input::Style + 'a {
text_input_style
}
pub fn container<'a>(style: style::Container) -> impl Fn(&Theme) -> container::Style + 'a {
move |theme| container_style(theme, &style)
}
pub fn card_container<'a>() -> impl Fn(&Theme) -> container::Style + 'a {
Self::container(style::Container::Card)
}
pub fn sheet_container<'a>() -> impl Fn(&Theme) -> container::Style + 'a {
Self::container(style::Container::Sheet)
}
pub fn group_container<'a>() -> impl Fn(&Theme) -> container::Style + 'a {
Self::container(style::Container::Group)
}
pub fn sidebar_container<'a>() -> impl Fn(&Theme) -> container::Style + 'a {
Self::container(style::Container::Sidebar)
}
pub fn radio<'a>() -> impl Fn(&Theme, radio::Status) -> radio::Style + 'a {
radio_style
}
pub fn checkbox<'a>() -> impl Fn(&Theme, checkbox::Status) -> checkbox::Style + 'a {
checkbox_style
}
pub fn pick_list<'a>() -> impl Fn(&Theme, pick_list::Status) -> pick_list::Style + 'a {
pick_list_style
}
pub fn theme(dark_mode: bool) -> Theme {
create_modern_theme(dark_mode)
}
pub fn light_theme() -> Theme {
Self::theme(false)
}
pub fn dark_theme() -> Theme {
Self::theme(true)
}
pub fn teal_button<'a>() -> impl Fn(&Theme, ButtonStatus) -> button::Style + 'a {
move |theme, status| {
let colors = get_theme_colors(theme);
let is_dark = is_dark_mode(theme);
let modern_base = |color: Color, text_color: Color| button::Style {
background: Some(Background::Color(color)),
text_color,
border: Border {
radius: CORNER_RADIUS.into(),
width: 0.0,
color: Color::TRANSPARENT,
},
shadow: Shadow {
color: Color { a: 0.1, ..Color::BLACK },
offset: Vector::new(0.0, 1.0),
blur_radius: 2.0,
},
};
let base_style = modern_base(colors.teal, Color::WHITE);
match status {
ButtonStatus::Active => base_style,
ButtonStatus::Hovered => button_hover_style(base_style, is_dark),
ButtonStatus::Pressed => button_pressed_style(base_style, is_dark),
ButtonStatus::Disabled => button_disabled_style(base_style),
}
}
}
pub fn indigo_button<'a>() -> impl Fn(&Theme, ButtonStatus) -> button::Style + 'a {
move |theme, status| {
let colors = get_theme_colors(theme);
let is_dark = is_dark_mode(theme);
let modern_base = |color: Color, text_color: Color| button::Style {
background: Some(Background::Color(color)),
text_color,
border: Border {
radius: CORNER_RADIUS.into(),
width: 0.0,
color: Color::TRANSPARENT,
},
shadow: Shadow {
color: Color { a: 0.1, ..Color::BLACK },
offset: Vector::new(0.0, 1.0),
blur_radius: 2.0,
},
};
let base_style = modern_base(colors.indigo, Color::WHITE);
match status {
ButtonStatus::Active => base_style,
ButtonStatus::Hovered => button_hover_style(base_style, is_dark),
ButtonStatus::Pressed => button_pressed_style(base_style, is_dark),
ButtonStatus::Disabled => button_disabled_style(base_style),
}
}
}
pub fn purple_button<'a>() -> impl Fn(&Theme, ButtonStatus) -> button::Style + 'a {
move |theme, status| {
let colors = get_theme_colors(theme);
let is_dark = is_dark_mode(theme);
let modern_base = |color: Color, text_color: Color| button::Style {
background: Some(Background::Color(color)),
text_color,
border: Border {
radius: CORNER_RADIUS.into(),
width: 0.0,
color: Color::TRANSPARENT,
},
shadow: Shadow {
color: Color { a: 0.1, ..Color::BLACK },
offset: Vector::new(0.0, 1.0),
blur_radius: 2.0,
},
};
let base_style = modern_base(colors.purple, Color::WHITE);
match status {
ButtonStatus::Active => base_style,
ButtonStatus::Hovered => button_hover_style(base_style, is_dark),
ButtonStatus::Pressed => button_pressed_style(base_style, is_dark),
ButtonStatus::Disabled => button_disabled_style(base_style),
}
}
}
pub fn pink_button<'a>() -> impl Fn(&Theme, ButtonStatus) -> button::Style + 'a {
move |theme, status| {
let colors = get_theme_colors(theme);
let is_dark = is_dark_mode(theme);
let modern_base = |color: Color, text_color: Color| button::Style {
background: Some(Background::Color(color)),
text_color,
border: Border {
radius: CORNER_RADIUS.into(),
width: 0.0,
color: Color::TRANSPARENT,
},
shadow: Shadow {
color: Color { a: 0.1, ..Color::BLACK },
offset: Vector::new(0.0, 1.0),
blur_radius: 2.0,
},
};
let base_style = modern_base(colors.pink, Color::WHITE);
match status {
ButtonStatus::Active => base_style,
ButtonStatus::Hovered => button_hover_style(base_style, is_dark),
ButtonStatus::Pressed => button_pressed_style(base_style, is_dark),
ButtonStatus::Disabled => button_disabled_style(base_style),
}
}
}
pub fn gray_button<'a>() -> impl Fn(&Theme, ButtonStatus) -> button::Style + 'a {
move |theme, status| {
let colors = get_theme_colors(theme);
let is_dark = is_dark_mode(theme);
let gray_color = if is_dark {
colors::gray::GRAY3_DARK
} else {
colors::gray::GRAY4_LIGHT
};
let modern_base = |color: Color, text_color: Color| button::Style {
background: Some(Background::Color(color)),
text_color,
border: Border {
radius: CORNER_RADIUS.into(),
width: 0.0,
color: Color::TRANSPARENT,
},
shadow: Shadow {
color: Color { a: 0.1, ..Color::BLACK },
offset: Vector::new(0.0, 1.0),
blur_radius: 2.0,
},
};
let base_style = modern_base(gray_color, colors.text);
match status {
ButtonStatus::Active => base_style,
ButtonStatus::Hovered => button_hover_style(base_style, is_dark),
ButtonStatus::Pressed => button_pressed_style(base_style, is_dark),
ButtonStatus::Disabled => button_disabled_style(base_style),
}
}
}
pub fn tinted_button<'a>(color_variant: TintedButtonColor) -> impl Fn(&Theme, ButtonStatus) -> button::Style + 'a {
move |theme, status| {
let colors = get_theme_colors(theme);
let is_dark = is_dark_mode(theme);
let (base_color, text_color) = match color_variant {
TintedButtonColor::Blue => (colors.blue, Color::WHITE),
TintedButtonColor::Green => (colors.green, Color::WHITE),
TintedButtonColor::Red => (colors.red, Color::WHITE),
TintedButtonColor::Orange => (colors.orange, Color::WHITE),
TintedButtonColor::Purple => (colors.purple, Color::WHITE),
TintedButtonColor::Teal => (colors.teal, Color::WHITE),
TintedButtonColor::Pink => (colors.pink, Color::WHITE),
TintedButtonColor::Indigo => (colors.indigo, Color::WHITE),
};
let tinted_color = Color {
r: base_color.r,
g: base_color.g,
b: base_color.b,
a: 0.2, };
let modern_base = |color: Color, text_color: Color| button::Style {
background: Some(Background::Color(color)),
text_color,
border: Border {
radius: CORNER_RADIUS.into(),
width: 0.0,
color: Color::TRANSPARENT,
},
shadow: Shadow {
color: Color { a: 0.05, ..Color::BLACK }, offset: Vector::new(0.0, 1.0),
blur_radius: 2.0,
},
};
let base_style = modern_base(tinted_color, base_color);
match status {
ButtonStatus::Active => base_style,
ButtonStatus::Hovered => button_hover_style(base_style, is_dark),
ButtonStatus::Pressed => button_pressed_style(base_style, is_dark),
ButtonStatus::Disabled => button_disabled_style(base_style),
}
}
}
pub fn blue_tinted_button<'a>() -> impl Fn(&Theme, ButtonStatus) -> button::Style + 'a {
Self::tinted_button(TintedButtonColor::Blue)
}
pub fn green_tinted_button<'a>() -> impl Fn(&Theme, ButtonStatus) -> button::Style + 'a {
Self::tinted_button(TintedButtonColor::Green)
}
pub fn red_tinted_button<'a>() -> impl Fn(&Theme, ButtonStatus) -> button::Style + 'a {
Self::tinted_button(TintedButtonColor::Red)
}
pub fn orange_tinted_button<'a>() -> impl Fn(&Theme, ButtonStatus) -> button::Style + 'a {
Self::tinted_button(TintedButtonColor::Orange)
}
pub fn purple_tinted_button<'a>() -> impl Fn(&Theme, ButtonStatus) -> button::Style + 'a {
Self::tinted_button(TintedButtonColor::Purple)
}
pub fn teal_tinted_button<'a>() -> impl Fn(&Theme, ButtonStatus) -> button::Style + 'a {
Self::tinted_button(TintedButtonColor::Teal)
}
pub fn indigo_tinted_button<'a>() -> impl Fn(&Theme, ButtonStatus) -> button::Style + 'a {
Self::tinted_button(TintedButtonColor::Indigo)
}
pub fn pink_tinted_button<'a>() -> impl Fn(&Theme, ButtonStatus) -> button::Style + 'a {
Self::tinted_button(TintedButtonColor::Pink)
}
pub fn sized_button<'a>(
style_fn: impl Fn(&Theme, ButtonStatus) -> button::Style + 'a,
size: ButtonSize
) -> impl Fn(&Theme, ButtonStatus) -> button::Style + 'a {
move |theme, status| {
let mut base_style = style_fn(theme, status);
base_style.border = Border {
radius: match size {
ButtonSize::Small => (CORNER_RADIUS * 0.8).into(),
ButtonSize::Medium => CORNER_RADIUS.into(),
ButtonSize::Large => (CORNER_RADIUS * 1.2).into(),
},
..base_style.border
};
base_style
}
}
pub fn separated_container<'a>() -> impl Fn(&Theme) -> container::Style + 'a {
move |theme| {
let colors = get_theme_colors(theme);
container::Style {
text_color: Some(colors.text),
background: Some(Background::Color(colors.background)),
border: Border {
radius: 0.0.into(),
width: 0.0,
color: Color::TRANSPARENT,
},
shadow: Shadow::default(),
}
}
}
pub fn accent_container<'a>() -> impl Fn(&Theme) -> container::Style + 'a {
move |theme| {
let colors = get_theme_colors(theme);
container::Style {
text_color: Some(colors.text),
background: Some(Background::Color(colors.background)),
border: Border {
radius: 8.0.into(),
width: 2.0,
color: colors.blue,
},
shadow: Shadow {
color: Color { a: 0.1, ..Color::BLACK },
offset: Vector::new(0.0, 2.0),
blur_radius: 4.0,
},
}
}
}
pub fn toolbar_container<'a>() -> impl Fn(&Theme) -> container::Style + 'a {
move |theme| {
let colors = get_theme_colors(theme);
container::Style {
text_color: Some(colors.text),
background: Some(Background::Color(colors.system_bg)),
border: Border {
radius: 0.0.into(),
width: 0.0,
color: Color::TRANSPARENT,
},
shadow: Shadow {
color: Color { a: 0.05, ..Color::BLACK },
offset: Vector::new(0.0, 1.0),
blur_radius: 2.0,
},
}
}
}
pub fn floating_container<'a>() -> impl Fn(&Theme) -> container::Style + 'a {
move |theme| {
let colors = get_theme_colors(theme);
container::Style {
text_color: Some(colors.text),
background: Some(Background::Color(colors.card_bg)),
border: Border {
radius: 10.0.into(),
width: 0.0,
color: Color::TRANSPARENT,
},
shadow: Shadow {
color: Color { a: 0.25, ..Color::BLACK },
offset: Vector::new(0.0, 4.0),
blur_radius: 16.0,
},
}
}
}
pub fn search_input<'a>() -> impl Fn(&Theme, TextInputStatus) -> text_input::Style + 'a {
move |theme, status| {
let colors = get_theme_colors(theme);
let base_style = text_input::Style {
background: Background::Color(colors.system_bg),
border: Border {
radius: CORNER_RADIUS.into(),
width: 0.0,
color: Color::TRANSPARENT,
},
icon: colors.tertiary_text,
placeholder: colors.placeholder,
value: colors.text,
selection: colors.selection,
};
match status {
TextInputStatus::Active => base_style,
TextInputStatus::Hovered => base_style,
TextInputStatus::Focused => text_input::Style {
background: Background::Color(colors.tertiary_background),
..base_style
},
TextInputStatus::Disabled => text_input::Style {
background: Background::Color(colors.system_bg.scale_alpha(0.7)),
value: colors.text.scale_alpha(0.5),
..base_style
},
}
}
}
pub fn inline_text_input<'a>() -> impl Fn(&Theme, TextInputStatus) -> text_input::Style + 'a {
move |theme, status| {
let colors = get_theme_colors(theme);
let base_style = text_input::Style {
background: Background::Color(Color::TRANSPARENT),
border: Border {
radius: 0.0.into(),
width: 1.0,
color: colors.separator,
},
icon: colors.text,
placeholder: colors.placeholder,
value: colors.text,
selection: colors.selection,
};
match status {
TextInputStatus::Active => base_style,
TextInputStatus::Hovered => text_input::Style {
border: Border {
color: colors.tertiary_text,
..base_style.border
},
..base_style
},
TextInputStatus::Focused => text_input::Style {
border: Border {
color: colors.blue,
width: 2.0,
..base_style.border
},
..base_style
},
TextInputStatus::Disabled => text_input::Style {
border: Border {
color: colors.separator.scale_alpha(0.5),
..base_style.border
},
value: colors.text.scale_alpha(0.5),
..base_style
},
}
}
}
pub fn danger_text_input<'a>() -> impl Fn(&Theme, TextInputStatus) -> text_input::Style + 'a {
move |theme, status| {
let colors = get_theme_colors(theme);
let base_style = text_input_style(theme, status);
text_input::Style {
border: Border {
color: colors.red, width: 1.0,
..base_style.border
},
background: Background::Color(
if is_dark_mode(theme) {
Color { r: 0.3, g: 0.0, b: 0.0, a: 0.2 }
} else {
Color { r: 1.0, g: 0.9, b: 0.9, a: 1.0 }
}
),
..base_style
}
}
}
pub fn warning_text_input<'a>() -> impl Fn(&Theme, TextInputStatus) -> text_input::Style + 'a {
move |theme, status| {
let colors = get_theme_colors(theme);
let base_style = text_input_style(theme, status);
text_input::Style {
border: Border {
color: colors.orange, width: 1.0,
..base_style.border
},
..base_style
}
}
}
pub fn primary_text<'a>() -> impl Fn(&Theme) -> text::Style + 'a {
|theme| {
let colors = get_theme_colors(theme);
text::Style {
color: Some(colors.text),
}
}
}
pub fn secondary_text<'a>() -> impl Fn(&Theme) -> text::Style + 'a {
|theme| {
let colors = get_theme_colors(theme);
text::Style {
color: Some(colors.secondary_text),
}
}
}
pub fn tertiary_text<'a>() -> impl Fn(&Theme) -> text::Style + 'a {
|theme| {
let colors = get_theme_colors(theme);
text::Style {
color: Some(colors.tertiary_text),
}
}
}
pub fn link_text<'a>() -> impl Fn(&Theme) -> text::Style + 'a {
|theme| {
let colors = get_theme_colors(theme);
text::Style {
color: Some(colors.link),
}
}
}
pub fn colored_text<'a>(
light_color: Color,
dark_color: Color
) -> impl Fn(&Theme) -> text::Style + 'a {
move |theme| {
let is_dark = is_dark_mode(theme);
let color = if is_dark { dark_color } else { light_color };
text::Style {
color: Some(color),
}
}
}
pub fn red_text<'a>() -> impl Fn(&Theme) -> text::Style + 'a {
Self::colored_text(colors::system::RED, colors::system::RED_DARK)
}
pub fn blue_text<'a>() -> impl Fn(&Theme) -> text::Style + 'a {
Self::colored_text(colors::system::BLUE, colors::system::BLUE_DARK)
}
pub fn green_text<'a>() -> impl Fn(&Theme) -> text::Style + 'a {
Self::colored_text(colors::system::GREEN, colors::system::GREEN_DARK)
}
pub fn orange_text<'a>() -> impl Fn(&Theme) -> text::Style + 'a {
Self::colored_text(colors::system::ORANGE, colors::system::ORANGE_DARK)
}
pub fn yellow_text<'a>() -> impl Fn(&Theme) -> text::Style + 'a {
Self::colored_text(colors::system::YELLOW, colors::system::YELLOW_DARK)
}
pub fn purple_text<'a>() -> impl Fn(&Theme) -> text::Style + 'a {
Self::colored_text(colors::system::PURPLE, colors::system::PURPLE_DARK)
}
pub fn pink_text<'a>() -> impl Fn(&Theme) -> text::Style + 'a {
Self::colored_text(colors::system::PINK, colors::system::PINK_DARK)
}
pub fn teal_text<'a>() -> impl Fn(&Theme) -> text::Style + 'a {
Self::colored_text(colors::system::TEAL, colors::system::TEAL_DARK)
}
pub fn indigo_text<'a>() -> impl Fn(&Theme) -> text::Style + 'a {
Self::colored_text(colors::system::INDIGO, colors::system::INDIGO_DARK)
}
pub fn mint_text<'a>() -> impl Fn(&Theme) -> text::Style + 'a {
Self::colored_text(colors::system::MINT, colors::system::MINT_DARK)
}
pub fn brown_text<'a>() -> impl Fn(&Theme) -> text::Style + 'a {
Self::colored_text(colors::system::BROWN, colors::system::BROWN_DARK)
}
pub fn success_text<'a>() -> impl Fn(&Theme) -> text::Style + 'a {
Self::green_text()
}
pub fn warning_text<'a>() -> impl Fn(&Theme) -> text::Style + 'a {
Self::orange_text()
}
pub fn error_text<'a>() -> impl Fn(&Theme) -> text::Style + 'a {
Self::red_text()
}
pub fn combo_box<'a>() -> impl Fn(&Theme, text_input::Status) -> text_input::Style + 'a {
text_input_style
}
pub fn combo_box_menu<'a>() -> impl Fn(&Theme) -> menu::Style + 'a {
|theme| {
let colors = get_theme_colors(theme);
menu::Style {
text_color: colors.text,
background: Background::Color(colors.card_bg),
border: Border {
radius: TINY_CORNER_RADIUS.into(),
width: 1.0,
color: colors.input_border,
},
selected_text_color: Color::WHITE,
selected_background: Background::Color(colors.blue),
}
}
}
}
fn button_style(theme: &Theme, class: &style::Button, status: ButtonStatus) -> button::Style {
let colors = get_theme_colors(theme);
let is_dark = is_dark_mode(theme);
let modern_base = |color: Color, text_color: Color| button::Style {
background: Some(Background::Color(color)),
text_color,
border: Border {
radius: CORNER_RADIUS.into(), width: 0.0, color: Color::TRANSPARENT,
},
shadow: Shadow {
color: Color { a: 0.1, ..Color::BLACK },
offset: Vector::new(0.0, 1.0),
blur_radius: 2.0,
},
};
let outlined = |color: Color, text_color: Color| button::Style {
background: Some(Background::Color(Color::TRANSPARENT)),
text_color,
border: Border {
radius: CORNER_RADIUS.into(),
width: 1.0,
color,
},
shadow: Shadow::default(),
};
let transparent = |text_color: Color| button::Style {
background: Some(Background::Color(Color::TRANSPARENT)),
text_color,
border: Border::default(),
shadow: Shadow::default(),
};
let base_style = match class {
style::Button::Primary => modern_base(colors.blue, Color::WHITE),
style::Button::Secondary => outlined(colors.blue, colors.blue),
style::Button::Success => modern_base(colors.green, Color::WHITE),
style::Button::Warning => modern_base(colors.orange, if is_dark { Color::BLACK } else { Color::WHITE }),
style::Button::Danger => modern_base(colors.red, Color::WHITE),
style::Button::Link => transparent(colors.blue),
style::Button::System => modern_base(colors.system_bg, colors.text),
style::Button::Plain => transparent(colors.text),
};
match status {
ButtonStatus::Active => base_style,
ButtonStatus::Hovered => {
let adjust_color = |color: Color| -> Color {
if is_dark {
Color {
r: (color.r + 0.05).min(1.0),
g: (color.g + 0.05).min(1.0),
b: (color.b + 0.05).min(1.0),
a: color.a,
}
} else {
Color {
r: (color.r - 0.05).max(0.0),
g: (color.g - 0.05).max(0.0),
b: (color.b - 0.05).max(0.0),
a: color.a,
}
}
};
match class {
style::Button::Link | style::Button::Plain => {
button::Style {
text_color: base_style.text_color.scale_alpha(0.8),
..base_style
}
},
_ => {
if let Some(Background::Color(color)) = base_style.background {
button::Style {
background: Some(Background::Color(adjust_color(color))),
..base_style
}
} else {
base_style
}
}
}
},
ButtonStatus::Pressed => {
let adjust_color = |color: Color| -> Color {
if is_dark {
Color {
r: (color.r + 0.1).min(1.0),
g: (color.g + 0.1).min(1.0),
b: (color.b + 0.1).min(1.0),
a: color.a,
}
} else {
Color {
r: (color.r - 0.1).max(0.0),
g: (color.g - 0.1).max(0.0),
b: (color.b - 0.1).max(0.0),
a: color.a,
}
}
};
let mut pressed_style = base_style;
pressed_style.shadow = Shadow::default();
match class {
style::Button::Link | style::Button::Plain => {
pressed_style.text_color = base_style.text_color.scale_alpha(0.6);
pressed_style
},
_ => {
if let Some(Background::Color(color)) = base_style.background {
pressed_style.background = Some(Background::Color(adjust_color(color)));
}
pressed_style
}
}
},
ButtonStatus::Disabled => {
button::Style {
background: base_style.background.map(|bg| match bg {
Background::Color(color) => Background::Color(color.scale_alpha(0.5)),
_ => bg,
}),
text_color: base_style.text_color.scale_alpha(0.5),
border: Border {
color: base_style.border.color.scale_alpha(0.5),
..base_style.border
},
shadow: Shadow::default(), }
},
}
}