diff options
| author | 2020-05-04 23:35:09 +0200 | |
|---|---|---|
| committer | 2020-05-04 23:35:09 +0200 | |
| commit | 67b2ccb4d5ae19d7c325f973b51fb02db03c853c (patch) | |
| tree | de0c3fc4ad546403cf68b1dfb0a50db94e9f3197 /examples | |
| parent | 2f41ccee1c7b52f872d68d2e5ebd68ea49a1559b (diff) | |
| parent | 27aad74a32fd8ac2b12f9d32df8a3b61a3175457 (diff) | |
| download | iced-67b2ccb4d5ae19d7c325f973b51fb02db03c853c.tar.gz iced-67b2ccb4d5ae19d7c325f973b51fb02db03c853c.tar.bz2 iced-67b2ccb4d5ae19d7c325f973b51fb02db03c853c.zip  | |
Merge branch 'master' into feature/canvas-interaction
Diffstat (limited to 'examples')
| -rw-r--r-- | examples/README.md | 1 | ||||
| -rw-r--r-- | examples/color_palette/Cargo.toml | 10 | ||||
| -rw-r--r-- | examples/color_palette/README.md | 15 | ||||
| -rw-r--r-- | examples/color_palette/screenshot.png | bin | 0 -> 105201 bytes | |||
| -rw-r--r-- | examples/color_palette/src/main.rs | 444 | 
5 files changed, 470 insertions, 0 deletions
diff --git a/examples/README.md b/examples/README.md index 5d880d71..7e7bda9d 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 the `Canvas` widget.  - [`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. diff --git a/examples/color_palette/Cargo.toml b/examples/color_palette/Cargo.toml new file mode 100644 index 00000000..00f33e20 --- /dev/null +++ b/examples/color_palette/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "color_palette" +version = "0.1.0" +authors = ["Clark Moody <clark@clarkmoody.com>"] +edition = "2018" +publish = false + +[dependencies] +iced = { path = "../..", features = ["canvas", "palette"] } +palette = "0.5.0" diff --git a/examples/color_palette/README.md b/examples/color_palette/README.md new file mode 100644 index 00000000..e70188f8 --- /dev/null +++ b/examples/color_palette/README.md @@ -0,0 +1,15 @@ +## Color palette + +A color palette generator, based on a user-defined root color. + +<div align="center"> +  <a href="https://gfycat.com/dirtylonebighornsheep"> +    <img src="https://github.com/hecrj/iced/raw/1a8d253611d3796b0a32b2f096bb54565a5292e0/examples/color_palette/screenshot.png"> +  </a> +</div> + +You can run it with `cargo run`: + +``` +cargo run --package color_palette +``` diff --git a/examples/color_palette/screenshot.png b/examples/color_palette/screenshot.png Binary files differnew file mode 100644 index 00000000..aa4772e0 --- /dev/null +++ b/examples/color_palette/screenshot.png diff --git a/examples/color_palette/src/main.rs b/examples/color_palette/src/main.rs new file mode 100644 index 00000000..073a6734 --- /dev/null +++ b/examples/color_palette/src/main.rs @@ -0,0 +1,444 @@ +use iced::{ +    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; +use std::ops::RangeInclusive; + +pub fn main() { +    ColorPalette::run(Settings { +        antialiasing: true, +        ..Settings::default() +    }) +} + +#[derive(Default)] +pub struct ColorPalette { +    theme: Theme, +    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<Theme>, +} + +#[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<Message> { +        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<Color>, +    base: Color, +    higher: Vec<Color>, +} + +impl Theme { +    pub fn new(base: impl Into<Color>) -> 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<Item = &Color> { +        self.lower +            .iter() +            .chain(std::iter::once(&self.base)) +            .chain(self.higher.iter()) +    } +} + +impl canvas::Drawable for Theme { +    fn draw(&self, frame: &mut canvas::Frame) { +        use canvas::Path; +        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 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, +            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::rectangle(anchor, box_size); +            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.with_save(|frame| { +                    frame.translate(Vector::new(triangle_x, box_size.height)); +                    frame.scale(10.0); + +                    frame.fill(&triangle, 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::rectangle(anchor, box_size); +            frame.fill(&rect, 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<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 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) +            .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))) +            .push(Slider::new(s3, cr3, c3, move |v| C::new(c1, c2, v))) +            .push( +                Text::new(color.to_string()) +                    .width(Length::Units(185)) +                    .size(14), +            ) +            .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 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 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 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 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 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 to_string(&self) -> String { +        format!( +            "Lch({:.1}, {:.1}, {:.1})", +            self.l, +            self.chroma, +            self.hue.to_positive_degrees() +        ) +    } +}  | 
