/* 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 . */ // Game review consists of the board, some information about the players, the game tree, and any // commentary on the current move. This requires four major components, some of which are easier // than others. The game board has to be kept in sync with the game tree, so there's a // communication channel there. // // I'll get all of the information about the game from the core, and then render everything in the // UI. So this will be a heavy lift on the UI side. use crate::{components::{Goban, PlayerCard, ReviewTree}, ResourceManager}; use gtk::{prelude::*}; use otg_core::{Color, GameReviewViewModel}; /* #[derive(Default)] pub struct GameReviewPrivate { model: Rc>>, } #[glib::object_subclass] impl ObjectSubclass for GameReviewPrivate { const NAME: &'static str = "GameReview"; type Type = GameReview; type ParentType = gtk::Box; } impl ObjectImpl for GameReviewPrivate {} impl WidgetImpl for GameReviewPrivate {} impl BoxImpl for GameReviewPrivate {} glib::wrapper! { pub struct GameReview(ObjectSubclass) @extends gtk::Box, gtk::Widget, @implements gtk::Accessible; } impl GameReview { pub fn new(_api: CoreApi, record: GameRecord, resources: ResourceManager) -> Self { let s: Self = Object::builder().build(); s } } */ pub struct GameReview { widget: gtk::Box, resources: ResourceManager, view: GameReviewViewModel, } impl GameReview { pub fn new(view: GameReviewViewModel, resources: ResourceManager) -> Self { let widget = gtk::Box::builder().build(); let s = Self { widget, resources, view, }; s.render(); s } fn render(&self) { // It's actually really bad to be just throwing away errors. Panics make everyone unhappy. // This is not a fatal error, so I'll replace this `unwrap` call with something that // renders the board and notifies the user of a problem that cannot be resolved. let board_repr = self.view.game_view(); let board = Goban::new(board_repr, self.resources.clone()); /* s.attach(&board, 0, 0, 2, 2); s.attach(>k::Label::new(Some("white player")), 0, 2, 1, 1); s.attach(>k::Label::new(Some("black player")), 0, 2, 1, 2); s.attach(>k::Label::new(Some("chat")), 1, 2, 2, 2); */ let sidebar = gtk::Box::builder() .orientation(gtk::Orientation::Vertical) .spacing(4) .build(); // The review tree needs to know the record for being able to render all of the nodes. Once // keyboard input is being handled, the tree will have to be updated on each keystroke in // order to show the user where they are within the game record. let review_tree = ReviewTree::new(self.view.clone()); // I think most keyboard focus is going to end up being handled here in GameReview, as // keystrokes need to affect both the goban and the review tree simultanesouly. Possibly // also the player information, seeing as moving to a new position may mean that the score // and time remaining can be updated. let player_information_section = gtk::Box::builder() .orientation(gtk::Orientation::Horizontal) .spacing(4) .build(); player_information_section .append(&PlayerCard::new(Color::Black, &self.view.black_player())); player_information_section .append(&PlayerCard::new(Color::White, &self.view.white_player())); self.widget.append(&board); sidebar.append(&player_information_section); sidebar.append(&review_tree.widget()); self.widget.append(&sidebar); } pub fn widget(&self) -> gtk::Widget { self.widget.clone().upcast::() } }