/* Copyright 2024, Savanni D'Gerinel This file is part of On the Grid. On the Grid is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. On the Grid is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with On the Grid. If not, see . */ // I have an old Board class which I'm going to update. I'll just copy over the rendering code, but // at the same time I am going to work pretty heavily on the API. // // For a game review, the board needs to interact very well with a game record. So I have to keep // in mind all of that as I work on the API. // // Also, this is going to be a cross-platform application. Today it is Gnome + Rust, but as I // progress I will also need a Progressive Web App so that I can run this on my tablet. Especially // useful if I'm out socializing and happen to be talking to somebody who would enjoy a relaxing // game. Anyway, that is going to impact some of my API decisions. // // First, though, I need to rename my game record. // // Now, let's get the existing code compiling again. // // Okay, that wasn't so bad. I'm a little confused that I don't have a code action for renaming a // symbol, but I'll fix that some other time. Anyway, now let's focus on the goban. // Now, we know what kind of object we have for the current board representation. Let's make use of // that. use crate::perftrace; use gio::resources_lookup_data; use glib::Object; use gtk::{ gdk_pixbuf::{InterpType, Pixbuf}, prelude::*, subclass::prelude::*, }; use image::io::Reader as ImageReader; use std::{cell::RefCell, io::Cursor, rc::Rc}; const WIDTH: i32 = 800; const HEIGHT: i32 = 800; const MARGIN: i32 = 20; // Internal representation of the Goban drawing area. #[derive(Default)] pub struct GobanPrivate { board_state: Rc>, } impl GobanPrivate {} #[glib::object_subclass] impl ObjectSubclass for GobanPrivate { const NAME: &'static str = "Goban"; type Type = Goban; type ParentType = gtk::DrawingArea; } impl ObjectImpl for GobanPrivate {} impl WidgetImpl for GobanPrivate {} impl DrawingAreaImpl for GobanPrivate {} // This Goban, being in the `components` crate, is merely the rendering of a board. This is not // the primary representation of the board. // // In a game of Go, there are certain rules about what are valid moves and what are not. // Internally, I want to keep track of those, and doing so requires a few things. // // - We can never repeat a game state (though I think maybe that is allowed in a few rulesets, but // I'm coding to the AGA ruleset) // - We can never play a suicidal move // // Finally, updating the board state is non-GUI logic. So, sorry, might be dropping away from GUI // code for a while to work on the backend representation, some of which already exists. glib::wrapper! { pub struct Goban(ObjectSubclass) @extends gtk::DrawingArea, gtk::Widget; } impl Goban { pub fn new(board_state: otg_core::Goban) -> Self { let s: Self = Object::builder().build(); *s.imp().board_state.borrow_mut() = board_state; s.set_width_request(WIDTH); s.set_height_request(HEIGHT); s.set_draw_func({ let s = s.clone(); move |_, ctx, width, height| { perftrace("render drawing area", || s.redraw(ctx, width, height)); } }); s } fn redraw(&self, ctx: &cairo::Context, width: i32, height: i32) { println!("{} x {}", width, height); /* let wood_texture = resources_lookup_data( "/com/luminescent-dreams/otg-gtk/wood_texture.jpg", gio::ResourceLookupFlags::NONE, ) .unwrap(); let background = ImageReader::new(Cursor::new(wood_texture)) .with_guessed_format() .unwrap() .decode(); let background = background.map(|background| { Pixbuf::from_bytes( &glib::Bytes::from(background.as_bytes()), gtk::gdk_pixbuf::Colorspace::Rgb, false, 8, background.width() as i32, background.height() as i32, background.to_rgb8().sample_layout().height_stride as i32, ) .scale_simple(WIDTH, HEIGHT, InterpType::Nearest) }); match background { Ok(Some(ref background)) => { ctx.set_source_pixbuf(background, 0., 0.); ctx.paint().expect("paint should never fail"); } Ok(None) | Err(_) => ctx.set_source_rgb(0.7, 0.7, 0.7), } */ ctx.set_source_rgb(0.7, 0.7, 0.7); let _ = ctx.paint(); } }