diff options
Diffstat (limited to 'examples')
-rw-r--r-- | examples/README.md | 1 | ||||
-rw-r--r-- | examples/game_of_life/src/main.rs | 84 | ||||
-rw-r--r-- | examples/game_of_life/src/preset.rs | 142 | ||||
-rw-r--r-- | examples/game_of_life/src/style.rs | 56 | ||||
-rw-r--r-- | examples/pick_list/Cargo.toml | 9 | ||||
-rw-r--r-- | examples/pick_list/README.md | 18 | ||||
-rw-r--r-- | examples/pick_list/src/main.rs | 113 | ||||
-rw-r--r-- | examples/todos/src/main.rs | 4 |
8 files changed, 412 insertions, 15 deletions
diff --git a/examples/README.md b/examples/README.md index 8e1b781f..34a916a1 100644 --- a/examples/README.md +++ b/examples/README.md @@ -100,6 +100,7 @@ A bunch of simpler examples exist: - [`geometry`](geometry), a custom widget showcasing how to draw geometry with the `Mesh2D` primitive in [`iced_wgpu`](../wgpu). - [`integration`](integration), a demonstration of how to integrate Iced in an existing graphical application. - [`pane_grid`](pane_grid), a grid of panes that can be split, resized, and reorganized. +- [`pick_list`](pick_list), a dropdown list of selectable options. - [`pokedex`](pokedex), an application that displays a random Pokédex entry (sprite included!) by using the [PokéAPI]. - [`progress_bar`](progress_bar), a simple progress bar that can be filled by using a slider. - [`solar_system`](solar_system), an animated solar system drawn using the `Canvas` widget and showcasing how to compose different transforms. diff --git a/examples/game_of_life/src/main.rs b/examples/game_of_life/src/main.rs index 080d55c0..27d4eec1 100644 --- a/examples/game_of_life/src/main.rs +++ b/examples/game_of_life/src/main.rs @@ -1,15 +1,19 @@ //! 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; mod style; use grid::Grid; +use iced::button::{self, Button}; +use iced::executor; +use iced::pick_list::{self, PickList}; +use iced::slider::{self, Slider}; +use iced::time; use iced::{ - button::{self, Button}, - executor, - slider::{self, Slider}, - time, Align, Application, Checkbox, Column, Command, Container, Element, - Length, Row, Settings, Subscription, Text, + Align, Application, Checkbox, Column, Command, Container, Element, Length, + Row, Settings, Subscription, Text, }; +use preset::Preset; use std::time::{Duration, Instant}; pub fn main() { @@ -38,6 +42,7 @@ enum Message { Next, Clear, SpeedChanged(f32), + PresetPicked(Preset), } impl Application for GameOfLife { @@ -48,7 +53,7 @@ impl Application for GameOfLife { fn new(_flags: ()) -> (Self, Command<Message>) { ( Self { - speed: 1, + speed: 5, ..Self::default() }, Command::none(), @@ -93,6 +98,9 @@ impl Application for GameOfLife { self.speed = speed.round() as usize; } } + Message::PresetPicked(new_preset) => { + self.grid = Grid::from_preset(new_preset); + } } Command::none() @@ -113,6 +121,7 @@ impl Application for GameOfLife { self.is_playing, self.grid.are_lines_visible(), selected_speed, + self.grid.preset(), ); let content = Column::new() @@ -128,6 +137,7 @@ impl Application for GameOfLife { } mod grid { + use crate::Preset; use iced::{ canvas::{ self, Cache, Canvas, Cursor, Event, Frame, Geometry, Path, Text, @@ -142,6 +152,7 @@ mod grid { pub struct Grid { state: State, + preset: Preset, interaction: Interaction, life_cache: Cache, grid_cache: Cache, @@ -171,8 +182,24 @@ mod grid { 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::default(), + state: State::with_life( + preset + .life() + .into_iter() + .map(|(i, j)| Cell { i, j }) + .collect(), + ), + preset, interaction: Interaction::None, life_cache: Cache::default(), grid_cache: Cache::default(), @@ -184,11 +211,6 @@ mod grid { version: 0, } } - } - - impl Grid { - const MIN_SCALING: f32 = 0.1; - const MAX_SCALING: f32 = 2.0; pub fn tick( &mut self, @@ -217,10 +239,14 @@ mod grid { 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::Ticked { result: Ok(life), @@ -230,6 +256,7 @@ mod grid { self.state.update(life); self.life_cache.clear(); + self.version += 1; self.last_tick_duration = tick_duration; } Message::Ticked { @@ -250,11 +277,16 @@ mod grid { pub fn clear(&mut self) { self.state = State::default(); + self.preset = Preset::Custom; self.version += 1; self.life_cache.clear(); } + pub fn preset(&self) -> Preset { + self.preset + } + pub fn toggle_lines(&mut self, enabled: bool) { self.show_lines = enabled; } @@ -533,6 +565,13 @@ mod grid { } impl State { + pub fn with_life(life: Life) -> Self { + Self { + life, + ..Self::default() + } + } + fn cell_count(&self) -> usize { self.life.len() + self.births.len() } @@ -647,6 +686,14 @@ mod grid { } } + 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") @@ -741,6 +788,7 @@ struct Controls { next_button: button::State, clear_button: button::State, speed_slider: slider::State, + preset_list: pick_list::State, } impl Controls { @@ -749,6 +797,7 @@ impl Controls { is_playing: bool, is_grid_enabled: bool, speed: usize, + preset: Preset, ) -> Element<'a, Message> { let playback_controls = Row::new() .spacing(10) @@ -794,6 +843,17 @@ impl Controls { .text_size(16), ) .push( + PickList::new( + &mut self.preset_list, + preset::ALL, + Some(preset), + Message::PresetPicked, + ) + .padding(8) + .text_size(16) + .style(style::PickList), + ) + .push( Button::new(&mut self.clear_button, Text::new("Clear")) .on_press(Message::Clear) .style(style::Clear), diff --git a/examples/game_of_life/src/preset.rs b/examples/game_of_life/src/preset.rs new file mode 100644 index 00000000..05157b6a --- /dev/null +++ b/examples/game_of_life/src/preset.rs @@ -0,0 +1,142 @@ +#[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/game_of_life/src/style.rs b/examples/game_of_life/src/style.rs index d59569f2..308ce43c 100644 --- a/examples/game_of_life/src/style.rs +++ b/examples/game_of_life/src/style.rs @@ -1,4 +1,4 @@ -use iced::{button, container, slider, Background, Color}; +use iced::{button, container, pick_list, slider, Background, Color}; const ACTIVE: Color = Color::from_rgb( 0x72 as f32 / 255.0, @@ -18,6 +18,12 @@ const HOVERED: Color = Color::from_rgb( 0xC4 as f32 / 255.0, ); +const BACKGROUND: Color = Color::from_rgb( + 0x2F as f32 / 255.0, + 0x31 as f32 / 255.0, + 0x36 as f32 / 255.0, +); + pub struct Container; impl container::StyleSheet for Container { @@ -132,3 +138,51 @@ impl slider::StyleSheet for Slider { } } } + +pub struct PickList; + +impl pick_list::StyleSheet for PickList { + fn menu(&self) -> pick_list::Menu { + pick_list::Menu { + text_color: Color::WHITE, + background: BACKGROUND.into(), + border_width: 1, + border_color: Color { + a: 0.7, + ..Color::BLACK + }, + selected_background: Color { + a: 0.5, + ..Color::BLACK + } + .into(), + selected_text_color: Color::WHITE, + } + } + + fn active(&self) -> pick_list::Style { + pick_list::Style { + text_color: Color::WHITE, + background: BACKGROUND.into(), + border_width: 1, + border_color: Color { + a: 0.6, + ..Color::BLACK + }, + border_radius: 2, + icon_size: 0.5, + } + } + + fn hovered(&self) -> pick_list::Style { + let active = self.active(); + + pick_list::Style { + border_color: Color { + a: 0.9, + ..Color::BLACK + }, + ..active + } + } +} diff --git a/examples/pick_list/Cargo.toml b/examples/pick_list/Cargo.toml new file mode 100644 index 00000000..a87d7217 --- /dev/null +++ b/examples/pick_list/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "pick_list" +version = "0.1.0" +authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"] +edition = "2018" +publish = false + +[dependencies] +iced = { path = "../..", features = ["debug"] } diff --git a/examples/pick_list/README.md b/examples/pick_list/README.md new file mode 100644 index 00000000..6dc80bf4 --- /dev/null +++ b/examples/pick_list/README.md @@ -0,0 +1,18 @@ +## Pick-list + +A dropdown list of selectable options. + +It displays and positions an overlay based on the window position of the widget. + +The __[`main`]__ file contains all the code of the example. + +<div align="center"> + <img src="https://user-images.githubusercontent.com/518289/87125075-2c232e80-c28a-11ea-95c2-769c610b8843.gif"> +</div> + +You can run it with `cargo run`: +``` +cargo run --package pick_list +``` + +[`main`]: src/main.rs diff --git a/examples/pick_list/src/main.rs b/examples/pick_list/src/main.rs new file mode 100644 index 00000000..66ed6c6f --- /dev/null +++ b/examples/pick_list/src/main.rs @@ -0,0 +1,113 @@ +use iced::{ + pick_list, scrollable, Align, Container, Element, Length, PickList, + Sandbox, Scrollable, Settings, Space, Text, +}; + +pub fn main() { + Example::run(Settings::default()) +} + +#[derive(Default)] +struct Example { + scroll: scrollable::State, + pick_list: pick_list::State, + selected_language: 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 = language; + } + } + } + + fn view(&mut self) -> Element<Message> { + let pick_list = PickList::new( + &mut self.pick_list, + &Language::ALL[..], + Some(self.selected_language), + Message::LanguageSelected, + ); + + let mut content = Scrollable::new(&mut self.scroll) + .width(Length::Fill) + .align_items(Align::Center) + .spacing(10) + .push(Space::with_height(Length::Units(600))) + .push(Text::new("Which is your favorite language?")) + .push(pick_list); + + content = content.push(Space::with_height(Length::Units(600))); + + Container::new(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/todos/src/main.rs b/examples/todos/src/main.rs index c9cbcc69..5713a6f2 100644 --- a/examples/todos/src/main.rs +++ b/examples/todos/src/main.rs @@ -425,7 +425,7 @@ impl Filter { } } -fn loading_message() -> Element<'static, Message> { +fn loading_message<'a>() -> Element<'a, Message> { Container::new( Text::new("Loading...") .horizontal_alignment(HorizontalAlignment::Center) @@ -437,7 +437,7 @@ fn loading_message() -> Element<'static, Message> { .into() } -fn empty_message(message: &str) -> Element<'static, Message> { +fn empty_message<'a>(message: &str) -> Element<'a, Message> { Container::new( Text::new(message) .width(Length::Fill) |