From 2cfb307f8c3927a0876c6b754a5d7d673b9edfee Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 21 May 2022 17:33:31 -0400 Subject: Implement basic theming `Palette` --- style/src/theme/palette.rs | 202 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 style/src/theme/palette.rs (limited to 'style/src/theme/palette.rs') diff --git a/style/src/theme/palette.rs b/style/src/theme/palette.rs new file mode 100644 index 00000000..74139e6b --- /dev/null +++ b/style/src/theme/palette.rs @@ -0,0 +1,202 @@ +use iced_core::Color; + +use lazy_static::lazy_static; +use palette::{FromColor, Hsl, Mix, RelativeContrast, Srgb}; + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct Palette { + background: Color, + text: Color, + primary: Color, + success: Color, + danger: Color, +} + +impl Palette { + pub const LIGHT: Self = Self { + background: Color::WHITE, + text: Color::BLACK, + primary: Color::from_rgb( + 0x5E as f32 / 255.0, + 0x7C as f32 / 255.0, + 0xE2 as f32 / 255.0, + ), + success: Color::from_rgb( + 0x12 as f32 / 255.0, + 0x66 as f32 / 255.0, + 0x4F as f32 / 255.0, + ), + danger: Color::from_rgb( + 0xC3 as f32 / 255.0, + 0x42 as f32 / 255.0, + 0x3F as f32 / 255.0, + ), + }; + + pub const DARK: Self = Self { + background: Color::WHITE, + text: Color::BLACK, + primary: Color::from_rgb( + 0x5E as f32 / 255.0, + 0x7C as f32 / 255.0, + 0xE2 as f32 / 255.0, + ), + success: Color::from_rgb( + 0x12 as f32 / 255.0, + 0x66 as f32 / 255.0, + 0x4F as f32 / 255.0, + ), + danger: Color::from_rgb( + 0xC3 as f32 / 255.0, + 0x42 as f32 / 255.0, + 0x3F as f32 / 255.0, + ), + }; +} + +pub struct Extended { + pub background: Background, + pub primary: Group, + pub success: Group, + pub danger: Group, +} + +lazy_static! { + pub static ref EXTENDED_LIGHT: Extended = + Extended::generate(Palette::LIGHT); + pub static ref EXTENDED_DARK: Extended = Extended::generate(Palette::DARK); +} + +impl Extended { + pub fn generate(palette: Palette) -> Self { + Self { + background: Background::new(palette.background, palette.text), + primary: Group::new( + palette.primary, + palette.background, + palette.text, + ), + success: Group::new( + palette.success, + palette.background, + palette.text, + ), + danger: Group::new( + palette.danger, + palette.background, + palette.text, + ), + } + } +} + +pub struct Background { + pub base: Color, + pub weak: Color, + pub strong: Color, + pub text: Color, +} + +impl Background { + pub fn new(base: Color, text: Color) -> Self { + Self { + base, + weak: muted(base, 0.1), + strong: muted(base, 0.2), + text, + } + } +} + +pub struct Group { + pub base: Color, + pub weak: Color, + pub strong: Color, + pub text: Color, +} + +impl Group { + pub fn new(base: Color, background: Color, text: Color) -> Self { + Self { + base, + weak: mix(base, background, 0.4), + strong: if is_dark(background) { + lighten(base, 0.1) + } else { + darken(base, 0.1) + }, + text: if is_readable(base, text) { + text + } else if is_dark(text) { + Color::WHITE + } else { + Color::BLACK + }, + } + } +} + +fn muted(color: Color, amount: f32) -> Color { + let mut hsl = to_hsl(color); + + hsl.lightness = if is_dark(color) { + let delta = amount * (1.0 - hsl.lightness).powi(7); + hsl.lightness + delta + } else { + let delta = amount * hsl.lightness.powi(5); + hsl.lightness - delta + }; + + from_hsl(hsl) +} + +fn darken(color: Color, amount: f32) -> Color { + let mut hsl = to_hsl(color); + + hsl.lightness = if hsl.lightness - amount < 0.0 { + 0.0 + } else { + hsl.lightness - amount + }; + + from_hsl(hsl) +} + +fn mix(a: Color, b: Color, factor: f32) -> Color { + let a_lin = Srgb::from(a).into_linear(); + let b_lin = Srgb::from(b).into_linear(); + + let mixed = a_lin.mix(&b_lin, factor); + Srgb::from_linear(mixed).into() +} + +fn lighten(color: Color, amount: f32) -> Color { + let mut hsl = to_hsl(color); + + hsl.lightness = if hsl.lightness + amount > 1.0 { + 1.0 + } else { + hsl.lightness + amount + }; + + from_hsl(hsl) +} + +fn is_dark(color: Color) -> bool { + to_hsl(color).lightness < 0.5 +} + +fn is_readable(a: Color, b: Color) -> bool { + let a_srgb = Srgb::from(a); + let b_srgb = Srgb::from(b); + + a_srgb.has_enhanced_contrast_text(&b_srgb) +} + +fn to_hsl(color: Color) -> Hsl { + Hsl::from_color(Srgb::from(color)) +} + +fn from_hsl(hsl: Hsl) -> Color { + Srgb::from_color(hsl).into() +} -- cgit From 3a820b45f336398c48f8bedf7b8c4b8af876efff Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 26 May 2022 00:40:27 +0200 Subject: Implement theme styling for `Slider` --- style/src/theme/palette.rs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'style/src/theme/palette.rs') diff --git a/style/src/theme/palette.rs b/style/src/theme/palette.rs index 74139e6b..bbb122ef 100644 --- a/style/src/theme/palette.rs +++ b/style/src/theme/palette.rs @@ -59,6 +59,7 @@ pub struct Extended { pub primary: Group, pub success: Group, pub danger: Group, + pub border: Color, } lazy_static! { @@ -86,6 +87,7 @@ impl Extended { palette.background, palette.text, ), + border: mix(palette.background, palette.text, 0.7), } } } -- cgit From 822a3cd04f9edeb887d85164b0b3e556c3fde6bb Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 26 May 2022 01:10:26 +0200 Subject: Let a `Theme` control the `text_color` of an application --- style/src/theme/palette.rs | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) (limited to 'style/src/theme/palette.rs') diff --git a/style/src/theme/palette.rs b/style/src/theme/palette.rs index bbb122ef..0394700b 100644 --- a/style/src/theme/palette.rs +++ b/style/src/theme/palette.rs @@ -34,8 +34,12 @@ impl Palette { }; pub const DARK: Self = Self { - background: Color::WHITE, - text: Color::BLACK, + background: Color::from_rgb( + 0x20 as f32 / 255.0, + 0x22 as f32 / 255.0, + 0x25 as f32 / 255.0, + ), + text: Color::from_rgb(0.90, 0.90, 0.90), primary: Color::from_rgb( 0x5E as f32 / 255.0, 0x7C as f32 / 255.0, @@ -119,21 +123,17 @@ pub struct Group { impl Group { pub fn new(base: Color, background: Color, text: Color) -> Self { + let strong = if is_dark(base) { + lighten(base, 0.1) + } else { + darken(base, 0.1) + }; + Self { base, weak: mix(base, background, 0.4), - strong: if is_dark(background) { - lighten(base, 0.1) - } else { - darken(base, 0.1) - }, - text: if is_readable(base, text) { - text - } else if is_dark(text) { - Color::WHITE - } else { - Color::BLACK - }, + strong, + text: readable(strong, text), } } } @@ -184,8 +184,18 @@ fn lighten(color: Color, amount: f32) -> Color { from_hsl(hsl) } +fn readable(background: Color, text: Color) -> Color { + if is_readable(background, text) { + text + } else if is_dark(background) { + Color::WHITE + } else { + Color::BLACK + } +} + fn is_dark(color: Color) -> bool { - to_hsl(color).lightness < 0.5 + to_hsl(color).lightness < 0.6 } fn is_readable(a: Color, b: Color) -> bool { -- cgit From 5a39dad506a1614fd0ccacb1f56e83acbaf4961a Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 26 May 2022 05:31:25 +0200 Subject: Tweak styling of `Slider` and improve contrast of `Background` --- style/src/theme/palette.rs | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) (limited to 'style/src/theme/palette.rs') diff --git a/style/src/theme/palette.rs b/style/src/theme/palette.rs index 0394700b..acc147ed 100644 --- a/style/src/theme/palette.rs +++ b/style/src/theme/palette.rs @@ -63,7 +63,6 @@ pub struct Extended { pub primary: Group, pub success: Group, pub danger: Group, - pub border: Color, } lazy_static! { @@ -91,7 +90,6 @@ impl Extended { palette.background, palette.text, ), - border: mix(palette.background, palette.text, 0.7), } } } @@ -107,8 +105,8 @@ impl Background { pub fn new(base: Color, text: Color) -> Self { Self { base, - weak: muted(base, 0.1), - strong: muted(base, 0.2), + weak: mix(base, text, 0.15), + strong: mix(base, text, 0.25), text, } } @@ -142,7 +140,7 @@ fn muted(color: Color, amount: f32) -> Color { let mut hsl = to_hsl(color); hsl.lightness = if is_dark(color) { - let delta = amount * (1.0 - hsl.lightness).powi(7); + let delta = amount * (1.0 - hsl.lightness).powi(5); hsl.lightness + delta } else { let delta = amount * hsl.lightness.powi(5); @@ -164,14 +162,6 @@ fn darken(color: Color, amount: f32) -> Color { from_hsl(hsl) } -fn mix(a: Color, b: Color, factor: f32) -> Color { - let a_lin = Srgb::from(a).into_linear(); - let b_lin = Srgb::from(b).into_linear(); - - let mixed = a_lin.mix(&b_lin, factor); - Srgb::from_linear(mixed).into() -} - fn lighten(color: Color, amount: f32) -> Color { let mut hsl = to_hsl(color); @@ -184,6 +174,14 @@ fn lighten(color: Color, amount: f32) -> Color { from_hsl(hsl) } +fn mix(a: Color, b: Color, factor: f32) -> Color { + let a_lin = Srgb::from(a).into_linear(); + let b_lin = Srgb::from(b).into_linear(); + + let mixed = a_lin.mix(&b_lin, factor); + Srgb::from_linear(mixed).into() +} + fn readable(background: Color, text: Color) -> Color { if is_readable(background, text) { text -- cgit From d988d813d77bc23147a179586206048e6cc42157 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 26 May 2022 23:58:56 +0200 Subject: Introduce specific types for each `palette::Extended` field We will have more control over color calculations for each semantic purpose this way. --- style/src/theme/palette.rs | 139 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 109 insertions(+), 30 deletions(-) (limited to 'style/src/theme/palette.rs') diff --git a/style/src/theme/palette.rs b/style/src/theme/palette.rs index acc147ed..3d1ca097 100644 --- a/style/src/theme/palette.rs +++ b/style/src/theme/palette.rs @@ -60,9 +60,10 @@ impl Palette { pub struct Extended { pub background: Background, - pub primary: Group, - pub success: Group, - pub danger: Group, + pub primary: Primary, + pub secondary: Secondary, + pub success: Success, + pub danger: Danger, } lazy_static! { @@ -75,17 +76,18 @@ impl Extended { pub fn generate(palette: Palette) -> Self { Self { background: Background::new(palette.background, palette.text), - primary: Group::new( + primary: Primary::generate( palette.primary, palette.background, palette.text, ), - success: Group::new( + secondary: Secondary::generate(palette.background, palette.text), + success: Success::generate( palette.success, palette.background, palette.text, ), - danger: Group::new( + danger: Danger::generate( palette.danger, palette.background, palette.text, @@ -94,44 +96,113 @@ impl Extended { } } -pub struct Background { - pub base: Color, - pub weak: Color, - pub strong: Color, +#[derive(Debug, Clone, Copy)] +pub struct Pair { + pub color: Color, pub text: Color, } +impl Pair { + pub fn new(color: Color, text: Color) -> Self { + Self { + color, + text: readable(color, text), + } + } +} + +pub struct Background { + pub base: Pair, + pub weak: Pair, + pub strong: Pair, +} + impl Background { pub fn new(base: Color, text: Color) -> Self { + let weak = mix(base, text, 0.15); + let strong = mix(base, text, 0.25); + Self { - base, - weak: mix(base, text, 0.15), - strong: mix(base, text, 0.25), - text, + base: Pair::new(base, text), + weak: Pair::new(weak, text), + strong: Pair::new(strong, text), } } } -pub struct Group { - pub base: Color, - pub weak: Color, - pub strong: Color, - pub text: Color, +pub struct Primary { + pub base: Pair, + pub weak: Pair, + pub strong: Pair, +} + +impl Primary { + pub fn generate(base: Color, background: Color, text: Color) -> Self { + let weak = mix(base, background, 0.4); + let strong = deviate(base, 0.1); + + Self { + base: Pair::new(base, text), + weak: Pair::new(weak, text), + strong: Pair::new(strong, text), + } + } } -impl Group { - pub fn new(base: Color, background: Color, text: Color) -> Self { - let strong = if is_dark(base) { - lighten(base, 0.1) - } else { - darken(base, 0.1) - }; +pub struct Secondary { + pub base: Pair, + pub weak: Pair, + pub strong: Pair, +} + +impl Secondary { + pub fn generate(base: Color, text: Color) -> Self { + let base = mix(base, text, 0.2); + let weak = mix(base, text, 0.1); + let strong = mix(base, text, 0.3); Self { - base, - weak: mix(base, background, 0.4), - strong, - text: readable(strong, text), + base: Pair::new(base, text), + weak: Pair::new(weak, text), + strong: Pair::new(strong, text), + } + } +} + +pub struct Success { + pub base: Pair, + pub weak: Pair, + pub strong: Pair, +} + +impl Success { + pub fn generate(base: Color, background: Color, text: Color) -> Self { + let weak = mix(base, background, 0.4); + let strong = deviate(base, 0.1); + + Self { + base: Pair::new(base, text), + weak: Pair::new(weak, text), + strong: Pair::new(strong, text), + } + } +} + +pub struct Danger { + pub base: Pair, + pub weak: Pair, + pub strong: Pair, +} + +impl Danger { + pub fn generate(base: Color, background: Color, text: Color) -> Self { + let weak = mix(base, background, 0.4); + let strong = deviate(base, 0.1); + + Self { + base: Pair::new(base, text), + weak: Pair::new(weak, text), + strong: Pair::new(strong, text), } } } @@ -174,6 +245,14 @@ fn lighten(color: Color, amount: f32) -> Color { from_hsl(hsl) } +fn deviate(color: Color, amount: f32) -> Color { + if is_dark(color) { + lighten(color, amount) + } else { + darken(color, amount) + } +} + fn mix(a: Color, b: Color, factor: f32) -> Color { let a_lin = Srgb::from(a).into_linear(); let b_lin = Srgb::from(b).into_linear(); -- cgit From ce53d3933c860cd958636cce415ac97c04aee746 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 7 Jun 2022 01:11:35 +0200 Subject: Implement theme styling for `TextInput` --- style/src/theme/palette.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'style/src/theme/palette.rs') diff --git a/style/src/theme/palette.rs b/style/src/theme/palette.rs index 3d1ca097..fb23bb42 100644 --- a/style/src/theme/palette.rs +++ b/style/src/theme/palette.rs @@ -120,7 +120,7 @@ pub struct Background { impl Background { pub fn new(base: Color, text: Color) -> Self { let weak = mix(base, text, 0.15); - let strong = mix(base, text, 0.25); + let strong = mix(base, text, 0.40); Self { base: Pair::new(base, text), -- cgit From 3a22faaa204790126fc27a50d597fa7d069f8f8e Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 7 Jun 2022 04:55:46 +0200 Subject: Remove unused code warnings --- style/src/theme/palette.rs | 14 -------------- 1 file changed, 14 deletions(-) (limited to 'style/src/theme/palette.rs') diff --git a/style/src/theme/palette.rs b/style/src/theme/palette.rs index fb23bb42..cb8bb6e6 100644 --- a/style/src/theme/palette.rs +++ b/style/src/theme/palette.rs @@ -207,20 +207,6 @@ impl Danger { } } -fn muted(color: Color, amount: f32) -> Color { - let mut hsl = to_hsl(color); - - hsl.lightness = if is_dark(color) { - let delta = amount * (1.0 - hsl.lightness).powi(5); - hsl.lightness + delta - } else { - let delta = amount * hsl.lightness.powi(5); - hsl.lightness - delta - }; - - from_hsl(hsl) -} - fn darken(color: Color, amount: f32) -> Color { let mut hsl = to_hsl(color); -- cgit