diff options
Diffstat (limited to '')
| -rw-r--r-- | examples/ggez/main.rs | 101 | ||||
| -rw-r--r-- | examples/ggez/tour.rs | 545 | 
2 files changed, 618 insertions, 28 deletions
| diff --git a/examples/ggez/main.rs b/examples/ggez/main.rs index 3c02157a..8f1393ab 100644 --- a/examples/ggez/main.rs +++ b/examples/ggez/main.rs @@ -1,8 +1,10 @@  mod renderer; +mod tour;  mod widget;  use renderer::Renderer; -use widget::{button, Button, Checkbox, Column, Text}; +use tour::Tour; +use widget::Column;  use ggez;  use ggez::event; @@ -10,7 +12,13 @@ use ggez::graphics;  use ggez::input::mouse;  pub fn main() -> ggez::GameResult { -    let cb = ggez::ContextBuilder::new("iced", "ggez"); +    let cb = ggez::ContextBuilder::new("iced", "ggez").window_mode( +        ggez::conf::WindowMode { +            width: 1280.0, +            height: 1024.0, +            ..ggez::conf::WindowMode::default() +        }, +    );      let (ctx, event_loop) = &mut cb.build()?;      let state = &mut Game::new(ctx)?;      event::run(ctx, event_loop, state) @@ -20,16 +28,18 @@ struct Game {      spritesheet: graphics::Image,      runtime: iced::Runtime, -    button: button::State, +    tour: Tour,  }  impl Game {      fn new(context: &mut ggez::Context) -> ggez::GameResult<Game> { +        graphics::set_default_filter(context, graphics::FilterMode::Nearest); +          Ok(Game {              spritesheet: graphics::Image::new(context, "/ui.png").unwrap(),              runtime: iced::Runtime::new(), -            button: button::State::new(), +            tour: Tour::new(),          })      }  } @@ -39,6 +49,36 @@ impl event::EventHandler for Game {          Ok(())      } +    fn mouse_button_down_event( +        &mut self, +        _context: &mut ggez::Context, +        button: mouse::MouseButton, +        _x: f32, +        _y: f32, +    ) { +        self.runtime.on_event(iced::Event::Mouse( +            iced::input::mouse::Event::Input { +                state: iced::input::ButtonState::Pressed, +                button: iced::input::mouse::Button::Left, +            }, +        )); +    } + +    fn mouse_button_up_event( +        &mut self, +        _context: &mut ggez::Context, +        button: mouse::MouseButton, +        _x: f32, +        _y: f32, +    ) { +        self.runtime.on_event(iced::Event::Mouse( +            iced::input::mouse::Event::Input { +                state: iced::input::ButtonState::Released, +                button: iced::input::mouse::Button::Left, +            }, +        )); +    } +      fn mouse_motion_event(          &mut self,          _context: &mut ggez::Context, @@ -52,34 +92,40 @@ impl event::EventHandler for Game {          ));      } -    fn draw(&mut self, context: &mut ggez::Context) -> ggez::GameResult { -        graphics::clear(context, [0.1, 0.2, 0.3, 1.0].into()); - -        let screen = graphics::screen_coordinates(context); +    fn resize_event( +        &mut self, +        context: &mut ggez::Context, +        width: f32, +        height: f32, +    ) { +        graphics::set_screen_coordinates( +            context, +            graphics::Rect { +                x: 0.0, +                y: 0.0, +                w: width, +                h: height, +            }, +        ) +        .expect("Set screen coordinates"); +    } -        let cursor = { -            let hello = Text::new("Hello, iced!"); +    fn draw(&mut self, context: &mut ggez::Context) -> ggez::GameResult { +        graphics::clear(context, [0.3, 0.3, 0.6, 1.0].into()); -            let checkbox = -                Checkbox::new(true, "Check me!", Message::CheckboxToggled); +        self.tour.draw(context).expect("Draw tour"); -            let button = Button::new(&mut self.button, "Press me!") -                .width(200) -                .align_self(iced::Align::End); +        let screen = graphics::screen_coordinates(context); -            let widgets = Column::new() -                .max_width(600) -                .spacing(20) -                .push(hello) -                .push(checkbox) -                .push(button); +        let (messages, cursor) = { +            let layout = self.tour.layout();              let content = Column::new()                  .width(screen.w as u32)                  .height(screen.h as u32)                  .align_items(iced::Align::Center)                  .justify_content(iced::Justify::Center) -                .push(widgets); +                .push(layout);              let renderer =                  &mut Renderer::new(context, self.spritesheet.clone()); @@ -91,9 +137,13 @@ impl event::EventHandler for Game {              renderer.flush(); -            cursor +            (messages, cursor)          }; +        for message in messages { +            self.tour.react(message); +        } +          mouse::set_cursor_type(context, into_cursor_type(cursor));          graphics::present(context)?; @@ -101,11 +151,6 @@ impl event::EventHandler for Game {      }  } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -enum Message { -    CheckboxToggled(bool), -} -  fn into_cursor_type(cursor: iced::MouseCursor) -> mouse::MouseCursor {      match cursor {          iced::MouseCursor::OutOfBounds => mouse::MouseCursor::Default, 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, +} | 
