first_push

This commit is contained in:
Your Name 2025-04-27 14:44:33 +02:00
commit ca41081e8f
18 changed files with 5559 additions and 0 deletions

4215
Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

29
Cargo.toml Normal file
View file

@ -0,0 +1,29 @@
[package]
name = "iced-material"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
palette = "*"
freedesktop-icons = "*"
[dependencies.iced]
#git = "https://github.com/ibaryshnikov/iced.git"
#rev = "901bbeb"
features = ["wgpu","lazy","svg","advanced"]
path = "../../iced"
[dependencies.iced_winit]
#git = "https://github.com/ibaryshnikov/iced.git"
#rev = "901bbeb"
path = "../../iced/winit"
[dependencies.iced_wgpu]
#git = "https://github.com/ibaryshnikov/iced.git"
#rev = "901bbeb"
path = "../../iced/wgpu"
#iced = {git="https://github.com/iced-rs/iced",features=["lazy","svg","advanced"]}

71
src/header.rs Normal file
View file

@ -0,0 +1,71 @@
use crate::iced_material;
use crate::icon;
use iced::{
alignment::Alignment,
widget::{button, container, row, text, Space},
Element, Length, Padding,
};
//pub const fun stri
pub fn header<'a, Message, Renderer>(
side_button: Message,
back_message: Message,
settings_button: Message,
exit_button: Message,
//page: &Page,
title: &str,
) -> Element<'a, Message, crate::theme::Theme, Renderer>
where
Message: 'a + Clone,
//Theme: 'a + text::Catalog + button::Catalog + svg::Catalog + container::Catalog,
Renderer: 'a + iced::advanced::text::Renderer + iced::advanced::svg::Renderer,
{
container(
row![
row!(
button(icon!("panel-left"))
.padding(4)
.width(30)
.height(30)
.on_press(side_button),
button(icon!("chevron-left"))
.padding(4)
.width(30)
.height(30)
.on_press(back_message),
)
.spacing(10),
Space::with_width(Length::Fill),
text!("{}", title),
Space::with_width(Length::Fill),
row!(
button(icon!("settings"))
.padding(4)
.on_press(settings_button)
.width(30)
.height(30),
button(icon!("x"))
.padding(4)
.on_press(exit_button)
.width(30)
.height(30)
)
.spacing(10),
]
.padding({
if cfg!(target_os = "android") {
Padding {
top: 40.,
left: 10.,
right: 10.,
bottom: 10.,
}
} else {
Padding::new(10.)
}
})
.align_y(Alignment::Center)
.spacing(10),
)
.style(crate::theme::container::grey)
.into()
}

19
src/lib.rs Normal file
View file

@ -0,0 +1,19 @@
use iced::widget::{svg, Svg};
pub mod header;
pub mod sidebar;
pub mod theme;
pub fn svg_icon<'a>(bytes: &'static [u8]) -> Svg<'a, theme::Theme> {
svg(svg::Handle::from_memory(bytes))
}
use crate as iced_material;
#[macro_export]
macro_rules! icon {
($message_id:literal) => {{
iced_material::svg_icon(include_bytes!(concat!(
concat!("/home/me/workspace/lucide/icons/", $message_id),
".svg"
)))
}};
}

51
src/sidebar.rs Normal file
View file

@ -0,0 +1,51 @@
use iced::{
alignment::Alignment,
widget::{button, column, container, scrollable, text},
Element, Length,
};
pub fn sidebar<'a, Message, Renderer>(
names: &[&str],
f: impl Fn(u8) -> Message + 'a,
) -> Element<'a, Message, crate::theme::Theme, Renderer>
where
Message: 'a + Clone,
// Theme: 'a
// + text::Catalog
// + button::Catalog
// + svg::Catalog
// + container::Catalog
// + scrollable::Catalog,
Renderer: 'a + iced::advanced::text::Renderer,
{
let options: Vec<Element<'_, _, _, _>> = names
.iter()
.enumerate()
.map(|(i, title)| {
// hey
button(container(text!("{}", title)).center_y(Length::Fill))
.width(Length::Fill)
.height(50)
.on_press(f(i as u8))
// .style(move |theme, status| {
// if i as u8 == self.selected {
// text_button(theme, button::Status::Pressed)
// } else {
// text_button(theme, status)
// }
// })
.into()
})
.collect();
container(scrollable(
column(options)
.padding(10)
.align_x(Alignment::Center)
.spacing(10),
))
.height(Length::Fill)
.center_x(Length::Fill)
.style(crate::theme::container::light_grey)
.into()
//
}

366
src/theme/button.rs Normal file
View file

@ -0,0 +1,366 @@
use iced::widget::button::{Catalog, Status, Style, StyleFn};
use iced::{Background, Border, Color};
use super::Theme;
impl Catalog for Theme {
type Class<'a> = StyleFn<'a, Self>;
fn default<'a>() -> Self::Class<'a> {
Box::new(text_button)
}
fn style(&self, class: &Self::Class<'_>, status: Status) -> Style {
class(self, status)
}
}
pub fn text_button(theme: &Theme, status: Status) -> Style {
match status {
Status::Active | Status::Pressed => Style {
background: Some(Background::Color(theme.colors().text.high_alpha)),
text_color: theme.colors().text.base,
border: Border {
radius: 10.0.into(),
..Default::default()
},
..Default::default()
},
Status::Hovered => Style {
background: Some(Background::Color(theme.colors().text.med_alpha)),
text_color: theme.colors().text.base,
border: Border {
radius: 10.0.into(),
..Default::default()
},
..Default::default()
},
Status::Disabled => {
let active = text_button(theme, Status::Active);
Style {
text_color: Color {
a: 0.2,
..active.text_color
},
border: Border {
radius: 10.0.into(),
color: Color {
a: 0.2,
..active.text_color
},
..Default::default()
},
..active
}
}
}
}
pub fn icon_button(theme: &Theme, status: Status) -> Style {
match status {
Status::Active | Status::Pressed => Style {
background: Some(Background::Color(theme.colors().text.high_alpha)),
text_color: theme.colors().text.base,
border: Border {
radius: 10.0.into(),
width: 0.0.into(),
..Default::default()
},
..Default::default()
},
Status::Hovered => Style {
background: Some(Background::Color(theme.colors().text.med_alpha)),
text_color: theme.colors().text.base,
border: Border {
radius: 10.0.into(),
width: 0.0.into(),
..Default::default()
},
..Default::default()
},
Status::Disabled => {
let active = text_button(theme, Status::Active);
Style {
text_color: Color {
a: 0.2,
..active.text_color
},
border: Border {
color: Color {
a: 0.2,
..active.text_color
},
width: 0.0.into(),
..Default::default()
},
..active
}
}
}
}
pub fn unselected(theme: &Theme, _status: Status) -> Style {
Style {
background: Some(Background::Color(theme.colors().background.dark)),
text_color: theme.colors().text.base,
border: Border {
radius: 10.0.into(),
..Default::default()
},
..Default::default()
}
}
pub fn selected(theme: &Theme, _status: Status) -> Style {
Style {
background: Some(Background::Color(theme.colors().background.darkest)),
text_color: theme.colors().text.base,
border: Border {
radius: 10.0.into(),
..Default::default()
},
..Default::default()
}
}
pub fn secondary(theme: &Theme, status: Status) -> Style {
match status {
Status::Active | Status::Pressed => Style {
background: Some(Background::Color(theme.colors().accent.high_alpha)),
text_color: theme.colors().accent.base,
border: Border {
radius: 3.0.into(),
..Default::default()
},
..Default::default()
},
Status::Hovered => Style {
background: Some(Background::Color(theme.colors().accent.med_alpha)),
text_color: theme.colors().accent.base,
border: Border {
radius: 3.0.into(),
..Default::default()
},
..Default::default()
},
Status::Disabled => {
let active = secondary(theme, Status::Active);
Style {
text_color: Color {
a: 0.2,
..active.text_color
},
border: Border {
color: Color {
a: 0.2,
..active.text_color
},
..Default::default()
},
..active
}
}
}
}
pub fn tertiary(theme: &Theme, status: Status, selected: bool) -> Style {
match status {
Status::Active | Status::Pressed => Style {
background: Some(Background::Color(if selected {
theme.colors().action.med_alpha
} else {
theme.colors().background.dark
})),
border: Border {
color: if selected {
theme.colors().action.low_alpha
} else if theme.colors().is_dark_theme() {
theme.colors().background.lightest
} else {
theme.colors().background.darkest
},
width: 1.0,
radius: 3.0.into(),
},
..Default::default()
},
Status::Hovered => {
let active = tertiary(theme, Status::Active, selected);
Style {
background: Some(Background::Color(if selected {
theme.colors().action.high_alpha
} else if theme.colors().is_dark_theme() {
theme.colors().background.light
} else {
theme.colors().background.darker
})),
..active
}
}
Status::Disabled => {
let active = tertiary(theme, Status::Active, selected);
Style {
text_color: Color {
a: 0.2,
..active.text_color
},
border: Border {
color: Color {
a: 0.2,
..active.text_color
},
..Default::default()
},
..active
}
}
}
}
pub fn context(theme: &Theme, status: Status) -> Style {
match status {
Status::Active | Status::Pressed => Style {
background: Some(Background::Color(Color::TRANSPARENT)),
border: Border {
radius: 4.0.into(),
..Default::default()
},
..Default::default()
},
Status::Hovered => {
let active = context(theme, Status::Active);
Style {
background: Some(Background::Color(theme.colors().background.darker)),
..active
}
}
Status::Disabled => {
let active = context(theme, Status::Active);
Style {
text_color: Color {
a: 0.2,
..active.text_color
},
border: Border {
color: Color {
a: 0.2,
..active.text_color
},
..Default::default()
},
..active
}
}
}
}
pub fn bare(_theme: &Theme, status: Status) -> Style {
match status {
Status::Active | Status::Pressed | Status::Hovered => Style {
background: Some(Background::Color(Color::TRANSPARENT)),
..Default::default()
},
Status::Disabled => {
let active = bare(_theme, Status::Active);
Style {
text_color: Color {
a: 0.2,
..active.text_color
},
border: Border {
color: Color {
a: 0.2,
..active.text_color
},
..Default::default()
},
..active
}
}
}
}
pub fn side_menu(theme: &Theme, status: Status) -> Style {
match status {
Status::Active | Status::Pressed => Style {
background: None,
border: Border {
radius: 3.0.into(),
..Default::default()
},
..Default::default()
},
Status::Hovered => {
let active = side_menu(theme, Status::Active);
Style {
background: Some(Background::Color(theme.colors().background.dark)),
..active
}
}
Status::Disabled => {
let active = side_menu(theme, Status::Active);
Style {
text_color: Color {
a: 0.2,
..active.text_color
},
border: Border {
color: Color {
a: 0.2,
..active.text_color
},
..Default::default()
},
..active
}
}
}
}
pub fn side_menu_selected(theme: &Theme, status: Status) -> Style {
match status {
Status::Active | Status::Pressed => Style {
background: Some(Background::Color(theme.colors().background.darker)),
border: Border {
radius: 3.0.into(),
..Default::default()
},
..Default::default()
},
Status::Hovered => {
let active = side_menu_selected(theme, Status::Active);
Style {
background: Some(Background::Color(theme.colors().background.darkest)),
..active
}
}
Status::Disabled => {
let active = side_menu_selected(theme, Status::Active);
Style {
text_color: Color {
a: 0.2,
..active.text_color
},
border: Border {
color: Color {
a: 0.2,
..active.text_color
},
..Default::default()
},
..active
}
}
}
}

33
src/theme/checkbox.rs Normal file
View file

@ -0,0 +1,33 @@
use iced::{
widget::checkbox::{Catalog, Status, Style, StyleFn},
Border,
};
use super::Theme;
impl Catalog for Theme {
type Class<'a> = StyleFn<'a, Self>;
fn default<'a>() -> Self::Class<'a> {
Box::new(primary)
}
fn style(&self, class: &Self::Class<'_>, status: iced::widget::checkbox::Status) -> Style {
class(self, status)
}
}
pub fn primary(theme: &Theme, status: Status) -> Style {
match status {
_ => Style {
background: iced::Background::Color(theme.colors().background.base),
icon_color: theme.colors().accent.base,
border: Border {
color: theme.colors().accent.base,
width: 1.0,
radius: 2.into(),
},
text_color: Some(theme.colors().text.base),
},
}
}

147
src/theme/container.rs Normal file
View file

@ -0,0 +1,147 @@
use iced::widget::container::{transparent, Catalog, Style, StyleFn};
use iced::{Background, Border, Color};
use super::Theme;
impl Catalog for Theme {
type Class<'a> = StyleFn<'a, Self>;
fn default<'a>() -> Self::Class<'a> {
Box::new(transparent)
}
fn style(&self, class: &Self::Class<'_>) -> Style {
class(self)
}
}
pub fn grey(theme: &Theme) -> Style {
let background = theme.colors().background.darker;
Style {
background: Some(Background::Color(background)),
text_color: Some(theme.colors().text.base),
..Default::default()
}
}
pub fn light_grey(theme: &Theme) -> Style {
let background = theme.colors().background.dark;
Style {
background: Some(Background::Color(background)),
text_color: Some(theme.colors().text.base),
..Default::default()
}
}
pub fn primary(theme: &Theme) -> Style {
Style {
background: Some(Background::Color(theme.colors().background.base)),
text_color: Some(theme.colors().text.base),
..Default::default()
}
}
pub fn pane_body(theme: &Theme) -> Style {
Style {
background: Some(Background::Color(theme.colors().background.dark)),
border: Border {
radius: 4.0.into(),
width: 1.0,
color: Color::TRANSPARENT,
},
..Default::default()
}
}
pub fn pane_body_selected(theme: &Theme) -> Style {
let pane_body = pane_body(theme);
Style {
border: Border {
color: theme.colors().action.base,
..pane_body.border
},
..pane_body
}
}
pub fn command(_theme: &Theme) -> Style {
Style {
background: None,
..Default::default()
}
}
pub fn command_selected(theme: &Theme) -> Style {
Style {
background: Some(Background::Color(theme.colors().background.darker)),
border: Border {
radius: 3.0.into(),
..Default::default()
},
..Default::default()
}
}
pub fn context(theme: &Theme) -> Style {
Style {
//TODO: Blur background when possible?
background: Some(Background::Color(theme.colors().background.base)),
border: Border {
radius: 4.0.into(),
width: 1.0,
color: theme.colors().background.darker,
},
..Default::default()
}
}
pub fn highlight(theme: &Theme) -> Style {
Style {
background: Some(Background::Color(theme.colors().info.high_alpha)),
border: Border {
radius: 0.0.into(),
..Default::default()
},
..Default::default()
}
}
pub fn semi_transparent(theme: &Theme) -> Style {
Style {
background: Some(
Color {
a: 0.80,
..theme.colors().background.base
}
.into(),
),
..Default::default()
}
}
pub fn default_banner(theme: &Theme) -> Style {
Style {
background: Some(Background::Color(theme.colors().background.dark)),
border: Border {
radius: 4.0.into(),
width: 1.0,
color: theme.colors().background.lighter,
},
..Default::default()
}
}
pub fn error_modal(theme: &Theme) -> Style {
Style {
background: Some(Background::Color(theme.colors().background.dark)),
border: Border {
radius: 4.0.into(),
width: 1.0,
color: theme.colors().error.base,
},
..Default::default()
}
}

200
src/theme/data.rs Normal file
View file

@ -0,0 +1,200 @@
use iced::Color;
use palette::rgb::Rgb;
use palette::{DarkenAssign, FromColor, LightenAssign, Mix, Okhsl, Srgb};
#[derive(Debug, Clone)]
pub struct ColorTheme {
pub name: String,
pub colors: Colors,
}
impl ColorTheme {
pub fn new(name: String, palette: &Palette) -> Self {
ColorTheme {
name,
colors: Colors::new(palette),
}
}
}
impl Default for ColorTheme {
fn default() -> Self {
Self {
name: "Light".to_string(),
colors: Colors::new(&Palette::default()),
}
}
}
#[derive(Debug, Clone)]
pub struct Colors {
pub background: Subpalette,
pub text: Subpalette,
pub action: Subpalette,
pub accent: Subpalette,
pub alert: Subpalette,
pub error: Subpalette,
pub info: Subpalette,
pub success: Subpalette,
}
impl Colors {
pub fn new(palette: &Palette) -> Self {
Colors {
background: Subpalette::from_color(palette.background, palette),
text: Subpalette::from_color(palette.text, palette),
action: Subpalette::from_color(palette.action, palette),
accent: Subpalette::from_color(palette.accent, palette),
alert: Subpalette::from_color(palette.alert, palette),
error: Subpalette::from_color(palette.error, palette),
info: Subpalette::from_color(palette.info, palette),
success: Subpalette::from_color(palette.success, palette),
}
}
pub fn is_dark_theme(&self) -> bool {
self.background.is_dark()
}
}
#[derive(Debug, Clone)]
pub struct Subpalette {
pub base: Color,
pub light: Color,
pub lighter: Color,
pub lightest: Color,
pub dark: Color,
pub darker: Color,
pub darkest: Color,
pub low_alpha: Color,
pub med_alpha: Color,
pub high_alpha: Color,
}
impl Subpalette {
pub fn from_color(color: Color, palette: &Palette) -> Subpalette {
let is_dark = is_dark(palette.background);
Subpalette {
base: color,
light: lighten(color, 0.03),
lighter: lighten(color, 0.06),
lightest: lighten(color, 0.12),
dark: darken(color, 0.03),
darker: darken(color, 0.06),
darkest: darken(color, 0.12),
low_alpha: if is_dark {
alpha(color, 0.4)
} else {
alpha(color, 0.8)
},
med_alpha: if is_dark {
alpha(color, 0.2)
} else {
alpha(color, 0.4)
},
high_alpha: if is_dark {
alpha(color, 0.1)
} else {
alpha(color, 0.3)
},
}
}
pub fn is_dark(&self) -> bool {
is_dark(self.base)
}
}
#[derive(Debug, Clone, Copy)]
pub struct Palette {
pub background: Color,
pub text: Color,
pub action: Color,
pub accent: Color,
pub alert: Color,
pub error: Color,
pub info: Color,
pub success: Color,
}
impl Default for Palette {
fn default() -> Palette {
Palette {
background: hex_to_color("#2b292d").unwrap(),
text: hex_to_color("#fecdb2").unwrap(),
action: hex_to_color("#b1b695").unwrap(),
accent: hex_to_color("#d1d1e0").unwrap(),
alert: hex_to_color("#ffa07a").unwrap(),
error: hex_to_color("#e06b75").unwrap(),
info: hex_to_color("#f5d76e").unwrap(),
success: hex_to_color("#b1b695").unwrap(),
}
}
}
pub fn hex_to_color(hex: &str) -> Option<Color> {
if hex.len() == 7 {
let hash = &hex[0..1];
let r = u8::from_str_radix(&hex[1..3], 16);
let g = u8::from_str_radix(&hex[3..5], 16);
let b = u8::from_str_radix(&hex[5..7], 16);
return match (hash, r, g, b) {
("#", Ok(r), Ok(g), Ok(b)) => Some(Color {
r: r as f32 / 255.0,
g: g as f32 / 255.0,
b: b as f32 / 255.0,
a: 1.0,
}),
_ => None,
};
}
None
}
pub fn is_dark(color: Color) -> bool {
to_hsl(color).lightness < 0.5
}
pub fn to_hsl(color: Color) -> Okhsl {
let mut hsl = Okhsl::from_color(Srgb::new(color.r, color.g, color.b));
if hsl.saturation.is_nan() {
hsl.saturation = Okhsl::max_saturation();
}
hsl
}
pub fn from_hsl(hsl: Okhsl) -> Color {
let color = Srgb::from_color(hsl);
Color::from_rgb(color.red, color.green, color.blue)
}
pub fn alpha(color: Color, alpha: f32) -> Color {
Color { a: alpha, ..color }
}
pub fn mix(a: Color, b: Color, factor: f32) -> Color {
let a_hsl = to_hsl(a);
let b_hsl = to_hsl(b);
let mixed = a_hsl.mix(b_hsl, factor);
from_hsl(mixed)
}
pub fn lighten(color: Color, amount: f32) -> Color {
let mut hsl = to_hsl(color);
hsl.lighten_fixed_assign(amount);
from_hsl(hsl)
}
pub fn darken(color: Color, amount: f32) -> Color {
let mut hsl = to_hsl(color);
hsl.darken_fixed_assign(amount);
from_hsl(hsl)
}

33
src/theme/menu.rs Normal file
View file

@ -0,0 +1,33 @@
pub use iced::widget::overlay::menu::Style;
use iced::{
widget::overlay::menu::{Catalog, StyleFn},
Background, Border,
};
use super::Theme;
impl Catalog for Theme {
type Class<'a> = StyleFn<'a, Self>;
fn default<'a>() -> StyleFn<'a, Self> {
Box::new(primary)
}
fn style(&self, class: &StyleFn<'_, Self>) -> Style {
class(self)
}
}
pub fn primary(theme: &Theme) -> Style {
Style {
text_color: theme.colors().text.base,
background: Background::Color(theme.colors().background.darker),
border: Border {
width: 1.0,
radius: 4.0.into(),
color: theme.colors().background.darker,
},
selected_text_color: theme.colors().text.low_alpha,
selected_background: Background::Color(theme.colors().background.dark),
}
}

100
src/theme/mod.rs Normal file
View file

@ -0,0 +1,100 @@
pub mod button;
pub mod checkbox;
pub mod container;
pub mod data;
pub mod menu;
pub mod pick_list;
pub mod scrollable;
pub mod svg;
pub mod text;
pub mod text_editor;
pub mod text_input;
pub mod toggler;
use data::ColorTheme;
use data::Colors;
use iced::color;
// TODO: If we use non-standard font sizes, we should consider
// Config.font.size since it's user configurable
pub const TEXT_SIZE: f32 = 13.0;
pub const ICON_SIZE: f32 = 12.0;
#[derive(Debug, Clone)]
pub enum Theme {
Selected(ColorTheme),
Preview {
selected: ColorTheme,
preview: ColorTheme,
},
}
impl Theme {
pub fn light() -> Self {
let palette = data::Palette {
accent: color!(0x1c71d8),
success: color!(0x1b8553),
error: color!(0xc01c28),
background: color!(0xfafafa),
alert: color!(0x9c6e03),
text: color!(0x000000),
//
action: color!(0x62a0ea),
info: color!(0x1b8553),
};
Theme::Selected(ColorTheme {
name: String::from("light"),
colors: Colors::new(&palette),
})
}
pub fn dark() -> Self {
let palette = data::Palette {
accent: color!(0x78aeed),
success: color!(0x8ff0a4),
error: color!(0xff7b63),
background: color!(0x242424),
alert: color!(0xf8e45c),
text: color!(0xffffff),
//
action: color!(0x99c1f1),
info: color!(0x1b8553),
};
Theme::Selected(ColorTheme {
name: String::from("light"),
colors: Colors::new(&palette),
})
}
pub fn preview(&self, theme: ColorTheme) -> Self {
match self {
Theme::Selected(selected) | Theme::Preview { selected, .. } => Self::Preview {
selected: selected.clone(),
preview: theme,
},
}
}
pub fn selected(&self) -> Self {
match self {
Theme::Selected(selected) | Theme::Preview { selected, .. } => {
Self::Selected(selected.clone())
}
}
}
pub fn colors(&self) -> &Colors {
match self {
Theme::Selected(selected) => &selected.colors,
Theme::Preview { preview, .. } => &preview.colors,
}
}
}
impl From<ColorTheme> for Theme {
fn from(theme: ColorTheme) -> Self {
Theme::Selected(theme)
}
}
impl Default for Theme {
fn default() -> Self {
Self::from(ColorTheme::default())
}
}

26
src/theme/pick_list.rs Normal file
View file

@ -0,0 +1,26 @@
use iced::widget::pick_list::{Catalog, Status, Style, StyleFn};
use iced::{Background, Border};
use super::Theme;
impl Catalog for Theme {
type Class<'a> = StyleFn<'a, Self>;
fn default<'a>() -> StyleFn<'a, Self> {
Box::new(primary)
}
fn style(&self, class: &StyleFn<'_, Self>, status: Status) -> Style {
class(self, status)
}
}
pub fn primary(theme: &Theme, _status: Status) -> Style {
Style {
text_color: theme.colors().text.base,
placeholder_color: theme.colors().background.dark,
handle_color: theme.colors().background.darkest,
background: Background::Color(theme.colors().background.darker),
border: Border::default().rounded(5),
}
}

87
src/theme/scrollable.rs Normal file
View file

@ -0,0 +1,87 @@
use iced::{
widget::{
container,
scrollable::{Catalog, Rail, Scrollbar, Scroller, Status, Style, StyleFn},
},
Background, Border, Color, Shadow,
};
use super::Theme;
impl Catalog for Theme {
type Class<'a> = StyleFn<'a, Self>;
fn default<'a>() -> Self::Class<'a> {
Box::new(primary)
}
fn style(&self, class: &Self::Class<'_>, status: Status) -> Style {
class(self, status)
}
}
pub fn primary(theme: &Theme, status: Status) -> Style {
let rail = Rail {
background: None,
border: Border::default(),
scroller: Scroller {
color: theme.colors().background.darker,
border: Border {
radius: 8.0.into(),
width: 0.0,
color: Color::TRANSPARENT,
},
},
};
match status {
Status::Active { .. } | Status::Hovered { .. } | Status::Dragged { .. } => Style {
container: container::Style {
text_color: None,
background: None,
border: Border {
radius: 8.0.into(),
width: 1.0,
color: Color::TRANSPARENT,
},
shadow: Shadow::default(),
},
vertical_rail: rail,
horizontal_rail: rail,
gap: None,
},
}
}
pub fn hidden(_theme: &Theme, status: Status) -> Style {
let rail = Rail {
background: None,
border: Border::default(),
scroller: Scroller {
color: Color::TRANSPARENT,
border: Border {
radius: 0.0.into(),
width: 0.0,
color: Color::TRANSPARENT,
},
},
};
match status {
Status::Active { .. } | Status::Hovered { .. } | Status::Dragged { .. } => Style {
container: container::Style {
text_color: None,
background: Some(Background::Color(Color::TRANSPARENT)),
border: Border {
radius: 8.0.into(),
width: 1.0,
color: Color::TRANSPARENT,
},
shadow: Shadow::default(),
},
vertical_rail: rail,
horizontal_rail: rail,
gap: None,
},
}
}

31
src/theme/svg.rs Normal file
View file

@ -0,0 +1,31 @@
#[allow(unused_variables)]
use super::Theme;
use iced::widget::svg::{Catalog, Status, Style, StyleFn};
impl Catalog for Theme {
type Class<'a> = StyleFn<'a, Self>;
fn default<'a>() -> Self::Class<'a> {
Box::new(primary)
}
fn style(&self, class: &Self::Class<'_>, status: Status) -> Style {
class(self, status)
}
}
pub fn primary(_theme: &Theme, _status: Status) -> Style {
Style::default()
}
pub fn error(theme: &Theme, _status: Status) -> Style {
Style {
color: Some(theme.colors().error.base),
}
}
pub fn success(theme: &Theme, _status: Status) -> Style {
Style {
color: Some(theme.colors().success.base),
}
}

67
src/theme/text.rs Normal file
View file

@ -0,0 +1,67 @@
use iced::widget::text::{Catalog, Style, StyleFn};
use super::Theme;
impl Catalog for Theme {
type Class<'a> = StyleFn<'a, Self>;
fn default<'a>() -> Self::Class<'a> {
Box::new(none)
}
fn style(&self, class: &Self::Class<'_>) -> Style {
class(self)
}
}
pub fn none(_theme: &Theme) -> Style {
Style { color: None }
}
pub fn primary(theme: &Theme) -> Style {
Style {
color: Some(theme.colors().text.base),
}
}
pub fn accent(theme: &Theme) -> Style {
Style {
color: Some(theme.colors().accent.base),
}
}
pub fn alert(theme: &Theme) -> Style {
Style {
color: Some(theme.colors().alert.base),
}
}
pub fn info(theme: &Theme) -> Style {
Style {
color: Some(theme.colors().info.base),
}
}
pub fn error(theme: &Theme) -> Style {
Style {
color: Some(theme.colors().error.base),
}
}
pub fn success(theme: &Theme) -> Style {
Style {
color: Some(theme.colors().success.base),
}
}
pub fn transparent(theme: &Theme) -> Style {
Style {
color: Some(theme.colors().text.low_alpha),
}
}
pub fn transparent_accent(theme: &Theme) -> Style {
Style {
color: Some(theme.colors().accent.low_alpha),
}
}

29
src/theme/text_editor.rs Normal file
View file

@ -0,0 +1,29 @@
use iced::{
widget::text_editor::{Catalog, Status, Style, StyleFn},
Border,
};
use super::Theme;
impl Catalog for Theme {
type Class<'a> = StyleFn<'a, Self>;
fn default<'a>() -> Self::Class<'a> {
Box::new(none)
}
fn style(&self, class: &Self::Class<'_>, status: Status) -> Style {
class(self, status)
}
}
pub fn none(_theme: &Theme, _state: Status) -> Style {
Style {
value: _theme.colors().text.base,
selection: _theme.colors().text.light,
icon: _theme.colors().text.base,
placeholder: _theme.colors().text.lighter,
border: Border::default().rounded(5.),
background: iced::Background::from(_theme.colors().background.darker),
}
}

29
src/theme/text_input.rs Normal file
View file

@ -0,0 +1,29 @@
use iced::{
widget::text_input::{Catalog, Status, Style, StyleFn},
Border,
};
use super::Theme;
impl Catalog for Theme {
type Class<'a> = StyleFn<'a, Self>;
fn default<'a>() -> Self::Class<'a> {
Box::new(none)
}
fn style(&self, class: &Self::Class<'_>, status: Status) -> Style {
class(self, status)
}
}
pub fn none(_theme: &Theme, _state: Status) -> Style {
Style {
value: _theme.colors().text.base,
selection: _theme.colors().text.light,
icon: _theme.colors().text.base,
placeholder: _theme.colors().text.lighter,
border: Border::default().rounded(10.),
background: iced::Background::from(_theme.colors().background.base),
}
}

26
src/theme/toggler.rs Normal file
View file

@ -0,0 +1,26 @@
use iced::widget::toggler::{Catalog, Status, Style, StyleFn};
use super::Theme;
impl Catalog for Theme {
type Class<'a> = StyleFn<'a, Self>;
fn default<'a>() -> Self::Class<'a> {
Box::new(primary)
}
fn style(&self, class: &Self::Class<'_>, status: iced::widget::toggler::Status) -> Style {
class(self, status)
}
}
pub fn primary(_theme: &Theme, _status: Status) -> Style {
Style {
background: _theme.colors().background.darkest,
background_border_width: 10.0,
background_border_color: _theme.colors().background.darkest,
foreground: _theme.colors().text.lightest,
foreground_border_width: 10.0,
foreground_border_color: _theme.colors().text.high_alpha,
}
}