diff options
Diffstat (limited to 'examples/pure')
25 files changed, 0 insertions, 3791 deletions
diff --git a/examples/pure/arc/Cargo.toml b/examples/pure/arc/Cargo.toml deleted file mode 100644 index 22113cf1..00000000 --- a/examples/pure/arc/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "arc" -version = "0.1.0" -authors = ["ThatsNoMoon <git@thatsnomoon.dev>"] -edition = "2021" -publish = false - -[dependencies] -iced = { path = "../../..", features = ["pure", "canvas", "tokio", "debug"] } diff --git a/examples/pure/arc/README.md b/examples/pure/arc/README.md deleted file mode 100644 index 303253da..00000000 --- a/examples/pure/arc/README.md +++ /dev/null @@ -1,14 +0,0 @@ -## Arc - -An application that uses the `Canvas` widget to draw a rotating arc. - -This is a simple demo for https://github.com/iced-rs/iced/pull/1358. - -The __[`main`]__ file contains all the code of the example. - -You can run it with `cargo run`: -``` -cargo run --package arc -``` - -[`main`]: src/main.rs diff --git a/examples/pure/arc/src/main.rs b/examples/pure/arc/src/main.rs deleted file mode 100644 index df0e1e8a..00000000 --- a/examples/pure/arc/src/main.rs +++ /dev/null @@ -1,124 +0,0 @@ -use std::{f32::consts::PI, time::Instant}; - -use iced::executor; -use iced::pure::widget::canvas::{ -    self, Cache, Canvas, Cursor, Geometry, Path, Stroke, -}; -use iced::pure::{Application, Element}; -use iced::{Command, Length, Point, Rectangle, Settings, Subscription, Theme}; - -pub fn main() -> iced::Result { -    Arc::run(Settings { -        antialiasing: true, -        ..Settings::default() -    }) -} - -struct Arc { -    start: Instant, -    cache: Cache, -} - -#[derive(Debug, Clone, Copy)] -enum Message { -    Tick, -} - -impl Application for Arc { -    type Executor = executor::Default; -    type Message = Message; -    type Theme = Theme; -    type Flags = (); - -    fn new(_flags: ()) -> (Self, Command<Message>) { -        ( -            Arc { -                start: Instant::now(), -                cache: Default::default(), -            }, -            Command::none(), -        ) -    } - -    fn title(&self) -> String { -        String::from("Arc - Iced") -    } - -    fn update(&mut self, _: Message) -> Command<Message> { -        self.cache.clear(); - -        Command::none() -    } - -    fn subscription(&self) -> Subscription<Message> { -        iced::time::every(std::time::Duration::from_millis(10)) -            .map(|_| Message::Tick) -    } - -    fn view(&self) -> Element<Message> { -        Canvas::new(self) -            .width(Length::Fill) -            .height(Length::Fill) -            .into() -    } - -    fn theme(&self) -> Theme { -        Theme::Dark -    } -} - -impl<Message> canvas::Program<Message> for Arc { -    type State = (); - -    fn draw( -        &self, -        _state: &Self::State, -        theme: &Theme, -        bounds: Rectangle, -        _cursor: Cursor, -    ) -> Vec<Geometry> { -        let geometry = self.cache.draw(bounds.size(), |frame| { -            let palette = theme.palette(); - -            let center = frame.center(); -            let radius = frame.width().min(frame.height()) / 5.0; - -            let start = Point::new(center.x, center.y - radius); - -            let angle = (self.start.elapsed().as_millis() % 10_000) as f32 -                / 10_000.0 -                * 2.0 -                * PI; - -            let end = Point::new( -                center.x + radius * angle.cos(), -                center.y + radius * angle.sin(), -            ); - -            let circles = Path::new(|b| { -                b.circle(start, 10.0); -                b.move_to(end); -                b.circle(end, 10.0); -            }); - -            frame.fill(&circles, palette.text); - -            let path = Path::new(|b| { -                b.move_to(start); -                b.arc_to(center, end, 50.0); -                b.line_to(end); -            }); - -            frame.stroke( -                &path, -                Stroke { -                    color: palette.text, -                    width: 10.0, -                    ..Stroke::default() -                }, -            ); -        }); - -        vec![geometry] -    } -} diff --git a/examples/pure/color_palette/Cargo.toml b/examples/pure/color_palette/Cargo.toml deleted file mode 100644 index d08309d5..00000000 --- a/examples/pure/color_palette/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "pure_color_palette" -version = "0.1.0" -authors = ["Clark Moody <clark@clarkmoody.com>"] -edition = "2021" -publish = false - -[dependencies] -iced = { path = "../../..", features = ["pure", "canvas", "palette"] } -palette = "0.6.0" diff --git a/examples/pure/color_palette/README.md b/examples/pure/color_palette/README.md deleted file mode 100644 index f90020b1..00000000 --- a/examples/pure/color_palette/README.md +++ /dev/null @@ -1,15 +0,0 @@ -## Color palette - -A color palette generator, based on a user-defined root color. - -<div align="center"> -  <a href="https://gfycat.com/dirtylonebighornsheep"> -    <img src="screenshot.png"> -  </a> -</div> - -You can run it with `cargo run`: - -``` -cargo run --package pure_color_palette -``` diff --git a/examples/pure/color_palette/screenshot.png b/examples/pure/color_palette/screenshot.png Binary files differdeleted file mode 100644 index e8da35c4..00000000 --- a/examples/pure/color_palette/screenshot.png +++ /dev/null diff --git a/examples/pure/color_palette/src/main.rs b/examples/pure/color_palette/src/main.rs deleted file mode 100644 index 8a58afa7..00000000 --- a/examples/pure/color_palette/src/main.rs +++ /dev/null @@ -1,465 +0,0 @@ -use iced::pure::{ -    column, row, text, -    widget::canvas::{self, Canvas, Cursor, Frame, Geometry, Path}, -    widget::Slider, -    Element, Sandbox, -}; -use iced::{ -    alignment, Alignment, Color, Length, Point, Rectangle, Settings, Size, -    Vector, -}; -use palette::{self, convert::FromColor, Hsl, Srgb}; -use std::marker::PhantomData; -use std::ops::RangeInclusive; - -pub fn main() -> iced::Result { -    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>, -} - -#[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_color(hsl), -            Message::HsvColorChanged(hsv) => palette::Srgb::from_color(hsv), -            Message::HwbColorChanged(hwb) => palette::Srgb::from_color(hwb), -            Message::LabColorChanged(lab) => palette::Srgb::from_color(lab), -            Message::LchColorChanged(lch) => palette::Srgb::from_color(lch), -        }; - -        self.theme = Theme::new(srgb); -    } - -    fn view(&self) -> Element<Message> { -        let base = self.theme.base; - -        let srgb = palette::Srgb::from(base); -        let hsl = palette::Hsl::from_color(srgb); -        let hsv = palette::Hsv::from_color(srgb); -        let hwb = palette::Hwb::from_color(srgb); -        let lab = palette::Lab::from_color(srgb); -        let lch = palette::Lch::from_color(srgb); - -        column() -            .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(self.theme.view()) -            .into() -    } -} - -#[derive(Debug)] -struct Theme { -    lower: Vec<Color>, -    base: Color, -    higher: Vec<Color>, -    canvas_cache: canvas::Cache, -} - -impl Theme { -    pub fn new(base: impl Into<Color>) -> Theme { -        use palette::{Hue, Shade}; - -        let base = base.into(); - -        // Convert to HSL color for manipulation -        let hsl = Hsl::from_color(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(color).into()) -                .collect(), -            base, -            higher: higher -                .iter() -                .map(|&color| Srgb::from_color(color).into()) -                .collect(), -            canvas_cache: canvas::Cache::default(), -        } -    } - -    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()) -    } - -    pub fn view(&self) -> Element<Message> { -        Canvas::new(self) -            .width(Length::Fill) -            .height(Length::Fill) -            .into() -    } - -    fn draw(&self, frame: &mut Frame) { -        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: alignment::Horizontal::Center, -            vertical_alignment: alignment::Vertical::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, -            }; -            frame.fill_rectangle(anchor, box_size, 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 = alignment::Vertical::Bottom; - -        let hsl = Hsl::from_color(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_color(graded).into(); - -            let anchor = Point { -                x: (i as f32) * box_size.width, -                y: box_size.height + 2.0 * pad, -            }; - -            frame.fill_rectangle(anchor, box_size, 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<Message> canvas::Program<Message> for Theme { -    type State = (); - -    fn draw( -        &self, -        _state: &Self::State, -        _theme: &iced::Theme, -        bounds: Rectangle, -        _cursor: Cursor, -    ) -> Vec<Geometry> { -        let theme = self.canvas_cache.draw(bounds.size(), |frame| { -            self.draw(frame); -        }); - -        vec![theme] -    } -} - -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> { -    color_space: PhantomData<C>, -} - -trait ColorSpace: Sized { -    const LABEL: &'static str; -    const COMPONENT_RANGES: [RangeInclusive<f64>; 3]; - -    fn new(a: f32, b: f32, c: f32) -> Self; - -    fn components(&self) -> [f32; 3]; - -    fn to_string(&self) -> String; -} - -impl<C: ColorSpace + Copy> ColorPicker<C> { -    fn view(&self, color: C) -> Element<C> { -        let [c1, c2, c3] = color.components(); -        let [cr1, cr2, cr3] = C::COMPONENT_RANGES; - -        fn slider<'a, C: Clone>( -            range: RangeInclusive<f64>, -            component: f32, -            update: impl Fn(f32) -> C + 'a, -        ) -> Slider<'a, f64, C, iced::Renderer> { -            Slider::new(range, f64::from(component), move |v| update(v as f32)) -                .step(0.01) -        } - -        row() -            .spacing(10) -            .align_items(Alignment::Center) -            .push(text(C::LABEL).width(Length::Units(50))) -            .push(slider(cr1, c1, move |v| C::new(v, c2, c3))) -            .push(slider(cr2, c2, move |v| C::new(c1, v, c3))) -            .push(slider(cr3, c3, move |v| C::new(c1, c2, v))) -            .push(text(color.to_string()).width(Length::Units(185)).size(14)) -            .into() -    } -} - -impl ColorSpace for Color { -    const LABEL: &'static str = "RGB"; -    const COMPONENT_RANGES: [RangeInclusive<f64>; 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<f64>; 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<f64>; 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<f64>; 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<f64>; 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<f64>; 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() -        ) -    } -} diff --git a/examples/pure/component/Cargo.toml b/examples/pure/component/Cargo.toml deleted file mode 100644 index b6c7a513..00000000 --- a/examples/pure/component/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "pure_component" -version = "0.1.0" -authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"] -edition = "2021" -publish = false - -[dependencies] -iced = { path = "../../..", features = ["debug", "pure"] } -iced_native = { path = "../../../native" } -iced_lazy = { path = "../../../lazy", features = ["pure"] } -iced_pure = { path = "../../../pure" } diff --git a/examples/pure/component/src/main.rs b/examples/pure/component/src/main.rs deleted file mode 100644 index db22d019..00000000 --- a/examples/pure/component/src/main.rs +++ /dev/null @@ -1,172 +0,0 @@ -use iced::pure::container; -use iced::pure::{Element, Sandbox}; -use iced::{Length, Settings}; - -use numeric_input::numeric_input; - -pub fn main() -> iced::Result { -    Component::run(Settings::default()) -} - -#[derive(Default)] -struct Component { -    value: Option<u32>, -} - -#[derive(Debug, Clone, Copy)] -enum Message { -    NumericInputChanged(Option<u32>), -} - -impl Sandbox for Component { -    type Message = Message; - -    fn new() -> Self { -        Self::default() -    } - -    fn title(&self) -> String { -        String::from("Component - Iced") -    } - -    fn update(&mut self, message: Message) { -        match message { -            Message::NumericInputChanged(value) => { -                self.value = value; -            } -        } -    } - -    fn view(&self) -> Element<Message> { -        container(numeric_input(self.value, Message::NumericInputChanged)) -            .padding(20) -            .height(Length::Fill) -            .center_y() -            .into() -    } -} - -mod numeric_input { -    use iced_lazy::pure::{self, Component}; -    use iced_native::alignment::{self, Alignment}; -    use iced_native::text; -    use iced_native::widget; -    use iced_native::Length; -    use iced_pure::Element; -    use iced_pure::{button, row, text, text_input}; - -    pub struct NumericInput<Message> { -        value: Option<u32>, -        on_change: Box<dyn Fn(Option<u32>) -> Message>, -    } - -    pub fn numeric_input<Message>( -        value: Option<u32>, -        on_change: impl Fn(Option<u32>) -> Message + 'static, -    ) -> NumericInput<Message> { -        NumericInput::new(value, on_change) -    } - -    #[derive(Debug, Clone)] -    pub enum Event { -        InputChanged(String), -        IncrementPressed, -        DecrementPressed, -    } - -    impl<Message> NumericInput<Message> { -        pub fn new( -            value: Option<u32>, -            on_change: impl Fn(Option<u32>) -> Message + 'static, -        ) -> Self { -            Self { -                value, -                on_change: Box::new(on_change), -            } -        } -    } - -    impl<Message, Renderer> Component<Message, Renderer> for NumericInput<Message> -    where -        Renderer: text::Renderer + 'static, -        Renderer::Theme: widget::button::StyleSheet -            + widget::text_input::StyleSheet -            + widget::text::StyleSheet, -    { -        type State = (); -        type Event = Event; - -        fn update( -            &mut self, -            _state: &mut Self::State, -            event: Event, -        ) -> Option<Message> { -            match event { -                Event::IncrementPressed => Some((self.on_change)(Some( -                    self.value.unwrap_or_default().saturating_add(1), -                ))), -                Event::DecrementPressed => Some((self.on_change)(Some( -                    self.value.unwrap_or_default().saturating_sub(1), -                ))), -                Event::InputChanged(value) => { -                    if value.is_empty() { -                        Some((self.on_change)(None)) -                    } else { -                        value -                            .parse() -                            .ok() -                            .map(Some) -                            .map(self.on_change.as_ref()) -                    } -                } -            } -        } - -        fn view(&self, _state: &Self::State) -> Element<Event, Renderer> { -            let button = |label, on_press| { -                button( -                    text(label) -                        .width(Length::Fill) -                        .height(Length::Fill) -                        .horizontal_alignment(alignment::Horizontal::Center) -                        .vertical_alignment(alignment::Vertical::Center), -                ) -                .width(Length::Units(50)) -                .on_press(on_press) -            }; - -            row() -                .push(button("-", Event::DecrementPressed)) -                .push( -                    text_input( -                        "Type a number", -                        self.value -                            .as_ref() -                            .map(u32::to_string) -                            .as_deref() -                            .unwrap_or(""), -                        Event::InputChanged, -                    ) -                    .padding(10), -                ) -                .push(button("+", Event::IncrementPressed)) -                .align_items(Alignment::Fill) -                .spacing(10) -                .into() -        } -    } - -    impl<'a, Message, Renderer> From<NumericInput<Message>> -        for Element<'a, Message, Renderer> -    where -        Message: 'a, -        Renderer: 'static + text::Renderer, -        Renderer::Theme: widget::button::StyleSheet -            + widget::text_input::StyleSheet -            + widget::text::StyleSheet, -    { -        fn from(numeric_input: NumericInput<Message>) -> Self { -            pure::component(numeric_input) -        } -    } -} diff --git a/examples/pure/counter/Cargo.toml b/examples/pure/counter/Cargo.toml deleted file mode 100644 index 2fcd22d4..00000000 --- a/examples/pure/counter/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "pure_counter" -version = "0.1.0" -authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"] -edition = "2021" -publish = false - -[dependencies] -iced = { path = "../../..", features = ["pure"] } diff --git a/examples/pure/counter/src/main.rs b/examples/pure/counter/src/main.rs deleted file mode 100644 index 726009df..00000000 --- a/examples/pure/counter/src/main.rs +++ /dev/null @@ -1,49 +0,0 @@ -use iced::pure::{button, column, text, Element, Sandbox}; -use iced::{Alignment, Settings}; - -pub fn main() -> iced::Result { -    Counter::run(Settings::default()) -} - -struct Counter { -    value: i32, -} - -#[derive(Debug, Clone, Copy)] -enum Message { -    IncrementPressed, -    DecrementPressed, -} - -impl Sandbox for Counter { -    type Message = Message; - -    fn new() -> Self { -        Self { value: 0 } -    } - -    fn title(&self) -> String { -        String::from("Counter - Iced") -    } - -    fn update(&mut self, message: Message) { -        match message { -            Message::IncrementPressed => { -                self.value += 1; -            } -            Message::DecrementPressed => { -                self.value -= 1; -            } -        } -    } - -    fn view(&self) -> Element<Message> { -        column() -            .padding(20) -            .align_items(Alignment::Center) -            .push(button("Increment").on_press(Message::IncrementPressed)) -            .push(text(self.value.to_string()).size(50)) -            .push(button("Decrement").on_press(Message::DecrementPressed)) -            .into() -    } -} diff --git a/examples/pure/game_of_life/Cargo.toml b/examples/pure/game_of_life/Cargo.toml deleted file mode 100644 index 22e38f00..00000000 --- a/examples/pure/game_of_life/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "pure_game_of_life" -version = "0.1.0" -authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"] -edition = "2021" -publish = false - -[dependencies] -iced = { path = "../../..", features = ["pure", "canvas", "tokio", "debug"] } -tokio = { version = "1.0", features = ["sync"] } -itertools = "0.9" -rustc-hash = "1.1" -env_logger = "0.9" diff --git a/examples/pure/game_of_life/README.md b/examples/pure/game_of_life/README.md deleted file mode 100644 index aa39201c..00000000 --- a/examples/pure/game_of_life/README.md +++ /dev/null @@ -1,22 +0,0 @@ -## Game of Life - -An interactive version of the [Game of Life], invented by [John Horton Conway]. - -It runs a simulation in a background thread while allowing interaction with a `Canvas` that displays an infinite grid with zooming, panning, and drawing support. - -The __[`main`]__ file contains the relevant code of the example. - -<div align="center"> -  <a href="https://gfycat.com/WhichPaltryChick"> -    <img src="https://thumbs.gfycat.com/WhichPaltryChick-size_restricted.gif"> -  </a> -</div> - -You can run it with `cargo run`: -``` -cargo run --package game_of_life -``` - -[`main`]: src/main.rs -[Game of Life]: https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life -[John Horton Conway]: https://en.wikipedia.org/wiki/John_Horton_Conway diff --git a/examples/pure/game_of_life/src/main.rs b/examples/pure/game_of_life/src/main.rs deleted file mode 100644 index cf6560d6..00000000 --- a/examples/pure/game_of_life/src/main.rs +++ /dev/null @@ -1,903 +0,0 @@ -//! This example showcases an interactive version of the Game of Life, invented -//! by John Conway. It leverages a `Canvas` together with other widgets. -mod preset; - -use grid::Grid; -use preset::Preset; - -use iced::executor; -use iced::pure::{ -    button, checkbox, column, container, pick_list, row, slider, text, -}; -use iced::pure::{Application, Element}; -use iced::theme::{self, Theme}; -use iced::time; -use iced::window; -use iced::{Alignment, Command, Length, Settings, Subscription}; -use std::time::{Duration, Instant}; - -pub fn main() -> iced::Result { -    env_logger::builder().format_timestamp(None).init(); - -    GameOfLife::run(Settings { -        antialiasing: true, -        window: window::Settings { -            position: window::Position::Centered, -            ..window::Settings::default() -        }, -        ..Settings::default() -    }) -} - -#[derive(Default)] -struct GameOfLife { -    grid: Grid, -    is_playing: bool, -    queued_ticks: usize, -    speed: usize, -    next_speed: Option<usize>, -    version: usize, -} - -#[derive(Debug, Clone)] -enum Message { -    Grid(grid::Message, usize), -    Tick(Instant), -    TogglePlayback, -    ToggleGrid(bool), -    Next, -    Clear, -    SpeedChanged(f32), -    PresetPicked(Preset), -} - -impl Application for GameOfLife { -    type Message = Message; -    type Theme = Theme; -    type Executor = executor::Default; -    type Flags = (); - -    fn new(_flags: ()) -> (Self, Command<Message>) { -        ( -            Self { -                speed: 5, -                ..Self::default() -            }, -            Command::none(), -        ) -    } - -    fn title(&self) -> String { -        String::from("Game of Life - Iced") -    } - -    fn update(&mut self, message: Message) -> Command<Message> { -        match message { -            Message::Grid(message, version) => { -                if version == self.version { -                    self.grid.update(message); -                } -            } -            Message::Tick(_) | Message::Next => { -                self.queued_ticks = (self.queued_ticks + 1).min(self.speed); - -                if let Some(task) = self.grid.tick(self.queued_ticks) { -                    if let Some(speed) = self.next_speed.take() { -                        self.speed = speed; -                    } - -                    self.queued_ticks = 0; - -                    let version = self.version; - -                    return Command::perform(task, move |message| { -                        Message::Grid(message, version) -                    }); -                } -            } -            Message::TogglePlayback => { -                self.is_playing = !self.is_playing; -            } -            Message::ToggleGrid(show_grid_lines) => { -                self.grid.toggle_lines(show_grid_lines); -            } -            Message::Clear => { -                self.grid.clear(); -                self.version += 1; -            } -            Message::SpeedChanged(speed) => { -                if self.is_playing { -                    self.next_speed = Some(speed.round() as usize); -                } else { -                    self.speed = speed.round() as usize; -                } -            } -            Message::PresetPicked(new_preset) => { -                self.grid = Grid::from_preset(new_preset); -                self.version += 1; -            } -        } - -        Command::none() -    } - -    fn subscription(&self) -> Subscription<Message> { -        if self.is_playing { -            time::every(Duration::from_millis(1000 / self.speed as u64)) -                .map(Message::Tick) -        } else { -            Subscription::none() -        } -    } - -    fn view(&self) -> Element<Message> { -        let version = self.version; -        let selected_speed = self.next_speed.unwrap_or(self.speed); -        let controls = view_controls( -            self.is_playing, -            self.grid.are_lines_visible(), -            selected_speed, -            self.grid.preset(), -        ); - -        let content = column() -            .push( -                self.grid -                    .view() -                    .map(move |message| Message::Grid(message, version)), -            ) -            .push(controls); - -        container(content) -            .width(Length::Fill) -            .height(Length::Fill) -            .into() -    } - -    fn theme(&self) -> Theme { -        Theme::Dark -    } -} - -fn view_controls<'a>( -    is_playing: bool, -    is_grid_enabled: bool, -    speed: usize, -    preset: Preset, -) -> Element<'a, Message> { -    let playback_controls = row() -        .spacing(10) -        .push( -            button(if is_playing { "Pause" } else { "Play" }) -                .on_press(Message::TogglePlayback), -        ) -        .push( -            button("Next") -                .on_press(Message::Next) -                .style(theme::Button::Secondary), -        ); - -    let speed_controls = row() -        .width(Length::Fill) -        .align_items(Alignment::Center) -        .spacing(10) -        .push(slider(1.0..=1000.0, speed as f32, Message::SpeedChanged)) -        .push(text(format!("x{}", speed)).size(16)); - -    row() -        .padding(10) -        .spacing(20) -        .align_items(Alignment::Center) -        .push(playback_controls) -        .push(speed_controls) -        .push( -            checkbox("Grid", is_grid_enabled, Message::ToggleGrid) -                .size(16) -                .spacing(5) -                .text_size(16), -        ) -        .push( -            pick_list(preset::ALL, Some(preset), Message::PresetPicked) -                .padding(8) -                .text_size(16), -        ) -        .push( -            button("Clear") -                .on_press(Message::Clear) -                .style(theme::Button::Destructive), -        ) -        .into() -} - -mod grid { -    use crate::Preset; -    use iced::pure::widget::canvas::event::{self, Event}; -    use iced::pure::widget::canvas::{ -        self, Cache, Canvas, Cursor, Frame, Geometry, Path, Text, -    }; -    use iced::pure::Element; -    use iced::{ -        alignment, mouse, Color, Length, Point, Rectangle, Size, Theme, Vector, -    }; -    use rustc_hash::{FxHashMap, FxHashSet}; -    use std::future::Future; -    use std::ops::RangeInclusive; -    use std::time::{Duration, Instant}; - -    pub struct Grid { -        state: State, -        preset: Preset, -        life_cache: Cache, -        grid_cache: Cache, -        translation: Vector, -        scaling: f32, -        show_lines: bool, -        last_tick_duration: Duration, -        last_queued_ticks: usize, -    } - -    #[derive(Debug, Clone)] -    pub enum Message { -        Populate(Cell), -        Unpopulate(Cell), -        Translated(Vector), -        Scaled(f32, Option<Vector>), -        Ticked { -            result: Result<Life, TickError>, -            tick_duration: Duration, -        }, -    } - -    #[derive(Debug, Clone)] -    pub enum TickError { -        JoinFailed, -    } - -    impl Default for Grid { -        fn default() -> Self { -            Self::from_preset(Preset::default()) -        } -    } - -    impl Grid { -        const MIN_SCALING: f32 = 0.1; -        const MAX_SCALING: f32 = 2.0; - -        pub fn from_preset(preset: Preset) -> Self { -            Self { -                state: State::with_life( -                    preset -                        .life() -                        .into_iter() -                        .map(|(i, j)| Cell { i, j }) -                        .collect(), -                ), -                preset, -                life_cache: Cache::default(), -                grid_cache: Cache::default(), -                translation: Vector::default(), -                scaling: 1.0, -                show_lines: true, -                last_tick_duration: Duration::default(), -                last_queued_ticks: 0, -            } -        } - -        pub fn tick( -            &mut self, -            amount: usize, -        ) -> Option<impl Future<Output = Message>> { -            let tick = self.state.tick(amount)?; - -            self.last_queued_ticks = amount; - -            Some(async move { -                let start = Instant::now(); -                let result = tick.await; -                let tick_duration = start.elapsed() / amount as u32; - -                Message::Ticked { -                    result, -                    tick_duration, -                } -            }) -        } - -        pub fn update(&mut self, message: Message) { -            match message { -                Message::Populate(cell) => { -                    self.state.populate(cell); -                    self.life_cache.clear(); - -                    self.preset = Preset::Custom; -                } -                Message::Unpopulate(cell) => { -                    self.state.unpopulate(&cell); -                    self.life_cache.clear(); - -                    self.preset = Preset::Custom; -                } -                Message::Translated(translation) => { -                    self.translation = translation; - -                    self.life_cache.clear(); -                    self.grid_cache.clear(); -                } -                Message::Scaled(scaling, translation) => { -                    self.scaling = scaling; - -                    if let Some(translation) = translation { -                        self.translation = translation; -                    } - -                    self.life_cache.clear(); -                    self.grid_cache.clear(); -                } -                Message::Ticked { -                    result: Ok(life), -                    tick_duration, -                } => { -                    self.state.update(life); -                    self.life_cache.clear(); - -                    self.last_tick_duration = tick_duration; -                } -                Message::Ticked { -                    result: Err(error), .. -                } => { -                    dbg!(error); -                } -            } -        } - -        pub fn view(&self) -> Element<Message> { -            Canvas::new(self) -                .width(Length::Fill) -                .height(Length::Fill) -                .into() -        } - -        pub fn clear(&mut self) { -            self.state = State::default(); -            self.preset = Preset::Custom; - -            self.life_cache.clear(); -        } - -        pub fn preset(&self) -> Preset { -            self.preset -        } - -        pub fn toggle_lines(&mut self, enabled: bool) { -            self.show_lines = enabled; -        } - -        pub fn are_lines_visible(&self) -> bool { -            self.show_lines -        } - -        fn visible_region(&self, size: Size) -> Region { -            let width = size.width / self.scaling; -            let height = size.height / self.scaling; - -            Region { -                x: -self.translation.x - width / 2.0, -                y: -self.translation.y - height / 2.0, -                width, -                height, -            } -        } - -        fn project(&self, position: Point, size: Size) -> Point { -            let region = self.visible_region(size); - -            Point::new( -                position.x / self.scaling + region.x, -                position.y / self.scaling + region.y, -            ) -        } -    } - -    impl canvas::Program<Message> for Grid { -        type State = Interaction; - -        fn update( -            &self, -            interaction: &mut Interaction, -            event: Event, -            bounds: Rectangle, -            cursor: Cursor, -        ) -> (event::Status, Option<Message>) { -            if let Event::Mouse(mouse::Event::ButtonReleased(_)) = event { -                *interaction = Interaction::None; -            } - -            let cursor_position = -                if let Some(position) = cursor.position_in(&bounds) { -                    position -                } else { -                    return (event::Status::Ignored, None); -                }; - -            let cell = Cell::at(self.project(cursor_position, bounds.size())); -            let is_populated = self.state.contains(&cell); - -            let (populate, unpopulate) = if is_populated { -                (None, Some(Message::Unpopulate(cell))) -            } else { -                (Some(Message::Populate(cell)), None) -            }; - -            match event { -                Event::Mouse(mouse_event) => match mouse_event { -                    mouse::Event::ButtonPressed(button) => { -                        let message = match button { -                            mouse::Button::Left => { -                                *interaction = if is_populated { -                                    Interaction::Erasing -                                } else { -                                    Interaction::Drawing -                                }; - -                                populate.or(unpopulate) -                            } -                            mouse::Button::Right => { -                                *interaction = Interaction::Panning { -                                    translation: self.translation, -                                    start: cursor_position, -                                }; - -                                None -                            } -                            _ => None, -                        }; - -                        (event::Status::Captured, message) -                    } -                    mouse::Event::CursorMoved { .. } => { -                        let message = match *interaction { -                            Interaction::Drawing => populate, -                            Interaction::Erasing => unpopulate, -                            Interaction::Panning { translation, start } => { -                                Some(Message::Translated( -                                    translation -                                        + (cursor_position - start) -                                            * (1.0 / self.scaling), -                                )) -                            } -                            _ => None, -                        }; - -                        let event_status = match interaction { -                            Interaction::None => event::Status::Ignored, -                            _ => event::Status::Captured, -                        }; - -                        (event_status, message) -                    } -                    mouse::Event::WheelScrolled { delta } => match delta { -                        mouse::ScrollDelta::Lines { y, .. } -                        | mouse::ScrollDelta::Pixels { y, .. } => { -                            if y < 0.0 && self.scaling > Self::MIN_SCALING -                                || y > 0.0 && self.scaling < Self::MAX_SCALING -                            { -                                let old_scaling = self.scaling; - -                                let scaling = (self.scaling * (1.0 + y / 30.0)) -                                    .max(Self::MIN_SCALING) -                                    .min(Self::MAX_SCALING); - -                                let translation = -                                    if let Some(cursor_to_center) = -                                        cursor.position_from(bounds.center()) -                                    { -                                        let factor = scaling - old_scaling; - -                                        Some( -                                            self.translation -                                                - Vector::new( -                                                    cursor_to_center.x * factor -                                                        / (old_scaling -                                                            * old_scaling), -                                                    cursor_to_center.y * factor -                                                        / (old_scaling -                                                            * old_scaling), -                                                ), -                                        ) -                                    } else { -                                        None -                                    }; - -                                ( -                                    event::Status::Captured, -                                    Some(Message::Scaled(scaling, translation)), -                                ) -                            } else { -                                (event::Status::Captured, None) -                            } -                        } -                    }, -                    _ => (event::Status::Ignored, None), -                }, -                _ => (event::Status::Ignored, None), -            } -        } - -        fn draw( -            &self, -            _interaction: &Interaction, -            _theme: &Theme, -            bounds: Rectangle, -            cursor: Cursor, -        ) -> Vec<Geometry> { -            let center = Vector::new(bounds.width / 2.0, bounds.height / 2.0); - -            let life = self.life_cache.draw(bounds.size(), |frame| { -                let background = Path::rectangle(Point::ORIGIN, frame.size()); -                frame.fill(&background, Color::from_rgb8(0x40, 0x44, 0x4B)); - -                frame.with_save(|frame| { -                    frame.translate(center); -                    frame.scale(self.scaling); -                    frame.translate(self.translation); -                    frame.scale(Cell::SIZE as f32); - -                    let region = self.visible_region(frame.size()); - -                    for cell in region.cull(self.state.cells()) { -                        frame.fill_rectangle( -                            Point::new(cell.j as f32, cell.i as f32), -                            Size::UNIT, -                            Color::WHITE, -                        ); -                    } -                }); -            }); - -            let overlay = { -                let mut frame = Frame::new(bounds.size()); - -                let hovered_cell = -                    cursor.position_in(&bounds).map(|position| { -                        Cell::at(self.project(position, frame.size())) -                    }); - -                if let Some(cell) = hovered_cell { -                    frame.with_save(|frame| { -                        frame.translate(center); -                        frame.scale(self.scaling); -                        frame.translate(self.translation); -                        frame.scale(Cell::SIZE as f32); - -                        frame.fill_rectangle( -                            Point::new(cell.j as f32, cell.i as f32), -                            Size::UNIT, -                            Color { -                                a: 0.5, -                                ..Color::BLACK -                            }, -                        ); -                    }); -                } - -                let text = Text { -                    color: Color::WHITE, -                    size: 14.0, -                    position: Point::new(frame.width(), frame.height()), -                    horizontal_alignment: alignment::Horizontal::Right, -                    vertical_alignment: alignment::Vertical::Bottom, -                    ..Text::default() -                }; - -                if let Some(cell) = hovered_cell { -                    frame.fill_text(Text { -                        content: format!("({}, {})", cell.j, cell.i), -                        position: text.position - Vector::new(0.0, 16.0), -                        ..text -                    }); -                } - -                let cell_count = self.state.cell_count(); - -                frame.fill_text(Text { -                    content: format!( -                        "{} cell{} @ {:?} ({})", -                        cell_count, -                        if cell_count == 1 { "" } else { "s" }, -                        self.last_tick_duration, -                        self.last_queued_ticks -                    ), -                    ..text -                }); - -                frame.into_geometry() -            }; - -            if self.scaling < 0.2 || !self.show_lines { -                vec![life, overlay] -            } else { -                let grid = self.grid_cache.draw(bounds.size(), |frame| { -                    frame.translate(center); -                    frame.scale(self.scaling); -                    frame.translate(self.translation); -                    frame.scale(Cell::SIZE as f32); - -                    let region = self.visible_region(frame.size()); -                    let rows = region.rows(); -                    let columns = region.columns(); -                    let (total_rows, total_columns) = -                        (rows.clone().count(), columns.clone().count()); -                    let width = 2.0 / Cell::SIZE as f32; -                    let color = Color::from_rgb8(70, 74, 83); - -                    frame.translate(Vector::new(-width / 2.0, -width / 2.0)); - -                    for row in region.rows() { -                        frame.fill_rectangle( -                            Point::new(*columns.start() as f32, row as f32), -                            Size::new(total_columns as f32, width), -                            color, -                        ); -                    } - -                    for column in region.columns() { -                        frame.fill_rectangle( -                            Point::new(column as f32, *rows.start() as f32), -                            Size::new(width, total_rows as f32), -                            color, -                        ); -                    } -                }); - -                vec![life, grid, overlay] -            } -        } - -        fn mouse_interaction( -            &self, -            interaction: &Interaction, -            bounds: Rectangle, -            cursor: Cursor, -        ) -> mouse::Interaction { -            match interaction { -                Interaction::Drawing => mouse::Interaction::Crosshair, -                Interaction::Erasing => mouse::Interaction::Crosshair, -                Interaction::Panning { .. } => mouse::Interaction::Grabbing, -                Interaction::None if cursor.is_over(&bounds) => { -                    mouse::Interaction::Crosshair -                } -                _ => mouse::Interaction::default(), -            } -        } -    } - -    #[derive(Default)] -    struct State { -        life: Life, -        births: FxHashSet<Cell>, -        is_ticking: bool, -    } - -    impl State { -        pub fn with_life(life: Life) -> Self { -            Self { -                life, -                ..Self::default() -            } -        } - -        fn cell_count(&self) -> usize { -            self.life.len() + self.births.len() -        } - -        fn contains(&self, cell: &Cell) -> bool { -            self.life.contains(cell) || self.births.contains(cell) -        } - -        fn cells(&self) -> impl Iterator<Item = &Cell> { -            self.life.iter().chain(self.births.iter()) -        } - -        fn populate(&mut self, cell: Cell) { -            if self.is_ticking { -                self.births.insert(cell); -            } else { -                self.life.populate(cell); -            } -        } - -        fn unpopulate(&mut self, cell: &Cell) { -            if self.is_ticking { -                let _ = self.births.remove(cell); -            } else { -                self.life.unpopulate(cell); -            } -        } - -        fn update(&mut self, mut life: Life) { -            self.births.drain().for_each(|cell| life.populate(cell)); - -            self.life = life; -            self.is_ticking = false; -        } - -        fn tick( -            &mut self, -            amount: usize, -        ) -> Option<impl Future<Output = Result<Life, TickError>>> { -            if self.is_ticking { -                return None; -            } - -            self.is_ticking = true; - -            let mut life = self.life.clone(); - -            Some(async move { -                tokio::task::spawn_blocking(move || { -                    for _ in 0..amount { -                        life.tick(); -                    } - -                    life -                }) -                .await -                .map_err(|_| TickError::JoinFailed) -            }) -        } -    } - -    #[derive(Clone, Default)] -    pub struct Life { -        cells: FxHashSet<Cell>, -    } - -    impl Life { -        fn len(&self) -> usize { -            self.cells.len() -        } - -        fn contains(&self, cell: &Cell) -> bool { -            self.cells.contains(cell) -        } - -        fn populate(&mut self, cell: Cell) { -            self.cells.insert(cell); -        } - -        fn unpopulate(&mut self, cell: &Cell) { -            let _ = self.cells.remove(cell); -        } - -        fn tick(&mut self) { -            let mut adjacent_life = FxHashMap::default(); - -            for cell in &self.cells { -                let _ = adjacent_life.entry(*cell).or_insert(0); - -                for neighbor in Cell::neighbors(*cell) { -                    let amount = adjacent_life.entry(neighbor).or_insert(0); - -                    *amount += 1; -                } -            } - -            for (cell, amount) in adjacent_life.iter() { -                match amount { -                    2 => {} -                    3 => { -                        let _ = self.cells.insert(*cell); -                    } -                    _ => { -                        let _ = self.cells.remove(cell); -                    } -                } -            } -        } - -        pub fn iter(&self) -> impl Iterator<Item = &Cell> { -            self.cells.iter() -        } -    } - -    impl std::iter::FromIterator<Cell> for Life { -        fn from_iter<I: IntoIterator<Item = Cell>>(iter: I) -> Self { -            Life { -                cells: iter.into_iter().collect(), -            } -        } -    } - -    impl std::fmt::Debug for Life { -        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { -            f.debug_struct("Life") -                .field("cells", &self.cells.len()) -                .finish() -        } -    } - -    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -    pub struct Cell { -        i: isize, -        j: isize, -    } - -    impl Cell { -        const SIZE: usize = 20; - -        fn at(position: Point) -> Cell { -            let i = (position.y / Cell::SIZE as f32).ceil() as isize; -            let j = (position.x / Cell::SIZE as f32).ceil() as isize; - -            Cell { -                i: i.saturating_sub(1), -                j: j.saturating_sub(1), -            } -        } - -        fn cluster(cell: Cell) -> impl Iterator<Item = Cell> { -            use itertools::Itertools; - -            let rows = cell.i.saturating_sub(1)..=cell.i.saturating_add(1); -            let columns = cell.j.saturating_sub(1)..=cell.j.saturating_add(1); - -            rows.cartesian_product(columns).map(|(i, j)| Cell { i, j }) -        } - -        fn neighbors(cell: Cell) -> impl Iterator<Item = Cell> { -            Cell::cluster(cell).filter(move |candidate| *candidate != cell) -        } -    } - -    pub struct Region { -        x: f32, -        y: f32, -        width: f32, -        height: f32, -    } - -    impl Region { -        fn rows(&self) -> RangeInclusive<isize> { -            let first_row = (self.y / Cell::SIZE as f32).floor() as isize; - -            let visible_rows = -                (self.height / Cell::SIZE as f32).ceil() as isize; - -            first_row..=first_row + visible_rows -        } - -        fn columns(&self) -> RangeInclusive<isize> { -            let first_column = (self.x / Cell::SIZE as f32).floor() as isize; - -            let visible_columns = -                (self.width / Cell::SIZE as f32).ceil() as isize; - -            first_column..=first_column + visible_columns -        } - -        fn cull<'a>( -            &self, -            cells: impl Iterator<Item = &'a Cell>, -        ) -> impl Iterator<Item = &'a Cell> { -            let rows = self.rows(); -            let columns = self.columns(); - -            cells.filter(move |cell| { -                rows.contains(&cell.i) && columns.contains(&cell.j) -            }) -        } -    } - -    pub enum Interaction { -        None, -        Drawing, -        Erasing, -        Panning { translation: Vector, start: Point }, -    } - -    impl Default for Interaction { -        fn default() -> Self { -            Self::None -        } -    } -} diff --git a/examples/pure/game_of_life/src/preset.rs b/examples/pure/game_of_life/src/preset.rs deleted file mode 100644 index 964b9120..00000000 --- a/examples/pure/game_of_life/src/preset.rs +++ /dev/null @@ -1,142 +0,0 @@ -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum Preset { -    Custom, -    Xkcd, -    Glider, -    SmallExploder, -    Exploder, -    TenCellRow, -    LightweightSpaceship, -    Tumbler, -    GliderGun, -    Acorn, -} - -pub static ALL: &[Preset] = &[ -    Preset::Custom, -    Preset::Xkcd, -    Preset::Glider, -    Preset::SmallExploder, -    Preset::Exploder, -    Preset::TenCellRow, -    Preset::LightweightSpaceship, -    Preset::Tumbler, -    Preset::GliderGun, -    Preset::Acorn, -]; - -impl Preset { -    pub fn life(self) -> Vec<(isize, isize)> { -        #[rustfmt::skip] -        let cells = match self { -            Preset::Custom => vec![], -            Preset::Xkcd => vec![ -                "  xxx  ", -                "  x x  ", -                "  x x  ", -                "   x   ", -                "x xxx  ", -                " x x x ", -                "   x  x", -                "  x x  ", -                "  x x  ", -            ], -            Preset::Glider => vec![ -                " x ", -                "  x", -                "xxx" -            ], -            Preset::SmallExploder => vec![ -                " x ", -                "xxx", -                "x x", -                " x ", -            ], -            Preset::Exploder => vec![ -                "x x x", -                "x   x", -                "x   x", -                "x   x", -                "x x x", -            ], -            Preset::TenCellRow => vec![ -                "xxxxxxxxxx", -            ], -            Preset::LightweightSpaceship => vec![ -                " xxxxx", -                "x    x", -                "     x", -                "x   x ", -            ], -            Preset::Tumbler => vec![ -                " xx xx ", -                " xx xx ", -                "  x x  ", -                "x x x x", -                "x x x x", -                "xx   xx", -            ], -            Preset::GliderGun => vec![ -                "                        x           ", -                "                      x x           ", -                "            xx      xx            xx", -                "           x   x    xx            xx", -                "xx        x     x   xx              ", -                "xx        x   x xx    x x           ", -                "          x     x       x           ", -                "           x   x                    ", -                "            xx                      ", -            ], -            Preset::Acorn => vec![ -                " x     ", -                "   x   ", -                "xx  xxx", -            ], -        }; - -        let start_row = -(cells.len() as isize / 2); - -        cells -            .into_iter() -            .enumerate() -            .flat_map(|(i, cells)| { -                let start_column = -(cells.len() as isize / 2); - -                cells -                    .chars() -                    .enumerate() -                    .filter(|(_, c)| !c.is_whitespace()) -                    .map(move |(j, _)| { -                        (start_row + i as isize, start_column + j as isize) -                    }) -            }) -            .collect() -    } -} - -impl Default for Preset { -    fn default() -> Preset { -        Preset::Xkcd -    } -} - -impl std::fmt::Display for Preset { -    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { -        write!( -            f, -            "{}", -            match self { -                Preset::Custom => "Custom", -                Preset::Xkcd => "xkcd #2293", -                Preset::Glider => "Glider", -                Preset::SmallExploder => "Small Exploder", -                Preset::Exploder => "Exploder", -                Preset::TenCellRow => "10 Cell Row", -                Preset::LightweightSpaceship => "Lightweight spaceship", -                Preset::Tumbler => "Tumbler", -                Preset::GliderGun => "Gosper Glider Gun", -                Preset::Acorn => "Acorn", -            } -        ) -    } -} diff --git a/examples/pure/pane_grid/Cargo.toml b/examples/pure/pane_grid/Cargo.toml deleted file mode 100644 index a51cdaf0..00000000 --- a/examples/pure/pane_grid/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "pure_pane_grid" -version = "0.1.0" -authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"] -edition = "2021" -publish = false - -[dependencies] -iced = { path = "../../..", features = ["pure", "debug"] } -iced_native = { path = "../../../native" } -iced_lazy = { path = "../../../lazy", features = ["pure"] } diff --git a/examples/pure/pane_grid/src/main.rs b/examples/pure/pane_grid/src/main.rs deleted file mode 100644 index e85ed78d..00000000 --- a/examples/pure/pane_grid/src/main.rs +++ /dev/null @@ -1,369 +0,0 @@ -use iced::alignment::{self, Alignment}; -use iced::executor; -use iced::keyboard; -use iced::pure::widget::pane_grid::{self, PaneGrid}; -use iced::pure::{button, column, container, row, scrollable, text}; -use iced::pure::{Application, Element}; -use iced::theme::{self, Theme}; -use iced::{Color, Command, Length, Settings, Size, Subscription}; -use iced_lazy::pure::responsive; -use iced_native::{event, subscription, Event}; - -pub fn main() -> iced::Result { -    Example::run(Settings::default()) -} - -struct Example { -    panes: pane_grid::State<Pane>, -    panes_created: usize, -    focus: Option<pane_grid::Pane>, -} - -#[derive(Debug, Clone, Copy)] -enum Message { -    Split(pane_grid::Axis, pane_grid::Pane), -    SplitFocused(pane_grid::Axis), -    FocusAdjacent(pane_grid::Direction), -    Clicked(pane_grid::Pane), -    Dragged(pane_grid::DragEvent), -    Resized(pane_grid::ResizeEvent), -    TogglePin(pane_grid::Pane), -    Close(pane_grid::Pane), -    CloseFocused, -} - -impl Application for Example { -    type Message = Message; -    type Theme = Theme; -    type Executor = executor::Default; -    type Flags = (); - -    fn new(_flags: ()) -> (Self, Command<Message>) { -        let (panes, _) = pane_grid::State::new(Pane::new(0)); - -        ( -            Example { -                panes, -                panes_created: 1, -                focus: None, -            }, -            Command::none(), -        ) -    } - -    fn title(&self) -> String { -        String::from("Pane grid - Iced") -    } - -    fn update(&mut self, message: Message) -> Command<Message> { -        match message { -            Message::Split(axis, pane) => { -                let result = self.panes.split( -                    axis, -                    &pane, -                    Pane::new(self.panes_created), -                ); - -                if let Some((pane, _)) = result { -                    self.focus = Some(pane); -                } - -                self.panes_created += 1; -            } -            Message::SplitFocused(axis) => { -                if let Some(pane) = self.focus { -                    let result = self.panes.split( -                        axis, -                        &pane, -                        Pane::new(self.panes_created), -                    ); - -                    if let Some((pane, _)) = result { -                        self.focus = Some(pane); -                    } - -                    self.panes_created += 1; -                } -            } -            Message::FocusAdjacent(direction) => { -                if let Some(pane) = self.focus { -                    if let Some(adjacent) = -                        self.panes.adjacent(&pane, direction) -                    { -                        self.focus = Some(adjacent); -                    } -                } -            } -            Message::Clicked(pane) => { -                self.focus = Some(pane); -            } -            Message::Resized(pane_grid::ResizeEvent { split, ratio }) => { -                self.panes.resize(&split, ratio); -            } -            Message::Dragged(pane_grid::DragEvent::Dropped { -                pane, -                target, -            }) => { -                self.panes.swap(&pane, &target); -            } -            Message::Dragged(_) => {} -            Message::TogglePin(pane) => { -                if let Some(Pane { is_pinned, .. }) = self.panes.get_mut(&pane) -                { -                    *is_pinned = !*is_pinned; -                } -            } -            Message::Close(pane) => { -                if let Some((_, sibling)) = self.panes.close(&pane) { -                    self.focus = Some(sibling); -                } -            } -            Message::CloseFocused => { -                if let Some(pane) = self.focus { -                    if let Some(Pane { is_pinned, .. }) = self.panes.get(&pane) -                    { -                        if !is_pinned { -                            if let Some((_, sibling)) = self.panes.close(&pane) -                            { -                                self.focus = Some(sibling); -                            } -                        } -                    } -                } -            } -        } - -        Command::none() -    } - -    fn subscription(&self) -> Subscription<Message> { -        subscription::events_with(|event, status| { -            if let event::Status::Captured = status { -                return None; -            } - -            match event { -                Event::Keyboard(keyboard::Event::KeyPressed { -                    modifiers, -                    key_code, -                }) if modifiers.command() => handle_hotkey(key_code), -                _ => None, -            } -        }) -    } - -    fn view(&self) -> Element<Message> { -        let focus = self.focus; -        let total_panes = self.panes.len(); - -        let pane_grid = PaneGrid::new(&self.panes, |id, pane| { -            let is_focused = focus == Some(id); - -            let pin_button = button( -                text(if pane.is_pinned { "Unpin" } else { "Pin" }).size(14), -            ) -            .on_press(Message::TogglePin(id)) -            .padding(3); - -            let title = row() -                .push(pin_button) -                .push("Pane") -                .push(text(pane.id.to_string()).style(if is_focused { -                    PANE_ID_COLOR_FOCUSED -                } else { -                    PANE_ID_COLOR_UNFOCUSED -                })) -                .spacing(5); - -            let title_bar = pane_grid::TitleBar::new(title) -                .controls(view_controls(id, total_panes, pane.is_pinned)) -                .padding(10) -                .style(if is_focused { -                    style::title_bar_focused -                } else { -                    style::title_bar_active -                }); - -            pane_grid::Content::new(responsive(move |size| { -                view_content(id, total_panes, pane.is_pinned, size) -            })) -            .title_bar(title_bar) -            .style(if is_focused { -                style::pane_focused -            } else { -                style::pane_active -            }) -        }) -        .width(Length::Fill) -        .height(Length::Fill) -        .spacing(10) -        .on_click(Message::Clicked) -        .on_drag(Message::Dragged) -        .on_resize(10, Message::Resized); - -        container(pane_grid) -            .width(Length::Fill) -            .height(Length::Fill) -            .padding(10) -            .into() -    } -} - -const PANE_ID_COLOR_UNFOCUSED: Color = Color::from_rgb( -    0xFF as f32 / 255.0, -    0xC7 as f32 / 255.0, -    0xC7 as f32 / 255.0, -); -const PANE_ID_COLOR_FOCUSED: Color = Color::from_rgb( -    0xFF as f32 / 255.0, -    0x47 as f32 / 255.0, -    0x47 as f32 / 255.0, -); - -fn handle_hotkey(key_code: keyboard::KeyCode) -> Option<Message> { -    use keyboard::KeyCode; -    use pane_grid::{Axis, Direction}; - -    let direction = match key_code { -        KeyCode::Up => Some(Direction::Up), -        KeyCode::Down => Some(Direction::Down), -        KeyCode::Left => Some(Direction::Left), -        KeyCode::Right => Some(Direction::Right), -        _ => None, -    }; - -    match key_code { -        KeyCode::V => Some(Message::SplitFocused(Axis::Vertical)), -        KeyCode::H => Some(Message::SplitFocused(Axis::Horizontal)), -        KeyCode::W => Some(Message::CloseFocused), -        _ => direction.map(Message::FocusAdjacent), -    } -} - -struct Pane { -    id: usize, -    pub is_pinned: bool, -} - -impl Pane { -    fn new(id: usize) -> Self { -        Self { -            id, -            is_pinned: false, -        } -    } -} - -fn view_content<'a>( -    pane: pane_grid::Pane, -    total_panes: usize, -    is_pinned: bool, -    size: Size, -) -> Element<'a, Message> { -    let button = |label, message| { -        button( -            text(label) -                .width(Length::Fill) -                .horizontal_alignment(alignment::Horizontal::Center) -                .size(16), -        ) -        .width(Length::Fill) -        .padding(8) -        .on_press(message) -    }; - -    let mut controls = column() -        .spacing(5) -        .max_width(150) -        .push(button( -            "Split horizontally", -            Message::Split(pane_grid::Axis::Horizontal, pane), -        )) -        .push(button( -            "Split vertically", -            Message::Split(pane_grid::Axis::Vertical, pane), -        )); - -    if total_panes > 1 && !is_pinned { -        controls = controls.push( -            button("Close", Message::Close(pane)) -                .style(theme::Button::Destructive), -        ); -    } - -    let content = column() -        .width(Length::Fill) -        .spacing(10) -        .align_items(Alignment::Center) -        .push(text(format!("{}x{}", size.width, size.height)).size(24)) -        .push(controls); - -    container(scrollable(content)) -        .width(Length::Fill) -        .height(Length::Fill) -        .padding(5) -        .center_y() -        .into() -} - -fn view_controls<'a>( -    pane: pane_grid::Pane, -    total_panes: usize, -    is_pinned: bool, -) -> Element<'a, Message> { -    let mut button = button(text("Close").size(14)) -        .style(theme::Button::Destructive) -        .padding(3); - -    if total_panes > 1 && !is_pinned { -        button = button.on_press(Message::Close(pane)); -    } - -    button.into() -} - -mod style { -    use iced::{container, Theme}; - -    pub fn title_bar_active(theme: &Theme) -> container::Appearance { -        let palette = theme.extended_palette(); - -        container::Appearance { -            text_color: Some(palette.background.strong.text), -            background: Some(palette.background.strong.color.into()), -            ..Default::default() -        } -    } - -    pub fn title_bar_focused(theme: &Theme) -> container::Appearance { -        let palette = theme.extended_palette(); - -        container::Appearance { -            text_color: Some(palette.primary.strong.text), -            background: Some(palette.primary.strong.color.into()), -            ..Default::default() -        } -    } - -    pub fn pane_active(theme: &Theme) -> container::Appearance { -        let palette = theme.extended_palette(); - -        container::Appearance { -            background: Some(palette.background.weak.color.into()), -            border_width: 2.0, -            border_color: palette.background.strong.color, -            ..Default::default() -        } -    } - -    pub fn pane_focused(theme: &Theme) -> container::Appearance { -        let palette = theme.extended_palette(); - -        container::Appearance { -            background: Some(palette.background.weak.color.into()), -            border_width: 2.0, -            border_color: palette.primary.strong.color, -            ..Default::default() -        } -    } -} diff --git a/examples/pure/pick_list/Cargo.toml b/examples/pure/pick_list/Cargo.toml deleted file mode 100644 index c0fcac3c..00000000 --- a/examples/pure/pick_list/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "pure_pick_list" -version = "0.1.0" -authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"] -edition = "2021" -publish = false - -[dependencies] -iced = { path = "../../..", features = ["debug", "pure"] } diff --git a/examples/pure/pick_list/src/main.rs b/examples/pure/pick_list/src/main.rs deleted file mode 100644 index b9947107..00000000 --- a/examples/pure/pick_list/src/main.rs +++ /dev/null @@ -1,109 +0,0 @@ -use iced::pure::{column, container, pick_list, scrollable, vertical_space}; -use iced::pure::{Element, Sandbox}; -use iced::{Alignment, Length, Settings}; - -pub fn main() -> iced::Result { -    Example::run(Settings::default()) -} - -#[derive(Default)] -struct Example { -    selected_language: Option<Language>, -} - -#[derive(Debug, Clone, Copy)] -enum Message { -    LanguageSelected(Language), -} - -impl Sandbox for Example { -    type Message = Message; - -    fn new() -> Self { -        Self::default() -    } - -    fn title(&self) -> String { -        String::from("Pick list - Iced") -    } - -    fn update(&mut self, message: Message) { -        match message { -            Message::LanguageSelected(language) => { -                self.selected_language = Some(language); -            } -        } -    } - -    fn view(&self) -> Element<Message> { -        let pick_list = pick_list( -            &Language::ALL[..], -            self.selected_language, -            Message::LanguageSelected, -        ) -        .placeholder("Choose a language..."); - -        let content = column() -            .width(Length::Fill) -            .align_items(Alignment::Center) -            .spacing(10) -            .push(vertical_space(Length::Units(600))) -            .push("Which is your favorite language?") -            .push(pick_list) -            .push(vertical_space(Length::Units(600))); - -        container(scrollable(content)) -            .width(Length::Fill) -            .height(Length::Fill) -            .center_x() -            .center_y() -            .into() -    } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum Language { -    Rust, -    Elm, -    Ruby, -    Haskell, -    C, -    Javascript, -    Other, -} - -impl Language { -    const ALL: [Language; 7] = [ -        Language::C, -        Language::Elm, -        Language::Ruby, -        Language::Haskell, -        Language::Rust, -        Language::Javascript, -        Language::Other, -    ]; -} - -impl Default for Language { -    fn default() -> Language { -        Language::Rust -    } -} - -impl std::fmt::Display for Language { -    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { -        write!( -            f, -            "{}", -            match self { -                Language::Rust => "Rust", -                Language::Elm => "Elm", -                Language::Ruby => "Ruby", -                Language::Haskell => "Haskell", -                Language::C => "C", -                Language::Javascript => "Javascript", -                Language::Other => "Some other language", -            } -        ) -    } -} diff --git a/examples/pure/todos/Cargo.toml b/examples/pure/todos/Cargo.toml deleted file mode 100644 index 217179e8..00000000 --- a/examples/pure/todos/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "pure_todos" -version = "0.1.0" -authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"] -edition = "2021" -publish = false - -[dependencies] -iced = { path = "../../..", features = ["async-std", "debug", "default_system_font", "pure"] } -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" - -[target.'cfg(not(target_arch = "wasm32"))'.dependencies] -async-std = "1.0" -directories-next = "2.0" - -[target.'cfg(target_arch = "wasm32")'.dependencies] -web-sys = { version = "0.3", features = ["Window", "Storage"] } -wasm-timer = "0.2" diff --git a/examples/pure/todos/src/main.rs b/examples/pure/todos/src/main.rs deleted file mode 100644 index 9a74ea71..00000000 --- a/examples/pure/todos/src/main.rs +++ /dev/null @@ -1,556 +0,0 @@ -use iced::alignment::{self, Alignment}; -use iced::pure::widget::Text; -use iced::pure::{ -    button, checkbox, column, container, row, scrollable, text, text_input, -    Application, Element, -}; -use iced::theme::{self, Theme}; -use iced::window; -use iced::{Color, Command, Font, Length, Settings}; -use serde::{Deserialize, Serialize}; - -pub fn main() -> iced::Result { -    Todos::run(Settings { -        window: window::Settings { -            size: (500, 800), -            ..window::Settings::default() -        }, -        ..Settings::default() -    }) -} - -#[derive(Debug)] -enum Todos { -    Loading, -    Loaded(State), -} - -#[derive(Debug, Default)] -struct State { -    input_value: String, -    filter: Filter, -    tasks: Vec<Task>, -    dirty: bool, -    saving: bool, -} - -#[derive(Debug, Clone)] -enum Message { -    Loaded(Result<SavedState, LoadError>), -    Saved(Result<(), SaveError>), -    InputChanged(String), -    CreateTask, -    FilterChanged(Filter), -    TaskMessage(usize, TaskMessage), -} - -impl Application for Todos { -    type Message = Message; -    type Theme = Theme; -    type Executor = iced::executor::Default; -    type Flags = (); - -    fn new(_flags: ()) -> (Todos, Command<Message>) { -        ( -            Todos::Loading, -            Command::perform(SavedState::load(), Message::Loaded), -        ) -    } - -    fn title(&self) -> String { -        let dirty = match self { -            Todos::Loading => false, -            Todos::Loaded(state) => state.dirty, -        }; - -        format!("Todos{} - Iced", if dirty { "*" } else { "" }) -    } - -    fn update(&mut self, message: Message) -> Command<Message> { -        match self { -            Todos::Loading => { -                match message { -                    Message::Loaded(Ok(state)) => { -                        *self = Todos::Loaded(State { -                            input_value: state.input_value, -                            filter: state.filter, -                            tasks: state.tasks, -                            ..State::default() -                        }); -                    } -                    Message::Loaded(Err(_)) => { -                        *self = Todos::Loaded(State::default()); -                    } -                    _ => {} -                } - -                Command::none() -            } -            Todos::Loaded(state) => { -                let mut saved = false; - -                match message { -                    Message::InputChanged(value) => { -                        state.input_value = value; -                    } -                    Message::CreateTask => { -                        if !state.input_value.is_empty() { -                            state -                                .tasks -                                .push(Task::new(state.input_value.clone())); -                            state.input_value.clear(); -                        } -                    } -                    Message::FilterChanged(filter) => { -                        state.filter = filter; -                    } -                    Message::TaskMessage(i, TaskMessage::Delete) => { -                        state.tasks.remove(i); -                    } -                    Message::TaskMessage(i, task_message) => { -                        if let Some(task) = state.tasks.get_mut(i) { -                            task.update(task_message); -                        } -                    } -                    Message::Saved(_) => { -                        state.saving = false; -                        saved = true; -                    } -                    _ => {} -                } - -                if !saved { -                    state.dirty = true; -                } - -                if state.dirty && !state.saving { -                    state.dirty = false; -                    state.saving = true; - -                    Command::perform( -                        SavedState { -                            input_value: state.input_value.clone(), -                            filter: state.filter, -                            tasks: state.tasks.clone(), -                        } -                        .save(), -                        Message::Saved, -                    ) -                } else { -                    Command::none() -                } -            } -        } -    } - -    fn view(&self) -> Element<Message> { -        match self { -            Todos::Loading => loading_message(), -            Todos::Loaded(State { -                input_value, -                filter, -                tasks, -                .. -            }) => { -                let title = text("todos") -                    .width(Length::Fill) -                    .size(100) -                    .style(Color::from([0.5, 0.5, 0.5])) -                    .horizontal_alignment(alignment::Horizontal::Center); - -                let input = text_input( -                    "What needs to be done?", -                    input_value, -                    Message::InputChanged, -                ) -                .padding(15) -                .size(30) -                .on_submit(Message::CreateTask); - -                let controls = view_controls(tasks, *filter); -                let filtered_tasks = -                    tasks.iter().filter(|task| filter.matches(task)); - -                let tasks: Element<_> = if filtered_tasks.count() > 0 { -                    tasks -                        .iter() -                        .enumerate() -                        .filter(|(_, task)| filter.matches(task)) -                        .fold(column().spacing(20), |column, (i, task)| { -                            column.push(task.view().map(move |message| { -                                Message::TaskMessage(i, message) -                            })) -                        }) -                        .into() -                } else { -                    empty_message(match filter { -                        Filter::All => "You have not created a task yet...", -                        Filter::Active => "All your tasks are done! :D", -                        Filter::Completed => { -                            "You have not completed a task yet..." -                        } -                    }) -                }; - -                let content = column() -                    .spacing(20) -                    .max_width(800) -                    .push(title) -                    .push(input) -                    .push(controls) -                    .push(tasks); - -                scrollable( -                    container(content) -                        .width(Length::Fill) -                        .padding(40) -                        .center_x(), -                ) -                .into() -            } -        } -    } -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -struct Task { -    description: String, -    completed: bool, - -    #[serde(skip)] -    state: TaskState, -} - -#[derive(Debug, Clone)] -pub enum TaskState { -    Idle, -    Editing, -} - -impl Default for TaskState { -    fn default() -> Self { -        Self::Idle -    } -} - -#[derive(Debug, Clone)] -pub enum TaskMessage { -    Completed(bool), -    Edit, -    DescriptionEdited(String), -    FinishEdition, -    Delete, -} - -impl Task { -    fn new(description: String) -> Self { -        Task { -            description, -            completed: false, -            state: TaskState::Idle, -        } -    } - -    fn update(&mut self, message: TaskMessage) { -        match message { -            TaskMessage::Completed(completed) => { -                self.completed = completed; -            } -            TaskMessage::Edit => { -                self.state = TaskState::Editing; -            } -            TaskMessage::DescriptionEdited(new_description) => { -                self.description = new_description; -            } -            TaskMessage::FinishEdition => { -                if !self.description.is_empty() { -                    self.state = TaskState::Idle; -                } -            } -            TaskMessage::Delete => {} -        } -    } - -    fn view(&self) -> Element<TaskMessage> { -        match &self.state { -            TaskState::Idle => { -                let checkbox = checkbox( -                    &self.description, -                    self.completed, -                    TaskMessage::Completed, -                ) -                .width(Length::Fill); - -                row() -                    .spacing(20) -                    .align_items(Alignment::Center) -                    .push(checkbox) -                    .push( -                        button(edit_icon()) -                            .on_press(TaskMessage::Edit) -                            .padding(10) -                            .style(theme::Button::Text), -                    ) -                    .into() -            } -            TaskState::Editing => { -                let text_input = text_input( -                    "Describe your task...", -                    &self.description, -                    TaskMessage::DescriptionEdited, -                ) -                .on_submit(TaskMessage::FinishEdition) -                .padding(10); - -                row() -                    .spacing(20) -                    .align_items(Alignment::Center) -                    .push(text_input) -                    .push( -                        button( -                            row() -                                .spacing(10) -                                .push(delete_icon()) -                                .push("Delete"), -                        ) -                        .on_press(TaskMessage::Delete) -                        .padding(10) -                        .style(theme::Button::Destructive), -                    ) -                    .into() -            } -        } -    } -} - -fn view_controls(tasks: &[Task], current_filter: Filter) -> Element<Message> { -    let tasks_left = tasks.iter().filter(|task| !task.completed).count(); - -    let filter_button = |label, filter, current_filter| { -        let label = text(label).size(16); - -        let button = button(label).style(if filter == current_filter { -            theme::Button::Primary -        } else { -            theme::Button::Text -        }); - -        button.on_press(Message::FilterChanged(filter)).padding(8) -    }; - -    row() -        .spacing(20) -        .align_items(Alignment::Center) -        .push( -            text(format!( -                "{} {} left", -                tasks_left, -                if tasks_left == 1 { "task" } else { "tasks" } -            )) -            .width(Length::Fill) -            .size(16), -        ) -        .push( -            row() -                .width(Length::Shrink) -                .spacing(10) -                .push(filter_button("All", Filter::All, current_filter)) -                .push(filter_button("Active", Filter::Active, current_filter)) -                .push(filter_button( -                    "Completed", -                    Filter::Completed, -                    current_filter, -                )), -        ) -        .into() -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] -pub enum Filter { -    All, -    Active, -    Completed, -} - -impl Default for Filter { -    fn default() -> Self { -        Filter::All -    } -} - -impl Filter { -    fn matches(&self, task: &Task) -> bool { -        match self { -            Filter::All => true, -            Filter::Active => !task.completed, -            Filter::Completed => task.completed, -        } -    } -} - -fn loading_message<'a>() -> Element<'a, Message> { -    container( -        text("Loading...") -            .horizontal_alignment(alignment::Horizontal::Center) -            .size(50), -    ) -    .width(Length::Fill) -    .height(Length::Fill) -    .center_y() -    .into() -} - -fn empty_message(message: &str) -> Element<'_, Message> { -    container( -        text(message) -            .width(Length::Fill) -            .size(25) -            .horizontal_alignment(alignment::Horizontal::Center) -            .style(Color::from([0.7, 0.7, 0.7])), -    ) -    .width(Length::Fill) -    .height(Length::Units(200)) -    .center_y() -    .into() -} - -// Fonts -const ICONS: Font = Font::External { -    name: "Icons", -    bytes: include_bytes!("../../../todos/fonts/icons.ttf"), -}; - -fn icon(unicode: char) -> Text { -    Text::new(unicode.to_string()) -        .font(ICONS) -        .width(Length::Units(20)) -        .horizontal_alignment(alignment::Horizontal::Center) -        .size(20) -} - -fn edit_icon() -> Text { -    icon('\u{F303}') -} - -fn delete_icon() -> Text { -    icon('\u{F1F8}') -} - -// Persistence -#[derive(Debug, Clone, Serialize, Deserialize)] -struct SavedState { -    input_value: String, -    filter: Filter, -    tasks: Vec<Task>, -} - -#[derive(Debug, Clone)] -enum LoadError { -    File, -    Format, -} - -#[derive(Debug, Clone)] -enum SaveError { -    File, -    Write, -    Format, -} - -#[cfg(not(target_arch = "wasm32"))] -impl SavedState { -    fn path() -> std::path::PathBuf { -        let mut path = if let Some(project_dirs) = -            directories_next::ProjectDirs::from("rs", "Iced", "Todos") -        { -            project_dirs.data_dir().into() -        } else { -            std::env::current_dir().unwrap_or_default() -        }; - -        path.push("todos.json"); - -        path -    } - -    async fn load() -> Result<SavedState, LoadError> { -        use async_std::prelude::*; - -        let mut contents = String::new(); - -        let mut file = async_std::fs::File::open(Self::path()) -            .await -            .map_err(|_| LoadError::File)?; - -        file.read_to_string(&mut contents) -            .await -            .map_err(|_| LoadError::File)?; - -        serde_json::from_str(&contents).map_err(|_| LoadError::Format) -    } - -    async fn save(self) -> Result<(), SaveError> { -        use async_std::prelude::*; - -        let json = serde_json::to_string_pretty(&self) -            .map_err(|_| SaveError::Format)?; - -        let path = Self::path(); - -        if let Some(dir) = path.parent() { -            async_std::fs::create_dir_all(dir) -                .await -                .map_err(|_| SaveError::File)?; -        } - -        { -            let mut file = async_std::fs::File::create(path) -                .await -                .map_err(|_| SaveError::File)?; - -            file.write_all(json.as_bytes()) -                .await -                .map_err(|_| SaveError::Write)?; -        } - -        // This is a simple way to save at most once every couple seconds -        async_std::task::sleep(std::time::Duration::from_secs(2)).await; - -        Ok(()) -    } -} - -#[cfg(target_arch = "wasm32")] -impl SavedState { -    fn storage() -> Option<web_sys::Storage> { -        let window = web_sys::window()?; - -        window.local_storage().ok()? -    } - -    async fn load() -> Result<SavedState, LoadError> { -        let storage = Self::storage().ok_or(LoadError::File)?; - -        let contents = storage -            .get_item("state") -            .map_err(|_| LoadError::File)? -            .ok_or(LoadError::File)?; - -        serde_json::from_str(&contents).map_err(|_| LoadError::Format) -    } - -    async fn save(self) -> Result<(), SaveError> { -        let storage = Self::storage().ok_or(SaveError::File)?; - -        let json = serde_json::to_string_pretty(&self) -            .map_err(|_| SaveError::Format)?; - -        storage -            .set_item("state", &json) -            .map_err(|_| SaveError::Write)?; - -        let _ = wasm_timer::Delay::new(std::time::Duration::from_secs(2)).await; - -        Ok(()) -    } -} diff --git a/examples/pure/tooltip/Cargo.toml b/examples/pure/tooltip/Cargo.toml deleted file mode 100644 index d84dfb37..00000000 --- a/examples/pure/tooltip/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "pure_tooltip" -version = "0.1.0" -authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>", "Casper Rogild Storm"] -edition = "2021" -publish = false - -[dependencies] -iced = { path = "../../..", features = ["pure"] } diff --git a/examples/pure/tooltip/src/main.rs b/examples/pure/tooltip/src/main.rs deleted file mode 100644 index e9a6c111..00000000 --- a/examples/pure/tooltip/src/main.rs +++ /dev/null @@ -1,76 +0,0 @@ -use iced::pure::widget::tooltip::Position; -use iced::pure::{button, container, tooltip}; -use iced::pure::{Element, Sandbox}; -use iced::theme; -use iced::{Length, Settings}; - -pub fn main() -> iced::Result { -    Example::run(Settings::default()) -} - -struct Example { -    position: Position, -} - -#[derive(Debug, Clone)] -enum Message { -    ChangePosition, -} - -impl Sandbox for Example { -    type Message = Message; - -    fn new() -> Self { -        Self { -            position: Position::Bottom, -        } -    } - -    fn title(&self) -> String { -        String::from("Tooltip - Iced") -    } - -    fn update(&mut self, message: Message) { -        match message { -            Message::ChangePosition => { -                let position = match &self.position { -                    Position::FollowCursor => Position::Top, -                    Position::Top => Position::Bottom, -                    Position::Bottom => Position::Left, -                    Position::Left => Position::Right, -                    Position::Right => Position::FollowCursor, -                }; - -                self.position = position -            } -        } -    } - -    fn view(&self) -> Element<Message> { -        let tooltip = tooltip( -            button("Press to change position") -                .on_press(Message::ChangePosition), -            position_to_text(self.position), -            self.position, -        ) -        .gap(10) -        .style(theme::Container::Box); - -        container(tooltip) -            .width(Length::Fill) -            .height(Length::Fill) -            .center_x() -            .center_y() -            .into() -    } -} - -fn position_to_text<'a>(position: Position) -> &'a str { -    match position { -        Position::FollowCursor => "Follow Cursor", -        Position::Top => "Top", -        Position::Bottom => "Bottom", -        Position::Left => "Left", -        Position::Right => "Right", -    } -} diff --git a/examples/pure/tour/Cargo.toml b/examples/pure/tour/Cargo.toml deleted file mode 100644 index 8ce5f198..00000000 --- a/examples/pure/tour/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "pure_tour" -version = "0.1.0" -authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"] -edition = "2021" -publish = false - -[dependencies] -iced = { path = "../../..", features = ["image", "debug", "pure"] } -env_logger = "0.8" diff --git a/examples/pure/tour/src/main.rs b/examples/pure/tour/src/main.rs deleted file mode 100644 index 05c269c3..00000000 --- a/examples/pure/tour/src/main.rs +++ /dev/null @@ -1,664 +0,0 @@ -use iced::alignment; -use iced::pure::widget::{Button, Column, Container, Slider}; -use iced::pure::{ -    checkbox, column, container, horizontal_space, image, radio, row, -    scrollable, slider, text, text_input, toggler, vertical_space, -}; -use iced::pure::{Element, Sandbox}; -use iced::theme; -use iced::{Color, Length, Renderer, Settings}; - -pub fn main() -> iced::Result { -    env_logger::init(); - -    Tour::run(Settings::default()) -} - -pub struct Tour { -    steps: Steps, -    debug: bool, -} - -impl Sandbox for Tour { -    type Message = Message; - -    fn new() -> Tour { -        Tour { -            steps: Steps::new(), -            debug: false, -        } -    } - -    fn title(&self) -> String { -        format!("{} - Iced", self.steps.title()) -    } - -    fn update(&mut self, event: Message) { -        match event { -            Message::BackPressed => { -                self.steps.go_back(); -            } -            Message::NextPressed => { -                self.steps.advance(); -            } -            Message::StepMessage(step_msg) => { -                self.steps.update(step_msg, &mut self.debug); -            } -        } -    } - -    fn view(&self) -> Element<Message> { -        let Tour { steps, .. } = self; - -        let mut controls = row(); - -        if steps.has_previous() { -            controls = controls.push( -                button("Back") -                    .on_press(Message::BackPressed) -                    .style(theme::Button::Secondary), -            ); -        } - -        controls = controls.push(horizontal_space(Length::Fill)); - -        if steps.can_continue() { -            controls = controls.push( -                button("Next") -                    .on_press(Message::NextPressed) -                    .style(theme::Button::Primary), -            ); -        } - -        let content: Element<_> = column() -            .max_width(540) -            .spacing(20) -            .padding(20) -            .push(steps.view(self.debug).map(Message::StepMessage)) -            .push(controls) -            .into(); - -        let scrollable = -            scrollable(container(content).width(Length::Fill).center_x()); - -        container(scrollable).height(Length::Fill).center_y().into() -    } -} - -#[derive(Debug, Clone)] -pub enum Message { -    BackPressed, -    NextPressed, -    StepMessage(StepMessage), -} - -struct Steps { -    steps: Vec<Step>, -    current: usize, -} - -impl Steps { -    fn new() -> Steps { -        Steps { -            steps: vec![ -                Step::Welcome, -                Step::Slider { value: 50 }, -                Step::RowsAndColumns { -                    layout: Layout::Row, -                    spacing: 20, -                }, -                Step::Text { -                    size: 30, -                    color: Color::BLACK, -                }, -                Step::Radio { selection: None }, -                Step::Toggler { -                    can_continue: false, -                }, -                Step::Image { width: 300 }, -                Step::Scrollable, -                Step::TextInput { -                    value: String::new(), -                    is_secure: false, -                }, -                Step::Debugger, -                Step::End, -            ], -            current: 0, -        } -    } - -    fn update(&mut self, msg: StepMessage, debug: &mut bool) { -        self.steps[self.current].update(msg, debug); -    } - -    fn view(&self, debug: bool) -> Element<StepMessage> { -        self.steps[self.current].view(debug) -    } - -    fn advance(&mut self) { -        if self.can_continue() { -            self.current += 1; -        } -    } - -    fn go_back(&mut self) { -        if self.has_previous() { -            self.current -= 1; -        } -    } - -    fn has_previous(&self) -> bool { -        self.current > 0 -    } - -    fn can_continue(&self) -> bool { -        self.current + 1 < self.steps.len() -            && self.steps[self.current].can_continue() -    } - -    fn title(&self) -> &str { -        self.steps[self.current].title() -    } -} - -enum Step { -    Welcome, -    Slider { value: u8 }, -    RowsAndColumns { layout: Layout, spacing: u16 }, -    Text { size: u16, color: Color }, -    Radio { selection: Option<Language> }, -    Toggler { can_continue: bool }, -    Image { width: u16 }, -    Scrollable, -    TextInput { value: String, is_secure: bool }, -    Debugger, -    End, -} - -#[derive(Debug, Clone)] -pub enum StepMessage { -    SliderChanged(u8), -    LayoutChanged(Layout), -    SpacingChanged(u16), -    TextSizeChanged(u16), -    TextColorChanged(Color), -    LanguageSelected(Language), -    ImageWidthChanged(u16), -    InputChanged(String), -    ToggleSecureInput(bool), -    DebugToggled(bool), -    TogglerChanged(bool), -} - -impl<'a> Step { -    fn update(&mut self, msg: StepMessage, debug: &mut bool) { -        match msg { -            StepMessage::DebugToggled(value) => { -                if let Step::Debugger = self { -                    *debug = value; -                } -            } -            StepMessage::LanguageSelected(language) => { -                if let Step::Radio { selection } = self { -                    *selection = Some(language); -                } -            } -            StepMessage::SliderChanged(new_value) => { -                if let Step::Slider { value, .. } = self { -                    *value = new_value; -                } -            } -            StepMessage::TextSizeChanged(new_size) => { -                if let Step::Text { size, .. } = self { -                    *size = new_size; -                } -            } -            StepMessage::TextColorChanged(new_color) => { -                if let Step::Text { color, .. } = self { -                    *color = new_color; -                } -            } -            StepMessage::LayoutChanged(new_layout) => { -                if let Step::RowsAndColumns { layout, .. } = self { -                    *layout = new_layout; -                } -            } -            StepMessage::SpacingChanged(new_spacing) => { -                if let Step::RowsAndColumns { spacing, .. } = self { -                    *spacing = new_spacing; -                } -            } -            StepMessage::ImageWidthChanged(new_width) => { -                if let Step::Image { width, .. } = self { -                    *width = new_width; -                } -            } -            StepMessage::InputChanged(new_value) => { -                if let Step::TextInput { value, .. } = self { -                    *value = new_value; -                } -            } -            StepMessage::ToggleSecureInput(toggle) => { -                if let Step::TextInput { is_secure, .. } = self { -                    *is_secure = toggle; -                } -            } -            StepMessage::TogglerChanged(value) => { -                if let Step::Toggler { can_continue, .. } = self { -                    *can_continue = value; -                } -            } -        }; -    } - -    fn title(&self) -> &str { -        match self { -            Step::Welcome => "Welcome", -            Step::Radio { .. } => "Radio button", -            Step::Toggler { .. } => "Toggler", -            Step::Slider { .. } => "Slider", -            Step::Text { .. } => "Text", -            Step::Image { .. } => "Image", -            Step::RowsAndColumns { .. } => "Rows and columns", -            Step::Scrollable => "Scrollable", -            Step::TextInput { .. } => "Text input", -            Step::Debugger => "Debugger", -            Step::End => "End", -        } -    } - -    fn can_continue(&self) -> bool { -        match self { -            Step::Welcome => true, -            Step::Radio { selection } => *selection == Some(Language::Rust), -            Step::Toggler { can_continue } => *can_continue, -            Step::Slider { .. } => true, -            Step::Text { .. } => true, -            Step::Image { .. } => true, -            Step::RowsAndColumns { .. } => true, -            Step::Scrollable => true, -            Step::TextInput { value, .. } => !value.is_empty(), -            Step::Debugger => true, -            Step::End => false, -        } -    } - -    fn view(&self, debug: bool) -> Element<StepMessage> { -        match self { -            Step::Welcome => Self::welcome(), -            Step::Radio { selection } => Self::radio(*selection), -            Step::Toggler { can_continue } => Self::toggler(*can_continue), -            Step::Slider { value } => Self::slider(*value), -            Step::Text { size, color } => Self::text(*size, *color), -            Step::Image { width } => Self::image(*width), -            Step::RowsAndColumns { layout, spacing } => { -                Self::rows_and_columns(*layout, *spacing) -            } -            Step::Scrollable => Self::scrollable(), -            Step::TextInput { value, is_secure } => { -                Self::text_input(value, *is_secure) -            } -            Step::Debugger => Self::debugger(debug), -            Step::End => Self::end(), -        } -        .into() -    } - -    fn container(title: &str) -> Column<'a, StepMessage> { -        column().spacing(20).push(text(title).size(50)) -    } - -    fn welcome() -> Column<'a, StepMessage> { -        Self::container("Welcome!") -            .push( -                "This is a simple tour meant to showcase a bunch of widgets \ -                 that can be easily implemented on top of Iced.", -            ) -            .push( -                "Iced is a cross-platform GUI library for Rust focused on \ -                 simplicity and type-safety. It is heavily inspired by Elm.", -            ) -            .push( -                "It was originally born as part of Coffee, an opinionated \ -                 2D game engine for Rust.", -            ) -            .push( -                "On native platforms, Iced provides by default a renderer \ -                 built on top of wgpu, a graphics library supporting Vulkan, \ -                 Metal, DX11, and DX12.", -            ) -            .push( -                "Additionally, this tour can also run on WebAssembly thanks \ -                 to dodrio, an experimental VDOM library for Rust.", -            ) -            .push( -                "You will need to interact with the UI in order to reach the \ -                 end!", -            ) -    } - -    fn slider(value: u8) -> Column<'a, StepMessage> { -        Self::container("Slider") -            .push( -                "A slider allows you to smoothly select a value from a range \ -                 of values.", -            ) -            .push( -                "The following slider lets you choose an integer from \ -                 0 to 100:", -            ) -            .push(slider(0..=100, value, StepMessage::SliderChanged)) -            .push( -                text(value.to_string()) -                    .width(Length::Fill) -                    .horizontal_alignment(alignment::Horizontal::Center), -            ) -    } - -    fn rows_and_columns( -        layout: Layout, -        spacing: u16, -    ) -> Column<'a, StepMessage> { -        let row_radio = -            radio("Row", Layout::Row, Some(layout), StepMessage::LayoutChanged); - -        let column_radio = radio( -            "Column", -            Layout::Column, -            Some(layout), -            StepMessage::LayoutChanged, -        ); - -        let layout_section: Element<_> = match layout { -            Layout::Row => row() -                .spacing(spacing) -                .push(row_radio) -                .push(column_radio) -                .into(), -            Layout::Column => column() -                .spacing(spacing) -                .push(row_radio) -                .push(column_radio) -                .into(), -        }; - -        let spacing_section = column() -            .spacing(10) -            .push(slider(0..=80, spacing, StepMessage::SpacingChanged)) -            .push( -                text(format!("{} px", spacing)) -                    .width(Length::Fill) -                    .horizontal_alignment(alignment::Horizontal::Center), -            ); - -        Self::container("Rows and columns") -            .spacing(spacing) -            .push( -                "Iced uses a layout model based on flexbox to position UI \ -                 elements.", -            ) -            .push( -                "Rows and columns can be used to distribute content \ -                 horizontally or vertically, respectively.", -            ) -            .push(layout_section) -            .push("You can also easily change the spacing between elements:") -            .push(spacing_section) -    } - -    fn text(size: u16, color: Color) -> Column<'a, StepMessage> { -        let size_section = column() -            .padding(20) -            .spacing(20) -            .push("You can change its size:") -            .push(text(format!("This text is {} pixels", size)).size(size)) -            .push(Slider::new(10..=70, size, StepMessage::TextSizeChanged)); - -        let color_sliders = row() -            .spacing(10) -            .push(color_slider(color.r, move |r| Color { r, ..color })) -            .push(color_slider(color.g, move |g| Color { g, ..color })) -            .push(color_slider(color.b, move |b| Color { b, ..color })); - -        let color_section = column() -            .padding(20) -            .spacing(20) -            .push("And its color:") -            .push(text(format!("{:?}", color)).style(color)) -            .push(color_sliders); - -        Self::container("Text") -            .push( -                "Text is probably the most essential widget for your UI. \ -                 It will try to adapt to the dimensions of its container.", -            ) -            .push(size_section) -            .push(color_section) -    } - -    fn radio(selection: Option<Language>) -> Column<'a, StepMessage> { -        let question = column() -            .padding(20) -            .spacing(10) -            .push(text("Iced is written in...").size(24)) -            .push(Language::all().iter().cloned().fold( -                column().padding(10).spacing(20), -                |choices, language| { -                    choices.push(radio( -                        language, -                        language, -                        selection, -                        StepMessage::LanguageSelected, -                    )) -                }, -            )); - -        Self::container("Radio button") -            .push( -                "A radio button is normally used to represent a choice... \ -                 Surprise test!", -            ) -            .push(question) -            .push( -                "Iced works very well with iterators! The list above is \ -                 basically created by folding a column over the different \ -                 choices, creating a radio button for each one of them!", -            ) -    } - -    fn toggler(can_continue: bool) -> Column<'a, StepMessage> { -        Self::container("Toggler") -            .push("A toggler is mostly used to enable or disable something.") -            .push( -                Container::new(toggler( -                    "Toggle me to continue...".to_owned(), -                    can_continue, -                    StepMessage::TogglerChanged, -                )) -                .padding([0, 40]), -            ) -    } - -    fn image(width: u16) -> Column<'a, StepMessage> { -        Self::container("Image") -            .push("An image that tries to keep its aspect ratio.") -            .push(ferris(width)) -            .push(slider(100..=500, width, StepMessage::ImageWidthChanged)) -            .push( -                text(format!("Width: {} px", width)) -                    .width(Length::Fill) -                    .horizontal_alignment(alignment::Horizontal::Center), -            ) -    } - -    fn scrollable() -> Column<'a, StepMessage> { -        Self::container("Scrollable") -            .push( -                "Iced supports scrollable content. Try it out! Find the \ -                 button further below.", -            ) -            .push( -                text("Tip: You can use the scrollbar to scroll down faster!") -                    .size(16), -            ) -            .push(vertical_space(Length::Units(4096))) -            .push( -                text("You are halfway there!") -                    .width(Length::Fill) -                    .size(30) -                    .horizontal_alignment(alignment::Horizontal::Center), -            ) -            .push(vertical_space(Length::Units(4096))) -            .push(ferris(300)) -            .push( -                text("You made it!") -                    .width(Length::Fill) -                    .size(50) -                    .horizontal_alignment(alignment::Horizontal::Center), -            ) -    } - -    fn text_input(value: &str, is_secure: bool) -> Column<'a, StepMessage> { -        let text_input = text_input( -            "Type something to continue...", -            value, -            StepMessage::InputChanged, -        ) -        .padding(10) -        .size(30); - -        Self::container("Text input") -            .push("Use a text input to ask for different kinds of information.") -            .push(if is_secure { -                text_input.password() -            } else { -                text_input -            }) -            .push(checkbox( -                "Enable password mode", -                is_secure, -                StepMessage::ToggleSecureInput, -            )) -            .push( -                "A text input produces a message every time it changes. It is \ -                 very easy to keep track of its contents:", -            ) -            .push( -                text(if value.is_empty() { -                    "You have not typed anything yet..." -                } else { -                    value -                }) -                .width(Length::Fill) -                .horizontal_alignment(alignment::Horizontal::Center), -            ) -    } - -    fn debugger(debug: bool) -> Column<'a, StepMessage> { -        Self::container("Debugger") -            .push( -                "You can ask Iced to visually explain the layouting of the \ -                 different elements comprising your UI!", -            ) -            .push( -                "Give it a shot! Check the following checkbox to be able to \ -                 see element boundaries.", -            ) -            .push(if cfg!(target_arch = "wasm32") { -                Element::new( -                    text("Not available on web yet!") -                        .style(Color::from([0.7, 0.7, 0.7])) -                        .horizontal_alignment(alignment::Horizontal::Center), -                ) -            } else { -                checkbox("Explain layout", debug, StepMessage::DebugToggled) -                    .into() -            }) -            .push("Feel free to go back and take a look.") -    } - -    fn end() -> Column<'a, StepMessage> { -        Self::container("You reached the end!") -            .push("This tour will be updated as more features are added.") -            .push("Make sure to keep an eye on it!") -    } -} - -fn ferris<'a>(width: u16) -> Container<'a, StepMessage> { -    container( -        // This should go away once we unify resource loading on native -        // platforms -        if cfg!(target_arch = "wasm32") { -            image("tour/images/ferris.png") -        } else { -            image(format!( -                "{}/../../tour/images/ferris.png", -                env!("CARGO_MANIFEST_DIR") -            )) -        } -        .width(Length::Units(width)), -    ) -    .width(Length::Fill) -    .center_x() -} - -fn button<'a, Message: Clone>(label: &str) -> Button<'a, Message> { -    iced::pure::button( -        text(label).horizontal_alignment(alignment::Horizontal::Center), -    ) -    .padding(12) -    .width(Length::Units(100)) -} - -fn color_slider<'a>( -    component: f32, -    update: impl Fn(f32) -> Color + 'a, -) -> Slider<'a, f64, StepMessage, Renderer> { -    slider(0.0..=1.0, f64::from(component), move |c| { -        StepMessage::TextColorChanged(update(c as f32)) -    }) -    .step(0.01) -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum Language { -    Rust, -    Elm, -    Ruby, -    Haskell, -    C, -    Other, -} - -impl Language { -    fn all() -> [Language; 6] { -        [ -            Language::C, -            Language::Elm, -            Language::Ruby, -            Language::Haskell, -            Language::Rust, -            Language::Other, -        ] -    } -} - -impl From<Language> for String { -    fn from(language: Language) -> String { -        String::from(match language { -            Language::Rust => "Rust", -            Language::Elm => "Elm", -            Language::Ruby => "Ruby", -            Language::Haskell => "Haskell", -            Language::C => "C", -            Language::Other => "Other", -        }) -    } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum Layout { -    Row, -    Column, -}  | 
