diff options
| author | 2019-07-23 12:07:32 +0200 | |
|---|---|---|
| committer | 2019-07-23 12:07:32 +0200 | |
| commit | 666e6761bcabf454d0a5d8c35a6fefc1e49a16aa (patch) | |
| tree | b426ed1e5c258bdcb9cf16cf0c3f3d20364b07a3 /examples/ggez/tour.rs | |
| parent | 8f6ea4bdc99ef5960316d2230676495f2d90c30e (diff) | |
| download | iced-666e6761bcabf454d0a5d8c35a6fefc1e49a16aa.tar.gz iced-666e6761bcabf454d0a5d8c35a6fefc1e49a16aa.tar.bz2 iced-666e6761bcabf454d0a5d8c35a6fefc1e49a16aa.zip  | |
Show Coffee's UI tour in `ggez` example
Diffstat (limited to '')
| -rw-r--r-- | examples/ggez/tour.rs | 545 | 
1 files changed, 545 insertions, 0 deletions
diff --git a/examples/ggez/tour.rs b/examples/ggez/tour.rs new file mode 100644 index 00000000..3808fdda --- /dev/null +++ b/examples/ggez/tour.rs @@ -0,0 +1,545 @@ +use super::widget::{ +    button, slider, Button, Checkbox, Column, Element, Radio, Row, Slider, Text, +}; + +use ggez::graphics::{Color, BLACK}; +use iced::{text::HorizontalAlignment, Align}; + +pub struct Tour { +    steps: Steps, +    back_button: button::State, +    next_button: button::State, +} + +impl Tour { +    pub fn new() -> Tour { +        Tour { +            steps: Steps::new(), +            back_button: button::State::new(), +            next_button: button::State::new(), +        } +    } + +    pub fn draw(&mut self, context: &mut ggez::Context) -> ggez::GameResult { +        Ok(()) +    } + +    pub fn react(&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); +            } +        } +    } + +    pub fn layout(&mut self) -> Element<Message> { +        let Tour { +            steps, +            back_button, +            next_button, +        } = self; + +        let mut controls = Row::new(); + +        if steps.has_previous() { +            controls = controls.push( +                Button::new(back_button, "Back") +                    .on_press(Message::BackPressed) +                    .class(button::Class::Secondary), +            ); +        } + +        controls = controls.push(Column::new()); + +        if steps.can_continue() { +            controls = controls.push( +                Button::new(next_button, "Next").on_press(Message::NextPressed), +            ); +        } + +        Column::new() +            .max_width(500) +            .spacing(20) +            .push(steps.layout().map(Message::StepMessage)) +            .push(controls) +            .into() +    } +} + +#[derive(Debug, Clone, Copy)] +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::Buttons { +                    primary: button::State::new(), +                    secondary: button::State::new(), +                    positive: button::State::new(), +                }, +                Step::Checkbox { is_checked: false }, +                Step::Radio { selection: None }, +                Step::Slider { +                    state: slider::State::new(), +                    value: 50, +                }, +                Step::Text { +                    size_slider: slider::State::new(), +                    size: 30, +                    color_sliders: [slider::State::new(); 3], +                    color: BLACK, +                }, +                Step::RowsAndColumns { +                    layout: Layout::Row, +                    spacing_slider: slider::State::new(), +                    spacing: 20, +                }, +                Step::End, +            ], +            current: 0, +        } +    } + +    fn update(&mut self, msg: StepMessage) { +        self.steps[self.current].update(msg); +    } + +    fn layout(&mut self) -> Element<StepMessage> { +        self.steps[self.current].layout() +    } + +    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() +    } +} + +enum Step { +    Welcome, +    Buttons { +        primary: button::State, +        secondary: button::State, +        positive: button::State, +    }, +    Checkbox { +        is_checked: bool, +    }, +    Radio { +        selection: Option<Language>, +    }, +    Slider { +        state: slider::State, +        value: u16, +    }, +    Text { +        size_slider: slider::State, +        size: u16, +        color_sliders: [slider::State; 3], +        color: Color, +    }, +    RowsAndColumns { +        layout: Layout, +        spacing_slider: slider::State, +        spacing: u16, +    }, +    End, +} + +#[derive(Debug, Clone, Copy)] +pub enum StepMessage { +    CheckboxToggled(bool), +    LanguageSelected(Language), +    SliderChanged(f32), +    TextSizeChanged(f32), +    TextColorChanged(Color), +    LayoutChanged(Layout), +    SpacingChanged(f32), +} + +impl<'a> Step { +    fn update(&mut self, msg: StepMessage) { +        match msg { +            StepMessage::CheckboxToggled(value) => { +                if let Step::Checkbox { is_checked } = self { +                    *is_checked = 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.round() as u16; +                } +            } +            StepMessage::TextSizeChanged(new_size) => { +                if let Step::Text { size, .. } = self { +                    *size = new_size.round() as u16; +                } +            } +            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.round() as u16; +                } +            } +        }; +    } + +    fn can_continue(&self) -> bool { +        match self { +            Step::Welcome => true, +            Step::Buttons { .. } => true, +            Step::Checkbox { is_checked } => *is_checked, +            Step::Radio { selection } => *selection == Some(Language::Rust), +            Step::Slider { .. } => true, +            Step::Text { .. } => true, +            Step::RowsAndColumns { .. } => true, +            Step::End => false, +        } +    } + +    fn layout(&mut self) -> Element<StepMessage> { +        match self { +            Step::Welcome => Self::welcome().into(), +            Step::Buttons { +                primary, +                secondary, +                positive, +            } => Self::buttons(primary, secondary, positive).into(), +            Step::Checkbox { is_checked } => Self::checkbox(*is_checked).into(), +            Step::Radio { selection } => Self::radio(*selection).into(), +            Step::Slider { state, value } => Self::slider(state, *value).into(), +            Step::Text { +                size_slider, +                size, +                color_sliders, +                color, +            } => Self::text(size_slider, *size, color_sliders, *color).into(), +            Step::RowsAndColumns { +                layout, +                spacing_slider, +                spacing, +            } => { +                Self::rows_and_columns(*layout, spacing_slider, *spacing).into() +            } +            Step::End => Self::end().into(), +        } +    } + +    fn container(title: &str) -> Column<'a, StepMessage> { +        Column::new() +            .spacing(20) +            .align_items(Align::Stretch) +            .push(Text::new(title).size(50)) +    } + +    fn welcome() -> Column<'a, StepMessage> { +        Self::container("Welcome!") +            .push(Text::new( +                "This is a tour that introduces some of the features and \ +                 concepts related with UI development in Iced.", +            )) +            .push(Text::new( +                "You will need to interact with the UI in order to reach the \ +                 end!", +            )) +    } + +    fn buttons( +        primary: &'a mut button::State, +        secondary: &'a mut button::State, +        positive: &'a mut button::State, +    ) -> Column<'a, StepMessage> { +        Self::container("Button") +            .push(Text::new("A button can fire actions when clicked.")) +            .push(Text::new( +                "As of now, there are 3 different types of buttons: \ +                 primary, secondary, and positive.", +            )) +            .push(Button::new(primary, "Primary")) +            .push( +                Button::new(secondary, "Secondary") +                    .class(button::Class::Secondary), +            ) +            .push( +                Button::new(positive, "Positive") +                    .class(button::Class::Positive), +            ) +            .push(Text::new( +                "Additional types will be added in the near future! Choose \ +                 each type smartly depending on the situation.", +            )) +    } + +    fn checkbox(is_checked: bool) -> Column<'a, StepMessage> { +        Self::container("Checkbox") +            .push(Text::new( +                "A box that can be checked. Useful to build toggle controls.", +            )) +            .push(Checkbox::new( +                is_checked, +                "Show \"Next\" button", +                StepMessage::CheckboxToggled, +            )) +            .push(Text::new( +                "A checkbox always has a label, and both the checkbox and its \ +                 label can be clicked to toggle it.", +            )) +    } + +    fn radio(selection: Option<Language>) -> Column<'a, StepMessage> { +        let question = Column::new() +            .padding(20) +            .spacing(10) +            .push(Text::new("Iced is written in...")) +            .push(Language::all().iter().cloned().fold( +                Column::new().padding(10).spacing(20), +                |choices, language| { +                    choices.push(Radio::new( +                        language, +                        language.into(), +                        selection, +                        StepMessage::LanguageSelected, +                    )) +                }, +            )); + +        Self::container("Radio button") +            .push(Text::new( +                "A radio button is normally used to represent a choice. Like \ +                 a checkbox, it always has a label.", +            )) +            .push(question) +    } + +    fn slider( +        state: &'a mut slider::State, +        value: u16, +    ) -> Column<'a, StepMessage> { +        Self::container("Slider") +            .push(Text::new( +                "A slider allows you to smoothly select a value from a range \ +                 of values.", +            )) +            .push(Text::new( +                "The following slider lets you choose an integer from \ +                 0 to 100:", +            )) +            .push(Slider::new( +                state, +                0.0..=100.0, +                value as f32, +                StepMessage::SliderChanged, +            )) +            .push( +                Text::new(&value.to_string()) +                    .horizontal_alignment(HorizontalAlignment::Center), +            ) +    } + +    fn text( +        size_slider: &'a mut slider::State, +        size: u16, +        color_sliders: &'a mut [slider::State; 3], +        color: Color, +    ) -> Column<'a, StepMessage> { +        let size_section = Column::new() +            .padding(20) +            .spacing(20) +            .push(Text::new("You can change its size:")) +            .push( +                Text::new(&format!("This text is {} points", size)).size(size), +            ) +            .push(Slider::new( +                size_slider, +                10.0..=50.0, +                size as f32, +                StepMessage::TextSizeChanged, +            )); + +        let [red, green, blue] = color_sliders; +        let color_section = Column::new() +            .padding(20) +            .spacing(20) +            .push(Text::new("And its color:")) +            .push(Text::new(&format!("{:?}", color)).color(color)) +            .push( +                Row::new() +                    .spacing(10) +                    .push(Slider::new(red, 0.0..=1.0, color.r, move |r| { +                        StepMessage::TextColorChanged(Color { r, ..color }) +                    })) +                    .push(Slider::new(green, 0.0..=1.0, color.g, move |g| { +                        StepMessage::TextColorChanged(Color { g, ..color }) +                    })) +                    .push(Slider::new(blue, 0.0..=1.0, color.b, move |b| { +                        StepMessage::TextColorChanged(Color { b, ..color }) +                    })), +            ); + +        Self::container("Text") +            .push(Text::new( +                "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 rows_and_columns( +        layout: Layout, +        spacing_slider: &'a mut slider::State, +        spacing: u16, +    ) -> Column<'a, StepMessage> { +        let row_radio = Radio::new( +            Layout::Row, +            "Row", +            Some(layout), +            StepMessage::LayoutChanged, +        ); + +        let column_radio = Radio::new( +            Layout::Column, +            "Column", +            Some(layout), +            StepMessage::LayoutChanged, +        ); + +        let layout_section: Element<_> = match layout { +            Layout::Row => Row::new() +                .spacing(spacing) +                .push(row_radio) +                .push(column_radio) +                .into(), +            Layout::Column => Column::new() +                .spacing(spacing) +                .push(row_radio) +                .push(column_radio) +                .into(), +        }; + +        let spacing_section = Column::new() +            .spacing(10) +            .push(Slider::new( +                spacing_slider, +                0.0..=100.0, +                spacing as f32, +                StepMessage::SpacingChanged, +            )) +            .push( +                Text::new(&format!("{} px", spacing)) +                    .horizontal_alignment(HorizontalAlignment::Center), +            ); + +        Self::container("Rows and columns") +            .spacing(spacing) +            .push(Text::new( +                "Iced uses a layout model based on flexbox to position UI \ +                 elements.", +            )) +            .push(Text::new( +                "Rows and columns can be used to distribute content \ +                 horizontally or vertically, respectively.", +            )) +            .push(layout_section) +            .push(Text::new( +                "You can also easily change the spacing between elements:", +            )) +            .push(spacing_section) +    } + +    fn end() -> Column<'a, StepMessage> { +        Self::container("You reached the end!") +            .push(Text::new( +                "This tour will be extended as more features are added.", +            )) +            .push(Text::new("Make sure to keep an eye on it!")) +    } +} + +#[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 &str { +    fn from(language: Language) -> &'static str { +        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, +}  | 
