From 62fddce2e676123b1325a16d144a1d0674dcd1ff Mon Sep 17 00:00:00 2001 From: Clark Moody Date: Fri, 21 Feb 2020 15:22:54 -0600 Subject: Add check_rgba fn to clamp float values to [0,1] --- core/src/color.rs | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/core/src/color.rs b/core/src/color.rs index db509b88..799d85c9 100644 --- a/core/src/color.rs +++ b/core/src/color.rs @@ -2,12 +2,17 @@ #[derive(Debug, Clone, Copy, PartialEq)] #[allow(missing_docs)] pub struct Color { + /// Red component, 0.0 - 1.0 pub r: f32, + /// Green component, 0.0 - 1.0 pub g: f32, + /// Blue component, 0.0 - 1.0 pub b: f32, + /// Transparency, 0.0 - 1.0 pub a: f32, } + impl Color { /// The black color. pub const BLACK: Color = Color { @@ -33,11 +38,26 @@ impl Color { a: 0.0, }; + /// Calmps a float value to the range [0.0, 1.0] + pub fn clamp(v: f32) -> f32 { + v.max(0.0f32).min(1.0f32) + } + + /// Ensures RGBA values on the range [0.0, 1.0] + pub fn check_rgba(r: f32, g: f32, b: f32, a:f32) -> Color { + Color { + r: Color::clamp(r), + g: Color::clamp(g), + b: Color::clamp(b), + a: Color::clamp(a), + } + } + /// Creates a [`Color`] from its RGB components. /// /// [`Color`]: struct.Color.html - pub const fn from_rgb(r: f32, g: f32, b: f32) -> Color { - Color { r, g, b, a: 1.0 } + pub fn from_rgb(r: f32, g: f32, b: f32) -> Color { + Color::check_rgba(r, g, b, 1.0) } /// Creates a [`Color`] from its RGB8 components. @@ -55,7 +75,7 @@ impl Color { r: f32::from(r) / 255.0, g: f32::from(g) / 255.0, b: f32::from(b) / 255.0, - a, + a: Color::clamp(a), } } -- cgit From 27a4cbccea91c508b914f2211a07aec2a4bed96e Mon Sep 17 00:00:00 2001 From: Clark Moody Date: Fri, 21 Feb 2020 15:40:37 -0600 Subject: Add inversion functions, rename check_rgba -> new --- core/src/color.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/core/src/color.rs b/core/src/color.rs index 799d85c9..fbc160e3 100644 --- a/core/src/color.rs +++ b/core/src/color.rs @@ -1,6 +1,5 @@ /// A color in the sRGB color space. #[derive(Debug, Clone, Copy, PartialEq)] -#[allow(missing_docs)] pub struct Color { /// Red component, 0.0 - 1.0 pub r: f32, @@ -12,7 +11,6 @@ pub struct Color { pub a: f32, } - impl Color { /// The black color. pub const BLACK: Color = Color { @@ -44,7 +42,7 @@ impl Color { } /// Ensures RGBA values on the range [0.0, 1.0] - pub fn check_rgba(r: f32, g: f32, b: f32, a:f32) -> Color { + pub fn new(r: f32, g: f32, b: f32, a: f32) -> Color { Color { r: Color::clamp(r), g: Color::clamp(g), @@ -57,7 +55,7 @@ impl Color { /// /// [`Color`]: struct.Color.html pub fn from_rgb(r: f32, g: f32, b: f32) -> Color { - Color::check_rgba(r, g, b, 1.0) + Color::new(r, g, b, 1.0) } /// Creates a [`Color`] from its RGB8 components. @@ -100,6 +98,18 @@ impl Color { self.a, ] } + + /// Invert the Color in-place + pub fn invert(&mut self) { + self.r = Color::clamp(1.0f32 - self.r); + self.b = Color::clamp(1.0f32 - self.g); + self.g = Color::clamp(1.0f32 - self.b); + } + + /// Return an inverted Color + pub fn inverse(self) -> Color { + Color::new(1.0f32 - self.r, 1.0f32 - self.g, 1.0f32 - self.b, self.a) + } } impl From<[f32; 3]> for Color { -- cgit From 0ff3cbf543e84b1e4d1965efe3d6cce3b1fb5900 Mon Sep 17 00:00:00 2001 From: Clark Moody Date: Fri, 21 Feb 2020 16:42:12 -0600 Subject: HSLColor struct, with conversions to/from RGB --- core/src/color.rs | 122 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 106 insertions(+), 16 deletions(-) diff --git a/core/src/color.rs b/core/src/color.rs index fbc160e3..a46f44ee 100644 --- a/core/src/color.rs +++ b/core/src/color.rs @@ -36,18 +36,13 @@ impl Color { a: 0.0, }; - /// Calmps a float value to the range [0.0, 1.0] - pub fn clamp(v: f32) -> f32 { - v.max(0.0f32).min(1.0f32) - } - - /// Ensures RGBA values on the range [0.0, 1.0] + /// New Color with range checks pub fn new(r: f32, g: f32, b: f32, a: f32) -> Color { Color { - r: Color::clamp(r), - g: Color::clamp(g), - b: Color::clamp(b), - a: Color::clamp(a), + r: clamp(r), + g: clamp(g), + b: clamp(b), + a: clamp(a), } } @@ -73,7 +68,7 @@ impl Color { r: f32::from(r) / 255.0, g: f32::from(g) / 255.0, b: f32::from(b) / 255.0, - a: Color::clamp(a), + a: clamp(a), } } @@ -101,9 +96,9 @@ impl Color { /// Invert the Color in-place pub fn invert(&mut self) { - self.r = Color::clamp(1.0f32 - self.r); - self.b = Color::clamp(1.0f32 - self.g); - self.g = Color::clamp(1.0f32 - self.b); + self.r = clamp(1.0f32 - self.r); + self.b = clamp(1.0f32 - self.g); + self.g = clamp(1.0f32 - self.b); } /// Return an inverted Color @@ -114,12 +109,107 @@ impl Color { impl From<[f32; 3]> for Color { fn from([r, g, b]: [f32; 3]) -> Self { - Color { r, g, b, a: 1.0 } + Color::new(r, g, b, 1.0) } } impl From<[f32; 4]> for Color { fn from([r, g, b, a]: [f32; 4]) -> Self { - Color { r, g, b, a } + Color::new(r, g, b, a) + } +} + +impl From for Color { + fn from(hsl: HSLColor) -> Self { + // Compute Chroma + let ch = (1.0 - (2.0 * hsl.l - 1.0).abs()) * hsl.s; + + // Quantized Hue: H' + let hp: u8 = (hsl.h / 60.0).ceil() as u8; + let x: f32 = ch * f32::from(1 - ((hp % 2) - 1)); + + // Intermediate RGB values + let (r1, g1, b1): (f32, f32, f32) = match hp { + 1 => (ch, x, 0.0), + 2 => (x, ch, 0.0), + 3 => (0.0, ch, x), + 4 => (0.0, x, ch), + 5 => (x, 0.0, ch), + 6 => (ch, 0.0, x), + _ => (0.0, 0.0, 0.0), + }; + + // Match lightness + let m = hsl.l - ch / 2.0; + + Color::new(r1 + m, g1 + m, b1 + m, hsl.a) + } +} + +/// A color in the HSL color space. +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct HSLColor { + /// Hue, 0.0 - 360.0 + pub h: f32, + /// Saturation, 0.0 - 1.0 + pub s: f32, + /// Lightness, 0.0 - 1.0 + pub l: f32, + /// Transparency, 0.0 - 1.0 + pub a: f32, +} + +impl HSLColor { + /// New HSLColor with range checks + pub fn new(h: f32, s: f32, l: f32, a: f32) -> HSLColor { + HSLColor { + h: clamp_hue(h), + s: clamp(s), + l: clamp(l), + a: clamp(a), + } + } +} + +impl From for HSLColor { + fn from(c: Color) -> Self { + // https://en.wikipedia.org/wiki/HSL_and_HSV#From_RGB + + // Maximum of the RGB: color Value (for HSV) + let v: f32 = c.r.max(c.g).max(c.b); + // Minimum of the RGB values + let m: f32 = c.r.min(c.g).min(c.b); + // Chroma + let ch: f32 = v - m; + // Lightness + let l: f32 = (v + m) / 2.0; + + // Determine Hue + let mut h = 0.0f32; + if c.r >= c.g && c.r >= c.b { + h = 60.0 * (c.g - c.b) / ch; + } else if c.g >= c.r && c.g >= c.b { + h = 60.0 * (2.0 + (c.b - c.r) / ch); + } else if c.b >= c.r && c.b >= c.g { + h = 60.0 * (4.0 + (c.r - c.g) / ch); + } + + // Determine saturation + let mut s = 0.0f32; + if l > 0.0 && l < 1.0 { + s = (v - l) / l.min(1.0 - l); + } + + HSLColor::new(h, s, l, c.a) } } + +/// Calmps a float value to the range [0.0, 1.0] +pub fn clamp(v: f32) -> f32 { + v.max(0.0f32).min(1.0f32) +} + +/// Calmps a float value to the range [0.0, 360.0] +pub fn clamp_hue(v: f32) -> f32 { + v.max(0.0f32).min(360.0f32) +} -- cgit From 63933e26d2ffd530fc1d8c9a7d7b94927c0e8cc8 Mon Sep 17 00:00:00 2001 From: Clark Moody Date: Tue, 31 Mar 2020 12:11:37 -0500 Subject: Add `palette` dependency behind "colors" feature flag --- Cargo.toml | 3 +++ core/Cargo.toml | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 9c52ea8f..7f6e1f0f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,8 @@ debug = ["iced_winit/debug"] tokio = ["iced_futures/tokio"] # Enables `async-std` as the `executor::Default` on native platforms async-std = ["iced_futures/async-std"] +# Enables advanced color conversion via `palette` +colors = ["iced_core/colors"] [badges] maintenance = { status = "actively-developed" } @@ -57,6 +59,7 @@ members = [ ] [dependencies] +iced_core = { version = "0.1", path = "core" } iced_futures = { version = "0.1", path = "futures" } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] diff --git a/core/Cargo.toml b/core/Cargo.toml index 837f6aae..e05f824c 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -7,4 +7,11 @@ description = "The essential concepts of Iced" license = "MIT" repository = "https://github.com/hecrj/iced" +[features] +colors = ["palette"] + [dependencies] + +[dependencies.palette] +version = "0.5.0" +optional = true -- cgit From 831a07f720d522954a75b159ccc00824f3affee6 Mon Sep 17 00:00:00 2001 From: Clark Moody Date: Tue, 31 Mar 2020 15:20:47 -0500 Subject: Conversion to palette's Srgba type --- core/src/color.rs | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/core/src/color.rs b/core/src/color.rs index a46f44ee..ce0ea5ed 100644 --- a/core/src/color.rs +++ b/core/src/color.rs @@ -1,3 +1,6 @@ +#[cfg(feature = "colors")] +use palette::rgb::Srgba; + /// A color in the sRGB color space. #[derive(Debug, Clone, Copy, PartialEq)] pub struct Color { @@ -94,6 +97,24 @@ impl Color { ] } + #[cfg(feature = "colors")] + /// Convert from palette's [`Srgba`] type to a [`Color`] + /// + /// [`Srgba`]: ../palette/rgb/type.Srgba.html + /// [`Color`]: struct.Color.html + pub fn from_srgba(srgba: Srgba) -> Color { + Color::new(srgba.red, srgba.green, srgba.blue, srgba.alpha) + } + + #[cfg(feature = "colors")] + /// Convert from [`Color`] to palette's [`Srgba`] type + /// + /// [`Color`]: struct.Color.html + /// [`Srgba`]: ../palette/rgb/type.Srgba.html + pub fn into_srgba(self) -> Srgba { + Srgba::new(self.r, self.g, self.b, self.a) + } + /// Invert the Color in-place pub fn invert(&mut self) { self.r = clamp(1.0f32 - self.r); @@ -119,6 +140,28 @@ impl From<[f32; 4]> for Color { } } +#[cfg(feature = "colors")] +/// Convert from palette's [`Srgba`] type to a [`Color`] +/// +/// [`Srgba`]: ../palette/rgb/type.Srgba.html +/// [`Color`]: struct.Color.html +impl From for Color { + fn from(srgba: Srgba) -> Self { + Color::new(srgba.red, srgba.green, srgba.blue, srgba.alpha) + } +} + +#[cfg(feature = "colors")] +/// Convert from [`Color`] to palette's [`Srgba`] type +/// +/// [`Color`]: struct.Color.html +/// [`Srgba`]: ../palette/rgb/type.Srgba.html +impl From for Srgba { + fn from(c: Color) -> Self { + Srgba::new(c.r, c.g, c.b, c.a) + } +} + impl From for Color { fn from(hsl: HSLColor) -> Self { // Compute Chroma -- cgit From 4009f0cf73cdf261a3218a44706fdd1434653c8f Mon Sep 17 00:00:00 2001 From: Clark Moody Date: Tue, 31 Mar 2020 15:31:44 -0500 Subject: Remove HSLColor --- core/src/color.rs | 90 ------------------------------------------------------- 1 file changed, 90 deletions(-) diff --git a/core/src/color.rs b/core/src/color.rs index ce0ea5ed..33eeedaf 100644 --- a/core/src/color.rs +++ b/core/src/color.rs @@ -162,97 +162,7 @@ impl From for Srgba { } } -impl From for Color { - fn from(hsl: HSLColor) -> Self { - // Compute Chroma - let ch = (1.0 - (2.0 * hsl.l - 1.0).abs()) * hsl.s; - - // Quantized Hue: H' - let hp: u8 = (hsl.h / 60.0).ceil() as u8; - let x: f32 = ch * f32::from(1 - ((hp % 2) - 1)); - - // Intermediate RGB values - let (r1, g1, b1): (f32, f32, f32) = match hp { - 1 => (ch, x, 0.0), - 2 => (x, ch, 0.0), - 3 => (0.0, ch, x), - 4 => (0.0, x, ch), - 5 => (x, 0.0, ch), - 6 => (ch, 0.0, x), - _ => (0.0, 0.0, 0.0), - }; - - // Match lightness - let m = hsl.l - ch / 2.0; - - Color::new(r1 + m, g1 + m, b1 + m, hsl.a) - } -} - -/// A color in the HSL color space. -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct HSLColor { - /// Hue, 0.0 - 360.0 - pub h: f32, - /// Saturation, 0.0 - 1.0 - pub s: f32, - /// Lightness, 0.0 - 1.0 - pub l: f32, - /// Transparency, 0.0 - 1.0 - pub a: f32, -} - -impl HSLColor { - /// New HSLColor with range checks - pub fn new(h: f32, s: f32, l: f32, a: f32) -> HSLColor { - HSLColor { - h: clamp_hue(h), - s: clamp(s), - l: clamp(l), - a: clamp(a), - } - } -} - -impl From for HSLColor { - fn from(c: Color) -> Self { - // https://en.wikipedia.org/wiki/HSL_and_HSV#From_RGB - - // Maximum of the RGB: color Value (for HSV) - let v: f32 = c.r.max(c.g).max(c.b); - // Minimum of the RGB values - let m: f32 = c.r.min(c.g).min(c.b); - // Chroma - let ch: f32 = v - m; - // Lightness - let l: f32 = (v + m) / 2.0; - - // Determine Hue - let mut h = 0.0f32; - if c.r >= c.g && c.r >= c.b { - h = 60.0 * (c.g - c.b) / ch; - } else if c.g >= c.r && c.g >= c.b { - h = 60.0 * (2.0 + (c.b - c.r) / ch); - } else if c.b >= c.r && c.b >= c.g { - h = 60.0 * (4.0 + (c.r - c.g) / ch); - } - - // Determine saturation - let mut s = 0.0f32; - if l > 0.0 && l < 1.0 { - s = (v - l) / l.min(1.0 - l); - } - - HSLColor::new(h, s, l, c.a) - } -} - /// Calmps a float value to the range [0.0, 1.0] pub fn clamp(v: f32) -> f32 { v.max(0.0f32).min(1.0f32) } - -/// Calmps a float value to the range [0.0, 360.0] -pub fn clamp_hue(v: f32) -> f32 { - v.max(0.0f32).min(360.0f32) -} -- cgit From bb443988197ecfbb1effe9ae73ec5533db7e3339 Mon Sep 17 00:00:00 2001 From: Clark Moody Date: Tue, 31 Mar 2020 16:09:06 -0500 Subject: Revert from_rgb to const --- core/src/color.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/color.rs b/core/src/color.rs index 33eeedaf..4ac2f8a7 100644 --- a/core/src/color.rs +++ b/core/src/color.rs @@ -52,8 +52,8 @@ impl Color { /// Creates a [`Color`] from its RGB components. /// /// [`Color`]: struct.Color.html - pub fn from_rgb(r: f32, g: f32, b: f32) -> Color { - Color::new(r, g, b, 1.0) + pub const fn from_rgb(r: f32, g: f32, b: f32) -> Color { + Color { r, g, b, a: 1.0 } } /// Creates a [`Color`] from its RGB8 components. -- cgit From fd484c76381cd77fdd485939f6435df115f1ca65 Mon Sep 17 00:00:00 2001 From: Clark Moody Date: Tue, 31 Mar 2020 16:12:18 -0500 Subject: Fix docstring typo --- core/src/color.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/color.rs b/core/src/color.rs index 4ac2f8a7..8a0a26ba 100644 --- a/core/src/color.rs +++ b/core/src/color.rs @@ -162,7 +162,7 @@ impl From for Srgba { } } -/// Calmps a float value to the range [0.0, 1.0] +/// Clamps a float value to the range [0.0, 1.0] pub fn clamp(v: f32) -> f32 { v.max(0.0f32).min(1.0f32) } -- cgit From 9a4ad3d6a7de5305cc992b140ca208a4277f75ea Mon Sep 17 00:00:00 2001 From: Clark Moody Date: Tue, 31 Mar 2020 16:40:44 -0500 Subject: Use debug assertions instead of clamp --- core/src/color.rs | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/core/src/color.rs b/core/src/color.rs index 8a0a26ba..b7445a8c 100644 --- a/core/src/color.rs +++ b/core/src/color.rs @@ -41,12 +41,24 @@ impl Color { /// New Color with range checks pub fn new(r: f32, g: f32, b: f32, a: f32) -> Color { - Color { - r: clamp(r), - g: clamp(g), - b: clamp(b), - a: clamp(a), - } + debug_assert!( + (0.0f32..=1.0f32).contains(&r), + "Red component must be on [0, 1]" + ); + debug_assert!( + (0.0f32..=1.0f32).contains(&g), + "Green component must be on [0, 1]" + ); + debug_assert!( + (0.0f32..=1.0f32).contains(&b), + "Blue component must be on [0, 1]" + ); + debug_assert!( + (0.0f32..=1.0f32).contains(&a), + "Alpha component must be on [0, 1]" + ); + + Color { r, g, b, a } } /// Creates a [`Color`] from its RGB components. @@ -71,7 +83,7 @@ impl Color { r: f32::from(r) / 255.0, g: f32::from(g) / 255.0, b: f32::from(b) / 255.0, - a: clamp(a), + a, } } @@ -117,9 +129,9 @@ impl Color { /// Invert the Color in-place pub fn invert(&mut self) { - self.r = clamp(1.0f32 - self.r); - self.b = clamp(1.0f32 - self.g); - self.g = clamp(1.0f32 - self.b); + self.r = 1.0f32 - self.r; + self.b = 1.0f32 - self.g; + self.g = 1.0f32 - self.b; } /// Return an inverted Color @@ -161,8 +173,3 @@ impl From for Srgba { Srgba::new(c.r, c.g, c.b, c.a) } } - -/// Clamps a float value to the range [0.0, 1.0] -pub fn clamp(v: f32) -> f32 { - v.max(0.0f32).min(1.0f32) -} -- cgit From e926e4374242cc590ac507b059e3ce0cfa97d52f Mon Sep 17 00:00:00 2001 From: Clark Moody Date: Tue, 31 Mar 2020 16:41:07 -0500 Subject: Add const from_rgba, for RGBA initialization --- core/src/color.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/core/src/color.rs b/core/src/color.rs index b7445a8c..4f0d974b 100644 --- a/core/src/color.rs +++ b/core/src/color.rs @@ -65,7 +65,14 @@ impl Color { /// /// [`Color`]: struct.Color.html pub const fn from_rgb(r: f32, g: f32, b: f32) -> Color { - Color { r, g, b, a: 1.0 } + Color::from_rgba(r, g, b, 1.0f32) + } + + /// Creates a [`Color`] from its RGBA components. + /// + /// [`Color`]: struct.Color.html + pub const fn from_rgba(r: f32, g: f32, b: f32, a: f32) -> Color { + Color { r, g, b, a } } /// Creates a [`Color`] from its RGB8 components. -- cgit From 7b15e4b0e29c64d53a00fbe6709ff2069630fec5 Mon Sep 17 00:00:00 2001 From: Clark Moody Date: Tue, 31 Mar 2020 16:44:02 -0500 Subject: Feature name colors -> palette --- Cargo.toml | 2 +- core/Cargo.toml | 3 --- core/src/color.rs | 10 +++++----- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7f6e1f0f..9b88f9ec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ tokio = ["iced_futures/tokio"] # Enables `async-std` as the `executor::Default` on native platforms async-std = ["iced_futures/async-std"] # Enables advanced color conversion via `palette` -colors = ["iced_core/colors"] +palette = ["iced_core/palette"] [badges] maintenance = { status = "actively-developed" } diff --git a/core/Cargo.toml b/core/Cargo.toml index e05f824c..b52bf315 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -7,9 +7,6 @@ description = "The essential concepts of Iced" license = "MIT" repository = "https://github.com/hecrj/iced" -[features] -colors = ["palette"] - [dependencies] [dependencies.palette] diff --git a/core/src/color.rs b/core/src/color.rs index 4f0d974b..be1a2870 100644 --- a/core/src/color.rs +++ b/core/src/color.rs @@ -1,4 +1,4 @@ -#[cfg(feature = "colors")] +#[cfg(feature = "palette")] use palette::rgb::Srgba; /// A color in the sRGB color space. @@ -116,7 +116,7 @@ impl Color { ] } - #[cfg(feature = "colors")] + #[cfg(feature = "palette")] /// Convert from palette's [`Srgba`] type to a [`Color`] /// /// [`Srgba`]: ../palette/rgb/type.Srgba.html @@ -125,7 +125,7 @@ impl Color { Color::new(srgba.red, srgba.green, srgba.blue, srgba.alpha) } - #[cfg(feature = "colors")] + #[cfg(feature = "palette")] /// Convert from [`Color`] to palette's [`Srgba`] type /// /// [`Color`]: struct.Color.html @@ -159,7 +159,7 @@ impl From<[f32; 4]> for Color { } } -#[cfg(feature = "colors")] +#[cfg(feature = "palette")] /// Convert from palette's [`Srgba`] type to a [`Color`] /// /// [`Srgba`]: ../palette/rgb/type.Srgba.html @@ -170,7 +170,7 @@ impl From for Color { } } -#[cfg(feature = "colors")] +#[cfg(feature = "palette")] /// Convert from [`Color`] to palette's [`Srgba`] type /// /// [`Color`]: struct.Color.html -- cgit From 408e9e566f740a4a9eb564492e96714dd5db4cc3 Mon Sep 17 00:00:00 2001 From: Clark Moody Date: Tue, 31 Mar 2020 17:36:09 -0500 Subject: Add palette test for Color <-> Srgba & manipulation --- core/src/color.rs | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/core/src/color.rs b/core/src/color.rs index be1a2870..67433ded 100644 --- a/core/src/color.rs +++ b/core/src/color.rs @@ -180,3 +180,44 @@ impl From for Srgba { Srgba::new(c.r, c.g, c.b, c.a) } } + +#[cfg(feature = "palette")] +#[cfg(test)] +mod tests { + use super::*; + use palette::Blend; + + #[test] + fn srgba_traits() { + let c = Color::from_rgb(0.5, 0.4, 0.3); + // Round-trip conversion to the palette:Srgba type + let s: Srgba = c.into(); + let r: Color = s.into(); + assert_eq!(c, r); + } + + #[test] + fn color_manipulation() { + let c1 = Color::from_rgb(0.5, 0.4, 0.3); + let c2 = Color::from_rgb(0.2, 0.5, 0.3); + + // Convert to linear color for manipulation + let l1 = c1.into_srgba().into_linear(); + let l2 = c2.into_srgba().into_linear(); + + // Take the lighter of each of the RGB components + let lighter = l1.lighten(l2); + + // Convert back to our Color + let r: Color = Srgba::from_linear(lighter).into(); + assert_eq!( + r, + Color { + r: 0.5, + g: 0.5, + b: 0.3, + a: 1.0 + } + ); + } +} -- cgit From a95d494f707b9492d180b41bd93565b21c729dd8 Mon Sep 17 00:00:00 2001 From: Clark Moody Date: Wed, 1 Apr 2020 16:15:29 -0500 Subject: Remove redundant from_srgba and into_srgba methods --- core/src/color.rs | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/core/src/color.rs b/core/src/color.rs index 67433ded..57765df0 100644 --- a/core/src/color.rs +++ b/core/src/color.rs @@ -116,24 +116,6 @@ impl Color { ] } - #[cfg(feature = "palette")] - /// Convert from palette's [`Srgba`] type to a [`Color`] - /// - /// [`Srgba`]: ../palette/rgb/type.Srgba.html - /// [`Color`]: struct.Color.html - pub fn from_srgba(srgba: Srgba) -> Color { - Color::new(srgba.red, srgba.green, srgba.blue, srgba.alpha) - } - - #[cfg(feature = "palette")] - /// Convert from [`Color`] to palette's [`Srgba`] type - /// - /// [`Color`]: struct.Color.html - /// [`Srgba`]: ../palette/rgb/type.Srgba.html - pub fn into_srgba(self) -> Srgba { - Srgba::new(self.r, self.g, self.b, self.a) - } - /// Invert the Color in-place pub fn invert(&mut self) { self.r = 1.0f32 - self.r; @@ -202,8 +184,8 @@ mod tests { let c2 = Color::from_rgb(0.2, 0.5, 0.3); // Convert to linear color for manipulation - let l1 = c1.into_srgba().into_linear(); - let l2 = c2.into_srgba().into_linear(); + let l1 = Srgba::from(c1).into_linear(); + let l2 = Srgba::from(c2).into_linear(); // Take the lighter of each of the RGB components let lighter = l1.lighten(l2); -- cgit From 56ce01e262832b78530b0721f735a95919651d91 Mon Sep 17 00:00:00 2001 From: Clark Moody Date: Wed, 1 Apr 2020 16:17:46 -0500 Subject: Simplify range declaration --- core/src/color.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/color.rs b/core/src/color.rs index 57765df0..eff14948 100644 --- a/core/src/color.rs +++ b/core/src/color.rs @@ -42,19 +42,19 @@ impl Color { /// New Color with range checks pub fn new(r: f32, g: f32, b: f32, a: f32) -> Color { debug_assert!( - (0.0f32..=1.0f32).contains(&r), + (0.0..=1.0).contains(&r), "Red component must be on [0, 1]" ); debug_assert!( - (0.0f32..=1.0f32).contains(&g), + (0.0..=1.0).contains(&g), "Green component must be on [0, 1]" ); debug_assert!( - (0.0f32..=1.0f32).contains(&b), + (0.0..=1.0).contains(&b), "Blue component must be on [0, 1]" ); debug_assert!( - (0.0f32..=1.0f32).contains(&a), + (0.0..=1.0).contains(&a), "Alpha component must be on [0, 1]" ); -- cgit From ea3b7b528275c7ae8a336004ad77f85341599335 Mon Sep 17 00:00:00 2001 From: Clark Moody Date: Thu, 2 Apr 2020 15:24:40 -0500 Subject: Derive Default for Color --- core/src/color.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/color.rs b/core/src/color.rs index eff14948..56d5455f 100644 --- a/core/src/color.rs +++ b/core/src/color.rs @@ -2,7 +2,7 @@ use palette::rgb::Srgba; /// A color in the sRGB color space. -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq, Default)] pub struct Color { /// Red component, 0.0 - 1.0 pub r: f32, -- cgit From 04be010fbdf84300531b806fa8855f57bbf727b7 Mon Sep 17 00:00:00 2001 From: Clark Moody Date: Thu, 2 Apr 2020 17:29:26 -0500 Subject: Conversion traits for palette::Srgb --- core/src/color.rs | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/core/src/color.rs b/core/src/color.rs index 56d5455f..c061add6 100644 --- a/core/src/color.rs +++ b/core/src/color.rs @@ -1,5 +1,5 @@ #[cfg(feature = "palette")] -use palette::rgb::Srgba; +use palette::rgb::{Srgb, Srgba}; /// A color in the sRGB color space. #[derive(Debug, Clone, Copy, PartialEq, Default)] @@ -163,6 +163,28 @@ impl From for Srgba { } } +#[cfg(feature = "palette")] +/// Convert from palette's [`Srgb`] type to a [`Color`] +/// +/// [`Srgb`]: ../palette/rgb/type.Srgb.html +/// [`Color`]: struct.Color.html +impl From for Color { + fn from(srgb: Srgb) -> Self { + Color::new(srgb.red, srgb.green, srgb.blue, 1.0) + } +} + +#[cfg(feature = "palette")] +/// Convert from [`Color`] to palette's [`Srgb`] type +/// +/// [`Color`]: struct.Color.html +/// [`Srgb`]: ../palette/rgb/type.Srgb.html +impl From for Srgb { + fn from(c: Color) -> Self { + Srgb::new(c.r, c.g, c.b) + } +} + #[cfg(feature = "palette")] #[cfg(test)] mod tests { -- cgit From 71657b50dd69d860663051c588ff643242e971a7 Mon Sep 17 00:00:00 2001 From: Clark Moody Date: Thu, 2 Apr 2020 17:49:23 -0500 Subject: Conditional re-export of palette from iced_core --- core/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/src/lib.rs b/core/src/lib.rs index c2887a0b..ca6013da 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -35,3 +35,6 @@ pub use point::Point; pub use rectangle::Rectangle; pub use size::Size; pub use vector::Vector; + +#[cfg(feature = "palette")] +pub use palette; -- cgit From 664a63a4b8c1b0b945ca45b1181ead040a12fa73 Mon Sep 17 00:00:00 2001 From: Clark Moody Date: Thu, 2 Apr 2020 17:52:21 -0500 Subject: Add example program: color palette Sliders for many color spaces update as any other sliders are moved around. Color is space is clamped to sRGB, so Lab and Lch color spaces cannot be fully expressed. TODO: - Real-time manipulation of base color to create a color scheme. - Show slider value under each slider - Show output values in text boxes for each color space --- Cargo.toml | 1 + examples/color_palette/Cargo.toml | 14 +++ examples/color_palette/README.md | 9 ++ examples/color_palette/src/main.rs | 250 +++++++++++++++++++++++++++++++++++++ 4 files changed, 274 insertions(+) create mode 100644 examples/color_palette/Cargo.toml create mode 100644 examples/color_palette/README.md create mode 100644 examples/color_palette/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index 9b88f9ec..8f0a95c3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,7 @@ members = [ "winit", "examples/bezier_tool", "examples/clock", + "examples/color_palette", "examples/counter", "examples/custom_widget", "examples/download_progress", diff --git a/examples/color_palette/Cargo.toml b/examples/color_palette/Cargo.toml new file mode 100644 index 00000000..0ad6708c --- /dev/null +++ b/examples/color_palette/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "color_palette" +version = "0.1.0" +authors = ["Clark Moody "] +edition = "2018" +publish = false + +[features] +palette = [] + +[dependencies] +iced = { path = "../..", features = ["palette"] } +iced_core = { path = "../../core" } +iced_native = { path = "../../native" } diff --git a/examples/color_palette/README.md b/examples/color_palette/README.md new file mode 100644 index 00000000..b646f3b3 --- /dev/null +++ b/examples/color_palette/README.md @@ -0,0 +1,9 @@ +## Color Palette + +A color palette generator, based on a user-defined root color. + +You can run it with `cargo run`: + +``` +cargo run --package color_palette +``` diff --git a/examples/color_palette/src/main.rs b/examples/color_palette/src/main.rs new file mode 100644 index 00000000..1c9fffbc --- /dev/null +++ b/examples/color_palette/src/main.rs @@ -0,0 +1,250 @@ +use iced::{ + slider, Color, Column, Element, Row, Sandbox, Settings, Slider, Text, +}; +use iced_core::palette::{self, Limited}; + +pub fn main() { + ColorPalette::run(Settings::default()) +} + +#[derive(Default)] +pub struct ColorPalette { + base_color: Color, + rgb_sliders: [slider::State; 3], + hsl_sliders: [slider::State; 3], + hsv_sliders: [slider::State; 3], + hwb_sliders: [slider::State; 3], + lab_sliders: [slider::State; 3], + lch_sliders: [slider::State; 3], +} + +#[derive(Debug, Clone, Copy)] +pub enum Message { + RgbColorChanged(Color), + HslColorChanged(palette::Hsl), + HsvColorChanged(palette::Hsv), + HwbColorChanged(palette::Hwb), + LabColorChanged(palette::Lab), + LchColorChanged(palette::Lch), +} + +impl Sandbox for ColorPalette { + type Message = Message; + + fn new() -> Self { + let mut s = Self::default(); + s.base_color = Color::from_rgb8(27, 135, 199); + s + } + + fn title(&self) -> String { + String::from("Color Palette") + } + + fn update(&mut self, message: Message) { + let mut srgb = match message { + Message::RgbColorChanged(rgb) => palette::Srgb::from(rgb), + Message::HslColorChanged(hsl) => palette::Srgb::from(hsl), + Message::HsvColorChanged(hsv) => palette::Srgb::from(hsv), + Message::HwbColorChanged(hwb) => palette::Srgb::from(hwb), + Message::LabColorChanged(lab) => palette::Srgb::from(lab), + Message::LchColorChanged(lch) => palette::Srgb::from(lch), + }; + srgb.clamp_self(); + self.base_color = Color::from(srgb); + } + + fn view(&mut self) -> Element { + let [rgb1, rgb2, rgb3] = &mut self.rgb_sliders; + let [hsl1, hsl2, hsl3] = &mut self.hsl_sliders; + let [hsv1, hsv2, hsv3] = &mut self.hsv_sliders; + let [hwb1, hwb2, hwb3] = &mut self.hwb_sliders; + let [lab1, lab2, lab3] = &mut self.lab_sliders; + let [lch1, lch2, lch3] = &mut self.lch_sliders; + + let color = self.base_color; + let srgb = palette::Srgb::from(self.base_color); + let hsl = palette::Hsl::from(srgb); + let hsv = palette::Hsv::from(srgb); + let hwb = palette::Hwb::from(srgb); + let lab = palette::Lab::from(srgb); + let lch = palette::Lch::from(srgb); + + Column::new() + .padding(20) + .spacing(20) + .push( + Row::new() + .spacing(10) + .push(Text::new("RGB")) + .push(Slider::new(rgb1, 0.0..=1.0, color.r, move |r| { + Message::RgbColorChanged(Color { r, ..color }) + })) + .push(Slider::new(rgb2, 0.0..=1.0, color.g, move |g| { + Message::RgbColorChanged(Color { g, ..color }) + })) + .push(Slider::new(rgb3, 0.0..=1.0, color.b, move |b| { + Message::RgbColorChanged(Color { b, ..color }) + })), + ) + .push( + Row::new() + .spacing(10) + .push(Text::new("HSL")) + .push(Slider::new( + hsl1, + 0.0..=360.0, + hsl.hue.to_positive_degrees(), + move |hue| { + Message::HslColorChanged(palette::Hsl { + hue: palette::RgbHue::from_degrees(hue), + ..hsl + }) + }, + )) + .push(Slider::new( + hsl2, + 0.0..=1.0, + hsl.saturation, + move |saturation| { + Message::HslColorChanged(palette::Hsl { + saturation, + ..hsl + }) + }, + )) + .push(Slider::new( + hsl3, + 0.0..=1.0, + hsl.lightness, + move |lightness| { + Message::HslColorChanged(palette::Hsl { + lightness, + ..hsl + }) + }, + )), + ) + .push( + Row::new() + .spacing(10) + .push(Text::new("HSV")) + .push(Slider::new( + hsv1, + 0.0..=360.0, + hsv.hue.to_positive_degrees(), + move |hue| { + Message::HsvColorChanged(palette::Hsv { + hue: palette::RgbHue::from_degrees(hue), + ..hsv + }) + }, + )) + .push(Slider::new( + hsv2, + 0.0..=1.0, + hsv.saturation, + move |saturation| { + Message::HsvColorChanged(palette::Hsv { + saturation, + ..hsv + }) + }, + )) + .push(Slider::new( + hsv3, + 0.0..=1.0, + hsv.value, + move |value| { + Message::HsvColorChanged(palette::Hsv { + value, + ..hsv + }) + }, + )), + ) + .push( + Row::new() + .spacing(10) + .push(Text::new("HWB")) + .push(Slider::new( + hwb1, + 0.0..=360.0, + hwb.hue.to_positive_degrees(), + move |hue| { + Message::HwbColorChanged(palette::Hwb { + hue: palette::RgbHue::from_degrees(hue), + ..hwb + }) + }, + )) + .push(Slider::new( + hwb2, + 0.0..=1.0, + hwb.whiteness, + move |whiteness| { + Message::HwbColorChanged(palette::Hwb { + whiteness, + ..hwb + }) + }, + )) + .push(Slider::new( + hwb3, + 0.0..=1.0, + hwb.blackness, + move |blackness| { + Message::HwbColorChanged(palette::Hwb { + blackness, + ..hwb + }) + }, + )), + ) + .push( + Row::new() + .spacing(10) + .push(Text::new("Lab")) + .push(Slider::new(lab1, 0.0..=100.0, lab.l, move |l| { + Message::LabColorChanged(palette::Lab { l, ..lab }) + })) + .push(Slider::new(lab2, -128.0..=127.0, lab.a, move |a| { + Message::LabColorChanged(palette::Lab { a, ..lab }) + })) + .push(Slider::new(lab3, -128.0..=127.0, lab.b, move |b| { + Message::LabColorChanged(palette::Lab { b, ..lab }) + })), + ) + .push( + Row::new() + .spacing(10) + .push(Text::new("Lch")) + .push(Slider::new(lch1, 0.0..=100.0, lch.l, move |l| { + Message::LchColorChanged(palette::Lch { l, ..lch }) + })) + .push(Slider::new( + lch2, + 0.0..=128.0, + lch.chroma, + move |chroma| { + Message::LchColorChanged(palette::Lch { + chroma, + ..lch + }) + }, + )) + .push(Slider::new( + lch3, + 0.0..=360.0, + lch.hue.to_positive_degrees(), + move |hue| { + Message::LchColorChanged(palette::Lch { + hue: palette::LabHue::from_degrees(hue), + ..lch + }) + }, + )), + ) + .into() + } +} -- cgit From 6b18e78e535d50f648bd5ba739eb29b3c76a7965 Mon Sep 17 00:00:00 2001 From: Clark Moody Date: Wed, 8 Apr 2020 17:45:54 -0500 Subject: Use canvas to draw color palette for example --- Cargo.toml | 2 +- examples/color_palette/Cargo.toml | 2 +- examples/color_palette/src/main.rs | 106 +++++++++++++++++++++++++++++++++---- 3 files changed, 99 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8f0a95c3..206409bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,7 +60,7 @@ members = [ ] [dependencies] -iced_core = { version = "0.1", path = "core" } +iced_core = { version = "0.2", path = "core" } iced_futures = { version = "0.1", path = "futures" } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] diff --git a/examples/color_palette/Cargo.toml b/examples/color_palette/Cargo.toml index 0ad6708c..ad7a0114 100644 --- a/examples/color_palette/Cargo.toml +++ b/examples/color_palette/Cargo.toml @@ -9,6 +9,6 @@ publish = false palette = [] [dependencies] -iced = { path = "../..", features = ["palette"] } +iced = { path = "../..", features = ["canvas", "palette"] } iced_core = { path = "../../core" } iced_native = { path = "../../native" } diff --git a/examples/color_palette/src/main.rs b/examples/color_palette/src/main.rs index 1c9fffbc..267cc58c 100644 --- a/examples/color_palette/src/main.rs +++ b/examples/color_palette/src/main.rs @@ -1,5 +1,6 @@ use iced::{ - slider, Color, Column, Element, Row, Sandbox, Settings, Slider, Text, + canvas, slider, Canvas, Color, Column, Element, Length, Point, Row, + Sandbox, Settings, Slider, Text, }; use iced_core::palette::{self, Limited}; @@ -7,15 +8,39 @@ pub fn main() { ColorPalette::run(Settings::default()) } -#[derive(Default)] +#[derive(Debug, Default)] +pub struct State { + color: Color, + theme: Vec, +} + +fn generate_theme(base_color: &Color) -> Vec { + use palette::{Hsl, Hue, Shade, Srgb}; + let mut theme = Vec::::new(); + // Convert to linear color for manipulation + let srgb = Srgb::from(*base_color); + + let hsl = Hsl::from(srgb); + + theme.push(Srgb::from(hsl.shift_hue(-120.0)).clamp().into()); + theme.push(Srgb::from(hsl.shift_hue(-115.0).darken(0.075)).clamp().into()); + theme.push(Srgb::from(hsl.darken(0.075)).clamp().into()); + theme.push(*base_color); + theme.push(Srgb::from(hsl.lighten(0.075)).clamp().into()); + theme.push(Srgb::from(hsl.shift_hue(115.0).darken(0.075)).clamp().into()); + theme.push(Srgb::from(hsl.shift_hue(120.0)).clamp().into()); + theme +} + pub struct ColorPalette { - base_color: Color, + state: State, rgb_sliders: [slider::State; 3], hsl_sliders: [slider::State; 3], hsv_sliders: [slider::State; 3], hwb_sliders: [slider::State; 3], lab_sliders: [slider::State; 3], lch_sliders: [slider::State; 3], + canvas_layer: canvas::layer::Cache, } #[derive(Debug, Clone, Copy)] @@ -32,9 +57,24 @@ impl Sandbox for ColorPalette { type Message = Message; fn new() -> Self { - let mut s = Self::default(); - s.base_color = Color::from_rgb8(27, 135, 199); - s + fn triple_slider() -> [slider::State; 3] { + [ + slider::State::new(), + slider::State::new(), + slider::State::new(), + ] + } + + ColorPalette { + state: State::new(), + rgb_sliders: triple_slider(), + hsl_sliders: triple_slider(), + hsv_sliders: triple_slider(), + hwb_sliders: triple_slider(), + lab_sliders: triple_slider(), + lch_sliders: triple_slider(), + canvas_layer: canvas::layer::Cache::new(), + } } fn title(&self) -> String { @@ -51,7 +91,11 @@ impl Sandbox for ColorPalette { Message::LchColorChanged(lch) => palette::Srgb::from(lch), }; srgb.clamp_self(); - self.base_color = Color::from(srgb); + self.canvas_layer.clear(); + self.state.color = Color::from(srgb); + + // Set theme colors + self.state.theme = generate_theme(&self.state.color); } fn view(&mut self) -> Element { @@ -62,8 +106,8 @@ impl Sandbox for ColorPalette { let [lab1, lab2, lab3] = &mut self.lab_sliders; let [lch1, lch2, lch3] = &mut self.lch_sliders; - let color = self.base_color; - let srgb = palette::Srgb::from(self.base_color); + let color = self.state.color; + let srgb = palette::Srgb::from(self.state.color); let hsl = palette::Hsl::from(srgb); let hsv = palette::Hsv::from(srgb); let hwb = palette::Hwb::from(srgb); @@ -245,6 +289,50 @@ impl Sandbox for ColorPalette { }, )), ) + .push( + Canvas::new() + .width(Length::Fill) + .height(Length::Units(150)) + .push(self.canvas_layer.with(&self.state)), + ) .into() } } + +impl State { + pub fn new() -> State { + let base = Color::from_rgb8(27, 135, 199); + State { + color: base, + theme: generate_theme(&base), + } + } +} + +impl canvas::Drawable for State { + fn draw(&self, frame: &mut canvas::Frame) { + use canvas::{Fill, Path}; + if self.theme.len() == 0 { + println!("Zero len"); + return; + } + + let box_width = frame.width() / self.theme.len() as f32; + for i in 0..self.theme.len() { + let anchor = Point { + x: (i as f32) * box_width, + y: 0.0, + }; + let rect = Path::new(|path| { + path.move_to(anchor); + path.line_to(Point { x: anchor.x + box_width, y: anchor.y }); + path.line_to(Point { + x: anchor.x + box_width, + y: anchor.y + frame.height(), + }); + path.line_to(Point { x: anchor.x, y: anchor.y + frame.height() }); + }); + frame.fill(&rect, Fill::Color(self.theme[i])); + } + } +} -- cgit From b1328f193cceb803e81e59230ff4ca89072ef5a5 Mon Sep 17 00:00:00 2001 From: Clark Moody Date: Thu, 9 Apr 2020 13:11:39 -0500 Subject: More theme colors and gradient of lightness --- examples/color_palette/src/main.rs | 76 +++++++++++++++++++++++++++++++++----- 1 file changed, 67 insertions(+), 9 deletions(-) diff --git a/examples/color_palette/src/main.rs b/examples/color_palette/src/main.rs index 267cc58c..f7918df4 100644 --- a/examples/color_palette/src/main.rs +++ b/examples/color_palette/src/main.rs @@ -17,18 +17,34 @@ pub struct State { fn generate_theme(base_color: &Color) -> Vec { use palette::{Hsl, Hue, Shade, Srgb}; let mut theme = Vec::::new(); - // Convert to linear color for manipulation - let srgb = Srgb::from(*base_color); - - let hsl = Hsl::from(srgb); + // Convert to HSL color for manipulation + let hsl = Hsl::from(Srgb::from(*base_color)); + theme.push( + Srgb::from(hsl.shift_hue(-135.0).lighten(0.075)) + .clamp() + .into(), + ); theme.push(Srgb::from(hsl.shift_hue(-120.0)).clamp().into()); - theme.push(Srgb::from(hsl.shift_hue(-115.0).darken(0.075)).clamp().into()); + theme.push( + Srgb::from(hsl.shift_hue(-105.0).darken(0.075)) + .clamp() + .into(), + ); theme.push(Srgb::from(hsl.darken(0.075)).clamp().into()); theme.push(*base_color); theme.push(Srgb::from(hsl.lighten(0.075)).clamp().into()); - theme.push(Srgb::from(hsl.shift_hue(115.0).darken(0.075)).clamp().into()); + theme.push( + Srgb::from(hsl.shift_hue(105.0).darken(0.075)) + .clamp() + .into(), + ); theme.push(Srgb::from(hsl.shift_hue(120.0)).clamp().into()); + theme.push( + Srgb::from(hsl.shift_hue(135.0).lighten(0.075)) + .clamp() + .into(), + ); theme } @@ -312,12 +328,17 @@ impl State { impl canvas::Drawable for State { fn draw(&self, frame: &mut canvas::Frame) { use canvas::{Fill, Path}; + use palette::{Hsl, Srgb}; + if self.theme.len() == 0 { println!("Zero len"); return; } + let pad = 5.0; + let box_width = frame.width() / self.theme.len() as f32; + let box_height = frame.height() / 2.0 - pad; for i in 0..self.theme.len() { let anchor = Point { x: (i as f32) * box_width, @@ -325,14 +346,51 @@ impl canvas::Drawable for State { }; let rect = Path::new(|path| { path.move_to(anchor); - path.line_to(Point { x: anchor.x + box_width, y: anchor.y }); path.line_to(Point { x: anchor.x + box_width, - y: anchor.y + frame.height(), + y: anchor.y, + }); + path.line_to(Point { + x: anchor.x + box_width, + y: anchor.y + box_height, + }); + path.line_to(Point { + x: anchor.x, + y: anchor.y + box_height, }); - path.line_to(Point { x: anchor.x, y: anchor.y + frame.height() }); }); frame.fill(&rect, Fill::Color(self.theme[i])); } + + let hsl = Hsl::from(Srgb::from(self.color)); + for i in 0..self.theme.len() { + let pct = (i as f32 + 1.0) / (self.theme.len() as f32 + 1.0); + let graded = Hsl { + lightness: 1.0 - pct, + ..hsl + }; + let color: Color = Srgb::from(graded.clamp()).into(); + + let anchor = Point { + x: (i as f32) * box_width, + y: box_height + 2.0 * pad, + }; + let rect = Path::new(|path| { + path.move_to(anchor); + path.line_to(Point { + x: anchor.x + box_width, + y: anchor.y, + }); + path.line_to(Point { + x: anchor.x + box_width, + y: anchor.y + box_height, + }); + path.line_to(Point { + x: anchor.x, + y: anchor.y + box_height, + }); + }); + frame.fill(&rect, Fill::Color(color)); + } } } -- cgit From 39fd8ad9e973b8f6ec9e4e4d08f4e8aca72b069e Mon Sep 17 00:00:00 2001 From: Clark Moody Date: Thu, 9 Apr 2020 17:49:29 -0500 Subject: TextInput fields with color encodings. Draw shades. --- examples/color_palette/src/main.rs | 287 ++++++++++++++++++++++++++++++++++--- 1 file changed, 266 insertions(+), 21 deletions(-) diff --git a/examples/color_palette/src/main.rs b/examples/color_palette/src/main.rs index f7918df4..fc733787 100644 --- a/examples/color_palette/src/main.rs +++ b/examples/color_palette/src/main.rs @@ -1,11 +1,14 @@ use iced::{ - canvas, slider, Canvas, Color, Column, Element, Length, Point, Row, - Sandbox, Settings, Slider, Text, + canvas, slider, text_input, Canvas, Color, Column, Element, Length, Point, + Row, Sandbox, Settings, Slider, Text, TextInput, }; use iced_core::palette::{self, Limited}; pub fn main() { - ColorPalette::run(Settings::default()) + ColorPalette::run(Settings { + antialiasing: true, + ..Settings::default() + }) } #[derive(Debug, Default)] @@ -56,6 +59,18 @@ pub struct ColorPalette { hwb_sliders: [slider::State; 3], lab_sliders: [slider::State; 3], lch_sliders: [slider::State; 3], + rgb_text_state: text_input::State, + hsl_text_state: text_input::State, + hsv_text_state: text_input::State, + hwb_text_state: text_input::State, + lab_text_state: text_input::State, + lch_text_state: text_input::State, + rgb_text_value: String, + hsl_text_value: String, + hsv_text_value: String, + hwb_text_value: String, + lab_text_value: String, + lch_text_value: String, canvas_layer: canvas::layer::Cache, } @@ -67,6 +82,7 @@ pub enum Message { HwbColorChanged(palette::Hwb), LabColorChanged(palette::Lab), LchColorChanged(palette::Lch), + TextInput, } impl Sandbox for ColorPalette { @@ -81,14 +97,34 @@ impl Sandbox for ColorPalette { ] } + let state = State::new(); + let rgb_text_value = color_str(&state.color, ColorFormat::Rgb); + let hsl_text_value = color_str(&state.color, ColorFormat::Hsl); + let hsv_text_value = color_str(&state.color, ColorFormat::Hsv); + let hwb_text_value = color_str(&state.color, ColorFormat::Hwb); + let lab_text_value = color_str(&state.color, ColorFormat::Lab); + let lch_text_value = color_str(&state.color, ColorFormat::Lch); + ColorPalette { - state: State::new(), + state, rgb_sliders: triple_slider(), hsl_sliders: triple_slider(), hsv_sliders: triple_slider(), hwb_sliders: triple_slider(), lab_sliders: triple_slider(), lch_sliders: triple_slider(), + rgb_text_state: text_input::State::new(), + hsl_text_state: text_input::State::new(), + hsv_text_state: text_input::State::new(), + hwb_text_state: text_input::State::new(), + lab_text_state: text_input::State::new(), + lch_text_state: text_input::State::new(), + rgb_text_value, + hsl_text_value, + hsv_text_value, + hwb_text_value, + lab_text_value, + lch_text_value, canvas_layer: canvas::layer::Cache::new(), } } @@ -98,6 +134,11 @@ impl Sandbox for ColorPalette { } fn update(&mut self, message: Message) { + match message { + Message::TextInput => return, + _ => {} + } + let mut srgb = match message { Message::RgbColorChanged(rgb) => palette::Srgb::from(rgb), Message::HslColorChanged(hsl) => palette::Srgb::from(hsl), @@ -105,6 +146,7 @@ impl Sandbox for ColorPalette { Message::HwbColorChanged(hwb) => palette::Srgb::from(hwb), Message::LabColorChanged(lab) => palette::Srgb::from(lab), Message::LchColorChanged(lch) => palette::Srgb::from(lch), + _ => return, }; srgb.clamp_self(); self.canvas_layer.clear(); @@ -112,6 +154,14 @@ impl Sandbox for ColorPalette { // Set theme colors self.state.theme = generate_theme(&self.state.color); + + // Set text + self.rgb_text_value = color_str(&self.state.color, ColorFormat::Rgb); + self.hsl_text_value = color_str(&self.state.color, ColorFormat::Hsl); + self.hsv_text_value = color_str(&self.state.color, ColorFormat::Hsv); + self.hwb_text_value = color_str(&self.state.color, ColorFormat::Hwb); + self.lab_text_value = color_str(&self.state.color, ColorFormat::Lab); + self.lch_text_value = color_str(&self.state.color, ColorFormat::Lch); } fn view(&mut self) -> Element { @@ -131,12 +181,12 @@ impl Sandbox for ColorPalette { let lch = palette::Lch::from(srgb); Column::new() - .padding(20) - .spacing(20) + .padding(10) + .spacing(10) .push( Row::new() .spacing(10) - .push(Text::new("RGB")) + .push(Text::new("RGB").width(Length::Units(50))) .push(Slider::new(rgb1, 0.0..=1.0, color.r, move |r| { Message::RgbColorChanged(Color { r, ..color }) })) @@ -145,12 +195,23 @@ impl Sandbox for ColorPalette { })) .push(Slider::new(rgb3, 0.0..=1.0, color.b, move |b| { Message::RgbColorChanged(Color { b, ..color }) - })), + })) + .push( + TextInput::new( + &mut self.rgb_text_state, + "", + &mut self.rgb_text_value, + |_s| Message::TextInput, + ) + .width(Length::Units(150)) + .size(14) + .padding(2), + ), ) .push( Row::new() .spacing(10) - .push(Text::new("HSL")) + .push(Text::new("HSL").width(Length::Units(50))) .push(Slider::new( hsl1, 0.0..=360.0, @@ -183,12 +244,23 @@ impl Sandbox for ColorPalette { ..hsl }) }, - )), + )) + .push( + TextInput::new( + &mut self.hsl_text_state, + "", + &mut self.hsl_text_value, + |_s| Message::TextInput, + ) + .width(Length::Units(150)) + .size(14) + .padding(2), + ), ) .push( Row::new() .spacing(10) - .push(Text::new("HSV")) + .push(Text::new("HSV").width(Length::Units(50))) .push(Slider::new( hsv1, 0.0..=360.0, @@ -221,12 +293,23 @@ impl Sandbox for ColorPalette { ..hsv }) }, - )), + )) + .push( + TextInput::new( + &mut self.hsv_text_state, + "", + &mut self.hsv_text_value, + |_s| Message::TextInput, + ) + .width(Length::Units(150)) + .size(14) + .padding(2), + ), ) .push( Row::new() .spacing(10) - .push(Text::new("HWB")) + .push(Text::new("HWB").width(Length::Units(50))) .push(Slider::new( hwb1, 0.0..=360.0, @@ -259,12 +342,23 @@ impl Sandbox for ColorPalette { ..hwb }) }, - )), + )) + .push( + TextInput::new( + &mut self.hwb_text_state, + "", + &mut self.hwb_text_value, + |_s| Message::TextInput, + ) + .width(Length::Units(150)) + .size(14) + .padding(2), + ), ) .push( Row::new() .spacing(10) - .push(Text::new("Lab")) + .push(Text::new("Lab").width(Length::Units(50))) .push(Slider::new(lab1, 0.0..=100.0, lab.l, move |l| { Message::LabColorChanged(palette::Lab { l, ..lab }) })) @@ -273,12 +367,23 @@ impl Sandbox for ColorPalette { })) .push(Slider::new(lab3, -128.0..=127.0, lab.b, move |b| { Message::LabColorChanged(palette::Lab { b, ..lab }) - })), + })) + .push( + TextInput::new( + &mut self.lab_text_state, + "", + &mut self.lab_text_value, + |_s| Message::TextInput, + ) + .width(Length::Units(150)) + .size(14) + .padding(2), + ), ) .push( Row::new() .spacing(10) - .push(Text::new("Lch")) + .push(Text::new("Lch").width(Length::Units(50))) .push(Slider::new(lch1, 0.0..=100.0, lch.l, move |l| { Message::LchColorChanged(palette::Lch { l, ..lch }) })) @@ -303,12 +408,24 @@ impl Sandbox for ColorPalette { ..lch }) }, - )), + )) + .push( + TextInput::new( + &mut self.lch_text_state, + "", + &mut self.lch_text_value, + |_s| Message::TextInput, + ) + .width(Length::Units(150)) + .size(14) + .padding(2), + ), ) .push( Canvas::new() .width(Length::Fill) - .height(Length::Units(150)) + // .height(Length::Units(250)) + .height(Length::Fill) .push(self.canvas_layer.with(&self.state)), ) .into() @@ -317,7 +434,7 @@ impl Sandbox for ColorPalette { impl State { pub fn new() -> State { - let base = Color::from_rgb8(27, 135, 199); + let base = Color::from_rgb8(75, 128, 190); State { color: base, theme: generate_theme(&base), @@ -328,6 +445,7 @@ impl State { impl canvas::Drawable for State { fn draw(&self, frame: &mut canvas::Frame) { use canvas::{Fill, Path}; + use iced::{HorizontalAlignment, VerticalAlignment}; use palette::{Hsl, Srgb}; if self.theme.len() == 0 { @@ -335,10 +453,16 @@ impl canvas::Drawable for State { return; } - let pad = 5.0; + let pad = 20.0; let box_width = frame.width() / self.theme.len() as f32; let box_height = frame.height() / 2.0 - pad; + + let mut text = canvas::Text::default(); + text.horizontal_alignment = HorizontalAlignment::Left; + text.vertical_alignment = VerticalAlignment::Top; + text.size = 15.0; + for i in 0..self.theme.len() { let anchor = Point { x: (i as f32) * box_width, @@ -360,6 +484,57 @@ impl canvas::Drawable for State { }); }); frame.fill(&rect, Fill::Color(self.theme[i])); + + if self.theme[i] == self.color { + let cx = anchor.x + box_width / 2.0; + let tri_w = 10.0; + + let tri = Path::new(|path| { + path.move_to(Point { + x: cx - tri_w, + y: 0.0, + }); + path.line_to(Point { + x: cx + tri_w, + y: 0.0, + }); + path.line_to(Point { x: cx, y: tri_w }); + path.line_to(Point { + x: cx - tri_w, + y: 0.0, + }); + }); + frame.fill(&tri, Fill::Color(Color::WHITE)); + + let tri = Path::new(|path| { + path.move_to(Point { + x: cx - tri_w, + y: box_height, + }); + path.line_to(Point { + x: cx + tri_w, + y: box_height, + }); + path.line_to(Point { + x: cx, + y: box_height - tri_w, + }); + path.line_to(Point { + x: cx - tri_w, + y: box_height, + }); + }); + frame.fill(&tri, Fill::Color(Color::WHITE)); + } + + frame.fill_text(canvas::Text { + content: color_str(&self.theme[i], ColorFormat::Hex), + position: Point { + x: anchor.x, + y: box_height, + }, + ..text + }); } let hsl = Hsl::from(Srgb::from(self.color)); @@ -391,6 +566,76 @@ impl canvas::Drawable for State { }); }); frame.fill(&rect, Fill::Color(color)); + + frame.fill_text(canvas::Text { + content: color_str(&color, ColorFormat::Hex), + position: Point { + x: anchor.x, + y: box_height + 2.0 * pad - 15.0, + }, + ..text + }); + } + } +} + +enum ColorFormat { + Hex, + Rgb, + Hsl, + Hsv, + Hwb, + Lab, + Lch, +} + +fn color_str(color: &Color, color_format: ColorFormat) -> String { + let srgb = palette::Srgb::from(*color); + let hsl = palette::Hsl::from(srgb); + let hsv = palette::Hsv::from(srgb); + let hwb = palette::Hwb::from(srgb); + let lab = palette::Lab::from(srgb); + let lch = palette::Lch::from(srgb); + + match color_format { + ColorFormat::Hex => format!( + "#{:x}{:x}{:x}", + (255.0 * color.r).round() as u8, + (255.0 * color.g).round() as u8, + (255.0 * color.b).round() as u8 + ), + ColorFormat::Rgb => format!( + "rgb({:.0}, {:.0}, {:.0})", + 255.0 * color.r, + 255.0 * color.g, + 255.0 * color.b + ), + ColorFormat::Hsl => format!( + "hsl({:.1}, {:.1}%, {:.1}%)", + hsl.hue.to_positive_degrees(), + 100.0 * hsl.saturation, + 100.0 * hsl.lightness + ), + ColorFormat::Hsv => format!( + "hsv({:.1}, {:.1}%, {:.1}%)", + hsv.hue.to_positive_degrees(), + 100.0 * hsv.saturation, + 100.0 * hsv.value + ), + ColorFormat::Hwb => format!( + "hwb({:.1}, {:.1}%, {:.1}%)", + hwb.hue.to_positive_degrees(), + 100.0 * hwb.whiteness, + 100.0 * hwb.blackness + ), + ColorFormat::Lab => { + format!("Lab({:.1}, {:.1}, {:.1})", lab.l, lab.a, lab.b) } + ColorFormat::Lch => format!( + "Lch({:.1}, {:.1}, {:.1})", + lch.l, + lch.chroma, + lch.hue.to_positive_degrees() + ), } } -- cgit From 4b90241ea1d2139464587ce8475aeebbf283abc7 Mon Sep 17 00:00:00 2001 From: Clark Moody Date: Fri, 10 Apr 2020 14:59:57 -0500 Subject: Hex label text alignment --- examples/color_palette/src/main.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/examples/color_palette/src/main.rs b/examples/color_palette/src/main.rs index fc733787..b80db299 100644 --- a/examples/color_palette/src/main.rs +++ b/examples/color_palette/src/main.rs @@ -459,7 +459,7 @@ impl canvas::Drawable for State { let box_height = frame.height() / 2.0 - pad; let mut text = canvas::Text::default(); - text.horizontal_alignment = HorizontalAlignment::Left; + text.horizontal_alignment = HorizontalAlignment::Center; text.vertical_alignment = VerticalAlignment::Top; text.size = 15.0; @@ -530,13 +530,15 @@ impl canvas::Drawable for State { frame.fill_text(canvas::Text { content: color_str(&self.theme[i], ColorFormat::Hex), position: Point { - x: anchor.x, + x: anchor.x + box_width / 2.0, y: box_height, }, ..text }); } + text.vertical_alignment = VerticalAlignment::Bottom; + let hsl = Hsl::from(Srgb::from(self.color)); for i in 0..self.theme.len() { let pct = (i as f32 + 1.0) / (self.theme.len() as f32 + 1.0); @@ -570,8 +572,8 @@ impl canvas::Drawable for State { frame.fill_text(canvas::Text { content: color_str(&color, ColorFormat::Hex), position: Point { - x: anchor.x, - y: box_height + 2.0 * pad - 15.0, + x: anchor.x + box_width / 2.0, + y: box_height + 2.0 * pad, }, ..text }); -- cgit From 27fadad3246d555f52b991230a0352353d6700b4 Mon Sep 17 00:00:00 2001 From: Clark Moody Date: Fri, 24 Apr 2020 15:20:00 -0500 Subject: Do not re-export Palette from iced_core --- core/src/lib.rs | 3 --- examples/color_palette/Cargo.toml | 4 +--- examples/color_palette/src/main.rs | 2 +- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/core/src/lib.rs b/core/src/lib.rs index ca6013da..c2887a0b 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -35,6 +35,3 @@ pub use point::Point; pub use rectangle::Rectangle; pub use size::Size; pub use vector::Vector; - -#[cfg(feature = "palette")] -pub use palette; diff --git a/examples/color_palette/Cargo.toml b/examples/color_palette/Cargo.toml index ad7a0114..61c9f6b2 100644 --- a/examples/color_palette/Cargo.toml +++ b/examples/color_palette/Cargo.toml @@ -5,10 +5,8 @@ authors = ["Clark Moody "] edition = "2018" publish = false -[features] -palette = [] - [dependencies] iced = { path = "../..", features = ["canvas", "palette"] } iced_core = { path = "../../core" } iced_native = { path = "../../native" } +palette = "0.5.0" diff --git a/examples/color_palette/src/main.rs b/examples/color_palette/src/main.rs index b80db299..576a0e64 100644 --- a/examples/color_palette/src/main.rs +++ b/examples/color_palette/src/main.rs @@ -2,7 +2,7 @@ use iced::{ canvas, slider, text_input, Canvas, Color, Column, Element, Length, Point, Row, Sandbox, Settings, Slider, Text, TextInput, }; -use iced_core::palette::{self, Limited}; +use palette::{self, Limited}; pub fn main() { ColorPalette::run(Settings { -- cgit From 758a444d7f11809959aa73d7da32f06e98ecc89b Mon Sep 17 00:00:00 2001 From: Clark Moody Date: Fri, 24 Apr 2020 15:31:12 -0500 Subject: Replace text input fields for simple text --- examples/color_palette/src/main.rs | 95 ++++++++------------------------------ 1 file changed, 20 insertions(+), 75 deletions(-) diff --git a/examples/color_palette/src/main.rs b/examples/color_palette/src/main.rs index 576a0e64..464dc828 100644 --- a/examples/color_palette/src/main.rs +++ b/examples/color_palette/src/main.rs @@ -1,6 +1,6 @@ use iced::{ - canvas, slider, text_input, Canvas, Color, Column, Element, Length, Point, - Row, Sandbox, Settings, Slider, Text, TextInput, + canvas, slider, Canvas, Color, Column, Element, Length, Point, Row, + Sandbox, Settings, Slider, Text, }; use palette::{self, Limited}; @@ -59,12 +59,6 @@ pub struct ColorPalette { hwb_sliders: [slider::State; 3], lab_sliders: [slider::State; 3], lch_sliders: [slider::State; 3], - rgb_text_state: text_input::State, - hsl_text_state: text_input::State, - hsv_text_state: text_input::State, - hwb_text_state: text_input::State, - lab_text_state: text_input::State, - lch_text_state: text_input::State, rgb_text_value: String, hsl_text_value: String, hsv_text_value: String, @@ -82,7 +76,6 @@ pub enum Message { HwbColorChanged(palette::Hwb), LabColorChanged(palette::Lab), LchColorChanged(palette::Lch), - TextInput, } impl Sandbox for ColorPalette { @@ -113,12 +106,6 @@ impl Sandbox for ColorPalette { hwb_sliders: triple_slider(), lab_sliders: triple_slider(), lch_sliders: triple_slider(), - rgb_text_state: text_input::State::new(), - hsl_text_state: text_input::State::new(), - hsv_text_state: text_input::State::new(), - hwb_text_state: text_input::State::new(), - lab_text_state: text_input::State::new(), - lch_text_state: text_input::State::new(), rgb_text_value, hsl_text_value, hsv_text_value, @@ -134,11 +121,6 @@ impl Sandbox for ColorPalette { } fn update(&mut self, message: Message) { - match message { - Message::TextInput => return, - _ => {} - } - let mut srgb = match message { Message::RgbColorChanged(rgb) => palette::Srgb::from(rgb), Message::HslColorChanged(hsl) => palette::Srgb::from(hsl), @@ -146,7 +128,6 @@ impl Sandbox for ColorPalette { Message::HwbColorChanged(hwb) => palette::Srgb::from(hwb), Message::LabColorChanged(lab) => palette::Srgb::from(lab), Message::LchColorChanged(lch) => palette::Srgb::from(lch), - _ => return, }; srgb.clamp_self(); self.canvas_layer.clear(); @@ -197,15 +178,9 @@ impl Sandbox for ColorPalette { Message::RgbColorChanged(Color { b, ..color }) })) .push( - TextInput::new( - &mut self.rgb_text_state, - "", - &mut self.rgb_text_value, - |_s| Message::TextInput, - ) - .width(Length::Units(150)) - .size(14) - .padding(2), + Text::new(&self.rgb_text_value) + .width(Length::Units(185)) + .size(16), ), ) .push( @@ -246,15 +221,9 @@ impl Sandbox for ColorPalette { }, )) .push( - TextInput::new( - &mut self.hsl_text_state, - "", - &mut self.hsl_text_value, - |_s| Message::TextInput, - ) - .width(Length::Units(150)) - .size(14) - .padding(2), + Text::new(&self.hsl_text_value) + .width(Length::Units(185)) + .size(16), ), ) .push( @@ -295,15 +264,9 @@ impl Sandbox for ColorPalette { }, )) .push( - TextInput::new( - &mut self.hsv_text_state, - "", - &mut self.hsv_text_value, - |_s| Message::TextInput, - ) - .width(Length::Units(150)) - .size(14) - .padding(2), + Text::new(&self.hsv_text_value) + .width(Length::Units(185)) + .size(16), ), ) .push( @@ -344,15 +307,9 @@ impl Sandbox for ColorPalette { }, )) .push( - TextInput::new( - &mut self.hwb_text_state, - "", - &mut self.hwb_text_value, - |_s| Message::TextInput, - ) - .width(Length::Units(150)) - .size(14) - .padding(2), + Text::new(&self.hwb_text_value) + .width(Length::Units(185)) + .size(16), ), ) .push( @@ -369,15 +326,9 @@ impl Sandbox for ColorPalette { Message::LabColorChanged(palette::Lab { b, ..lab }) })) .push( - TextInput::new( - &mut self.lab_text_state, - "", - &mut self.lab_text_value, - |_s| Message::TextInput, - ) - .width(Length::Units(150)) - .size(14) - .padding(2), + Text::new(&self.lab_text_value) + .width(Length::Units(185)) + .size(16), ), ) .push( @@ -410,15 +361,9 @@ impl Sandbox for ColorPalette { }, )) .push( - TextInput::new( - &mut self.lch_text_state, - "", - &mut self.lch_text_value, - |_s| Message::TextInput, - ) - .width(Length::Units(150)) - .size(14) - .padding(2), + Text::new(&self.lch_text_value) + .width(Length::Units(185)) + .size(16), ), ) .push( -- cgit From 3e71eaee37bc3aea85feb0f643dcbd4ecc11d0c4 Mon Sep 17 00:00:00 2001 From: Clark Moody Date: Fri, 24 Apr 2020 15:40:28 -0500 Subject: Use Path::rectangle and Size for drawing swatches --- examples/color_palette/src/main.rs | 63 +++++++++++++------------------------- 1 file changed, 21 insertions(+), 42 deletions(-) diff --git a/examples/color_palette/src/main.rs b/examples/color_palette/src/main.rs index 464dc828..76a6bf17 100644 --- a/examples/color_palette/src/main.rs +++ b/examples/color_palette/src/main.rs @@ -1,6 +1,6 @@ use iced::{ - canvas, slider, Canvas, Color, Column, Element, Length, Point, Row, - Sandbox, Settings, Slider, Text, + canvas, slider, Canvas, Color, Column, Element, Length, Row, Sandbox, + Settings, Slider, Text, }; use palette::{self, Limited}; @@ -391,6 +391,7 @@ impl canvas::Drawable for State { fn draw(&self, frame: &mut canvas::Frame) { use canvas::{Fill, Path}; use iced::{HorizontalAlignment, VerticalAlignment}; + use iced_native::{Point, Size}; use palette::{Hsl, Srgb}; if self.theme.len() == 0 { @@ -400,8 +401,10 @@ impl canvas::Drawable for State { let pad = 20.0; - let box_width = frame.width() / self.theme.len() as f32; - let box_height = frame.height() / 2.0 - pad; + let box_size = Size { + width: frame.width() / self.theme.len() as f32, + height: frame.height() / 2.0 - pad, + }; let mut text = canvas::Text::default(); text.horizontal_alignment = HorizontalAlignment::Center; @@ -410,28 +413,16 @@ impl canvas::Drawable for State { for i in 0..self.theme.len() { let anchor = Point { - x: (i as f32) * box_width, + x: (i as f32) * box_size.width, y: 0.0, }; let rect = Path::new(|path| { - path.move_to(anchor); - path.line_to(Point { - x: anchor.x + box_width, - y: anchor.y, - }); - path.line_to(Point { - x: anchor.x + box_width, - y: anchor.y + box_height, - }); - path.line_to(Point { - x: anchor.x, - y: anchor.y + box_height, - }); + path.rectangle(anchor, box_size); }); frame.fill(&rect, Fill::Color(self.theme[i])); if self.theme[i] == self.color { - let cx = anchor.x + box_width / 2.0; + let cx = anchor.x + box_size.width / 2.0; let tri_w = 10.0; let tri = Path::new(|path| { @@ -454,19 +445,19 @@ impl canvas::Drawable for State { let tri = Path::new(|path| { path.move_to(Point { x: cx - tri_w, - y: box_height, + y: box_size.height, }); path.line_to(Point { x: cx + tri_w, - y: box_height, + y: box_size.height, }); path.line_to(Point { x: cx, - y: box_height - tri_w, + y: box_size.height - tri_w, }); path.line_to(Point { x: cx - tri_w, - y: box_height, + y: box_size.height, }); }); frame.fill(&tri, Fill::Color(Color::WHITE)); @@ -475,8 +466,8 @@ impl canvas::Drawable for State { frame.fill_text(canvas::Text { content: color_str(&self.theme[i], ColorFormat::Hex), position: Point { - x: anchor.x + box_width / 2.0, - y: box_height, + x: anchor.x + box_size.width / 2.0, + y: box_size.height, }, ..text }); @@ -494,31 +485,19 @@ impl canvas::Drawable for State { let color: Color = Srgb::from(graded.clamp()).into(); let anchor = Point { - x: (i as f32) * box_width, - y: box_height + 2.0 * pad, + x: (i as f32) * box_size.width, + y: box_size.height + 2.0 * pad, }; let rect = Path::new(|path| { - path.move_to(anchor); - path.line_to(Point { - x: anchor.x + box_width, - y: anchor.y, - }); - path.line_to(Point { - x: anchor.x + box_width, - y: anchor.y + box_height, - }); - path.line_to(Point { - x: anchor.x, - y: anchor.y + box_height, - }); + path.rectangle(anchor, box_size); }); frame.fill(&rect, Fill::Color(color)); frame.fill_text(canvas::Text { content: color_str(&color, ColorFormat::Hex), position: Point { - x: anchor.x + box_width / 2.0, - y: box_height + 2.0 * pad, + x: anchor.x + box_size.width / 2.0, + y: box_size.height + 2.0 * pad, }, ..text }); -- cgit From 430f78a693a87e9ba3ac4638cac96aab57dd3042 Mon Sep 17 00:00:00 2001 From: Clark Moody Date: Mon, 27 Apr 2020 16:25:13 -0500 Subject: Abstract into ColorPicker and ColorSpace trait Each color type implements ColorSpace to define its own representation and update methods. View sliders are implemented on the ColorPicker struct. --- examples/color_palette/src/main.rs | 635 +++++++++++++++++++------------------ 1 file changed, 323 insertions(+), 312 deletions(-) diff --git a/examples/color_palette/src/main.rs b/examples/color_palette/src/main.rs index 76a6bf17..993b7fb0 100644 --- a/examples/color_palette/src/main.rs +++ b/examples/color_palette/src/main.rs @@ -3,6 +3,8 @@ use iced::{ Settings, Slider, Text, }; use palette::{self, Limited}; +use std::marker::PhantomData; +use std::ops::RangeInclusive; pub fn main() { ColorPalette::run(Settings { @@ -51,20 +53,288 @@ fn generate_theme(base_color: &Color) -> Vec { theme } +struct ColorPicker { + sliders: [slider::State; 3], + color_space: PhantomData, +} + +trait ColorSpace: Sized { + const LABEL: &'static str; + const COMPONENT_RANGES: [RangeInclusive; 3]; + + fn new(a: f32, b: f32, c: f32) -> Self; + + fn components(&self) -> [f32; 3]; + + fn update_component(c: Self, i: usize, val: f32) -> Self; + + fn to_string(&self) -> String; +} + +impl ColorPicker { + fn view(&mut self, color: C) -> Element { + let [c1, c2, c3] = color.components(); + let [s1, s2, s3] = &mut self.sliders; + let [cr1, cr2, cr3] = C::COMPONENT_RANGES; + Row::new() + .spacing(10) + .push(Text::new(C::LABEL).width(Length::Units(50))) + .push(Slider::new(s1, cr1, c1, move |v| { + C::update_component(color, 0, v) + })) + .push(Slider::new(s2, cr2, c2, move |v| { + C::update_component(color, 1, v) + })) + .push(Slider::new(s3, cr3, c3, move |v| { + C::update_component(color, 2, v) + })) + .push( + Text::new(color.to_string()) + .width(Length::Units(185)) + .size(16), + ) + .into() + } +} + +impl ColorSpace for Color { + const LABEL: &'static str = "RGB"; + const COMPONENT_RANGES: [RangeInclusive; 3] = + [0.0..=1.0, 0.0..=1.0, 0.0..=1.0]; + + fn new(r: f32, g: f32, b: f32) -> Self { + Color::from_rgb(r, g, b) + } + + fn components(&self) -> [f32; 3] { + [self.r, self.g, self.b] + } + + fn update_component(c: Color, i: usize, val: f32) -> Self { + match i { + 0 => Color { r: val, ..c }, + 1 => Color { g: val, ..c }, + 2 => Color { b: val, ..c }, + _ => panic!("Invalid component index: {:?}", i), + } + } + + fn to_string(&self) -> String { + format!( + "rgb({:.0}, {:.0}, {:.0})", + 255.0 * self.r, + 255.0 * self.g, + 255.0 * self.b + ) + } +} + +impl ColorSpace for palette::Hsl { + const LABEL: &'static str = "HSL"; + const COMPONENT_RANGES: [RangeInclusive; 3] = + [0.0..=360.0, 0.0..=1.0, 0.0..=1.0]; + + fn new(hue: f32, saturation: f32, lightness: f32) -> Self { + palette::Hsl::new( + palette::RgbHue::from_degrees(hue), + saturation, + lightness, + ) + } + + fn components(&self) -> [f32; 3] { + [ + self.hue.to_positive_degrees(), + self.saturation, + self.lightness, + ] + } + + fn update_component(c: palette::Hsl, i: usize, val: f32) -> Self { + match i { + 0 => palette::Hsl { + hue: palette::RgbHue::from_degrees(val), + ..c + }, + 1 => palette::Hsl { + saturation: val, + ..c + }, + 2 => palette::Hsl { + lightness: val, + ..c + }, + _ => panic!("Invalid component index: {:?}", i), + } + } + + fn to_string(&self) -> String { + format!( + "hsl({:.1}, {:.1}%, {:.1}%)", + self.hue.to_positive_degrees(), + 100.0 * self.saturation, + 100.0 * self.lightness + ) + } +} + +impl ColorSpace for palette::Hsv { + const LABEL: &'static str = "HSV"; + const COMPONENT_RANGES: [RangeInclusive; 3] = + [0.0..=360.0, 0.0..=1.0, 0.0..=1.0]; + + fn new(hue: f32, saturation: f32, value: f32) -> Self { + palette::Hsv::new(palette::RgbHue::from_degrees(hue), saturation, value) + } + + fn components(&self) -> [f32; 3] { + [self.hue.to_positive_degrees(), self.saturation, self.value] + } + + fn update_component(c: palette::Hsv, i: usize, val: f32) -> Self { + match i { + 0 => palette::Hsv { + hue: palette::RgbHue::from_degrees(val), + ..c + }, + 1 => palette::Hsv { + saturation: val, + ..c + }, + 2 => palette::Hsv { value: val, ..c }, + _ => panic!("Invalid component index: {:?}", i), + } + } + + fn to_string(&self) -> String { + format!( + "hsv({:.1}, {:.1}%, {:.1}%)", + self.hue.to_positive_degrees(), + 100.0 * self.saturation, + 100.0 * self.value + ) + } +} + +impl ColorSpace for palette::Hwb { + const LABEL: &'static str = "HWB"; + const COMPONENT_RANGES: [RangeInclusive; 3] = + [0.0..=360.0, 0.0..=1.0, 0.0..=1.0]; + + fn new(hue: f32, whiteness: f32, blackness: f32) -> Self { + palette::Hwb::new( + palette::RgbHue::from_degrees(hue), + whiteness, + blackness, + ) + } + + fn components(&self) -> [f32; 3] { + [ + self.hue.to_positive_degrees(), + self.whiteness, + self.blackness, + ] + } + + fn update_component(c: palette::Hwb, i: usize, val: f32) -> Self { + match i { + 0 => palette::Hwb { + hue: palette::RgbHue::from_degrees(val), + ..c + }, + 1 => palette::Hwb { + whiteness: val, + ..c + }, + 2 => palette::Hwb { + blackness: val, + ..c + }, + _ => panic!("Invalid component index: {:?}", i), + } + } + + fn to_string(&self) -> String { + format!( + "hwb({:.1}, {:.1}%, {:.1}%)", + self.hue.to_positive_degrees(), + 100.0 * self.whiteness, + 100.0 * self.blackness + ) + } +} + +impl ColorSpace for palette::Lab { + const LABEL: &'static str = "Lab"; + const COMPONENT_RANGES: [RangeInclusive; 3] = + [0.0..=100.0, -128.0..=127.0, -128.0..=127.0]; + + fn new(l: f32, a: f32, b: f32) -> Self { + palette::Lab::new(l, a, b) + } + + fn components(&self) -> [f32; 3] { + [self.l, self.a, self.b] + } + + fn update_component(c: palette::Lab, i: usize, val: f32) -> Self { + match i { + 0 => palette::Lab { l: val, ..c }, + 1 => palette::Lab { a: val, ..c }, + 2 => palette::Lab { b: val, ..c }, + _ => panic!("Invalid component index: {:?}", i), + } + } + + fn to_string(&self) -> String { + format!("Lab({:.1}, {:.1}, {:.1})", self.l, self.a, self.b) + } +} + +impl ColorSpace for palette::Lch { + const LABEL: &'static str = "Lch"; + const COMPONENT_RANGES: [RangeInclusive; 3] = + [0.0..=100.0, 0.0..=128.0, 0.0..=360.0]; + + fn new(l: f32, chroma: f32, hue: f32) -> Self { + palette::Lch::new(l, chroma, palette::LabHue::from_degrees(hue)) + } + + fn components(&self) -> [f32; 3] { + [self.l, self.chroma, self.hue.to_positive_degrees()] + } + + fn update_component(c: palette::Lch, i: usize, val: f32) -> Self { + match i { + 0 => palette::Lch { l: val, ..c }, + 1 => palette::Lch { chroma: val, ..c }, + 2 => palette::Lch { + hue: palette::LabHue::from_degrees(val), + ..c + }, + _ => panic!("Invalid component index: {:?}", i), + } + } + + fn to_string(&self) -> String { + format!( + "Lch({:.1}, {:.1}, {:.1})", + self.l, + self.chroma, + self.hue.to_positive_degrees() + ) + } +} + pub struct ColorPalette { state: State, - rgb_sliders: [slider::State; 3], - hsl_sliders: [slider::State; 3], - hsv_sliders: [slider::State; 3], - hwb_sliders: [slider::State; 3], - lab_sliders: [slider::State; 3], - lch_sliders: [slider::State; 3], - rgb_text_value: String, - hsl_text_value: String, - hsv_text_value: String, - hwb_text_value: String, - lab_text_value: String, - lch_text_value: String, + rgb: ColorPicker, + hsl: ColorPicker, + hsv: ColorPicker, + hwb: ColorPicker, + lab: ColorPicker, + lch: ColorPicker, canvas_layer: canvas::layer::Cache, } @@ -90,28 +360,33 @@ impl Sandbox for ColorPalette { ] } - let state = State::new(); - let rgb_text_value = color_str(&state.color, ColorFormat::Rgb); - let hsl_text_value = color_str(&state.color, ColorFormat::Hsl); - let hsv_text_value = color_str(&state.color, ColorFormat::Hsv); - let hwb_text_value = color_str(&state.color, ColorFormat::Hwb); - let lab_text_value = color_str(&state.color, ColorFormat::Lab); - let lch_text_value = color_str(&state.color, ColorFormat::Lch); - ColorPalette { - state, - rgb_sliders: triple_slider(), - hsl_sliders: triple_slider(), - hsv_sliders: triple_slider(), - hwb_sliders: triple_slider(), - lab_sliders: triple_slider(), - lch_sliders: triple_slider(), - rgb_text_value, - hsl_text_value, - hsv_text_value, - hwb_text_value, - lab_text_value, - lch_text_value, + state: State::new(), + rgb: ColorPicker { + sliders: triple_slider(), + color_space: PhantomData::, + }, + hsl: ColorPicker { + sliders: triple_slider(), + color_space: PhantomData::, + }, + hsv: ColorPicker { + sliders: triple_slider(), + color_space: PhantomData::, + }, + + hwb: ColorPicker { + sliders: triple_slider(), + color_space: PhantomData::, + }, + lab: ColorPicker { + sliders: triple_slider(), + color_space: PhantomData::, + }, + lch: ColorPicker { + sliders: triple_slider(), + color_space: PhantomData::, + }, canvas_layer: canvas::layer::Cache::new(), } } @@ -135,24 +410,9 @@ impl Sandbox for ColorPalette { // Set theme colors self.state.theme = generate_theme(&self.state.color); - - // Set text - self.rgb_text_value = color_str(&self.state.color, ColorFormat::Rgb); - self.hsl_text_value = color_str(&self.state.color, ColorFormat::Hsl); - self.hsv_text_value = color_str(&self.state.color, ColorFormat::Hsv); - self.hwb_text_value = color_str(&self.state.color, ColorFormat::Hwb); - self.lab_text_value = color_str(&self.state.color, ColorFormat::Lab); - self.lch_text_value = color_str(&self.state.color, ColorFormat::Lch); } fn view(&mut self) -> Element { - let [rgb1, rgb2, rgb3] = &mut self.rgb_sliders; - let [hsl1, hsl2, hsl3] = &mut self.hsl_sliders; - let [hsv1, hsv2, hsv3] = &mut self.hsv_sliders; - let [hwb1, hwb2, hwb3] = &mut self.hwb_sliders; - let [lab1, lab2, lab3] = &mut self.lab_sliders; - let [lch1, lch2, lch3] = &mut self.lch_sliders; - let color = self.state.color; let srgb = palette::Srgb::from(self.state.color); let hsl = palette::Hsl::from(srgb); @@ -164,208 +424,12 @@ impl Sandbox for ColorPalette { Column::new() .padding(10) .spacing(10) - .push( - Row::new() - .spacing(10) - .push(Text::new("RGB").width(Length::Units(50))) - .push(Slider::new(rgb1, 0.0..=1.0, color.r, move |r| { - Message::RgbColorChanged(Color { r, ..color }) - })) - .push(Slider::new(rgb2, 0.0..=1.0, color.g, move |g| { - Message::RgbColorChanged(Color { g, ..color }) - })) - .push(Slider::new(rgb3, 0.0..=1.0, color.b, move |b| { - Message::RgbColorChanged(Color { b, ..color }) - })) - .push( - Text::new(&self.rgb_text_value) - .width(Length::Units(185)) - .size(16), - ), - ) - .push( - Row::new() - .spacing(10) - .push(Text::new("HSL").width(Length::Units(50))) - .push(Slider::new( - hsl1, - 0.0..=360.0, - hsl.hue.to_positive_degrees(), - move |hue| { - Message::HslColorChanged(palette::Hsl { - hue: palette::RgbHue::from_degrees(hue), - ..hsl - }) - }, - )) - .push(Slider::new( - hsl2, - 0.0..=1.0, - hsl.saturation, - move |saturation| { - Message::HslColorChanged(palette::Hsl { - saturation, - ..hsl - }) - }, - )) - .push(Slider::new( - hsl3, - 0.0..=1.0, - hsl.lightness, - move |lightness| { - Message::HslColorChanged(palette::Hsl { - lightness, - ..hsl - }) - }, - )) - .push( - Text::new(&self.hsl_text_value) - .width(Length::Units(185)) - .size(16), - ), - ) - .push( - Row::new() - .spacing(10) - .push(Text::new("HSV").width(Length::Units(50))) - .push(Slider::new( - hsv1, - 0.0..=360.0, - hsv.hue.to_positive_degrees(), - move |hue| { - Message::HsvColorChanged(palette::Hsv { - hue: palette::RgbHue::from_degrees(hue), - ..hsv - }) - }, - )) - .push(Slider::new( - hsv2, - 0.0..=1.0, - hsv.saturation, - move |saturation| { - Message::HsvColorChanged(palette::Hsv { - saturation, - ..hsv - }) - }, - )) - .push(Slider::new( - hsv3, - 0.0..=1.0, - hsv.value, - move |value| { - Message::HsvColorChanged(palette::Hsv { - value, - ..hsv - }) - }, - )) - .push( - Text::new(&self.hsv_text_value) - .width(Length::Units(185)) - .size(16), - ), - ) - .push( - Row::new() - .spacing(10) - .push(Text::new("HWB").width(Length::Units(50))) - .push(Slider::new( - hwb1, - 0.0..=360.0, - hwb.hue.to_positive_degrees(), - move |hue| { - Message::HwbColorChanged(palette::Hwb { - hue: palette::RgbHue::from_degrees(hue), - ..hwb - }) - }, - )) - .push(Slider::new( - hwb2, - 0.0..=1.0, - hwb.whiteness, - move |whiteness| { - Message::HwbColorChanged(palette::Hwb { - whiteness, - ..hwb - }) - }, - )) - .push(Slider::new( - hwb3, - 0.0..=1.0, - hwb.blackness, - move |blackness| { - Message::HwbColorChanged(palette::Hwb { - blackness, - ..hwb - }) - }, - )) - .push( - Text::new(&self.hwb_text_value) - .width(Length::Units(185)) - .size(16), - ), - ) - .push( - Row::new() - .spacing(10) - .push(Text::new("Lab").width(Length::Units(50))) - .push(Slider::new(lab1, 0.0..=100.0, lab.l, move |l| { - Message::LabColorChanged(palette::Lab { l, ..lab }) - })) - .push(Slider::new(lab2, -128.0..=127.0, lab.a, move |a| { - Message::LabColorChanged(palette::Lab { a, ..lab }) - })) - .push(Slider::new(lab3, -128.0..=127.0, lab.b, move |b| { - Message::LabColorChanged(palette::Lab { b, ..lab }) - })) - .push( - Text::new(&self.lab_text_value) - .width(Length::Units(185)) - .size(16), - ), - ) - .push( - Row::new() - .spacing(10) - .push(Text::new("Lch").width(Length::Units(50))) - .push(Slider::new(lch1, 0.0..=100.0, lch.l, move |l| { - Message::LchColorChanged(palette::Lch { l, ..lch }) - })) - .push(Slider::new( - lch2, - 0.0..=128.0, - lch.chroma, - move |chroma| { - Message::LchColorChanged(palette::Lch { - chroma, - ..lch - }) - }, - )) - .push(Slider::new( - lch3, - 0.0..=360.0, - lch.hue.to_positive_degrees(), - move |hue| { - Message::LchColorChanged(palette::Lch { - hue: palette::LabHue::from_degrees(hue), - ..lch - }) - }, - )) - .push( - Text::new(&self.lch_text_value) - .width(Length::Units(185)) - .size(16), - ), - ) + .push(self.rgb.view(color).map(Message::RgbColorChanged)) + .push(self.hsl.view(hsl).map(Message::HslColorChanged)) + .push(self.hsv.view(hsv).map(Message::HsvColorChanged)) + .push(self.hwb.view(hwb).map(Message::HwbColorChanged)) + .push(self.lab.view(lab).map(Message::LabColorChanged)) + .push(self.lch.view(lch).map(Message::LchColorChanged)) .push( Canvas::new() .width(Length::Fill) @@ -395,7 +459,6 @@ impl canvas::Drawable for State { use palette::{Hsl, Srgb}; if self.theme.len() == 0 { - println!("Zero len"); return; } @@ -464,7 +527,7 @@ impl canvas::Drawable for State { } frame.fill_text(canvas::Text { - content: color_str(&self.theme[i], ColorFormat::Hex), + content: color_hex_str(&self.theme[i]), position: Point { x: anchor.x + box_size.width / 2.0, y: box_size.height, @@ -494,7 +557,7 @@ impl canvas::Drawable for State { frame.fill(&rect, Fill::Color(color)); frame.fill_text(canvas::Text { - content: color_str(&color, ColorFormat::Hex), + content: color_hex_str(&color), position: Point { x: anchor.x + box_size.width / 2.0, y: box_size.height + 2.0 * pad, @@ -505,63 +568,11 @@ impl canvas::Drawable for State { } } -enum ColorFormat { - Hex, - Rgb, - Hsl, - Hsv, - Hwb, - Lab, - Lch, -} - -fn color_str(color: &Color, color_format: ColorFormat) -> String { - let srgb = palette::Srgb::from(*color); - let hsl = palette::Hsl::from(srgb); - let hsv = palette::Hsv::from(srgb); - let hwb = palette::Hwb::from(srgb); - let lab = palette::Lab::from(srgb); - let lch = palette::Lch::from(srgb); - - match color_format { - ColorFormat::Hex => format!( - "#{:x}{:x}{:x}", - (255.0 * color.r).round() as u8, - (255.0 * color.g).round() as u8, - (255.0 * color.b).round() as u8 - ), - ColorFormat::Rgb => format!( - "rgb({:.0}, {:.0}, {:.0})", - 255.0 * color.r, - 255.0 * color.g, - 255.0 * color.b - ), - ColorFormat::Hsl => format!( - "hsl({:.1}, {:.1}%, {:.1}%)", - hsl.hue.to_positive_degrees(), - 100.0 * hsl.saturation, - 100.0 * hsl.lightness - ), - ColorFormat::Hsv => format!( - "hsv({:.1}, {:.1}%, {:.1}%)", - hsv.hue.to_positive_degrees(), - 100.0 * hsv.saturation, - 100.0 * hsv.value - ), - ColorFormat::Hwb => format!( - "hwb({:.1}, {:.1}%, {:.1}%)", - hwb.hue.to_positive_degrees(), - 100.0 * hwb.whiteness, - 100.0 * hwb.blackness - ), - ColorFormat::Lab => { - format!("Lab({:.1}, {:.1}, {:.1})", lab.l, lab.a, lab.b) - } - ColorFormat::Lch => format!( - "Lch({:.1}, {:.1}, {:.1})", - lch.l, - lch.chroma, - lch.hue.to_positive_degrees() - ), - } +fn color_hex_str(color: &Color) -> String { + format!( + "#{:x}{:x}{:x}", + (255.0 * color.r).round() as u8, + (255.0 * color.g).round() as u8, + (255.0 * color.b).round() as u8 + ) } -- cgit From 11e4039b5644606e40d603397f1039686ecd6fe5 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 1 May 2020 21:43:11 +0200 Subject: Remove `update_component` in `color_palette` We can use `ColorSpace::new` instead --- examples/color_palette/src/main.rs | 96 ++------------------------------------ 1 file changed, 4 insertions(+), 92 deletions(-) diff --git a/examples/color_palette/src/main.rs b/examples/color_palette/src/main.rs index 993b7fb0..46a4d085 100644 --- a/examples/color_palette/src/main.rs +++ b/examples/color_palette/src/main.rs @@ -66,8 +66,6 @@ trait ColorSpace: Sized { fn components(&self) -> [f32; 3]; - fn update_component(c: Self, i: usize, val: f32) -> Self; - fn to_string(&self) -> String; } @@ -76,18 +74,13 @@ impl ColorPicker { let [c1, c2, c3] = color.components(); let [s1, s2, s3] = &mut self.sliders; let [cr1, cr2, cr3] = C::COMPONENT_RANGES; + Row::new() .spacing(10) .push(Text::new(C::LABEL).width(Length::Units(50))) - .push(Slider::new(s1, cr1, c1, move |v| { - C::update_component(color, 0, v) - })) - .push(Slider::new(s2, cr2, c2, move |v| { - C::update_component(color, 1, v) - })) - .push(Slider::new(s3, cr3, c3, move |v| { - C::update_component(color, 2, v) - })) + .push(Slider::new(s1, cr1, c1, move |v| C::new(v, c2, c3))) + .push(Slider::new(s2, cr2, c2, move |v| C::new(c1, v, c3))) + .push(Slider::new(s3, cr3, c3, move |v| C::new(c1, c2, v))) .push( Text::new(color.to_string()) .width(Length::Units(185)) @@ -110,15 +103,6 @@ impl ColorSpace for Color { [self.r, self.g, self.b] } - fn update_component(c: Color, i: usize, val: f32) -> Self { - match i { - 0 => Color { r: val, ..c }, - 1 => Color { g: val, ..c }, - 2 => Color { b: val, ..c }, - _ => panic!("Invalid component index: {:?}", i), - } - } - fn to_string(&self) -> String { format!( "rgb({:.0}, {:.0}, {:.0})", @@ -150,24 +134,6 @@ impl ColorSpace for palette::Hsl { ] } - fn update_component(c: palette::Hsl, i: usize, val: f32) -> Self { - match i { - 0 => palette::Hsl { - hue: palette::RgbHue::from_degrees(val), - ..c - }, - 1 => palette::Hsl { - saturation: val, - ..c - }, - 2 => palette::Hsl { - lightness: val, - ..c - }, - _ => panic!("Invalid component index: {:?}", i), - } - } - fn to_string(&self) -> String { format!( "hsl({:.1}, {:.1}%, {:.1}%)", @@ -191,21 +157,6 @@ impl ColorSpace for palette::Hsv { [self.hue.to_positive_degrees(), self.saturation, self.value] } - fn update_component(c: palette::Hsv, i: usize, val: f32) -> Self { - match i { - 0 => palette::Hsv { - hue: palette::RgbHue::from_degrees(val), - ..c - }, - 1 => palette::Hsv { - saturation: val, - ..c - }, - 2 => palette::Hsv { value: val, ..c }, - _ => panic!("Invalid component index: {:?}", i), - } - } - fn to_string(&self) -> String { format!( "hsv({:.1}, {:.1}%, {:.1}%)", @@ -237,24 +188,6 @@ impl ColorSpace for palette::Hwb { ] } - fn update_component(c: palette::Hwb, i: usize, val: f32) -> Self { - match i { - 0 => palette::Hwb { - hue: palette::RgbHue::from_degrees(val), - ..c - }, - 1 => palette::Hwb { - whiteness: val, - ..c - }, - 2 => palette::Hwb { - blackness: val, - ..c - }, - _ => panic!("Invalid component index: {:?}", i), - } - } - fn to_string(&self) -> String { format!( "hwb({:.1}, {:.1}%, {:.1}%)", @@ -278,15 +211,6 @@ impl ColorSpace for palette::Lab { [self.l, self.a, self.b] } - fn update_component(c: palette::Lab, i: usize, val: f32) -> Self { - match i { - 0 => palette::Lab { l: val, ..c }, - 1 => palette::Lab { a: val, ..c }, - 2 => palette::Lab { b: val, ..c }, - _ => panic!("Invalid component index: {:?}", i), - } - } - fn to_string(&self) -> String { format!("Lab({:.1}, {:.1}, {:.1})", self.l, self.a, self.b) } @@ -305,18 +229,6 @@ impl ColorSpace for palette::Lch { [self.l, self.chroma, self.hue.to_positive_degrees()] } - fn update_component(c: palette::Lch, i: usize, val: f32) -> Self { - match i { - 0 => palette::Lch { l: val, ..c }, - 1 => palette::Lch { chroma: val, ..c }, - 2 => palette::Lch { - hue: palette::LabHue::from_degrees(val), - ..c - }, - _ => panic!("Invalid component index: {:?}", i), - } - } - fn to_string(&self) -> String { format!( "Lch({:.1}, {:.1}, {:.1})", -- cgit From 0a011f90313dfbd77da5fdaa58bd93924ba7625c Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 1 May 2020 21:51:08 +0200 Subject: Improve `generate_theme` in `color_palette` --- examples/color_palette/src/main.rs | 42 ++++++++++++++------------------------ 1 file changed, 15 insertions(+), 27 deletions(-) diff --git a/examples/color_palette/src/main.rs b/examples/color_palette/src/main.rs index 46a4d085..12c24a64 100644 --- a/examples/color_palette/src/main.rs +++ b/examples/color_palette/src/main.rs @@ -21,36 +21,24 @@ pub struct State { fn generate_theme(base_color: &Color) -> Vec { use palette::{Hsl, Hue, Shade, Srgb}; - let mut theme = Vec::::new(); + // Convert to HSL color for manipulation let hsl = Hsl::from(Srgb::from(*base_color)); - theme.push( - Srgb::from(hsl.shift_hue(-135.0).lighten(0.075)) - .clamp() - .into(), - ); - theme.push(Srgb::from(hsl.shift_hue(-120.0)).clamp().into()); - theme.push( - Srgb::from(hsl.shift_hue(-105.0).darken(0.075)) - .clamp() - .into(), - ); - theme.push(Srgb::from(hsl.darken(0.075)).clamp().into()); - theme.push(*base_color); - theme.push(Srgb::from(hsl.lighten(0.075)).clamp().into()); - theme.push( - Srgb::from(hsl.shift_hue(105.0).darken(0.075)) - .clamp() - .into(), - ); - theme.push(Srgb::from(hsl.shift_hue(120.0)).clamp().into()); - theme.push( - Srgb::from(hsl.shift_hue(135.0).lighten(0.075)) - .clamp() - .into(), - ); - theme + [ + hsl.shift_hue(-135.0).lighten(0.075), + hsl.shift_hue(-120.0), + hsl.shift_hue(-105.0).darken(0.075), + hsl.darken(0.075), + hsl, + hsl.lighten(0.075), + hsl.shift_hue(105.0).darken(0.075), + hsl.shift_hue(120.0), + hsl.shift_hue(135.0).lighten(0.075), + ] + .iter() + .map(|&color| Srgb::from(color).clamp().into()) + .collect() } struct ColorPicker { -- cgit From 4d724a88e6b8b4f707501c2a45710354f8612b47 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 1 May 2020 22:24:34 +0200 Subject: Introduce `Theme` type in `color_palette` example --- examples/color_palette/src/main.rs | 191 +++++++++++++++++-------------------- 1 file changed, 88 insertions(+), 103 deletions(-) diff --git a/examples/color_palette/src/main.rs b/examples/color_palette/src/main.rs index 12c24a64..97363b75 100644 --- a/examples/color_palette/src/main.rs +++ b/examples/color_palette/src/main.rs @@ -1,6 +1,6 @@ use iced::{ - canvas, slider, Canvas, Color, Column, Element, Length, Row, Sandbox, - Settings, Slider, Text, + canvas, slider, Align, Canvas, Color, Column, Element, Length, Row, + Sandbox, Settings, Slider, Text, }; use palette::{self, Limited}; use std::marker::PhantomData; @@ -13,34 +13,68 @@ pub fn main() { }) } -#[derive(Debug, Default)] -pub struct State { - color: Color, - theme: Vec, +#[derive(Debug)] +pub struct Theme { + lower: Vec, + base: Color, + higher: Vec, } -fn generate_theme(base_color: &Color) -> Vec { - use palette::{Hsl, Hue, Shade, Srgb}; - - // Convert to HSL color for manipulation - let hsl = Hsl::from(Srgb::from(*base_color)); - - [ - hsl.shift_hue(-135.0).lighten(0.075), - hsl.shift_hue(-120.0), - hsl.shift_hue(-105.0).darken(0.075), - hsl.darken(0.075), - hsl, - hsl.lighten(0.075), - hsl.shift_hue(105.0).darken(0.075), - hsl.shift_hue(120.0), - hsl.shift_hue(135.0).lighten(0.075), - ] - .iter() - .map(|&color| Srgb::from(color).clamp().into()) - .collect() +impl Default for Theme { + fn default() -> Self { + Theme::new(Color::from_rgb8(75, 128, 190)) + } +} + +impl Theme { + pub fn new(base: impl Into) -> Theme { + use palette::{Hsl, Hue, Shade, Srgb}; + + let base = base.into(); + + // Convert to HSL color for manipulation + let hsl = Hsl::from(Srgb::from(base)); + + let lower = [ + hsl.shift_hue(-135.0).lighten(0.075), + hsl.shift_hue(-120.0), + hsl.shift_hue(-105.0).darken(0.075), + hsl.darken(0.075), + ]; + + let higher = [ + hsl.lighten(0.075), + hsl.shift_hue(105.0).darken(0.075), + hsl.shift_hue(120.0), + hsl.shift_hue(135.0).lighten(0.075), + ]; + + Theme { + lower: lower + .iter() + .map(|&color| Srgb::from(color).clamp().into()) + .collect(), + base, + higher: higher + .iter() + .map(|&color| Srgb::from(color).clamp().into()) + .collect(), + } + } + + pub fn len(&self) -> usize { + self.lower.len() + self.higher.len() + 1 + } + + pub fn colors(&self) -> impl Iterator { + self.lower + .iter() + .chain(std::iter::once(&self.base)) + .chain(self.higher.iter()) + } } +#[derive(Default)] struct ColorPicker { sliders: [slider::State; 3], color_space: PhantomData, @@ -65,6 +99,7 @@ impl ColorPicker { Row::new() .spacing(10) + .align_items(Align::Center) .push(Text::new(C::LABEL).width(Length::Units(50))) .push(Slider::new(s1, cr1, c1, move |v| C::new(v, c2, c3))) .push(Slider::new(s2, cr2, c2, move |v| C::new(c1, v, c3))) @@ -227,15 +262,16 @@ impl ColorSpace for palette::Lch { } } +#[derive(Default)] pub struct ColorPalette { - state: State, + theme: Theme, rgb: ColorPicker, hsl: ColorPicker, hsv: ColorPicker, hwb: ColorPicker, lab: ColorPicker, lch: ColorPicker, - canvas_layer: canvas::layer::Cache, + canvas_layer: canvas::layer::Cache, } #[derive(Debug, Clone, Copy)] @@ -252,43 +288,7 @@ impl Sandbox for ColorPalette { type Message = Message; fn new() -> Self { - fn triple_slider() -> [slider::State; 3] { - [ - slider::State::new(), - slider::State::new(), - slider::State::new(), - ] - } - - ColorPalette { - state: State::new(), - rgb: ColorPicker { - sliders: triple_slider(), - color_space: PhantomData::, - }, - hsl: ColorPicker { - sliders: triple_slider(), - color_space: PhantomData::, - }, - hsv: ColorPicker { - sliders: triple_slider(), - color_space: PhantomData::, - }, - - hwb: ColorPicker { - sliders: triple_slider(), - color_space: PhantomData::, - }, - lab: ColorPicker { - sliders: triple_slider(), - color_space: PhantomData::, - }, - lch: ColorPicker { - sliders: triple_slider(), - color_space: PhantomData::, - }, - canvas_layer: canvas::layer::Cache::new(), - } + Self::default() } fn title(&self) -> String { @@ -296,7 +296,7 @@ impl Sandbox for ColorPalette { } fn update(&mut self, message: Message) { - let mut srgb = match message { + let srgb = match message { Message::RgbColorChanged(rgb) => palette::Srgb::from(rgb), Message::HslColorChanged(hsl) => palette::Srgb::from(hsl), Message::HsvColorChanged(hsv) => palette::Srgb::from(hsv), @@ -304,17 +304,15 @@ impl Sandbox for ColorPalette { Message::LabColorChanged(lab) => palette::Srgb::from(lab), Message::LchColorChanged(lch) => palette::Srgb::from(lch), }; - srgb.clamp_self(); - self.canvas_layer.clear(); - self.state.color = Color::from(srgb); - // Set theme colors - self.state.theme = generate_theme(&self.state.color); + self.theme = Theme::new(srgb.clamp()); + self.canvas_layer.clear(); } fn view(&mut self) -> Element { - let color = self.state.color; - let srgb = palette::Srgb::from(self.state.color); + let base = self.theme.base; + + let srgb = palette::Srgb::from(base); let hsl = palette::Hsl::from(srgb); let hsv = palette::Hsv::from(srgb); let hwb = palette::Hwb::from(srgb); @@ -324,7 +322,7 @@ impl Sandbox for ColorPalette { Column::new() .padding(10) .spacing(10) - .push(self.rgb.view(color).map(Message::RgbColorChanged)) + .push(self.rgb.view(base).map(Message::RgbColorChanged)) .push(self.hsl.view(hsl).map(Message::HslColorChanged)) .push(self.hsv.view(hsv).map(Message::HsvColorChanged)) .push(self.hwb.view(hwb).map(Message::HwbColorChanged)) @@ -333,48 +331,35 @@ impl Sandbox for ColorPalette { .push( Canvas::new() .width(Length::Fill) - // .height(Length::Units(250)) .height(Length::Fill) - .push(self.canvas_layer.with(&self.state)), + .push(self.canvas_layer.with(&self.theme)), ) .into() } } -impl State { - pub fn new() -> State { - let base = Color::from_rgb8(75, 128, 190); - State { - color: base, - theme: generate_theme(&base), - } - } -} - -impl canvas::Drawable for State { +impl canvas::Drawable for Theme { fn draw(&self, frame: &mut canvas::Frame) { use canvas::{Fill, Path}; use iced::{HorizontalAlignment, VerticalAlignment}; use iced_native::{Point, Size}; use palette::{Hsl, Srgb}; - if self.theme.len() == 0 { - return; - } - let pad = 20.0; let box_size = Size { - width: frame.width() / self.theme.len() as f32, + width: frame.width() / self.len() as f32, height: frame.height() / 2.0 - pad, }; - let mut text = canvas::Text::default(); - text.horizontal_alignment = HorizontalAlignment::Center; - text.vertical_alignment = VerticalAlignment::Top; - text.size = 15.0; + let mut text = canvas::Text { + horizontal_alignment: HorizontalAlignment::Center, + vertical_alignment: VerticalAlignment::Top, + size: 15.0, + ..canvas::Text::default() + }; - for i in 0..self.theme.len() { + for (i, &color) in self.colors().enumerate() { let anchor = Point { x: (i as f32) * box_size.width, y: 0.0, @@ -382,9 +367,9 @@ impl canvas::Drawable for State { let rect = Path::new(|path| { path.rectangle(anchor, box_size); }); - frame.fill(&rect, Fill::Color(self.theme[i])); + frame.fill(&rect, Fill::Color(color)); - if self.theme[i] == self.color { + if self.base == color { let cx = anchor.x + box_size.width / 2.0; let tri_w = 10.0; @@ -427,7 +412,7 @@ impl canvas::Drawable for State { } frame.fill_text(canvas::Text { - content: color_hex_str(&self.theme[i]), + content: color_hex_str(&color), position: Point { x: anchor.x + box_size.width / 2.0, y: box_size.height, @@ -438,9 +423,9 @@ impl canvas::Drawable for State { text.vertical_alignment = VerticalAlignment::Bottom; - let hsl = Hsl::from(Srgb::from(self.color)); - for i in 0..self.theme.len() { - let pct = (i as f32 + 1.0) / (self.theme.len() as f32 + 1.0); + let hsl = Hsl::from(Srgb::from(self.base)); + for i in 0..self.len() { + let pct = (i as f32 + 1.0) / (self.len() as f32 + 1.0); let graded = Hsl { lightness: 1.0 - pct, ..hsl -- cgit From 555371f77e02c962c2312dab7f1f2510b03e352a Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 1 May 2020 22:27:14 +0200 Subject: Move application implementation in `color_palette` --- examples/color_palette/src/main.rs | 412 ++++++++++++++++++------------------- 1 file changed, 206 insertions(+), 206 deletions(-) diff --git a/examples/color_palette/src/main.rs b/examples/color_palette/src/main.rs index 97363b75..243fae1d 100644 --- a/examples/color_palette/src/main.rs +++ b/examples/color_palette/src/main.rs @@ -13,6 +13,82 @@ pub fn main() { }) } +#[derive(Default)] +pub struct ColorPalette { + theme: Theme, + rgb: ColorPicker, + hsl: ColorPicker, + hsv: ColorPicker, + hwb: ColorPicker, + lab: ColorPicker, + lch: ColorPicker, + canvas_layer: canvas::layer::Cache, +} + +#[derive(Debug, Clone, Copy)] +pub enum Message { + RgbColorChanged(Color), + HslColorChanged(palette::Hsl), + HsvColorChanged(palette::Hsv), + HwbColorChanged(palette::Hwb), + LabColorChanged(palette::Lab), + LchColorChanged(palette::Lch), +} + +impl Sandbox for ColorPalette { + type Message = Message; + + fn new() -> Self { + Self::default() + } + + fn title(&self) -> String { + String::from("Color palette - Iced") + } + + fn update(&mut self, message: Message) { + let srgb = match message { + Message::RgbColorChanged(rgb) => palette::Srgb::from(rgb), + Message::HslColorChanged(hsl) => palette::Srgb::from(hsl), + Message::HsvColorChanged(hsv) => palette::Srgb::from(hsv), + Message::HwbColorChanged(hwb) => palette::Srgb::from(hwb), + Message::LabColorChanged(lab) => palette::Srgb::from(lab), + Message::LchColorChanged(lch) => palette::Srgb::from(lch), + }; + + self.theme = Theme::new(srgb.clamp()); + self.canvas_layer.clear(); + } + + fn view(&mut self) -> Element { + let base = self.theme.base; + + let srgb = palette::Srgb::from(base); + let hsl = palette::Hsl::from(srgb); + let hsv = palette::Hsv::from(srgb); + let hwb = palette::Hwb::from(srgb); + let lab = palette::Lab::from(srgb); + let lch = palette::Lch::from(srgb); + + Column::new() + .padding(10) + .spacing(10) + .push(self.rgb.view(base).map(Message::RgbColorChanged)) + .push(self.hsl.view(hsl).map(Message::HslColorChanged)) + .push(self.hsv.view(hsv).map(Message::HsvColorChanged)) + .push(self.hwb.view(hwb).map(Message::HwbColorChanged)) + .push(self.lab.view(lab).map(Message::LabColorChanged)) + .push(self.lch.view(lch).map(Message::LchColorChanged)) + .push( + Canvas::new() + .width(Length::Fill) + .height(Length::Fill) + .push(self.canvas_layer.with(&self.theme)), + ) + .into() + } +} + #[derive(Debug)] pub struct Theme { lower: Vec, @@ -20,12 +96,6 @@ pub struct Theme { higher: Vec, } -impl Default for Theme { - fn default() -> Self { - Theme::new(Color::from_rgb8(75, 128, 190)) - } -} - impl Theme { pub fn new(base: impl Into) -> Theme { use palette::{Hsl, Hue, Shade, Srgb}; @@ -74,6 +144,136 @@ impl Theme { } } +impl canvas::Drawable for Theme { + fn draw(&self, frame: &mut canvas::Frame) { + use canvas::{Fill, Path}; + use iced::{HorizontalAlignment, VerticalAlignment}; + use iced_native::{Point, Size}; + use palette::{Hsl, Srgb}; + + let pad = 20.0; + + let box_size = Size { + width: frame.width() / self.len() as f32, + height: frame.height() / 2.0 - pad, + }; + + let mut text = canvas::Text { + horizontal_alignment: HorizontalAlignment::Center, + vertical_alignment: VerticalAlignment::Top, + size: 15.0, + ..canvas::Text::default() + }; + + for (i, &color) in self.colors().enumerate() { + let anchor = Point { + x: (i as f32) * box_size.width, + y: 0.0, + }; + let rect = Path::new(|path| { + path.rectangle(anchor, box_size); + }); + frame.fill(&rect, Fill::Color(color)); + + if self.base == color { + let cx = anchor.x + box_size.width / 2.0; + let tri_w = 10.0; + + let tri = Path::new(|path| { + path.move_to(Point { + x: cx - tri_w, + y: 0.0, + }); + path.line_to(Point { + x: cx + tri_w, + y: 0.0, + }); + path.line_to(Point { x: cx, y: tri_w }); + path.line_to(Point { + x: cx - tri_w, + y: 0.0, + }); + }); + frame.fill(&tri, Fill::Color(Color::WHITE)); + + let tri = Path::new(|path| { + path.move_to(Point { + x: cx - tri_w, + y: box_size.height, + }); + path.line_to(Point { + x: cx + tri_w, + y: box_size.height, + }); + path.line_to(Point { + x: cx, + y: box_size.height - tri_w, + }); + path.line_to(Point { + x: cx - tri_w, + y: box_size.height, + }); + }); + frame.fill(&tri, Fill::Color(Color::WHITE)); + } + + frame.fill_text(canvas::Text { + content: color_hex_string(&color), + position: Point { + x: anchor.x + box_size.width / 2.0, + y: box_size.height, + }, + ..text + }); + } + + text.vertical_alignment = VerticalAlignment::Bottom; + + let hsl = Hsl::from(Srgb::from(self.base)); + for i in 0..self.len() { + let pct = (i as f32 + 1.0) / (self.len() as f32 + 1.0); + let graded = Hsl { + lightness: 1.0 - pct, + ..hsl + }; + let color: Color = Srgb::from(graded.clamp()).into(); + + let anchor = Point { + x: (i as f32) * box_size.width, + y: box_size.height + 2.0 * pad, + }; + let rect = Path::new(|path| { + path.rectangle(anchor, box_size); + }); + frame.fill(&rect, Fill::Color(color)); + + frame.fill_text(canvas::Text { + content: color_hex_string(&color), + position: Point { + x: anchor.x + box_size.width / 2.0, + y: box_size.height + 2.0 * pad, + }, + ..text + }); + } + } +} + +impl Default for Theme { + fn default() -> Self { + Theme::new(Color::from_rgb8(75, 128, 190)) + } +} + +fn color_hex_string(color: &Color) -> String { + format!( + "#{:x}{:x}{:x}", + (255.0 * color.r).round() as u8, + (255.0 * color.g).round() as u8, + (255.0 * color.b).round() as u8 + ) +} + #[derive(Default)] struct ColorPicker { sliders: [slider::State; 3], @@ -261,203 +461,3 @@ impl ColorSpace for palette::Lch { ) } } - -#[derive(Default)] -pub struct ColorPalette { - theme: Theme, - rgb: ColorPicker, - hsl: ColorPicker, - hsv: ColorPicker, - hwb: ColorPicker, - lab: ColorPicker, - lch: ColorPicker, - canvas_layer: canvas::layer::Cache, -} - -#[derive(Debug, Clone, Copy)] -pub enum Message { - RgbColorChanged(Color), - HslColorChanged(palette::Hsl), - HsvColorChanged(palette::Hsv), - HwbColorChanged(palette::Hwb), - LabColorChanged(palette::Lab), - LchColorChanged(palette::Lch), -} - -impl Sandbox for ColorPalette { - type Message = Message; - - fn new() -> Self { - Self::default() - } - - fn title(&self) -> String { - String::from("Color Palette") - } - - fn update(&mut self, message: Message) { - let srgb = match message { - Message::RgbColorChanged(rgb) => palette::Srgb::from(rgb), - Message::HslColorChanged(hsl) => palette::Srgb::from(hsl), - Message::HsvColorChanged(hsv) => palette::Srgb::from(hsv), - Message::HwbColorChanged(hwb) => palette::Srgb::from(hwb), - Message::LabColorChanged(lab) => palette::Srgb::from(lab), - Message::LchColorChanged(lch) => palette::Srgb::from(lch), - }; - - self.theme = Theme::new(srgb.clamp()); - self.canvas_layer.clear(); - } - - fn view(&mut self) -> Element { - let base = self.theme.base; - - let srgb = palette::Srgb::from(base); - let hsl = palette::Hsl::from(srgb); - let hsv = palette::Hsv::from(srgb); - let hwb = palette::Hwb::from(srgb); - let lab = palette::Lab::from(srgb); - let lch = palette::Lch::from(srgb); - - Column::new() - .padding(10) - .spacing(10) - .push(self.rgb.view(base).map(Message::RgbColorChanged)) - .push(self.hsl.view(hsl).map(Message::HslColorChanged)) - .push(self.hsv.view(hsv).map(Message::HsvColorChanged)) - .push(self.hwb.view(hwb).map(Message::HwbColorChanged)) - .push(self.lab.view(lab).map(Message::LabColorChanged)) - .push(self.lch.view(lch).map(Message::LchColorChanged)) - .push( - Canvas::new() - .width(Length::Fill) - .height(Length::Fill) - .push(self.canvas_layer.with(&self.theme)), - ) - .into() - } -} - -impl canvas::Drawable for Theme { - fn draw(&self, frame: &mut canvas::Frame) { - use canvas::{Fill, Path}; - use iced::{HorizontalAlignment, VerticalAlignment}; - use iced_native::{Point, Size}; - use palette::{Hsl, Srgb}; - - let pad = 20.0; - - let box_size = Size { - width: frame.width() / self.len() as f32, - height: frame.height() / 2.0 - pad, - }; - - let mut text = canvas::Text { - horizontal_alignment: HorizontalAlignment::Center, - vertical_alignment: VerticalAlignment::Top, - size: 15.0, - ..canvas::Text::default() - }; - - for (i, &color) in self.colors().enumerate() { - let anchor = Point { - x: (i as f32) * box_size.width, - y: 0.0, - }; - let rect = Path::new(|path| { - path.rectangle(anchor, box_size); - }); - frame.fill(&rect, Fill::Color(color)); - - if self.base == color { - let cx = anchor.x + box_size.width / 2.0; - let tri_w = 10.0; - - let tri = Path::new(|path| { - path.move_to(Point { - x: cx - tri_w, - y: 0.0, - }); - path.line_to(Point { - x: cx + tri_w, - y: 0.0, - }); - path.line_to(Point { x: cx, y: tri_w }); - path.line_to(Point { - x: cx - tri_w, - y: 0.0, - }); - }); - frame.fill(&tri, Fill::Color(Color::WHITE)); - - let tri = Path::new(|path| { - path.move_to(Point { - x: cx - tri_w, - y: box_size.height, - }); - path.line_to(Point { - x: cx + tri_w, - y: box_size.height, - }); - path.line_to(Point { - x: cx, - y: box_size.height - tri_w, - }); - path.line_to(Point { - x: cx - tri_w, - y: box_size.height, - }); - }); - frame.fill(&tri, Fill::Color(Color::WHITE)); - } - - frame.fill_text(canvas::Text { - content: color_hex_str(&color), - position: Point { - x: anchor.x + box_size.width / 2.0, - y: box_size.height, - }, - ..text - }); - } - - text.vertical_alignment = VerticalAlignment::Bottom; - - let hsl = Hsl::from(Srgb::from(self.base)); - for i in 0..self.len() { - let pct = (i as f32 + 1.0) / (self.len() as f32 + 1.0); - let graded = Hsl { - lightness: 1.0 - pct, - ..hsl - }; - let color: Color = Srgb::from(graded.clamp()).into(); - - let anchor = Point { - x: (i as f32) * box_size.width, - y: box_size.height + 2.0 * pad, - }; - let rect = Path::new(|path| { - path.rectangle(anchor, box_size); - }); - frame.fill(&rect, Fill::Color(color)); - - frame.fill_text(canvas::Text { - content: color_hex_str(&color), - position: Point { - x: anchor.x + box_size.width / 2.0, - y: box_size.height + 2.0 * pad, - }, - ..text - }); - } - } -} - -fn color_hex_str(color: &Color) -> String { - format!( - "#{:x}{:x}{:x}", - (255.0 * color.r).round() as u8, - (255.0 * color.g).round() as u8, - (255.0 * color.b).round() as u8 - ) -} -- cgit From 573929d5ec99981ae3a4a0d675f1248932d56e61 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 1 May 2020 22:32:40 +0200 Subject: Use `Path::rectangle` directly in `color_palette` --- examples/color_palette/src/main.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/examples/color_palette/src/main.rs b/examples/color_palette/src/main.rs index 243fae1d..ff399e76 100644 --- a/examples/color_palette/src/main.rs +++ b/examples/color_palette/src/main.rs @@ -170,9 +170,7 @@ impl canvas::Drawable for Theme { x: (i as f32) * box_size.width, y: 0.0, }; - let rect = Path::new(|path| { - path.rectangle(anchor, box_size); - }); + let rect = Path::rectangle(anchor, box_size); frame.fill(&rect, Fill::Color(color)); if self.base == color { @@ -242,9 +240,8 @@ impl canvas::Drawable for Theme { x: (i as f32) * box_size.width, y: box_size.height + 2.0 * pad, }; - let rect = Path::new(|path| { - path.rectangle(anchor, box_size); - }); + + let rect = Path::rectangle(anchor, box_size); frame.fill(&rect, Fill::Color(color)); frame.fill_text(canvas::Text { -- cgit From 03ca7eea6c05b32c6273284c35883506e4cf6eb1 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 1 May 2020 22:45:47 +0200 Subject: Reuse triangle path with transforms in `color_palette` --- examples/color_palette/src/main.rs | 71 +++++++++++++++----------------------- 1 file changed, 28 insertions(+), 43 deletions(-) diff --git a/examples/color_palette/src/main.rs b/examples/color_palette/src/main.rs index ff399e76..b3ad98d0 100644 --- a/examples/color_palette/src/main.rs +++ b/examples/color_palette/src/main.rs @@ -1,6 +1,6 @@ use iced::{ canvas, slider, Align, Canvas, Color, Column, Element, Length, Row, - Sandbox, Settings, Slider, Text, + Sandbox, Settings, Slider, Text, Vector, }; use palette::{self, Limited}; use std::marker::PhantomData; @@ -146,7 +146,7 @@ impl Theme { impl canvas::Drawable for Theme { fn draw(&self, frame: &mut canvas::Frame) { - use canvas::{Fill, Path}; + use canvas::Path; use iced::{HorizontalAlignment, VerticalAlignment}; use iced_native::{Point, Size}; use palette::{Hsl, Srgb}; @@ -158,6 +158,13 @@ impl canvas::Drawable for Theme { height: frame.height() / 2.0 - pad, }; + let triangle = Path::new(|path| { + path.move_to(Point { x: 0.0, y: -0.5 }); + path.line_to(Point { x: -0.5, y: 0.0 }); + path.line_to(Point { x: 0.5, y: 0.0 }); + path.close(); + }); + let mut text = canvas::Text { horizontal_alignment: HorizontalAlignment::Center, vertical_alignment: VerticalAlignment::Top, @@ -171,48 +178,26 @@ impl canvas::Drawable for Theme { y: 0.0, }; let rect = Path::rectangle(anchor, box_size); - frame.fill(&rect, Fill::Color(color)); - - if self.base == color { - let cx = anchor.x + box_size.width / 2.0; - let tri_w = 10.0; - - let tri = Path::new(|path| { - path.move_to(Point { - x: cx - tri_w, - y: 0.0, - }); - path.line_to(Point { - x: cx + tri_w, - y: 0.0, - }); - path.line_to(Point { x: cx, y: tri_w }); - path.line_to(Point { - x: cx - tri_w, - y: 0.0, - }); + frame.fill(&rect, color); + + // We show a little indicator for the base color + if color == self.base { + let triangle_x = anchor.x + box_size.width / 2.0; + + frame.with_save(|frame| { + frame.translate(Vector::new(triangle_x, 0.0)); + frame.scale(10.0); + frame.rotate(std::f32::consts::PI); + + frame.fill(&triangle, Color::WHITE); }); - frame.fill(&tri, Fill::Color(Color::WHITE)); - - let tri = Path::new(|path| { - path.move_to(Point { - x: cx - tri_w, - y: box_size.height, - }); - path.line_to(Point { - x: cx + tri_w, - y: box_size.height, - }); - path.line_to(Point { - x: cx, - y: box_size.height - tri_w, - }); - path.line_to(Point { - x: cx - tri_w, - y: box_size.height, - }); + + frame.with_save(|frame| { + frame.translate(Vector::new(triangle_x, box_size.height)); + frame.scale(10.0); + + frame.fill(&triangle, Color::WHITE); }); - frame.fill(&tri, Fill::Color(Color::WHITE)); } frame.fill_text(canvas::Text { @@ -242,7 +227,7 @@ impl canvas::Drawable for Theme { }; let rect = Path::rectangle(anchor, box_size); - frame.fill(&rect, Fill::Color(color)); + frame.fill(&rect, color); frame.fill_text(canvas::Text { content: color_hex_string(&color), -- cgit From 24574b355d8c5c5e624524c6974df822da98befb Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 4 May 2020 22:50:25 +0200 Subject: Mention `color_palette` in examples `README` --- examples/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/README.md b/examples/README.md index 5aea51eb..f67a0dd2 100644 --- a/examples/README.md +++ b/examples/README.md @@ -71,6 +71,7 @@ A bunch of simpler examples exist: - [`bezier_tool`](bezier_tool), a Paint-like tool for drawing Bézier curves using [`lyon`]. - [`clock`](clock), an application that uses the `Canvas` widget to draw a clock and its hands to display the current time. +- [`color_palette`](color_palette), a color palette generator based on a user-defined root color. - [`counter`](counter), the classic counter example explained in the [`README`](../README.md). - [`custom_widget`](custom_widget), a demonstration of how to build a custom widget that draws a circle. - [`download_progress`](download_progress), a basic application that asynchronously downloads a dummy file of 100 MB and tracks the download progress. -- cgit From 1a8d253611d3796b0a32b2f096bb54565a5292e0 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 4 May 2020 22:51:20 +0200 Subject: Add screenshot of `color_palette` example --- examples/color_palette/screenshot.png | Bin 0 -> 105201 bytes examples/color_palette/src/main.rs | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 examples/color_palette/screenshot.png diff --git a/examples/color_palette/screenshot.png b/examples/color_palette/screenshot.png new file mode 100644 index 00000000..aa4772e0 Binary files /dev/null and b/examples/color_palette/screenshot.png differ diff --git a/examples/color_palette/src/main.rs b/examples/color_palette/src/main.rs index b3ad98d0..0092c6ad 100644 --- a/examples/color_palette/src/main.rs +++ b/examples/color_palette/src/main.rs @@ -289,7 +289,7 @@ impl ColorPicker { .push( Text::new(color.to_string()) .width(Length::Units(185)) - .size(16), + .size(14), ) .into() } -- cgit From 3d3e51a742ec940e19271897a8266172bffd6587 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 4 May 2020 22:53:07 +0200 Subject: Add screenshot to `README` of `color_palette` --- examples/color_palette/README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/examples/color_palette/README.md b/examples/color_palette/README.md index b646f3b3..e70188f8 100644 --- a/examples/color_palette/README.md +++ b/examples/color_palette/README.md @@ -1,7 +1,13 @@ -## Color Palette +## Color palette A color palette generator, based on a user-defined root color. + + You can run it with `cargo run`: ``` -- cgit From e3555174d7c12599d454cbe890248449dc3ea958 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 4 May 2020 22:55:10 +0200 Subject: Use only `iced` dependency for `color_palette` `Point` and `Size` are now properly re-exported. --- examples/color_palette/Cargo.toml | 2 -- examples/color_palette/src/main.rs | 7 +++---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/examples/color_palette/Cargo.toml b/examples/color_palette/Cargo.toml index 61c9f6b2..00f33e20 100644 --- a/examples/color_palette/Cargo.toml +++ b/examples/color_palette/Cargo.toml @@ -7,6 +7,4 @@ publish = false [dependencies] iced = { path = "../..", features = ["canvas", "palette"] } -iced_core = { path = "../../core" } -iced_native = { path = "../../native" } palette = "0.5.0" diff --git a/examples/color_palette/src/main.rs b/examples/color_palette/src/main.rs index 0092c6ad..073a6734 100644 --- a/examples/color_palette/src/main.rs +++ b/examples/color_palette/src/main.rs @@ -1,6 +1,7 @@ use iced::{ - canvas, slider, Align, Canvas, Color, Column, Element, Length, Row, - Sandbox, Settings, Slider, Text, Vector, + canvas, slider, Align, Canvas, Color, Column, Element, HorizontalAlignment, + Length, Point, Row, Sandbox, Settings, Size, Slider, Text, Vector, + VerticalAlignment, }; use palette::{self, Limited}; use std::marker::PhantomData; @@ -147,8 +148,6 @@ impl Theme { impl canvas::Drawable for Theme { fn draw(&self, frame: &mut canvas::Frame) { use canvas::Path; - use iced::{HorizontalAlignment, VerticalAlignment}; - use iced_native::{Point, Size}; use palette::{Hsl, Srgb}; let pad = 20.0; -- cgit From c0fd5de8a0dbb1b99de8c83e4f84c98a6219778b Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 4 May 2020 23:04:02 +0200 Subject: Improve minor documentation details in `Color` --- core/src/color.rs | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/core/src/color.rs b/core/src/color.rs index c061add6..a4c3d87c 100644 --- a/core/src/color.rs +++ b/core/src/color.rs @@ -39,7 +39,12 @@ impl Color { a: 0.0, }; - /// New Color with range checks + /// Creates a new [`Color`]. + /// + /// In debug mode, it will panic if the values are not in the correct + /// range: 0.0 - 1.0 + /// + /// [`Color`]: struct.Color.html pub fn new(r: f32, g: f32, b: f32, a: f32) -> Color { debug_assert!( (0.0..=1.0).contains(&r), @@ -116,14 +121,18 @@ impl Color { ] } - /// Invert the Color in-place + /// Inverts the [`Color`] in-place. + /// + /// [`Color`]: struct.Color.html pub fn invert(&mut self) { self.r = 1.0f32 - self.r; self.b = 1.0f32 - self.g; self.g = 1.0f32 - self.b; } - /// Return an inverted Color + /// Returns the inverted [`Color`]. + /// + /// [`Color`]: struct.Color.html pub fn inverse(self) -> Color { Color::new(1.0f32 - self.r, 1.0f32 - self.g, 1.0f32 - self.b, self.a) } @@ -142,9 +151,8 @@ impl From<[f32; 4]> for Color { } #[cfg(feature = "palette")] -/// Convert from palette's [`Srgba`] type to a [`Color`] +/// Converts from palette's `Srgba` type to a [`Color`]. /// -/// [`Srgba`]: ../palette/rgb/type.Srgba.html /// [`Color`]: struct.Color.html impl From for Color { fn from(srgba: Srgba) -> Self { @@ -153,10 +161,9 @@ impl From for Color { } #[cfg(feature = "palette")] -/// Convert from [`Color`] to palette's [`Srgba`] type +/// Converts from [`Color`] to palette's `Srgba` type. /// /// [`Color`]: struct.Color.html -/// [`Srgba`]: ../palette/rgb/type.Srgba.html impl From for Srgba { fn from(c: Color) -> Self { Srgba::new(c.r, c.g, c.b, c.a) @@ -164,9 +171,8 @@ impl From for Srgba { } #[cfg(feature = "palette")] -/// Convert from palette's [`Srgb`] type to a [`Color`] +/// Converts from palette's `Srgb` type to a [`Color`]. /// -/// [`Srgb`]: ../palette/rgb/type.Srgb.html /// [`Color`]: struct.Color.html impl From for Color { fn from(srgb: Srgb) -> Self { @@ -175,7 +181,7 @@ impl From for Color { } #[cfg(feature = "palette")] -/// Convert from [`Color`] to palette's [`Srgb`] type +/// Converts from [`Color`] to palette's `Srgb` type. /// /// [`Color`]: struct.Color.html /// [`Srgb`]: ../palette/rgb/type.Srgb.html -- cgit