/* use iced::widget::canvas::{self, Path, Stroke, Text}; use iced::{ executor, keyboard, time, Application, Color, Command, Element, Length, Point, Rectangle, Settings, Size, Subscription, }; use tetris_logic::{BlockGrid, MoveDirection, Tetromino}; */ use iced::{ widget::canvas::{self, Path, Stroke, Text}, Color, Element, Length, Pixels, Point, Renderer, Size, Subscription, Task, Theme, }; use tetris_logic::{BlockGrid, MoveDirection, Tetromino}; pub fn tetris_main() -> iced::Result { iced::application(Tetris::title, update, view) .subscription(subscription) .run_with(|| (Tetris::new(), Task::none())) } #[derive(Debug, Clone)] enum Message { Up, Down, Left, Right, Pause, Tick(chrono::DateTime), } fn update(state: &mut Tetris, message: Message) { match message { Message::Pause => state.paused = !state.paused, Message::Tick(_time) => { if !state.paused { state.ticks += 1; } } Message::Up => { state.blocks.rotate_active_piece(); } Message::Down => { state.blocks.move_active_piece(MoveDirection::HardDrop); } Message::Left => { state.blocks.move_active_piece(MoveDirection::Left); } Message::Right => { state.blocks.move_active_piece(MoveDirection::Right); } }; if state.blocks.piece_currently_active() { if state.ticks % 10 == 0 && !state.paused { state.blocks.move_active_piece(MoveDirection::SoftDrop); } } else { let piece: Tetromino = rand::random(); state.blocks.drop_piece(piece); } let lines_removed = state.blocks.clear_pieces(); state.lines_removed += lines_removed; } fn subscription(_state: &Tetris) -> Subscription { let keyboard_subscription = iced::keyboard::on_key_press(|key, _modifiers| { use iced::keyboard::key::{Key, Named}; match key { Key::Named(Named::ArrowUp) => Some(Message::Up), Key::Named(Named::ArrowDown) => Some(Message::Down), Key::Named(Named::ArrowLeft) => Some(Message::Left), Key::Named(Named::ArrowRight) => Some(Message::Right), Key::Named(Named::Space) => Some(Message::Pause), _ => None, } }); let time_subscription = iced::time::every(std::time::Duration::from_millis(50)) .map(|_| Message::Tick(chrono::Local::now())); Subscription::batch([time_subscription, keyboard_subscription]) } fn view(state: &Tetris) -> Element { iced::widget::canvas(state) .width(Length::Fill) .height(Length::Fill) .into() } struct Tetris { background_cache: canvas::Cache, blocks: BlockGrid, ticks: usize, paused: bool, lines_removed: u32, } impl Tetris { fn title(&self) -> String { String::from("Tetris - Iced") } fn new() -> Tetris { Tetris { background_cache: canvas::Cache::default(), blocks: BlockGrid::new(), ticks: 0, paused: false, lines_removed: 0, } } } impl canvas::Program for Tetris { type State = (); fn draw( &self, _state: &Self::State, renderer: &Renderer, _theme: &Theme, bounds: iced::Rectangle, _cursor: iced::mouse::Cursor, ) -> Vec> { let game_width = bounds.width / 3.0; let block_length = game_width / 10.0; let center = bounds.center(); let top_left = Point::new(center.x - game_width / 2.0, 0.0); let background = self .background_cache .draw(renderer, bounds.size(), |frame| { let game_size = Size::new(block_length * 10.0, block_length * 20.0); let game_bg = Path::rectangle(top_left, game_size); frame.fill(&game_bg, Color::BLACK); }); let block_size = Size::new(block_length, block_length); let mut frame = canvas::Frame::new(renderer, bounds.size()); for (i, j, tetronimo) in self.blocks.iter() { let point = Point::new( i as f32 * block_length + top_left.x, j as f32 * block_length + top_left.y, ); let block = Path::rectangle(point, block_size); let color = tetronimo.color(); let fill_color = Color::from_rgb8(color.0, color.1, color.2); frame.fill(&block, fill_color); let stroke_color = Color::from_rgb8( color.0.checked_sub(20).unwrap_or(0), color.1.checked_sub(20).unwrap_or(0), color.2.checked_sub(20).unwrap_or(0), ); let stroke = Stroke::default().with_width(3.0).with_color(stroke_color); frame.stroke(&block, stroke); } let text_color = Color::from_rgb8(255, 30, 30); let text_size = Pixels(32.0); let score = Text { content: format!("Lines removed: {}", self.lines_removed), position: Point::new(10.0, 30.0), size: text_size, color: text_color, ..Default::default() }; frame.fill_text(score); if self.paused { let paused = Text { content: "PAUSED".to_string(), position: Point::new(10.0, 60.0), color: text_color, size: text_size, ..Default::default() }; frame.fill_text(paused); } vec![background, frame.into_geometry()] } }