diff --git a/flake.nix b/flake.nix index 138422f..a5ff1b9 100644 --- a/flake.nix +++ b/flake.nix @@ -76,7 +76,7 @@ inherit pkgs; buildRustCrateForPkgs = customBuildInfo; rootFeatures = [ "screenplay" ]; - release = false; + release = true; }).rootCrate.build; }; }; diff --git a/kifu/kifu-core/src/api.rs b/kifu/kifu-core/src/api.rs index eb90866..983a5bc 100644 --- a/kifu/kifu-core/src/api.rs +++ b/kifu/kifu-core/src/api.rs @@ -2,11 +2,19 @@ use crate::types::AppState; use crate::ui::{playing_field, PlayingFieldView}; use std::sync::{Arc, RwLock}; +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Request { PlayingField, + PlayStoneRequest(PlayStoneRequest), } -#[derive(Debug)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct PlayStoneRequest { + pub column: u8, + pub row: u8, +} + +#[derive(Clone, Debug)] pub enum Response { PlayingFieldView(PlayingFieldView), } @@ -25,7 +33,18 @@ impl CoreApp { pub async fn dispatch(&self, request: Request) -> Response { match request { - Request::PlayingField => Response::PlayingFieldView(playing_field()), + Request::PlayingField => { + let app_state = self.state.read().unwrap(); + let game = app_state.game.as_ref().unwrap(); + Response::PlayingFieldView(playing_field(game)) + } + Request::PlayStoneRequest(request) => { + let mut app_state = self.state.write().unwrap(); + app_state.place_stone(request); + + let game = app_state.game.as_ref().unwrap(); + Response::PlayingFieldView(playing_field(game)) + } } } diff --git a/kifu/kifu-core/src/lib.rs b/kifu/kifu-core/src/lib.rs index a13d2c8..9c22bc8 100644 --- a/kifu/kifu-core/src/lib.rs +++ b/kifu/kifu-core/src/lib.rs @@ -1,5 +1,3 @@ -use std::time::Duration; - mod api; pub use api::{CoreApp, Request, Response}; diff --git a/kifu/kifu-core/src/types.rs b/kifu/kifu-core/src/types.rs index e37992a..e9c9f32 100644 --- a/kifu/kifu-core/src/types.rs +++ b/kifu/kifu-core/src/types.rs @@ -1,6 +1,7 @@ +use crate::api::PlayStoneRequest; use std::time::Duration; -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Color { Black, White, @@ -21,13 +22,24 @@ impl Default for Size { } } -pub(crate) struct AppState { - game: Option, +pub struct AppState { + pub game: Option, } impl AppState { pub fn new() -> Self { - Self { game: None } + Self { + game: Some(GameState::new()), + } + } + + pub fn place_stone(&mut self, req: PlayStoneRequest) { + match self.game { + Some(ref mut game) => { + game.place_stone(req.column, req.row); + } + None => {} + } } } @@ -55,18 +67,82 @@ impl From for String { } } -struct Player { +pub struct Player { name: String, rank: Rank, } -pub(crate) struct GameState { - goban: Vec>, - conversation: Vec, +pub struct GameState { + pub board: Board, + pub conversation: Vec, + pub current_player: Color, - white_player: Player, - black_player: Player, + pub white_player: Player, + pub black_player: Player, - white_clock: Duration, - black_clock: Duration, + pub white_clock: Duration, + pub black_clock: Duration, +} + +impl GameState { + fn new() -> GameState { + GameState { + board: Board::new(), + conversation: vec![], + current_player: Color::Black, + white_player: Player { + name: "Savanni".to_owned(), + rank: Rank::Kyu(10), + }, + black_player: Player { + name: "Opal".to_owned(), + rank: Rank::Kyu(10), + }, + white_clock: Duration::from_secs(600), + black_clock: Duration::from_secs(600), + } + } + + fn place_stone(&mut self, column: u8, row: u8) { + self.board.place_stone(column, row, self.current_player); + match self.current_player { + Color::White => self.current_player = Color::Black, + Color::Black => self.current_player = Color::White, + } + } +} + +pub struct Board { + pub size: Size, + pub spaces: Vec>, +} + +impl Board { + fn new() -> Self { + let mut spaces = Vec::new(); + for _ in 0..19 * 19 { + spaces.push(None); + } + Self { + size: Size { + width: 19, + height: 19, + }, + spaces, + } + } + + pub fn place_stone(&mut self, column: u8, row: u8, stone: Color) { + let addr = self.addr(column, row); + self.spaces[addr] = Some(stone); + } + + pub fn stone(&self, column: u8, row: u8) -> Option { + let addr = self.addr(column, row); + self.spaces[addr] + } + + fn addr(&self, column: u8, row: u8) -> usize { + ((row as usize) * (self.size.width as usize) + (column as usize)) as usize + } } diff --git a/kifu/kifu-core/src/ui/mod.rs b/kifu/kifu-core/src/ui/mod.rs index e7b40c0..abe5612 100644 --- a/kifu/kifu-core/src/ui/mod.rs +++ b/kifu/kifu-core/src/ui/mod.rs @@ -2,4 +2,7 @@ mod playing_field; pub use playing_field::{playing_field, PlayingFieldView}; mod types; -pub use types::{ChatElement, GobanElement, PlayerCardElement, StoneElement, TextFieldElement}; +pub use types::{ + BoardElement, ChatElement, IntersectionElement, PlayerCardElement, StoneElement, + TextFieldElement, +}; diff --git a/kifu/kifu-core/src/ui/playing_field.rs b/kifu/kifu-core/src/ui/playing_field.rs index 8787338..cdc9c12 100644 --- a/kifu/kifu-core/src/ui/playing_field.rs +++ b/kifu/kifu-core/src/ui/playing_field.rs @@ -1,9 +1,9 @@ -use crate::types::{Color, Size}; -use crate::ui::types; +use crate::types::Color; +use crate::{types::GameState, ui::types}; #[derive(Clone, Debug)] pub struct PlayingFieldView { - pub board: types::GobanElement, + pub board: types::BoardElement, pub player_card_black: types::PlayerCardElement, pub player_card_white: types::PlayerCardElement, pub chat: types::ChatElement, @@ -11,8 +11,8 @@ pub struct PlayingFieldView { pub current_player: Color, } -pub fn playing_field() -> PlayingFieldView { - let board = types::GobanElement::default(); +pub fn playing_field(game: &GameState) -> PlayingFieldView { + let board = types::BoardElement::from(&game.board); let player_card_black = types::PlayerCardElement { color: Color::Black, @@ -42,6 +42,6 @@ pub fn playing_field() -> PlayingFieldView { player_card_white, chat, message, - current_player: Color::White, + current_player: game.current_player, } } diff --git a/kifu/kifu-core/src/ui/types.rs b/kifu/kifu-core/src/ui/types.rs index 05092f3..39e2904 100644 --- a/kifu/kifu-core/src/ui/types.rs +++ b/kifu/kifu-core/src/ui/types.rs @@ -1,12 +1,16 @@ use crate::types::{Color, Size}; +use crate::{ + api::{PlayStoneRequest, Request}, + types::Board, +}; -#[derive(Clone, Debug)] +#[derive(Clone, Copy, Debug, PartialEq)] pub struct Jitter { pub x: i8, pub y: i8, } -#[derive(Clone, Debug)] +#[derive(Clone, Copy, Debug, PartialEq)] pub struct StoneElement { pub color: Color, pub jitter: Jitter, @@ -21,37 +25,73 @@ impl StoneElement { } } -#[derive(Clone, Debug)] -pub struct GobanElement { - pub size: Size, - pub spaces: Vec>, +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum IntersectionElement { + Unplayable, + Empty(Request), + Filled(StoneElement), } -impl GobanElement { +#[derive(Clone, Debug)] +pub struct BoardElement { + pub size: Size, + pub spaces: Vec, +} + +impl BoardElement { pub fn new(size: Size) -> Self { + let spaces: Vec = (0..size.height) + .map(|row| { + (0..size.width) + .map(|column| { + IntersectionElement::Empty(Request::PlayStoneRequest(PlayStoneRequest { + column, + row, + })) + }) + .collect::>() + }) + .collect::>>() + .concat(); + Self { size, spaces } + } + + pub fn stone(&self, column: u8, row: u8) -> IntersectionElement { + let addr = self.addr(column, row); + self.spaces[addr] + } + + fn addr(&self, column: u8, row: u8) -> usize { + ((row as usize) * (self.size.width as usize) + (column as usize)) as usize + } +} + +impl From<&Board> for BoardElement { + fn from(board: &Board) -> Self { + let spaces: Vec = (0..board.size.height) + .map(|row| { + (0..board.size.width) + .map(|column| match board.stone(column, row) { + Some(color) => IntersectionElement::Filled(StoneElement { + jitter: Jitter { x: 0, y: 0 }, + color, + }), + None => IntersectionElement::Empty(Request::PlayStoneRequest( + PlayStoneRequest { column, row }, + )), + }) + .collect::>() + }) + .collect::>>() + .concat(); Self { - size: size.clone(), - spaces: (0..((size.width as usize) * (size.height as usize))) - .map(|_| None) - .collect::>>(), + size: board.size, + spaces, } } - - pub fn stone_mut<'a>(&'a mut self, row: u8, col: u8) -> &'a mut Option { - let addr = self.addr(row, col); - &mut self.spaces[addr] - } - - pub fn stone(&self, row: u8, col: u8) -> Option { - self.spaces[self.addr(row, col)].clone() - } - - fn addr(&self, row: u8, col: u8) -> usize { - ((row as usize) * (self.size.width as usize) + (col as usize)) as usize - } } -impl Default for GobanElement { +impl Default for BoardElement { fn default() -> Self { Self::new(Size::default()) } diff --git a/kifu/kifu-gtk/src/lib.rs b/kifu/kifu-gtk/src/lib.rs index 6bae95d..bc9d45c 100644 --- a/kifu/kifu-gtk/src/lib.rs +++ b/kifu/kifu-gtk/src/lib.rs @@ -1 +1,21 @@ pub mod ui; +use kifu_core::{CoreApp, Request, Response}; +use std::sync::Arc; +use tokio::runtime::Runtime; + +#[derive(Clone)] +pub struct CoreApi { + pub gtk_tx: gtk::glib::Sender, + pub rt: Arc, + pub core: CoreApp, +} + +impl CoreApi { + pub fn dispatch(&self, request: Request) { + self.rt.spawn({ + let gtk_tx = self.gtk_tx.clone(); + let core = self.core.clone(); + async move { gtk_tx.send(core.dispatch(request).await) } + }); + } +} diff --git a/kifu/kifu-gtk/src/main.rs b/kifu/kifu-gtk/src/main.rs index a12ed30..2ede4f3 100644 --- a/kifu/kifu-gtk/src/main.rs +++ b/kifu/kifu-gtk/src/main.rs @@ -1,32 +1,7 @@ -use gio::resources_lookup_data; use gtk::prelude::*; use kifu_core::{CoreApp, Request, Response}; -use kifu_gtk::ui::PlayingField; -use std::{ - sync::{Arc, Mutex}, - time::Duration, -}; -use tokio::{ - runtime::Runtime, - sync::mpsc::{Receiver, Sender}, -}; - -#[derive(Clone)] -pub struct CoreApi { - gtk_tx: gtk::glib::Sender, - rt: Arc, - core: CoreApp, -} - -impl CoreApi { - pub fn dispatch(&self, request: Request) { - self.rt.spawn({ - let gtk_tx = self.gtk_tx.clone(); - let core = self.core.clone(); - async move { gtk_tx.send(core.dispatch(request).await) } - }); - } -} +use kifu_gtk::{ui::PlayingField, CoreApi}; +use std::sync::{Arc, RwLock}; fn main() { gio::resources_register_include!("com.luminescent-dreams.kifu-gtk.gresource") @@ -67,15 +42,29 @@ fn main() { let window = gtk::ApplicationWindow::new(app); window.present(); - gtk_rx.attach(None, move |message| { - println!("message: {:?}", message); - match message { - Response::PlayingFieldView(view) => { - let playing_field = PlayingField::new(view); - window.set_child(Some(&playing_field)); + gtk_rx.attach(None, { + let api = api.clone(); + let playing_field = Arc::new(RwLock::new(None)); + move |message| { + match message { + Response::PlayingFieldView(view) => { + let api = api.clone(); + + let start = std::time::Instant::now(); + let mut playing_field = playing_field.write().unwrap(); + if playing_field.is_none() { + let field = PlayingField::new(api, view); + window.set_child(Some(&field)); + *playing_field = Some(field); + } else { + playing_field.as_ref().map(|field| field.update_view(view)); + } + let end = std::time::Instant::now(); + println!("Time to render the playing field: {:?}", end - start); + } } + Continue(true) } - Continue(true) }); api.dispatch(Request::PlayingField); diff --git a/kifu/kifu-gtk/src/ui/goban.rs b/kifu/kifu-gtk/src/ui/board.rs similarity index 50% rename from kifu/kifu-gtk/src/ui/goban.rs rename to kifu/kifu-gtk/src/ui/board.rs index 2580500..7b442f5 100644 --- a/kifu/kifu-gtk/src/ui/goban.rs +++ b/kifu/kifu-gtk/src/ui/board.rs @@ -1,3 +1,4 @@ +use crate::CoreApi; use gio::resources_lookup_data; use glib::Object; use gtk::{ @@ -6,11 +7,15 @@ use gtk::{ subclass::prelude::*, }; use image::io::Reader as ImageReader; -use kifu_core::{ui::GobanElement, Color}; +use kifu_core::{ + ui::{BoardElement, IntersectionElement}, + Color, +}; use std::{cell::RefCell, io::Cursor, rc::Rc}; const WIDTH: i32 = 800; const HEIGHT: i32 = 800; +const MARGIN: i32 = 20; #[derive(Clone, Default, PartialEq)] struct Addr { @@ -18,36 +23,39 @@ struct Addr { column: u8, } -pub struct GobanPrivate { +pub struct BoardPrivate { drawing_area: gtk::DrawingArea, current_player: Rc>, - goban: Rc>, - cursor_location: Rc>, + board: Rc>, + cursor_location: Rc>>, + + api: Rc>>, } #[glib::object_subclass] -impl ObjectSubclass for GobanPrivate { - const NAME: &'static str = "Goban"; - type Type = Goban; +impl ObjectSubclass for BoardPrivate { + const NAME: &'static str = "Board"; + type Type = Board; type ParentType = gtk::Grid; - fn new() -> GobanPrivate { - GobanPrivate { + fn new() -> BoardPrivate { + BoardPrivate { drawing_area: Default::default(), current_player: Rc::new(RefCell::new(Color::Black)), - goban: Default::default(), + board: Default::default(), cursor_location: Default::default(), + api: Default::default(), } } } -impl ObjectImpl for GobanPrivate { +impl ObjectImpl for BoardPrivate { fn constructed(&self) { self.drawing_area.set_width_request(WIDTH); self.drawing_area.set_height_request(HEIGHT); - let goban = self.goban.clone(); + let board = self.board.clone(); let cursor_location = self.cursor_location.clone(); let current_player = self.current_player.clone(); @@ -75,7 +83,9 @@ impl ObjectImpl for GobanPrivate { self.drawing_area .set_draw_func(move |_, context, width, height| { - let goban = goban.borrow(); + let render_start = std::time::Instant::now(); + let board = board.borrow(); + match background { Ok(Some(ref background)) => { context.set_source_pixbuf(&background, 0., 0.); @@ -87,24 +97,36 @@ impl ObjectImpl for GobanPrivate { context.set_source_rgb(0.1, 0.1, 0.1); context.set_line_width(2.); - let hspace_between = ((width - 40) as f64) / ((goban.size.width - 1) as f64); - let vspace_between = ((height - 40) as f64) / ((goban.size.height - 1) as f64); + let hspace_between = ((width - 40) as f64) / ((board.size.width - 1) as f64); + let vspace_between = ((height - 40) as f64) / ((board.size.height - 1) as f64); let pen = Pen { - x_offset: 20., - y_offset: 20., + x_offset: MARGIN as f64, + y_offset: MARGIN as f64, hspace_between, vspace_between, }; - (0..goban.size.width).for_each(|col| { - context.move_to(20.0 + (col as f64) * hspace_between, 20.0); - context.line_to(20.0 + (col as f64) * hspace_between, (height as f64) - 20.0); + (0..board.size.width).for_each(|col| { + context.move_to( + (MARGIN as f64) + (col as f64) * hspace_between, + MARGIN as f64, + ); + context.line_to( + (MARGIN as f64) + (col as f64) * hspace_between, + (height as f64) - (MARGIN as f64), + ); let _ = context.stroke(); }); - (0..goban.size.height).for_each(|row| { - context.move_to(20.0, 20.0 + (row as f64) * vspace_between); - context.line_to((width - 20) as f64, 20.0 + (row as f64) * vspace_between); + (0..board.size.height).for_each(|row| { + context.move_to( + MARGIN as f64, + (MARGIN as f64) + (row as f64) * vspace_between, + ); + context.line_to( + (width - MARGIN) as f64, + (MARGIN as f64) + (row as f64) * vspace_between, + ); let _ = context.stroke(); }); @@ -117,61 +139,110 @@ impl ObjectImpl for GobanPrivate { (0..19).for_each(|col| { (0..19).for_each(|row| { - match goban.stone(row, col) { - None => {} - Some(element) => { - pen.stone(&context, row, col, element.color); + match board.stone(row, col) { + IntersectionElement::Filled(stone) => { + pen.stone(&context, row, col, stone.color); } + _ => {} }; }) }); let cursor = cursor_location.borrow(); - pen.ghost_stone(context, cursor.row, cursor.column, *current_player.borrow()); + match *cursor { + None => {} + Some(ref cursor) => match board.stone(cursor.row, cursor.column) { + IntersectionElement::Empty(_) => pen.ghost_stone( + context, + cursor.row, + cursor.column, + *current_player.borrow(), + ), + _ => {} + }, + } + let render_end = std::time::Instant::now(); + println!("board rendering time: {:?}", render_end - render_start); }); let motion_controller = gtk::EventControllerMotion::new(); { - let goban = self.goban.clone(); + let board = self.board.clone(); let cursor = self.cursor_location.clone(); let drawing_area = self.drawing_area.clone(); motion_controller.connect_motion(move |_, x, y| { - let goban = goban.borrow(); + let board = board.borrow(); let mut cursor = cursor.borrow_mut(); - let hspace_between = ((WIDTH - 40) as f64) / ((goban.size.width - 1) as f64); - let vspace_between = ((HEIGHT - 40) as f64) / ((goban.size.height - 1) as f64); + let hspace_between = ((WIDTH - 40) as f64) / ((board.size.width - 1) as f64); + let vspace_between = ((HEIGHT - 40) as f64) / ((board.size.height - 1) as f64); - let addr = Addr { - column: ((x.round() - 20.) / hspace_between).round() as u8, - row: ((y.round() - 20.) / vspace_between).round() as u8, + let addr = if x.round() < MARGIN as f64 + || x.round() > (WIDTH - MARGIN) as f64 + || y.round() < MARGIN as f64 + || y.round() > (HEIGHT - MARGIN) as f64 + { + None + } else { + Some(Addr { + column: ((x.round() - MARGIN as f64) / hspace_between).round() as u8, + row: ((y.round() - MARGIN as f64) / vspace_between).round() as u8, + }) }; - if *cursor != addr { + if *cursor != addr.clone() { *cursor = addr; drawing_area.queue_draw(); } }); } + let gesture = gtk::GestureClick::new(); + { + let board = self.board.clone(); + let cursor = self.cursor_location.clone(); + let api = self.api.clone(); + gesture.connect_released(move |_, _, _, _| { + let board = board.borrow(); + let cursor = cursor.borrow(); + match *cursor { + None => {} + Some(ref cursor) => match board.stone(cursor.row, cursor.column) { + IntersectionElement::Empty(request) => { + println!("need to send request: {:?}", request); + api.borrow() + .as_ref() + .expect("API must exist") + .dispatch(request); + } + _ => {} + }, + } + }); + } + self.drawing_area.add_controller(motion_controller); + self.drawing_area.add_controller(gesture); } } -impl WidgetImpl for GobanPrivate {} -impl GridImpl for GobanPrivate {} +impl WidgetImpl for BoardPrivate {} +impl GridImpl for BoardPrivate {} glib::wrapper! { - pub struct Goban(ObjectSubclass) @extends gtk::Grid, gtk::Widget; + pub struct Board(ObjectSubclass) @extends gtk::Grid, gtk::Widget; } -impl Goban { - pub fn new() -> Self { +impl Board { + pub fn new(api: CoreApi) -> Self { let s: Self = Object::builder().build(); + + *s.imp().api.borrow_mut() = Some(api); s.attach(&s.imp().drawing_area, 1, 1, 1, 1); + s } - pub fn set_board(&self, goban: GobanElement) { - *self.imp().goban.borrow_mut() = goban; + pub fn set_board(&self, board: BoardElement) { + *self.imp().board.borrow_mut() = board; self.imp().drawing_area.queue_draw(); } @@ -192,7 +263,7 @@ impl Pen { context.arc( self.x_offset + (col as f64) * self.hspace_between, self.y_offset + (row as f64) * self.vspace_between, - 10., + 5., 0., 2. * std::f64::consts::PI, ); @@ -204,14 +275,7 @@ impl Pen { Color::White => context.set_source_rgb(0.9, 0.9, 0.9), Color::Black => context.set_source_rgb(0.0, 0.0, 0.0), }; - context.arc( - 20.0 + (col as f64) * self.hspace_between, - 20.0 + (row as f64) * self.vspace_between, - 25.0, - 0.0, - 2.0 * std::f64::consts::PI, - ); - let _ = context.fill(); + self.draw_stone(context, row, col); } fn ghost_stone(&self, context: &cairo::Context, row: u8, col: u8, color: Color) { @@ -219,10 +283,15 @@ impl Pen { Color::White => context.set_source_rgba(0.9, 0.9, 0.9, 0.5), Color::Black => context.set_source_rgba(0.0, 0.0, 0.0, 0.5), }; + self.draw_stone(context, row, col); + } + + fn draw_stone(&self, context: &cairo::Context, row: u8, col: u8) { + let radius = self.hspace_between / 2. - 2.; context.arc( - 20.0 + (col as f64) * self.hspace_between, - 20.0 + (row as f64) * self.vspace_between, - 25.0, + self.x_offset + (col as f64) * self.hspace_between, + self.y_offset + (row as f64) * self.vspace_between, + radius, 0.0, 2.0 * std::f64::consts::PI, ); diff --git a/kifu/kifu-gtk/src/ui/mod.rs b/kifu/kifu-gtk/src/ui/mod.rs index 49630c7..6a469b3 100644 --- a/kifu/kifu-gtk/src/ui/mod.rs +++ b/kifu/kifu-gtk/src/ui/mod.rs @@ -7,8 +7,8 @@ pub use chat::Chat; mod playing_field; pub use playing_field::PlayingField; -mod goban; -pub use goban::Goban; +mod board; +pub use board::Board; #[cfg(feature = "screenplay")] pub use playing_field::playing_field_view; diff --git a/kifu/kifu-gtk/src/ui/player_card.rs b/kifu/kifu-gtk/src/ui/player_card.rs index 04e10e2..c10012c 100644 --- a/kifu/kifu-gtk/src/ui/player_card.rs +++ b/kifu/kifu-gtk/src/ui/player_card.rs @@ -1,6 +1,6 @@ use glib::Object; use gtk::{prelude::*, subclass::prelude::*}; -use kifu_core::{ui::PlayerCardElement, Color}; +use kifu_core::ui::PlayerCardElement; #[derive(Default)] pub struct PlayerCardPrivate { diff --git a/kifu/kifu-gtk/src/ui/playing_field.rs b/kifu/kifu-gtk/src/ui/playing_field.rs index 7ca1589..afcb93d 100644 --- a/kifu/kifu-gtk/src/ui/playing_field.rs +++ b/kifu/kifu-gtk/src/ui/playing_field.rs @@ -1,29 +1,12 @@ -use crate::ui::{Chat, Goban, PlayerCard}; +use crate::ui::{Board, Chat, PlayerCard}; +use crate::CoreApi; use glib::Object; use gtk::{prelude::*, subclass::prelude::*}; -use kifu_core::{ - ui::{ - ChatElement, GobanElement, PlayerCardElement, PlayingFieldView, StoneElement, - TextFieldElement, - }, - Color, Size, -}; +use kifu_core::ui::PlayingFieldView; use std::{cell::RefCell, rc::Rc}; -/* -#[derive(Clone, Debug)] -pub struct PlayingFieldView { - pub board: types::GobanElement, - pub player_card_black: types::PlayerCardElement, - pub player_card_white: types::PlayerCardElement, - pub chat: types::ChatElement, - pub message: types::TextFieldElement, - pub current_player: Color, -} -*/ - pub struct PlayingFieldPrivate { - goban: Goban, + board: Rc>>, player_card_white: Rc>>, player_card_black: Rc>>, chat: Rc>>, @@ -32,7 +15,7 @@ pub struct PlayingFieldPrivate { impl Default for PlayingFieldPrivate { fn default() -> Self { Self { - goban: Goban::new(), + board: Default::default(), player_card_white: Rc::new(RefCell::new(None)), player_card_black: Rc::new(RefCell::new(None)), chat: Rc::new(RefCell::new(None)), @@ -45,15 +28,6 @@ impl ObjectSubclass for PlayingFieldPrivate { const NAME: &'static str = "PlayingField"; type Type = PlayingField; type ParentType = gtk::Grid; - - fn new() -> Self { - Self { - goban: Goban::new(), - player_card_white: Rc::new(RefCell::new(None)), - player_card_black: Rc::new(RefCell::new(None)), - chat: Rc::new(RefCell::new(None)), - } - } } impl ObjectImpl for PlayingFieldPrivate {} @@ -65,16 +39,19 @@ glib::wrapper! { } impl PlayingField { - pub fn new(view: PlayingFieldView) -> PlayingField { + pub fn new(api: CoreApi, view: PlayingFieldView) -> PlayingField { let s: Self = Object::builder().build(); let player_card_white = PlayerCard::new(view.player_card_white); let player_card_black = PlayerCard::new(view.player_card_black); let chat = Chat::new(view.chat); - s.imp().goban.set_board(view.board); - - s.attach(&s.imp().goban, 1, 1, 1, 2); + *s.imp().board.borrow_mut() = Some(Board::new(api)); + s.imp() + .board + .borrow() + .as_ref() + .map(|board| s.attach(board, 1, 1, 1, 2)); s.attach(&player_card_black, 2, 1, 1, 1); s.attach(&player_card_white, 3, 1, 1, 1); s.attach(&chat, 2, 2, 2, 1); @@ -82,17 +59,32 @@ impl PlayingField { *s.imp().player_card_white.borrow_mut() = Some(player_card_white); *s.imp().player_card_black.borrow_mut() = Some(player_card_black); *s.imp().chat.borrow_mut() = Some(chat); - s.imp().goban.set_current_player(view.current_player); + + s.imp().board.borrow().as_ref().map(|board| { + board.set_board(view.board); + board.set_current_player(view.current_player); + }); s } + + pub fn update_view(&self, view: PlayingFieldView) { + self.imp().board.borrow().as_ref().map(|board| { + board.set_board(view.board); + board.set_current_player(view.current_player); + }); + } } #[cfg(feature = "screenplay")] pub fn playing_field_view() -> PlayingFieldView { - let mut board = GobanElement::default(); - *board.stone_mut(4, 4) = Some(StoneElement::new(Color::White)); - *board.stone_mut(15, 15) = Some(StoneElement::new(Color::Black)); + let mut board = BoardElement::new(Size { + width: 19, + height: 19, + }); + *board.stone_mut(4, 4) = IntersectionElement::Filled(StoneElement::new(Color::White)); + *board.stone_mut(15, 15) = IntersectionElement::Filled(StoneElement::new(Color::Black)); + *board.stone_mut(18, 18) = IntersectionElement::Unplayable; let player_card_black = PlayerCardElement { color: Color::Black, name: "Savanni".to_owned(), diff --git a/rust-toolchain.toml b/rust-toolchain.toml deleted file mode 100644 index 1cae25b..0000000 --- a/rust-toolchain.toml +++ /dev/null @@ -1,3 +0,0 @@ -[toolchain] -channel = "1.68.2" -components = ["rust-src"]