diff options
| author | 2019-10-03 00:01:45 +0200 | |
|---|---|---|
| committer | 2019-10-03 00:01:45 +0200 | |
| commit | e1b9d42bf1443ae4958aa9303255ef19c635debb (patch) | |
| tree | a7b7615dabc328a90300488ab8623740417277c8 /examples/tour/src/main.rs | |
| parent | 67d3fe67f312c4dfe9fe4af0f0cbc7cb23c30072 (diff) | |
| download | iced-e1b9d42bf1443ae4958aa9303255ef19c635debb.tar.gz iced-e1b9d42bf1443ae4958aa9303255ef19c635debb.tar.bz2 iced-e1b9d42bf1443ae4958aa9303255ef19c635debb.zip | |
Start `iced_winit` and `iced_wgpu`
Diffstat (limited to 'examples/tour/src/main.rs')
| -rw-r--r-- | examples/tour/src/main.rs | 712 | 
1 files changed, 547 insertions, 165 deletions
| diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs index a34d3298..5017041a 100644 --- a/examples/tour/src/main.rs +++ b/examples/tour/src/main.rs @@ -1,191 +1,573 @@ -use iced_tour::{iced_ggez, Tour}; - -use ggez; -use ggez::event; -use ggez::filesystem; -use ggez::graphics; -use ggez::input::mouse; - -pub fn main() -> ggez::GameResult { -    env_logger::init(); - -    let (context, event_loop) = { -        &mut ggez::ContextBuilder::new("iced", "ggez") -            .window_mode(ggez::conf::WindowMode { -                width: 1280.0, -                height: 1024.0, -                resizable: true, -                ..ggez::conf::WindowMode::default() -            }) -            .build()? -    }; - -    filesystem::mount( -        context, -        std::path::Path::new(env!("CARGO_MANIFEST_DIR")), -        true, -    ); - -    let state = &mut Game::new(context)?; - -    event::run(context, event_loop, state) -} +use iced::{ +    button, slider, text::HorizontalAlignment, Align, Button, Checkbox, Color, +    Column, Element, Image, Length, Radio, Row, Slider, Text, UserInterface, +}; -struct Game { -    spritesheet: graphics::Image, -    font: graphics::Font, -    images: iced_ggez::ImageCache, -    tour: Tour, +pub fn main() { +    let tour = Tour::new(); -    events: Vec<iced_native::Event>, -    cache: Option<iced_native::Cache>, +    tour.run();  } -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, "/resources/ui.png") -                .unwrap(), -            font: graphics::Font::new(context, "/resources/Roboto-Regular.ttf") -                .unwrap(), -            images: iced_ggez::ImageCache::new(), -            tour: Tour::new(), +pub struct Tour { +    steps: Steps, +    back_button: button::State, +    next_button: button::State, +    debug: bool, +} -            events: Vec::new(), -            cache: Some(iced_native::Cache::default()), -        }) +impl Tour { +    pub fn new() -> Tour { +        Tour { +            steps: Steps::new(), +            back_button: button::State::new(), +            next_button: button::State::new(), +            debug: false, +        }      }  } -impl event::EventHandler for Game { -    fn update(&mut self, _ctx: &mut ggez::Context) -> ggez::GameResult { -        Ok(()) -    } - -    fn mouse_button_down_event( -        &mut self, -        _context: &mut ggez::Context, -        _button: mouse::MouseButton, -        _x: f32, -        _y: f32, -    ) { -        self.events.push(iced_native::Event::Mouse( -            iced_native::input::mouse::Event::Input { -                state: iced_native::input::ButtonState::Pressed, -                button: iced_native::input::mouse::Button::Left, // TODO: Map `button` -            }, -        )); -    } - -    fn mouse_button_up_event( -        &mut self, -        _context: &mut ggez::Context, -        _button: mouse::MouseButton, -        _x: f32, -        _y: f32, -    ) { -        self.events.push(iced_native::Event::Mouse( -            iced_native::input::mouse::Event::Input { -                state: iced_native::input::ButtonState::Released, -                button: iced_native::input::mouse::Button::Left, // TODO: Map `button` -            }, -        )); -    } - -    fn mouse_motion_event( -        &mut self, -        _context: &mut ggez::Context, -        x: f32, -        y: f32, -        _dx: f32, -        _dy: f32, -    ) { -        self.events.push(iced_native::Event::Mouse( -            iced_native::input::mouse::Event::CursorMoved { x, y }, -        )); -    } - -    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"); -    } - -    fn draw(&mut self, context: &mut ggez::Context) -> ggez::GameResult { -        graphics::clear(context, graphics::WHITE); - -        let screen = graphics::screen_coordinates(context); - -        let (messages, cursor) = { -            let view = self.tour.view(); - -            let content = iced_ggez::Column::new() -                .width(iced_native::Length::Units(screen.w as u16)) -                .height(iced_native::Length::Units(screen.h as u16)) -                .padding(20) -                .align_items(iced_native::Align::Center) -                .justify_content(iced_native::Justify::Center) -                .push(view); - -            let renderer = &mut iced_ggez::Renderer::new( -                context, -                &mut self.images, -                self.spritesheet.clone(), -                self.font, +impl UserInterface for Tour { +    type Message = Message; + +    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(&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()); -            let mut ui = iced_native::UserInterface::build( -                content, -                self.cache.take().unwrap(), -                renderer, +        if steps.can_continue() { +            controls = controls.push( +                Button::new(next_button, "Next").on_press(Message::NextPressed),              ); +        } -            let messages = ui.update(self.events.drain(..)); -            let cursor = ui.draw(renderer); +        let element: Element<_> = Column::new() +            .max_width(Length::Units(500)) +            .spacing(20) +            .push(steps.view(self.debug).map(Message::StepMessage)) +            .push(controls) +            .into(); -            self.cache = Some(ui.into_cache()); +        if self.debug { +            element.explain(Color::BLACK) +        } else { +            element +        } +    } +} -            renderer.flush(); +#[derive(Debug, Clone, Copy)] +pub enum Message { +    BackPressed, +    NextPressed, +    StepMessage(StepMessage), +} -            (messages, cursor) -        }; +struct Steps { +    steps: Vec<Step>, +    current: usize, +} -        for message in messages { -            self.tour.update(message); +impl Steps { +    fn new() -> Steps { +        Steps { +            steps: vec![ +                Step::Welcome, +                Step::Slider { +                    state: slider::State::new(), +                    value: 50, +                }, +                Step::RowsAndColumns { +                    layout: Layout::Row, +                    spacing_slider: slider::State::new(), +                    spacing: 20, +                }, +                Step::Text { +                    size_slider: slider::State::new(), +                    size: 30, +                    color_sliders: [slider::State::new(); 3], +                    color: Color::BLACK, +                }, +                Step::Radio { selection: None }, +                Step::Image { +                    width: 300, +                    slider: slider::State::new(), +                }, +                Step::Debugger, +                Step::End, +            ], +            current: 0,          } +    } -        let cursor_type = into_cursor_type(cursor); +    fn update(&mut self, msg: StepMessage, debug: &mut bool) { +        self.steps[self.current].update(msg, debug); +    } + +    fn view(&mut self, debug: bool) -> Element<StepMessage> { +        self.steps[self.current].view(debug) +    } + +    fn advance(&mut self) { +        if self.can_continue() { +            self.current += 1; +        } +    } -        if mouse::cursor_type(context) != cursor_type { -            mouse::set_cursor_type(context, cursor_type); +    fn go_back(&mut self) { +        if self.has_previous() { +            self.current -= 1;          } +    } -        graphics::present(context)?; -        Ok(()) +    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, +    Slider { +        state: slider::State, +        value: u16, +    }, +    RowsAndColumns { +        layout: Layout, +        spacing_slider: slider::State, +        spacing: u16, +    }, +    Text { +        size_slider: slider::State, +        size: u16, +        color_sliders: [slider::State; 3], +        color: Color, +    }, +    Radio { +        selection: Option<Language>, +    }, +    Image { +        width: u16, +        slider: slider::State, +    }, +    Debugger, +    End, +} + +#[derive(Debug, Clone, Copy)] +pub enum StepMessage { +    SliderChanged(f32), +    LayoutChanged(Layout), +    SpacingChanged(f32), +    TextSizeChanged(f32), +    TextColorChanged(Color), +    LanguageSelected(Language), +    ImageWidthChanged(f32), +    DebugToggled(bool),  } -fn into_cursor_type(cursor: iced_native::MouseCursor) -> mouse::MouseCursor { -    match cursor { -        iced_native::MouseCursor::OutOfBounds => mouse::MouseCursor::Default, -        iced_native::MouseCursor::Idle => mouse::MouseCursor::Default, -        iced_native::MouseCursor::Pointer => mouse::MouseCursor::Hand, -        iced_native::MouseCursor::Working => mouse::MouseCursor::Progress, -        iced_native::MouseCursor::Grab => mouse::MouseCursor::Grab, -        iced_native::MouseCursor::Grabbing => mouse::MouseCursor::Grabbing, +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.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; +                } +            } +            StepMessage::ImageWidthChanged(new_width) => { +                if let Step::Image { width, .. } = self { +                    *width = new_width.round() as u16; +                } +            } +        }; +    } + +    fn can_continue(&self) -> bool { +        match self { +            Step::Welcome => true, +            Step::Radio { selection } => *selection == Some(Language::Rust), +            Step::Slider { .. } => true, +            Step::Text { .. } => true, +            Step::Image { .. } => true, +            Step::RowsAndColumns { .. } => true, +            Step::Debugger => true, +            Step::End => false, +        } +    } + +    fn view(&mut self, debug: bool) -> Element<StepMessage> { +        match self { +            Step::Welcome => Self::welcome().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::Image { width, slider } => Self::image(*width, slider).into(), +            Step::RowsAndColumns { +                layout, +                spacing_slider, +                spacing, +            } => { +                Self::rows_and_columns(*layout, spacing_slider, *spacing).into() +            } +            Step::Debugger => Self::debugger(debug).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 a simple tour meant to showcase a bunch of widgets that \ +                 can be easily implemented on top of Iced.", +            )) +            .push(Text::new( +                "Iced is a renderer-agnostic GUI library for Rust focused on \ +                 simplicity and type-safety. It is heavily inspired by Elm.", +            )) +            .push(Text::new( +                "It was originally born as part of Coffee, an opinionated \ +                 2D game engine for Rust.", +            )) +            .push(Text::new( +                "Iced does not provide a built-in renderer. This example runs \ +                 on WebAssembly using dodrio, an experimental VDOM library \ +                 for Rust.", +            )) +            .push(Text::new( +                "You will need to interact with the UI in order to reach the \ +                 end!", +            )) +    } + +    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 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..=80.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 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 {} pixels", size)).size(size), +            ) +            .push(Slider::new( +                size_slider, +                10.0..=70.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 radio(selection: Option<Language>) -> Column<'a, StepMessage> { +        let question = Column::new() +            .padding(20) +            .spacing(10) +            .push(Text::new("Iced is written in...").size(24)) +            .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... \ +                 Surprise test!", +            )) +            .push(question) +            .push(Text::new( +                "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 image( +        width: u16, +        slider: &'a mut slider::State, +    ) -> Column<'a, StepMessage> { +        Self::container("Image") +            .push(Text::new("An image that tries to keep its aspect ratio.")) +            .push( +                Image::new("resources/ferris.png") +                    .width(Length::Units(width)) +                    .align_self(Align::Center), +            ) +            .push(Slider::new( +                slider, +                100.0..=500.0, +                width as f32, +                StepMessage::ImageWidthChanged, +            )) +            .push( +                Text::new(&format!("Width: {} px", width.to_string())) +                    .horizontal_alignment(HorizontalAlignment::Center), +            ) +    } + +    fn debugger(debug: bool) -> Column<'a, StepMessage> { +        Self::container("Debugger") +            .push(Text::new( +                "You can ask Iced to visually explain the layouting of the \ +                 different elements comprising your UI!", +            )) +            .push(Text::new( +                "Give it a shot! Check the following checkbox to be able to \ +                 see element boundaries.", +            )) +            .push(Checkbox::new( +                debug, +                "Explain layout", +                StepMessage::DebugToggled, +            )) +            .push(Text::new("Feel free to go back and take a look.")) +    } + +    fn end() -> Column<'a, StepMessage> { +        Self::container("You reached the end!") +            .push(Text::new( +                "This tour will be updated 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,  } | 
