From bfe7216a722ea89fc454aa56860e252e45757623 Mon Sep 17 00:00:00 2001 From: Huck Boles Date: Fri, 16 Jun 2023 17:18:34 -0500 Subject: [PATCH] cleanup: into lib and main files --- src/lib.rs | 152 +++++++++++++++++++++++++++++++++++ src/main.rs | 172 +--------------------------------------- src/{grid.rs => map.rs} | 0 src/preset.rs | 137 -------------------------------- 4 files changed, 154 insertions(+), 307 deletions(-) create mode 100644 src/lib.rs rename src/{grid.rs => map.rs} (100%) delete mode 100644 src/preset.rs diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..b1cd57e --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,152 @@ +use iced::executor; +use iced::theme::{self, Theme}; +use iced::time; +use iced::widget::{button, column, container, row, slider, text}; +use iced::{Alignment, Application, Command, Element, Length, Subscription}; +use std::time::{Duration, Instant}; + +mod map; + +use map::*; + +#[derive(Default)] +pub struct CellSeq { + grid: Map, + is_playing: bool, + queued_ticks: usize, + speed: usize, + next_speed: Option, + version: usize, +} + +#[derive(Debug, Clone)] +pub enum Message { + Grid(map::Message, usize), + Tick(Instant), + TogglePlayback, + Next, + Clear, + SpeedChanged(f32), +} + +impl Application for CellSeq { + type Message = Message; + type Theme = Theme; + type Executor = executor::Default; + type Flags = (); + + fn new(_flags: ()) -> (Self, Command) { + ( + Self { + speed: 5, + ..Self::default() + }, + Command::none(), + ) + } + + fn title(&self) -> String { + String::from("Game of Life - Iced") + } + + fn update(&mut self, message: Message) -> Command { + match 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); + + if let Some(task) = self.grid.tick(self.queued_ticks) { + if let Some(speed) = self.next_speed.take() { + self.speed = speed; + } + + self.queued_ticks = 0; + + let version = self.version; + + return Command::perform(task, move |message| Message::Grid(message, version)); + } + } + Message::TogglePlayback => { + self.is_playing = !self.is_playing; + } + Message::Clear => { + self.grid.clear(); + self.version += 1; + } + Message::SpeedChanged(speed) => { + if self.is_playing { + self.next_speed = Some(speed.round() as usize); + } else { + self.speed = speed.round() as usize; + } + } + } + + Command::none() + } + + fn subscription(&self) -> Subscription { + if self.is_playing { + time::every(Duration::from_millis(1000 / self.speed as u64)).map(Message::Tick) + } else { + Subscription::none() + } + } + + fn view(&self) -> Element { + let version = self.version; + let selected_speed = self.next_speed.unwrap_or(self.speed); + let controls = view_controls(self.is_playing, selected_speed); + + let content = column![ + self.grid + .view() + .map(move |message| Message::Grid(message, version)), + controls, + ]; + + container(content) + .width(Length::Fill) + .height(Length::Fill) + .into() + } + + fn theme(&self) -> Theme { + Theme::Dark + } +} + +fn view_controls<'a>(is_playing: bool, speed: usize) -> 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, + button("Clear") + .on_press(Message::Clear) + .style(theme::Button::Destructive), + ] + .padding(10) + .spacing(20) + .align_items(Alignment::Center) + .into() +} diff --git a/src/main.rs b/src/main.rs index 1e28cb0..2dc2a69 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,16 +1,6 @@ -//! This example showcases an interactive version of the Game of Life, invented -//! by John Conway. It leverages a `Canvas` together with other widgets. -mod grid; +use cellseq::*; -use grid::Map; - -use iced::executor; -use iced::theme::{self, Theme}; -use iced::time; -use iced::widget::{button, checkbox, column, container, row, slider, text}; -use iced::window; -use iced::{Alignment, Application, Command, Element, Length, Settings, Subscription}; -use std::time::{Duration, Instant}; +use iced::{window, Application, Settings}; pub fn main() -> iced::Result { CellSeq::run(Settings { @@ -22,161 +12,3 @@ pub fn main() -> iced::Result { ..Settings::default() }) } - -#[derive(Default)] -struct CellSeq { - grid: Map, - is_playing: bool, - queued_ticks: usize, - speed: usize, - next_speed: Option, - version: usize, -} - -#[derive(Debug, Clone)] -enum Message { - Grid(grid::Message, usize), - Tick(Instant), - TogglePlayback, - ToggleGrid(bool), - Next, - Clear, - SpeedChanged(f32), -} - -impl Application for CellSeq { - type Message = Message; - type Theme = Theme; - type Executor = executor::Default; - type Flags = (); - - fn new(_flags: ()) -> (Self, Command) { - ( - Self { - speed: 5, - ..Self::default() - }, - Command::none(), - ) - } - - fn title(&self) -> String { - String::from("Game of Life - Iced") - } - - fn update(&mut self, message: Message) -> Command { - match 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); - - if let Some(task) = self.grid.tick(self.queued_ticks) { - if let Some(speed) = self.next_speed.take() { - self.speed = speed; - } - - self.queued_ticks = 0; - - let version = self.version; - - return Command::perform(task, move |message| Message::Grid(message, version)); - } - } - Message::TogglePlayback => { - self.is_playing = !self.is_playing; - } - Message::ToggleGrid(show_grid_lines) => { - self.grid.toggle_lines(show_grid_lines); - } - Message::Clear => { - self.grid.clear(); - self.version += 1; - } - Message::SpeedChanged(speed) => { - if self.is_playing { - self.next_speed = Some(speed.round() as usize); - } else { - self.speed = speed.round() as usize; - } - } - } - - Command::none() - } - - fn subscription(&self) -> Subscription { - if self.is_playing { - time::every(Duration::from_millis(1000 / self.speed as u64)).map(Message::Tick) - } else { - Subscription::none() - } - } - - fn view(&self) -> Element { - let version = self.version; - let selected_speed = self.next_speed.unwrap_or(self.speed); - let controls = view_controls( - self.is_playing, - self.grid.are_lines_visible(), - selected_speed, - ); - - let content = column![ - self.grid - .view() - .map(move |message| Message::Grid(message, version)), - controls, - ]; - - container(content) - .width(Length::Fill) - .height(Length::Fill) - .into() - } - - fn theme(&self) -> Theme { - Theme::Dark - } -} - -fn view_controls<'a>( - is_playing: bool, - is_grid_enabled: bool, - speed: usize, -) -> 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), - button("Clear") - .on_press(Message::Clear) - .style(theme::Button::Destructive), - ] - .padding(10) - .spacing(20) - .align_items(Alignment::Center) - .into() -} diff --git a/src/grid.rs b/src/map.rs similarity index 100% rename from src/grid.rs rename to src/map.rs diff --git a/src/preset.rs b/src/preset.rs deleted file mode 100644 index 552527b..0000000 --- a/src/preset.rs +++ /dev/null @@ -1,137 +0,0 @@ -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] -pub enum Preset { - Custom, - #[default] - 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 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", - } - ) - } -} -- 2.45.2