summaryrefslogtreecommitdiffstats
path: root/examples/game_of_life
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón Jiménez <hector0193@gmail.com>2022-07-27 06:49:20 +0200
committerLibravatar Héctor Ramón Jiménez <hector0193@gmail.com>2022-07-27 06:49:20 +0200
commitff2519b1d43d481987351a83b6dd7237524c21f0 (patch)
tree5731eeb7eb1247d4a8951de0d5bc5d8102640559 /examples/game_of_life
parentc44267b85f7aaa2997e3caf1323b837d95818c22 (diff)
downloadiced-ff2519b1d43d481987351a83b6dd7237524c21f0.tar.gz
iced-ff2519b1d43d481987351a83b6dd7237524c21f0.tar.bz2
iced-ff2519b1d43d481987351a83b6dd7237524c21f0.zip
Replace stateful widgets with new `iced_pure` API
Diffstat (limited to 'examples/game_of_life')
-rw-r--r--examples/game_of_life/src/main.rs270
1 files changed, 137 insertions, 133 deletions
diff --git a/examples/game_of_life/src/main.rs b/examples/game_of_life/src/main.rs
index 62ecc2d1..a2030275 100644
--- a/examples/game_of_life/src/main.rs
+++ b/examples/game_of_life/src/main.rs
@@ -3,18 +3,18 @@
mod preset;
use grid::Grid;
-use iced::button::{self, Button};
+use preset::Preset;
+
use iced::executor;
-use iced::pick_list::{self, PickList};
-use iced::slider::{self, Slider};
use iced::theme::{self, Theme};
use iced::time;
+use iced::widget::{
+ button, checkbox, column, container, pick_list, row, slider, text,
+};
use iced::window;
use iced::{
- Alignment, Application, Checkbox, Column, Command, Element, Length, Row,
- Settings, Subscription, Text,
+ Alignment, Application, Command, Element, Length, Settings, Subscription,
};
-use preset::Preset;
use std::time::{Duration, Instant};
pub fn main() -> iced::Result {
@@ -33,7 +33,6 @@ pub fn main() -> iced::Result {
#[derive(Default)]
struct GameOfLife {
grid: Grid,
- controls: Controls,
is_playing: bool,
queued_ticks: usize,
speed: usize,
@@ -132,23 +131,26 @@ impl Application for GameOfLife {
}
}
- fn view(&mut self) -> Element<Message> {
+ fn view(&self) -> Element<Message> {
let version = self.version;
let selected_speed = self.next_speed.unwrap_or(self.speed);
- let controls = self.controls.view(
+ let controls = view_controls(
self.is_playing,
self.grid.are_lines_visible(),
selected_speed,
self.grid.preset(),
);
- Column::new()
- .push(
- self.grid
- .view()
- .map(move |message| Message::Grid(message, version)),
- )
- .push(controls)
+ let content = column![
+ self.grid
+ .view()
+ .map(move |message| Message::Grid(message, version)),
+ controls
+ ];
+
+ container(content)
+ .width(Length::Fill)
+ .height(Length::Fill)
.into()
}
@@ -157,13 +159,59 @@ impl Application for GameOfLife {
}
}
+fn view_controls<'a>(
+ is_playing: bool,
+ is_grid_enabled: bool,
+ speed: usize,
+ preset: Preset,
+) -> Element<'a, Message> {
+ let playback_controls = row![
+ button(if is_playing { "Pause" } else { "Play" })
+ .on_press(Message::TogglePlayback),
+ button("Next")
+ .on_press(Message::Next)
+ .style(theme::Button::Secondary),
+ ]
+ .spacing(10);
+
+ let speed_controls = row![
+ slider(1.0..=1000.0, speed as f32, Message::SpeedChanged),
+ text(format!("x{}", speed)).size(16),
+ ]
+ .width(Length::Fill)
+ .align_items(Alignment::Center)
+ .spacing(10);
+
+ row![
+ playback_controls,
+ speed_controls,
+ checkbox("Grid", is_grid_enabled, Message::ToggleGrid)
+ .size(16)
+ .spacing(5)
+ .text_size(16),
+ pick_list(preset::ALL, Some(preset), Message::PresetPicked)
+ .padding(8)
+ .text_size(16),
+ button("Clear")
+ .on_press(Message::Clear)
+ .style(theme::Button::Destructive),
+ ]
+ .padding(10)
+ .spacing(20)
+ .align_items(Alignment::Center)
+ .into()
+}
+
mod grid {
use crate::Preset;
+ use iced::widget::canvas;
+ use iced::widget::canvas::event::{self, Event};
+ use iced::widget::canvas::{
+ Cache, Canvas, Cursor, Frame, Geometry, Path, Text,
+ };
use iced::{
- alignment,
- canvas::event::{self, Event},
- canvas::{self, Cache, Canvas, Cursor, Frame, Geometry, Path, Text},
- mouse, Color, Element, Length, Point, Rectangle, Size, Theme, Vector,
+ alignment, mouse, Color, Element, Length, Point, Rectangle, Size,
+ Theme, Vector,
};
use rustc_hash::{FxHashMap, FxHashSet};
use std::future::Future;
@@ -173,7 +221,6 @@ mod grid {
pub struct Grid {
state: State,
preset: Preset,
- interaction: Interaction,
life_cache: Cache,
grid_cache: Cache,
translation: Vector,
@@ -187,6 +234,8 @@ mod grid {
pub enum Message {
Populate(Cell),
Unpopulate(Cell),
+ Translated(Vector),
+ Scaled(f32, Option<Vector>),
Ticked {
result: Result<Life, TickError>,
tick_duration: Duration,
@@ -218,7 +267,6 @@ mod grid {
.collect(),
),
preset,
- interaction: Interaction::None,
life_cache: Cache::default(),
grid_cache: Cache::default(),
translation: Vector::default(),
@@ -263,6 +311,22 @@ mod grid {
self.preset = Preset::Custom;
}
+ Message::Translated(translation) => {
+ self.translation = translation;
+
+ self.life_cache.clear();
+ self.grid_cache.clear();
+ }
+ Message::Scaled(scaling, translation) => {
+ self.scaling = scaling;
+
+ if let Some(translation) = translation {
+ self.translation = translation;
+ }
+
+ self.life_cache.clear();
+ self.grid_cache.clear();
+ }
Message::Ticked {
result: Ok(life),
tick_duration,
@@ -280,7 +344,7 @@ mod grid {
}
}
- pub fn view(&mut self) -> Element<Message> {
+ pub fn view(&self) -> Element<Message> {
Canvas::new(self)
.width(Length::Fill)
.height(Length::Fill)
@@ -329,14 +393,17 @@ mod grid {
}
impl canvas::Program<Message> for Grid {
+ type State = Interaction;
+
fn update(
- &mut self,
+ &self,
+ interaction: &mut Interaction,
event: Event,
bounds: Rectangle,
cursor: Cursor,
) -> (event::Status, Option<Message>) {
if let Event::Mouse(mouse::Event::ButtonReleased(_)) = event {
- self.interaction = Interaction::None;
+ *interaction = Interaction::None;
}
let cursor_position =
@@ -360,7 +427,7 @@ mod grid {
mouse::Event::ButtonPressed(button) => {
let message = match button {
mouse::Button::Left => {
- self.interaction = if is_populated {
+ *interaction = if is_populated {
Interaction::Erasing
} else {
Interaction::Drawing
@@ -369,7 +436,7 @@ mod grid {
populate.or(unpopulate)
}
mouse::Button::Right => {
- self.interaction = Interaction::Panning {
+ *interaction = Interaction::Panning {
translation: self.translation,
start: cursor_position,
};
@@ -382,23 +449,20 @@ mod grid {
(event::Status::Captured, message)
}
mouse::Event::CursorMoved { .. } => {
- let message = match self.interaction {
+ let message = match *interaction {
Interaction::Drawing => populate,
Interaction::Erasing => unpopulate,
Interaction::Panning { translation, start } => {
- self.translation = translation
- + (cursor_position - start)
- * (1.0 / self.scaling);
-
- self.life_cache.clear();
- self.grid_cache.clear();
-
- None
+ Some(Message::Translated(
+ translation
+ + (cursor_position - start)
+ * (1.0 / self.scaling),
+ ))
}
_ => None,
};
- let event_status = match self.interaction {
+ let event_status = match interaction {
Interaction::None => event::Status::Ignored,
_ => event::Status::Captured,
};
@@ -413,30 +477,38 @@ mod grid {
{
let old_scaling = self.scaling;
- self.scaling = (self.scaling
- * (1.0 + y / 30.0))
+ let scaling = (self.scaling * (1.0 + y / 30.0))
.max(Self::MIN_SCALING)
.min(Self::MAX_SCALING);
- if let Some(cursor_to_center) =
- cursor.position_from(bounds.center())
- {
- let factor = self.scaling - old_scaling;
-
- self.translation = self.translation
- - Vector::new(
- cursor_to_center.x * factor
- / (old_scaling * old_scaling),
- cursor_to_center.y * factor
- / (old_scaling * old_scaling),
- );
- }
-
- self.life_cache.clear();
- self.grid_cache.clear();
+ let translation =
+ if let Some(cursor_to_center) =
+ cursor.position_from(bounds.center())
+ {
+ let factor = scaling - old_scaling;
+
+ Some(
+ self.translation
+ - Vector::new(
+ cursor_to_center.x * factor
+ / (old_scaling
+ * old_scaling),
+ cursor_to_center.y * factor
+ / (old_scaling
+ * old_scaling),
+ ),
+ )
+ } else {
+ None
+ };
+
+ (
+ event::Status::Captured,
+ Some(Message::Scaled(scaling, translation)),
+ )
+ } else {
+ (event::Status::Captured, None)
}
-
- (event::Status::Captured, None)
}
},
_ => (event::Status::Ignored, None),
@@ -447,6 +519,7 @@ mod grid {
fn draw(
&self,
+ _interaction: &Interaction,
_theme: &Theme,
bounds: Rectangle,
cursor: Cursor,
@@ -576,10 +649,11 @@ mod grid {
fn mouse_interaction(
&self,
+ interaction: &Interaction,
bounds: Rectangle,
cursor: Cursor,
) -> mouse::Interaction {
- match self.interaction {
+ match interaction {
Interaction::Drawing => mouse::Interaction::Crosshair,
Interaction::Erasing => mouse::Interaction::Crosshair,
Interaction::Panning { .. } => mouse::Interaction::Grabbing,
@@ -808,86 +882,16 @@ mod grid {
}
}
- enum Interaction {
+ pub enum Interaction {
None,
Drawing,
Erasing,
Panning { translation: Vector, start: Point },
}
-}
-
-#[derive(Default)]
-struct Controls {
- toggle_button: button::State,
- next_button: button::State,
- clear_button: button::State,
- speed_slider: slider::State,
- preset_list: pick_list::State<Preset>,
-}
-impl Controls {
- fn view(
- &mut self,
- is_playing: bool,
- is_grid_enabled: bool,
- speed: usize,
- preset: Preset,
- ) -> Element<Message> {
- let playback_controls = Row::new()
- .spacing(10)
- .push(
- Button::new(
- &mut self.toggle_button,
- Text::new(if is_playing { "Pause" } else { "Play" }),
- )
- .on_press(Message::TogglePlayback)
- .style(theme::Button::Primary),
- )
- .push(
- Button::new(&mut self.next_button, Text::new("Next"))
- .on_press(Message::Next)
- .style(theme::Button::Secondary),
- );
-
- let speed_controls = Row::new()
- .width(Length::Fill)
- .align_items(Alignment::Center)
- .spacing(10)
- .push(Slider::new(
- &mut self.speed_slider,
- 1.0..=1000.0,
- speed as f32,
- Message::SpeedChanged,
- ))
- .push(Text::new(format!("x{}", speed)).size(16));
-
- Row::new()
- .padding(10)
- .spacing(20)
- .align_items(Alignment::Center)
- .push(playback_controls)
- .push(speed_controls)
- .push(
- Checkbox::new(is_grid_enabled, "Grid", Message::ToggleGrid)
- .size(16)
- .spacing(5)
- .text_size(16),
- )
- .push(
- PickList::new(
- &mut self.preset_list,
- preset::ALL,
- Some(preset),
- Message::PresetPicked,
- )
- .padding(8)
- .text_size(16),
- )
- .push(
- Button::new(&mut self.clear_button, Text::new("Clear"))
- .on_press(Message::Clear)
- .style(theme::Button::Destructive),
- )
- .into()
+ impl Default for Interaction {
+ fn default() -> Self {
+ Self::None
+ }
}
}