Introduce Theme type in color_palette example

This commit is contained in:
Héctor Ramón Jiménez 2020-05-01 22:24:34 +02:00
parent 0a011f9031
commit 4d724a88e6

View File

@ -1,6 +1,6 @@
use iced::{ use iced::{
canvas, slider, Canvas, Color, Column, Element, Length, Row, Sandbox, canvas, slider, Align, Canvas, Color, Column, Element, Length, Row,
Settings, Slider, Text, Sandbox, Settings, Slider, Text,
}; };
use palette::{self, Limited}; use palette::{self, Limited};
use std::marker::PhantomData; use std::marker::PhantomData;
@ -13,34 +13,68 @@ pub fn main() {
}) })
} }
#[derive(Debug, Default)] #[derive(Debug)]
pub struct State { pub struct Theme {
color: Color, lower: Vec<Color>,
theme: Vec<Color>, base: Color,
higher: Vec<Color>,
} }
fn generate_theme(base_color: &Color) -> Vec<Color> { impl Default for Theme {
fn default() -> Self {
Theme::new(Color::from_rgb8(75, 128, 190))
}
}
impl Theme {
pub fn new(base: impl Into<Color>) -> Theme {
use palette::{Hsl, Hue, Shade, Srgb}; use palette::{Hsl, Hue, Shade, Srgb};
// Convert to HSL color for manipulation let base = base.into();
let hsl = Hsl::from(Srgb::from(*base_color));
[ // 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(-135.0).lighten(0.075),
hsl.shift_hue(-120.0), hsl.shift_hue(-120.0),
hsl.shift_hue(-105.0).darken(0.075), hsl.shift_hue(-105.0).darken(0.075),
hsl.darken(0.075), hsl.darken(0.075),
hsl, ];
let higher = [
hsl.lighten(0.075), hsl.lighten(0.075),
hsl.shift_hue(105.0).darken(0.075), hsl.shift_hue(105.0).darken(0.075),
hsl.shift_hue(120.0), hsl.shift_hue(120.0),
hsl.shift_hue(135.0).lighten(0.075), hsl.shift_hue(135.0).lighten(0.075),
] ];
Theme {
lower: lower
.iter() .iter()
.map(|&color| Srgb::from(color).clamp().into()) .map(|&color| Srgb::from(color).clamp().into())
.collect() .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<Item = &Color> {
self.lower
.iter()
.chain(std::iter::once(&self.base))
.chain(self.higher.iter())
}
}
#[derive(Default)]
struct ColorPicker<C: ColorSpace> { struct ColorPicker<C: ColorSpace> {
sliders: [slider::State; 3], sliders: [slider::State; 3],
color_space: PhantomData<C>, color_space: PhantomData<C>,
@ -65,6 +99,7 @@ impl<C: 'static + ColorSpace + Copy> ColorPicker<C> {
Row::new() Row::new()
.spacing(10) .spacing(10)
.align_items(Align::Center)
.push(Text::new(C::LABEL).width(Length::Units(50))) .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(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(s2, cr2, c2, move |v| C::new(c1, v, c3)))
@ -227,15 +262,16 @@ impl ColorSpace for palette::Lch {
} }
} }
#[derive(Default)]
pub struct ColorPalette { pub struct ColorPalette {
state: State, theme: Theme,
rgb: ColorPicker<Color>, rgb: ColorPicker<Color>,
hsl: ColorPicker<palette::Hsl>, hsl: ColorPicker<palette::Hsl>,
hsv: ColorPicker<palette::Hsv>, hsv: ColorPicker<palette::Hsv>,
hwb: ColorPicker<palette::Hwb>, hwb: ColorPicker<palette::Hwb>,
lab: ColorPicker<palette::Lab>, lab: ColorPicker<palette::Lab>,
lch: ColorPicker<palette::Lch>, lch: ColorPicker<palette::Lch>,
canvas_layer: canvas::layer::Cache<State>, canvas_layer: canvas::layer::Cache<Theme>,
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
@ -252,43 +288,7 @@ impl Sandbox for ColorPalette {
type Message = Message; type Message = Message;
fn new() -> Self { fn new() -> Self {
fn triple_slider() -> [slider::State; 3] { Self::default()
[
slider::State::new(),
slider::State::new(),
slider::State::new(),
]
}
ColorPalette {
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(),
}
} }
fn title(&self) -> String { fn title(&self) -> String {
@ -296,7 +296,7 @@ impl Sandbox for ColorPalette {
} }
fn update(&mut self, message: Message) { fn update(&mut self, message: Message) {
let mut srgb = match message { let srgb = match message {
Message::RgbColorChanged(rgb) => palette::Srgb::from(rgb), Message::RgbColorChanged(rgb) => palette::Srgb::from(rgb),
Message::HslColorChanged(hsl) => palette::Srgb::from(hsl), Message::HslColorChanged(hsl) => palette::Srgb::from(hsl),
Message::HsvColorChanged(hsv) => palette::Srgb::from(hsv), Message::HsvColorChanged(hsv) => palette::Srgb::from(hsv),
@ -304,17 +304,15 @@ impl Sandbox for ColorPalette {
Message::LabColorChanged(lab) => palette::Srgb::from(lab), Message::LabColorChanged(lab) => palette::Srgb::from(lab),
Message::LchColorChanged(lch) => palette::Srgb::from(lch), Message::LchColorChanged(lch) => palette::Srgb::from(lch),
}; };
srgb.clamp_self();
self.canvas_layer.clear();
self.state.color = Color::from(srgb);
// Set theme colors self.theme = Theme::new(srgb.clamp());
self.state.theme = generate_theme(&self.state.color); self.canvas_layer.clear();
} }
fn view(&mut self) -> Element<Message> { fn view(&mut self) -> Element<Message> {
let color = self.state.color; let base = self.theme.base;
let srgb = palette::Srgb::from(self.state.color);
let srgb = palette::Srgb::from(base);
let hsl = palette::Hsl::from(srgb); let hsl = palette::Hsl::from(srgb);
let hsv = palette::Hsv::from(srgb); let hsv = palette::Hsv::from(srgb);
let hwb = palette::Hwb::from(srgb); let hwb = palette::Hwb::from(srgb);
@ -324,7 +322,7 @@ impl Sandbox for ColorPalette {
Column::new() Column::new()
.padding(10) .padding(10)
.spacing(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.hsl.view(hsl).map(Message::HslColorChanged))
.push(self.hsv.view(hsv).map(Message::HsvColorChanged)) .push(self.hsv.view(hsv).map(Message::HsvColorChanged))
.push(self.hwb.view(hwb).map(Message::HwbColorChanged)) .push(self.hwb.view(hwb).map(Message::HwbColorChanged))
@ -333,48 +331,35 @@ impl Sandbox for ColorPalette {
.push( .push(
Canvas::new() Canvas::new()
.width(Length::Fill) .width(Length::Fill)
// .height(Length::Units(250))
.height(Length::Fill) .height(Length::Fill)
.push(self.canvas_layer.with(&self.state)), .push(self.canvas_layer.with(&self.theme)),
) )
.into() .into()
} }
} }
impl State { impl canvas::Drawable for Theme {
pub fn new() -> State {
let base = Color::from_rgb8(75, 128, 190);
State {
color: base,
theme: generate_theme(&base),
}
}
}
impl canvas::Drawable for State {
fn draw(&self, frame: &mut canvas::Frame) { fn draw(&self, frame: &mut canvas::Frame) {
use canvas::{Fill, Path}; use canvas::{Fill, Path};
use iced::{HorizontalAlignment, VerticalAlignment}; use iced::{HorizontalAlignment, VerticalAlignment};
use iced_native::{Point, Size}; use iced_native::{Point, Size};
use palette::{Hsl, Srgb}; use palette::{Hsl, Srgb};
if self.theme.len() == 0 {
return;
}
let pad = 20.0; let pad = 20.0;
let box_size = Size { 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, height: frame.height() / 2.0 - pad,
}; };
let mut text = canvas::Text::default(); let mut text = canvas::Text {
text.horizontal_alignment = HorizontalAlignment::Center; horizontal_alignment: HorizontalAlignment::Center,
text.vertical_alignment = VerticalAlignment::Top; vertical_alignment: VerticalAlignment::Top,
text.size = 15.0; size: 15.0,
..canvas::Text::default()
};
for i in 0..self.theme.len() { for (i, &color) in self.colors().enumerate() {
let anchor = Point { let anchor = Point {
x: (i as f32) * box_size.width, x: (i as f32) * box_size.width,
y: 0.0, y: 0.0,
@ -382,9 +367,9 @@ impl canvas::Drawable for State {
let rect = Path::new(|path| { let rect = Path::new(|path| {
path.rectangle(anchor, box_size); 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 cx = anchor.x + box_size.width / 2.0;
let tri_w = 10.0; let tri_w = 10.0;
@ -427,7 +412,7 @@ impl canvas::Drawable for State {
} }
frame.fill_text(canvas::Text { frame.fill_text(canvas::Text {
content: color_hex_str(&self.theme[i]), content: color_hex_str(&color),
position: Point { position: Point {
x: anchor.x + box_size.width / 2.0, x: anchor.x + box_size.width / 2.0,
y: box_size.height, y: box_size.height,
@ -438,9 +423,9 @@ impl canvas::Drawable for State {
text.vertical_alignment = VerticalAlignment::Bottom; text.vertical_alignment = VerticalAlignment::Bottom;
let hsl = Hsl::from(Srgb::from(self.color)); let hsl = Hsl::from(Srgb::from(self.base));
for i in 0..self.theme.len() { for i in 0..self.len() {
let pct = (i as f32 + 1.0) / (self.theme.len() as f32 + 1.0); let pct = (i as f32 + 1.0) / (self.len() as f32 + 1.0);
let graded = Hsl { let graded = Hsl {
lightness: 1.0 - pct, lightness: 1.0 - pct,
..hsl ..hsl