diff options
Diffstat (limited to 'examples/game_of_life')
-rw-r--r-- | examples/game_of_life/Cargo.toml | 2 | ||||
-rw-r--r-- | examples/game_of_life/README.md | 4 | ||||
-rw-r--r-- | examples/game_of_life/src/main.rs | 132 | ||||
-rw-r--r-- | examples/game_of_life/src/preset.rs | 13 | ||||
-rw-r--r-- | examples/game_of_life/src/style.rs | 19 |
5 files changed, 111 insertions, 59 deletions
diff --git a/examples/game_of_life/Cargo.toml b/examples/game_of_life/Cargo.toml index b9bb7f2a..ffd2f19e 100644 --- a/examples/game_of_life/Cargo.toml +++ b/examples/game_of_life/Cargo.toml @@ -7,6 +7,6 @@ publish = false [dependencies] iced = { path = "../..", features = ["canvas", "tokio", "debug"] } -tokio = { version = "0.2", features = ["blocking"] } +tokio = { version = "1.0", features = ["sync"] } itertools = "0.9" rustc-hash = "1.1" diff --git a/examples/game_of_life/README.md b/examples/game_of_life/README.md index 1aeb1455..aa39201c 100644 --- a/examples/game_of_life/README.md +++ b/examples/game_of_life/README.md @@ -7,8 +7,8 @@ It runs a simulation in a background thread while allowing interaction with a `C The __[`main`]__ file contains the relevant code of the example. <div align="center"> - <a href="https://gfycat.com/briefaccurateaardvark"> - <img src="https://thumbs.gfycat.com/BriefAccurateAardvark-size_restricted.gif"> + <a href="https://gfycat.com/WhichPaltryChick"> + <img src="https://thumbs.gfycat.com/WhichPaltryChick-size_restricted.gif"> </a> </div> diff --git a/examples/game_of_life/src/main.rs b/examples/game_of_life/src/main.rs index 332d2d95..c3e16e8b 100644 --- a/examples/game_of_life/src/main.rs +++ b/examples/game_of_life/src/main.rs @@ -6,12 +6,14 @@ mod style; use grid::Grid; use iced::button::{self, Button}; use iced::executor; +use iced::menu::{self, Menu}; use iced::pick_list::{self, PickList}; use iced::slider::{self, Slider}; use iced::time; +use iced::window; use iced::{ - Align, Application, Checkbox, Column, Command, Container, Element, Length, - Row, Settings, Subscription, Text, + Align, Application, Checkbox, Clipboard, Column, Command, Container, + Element, Length, Row, Settings, Subscription, Text, }; use preset::Preset; use std::time::{Duration, Instant}; @@ -19,6 +21,10 @@ use std::time::{Duration, Instant}; pub fn main() -> iced::Result { GameOfLife::run(Settings { antialiasing: true, + window: window::Settings { + position: window::Position::Centered, + ..window::Settings::default() + }, ..Settings::default() }) } @@ -31,11 +37,12 @@ struct GameOfLife { queued_ticks: usize, speed: usize, next_speed: Option<usize>, + version: usize, } #[derive(Debug, Clone)] enum Message { - Grid(grid::Message), + Grid(grid::Message, usize), Tick(Instant), TogglePlayback, ToggleGrid(bool), @@ -64,10 +71,16 @@ impl Application for GameOfLife { String::from("Game of Life - Iced") } - fn update(&mut self, message: Message) -> Command<Message> { + fn update( + &mut self, + message: Message, + _clipboard: &mut Clipboard, + ) -> Command<Message> { match message { - Message::Grid(message) => { - self.grid.update(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); @@ -79,7 +92,11 @@ impl Application for GameOfLife { self.queued_ticks = 0; - return Command::perform(task, Message::Grid); + let version = self.version; + + return Command::perform(task, move |message| { + Message::Grid(message, version) + }); } } Message::TogglePlayback => { @@ -90,6 +107,7 @@ impl Application for GameOfLife { } Message::Clear => { self.grid.clear(); + self.version += 1; } Message::SpeedChanged(speed) => { if self.is_playing { @@ -100,6 +118,7 @@ impl Application for GameOfLife { } Message::PresetPicked(new_preset) => { self.grid = Grid::from_preset(new_preset); + self.version += 1; } } @@ -115,7 +134,15 @@ impl Application for GameOfLife { } } + fn menu(&self) -> Menu<Message> { + Menu::with_entries(vec![menu::Entry::dropdown( + "Presets", + Preset::menu().map(Message::PresetPicked), + )]) + } + fn view(&mut self) -> Element<Message> { + let version = self.version; let selected_speed = self.next_speed.unwrap_or(self.speed); let controls = self.controls.view( self.is_playing, @@ -125,7 +152,11 @@ impl Application for GameOfLife { ); let content = Column::new() - .push(self.grid.view().map(Message::Grid)) + .push( + self.grid + .view() + .map(move |message| Message::Grid(message, version)), + ) .push(controls); Container::new(content) @@ -139,9 +170,8 @@ impl Application for GameOfLife { mod grid { use crate::Preset; use iced::{ - canvas::{ - self, Cache, Canvas, Cursor, Event, Frame, Geometry, Path, Text, - }, + canvas::event::{self, Event}, + canvas::{self, Cache, Canvas, Cursor, Frame, Geometry, Path, Text}, mouse, Color, Element, HorizontalAlignment, Length, Point, Rectangle, Size, Vector, VerticalAlignment, }; @@ -161,7 +191,6 @@ mod grid { show_lines: bool, last_tick_duration: Duration, last_queued_ticks: usize, - version: usize, } #[derive(Debug, Clone)] @@ -171,7 +200,6 @@ mod grid { Ticked { result: Result<Life, TickError>, tick_duration: Duration, - version: usize, }, } @@ -208,7 +236,6 @@ mod grid { show_lines: true, last_tick_duration: Duration::default(), last_queued_ticks: 0, - version: 0, } } @@ -216,7 +243,6 @@ mod grid { &mut self, amount: usize, ) -> Option<impl Future<Output = Message>> { - let version = self.version; let tick = self.state.tick(amount)?; self.last_queued_ticks = amount; @@ -228,7 +254,6 @@ mod grid { Message::Ticked { result, - version, tick_duration, } }) @@ -250,13 +275,11 @@ mod grid { } Message::Ticked { result: Ok(life), - version, tick_duration, - } if version == self.version => { + } => { self.state.update(life); self.life_cache.clear(); - self.version += 1; self.last_tick_duration = tick_duration; } Message::Ticked { @@ -264,7 +287,6 @@ mod grid { } => { dbg!(error); } - Message::Ticked { .. } => {} } } @@ -278,7 +300,6 @@ mod grid { pub fn clear(&mut self) { self.state = State::default(); self.preset = Preset::Custom; - self.version += 1; self.life_cache.clear(); } @@ -323,12 +344,18 @@ mod grid { event: Event, bounds: Rectangle, cursor: Cursor, - ) -> Option<Message> { + ) -> (event::Status, Option<Message>) { if let Event::Mouse(mouse::Event::ButtonReleased(_)) = event { self.interaction = Interaction::None; } - let cursor_position = cursor.position_in(&bounds)?; + 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); @@ -340,28 +367,32 @@ mod grid { match event { Event::Mouse(mouse_event) => match mouse_event { - mouse::Event::ButtonPressed(button) => match button { - mouse::Button::Left => { - self.interaction = if is_populated { - Interaction::Erasing - } else { - Interaction::Drawing - }; - - populate.or(unpopulate) - } - mouse::Button::Right => { - self.interaction = Interaction::Panning { - translation: self.translation, - start: cursor_position, - }; + mouse::Event::ButtonPressed(button) => { + let message = match button { + mouse::Button::Left => { + self.interaction = if is_populated { + Interaction::Erasing + } else { + Interaction::Drawing + }; + + populate.or(unpopulate) + } + mouse::Button::Right => { + self.interaction = Interaction::Panning { + translation: self.translation, + start: cursor_position, + }; - None - } - _ => None, - }, + None + } + _ => None, + }; + + (event::Status::Captured, message) + } mouse::Event::CursorMoved { .. } => { - match self.interaction { + let message = match self.interaction { Interaction::Drawing => populate, Interaction::Erasing => unpopulate, Interaction::Panning { translation, start } => { @@ -375,7 +406,14 @@ mod grid { None } _ => None, - } + }; + + let event_status = match self.interaction { + Interaction::None => event::Status::Ignored, + _ => event::Status::Captured, + }; + + (event_status, message) } mouse::Event::WheelScrolled { delta } => match delta { mouse::ScrollDelta::Lines { y, .. } @@ -408,12 +446,12 @@ mod grid { self.grid_cache.clear(); } - None + (event::Status::Captured, None) } }, - _ => None, + _ => (event::Status::Ignored, None), }, - _ => None, + _ => (event::Status::Ignored, None), } } diff --git a/examples/game_of_life/src/preset.rs b/examples/game_of_life/src/preset.rs index 05157b6a..1c199a72 100644 --- a/examples/game_of_life/src/preset.rs +++ b/examples/game_of_life/src/preset.rs @@ -1,3 +1,5 @@ +use iced::menu::{self, Menu}; + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Preset { Custom, @@ -26,6 +28,17 @@ pub static ALL: &[Preset] = &[ ]; impl Preset { + pub fn menu() -> Menu<Self> { + Menu::with_entries( + ALL.iter() + .copied() + .map(|preset| { + menu::Entry::item(preset.to_string(), None, preset) + }) + .collect(), + ) + } + pub fn life(self) -> Vec<(isize, isize)> { #[rustfmt::skip] let cells = match self { diff --git a/examples/game_of_life/src/style.rs b/examples/game_of_life/src/style.rs index 308ce43c..be9a0e96 100644 --- a/examples/game_of_life/src/style.rs +++ b/examples/game_of_life/src/style.rs @@ -44,7 +44,7 @@ impl button::StyleSheet for Button { fn active(&self) -> button::Style { button::Style { background: Some(Background::Color(ACTIVE)), - border_radius: 3, + border_radius: 3.0, text_color: Color::WHITE, ..button::Style::default() } @@ -60,7 +60,7 @@ impl button::StyleSheet for Button { fn pressed(&self) -> button::Style { button::Style { - border_width: 1, + border_width: 1.0, border_color: Color::WHITE, ..self.hovered() } @@ -73,7 +73,7 @@ impl button::StyleSheet for Clear { fn active(&self) -> button::Style { button::Style { background: Some(Background::Color(DESTRUCTIVE)), - border_radius: 3, + border_radius: 3.0, text_color: Color::WHITE, ..button::Style::default() } @@ -92,7 +92,7 @@ impl button::StyleSheet for Clear { fn pressed(&self) -> button::Style { button::Style { - border_width: 1, + border_width: 1.0, border_color: Color::WHITE, ..self.hovered() } @@ -106,9 +106,9 @@ impl slider::StyleSheet for Slider { slider::Style { rail_colors: (ACTIVE, Color { a: 0.1, ..ACTIVE }), handle: slider::Handle { - shape: slider::HandleShape::Circle { radius: 9 }, + shape: slider::HandleShape::Circle { radius: 9.0 }, color: ACTIVE, - border_width: 0, + border_width: 0.0, border_color: Color::TRANSPARENT, }, } @@ -146,7 +146,7 @@ impl pick_list::StyleSheet for PickList { pick_list::Menu { text_color: Color::WHITE, background: BACKGROUND.into(), - border_width: 1, + border_width: 1.0, border_color: Color { a: 0.7, ..Color::BLACK @@ -164,13 +164,14 @@ impl pick_list::StyleSheet for PickList { pick_list::Style { text_color: Color::WHITE, background: BACKGROUND.into(), - border_width: 1, + border_width: 1.0, border_color: Color { a: 0.6, ..Color::BLACK }, - border_radius: 2, + border_radius: 2.0, icon_size: 0.5, + ..pick_list::Style::default() } } |