use iced::{
button, checkbox, container, pick_list, progress_bar, radio, rule, scrollable, slider,
text_input, Color,
};
pub const MESSAGE_TIMESTAMP_SIZE: u16 = 13;
pub const MESSAGE_SIZE: u16 = 16;
pub const MESSAGE_SENDER_SIZE: u16 = 19;
pub const DATE_SEPERATOR_SIZE: u16 = 22;
pub const PADDING: u16 = 16;
pub const SPACING: u16 = 4;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Theme {
Light,
Dark,
}
impl Theme {
const SENDER_COLORS: [Color; 8] = [
Color::from_rgb(
0x6d as f32 / 255.0,
0xdd as f32 / 255.0,
0x18 as f32 / 255.0,
),
Color::from_rgb(
0xfc as f32 / 255.0,
0xd2 as f32 / 255.0,
0x00 as f32 / 255.0,
),
Color::from_rgb(
0xcc as f32 / 255.0,
0xf9 as f32 / 255.0,
0xff as f32 / 255.0,
),
Color::from_rgb(
0x3d as f32 / 255.0,
0xdb as f32 / 255.0,
0x8c as f32 / 255.0,
),
Color::from_rgb(
0xdd as f32 / 255.0,
0x6a as f32 / 255.0,
0x35 as f32 / 255.0,
),
Color::from_rgb(
0xe2 as f32 / 255.0,
0x22 as f32 / 255.0,
0x45 as f32 / 255.0,
),
Color::from_rgb(
0x09 as f32 / 255.0,
0xe5 as f32 / 255.0,
0x38 as f32 / 255.0,
),
Color::from_rgb(
0xd1 as f32 / 255.0,
0x32 as f32 / 255.0,
0x71 as f32 / 255.0,
),
];
pub fn calculate_sender_color(&self, name_len: usize) -> Color {
Theme::SENDER_COLORS[name_len % Theme::SENDER_COLORS.len()]
}
}
impl Default for Theme {
fn default() -> Theme {
Theme::Dark
}
}
impl From<Theme> for Box<dyn container::StyleSheet> {
fn from(theme: Theme) -> Self {
match theme {
Theme::Light => Default::default(),
Theme::Dark => dark::Container.into(),
}
}
}
impl From<Theme> for Box<dyn radio::StyleSheet> {
fn from(theme: Theme) -> Self {
match theme {
Theme::Light => Default::default(),
Theme::Dark => dark::Radio.into(),
}
}
}
impl From<Theme> for Box<dyn text_input::StyleSheet> {
fn from(theme: Theme) -> Self {
match theme {
Theme::Light => Default::default(),
Theme::Dark => dark::TextInput.into(),
}
}
}
impl From<Theme> for Box<dyn button::StyleSheet> {
fn from(theme: Theme) -> Self {
match theme {
Theme::Light => light::Button.into(),
Theme::Dark => dark::Button.into(),
}
}
}
impl From<Theme> for Box<dyn scrollable::StyleSheet> {
fn from(theme: Theme) -> Self {
match theme {
Theme::Light => Default::default(),
Theme::Dark => dark::Scrollable.into(),
}
}
}
impl From<Theme> for Box<dyn slider::StyleSheet> {
fn from(theme: Theme) -> Self {
match theme {
Theme::Light => Default::default(),
Theme::Dark => dark::Slider.into(),
}
}
}
impl From<Theme> for Box<dyn progress_bar::StyleSheet> {
fn from(theme: Theme) -> Self {
match theme {
Theme::Light => Default::default(),
Theme::Dark => dark::ProgressBar.into(),
}
}
}
impl From<Theme> for Box<dyn checkbox::StyleSheet> {
fn from(theme: Theme) -> Self {
match theme {
Theme::Light => Default::default(),
Theme::Dark => dark::Checkbox.into(),
}
}
}
impl From<Theme> for Box<dyn pick_list::StyleSheet> {
fn from(theme: Theme) -> Self {
match theme {
Theme::Light => Default::default(),
Theme::Dark => dark::PickList.into(),
}
}
}
impl From<Theme> for Box<dyn rule::StyleSheet> {
fn from(theme: Theme) -> Self {
match theme {
Theme::Light => Default::default(),
Theme::Dark => dark::Rule.into(),
}
}
}
pub struct BrightContainer;
impl From<BrightContainer> for Box<dyn container::StyleSheet> {
fn from(_: BrightContainer) -> Self {
dark::BrightContainer.into()
}
}
pub struct RoundContainer;
impl From<RoundContainer> for Box<dyn container::StyleSheet> {
fn from(_: RoundContainer) -> Self {
dark::RoundContainer.into()
}
}
pub struct DarkTextInput;
impl From<DarkTextInput> for Box<dyn text_input::StyleSheet> {
fn from(_: DarkTextInput) -> Self {
dark::DarkTextInput.into()
}
}
pub struct DarkButton;
impl From<DarkButton> for Box<dyn button::StyleSheet> {
fn from(_: DarkButton) -> Self {
dark::DarkButton.into()
}
}
pub struct TransparentButton;
impl From<TransparentButton> for Box<dyn button::StyleSheet> {
fn from(_: TransparentButton) -> Self {
dark::TransparentButton.into()
}
}
mod light {
use iced::{button, Color, Vector};
pub struct Button;
impl button::StyleSheet for Button {
fn active(&self) -> button::Style {
button::Style {
background: Color::from_rgb(0.11, 0.42, 0.87).into(),
border_radius: 12.0,
shadow_offset: Vector::new(1.0, 1.0),
text_color: Color::from_rgb8(0xEE, 0xEE, 0xEE),
..button::Style::default()
}
}
fn hovered(&self) -> button::Style {
button::Style {
text_color: Color::WHITE,
shadow_offset: Vector::new(1.0, 2.0),
..self.active()
}
}
}
}
mod dark {
use iced::{
button, checkbox, container, pick_list, progress_bar, radio, rule, scrollable, slider,
text_input, Color,
};
const DARK_BG: Color = Color::from_rgb(
0x36 as f32 / 255.0,
0x39 as f32 / 255.0,
0x3F as f32 / 255.0,
);
const BRIGHT_BG: Color = Color::from_rgb(
0x44 as f32 / 255.0,
0x48 as f32 / 255.0,
0x4F as f32 / 255.0,
);
const ACCENT: Color = Color::from_rgb(
0x60 as f32 / 255.0,
0x64 as f32 / 255.0,
0x6B as f32 / 255.0,
);
pub struct Container;
impl container::StyleSheet for Container {
fn style(&self) -> container::Style {
container::Style {
background: DARK_BG.into(),
text_color: Color::WHITE.into(),
..container::Style::default()
}
}
}
pub struct RoundContainer;
impl container::StyleSheet for RoundContainer {
fn style(&self) -> container::Style {
container::Style {
border_color: DARK_BG,
border_radius: 8.0,
border_width: 1.0,
..Container.style()
}
}
}
pub struct BrightContainer;
impl container::StyleSheet for BrightContainer {
fn style(&self) -> container::Style {
container::Style {
background: BRIGHT_BG.into(),
..Container.style()
}
}
}
pub struct Radio;
impl radio::StyleSheet for Radio {
fn active(&self) -> radio::Style {
radio::Style {
background: BRIGHT_BG.into(),
dot_color: ACCENT,
border_width: 1.0,
border_color: ACCENT,
}
}
fn hovered(&self) -> radio::Style {
radio::Style {
background: Color {
a: 0.5,
..BRIGHT_BG
}
.into(),
..self.active()
}
}
}
pub struct DarkTextInput;
impl text_input::StyleSheet for DarkTextInput {
fn active(&self) -> text_input::Style {
text_input::Style {
background: DARK_BG.into(),
..TextInput.active()
}
}
fn focused(&self) -> text_input::Style {
text_input::Style {
border_width: 3.0,
border_color: ACCENT,
..self.active()
}
}
fn placeholder_color(&self) -> Color {
Color::from_rgb(0.4, 0.4, 0.4)
}
fn value_color(&self) -> Color {
Color::WHITE
}
fn selection_color(&self) -> Color {
ACCENT
}
fn hovered(&self) -> text_input::Style {
text_input::Style {
border_width: 2.0,
border_color: Color { a: 0.5, ..ACCENT },
..self.focused()
}
}
}
pub struct TextInput;
impl text_input::StyleSheet for TextInput {
fn active(&self) -> text_input::Style {
text_input::Style {
background: BRIGHT_BG.into(),
border_radius: 8.0,
border_width: 0.0,
border_color: ACCENT,
}
}
fn focused(&self) -> text_input::Style {
text_input::Style {
border_width: 3.0,
border_color: ACCENT,
..self.active()
}
}
fn placeholder_color(&self) -> Color {
Color::from_rgb(0.6, 0.6, 0.6)
}
fn value_color(&self) -> Color {
Color::WHITE
}
fn selection_color(&self) -> Color {
ACCENT
}
fn hovered(&self) -> text_input::Style {
text_input::Style {
border_width: 2.0,
border_color: Color { a: 0.5, ..ACCENT },
..self.focused()
}
}
}
pub struct DarkButton;
impl button::StyleSheet for DarkButton {
fn active(&self) -> button::Style {
button::Style {
background: DARK_BG.into(),
border_radius: 8.0,
text_color: Color::WHITE,
..button::Style::default()
}
}
fn hovered(&self) -> button::Style {
button::Style {
background: ACCENT.into(),
..self.active()
}
}
fn pressed(&self) -> button::Style {
button::Style {
border_width: 1.0,
border_color: Color::WHITE,
..self.hovered()
}
}
fn disabled(&self) -> button::Style {
self.hovered()
}
}
pub struct TransparentButton;
impl button::StyleSheet for TransparentButton {
fn active(&self) -> button::Style {
button::Style {
background: None,
border_color: Color::TRANSPARENT,
border_radius: 0.0,
border_width: 0.0,
text_color: Color::WHITE,
..button::Style::default()
}
}
fn hovered(&self) -> button::Style {
self.active()
}
fn pressed(&self) -> button::Style {
self.active()
}
fn disabled(&self) -> button::Style {
self.active()
}
}
pub struct Button;
impl button::StyleSheet for Button {
fn active(&self) -> button::Style {
button::Style {
background: BRIGHT_BG.into(),
border_radius: 8.0,
text_color: Color::WHITE,
..button::Style::default()
}
}
fn hovered(&self) -> button::Style {
button::Style {
background: ACCENT.into(),
..self.active()
}
}
fn pressed(&self) -> button::Style {
button::Style {
border_width: 1.0,
border_color: Color::WHITE,
..self.hovered()
}
}
fn disabled(&self) -> button::Style {
self.hovered()
}
}
pub struct Scrollable;
impl scrollable::StyleSheet for Scrollable {
fn active(&self) -> scrollable::Scrollbar {
scrollable::Scrollbar {
background: Color::TRANSPARENT.into(),
border_radius: 2.0,
border_width: 0.0,
border_color: Color::TRANSPARENT,
scroller: scrollable::Scroller {
color: Color::TRANSPARENT,
border_radius: 2.0,
border_width: 0.0,
border_color: Color::TRANSPARENT,
},
}
}
fn hovered(&self) -> scrollable::Scrollbar {
let active = self.active();
scrollable::Scrollbar {
background: Color {
a: 0.5,
..BRIGHT_BG
}
.into(),
scroller: scrollable::Scroller {
color: ACCENT,
..active.scroller
},
..active
}
}
fn dragging(&self) -> scrollable::Scrollbar {
let hovered = self.hovered();
scrollable::Scrollbar {
scroller: scrollable::Scroller {
color: Color::from_rgb(0.85, 0.85, 0.85),
..hovered.scroller
},
..hovered
}
}
}
pub struct Slider;
impl slider::StyleSheet for Slider {
fn active(&self) -> slider::Style {
slider::Style {
rail_colors: (ACCENT, Color { a: 0.1, ..ACCENT }),
handle: slider::Handle {
shape: slider::HandleShape::Circle { radius: 9.0 },
color: ACCENT,
border_width: 0.0,
border_color: Color::TRANSPARENT,
},
}
}
fn hovered(&self) -> slider::Style {
let active = self.active();
slider::Style {
handle: slider::Handle {
color: ACCENT,
..active.handle
},
..active
}
}
fn dragging(&self) -> slider::Style {
let active = self.active();
slider::Style {
handle: slider::Handle {
color: Color::from_rgb(0.85, 0.85, 0.85),
..active.handle
},
..active
}
}
}
pub struct ProgressBar;
impl progress_bar::StyleSheet for ProgressBar {
fn style(&self) -> progress_bar::Style {
progress_bar::Style {
background: BRIGHT_BG.into(),
bar: ACCENT.into(),
border_radius: 10.0,
}
}
}
pub struct Checkbox;
impl checkbox::StyleSheet for Checkbox {
fn active(&self, is_checked: bool) -> checkbox::Style {
checkbox::Style {
background: if is_checked { ACCENT } else { BRIGHT_BG }.into(),
checkmark_color: Color::WHITE,
border_radius: 2.0,
border_width: 1.0,
border_color: ACCENT,
}
}
fn hovered(&self, is_checked: bool) -> checkbox::Style {
checkbox::Style {
background: Color {
a: 0.8,
..if is_checked { ACCENT } else { BRIGHT_BG }
}
.into(),
..self.active(is_checked)
}
}
}
pub struct PickList;
impl pick_list::StyleSheet for PickList {
fn menu(&self) -> pick_list::Menu {
pick_list::Menu {
background: BRIGHT_BG.into(),
text_color: Color::WHITE,
selected_background: ACCENT.into(),
selected_text_color: Color::WHITE,
border_width: 0.0,
..pick_list::Menu::default()
}
}
fn active(&self) -> pick_list::Style {
pick_list::Style {
background: DARK_BG.into(),
text_color: Color::WHITE,
border_width: 0.0,
..pick_list::Style::default()
}
}
fn hovered(&self) -> pick_list::Style {
pick_list::Style {
background: ACCENT.into(),
..self.active()
}
}
}
pub struct Rule;
impl rule::StyleSheet for Rule {
fn style(&self) -> rule::Style {
rule::Style {
color: BRIGHT_BG,
width: 2,
radius: 1.0,
fill_mode: rule::FillMode::Padded(15),
}
}
}
}