Start on the NewGame page
This commit is contained in:
parent
6f6579b7a7
commit
ef045117be
|
@ -1,5 +1,5 @@
|
||||||
use crate::types::AppState;
|
use crate::types::AppState;
|
||||||
use crate::ui::{playing_field, PlayingFieldView};
|
use crate::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;
|
||||||
|
@ -8,8 +8,12 @@ use typeshare::typeshare;
|
||||||
#[typeshare]
|
#[typeshare]
|
||||||
#[serde(tag = "type", content = "content")]
|
#[serde(tag = "type", content = "content")]
|
||||||
pub enum CoreRequest {
|
pub enum CoreRequest {
|
||||||
|
// CreateGameRequest(CreateGameRequest),
|
||||||
|
LaunchScreen,
|
||||||
|
NewGame,
|
||||||
PlayingField,
|
PlayingField,
|
||||||
PlayStoneRequest(PlayStoneRequest),
|
PlayStoneRequest(PlayStoneRequest),
|
||||||
|
StartGame,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
@ -23,6 +27,7 @@ pub struct PlayStoneRequest {
|
||||||
#[typeshare]
|
#[typeshare]
|
||||||
#[serde(tag = "type", content = "content")]
|
#[serde(tag = "type", content = "content")]
|
||||||
pub enum CoreResponse {
|
pub enum CoreResponse {
|
||||||
|
NewGameView(NewGameView),
|
||||||
PlayingFieldView(PlayingFieldView),
|
PlayingFieldView(PlayingFieldView),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,6 +45,23 @@ impl CoreApp {
|
||||||
|
|
||||||
pub async fn dispatch(&self, request: CoreRequest) -> CoreResponse {
|
pub async fn dispatch(&self, request: CoreRequest) -> CoreResponse {
|
||||||
match request {
|
match request {
|
||||||
|
/*
|
||||||
|
CoreRequest::LaunchScreen => {
|
||||||
|
let app_state = self.state.read().unwrap();
|
||||||
|
|
||||||
|
At launch, I want to either show a list of games in progress, the current game, or the game creation screen.
|
||||||
|
- if a live game is in progress, immmediately go to that game. Such a game will be classified at game creation, so it should be persisted to the state.
|
||||||
|
- if no live games are in progress, but there are slow games in progress, show a list of the slow games and let the player choose which one to jump into.
|
||||||
|
- if no games are in progress, show only the game creation screen
|
||||||
|
- game creation menu should be present both when there are only slow games and when there are no games
|
||||||
|
- the UI returned here will always be available in other places, such as when the user is viewing a game and wants to return to this page
|
||||||
|
|
||||||
|
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::LaunchScreen => CoreResponse::NewGameView(new_game()),
|
||||||
|
CoreRequest::NewGame => CoreResponse::NewGameView(new_game()),
|
||||||
CoreRequest::PlayingField => {
|
CoreRequest::PlayingField => {
|
||||||
let app_state = self.state.read().unwrap();
|
let app_state = self.state.read().unwrap();
|
||||||
let game = app_state.game.as_ref().unwrap();
|
let game = app_state.game.as_ref().unwrap();
|
||||||
|
@ -52,6 +74,9 @@ 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::StartGame => {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -65,8 +65,9 @@ impl AppState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
enum Rank {
|
#[typeshare]
|
||||||
|
pub enum Rank {
|
||||||
Kyu(u8),
|
Kyu(u8),
|
||||||
Dan(u8),
|
Dan(u8),
|
||||||
Pro(u8),
|
Pro(u8),
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
use crate::{
|
||||||
|
ui::types;
|
||||||
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use typeshare::typeshare;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub enum LaunchScreenView {
|
||||||
|
CreateGame(CreateGameView)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This will be called when the Kifu application starts.
|
||||||
|
pub fn launch_screen() -> LaunchScreenView {
|
||||||
|
}
|
|
@ -1,6 +1,12 @@
|
||||||
mod playing_field;
|
mod playing_field;
|
||||||
pub use playing_field::{playing_field, PlayingFieldView};
|
pub use playing_field::{playing_field, PlayingFieldView};
|
||||||
|
|
||||||
|
// mod launch_screen;
|
||||||
|
// pub use launch_screen::{launch_screen, LaunchScreenView};
|
||||||
|
|
||||||
|
mod new_game;
|
||||||
|
pub use new_game::{new_game, Menu, NewGameView, Player};
|
||||||
|
|
||||||
mod types;
|
mod types;
|
||||||
pub use types::{
|
pub use types::{
|
||||||
BoardElement, ChatElement, IntersectionElement, PlayerCardElement, StoneElement,
|
BoardElement, ChatElement, IntersectionElement, PlayerCardElement, StoneElement,
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
use crate::types::Rank;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use typeshare::typeshare;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
#[typeshare]
|
||||||
|
pub struct Menu<T: PartialEq + Eq> {
|
||||||
|
current: Option<T>,
|
||||||
|
options: Vec<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: PartialEq + Eq> Menu<T> {
|
||||||
|
pub fn new(options: Vec<T>, current: Option<T>) -> Menu<T> {
|
||||||
|
if let Some(ref current) = current {
|
||||||
|
assert!(options.contains(current));
|
||||||
|
}
|
||||||
|
Menu { current, options }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
#[typeshare]
|
||||||
|
pub enum Player {
|
||||||
|
Hotseat(HotseatPlayer),
|
||||||
|
Remote(RemotePlayer),
|
||||||
|
Bot(BotPlayer),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Player {
|
||||||
|
fn default() -> Player {
|
||||||
|
Player::Hotseat(HotseatPlayer::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, Default)]
|
||||||
|
#[typeshare]
|
||||||
|
pub struct HotseatPlayer {
|
||||||
|
pub name: Option<String>,
|
||||||
|
pub rank: Option<Rank>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
#[typeshare]
|
||||||
|
pub struct RemotePlayer {}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
#[typeshare]
|
||||||
|
pub struct BotPlayer {}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
#[typeshare]
|
||||||
|
pub struct NewGameView {
|
||||||
|
pub black_player: Player,
|
||||||
|
pub white_player: Player,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_game() -> NewGameView {
|
||||||
|
let black_player = Player::Hotseat(HotseatPlayer {
|
||||||
|
name: Some("black_player".to_owned()),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
let white_player = Player::Hotseat(HotseatPlayer {
|
||||||
|
name: Some("white_player".to_owned()),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
NewGameView {
|
||||||
|
black_player,
|
||||||
|
white_player,
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,46 @@
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
use kifu_core::{CoreApp, CoreRequest, CoreResponse};
|
use kifu_core::{CoreApp, CoreRequest, CoreResponse};
|
||||||
use kifu_gtk::{ui::PlayingField, CoreApi};
|
use kifu_gtk::{
|
||||||
|
ui::{NewGame, PlayingField},
|
||||||
|
CoreApi,
|
||||||
|
};
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
|
fn perftrace<F, A>(trace_name: &str, f: F) -> A
|
||||||
|
where
|
||||||
|
F: FnOnce() -> A,
|
||||||
|
{
|
||||||
|
let start = std::time::Instant::now();
|
||||||
|
let result = f();
|
||||||
|
let end = std::time::Instant::now();
|
||||||
|
println!("[Trace: {}] {:?}", trace_name, end - start);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_response(api: CoreApi, window: gtk::ApplicationWindow, message: CoreResponse) {
|
||||||
|
let playing_field = Arc::new(RwLock::new(None));
|
||||||
|
match message {
|
||||||
|
CoreResponse::NewGameView(view) => {
|
||||||
|
let api = api.clone();
|
||||||
|
|
||||||
|
let new_game = NewGame::new(api, view);
|
||||||
|
window.set_child(Some(&new_game));
|
||||||
|
}
|
||||||
|
CoreResponse::PlayingFieldView(view) => {
|
||||||
|
let api = api.clone();
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
gio::resources_register_include!("com.luminescent-dreams.kifu-gtk.gresource")
|
gio::resources_register_include!("com.luminescent-dreams.kifu-gtk.gresource")
|
||||||
.expect("Failed to register resources");
|
.expect("Failed to register resources");
|
||||||
|
@ -44,30 +82,15 @@ fn main() {
|
||||||
|
|
||||||
gtk_rx.attach(None, {
|
gtk_rx.attach(None, {
|
||||||
let api = api.clone();
|
let api = api.clone();
|
||||||
let playing_field = Arc::new(RwLock::new(None));
|
|
||||||
move |message| {
|
move |message| {
|
||||||
match message {
|
perftrace("handle_response", || {
|
||||||
CoreResponse::PlayingFieldView(view) => {
|
handle_response(api.clone(), window.clone(), message)
|
||||||
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(CoreRequest::PlayingField);
|
api.dispatch(CoreRequest::NewGame);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,9 @@ pub use chat::Chat;
|
||||||
mod playing_field;
|
mod playing_field;
|
||||||
pub use playing_field::PlayingField;
|
pub use playing_field::PlayingField;
|
||||||
|
|
||||||
|
mod new_game;
|
||||||
|
pub use new_game::NewGame;
|
||||||
|
|
||||||
mod board;
|
mod board;
|
||||||
pub use board::Board;
|
pub use board::Board;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
use crate::CoreApi;
|
||||||
|
use glib::Object;
|
||||||
|
use gtk::{prelude::*, subclass::prelude::*};
|
||||||
|
use kifu_core::ui::{NewGameView, Player};
|
||||||
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
|
pub struct NewGamePrivate {
|
||||||
|
black_player: gtk::Label,
|
||||||
|
white_player: gtk::Label,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for NewGamePrivate {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
black_player: gtk::Label::new(None),
|
||||||
|
white_player: gtk::Label::new(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[glib::object_subclass]
|
||||||
|
impl ObjectSubclass for NewGamePrivate {
|
||||||
|
const NAME: &'static str = "NewGame";
|
||||||
|
type Type = NewGame;
|
||||||
|
type ParentType = gtk::Grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ObjectImpl for NewGamePrivate {}
|
||||||
|
impl WidgetImpl for NewGamePrivate {}
|
||||||
|
impl GridImpl for NewGamePrivate {}
|
||||||
|
|
||||||
|
glib::wrapper! {
|
||||||
|
pub struct NewGame(ObjectSubclass<NewGamePrivate>) @extends gtk::Grid, gtk::Widget;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NewGame {
|
||||||
|
pub fn new(_api: CoreApi, view: NewGameView) -> NewGame {
|
||||||
|
let s: Self = Object::builder().build();
|
||||||
|
|
||||||
|
match view.black_player {
|
||||||
|
Player::Hotseat(player) => {
|
||||||
|
if let Some(name) = player.name {
|
||||||
|
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 {
|
||||||
|
Player::Hotseat(player) => {
|
||||||
|
if let Some(name) = player.name {
|
||||||
|
s.imp().white_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().white_player, 2, 1, 1, 1);
|
||||||
|
|
||||||
|
s
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue