summaryrefslogtreecommitdiffstats
path: root/examples
diff options
context:
space:
mode:
authorLibravatar Clark Moody <clark@clarkmoody.com>2020-04-27 16:25:13 -0500
committerLibravatar Clark Moody <clark@clarkmoody.com>2020-04-27 16:25:13 -0500
commit430f78a693a87e9ba3ac4638cac96aab57dd3042 (patch)
tree9a1dcb34ef72d7f5e82d0ddcbb8599b8bdd03942 /examples
parent3e71eaee37bc3aea85feb0f643dcbd4ecc11d0c4 (diff)
downloadiced-430f78a693a87e9ba3ac4638cac96aab57dd3042.tar.gz
iced-430f78a693a87e9ba3ac4638cac96aab57dd3042.tar.bz2
iced-430f78a693a87e9ba3ac4638cac96aab57dd3042.zip
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.
Diffstat (limited to 'examples')
-rw-r--r--examples/color_palette/src/main.rs635
1 files 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<Color> {
theme
}
+struct ColorPicker<C: ColorSpace> {
+ sliders: [slider::State; 3],
+ color_space: PhantomData<C>,
+}
+
+trait ColorSpace: Sized {
+ const LABEL: &'static str;
+ const COMPONENT_RANGES: [RangeInclusive<f32>; 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<C: 'static + ColorSpace + Copy> ColorPicker<C> {
+ fn view(&mut self, color: C) -> Element<C> {
+ 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<f32>; 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<f32>; 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<f32>; 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<f32>; 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<f32>; 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<f32>; 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<Color>,
+ hsl: ColorPicker<palette::Hsl>,
+ hsv: ColorPicker<palette::Hsv>,
+ hwb: ColorPicker<palette::Hwb>,
+ lab: ColorPicker<palette::Lab>,
+ lch: ColorPicker<palette::Lch>,
canvas_layer: canvas::layer::Cache<State>,
}
@@ -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::<Color>,
+ },
+ hsl: ColorPicker {
+ sliders: triple_slider(),
+ color_space: PhantomData::<palette::Hsl>,
+ },
+ hsv: ColorPicker {
+ sliders: triple_slider(),
+ color_space: PhantomData::<palette::Hsv>,
+ },
+
+ hwb: ColorPicker {
+ sliders: triple_slider(),
+ color_space: PhantomData::<palette::Hwb>,
+ },
+ lab: ColorPicker {
+ sliders: triple_slider(),
+ color_space: PhantomData::<palette::Lab>,
+ },
+ lch: ColorPicker {
+ sliders: triple_slider(),
+ color_space: PhantomData::<palette::Lch>,
+ },
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<Message> {
- 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
+ )
}