Compare commits
2 Commits
0aecaee760
...
c702ffcfab
Author | SHA1 | Date |
---|---|---|
Savanni D'Gerinel | c702ffcfab | |
Savanni D'Gerinel | 56d7ad074a |
|
@ -31,3 +31,5 @@ pub mod settings;
|
||||||
mod types;
|
mod types;
|
||||||
pub use types::{BoardError, Color, Config, ConfigOption, LibraryPath, Player, Rank, Size, Tree};
|
pub use types::{BoardError, Color, Config, ConfigOption, LibraryPath, Player, Rank, Size, Tree};
|
||||||
|
|
||||||
|
mod view_models;
|
||||||
|
pub use view_models::GameReviewViewModel;
|
||||||
|
|
|
@ -242,7 +242,7 @@ pub struct Tree<T> {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Node<T> {
|
pub struct Node<T> {
|
||||||
pub id: usize,
|
pub id: usize,
|
||||||
node: T,
|
pub content: T,
|
||||||
parent: Option<usize>,
|
parent: Option<usize>,
|
||||||
depth: usize,
|
depth: usize,
|
||||||
width: RefCell<Option<usize>>,
|
width: RefCell<Option<usize>>,
|
||||||
|
@ -254,7 +254,7 @@ impl<T> Tree<T> {
|
||||||
Tree {
|
Tree {
|
||||||
nodes: vec![Node {
|
nodes: vec![Node {
|
||||||
id: 0,
|
id: 0,
|
||||||
node: root,
|
content: root,
|
||||||
parent: None,
|
parent: None,
|
||||||
depth: 0,
|
depth: 0,
|
||||||
width: RefCell::new(None),
|
width: RefCell::new(None),
|
||||||
|
@ -264,7 +264,7 @@ impl<T> Tree<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn node(&self, idx: usize) -> &T {
|
pub fn node(&self, idx: usize) -> &T {
|
||||||
&self.nodes[idx].node
|
&self.nodes[idx].content
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a node to the parent specified by parent_idx. Return the new index. This cannot be used
|
// Add a node to the parent specified by parent_idx. Return the new index. This cannot be used
|
||||||
|
@ -275,7 +275,7 @@ impl<T> Tree<T> {
|
||||||
|
|
||||||
self.nodes.push(Node {
|
self.nodes.push(Node {
|
||||||
id: next_idx,
|
id: next_idx,
|
||||||
node,
|
content: node,
|
||||||
parent: Some(parent_idx),
|
parent: Some(parent_idx),
|
||||||
depth: parent.depth + 1,
|
depth: parent.depth + 1,
|
||||||
width: RefCell::new(None),
|
width: RefCell::new(None),
|
||||||
|
@ -576,14 +576,14 @@ mod test {
|
||||||
|
|
||||||
let mut iter = tree.bfs_iter();
|
let mut iter = tree.bfs_iter();
|
||||||
|
|
||||||
assert_matches!(iter.next(), Some(Node { node: uuid, .. }) => assert_eq!(*uuid, node_a.id));
|
assert_matches!(iter.next(), Some(Node { content: uuid, .. }) => assert_eq!(*uuid, node_a.id));
|
||||||
assert_matches!(iter.next(), Some(Node { node: uuid, .. }) => assert_eq!(*uuid, node_b.id));
|
assert_matches!(iter.next(), Some(Node { content: uuid, .. }) => assert_eq!(*uuid, node_b.id));
|
||||||
assert_matches!(iter.next(), Some(Node { node: uuid, .. }) => assert_eq!(*uuid, node_g.id));
|
assert_matches!(iter.next(), Some(Node { content: uuid, .. }) => assert_eq!(*uuid, node_g.id));
|
||||||
assert_matches!(iter.next(), Some(Node { node: uuid, .. }) => assert_eq!(*uuid, node_h.id));
|
assert_matches!(iter.next(), Some(Node { content: uuid, .. }) => assert_eq!(*uuid, node_h.id));
|
||||||
assert_matches!(iter.next(), Some(Node { node: uuid, .. }) => assert_eq!(*uuid, node_c.id));
|
assert_matches!(iter.next(), Some(Node { content: uuid, .. }) => assert_eq!(*uuid, node_c.id));
|
||||||
assert_matches!(iter.next(), Some(Node { node: uuid, .. }) => assert_eq!(*uuid, node_i.id));
|
assert_matches!(iter.next(), Some(Node { content: uuid, .. }) => assert_eq!(*uuid, node_i.id));
|
||||||
assert_matches!(iter.next(), Some(Node { node: uuid, .. }) => assert_eq!(*uuid, node_d.id));
|
assert_matches!(iter.next(), Some(Node { content: uuid, .. }) => assert_eq!(*uuid, node_d.id));
|
||||||
assert_matches!(iter.next(), Some(Node { node: uuid, .. }) => assert_eq!(*uuid, node_e.id));
|
assert_matches!(iter.next(), Some(Node { content: uuid, .. }) => assert_eq!(*uuid, node_e.id));
|
||||||
assert_matches!(iter.next(), Some(Node { node: uuid, .. }) => assert_eq!(*uuid, node_f.id));
|
assert_matches!(iter.next(), Some(Node { content: uuid, .. }) => assert_eq!(*uuid, node_f.id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
/*
|
||||||
|
Copyright 2024, Savanni D'Gerinel <savanni@luminescent-dreams.com>
|
||||||
|
|
||||||
|
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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Currenty my game review is able to show the current game and its tree. Now, I want to start
|
||||||
|
// tracking where in the tree that I am. This should be a combination of the abstract Tree and the
|
||||||
|
// gameTree. Chances are, if I just keep track of where I am in the abstract tree, I can find the
|
||||||
|
// relevant node in the game tree and then reproduce the line to get to that node.
|
||||||
|
//
|
||||||
|
// Moving through the game review tree shouldn't require a full invocatian. This object, and most
|
||||||
|
// other view models, should be exported to the UI.
|
||||||
|
|
||||||
|
use crate::{types::{BFSIter, Node}, Goban, Tree};
|
||||||
|
use sgf::{GameRecord, Player};
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
struct GameReviewViewModelPrivate {
|
||||||
|
current_position: Option<Uuid>,
|
||||||
|
game: GameRecord,
|
||||||
|
review_tree: Tree<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct GameReviewViewModel {
|
||||||
|
imp: Arc<RwLock<GameReviewViewModelPrivate>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GameReviewViewModel {
|
||||||
|
pub fn new(game: GameRecord) -> Self {
|
||||||
|
let review_tree = Tree::from(&game.children[0]);
|
||||||
|
let current_position = game.mainline().last().map(|node| node.id());
|
||||||
|
|
||||||
|
Self {
|
||||||
|
imp: Arc::new(RwLock::new(GameReviewViewModelPrivate {
|
||||||
|
current_position,
|
||||||
|
game,
|
||||||
|
review_tree,
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn black_player(&self) -> Player {
|
||||||
|
self.imp.read().unwrap().game.black_player.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn white_player(&self) -> Player {
|
||||||
|
self.imp.read().unwrap().game.white_player.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn game_view(&self) -> Goban {
|
||||||
|
let imp = self.imp.read().unwrap();
|
||||||
|
|
||||||
|
Goban::default().apply_moves(imp.game.mainline()).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn map_tree<F>(&self, f: F)
|
||||||
|
where
|
||||||
|
F: Fn(&Tree<Uuid>, &Node<Uuid>, Option<Uuid>),
|
||||||
|
{
|
||||||
|
let imp = self.imp.read().unwrap();
|
||||||
|
|
||||||
|
for node in imp.review_tree.bfs_iter() {
|
||||||
|
f(&imp.review_tree, node, imp.current_position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tree_max_depth(&self) -> usize {
|
||||||
|
self.imp.read().unwrap().review_tree.max_depth()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn move_forward(&self) {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn move_backward(&self) {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next_variant(&self) {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn previous_variant(&self) {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
/*
|
||||||
|
Copyright 2024, Savanni D'Gerinel <savanni@luminescent-dreams.com>
|
||||||
|
|
||||||
|
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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
mod game_review;
|
||||||
|
pub use game_review::GameReviewViewModel;
|
||||||
|
|
|
@ -18,11 +18,13 @@ use crate::{CoreApi, ResourceManager};
|
||||||
use adw::prelude::*;
|
use adw::prelude::*;
|
||||||
|
|
||||||
use otg_core::{
|
use otg_core::{
|
||||||
settings::{SettingsRequest, SettingsResponse},
|
settings::{SettingsRequest, SettingsResponse}, CoreRequest, CoreResponse, GameReviewViewModel
|
||||||
CoreRequest, CoreResponse,
|
|
||||||
};
|
};
|
||||||
use sgf::GameRecord;
|
use sgf::GameRecord;
|
||||||
use std::{rc::Rc, sync::{Arc, RwLock}};
|
use std::{
|
||||||
|
rc::Rc,
|
||||||
|
sync::{Arc, RwLock},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::views::{GameReview, HomeView, SettingsView};
|
use crate::views::{GameReview, HomeView, SettingsView};
|
||||||
|
|
||||||
|
@ -91,13 +93,16 @@ impl AppWindow {
|
||||||
|
|
||||||
pub fn open_game_review(&self, game_record: GameRecord) {
|
pub fn open_game_review(&self, game_record: GameRecord) {
|
||||||
let header = adw::HeaderBar::new();
|
let header = adw::HeaderBar::new();
|
||||||
let game_review = GameReview::new(self.core.clone(), game_record, self.resources.clone());
|
let game_review = GameReview::new(
|
||||||
|
GameReviewViewModel::new(game_record),
|
||||||
|
self.resources.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
let layout = gtk::Box::builder()
|
let layout = gtk::Box::builder()
|
||||||
.orientation(gtk::Orientation::Vertical)
|
.orientation(gtk::Orientation::Vertical)
|
||||||
.build();
|
.build();
|
||||||
layout.append(&header);
|
layout.append(&header);
|
||||||
layout.append(&game_review);
|
layout.append(&game_review.widget());
|
||||||
|
|
||||||
let page = adw::NavigationPage::builder()
|
let page = adw::NavigationPage::builder()
|
||||||
.can_pop(true)
|
.can_pop(true)
|
||||||
|
|
|
@ -15,48 +15,35 @@ You should have received a copy of the GNU General Public License along with On
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use cairo::Context;
|
use cairo::Context;
|
||||||
use glib::Object;
|
use gtk::prelude::*;
|
||||||
use gtk::{prelude::*, subclass::prelude::*};
|
use otg_core::GameReviewViewModel;
|
||||||
use otg_core::Tree;
|
|
||||||
use sgf::GameRecord;
|
|
||||||
use std::{cell::RefCell, rc::Rc};
|
|
||||||
use uuid::Uuid;
|
|
||||||
|
|
||||||
const WIDTH: i32 = 200;
|
const WIDTH: i32 = 200;
|
||||||
const HEIGHT: i32 = 800;
|
const HEIGHT: i32 = 800;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Clone)]
|
||||||
pub struct ReviewTreePrivate {
|
pub struct ReviewTree {
|
||||||
record: Rc<RefCell<Option<GameRecord>>>,
|
widget: gtk::ScrolledWindow,
|
||||||
tree: Rc<RefCell<Option<Tree<Uuid>>>>,
|
drawing_area: gtk::DrawingArea,
|
||||||
}
|
|
||||||
|
|
||||||
#[glib::object_subclass]
|
view: GameReviewViewModel,
|
||||||
impl ObjectSubclass for ReviewTreePrivate {
|
|
||||||
const NAME: &'static str = "ReviewTree";
|
|
||||||
type Type = ReviewTree;
|
|
||||||
type ParentType = gtk::DrawingArea;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ObjectImpl for ReviewTreePrivate {}
|
|
||||||
impl WidgetImpl for ReviewTreePrivate {}
|
|
||||||
impl DrawingAreaImpl for ReviewTreePrivate {}
|
|
||||||
|
|
||||||
glib::wrapper! {
|
|
||||||
pub struct ReviewTree(ObjectSubclass<ReviewTreePrivate>) @extends gtk::Widget, gtk::DrawingArea;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ReviewTree {
|
impl ReviewTree {
|
||||||
pub fn new(record: GameRecord) -> Self {
|
pub fn new(view: GameReviewViewModel) -> ReviewTree {
|
||||||
let s: Self = Object::new();
|
let drawing_area = gtk::DrawingArea::new();
|
||||||
|
let widget = gtk::ScrolledWindow::builder()
|
||||||
|
.child(&drawing_area)
|
||||||
|
.build();
|
||||||
|
|
||||||
*s.imp().tree.borrow_mut() = Some(Tree::from(&record.children[0]));
|
widget.set_width_request(WIDTH);
|
||||||
*s.imp().record.borrow_mut() = Some(record);
|
widget.set_height_request(HEIGHT);
|
||||||
|
|
||||||
s.set_width_request(WIDTH);
|
drawing_area.set_height_request(view.tree_max_depth() as i32 * 20 + 40);
|
||||||
s.set_height_request(HEIGHT);
|
|
||||||
|
|
||||||
s.set_draw_func({
|
let s = Self { widget, drawing_area, view };
|
||||||
|
|
||||||
|
s.drawing_area.set_draw_func({
|
||||||
let s = s.clone();
|
let s = s.clone();
|
||||||
move |_, ctx, width, height| {
|
move |_, ctx, width, height| {
|
||||||
s.redraw(ctx, width, height);
|
s.redraw(ctx, width, height);
|
||||||
|
@ -66,27 +53,27 @@ impl ReviewTree {
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn redraw(&self, ctx: &Context, _width: i32, _height: i32) {
|
fn redraw(&self, ctx: &Context, _width: i32, _height: i32) {
|
||||||
let tree: &Option<Tree<Uuid>> = &self.imp().tree.borrow();
|
println!("redraw: {} {}", _width, _height);
|
||||||
match tree {
|
self.view.map_tree(move |tree, node, current| {
|
||||||
Some(ref tree) => {
|
|
||||||
for node in tree.bfs_iter() {
|
|
||||||
// draw a circle given the coordinates of the nodes
|
|
||||||
// I don't know the indent. How do I keep track of that? Do I track the position of
|
|
||||||
// the parent? do I need to just make it more intrinsically a part of the position
|
|
||||||
// code?
|
|
||||||
ctx.set_source_rgb(0.7, 0.7, 0.7);
|
ctx.set_source_rgb(0.7, 0.7, 0.7);
|
||||||
let (row, column) = tree.position(node.id);
|
let (row, column) = tree.position(node.id);
|
||||||
let y = (row as f64) * 20. + 10.;
|
let y = (row as f64) * 20. + 10.;
|
||||||
let x = (column as f64) * 20. + 10.;
|
let x = (column as f64) * 20. + 10.;
|
||||||
ctx.arc(x, y, 5., 0., 2. * std::f64::consts::PI);
|
ctx.arc(x, y, 5., 0., 2. * std::f64::consts::PI);
|
||||||
|
|
||||||
|
if current == Some(node.content) {
|
||||||
|
ctx.set_line_width(3.);
|
||||||
|
} else {
|
||||||
|
ctx.set_line_width(1.);
|
||||||
|
}
|
||||||
|
|
||||||
let _ = ctx.stroke();
|
let _ = ctx.stroke();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
None => {
|
pub fn widget(&self) -> gtk::Widget {
|
||||||
// if there is no tree present, then there's nothing to draw!
|
self.widget.clone().upcast::<gtk::Widget>()
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,12 +110,6 @@ impl ReviewTree {
|
||||||
// A bottom-up traversal:
|
// A bottom-up traversal:
|
||||||
// - Figure out the number of nodes at each depth
|
// - Figure out the number of nodes at each depth
|
||||||
|
|
||||||
/*
|
|
||||||
struct Tree {
|
|
||||||
width: Vec<usize>, // the total width of the tree at each depth
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -23,15 +23,20 @@ You should have received a copy of the GNU General Public License along with On
|
||||||
// UI. So this will be a heavy lift on the UI side.
|
// UI. So this will be a heavy lift on the UI side.
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
components::{Goban, PlayerCard, ReviewTree}, CoreApi, ResourceManager
|
components::{Goban, PlayerCard, ReviewTree},
|
||||||
|
CoreApi, ResourceManager,
|
||||||
};
|
};
|
||||||
use glib::Object;
|
use glib::Object;
|
||||||
use gtk::{prelude::*, subclass::prelude::*};
|
use gtk::{prelude::*, subclass::prelude::*};
|
||||||
use otg_core::Color;
|
use otg_core::{Color, GameReviewViewModel};
|
||||||
use sgf::GameRecord;
|
use sgf::GameRecord;
|
||||||
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
|
/*
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct GameReviewPrivate {}
|
pub struct GameReviewPrivate {
|
||||||
|
model: Rc<RefCell<Option<GameReviewViewModel>>>,
|
||||||
|
}
|
||||||
|
|
||||||
#[glib::object_subclass]
|
#[glib::object_subclass]
|
||||||
impl ObjectSubclass for GameReviewPrivate {
|
impl ObjectSubclass for GameReviewPrivate {
|
||||||
|
@ -52,13 +57,45 @@ impl GameReview {
|
||||||
pub fn new(_api: CoreApi, record: GameRecord, resources: ResourceManager) -> Self {
|
pub fn new(_api: CoreApi, record: GameRecord, resources: ResourceManager) -> Self {
|
||||||
let s: Self = Object::builder().build();
|
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.
|
// 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
|
// 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.
|
// renders the board and notifies the user of a problem that cannot be resolved.
|
||||||
|
/*
|
||||||
let board_repr = otg_core::Goban::default()
|
let board_repr = otg_core::Goban::default()
|
||||||
.apply_moves(record.mainline())
|
.apply_moves(record.mainline())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let board = Goban::new(board_repr, resources);
|
*/
|
||||||
|
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(&board, 0, 0, 2, 2);
|
||||||
|
@ -75,7 +112,7 @@ impl GameReview {
|
||||||
// The review tree needs to know the record for being able to render all of the nodes. Once
|
// 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
|
// 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.
|
// order to show the user where they are within the game record.
|
||||||
let review_tree = ReviewTree::new(record.clone());
|
let review_tree = ReviewTree::new(self.view.clone());
|
||||||
|
|
||||||
// I think most keyboard focus is going to end up being handled here in GameReview, as
|
// 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
|
// keystrokes need to affect both the goban and the review tree simultanesouly. Possibly
|
||||||
|
@ -87,14 +124,18 @@ impl GameReview {
|
||||||
.spacing(4)
|
.spacing(4)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
player_information_section.append(&PlayerCard::new(Color::Black, &record.black_player));
|
player_information_section
|
||||||
player_information_section.append(&PlayerCard::new(Color::White, &record.white_player));
|
.append(&PlayerCard::new(Color::Black, &self.view.black_player()));
|
||||||
|
player_information_section
|
||||||
|
.append(&PlayerCard::new(Color::White, &self.view.white_player()));
|
||||||
|
|
||||||
s.append(&board);
|
self.widget.append(&board);
|
||||||
sidebar.append(&player_information_section);
|
sidebar.append(&player_information_section);
|
||||||
sidebar.append(&review_tree);
|
sidebar.append(&review_tree.widget());
|
||||||
s.append(&sidebar);
|
self.widget.append(&sidebar);
|
||||||
|
}
|
||||||
|
|
||||||
s
|
pub fn widget(&self) -> gtk::Widget {
|
||||||
|
self.widget.clone().upcast::<gtk::Widget>()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue