diff --git a/kifu/core/src/api.rs b/kifu/core/src/api.rs index 864953c..7e4e0d6 100644 --- a/kifu/core/src/api.rs +++ b/kifu/core/src/api.rs @@ -1,5 +1,5 @@ use crate::{ - types::{AppState, Rank}, + types::{AppState, GameState, Player, Rank}, ui::{new_game, playing_field, NewGameView, PlayingFieldView}, }; use serde::{Deserialize, Serialize}; @@ -41,8 +41,17 @@ pub enum PlayerInfoRequest { #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[typeshare] pub struct HotseatPlayerRequest { - pub name: Option, - pub rank: Option, + pub name: String, + pub rank: Option, +} + +impl From for Player { + fn from(p: HotseatPlayerRequest) -> Self { + Self { + name: p.name, + rank: p.rank.and_then(|r| Rank::try_from(r.as_ref()).ok()), + } + } } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -81,10 +90,25 @@ impl CoreApp { For the initial version, I want only to show the game creation screen. Then I will backtrack record application state so that the only decisions can be made. } */ - CoreRequest::CreateGame(_) => { - let app_state = self.state.write().unwrap(); - let game = app_state.game.as_ref().unwrap(); - CoreResponse::PlayingFieldView(playing_field(game)) + CoreRequest::CreateGame(create_request) => { + let mut app_state = self.state.write().unwrap(); + let white_player = { + match create_request.white_player { + PlayerInfoRequest::Hotseat(request) => Player::from(request), + } + }; + let black_player = { + match create_request.black_player { + PlayerInfoRequest::Hotseat(request) => Player::from(request), + } + }; + app_state.game = Some(GameState { + white_player, + black_player, + ..GameState::new() + }); + let game_state = app_state.game.as_ref().unwrap(); + CoreResponse::PlayingFieldView(playing_field(game_state)) } CoreRequest::LaunchScreen => CoreResponse::NewGameView(new_game()), CoreRequest::NewGame => CoreResponse::NewGameView(new_game()), diff --git a/kifu/core/src/types.rs b/kifu/core/src/types.rs index aafabf7..fec9790 100644 --- a/kifu/core/src/types.rs +++ b/kifu/core/src/types.rs @@ -93,8 +93,8 @@ impl From for String { #[derive(Debug)] pub struct Player { - name: String, - rank: Rank, + pub name: String, + pub rank: Option, } #[derive(Debug)] @@ -113,19 +113,19 @@ pub struct GameState { } impl GameState { - fn new() -> GameState { + pub fn new() -> GameState { GameState { board: Board::new(), past_positions: vec![], conversation: vec![], current_player: Color::Black, white_player: Player { - name: "Savanni".to_owned(), - rank: Rank::Kyu(10), + name: "".to_owned(), + rank: None, }, black_player: Player { - name: "Opal".to_owned(), - rank: Rank::Kyu(10), + name: "".to_owned(), + rank: None, }, white_clock: Duration::from_secs(600), black_clock: Duration::from_secs(600), diff --git a/kifu/core/src/ui/playing_field.rs b/kifu/core/src/ui/playing_field.rs index 2e8592c..d5ed200 100644 --- a/kifu/core/src/ui/playing_field.rs +++ b/kifu/core/src/ui/playing_field.rs @@ -21,16 +21,24 @@ pub fn playing_field(game: &GameState) -> PlayingFieldView { let player_card_black = types::PlayerCardElement { color: Color::Black, - name: "Savanni".to_owned(), - rank: "10k".to_owned(), - clock: "24:53".to_owned(), + name: game.black_player.name.clone(), + rank: game + .black_player + .rank + .map(String::from) + .unwrap_or("".to_owned()), + clock: "".to_owned(), }; let player_card_white = types::PlayerCardElement { color: Color::White, - name: "Opal".to_owned(), - rank: "10k".to_owned(), - clock: "25:00".to_owned(), + name: game.white_player.name.clone(), + rank: game + .black_player + .rank + .map(String::from) + .unwrap_or("".to_owned()), + clock: "".to_owned(), }; let chat = types::ChatElement { diff --git a/kifu/gtk/src/ui/new_game.rs b/kifu/gtk/src/ui/new_game.rs index df4dbe1..d037ef2 100644 --- a/kifu/gtk/src/ui/new_game.rs +++ b/kifu/gtk/src/ui/new_game.rs @@ -1,14 +1,14 @@ use crate::CoreApi; -use glib::{subclass, Object, ParamSpec, Properties, Value}; +use glib::Object; use gtk::{glib, prelude::*, subclass::prelude::*}; use kifu_core::{ ui::{NewGameView, PlayerElement}, CoreRequest, CreateGameRequest, HotseatPlayerRequest, PlayerInfoRequest, }; -use std::{cell::Cell, cell::RefCell, rc::Rc}; +use std::{cell::RefCell, rc::Rc}; struct PlayerDataEntryPrivate { - placeholder: gtk::Text, + name: gtk::Text, rank: gtk::DropDown, } @@ -16,7 +16,7 @@ impl Default for PlayerDataEntryPrivate { fn default() -> Self { let rank = gtk::DropDown::builder().build(); Self { - placeholder: gtk::Text::builder().build(), + name: gtk::Text::builder().build(), rank, } } @@ -47,7 +47,7 @@ impl PlayerDataEntry { match element { PlayerElement::Hotseat(player) => { if let Some(placeholder) = player.placeholder { - s.imp().placeholder.set_placeholder_text(Some(&placeholder)); + s.imp().name.set_placeholder_text(Some(&placeholder)); } player.ranks.iter().for_each(|rank| rank_model.append(>k::StringObject::new(rank))); } @@ -55,11 +55,31 @@ impl PlayerDataEntry { // PlayerElement::Bot(_) => s.imp().placeholder.set_text("bot player"), } - s.append(&s.imp().placeholder); + s.append(&s.imp().name); s.append(&s.imp().rank); s } + + pub fn text(&self) -> String { + let name = self.imp().name.buffer().text().to_string(); + if name.is_empty() { + self.imp() + .name + .placeholder_text() + .map(|s| s.to_string()) + .unwrap_or("".to_owned()) + } else { + name + } + } + + pub fn rank(&self) -> Option { + self.imp().rank.selected_item().and_then(|obj| { + let str_obj = obj.downcast::().ok()?; + Some(str_obj.string().clone().to_string()) + }) + } } pub struct NewGamePrivate { @@ -97,28 +117,34 @@ impl NewGame { let black_player = PlayerDataEntry::new(view.black_player); s.attach(&black_player, 1, 1, 1, 1); - *s.imp().black_player.borrow_mut() = Some(black_player); + *s.imp().black_player.borrow_mut() = Some(black_player.clone()); let white_player = PlayerDataEntry::new(view.white_player); s.attach(&white_player, 2, 1, 1, 1); - *s.imp().white_player.borrow_mut() = Some(white_player); + *s.imp().white_player.borrow_mut() = Some(white_player.clone()); let new_game_button = gtk::Button::builder().label(&view.start_game.label).build(); s.attach(&new_game_button, 2, 2, 1, 1); - new_game_button.connect_clicked(move |_| { - api.dispatch(CoreRequest::CreateGame(CreateGameRequest { - black_player: PlayerInfoRequest::Hotseat(HotseatPlayerRequest { - name: None, - rank: None, - }), - white_player: PlayerInfoRequest::Hotseat(HotseatPlayerRequest { - name: None, - rank: None, - }), - })); + new_game_button.connect_clicked({ + move |_| { + let black_player = black_player.clone(); + let white_player = white_player.clone(); + + api.dispatch(CoreRequest::CreateGame(CreateGameRequest { + black_player: player_info(black_player.clone()), + white_player: player_info(white_player.clone()), + })); + } }); s } } + +fn player_info(player: PlayerDataEntry) -> PlayerInfoRequest { + PlayerInfoRequest::Hotseat(HotseatPlayerRequest { + name: player.text(), + rank: player.rank(), + }) +}