summaryrefslogtreecommitdiffstats
path: root/examples/game_of_life
diff options
context:
space:
mode:
Diffstat (limited to 'examples/game_of_life')
-rw-r--r--examples/game_of_life/Cargo.toml2
-rw-r--r--examples/game_of_life/README.md4
-rw-r--r--examples/game_of_life/src/main.rs132
-rw-r--r--examples/game_of_life/src/preset.rs13
-rw-r--r--examples/game_of_life/src/style.rs19
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()
}
}