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