use glib::Object; use gtk::{prelude::*, subclass::prelude::*}; use std::cell::RefCell; const WIDTH: usize = 601; const HEIGHT: usize = 601; const CELL_SIZE: usize = 10; #[derive(Debug, Default)] pub struct SandViewPrivate { area: RefCell<SandArea>, } #[glib::object_subclass] impl ObjectSubclass for SandViewPrivate { const NAME: &'static str = "SandView"; type Type = SandView; type ParentType = gtk::DrawingArea; } impl ObjectImpl for SandViewPrivate {} impl WidgetImpl for SandViewPrivate {} impl DrawingAreaImpl for SandViewPrivate {} glib::wrapper! { pub struct SandView(ObjectSubclass<SandViewPrivate>) @extends gtk::DrawingArea, gtk::Widget; } impl Default for SandView { fn default() -> Self { let s: Self = Object::builder().build(); s.set_width_request(WIDTH as i32); s.set_height_request(HEIGHT as i32); s.set_draw_func({ let s = s.clone(); move |_, context, width, height| { println!("{} {}", width, height); context.set_source_rgb(0., 0., 0.); let _ = context.paint(); context.set_source_rgb(0.1, 0.1, 0.1); for x in (0..width).step_by(CELL_SIZE) { context.move_to(x as f64, 0.); context.line_to(x as f64, HEIGHT as f64); } for y in (0..height).step_by(CELL_SIZE) { context.move_to(0., y as f64); context.line_to(WIDTH as f64, y as f64); } let _ = context.stroke(); let area = s.imp().area.borrow(); for x in 0..area.width { for y in 0..area.height { if area.grain(x, y) { context.set_source_rgb(0.8, 0.8, 0.8); } else { context.set_source_rgb(0., 0., 0.); } context.rectangle( (x * CELL_SIZE + 1) as f64, (y * CELL_SIZE + 1) as f64, (CELL_SIZE - 2) as f64, (CELL_SIZE - 2) as f64, ); let _ = context.fill(); } } } }); s } } impl SandView { fn set_area(&self, area: SandArea) { *self.imp().area.borrow_mut() = area; } } #[derive(Clone, Debug)] struct SandArea { width: usize, height: usize, grains: Vec<bool>, } impl Default for SandArea { fn default() -> Self { let width = WIDTH / CELL_SIZE; let height = HEIGHT / CELL_SIZE; Self { width, height, grains: vec![false; width * height], } } } impl SandArea { pub fn add_grain(&mut self, x: usize, y: usize) { let addr = self.addr(x, y); self.grains[addr] = true; } pub fn grain(&self, x: usize, y: usize) -> bool { self.grains[self.addr(x, y)] } pub fn tick(self) -> Self { unimplemented!() } fn addr(&self, x: usize, y: usize) -> usize { y * self.width + x } } fn main() { let app = gtk::Application::builder() .application_id("com.luminescent-dreams.falling-sand") .build(); app.connect_activate(move |app| { let window = gtk::ApplicationWindow::new(app); window.present(); let view = SandView::default(); let mut sand_area = SandArea::default(); sand_area.add_grain(20, 20); view.set_area(sand_area.clone()); window.set_child(Some(&view)); }); app.run(); }