Create a launch screen that shows player name and rank
This commit is contained in:
parent
32ed1a2464
commit
d0db274110
|
@ -1,18 +1,20 @@
|
||||||
use crate::types::AppState;
|
use crate::{
|
||||||
use crate::ui::{new_game, playing_field, NewGameView, PlayingFieldView};
|
types::{AppState, Rank},
|
||||||
|
ui::{new_game, playing_field, NewGameView, PlayingFieldView},
|
||||||
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
use typeshare::typeshare;
|
use typeshare::typeshare;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
#[typeshare]
|
#[typeshare]
|
||||||
#[serde(tag = "type", content = "content")]
|
#[serde(tag = "type", content = "content")]
|
||||||
pub enum CoreRequest {
|
pub enum CoreRequest {
|
||||||
// CreateGameRequest(CreateGameRequest),
|
CreateGame(CreateGameRequest),
|
||||||
LaunchScreen,
|
LaunchScreen,
|
||||||
NewGame,
|
NewGame,
|
||||||
PlayingField,
|
PlayingField,
|
||||||
PlayStoneRequest(PlayStoneRequest),
|
PlayStone(PlayStoneRequest),
|
||||||
StartGame,
|
StartGame,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,6 +25,26 @@ pub struct PlayStoneRequest {
|
||||||
pub row: u8,
|
pub row: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
#[typeshare]
|
||||||
|
pub struct CreateGameRequest {
|
||||||
|
black_player: PlayerInfoRequest,
|
||||||
|
white_player: PlayerInfoRequest,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
#[typeshare]
|
||||||
|
pub enum PlayerInfoRequest {
|
||||||
|
Hotseat(HotseatPlayerRequest),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
#[typeshare]
|
||||||
|
pub struct HotseatPlayerRequest {
|
||||||
|
name: Option<String>,
|
||||||
|
rank: Option<Rank>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
#[typeshare]
|
#[typeshare]
|
||||||
#[serde(tag = "type", content = "content")]
|
#[serde(tag = "type", content = "content")]
|
||||||
|
@ -59,7 +81,11 @@ 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.
|
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::LaunchScreen => CoreResponse::NewGameView(new_game()),
|
CoreRequest::CreateGame(_) => {
|
||||||
|
let app_state = self.state.write().unwrap();
|
||||||
|
let game = app_state.game.as_ref().unwrap();
|
||||||
|
CoreResponse::PlayingFieldView(playing_field(game))
|
||||||
|
}
|
||||||
CoreRequest::LaunchScreen => CoreResponse::NewGameView(new_game()),
|
CoreRequest::LaunchScreen => CoreResponse::NewGameView(new_game()),
|
||||||
CoreRequest::NewGame => CoreResponse::NewGameView(new_game()),
|
CoreRequest::NewGame => CoreResponse::NewGameView(new_game()),
|
||||||
CoreRequest::PlayingField => {
|
CoreRequest::PlayingField => {
|
||||||
|
@ -67,7 +93,7 @@ impl CoreApp {
|
||||||
let game = app_state.game.as_ref().unwrap();
|
let game = app_state.game.as_ref().unwrap();
|
||||||
CoreResponse::PlayingFieldView(playing_field(game))
|
CoreResponse::PlayingFieldView(playing_field(game))
|
||||||
}
|
}
|
||||||
CoreRequest::PlayStoneRequest(request) => {
|
CoreRequest::PlayStone(request) => {
|
||||||
let mut app_state = self.state.write().unwrap();
|
let mut app_state = self.state.write().unwrap();
|
||||||
app_state.place_stone(request);
|
app_state.place_stone(request);
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ mod api;
|
||||||
pub use api::{CoreApp, CoreRequest, CoreResponse};
|
pub use api::{CoreApp, CoreRequest, CoreResponse};
|
||||||
|
|
||||||
mod types;
|
mod types;
|
||||||
pub use types::{BoardError, Color, Size};
|
pub use types::{BoardError, Color, Rank, Size};
|
||||||
pub mod ui;
|
pub mod ui;
|
||||||
|
|
||||||
mod board;
|
mod board;
|
||||||
|
|
|
@ -65,7 +65,7 @@ impl AppState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
#[typeshare]
|
#[typeshare]
|
||||||
pub enum Rank {
|
pub enum Rank {
|
||||||
Kyu(u8),
|
Kyu(u8),
|
||||||
|
|
|
@ -5,7 +5,7 @@ pub use playing_field::{playing_field, PlayingFieldView};
|
||||||
// pub use launch_screen::{launch_screen, LaunchScreenView};
|
// pub use launch_screen::{launch_screen, LaunchScreenView};
|
||||||
|
|
||||||
mod new_game;
|
mod new_game;
|
||||||
pub use new_game::{new_game, Menu, NewGameView, Player};
|
pub use new_game::{new_game, HotseatPlayerElement, Menu, NewGameView, PlayerElement};
|
||||||
|
|
||||||
mod types;
|
mod types;
|
||||||
pub use types::{
|
pub use types::{
|
||||||
|
|
|
@ -1,7 +1,20 @@
|
||||||
use crate::types::Rank;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use typeshare::typeshare;
|
use typeshare::typeshare;
|
||||||
|
|
||||||
|
fn rank_strings() -> Vec<String> {
|
||||||
|
vec![
|
||||||
|
"", "30 kyu", "29 kyu", "28 kyu", "27 kyu", "26 kyu", "25 kyu", "24 kyu", "23 kyu",
|
||||||
|
"22 kyu", "21 kyu", "20 kyu", "19 kyu", "18 kyu", "17 kyu", "16 kyu", "15 kyu", "14 kyu",
|
||||||
|
"13 kyu", "12 kyu", "11 kyu", "10 kyu", "9 kyu", "8 kyu", "7 kyu", "6 kyu", "5 kyu",
|
||||||
|
"4 kyu", "3 kyu", "2 kyu", "1 kyu", "1 dan", "2 dan", "3 dan", "4 dan", "5 dan", "6 dan",
|
||||||
|
"7 dan", "8 dan", "9 dan", "1 pro", "2 pro", "3 pro", "4 pro", "5 pro", "6 pro", "7 pro",
|
||||||
|
"8 pro", "9 pro",
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.map(|s| s.to_owned())
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
#[typeshare]
|
#[typeshare]
|
||||||
pub struct Menu<T: PartialEq + Eq> {
|
pub struct Menu<T: PartialEq + Eq> {
|
||||||
|
@ -20,48 +33,51 @@ impl<T: PartialEq + Eq> Menu<T> {
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
#[typeshare]
|
#[typeshare]
|
||||||
pub enum Player {
|
pub enum PlayerElement {
|
||||||
Hotseat(HotseatPlayer),
|
Hotseat(HotseatPlayerElement),
|
||||||
Remote(RemotePlayer),
|
// Remote(RemotePlayerElement),
|
||||||
Bot(BotPlayer),
|
// Bot(BotPlayerElement),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Player {
|
impl Default for PlayerElement {
|
||||||
fn default() -> Player {
|
fn default() -> PlayerElement {
|
||||||
Player::Hotseat(HotseatPlayer::default())
|
PlayerElement::Hotseat(HotseatPlayerElement::default())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, Default)]
|
#[derive(Clone, Debug, Serialize, Deserialize, Default)]
|
||||||
#[typeshare]
|
#[typeshare]
|
||||||
pub struct HotseatPlayer {
|
pub struct HotseatPlayerElement {
|
||||||
pub name: Option<String>,
|
pub placeholder: Option<String>,
|
||||||
pub rank: Option<Rank>,
|
pub default_rank: Option<String>,
|
||||||
|
pub ranks: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
#[typeshare]
|
#[typeshare]
|
||||||
pub struct RemotePlayer {}
|
pub struct RemotePlayerElement {}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
#[typeshare]
|
#[typeshare]
|
||||||
pub struct BotPlayer {}
|
pub struct BotPlayerElement {}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
#[typeshare]
|
#[typeshare]
|
||||||
pub struct NewGameView {
|
pub struct NewGameView {
|
||||||
pub black_player: Player,
|
pub black_player: PlayerElement,
|
||||||
pub white_player: Player,
|
pub white_player: PlayerElement,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_game() -> NewGameView {
|
pub fn new_game() -> NewGameView {
|
||||||
let black_player = Player::Hotseat(HotseatPlayer {
|
let black_player = PlayerElement::Hotseat(HotseatPlayerElement {
|
||||||
name: Some("black_player".to_owned()),
|
placeholder: Some("black player".to_owned()),
|
||||||
..Default::default()
|
default_rank: None,
|
||||||
|
ranks: rank_strings(),
|
||||||
});
|
});
|
||||||
let white_player = Player::Hotseat(HotseatPlayer {
|
let white_player = PlayerElement::Hotseat(HotseatPlayerElement {
|
||||||
name: Some("white_player".to_owned()),
|
placeholder: Some("white player".to_owned()),
|
||||||
..Default::default()
|
default_rank: None,
|
||||||
|
ranks: rank_strings(),
|
||||||
});
|
});
|
||||||
NewGameView {
|
NewGameView {
|
||||||
black_player,
|
black_player,
|
||||||
|
|
|
@ -31,7 +31,7 @@ impl StoneElement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
#[typeshare]
|
#[typeshare]
|
||||||
#[serde(tag = "type", content = "content")]
|
#[serde(tag = "type", content = "content")]
|
||||||
pub enum IntersectionElement {
|
pub enum IntersectionElement {
|
||||||
|
@ -53,9 +53,10 @@ impl BoardElement {
|
||||||
.map(|row| {
|
.map(|row| {
|
||||||
(0..size.width)
|
(0..size.width)
|
||||||
.map(|column| {
|
.map(|column| {
|
||||||
IntersectionElement::Empty(CoreRequest::PlayStoneRequest(
|
IntersectionElement::Empty(CoreRequest::PlayStone(PlayStoneRequest {
|
||||||
PlayStoneRequest { column, row },
|
column,
|
||||||
))
|
row,
|
||||||
|
}))
|
||||||
})
|
})
|
||||||
.collect::<Vec<IntersectionElement>>()
|
.collect::<Vec<IntersectionElement>>()
|
||||||
})
|
})
|
||||||
|
@ -66,7 +67,12 @@ impl BoardElement {
|
||||||
|
|
||||||
pub fn stone(&self, column: u8, row: u8) -> IntersectionElement {
|
pub fn stone(&self, column: u8, row: u8) -> IntersectionElement {
|
||||||
let addr = self.addr(column, row);
|
let addr = self.addr(column, row);
|
||||||
self.spaces[addr]
|
self.spaces[addr].clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stone_mut(&mut self, column: u8, row: u8) -> &mut IntersectionElement {
|
||||||
|
let addr = self.addr(column, row);
|
||||||
|
&mut self.spaces[addr]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn addr(&self, column: u8, row: u8) -> usize {
|
fn addr(&self, column: u8, row: u8) -> usize {
|
||||||
|
@ -76,23 +82,24 @@ impl BoardElement {
|
||||||
|
|
||||||
impl From<&Board> for BoardElement {
|
impl From<&Board> for BoardElement {
|
||||||
fn from(board: &Board) -> Self {
|
fn from(board: &Board) -> Self {
|
||||||
let spaces: Vec<IntersectionElement> = (0..board.size.height)
|
let spaces: Vec<IntersectionElement> =
|
||||||
.map(|row| {
|
(0..board.size.height)
|
||||||
(0..board.size.width)
|
.map(|row| {
|
||||||
.map(|column| match board.stone(&Coordinate { column, row }) {
|
(0..board.size.width)
|
||||||
Some(color) => IntersectionElement::Filled(StoneElement {
|
.map(|column| match board.stone(&Coordinate { column, row }) {
|
||||||
jitter: Jitter { x: 0, y: 0 },
|
Some(color) => IntersectionElement::Filled(StoneElement {
|
||||||
liberties: None,
|
jitter: Jitter { x: 0, y: 0 },
|
||||||
color,
|
liberties: None,
|
||||||
}),
|
color,
|
||||||
None => IntersectionElement::Empty(CoreRequest::PlayStoneRequest(
|
}),
|
||||||
PlayStoneRequest { column, row },
|
None => IntersectionElement::Empty(CoreRequest::PlayStone(
|
||||||
)),
|
PlayStoneRequest { column, row },
|
||||||
})
|
)),
|
||||||
.collect::<Vec<IntersectionElement>>()
|
})
|
||||||
})
|
.collect::<Vec<IntersectionElement>>()
|
||||||
.collect::<Vec<Vec<IntersectionElement>>>()
|
})
|
||||||
.concat();
|
.collect::<Vec<Vec<IntersectionElement>>>()
|
||||||
|
.concat();
|
||||||
Self {
|
Self {
|
||||||
size: board.size,
|
size: board.size,
|
||||||
spaces,
|
spaces,
|
||||||
|
|
|
@ -1,19 +1,74 @@
|
||||||
use crate::CoreApi;
|
use crate::CoreApi;
|
||||||
use glib::Object;
|
use glib::{subclass, Object, ParamSpec, Properties, Value};
|
||||||
use gtk::{prelude::*, subclass::prelude::*};
|
use gtk::{glib, prelude::*, subclass::prelude::*};
|
||||||
use kifu_core::ui::{NewGameView, Player};
|
use kifu_core::ui::{NewGameView, PlayerElement};
|
||||||
use std::{cell::RefCell, rc::Rc};
|
use std::{cell::Cell, cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
|
struct PlayerDataEntryPrivate {
|
||||||
|
placeholder: gtk::Text,
|
||||||
|
rank: gtk::DropDown,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for PlayerDataEntryPrivate {
|
||||||
|
fn default() -> Self {
|
||||||
|
let rank = gtk::DropDown::builder().build();
|
||||||
|
Self {
|
||||||
|
placeholder: gtk::Text::builder().build(),
|
||||||
|
rank,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[glib::object_subclass]
|
||||||
|
impl ObjectSubclass for PlayerDataEntryPrivate {
|
||||||
|
const NAME: &'static str = "PlayerDataEntry";
|
||||||
|
type Type = PlayerDataEntry;
|
||||||
|
type ParentType = gtk::Box;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ObjectImpl for PlayerDataEntryPrivate {}
|
||||||
|
impl WidgetImpl for PlayerDataEntryPrivate {}
|
||||||
|
impl BoxImpl for PlayerDataEntryPrivate {}
|
||||||
|
|
||||||
|
glib::wrapper! {
|
||||||
|
struct PlayerDataEntry(ObjectSubclass<PlayerDataEntryPrivate>) @extends gtk::Box, gtk::Widget;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlayerDataEntry {
|
||||||
|
pub fn new(element: PlayerElement) -> PlayerDataEntry {
|
||||||
|
let s: Self = Object::builder().build();
|
||||||
|
|
||||||
|
let rank_model = gio::ListStore::new(gtk::StringObject::static_type());
|
||||||
|
s.imp().rank.set_model(Some(&rank_model));
|
||||||
|
|
||||||
|
match element {
|
||||||
|
PlayerElement::Hotseat(player) => {
|
||||||
|
if let Some(placeholder) = player.placeholder {
|
||||||
|
s.imp().placeholder.set_placeholder_text(Some(&placeholder));
|
||||||
|
}
|
||||||
|
player.ranks.iter().for_each(|rank| rank_model.append(>k::StringObject::new(rank)));
|
||||||
|
}
|
||||||
|
// PlayerElement::Remote(_) => s.imp().placeholder.set_text("remote player"),
|
||||||
|
// PlayerElement::Bot(_) => s.imp().placeholder.set_text("bot player"),
|
||||||
|
}
|
||||||
|
|
||||||
|
s.append(&s.imp().placeholder);
|
||||||
|
s.append(&s.imp().rank);
|
||||||
|
|
||||||
|
s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct NewGamePrivate {
|
pub struct NewGamePrivate {
|
||||||
black_player: gtk::Label,
|
black_player: Rc<RefCell<Option<PlayerDataEntry>>>,
|
||||||
white_player: gtk::Label,
|
white_player: Rc<RefCell<Option<PlayerDataEntry>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for NewGamePrivate {
|
impl Default for NewGamePrivate {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
black_player: gtk::Label::new(None),
|
black_player: Rc::new(RefCell::new(None)),
|
||||||
white_player: gtk::Label::new(None),
|
white_player: Rc::new(RefCell::new(None)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,30 +89,26 @@ glib::wrapper! {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NewGame {
|
impl NewGame {
|
||||||
pub fn new(_api: CoreApi, view: NewGameView) -> NewGame {
|
pub fn new(api: CoreApi, view: NewGameView) -> NewGame {
|
||||||
let s: Self = Object::builder().build();
|
let s: Self = Object::builder().build();
|
||||||
|
|
||||||
match view.black_player {
|
let black_player = PlayerDataEntry::new(view.black_player);
|
||||||
Player::Hotseat(player) => {
|
s.attach(&black_player, 1, 1, 1, 1);
|
||||||
if let Some(name) = player.name {
|
*s.imp().black_player.borrow_mut() = Some(black_player);
|
||||||
s.imp().black_player.set_text(name.as_ref());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Player::Remote(_) => s.imp().black_player.set_text("remote player"),
|
|
||||||
Player::Bot(_) => s.imp().black_player.set_text("bot player"),
|
|
||||||
}
|
|
||||||
s.attach(&s.imp().black_player, 1, 1, 1, 1);
|
|
||||||
|
|
||||||
match view.white_player {
|
let white_player = PlayerDataEntry::new(view.white_player);
|
||||||
Player::Hotseat(player) => {
|
s.attach(&white_player, 2, 1, 1, 1);
|
||||||
if let Some(name) = player.name {
|
*s.imp().white_player.borrow_mut() = Some(white_player);
|
||||||
s.imp().white_player.set_text(name.as_ref());
|
|
||||||
}
|
let new_game_button = gtk::Button::builder().label("Start Game").build();
|
||||||
}
|
s.attach(&new_game_button, 2, 2, 1, 1);
|
||||||
Player::Remote(_) => s.imp().black_player.set_text("remote player"),
|
|
||||||
Player::Bot(_) => s.imp().black_player.set_text("bot player"),
|
/*
|
||||||
}
|
new_game_button.connect_clicked(move |_| {
|
||||||
s.attach(&s.imp().white_player, 2, 1, 1, 1);
|
api.dispatch(CoreRequest::CreatGameRequest(CreateGameRequest {
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue