Compare commits

..

4 Commits

23 changed files with 383 additions and 61 deletions

2
emseries/Cargo.lock generated
View File

@ -71,7 +71,7 @@ dependencies = [
[[package]] [[package]]
name = "emseries" name = "emseries"
version = "0.5.1" version = "0.6.0"
dependencies = [ dependencies = [
"chrono", "chrono",
"chrono-tz", "chrono-tz",

85
go-sgf/Cargo.lock generated
View File

@ -51,6 +51,7 @@ dependencies = [
"iana-time-zone", "iana-time-zone",
"js-sys", "js-sys",
"num-traits", "num-traits",
"serde",
"time", "time",
"wasm-bindgen", "wasm-bindgen",
"winapi", "winapi",
@ -68,7 +69,9 @@ version = "0.1.0"
dependencies = [ dependencies = [
"chrono", "chrono",
"nom", "nom",
"serde",
"thiserror", "thiserror",
"typeshare",
] ]
[[package]] [[package]]
@ -94,6 +97,12 @@ dependencies = [
"cc", "cc",
] ]
[[package]]
name = "itoa"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
[[package]] [[package]]
name = "js-sys" name = "js-sys"
version = "0.3.64" version = "0.3.64"
@ -170,6 +179,54 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "ryu"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
[[package]]
name = "serde"
version = "1.0.164"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.164"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.18",
]
[[package]]
name = "serde_json"
version = "1.0.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46266871c240a00b8f503b877622fe33430b3c7d963bdc0f2adc511e54a1eae3"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.18" version = "2.0.18"
@ -198,7 +255,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.18",
] ]
[[package]] [[package]]
@ -212,6 +269,28 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "typeshare"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f44d1a2f454cb35fbe05b218c410792697e76bd868f48d3a418f2cd1a7d527d6"
dependencies = [
"chrono",
"serde",
"serde_json",
"typeshare-annotation",
]
[[package]]
name = "typeshare-annotation"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc670d0e358428857cc3b4bf504c691e572fccaec9542ff09212d3f13d74b7a9"
dependencies = [
"quote",
"syn 1.0.109",
]
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.9" version = "1.0.9"
@ -245,7 +324,7 @@ dependencies = [
"once_cell", "once_cell",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.18",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -267,7 +346,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.18",
"wasm-bindgen-backend", "wasm-bindgen-backend",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]

View File

@ -6,6 +6,8 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
chrono = { version = "0.4", features = [ "serde" ] }
nom = { version = "7" } nom = { version = "7" }
serde = { version = "1", features = [ "derive" ] }
thiserror = { version = "1"} thiserror = { version = "1"}
chrono = { version = "0.4" } typeshare = { version = "1" }

View File

@ -1,6 +1,8 @@
use chrono::{Datelike, NaiveDate}; use chrono::{Datelike, NaiveDate};
use serde::{Deserialize, Serialize};
use std::num::ParseIntError; use std::num::ParseIntError;
use thiserror::Error; use thiserror::Error;
use typeshare::typeshare;
#[derive(Debug, Error, PartialEq)] #[derive(Debug, Error, PartialEq)]
pub enum Error { pub enum Error {
@ -14,7 +16,8 @@ pub enum Error {
Unsupported, Unsupported,
} }
#[derive(Clone, Debug, PartialEq, PartialOrd)] #[derive(Clone, Debug, PartialEq, PartialOrd, Deserialize, Serialize)]
#[typeshare]
pub enum Date { pub enum Date {
Year(i32), Year(i32),
YearMonth(i32, u32), YearMonth(i32, u32),

View File

@ -72,6 +72,8 @@ use crate::{
date::{self, parse_date_field, Date}, date::{self, parse_date_field, Date},
tree::{parse_collection, ParseSizeError, Size}, tree::{parse_collection, ParseSizeError, Size},
}; };
use serde::{Deserialize, Serialize};
use typeshare::typeshare;
#[derive(Debug)] #[derive(Debug)]
pub enum Error<'a> { pub enum Error<'a> {
@ -97,7 +99,8 @@ impl<'a> From<ParseSizeError> for Error<'a> {
} }
} }
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[typeshare]
pub enum Rank { pub enum Rank {
Kyu(u8), Kyu(u8),
Dan(u8), Dan(u8),
@ -118,6 +121,12 @@ impl TryFrom<&str> for Rank {
} }
} }
impl ToString for Rank {
fn to_string(&self) -> String {
unimplemented!()
}
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct GameTree { pub struct GameTree {
pub file_format: i8, pub file_format: i8,

View File

@ -1,6 +1,10 @@
pub mod date; mod date;
pub mod go; pub use date::Date;
pub mod tree;
mod go;
pub use go::{parse_sgf, GameTree, GameType, Rank};
mod tree;
use thiserror::Error; use thiserror::Error;

3
kifu/core/Cargo.lock generated
View File

@ -45,6 +45,7 @@ dependencies = [
"js-sys", "js-sys",
"num-integer", "num-integer",
"num-traits", "num-traits",
"serde",
"time", "time",
"wasm-bindgen", "wasm-bindgen",
"winapi", "winapi",
@ -125,7 +126,9 @@ version = "0.1.0"
dependencies = [ dependencies = [
"chrono", "chrono",
"nom", "nom",
"serde",
"thiserror", "thiserror",
"typeshare",
] ]
[[package]] [[package]]

View File

@ -1,7 +1,7 @@
use crate::{ use crate::{
types::{AppState, GameState, Player, Rank}, types::{AppState, GameState, Player, Rank},
ui::{new_game, playing_field, NewGameView, PlayingFieldView}, ui::{home, playing_field, HomeView, PlayingFieldView},
Config, Config, DatabasePath,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::sync::{Arc, RwLock}; use std::sync::{Arc, RwLock};
@ -12,8 +12,7 @@ use typeshare::typeshare;
#[serde(tag = "type", content = "content")] #[serde(tag = "type", content = "content")]
pub enum CoreRequest { pub enum CoreRequest {
CreateGame(CreateGameRequest), CreateGame(CreateGameRequest),
LaunchScreen, Home,
NewGame,
PlayingField, PlayingField,
PlayStone(PlayStoneRequest), PlayStone(PlayStoneRequest),
StartGame, StartGame,
@ -59,7 +58,7 @@ impl From<HotseatPlayerRequest> for Player {
#[typeshare] #[typeshare]
#[serde(tag = "type", content = "content")] #[serde(tag = "type", content = "content")]
pub enum CoreResponse { pub enum CoreResponse {
NewGameView(NewGameView), HomeView(HomeView),
PlayingFieldView(PlayingFieldView), PlayingFieldView(PlayingFieldView),
} }
@ -71,8 +70,14 @@ pub struct CoreApp {
impl CoreApp { impl CoreApp {
pub fn new(config_path: std::path::PathBuf) -> Self { pub fn new(config_path: std::path::PathBuf) -> Self {
println!("config_path: {:?}", config_path);
let config = Config::from_path(config_path).expect("configuration to open"); let config = Config::from_path(config_path).expect("configuration to open");
let state = Arc::new(RwLock::new(AppState::new()));
let db_path: DatabasePath = config.get();
let state = Arc::new(RwLock::new(AppState::new(db_path)));
println!("config: {:?}", config);
println!("games database: {:?}", state.read().unwrap().database.len());
Self { config, state } Self { config, state }
} }
@ -113,8 +118,9 @@ impl CoreApp {
let game_state = app_state.game.as_ref().unwrap(); let game_state = app_state.game.as_ref().unwrap();
CoreResponse::PlayingFieldView(playing_field(game_state)) CoreResponse::PlayingFieldView(playing_field(game_state))
} }
CoreRequest::LaunchScreen => CoreResponse::NewGameView(new_game()), CoreRequest::Home => {
CoreRequest::NewGame => CoreResponse::NewGameView(new_game()), CoreResponse::HomeView(home(self.state.read().unwrap().database.all_games()))
}
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();

View File

@ -36,6 +36,7 @@ enum OptionNames {
} }
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ConfigOption { pub enum ConfigOption {
DatabasePath(DatabasePath), DatabasePath(DatabasePath),
Me(Me), Me(Me),
@ -96,14 +97,14 @@ impl Config {
} }
} }
fn set(&mut self, val: ConfigOption) { pub fn set(&mut self, val: ConfigOption) {
let _ = match val { let _ = match val {
ConfigOption::DatabasePath(_) => self.values.insert(OptionNames::DatabasePath, val), ConfigOption::DatabasePath(_) => self.values.insert(OptionNames::DatabasePath, val),
ConfigOption::Me(_) => self.values.insert(OptionNames::Me, val), ConfigOption::Me(_) => self.values.insert(OptionNames::Me, val),
}; };
} }
fn get<'a, T>(&'a self) -> T pub fn get<'a, T>(&'a self) -> T
where where
T: From<&'a Self>, T: From<&'a Self>,
{ {
@ -111,9 +112,16 @@ impl Config {
} }
} }
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct DatabasePath(PathBuf); pub struct DatabasePath(PathBuf);
impl std::ops::Deref for DatabasePath {
type Target = PathBuf;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<&Config> for DatabasePath { impl From<&Config> for DatabasePath {
fn from(config: &Config) -> Self { fn from(config: &Config) -> Self {
match config.values.get(&OptionNames::DatabasePath) { match config.values.get(&OptionNames::DatabasePath) {
@ -123,7 +131,7 @@ impl From<&Config> for DatabasePath {
} }
} }
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Me(Player); pub struct Me(Player);
impl From<&Config> for Option<Me> { impl From<&Config> for Option<Me> {
@ -137,3 +145,54 @@ impl From<&Config> for Option<Me> {
}) })
} }
} }
impl std::ops::Deref for Me {
type Target = Player;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::types::Rank;
use cool_asserts::assert_matches;
#[test]
fn it_can_set_and_get_options() {
let mut config = Config::new(PathBuf::from("."));
config.set(ConfigOption::DatabasePath(DatabasePath(PathBuf::from(
"fixtures/five_games",
))));
config.set(ConfigOption::Me(Me(Player {
name: "Savanni".to_owned(),
rank: Some(Rank::Kyu(10)),
})));
}
#[test]
fn it_can_serialize_and_deserialize() {
let mut config = Config::new(PathBuf::from("."));
config.set(ConfigOption::DatabasePath(DatabasePath(PathBuf::from(
"fixtures/five_games",
))));
config.set(ConfigOption::Me(Me(Player {
name: "Savanni".to_owned(),
rank: Some(Rank::Kyu(10)),
})));
let s = serde_json::to_string(&config.values).unwrap();
println!("{}", s);
let values: HashMap<OptionNames, ConfigOption> = serde_json::from_str(s.as_ref()).unwrap();
println!("options: {:?}", values);
assert_matches!(values.get(&OptionNames::DatabasePath),
Some(ConfigOption::DatabasePath(db_path)) =>
assert_eq!(*db_path, config.get())
);
assert_matches!(values.get(&OptionNames::Me), Some(ConfigOption::Me(val)) =>
assert_eq!(Some(val.clone()), config.get())
);
}
}

View File

@ -1,6 +1,6 @@
use std::{ffi::OsStr, io::Read, os::unix::ffi::OsStrExt, path::PathBuf}; use std::{ffi::OsStr, io::Read, os::unix::ffi::OsStrExt, path::PathBuf};
use go_sgf::go::{parse_sgf, GameTree, GameType}; use go_sgf::{parse_sgf, GameTree};
use thiserror::Error; use thiserror::Error;
#[derive(Error, Debug)] #[derive(Error, Debug)]
@ -17,6 +17,7 @@ impl From<std::io::Error> for Error {
} }
} }
#[derive(Debug)]
pub struct Database { pub struct Database {
path: PathBuf, path: PathBuf,
games: Vec<GameTree>, games: Vec<GameTree>,
@ -49,6 +50,10 @@ impl Database {
Ok(Database { path, games }) Ok(Database { path, games })
} }
pub fn len(&self) -> usize {
self.games.len()
}
pub fn all_games(&self) -> impl Iterator<Item = &GameTree> { pub fn all_games(&self) -> impl Iterator<Item = &GameTree> {
self.games.iter() self.games.iter()
} }
@ -58,6 +63,7 @@ impl Database {
mod test { mod test {
use super::*; use super::*;
use cool_asserts::assert_matches; use cool_asserts::assert_matches;
use go_sgf::{Date, GameType};
#[test] #[test]
fn it_reads_empty_database() { fn it_reads_empty_database() {
@ -79,7 +85,7 @@ mod test {
Some(game) => { Some(game) => {
assert_eq!(game.info.black_player, Some("Steve".to_owned())); assert_eq!(game.info.black_player, Some("Steve".to_owned()));
assert_eq!(game.info.white_player, Some("Savanni".to_owned())); assert_eq!(game.info.white_player, Some("Savanni".to_owned()));
assert_eq!(game.info.date, vec![chrono::NaiveDate::from_ymd_opt(2023, 4, 19).unwrap()]); assert_eq!(game.info.date, vec![Date::Date(chrono::NaiveDate::from_ymd_opt(2023, 4, 19).unwrap())]);
assert_eq!(game.info.komi, Some(6.5)); assert_eq!(game.info.komi, Some(6.5));
} }
); );

View File

@ -13,4 +13,5 @@ mod database;
mod types; mod types;
pub use types::{BoardError, Color, Rank, Size}; pub use types::{BoardError, Color, Rank, Size};
pub mod ui; pub mod ui;

View File

@ -1,9 +1,11 @@
use crate::{ use crate::{
api::PlayStoneRequest, api::PlayStoneRequest,
board::{Board, Coordinate}, board::{Board, Coordinate},
config::DatabasePath,
database::Database,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::time::Duration; use std::{path::PathBuf, time::Duration};
use thiserror::Error; use thiserror::Error;
use typeshare::typeshare; use typeshare::typeshare;
@ -43,12 +45,14 @@ impl Default for Size {
#[derive(Debug)] #[derive(Debug)]
pub struct AppState { pub struct AppState {
pub game: Option<GameState>, pub game: Option<GameState>,
pub database: Database,
} }
impl AppState { impl AppState {
pub fn new() -> Self { pub fn new(database_path: DatabasePath) -> Self {
Self { Self {
game: Some(GameState::new()), game: Some(GameState::new()),
database: Database::open_path(database_path.to_path_buf()).unwrap(),
} }
} }
@ -91,7 +95,7 @@ impl From<Rank> for String {
} }
} }
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Player { pub struct Player {
pub name: String, pub name: String,
pub rank: Option<Rank>, pub rank: Option<Rank>,

View File

@ -0,0 +1,33 @@
use go_sgf::{Date, GameTree, Rank};
use serde::{Deserialize, Serialize};
use typeshare::typeshare;
#[derive(Clone, Debug, Deserialize, Serialize)]
#[typeshare]
pub struct GamePreviewElement {
pub date: Vec<Date>,
pub black_player: String,
pub black_rank: Option<Rank>,
pub white_player: String,
pub white_rank: Option<Rank>,
}
impl GamePreviewElement {
pub fn new(game: &GameTree) -> GamePreviewElement {
GamePreviewElement {
date: game.info.date.clone(),
black_player: game
.info
.black_player
.clone()
.unwrap_or("black_player".to_owned()),
black_rank: game.info.black_rank.clone(),
white_player: game
.info
.white_player
.clone()
.unwrap_or("white_player".to_owned()),
white_rank: game.info.white_rank.clone(),
}
}
}

View File

@ -1,2 +1,3 @@
pub mod action; pub mod action;
pub mod game_preview;
pub mod menu; pub mod menu;

View File

@ -1,4 +1,5 @@
use crate::ui::Action; use crate::ui::{Action, GamePreviewElement};
use go_sgf::GameTree;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use typeshare::typeshare; use typeshare::typeshare;
@ -48,13 +49,14 @@ pub struct BotPlayerElement {}
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
#[typeshare] #[typeshare]
pub struct NewGameView { pub struct HomeView {
pub black_player: PlayerElement, pub black_player: PlayerElement,
pub white_player: PlayerElement, pub white_player: PlayerElement,
pub games: Vec<GamePreviewElement>,
pub start_game: Action<()>, pub start_game: Action<()>,
} }
pub fn new_game() -> NewGameView { pub fn home<'a>(games: impl Iterator<Item = &'a GameTree>) -> HomeView {
let black_player = PlayerElement::Hotseat(HotseatPlayerElement { let black_player = PlayerElement::Hotseat(HotseatPlayerElement {
placeholder: Some("black player".to_owned()), placeholder: Some("black player".to_owned()),
default_rank: None, default_rank: None,
@ -65,9 +67,10 @@ pub fn new_game() -> NewGameView {
default_rank: None, default_rank: None,
ranks: rank_strings(), ranks: rank_strings(),
}); });
NewGameView { HomeView {
black_player, black_player,
white_player, white_player,
games: games.map(GamePreviewElement::new).collect(),
start_game: Action { start_game: Action {
id: "start-game-action".to_owned(), id: "start-game-action".to_owned(),
label: "New Game".to_owned(), label: "New Game".to_owned(),

View File

@ -1,5 +1,5 @@
mod elements; mod elements;
pub use elements::{action::Action, menu::Menu}; pub use elements::{action::Action, game_preview::GamePreviewElement, menu::Menu};
mod playing_field; mod playing_field;
pub use playing_field::{playing_field, PlayingFieldView}; pub use playing_field::{playing_field, PlayingFieldView};
@ -7,8 +7,8 @@ pub use playing_field::{playing_field, PlayingFieldView};
// mod launch_screen; // mod launch_screen;
// pub use launch_screen::{launch_screen, LaunchScreenView}; // pub use launch_screen::{launch_screen, LaunchScreenView};
mod new_game; mod home;
pub use new_game::{new_game, HotseatPlayerElement, NewGameView, PlayerElement}; pub use home::{home, HomeView, HotseatPlayerElement, PlayerElement};
mod types; mod types;
pub use types::{ pub use types::{

52
kifu/gtk/Cargo.lock generated
View File

@ -121,6 +121,8 @@ dependencies = [
"js-sys", "js-sys",
"num-integer", "num-integer",
"num-traits", "num-traits",
"serde",
"time",
"wasm-bindgen", "wasm-bindgen",
"winapi", "winapi",
] ]
@ -442,7 +444,7 @@ dependencies = [
"cfg-if", "cfg-if",
"js-sys", "js-sys",
"libc", "libc",
"wasi", "wasi 0.11.0+wasi-snapshot-preview1",
"wasm-bindgen", "wasm-bindgen",
] ]
@ -543,6 +545,17 @@ dependencies = [
"system-deps", "system-deps",
] ]
[[package]]
name = "go-sgf"
version = "0.1.0"
dependencies = [
"chrono",
"nom",
"serde",
"thiserror",
"typeshare",
]
[[package]] [[package]]
name = "gobject-sys" name = "gobject-sys"
version = "0.17.4" version = "0.17.4"
@ -785,6 +798,8 @@ dependencies = [
name = "kifu-core" name = "kifu-core"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"chrono",
"go-sgf",
"grid", "grid",
"serde", "serde",
"serde_json", "serde_json",
@ -862,6 +877,12 @@ dependencies = [
"autocfg", "autocfg",
] ]
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]] [[package]]
name = "miniz_oxide" name = "miniz_oxide"
version = "0.6.2" version = "0.6.2"
@ -879,7 +900,7 @@ checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9"
dependencies = [ dependencies = [
"libc", "libc",
"log", "log",
"wasi", "wasi 0.11.0+wasi-snapshot-preview1",
"windows-sys", "windows-sys",
] ]
@ -898,6 +919,16 @@ version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c" checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c"
[[package]]
name = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
"memchr",
"minimal-lexical",
]
[[package]] [[package]]
name = "num-integer" name = "num-integer"
version = "0.1.45" version = "0.1.45"
@ -1330,6 +1361,17 @@ dependencies = [
"weezl", "weezl",
] ]
[[package]]
name = "time"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a"
dependencies = [
"libc",
"wasi 0.10.0+wasi-snapshot-preview1",
"winapi",
]
[[package]] [[package]]
name = "tokio" name = "tokio"
version = "1.26.0" version = "1.26.0"
@ -1433,6 +1475,12 @@ version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wasi"
version = "0.10.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.11.0+wasi-snapshot-preview1" version = "0.11.0+wasi-snapshot-preview1"

View File

@ -2,6 +2,7 @@ release:
cargo build --release cargo build --release
dev: dev:
export CONFIG=.
cargo watch -x 'run --bin kifu-gtk' cargo watch -x 'run --bin kifu-gtk'
screenplay: screenplay:

1
kifu/gtk/config Normal file
View File

@ -0,0 +1 @@
{"Me":{"name":"Savanni","rank":{"Kyu":10}},"DatabasePath":"../core/fixtures/five_games"}

View File

@ -2,7 +2,7 @@ use gtk::prelude::*;
use kifu_core::{CoreApp, CoreRequest, CoreResponse}; use kifu_core::{CoreApp, CoreRequest, CoreResponse};
use kifu_gtk::{ use kifu_gtk::{
perftrace, perftrace,
ui::{NewGame, PlayingField}, ui::{Home, PlayingField},
CoreApi, CoreApi,
}; };
use std::sync::{Arc, RwLock}; use std::sync::{Arc, RwLock};
@ -10,10 +10,10 @@ use std::sync::{Arc, RwLock};
fn handle_response(api: CoreApi, window: gtk::ApplicationWindow, message: CoreResponse) { fn handle_response(api: CoreApi, window: gtk::ApplicationWindow, message: CoreResponse) {
let playing_field = Arc::new(RwLock::new(None)); let playing_field = Arc::new(RwLock::new(None));
match message { match message {
CoreResponse::NewGameView(view) => perftrace("NewGameView", || { CoreResponse::HomeView(view) => perftrace("HomeView", || {
let api = api.clone(); let api = api.clone();
let new_game = NewGame::new(api, view); let new_game = Home::new(api, view);
window.set_child(Some(&new_game)); window.set_child(Some(&new_game));
}), }),
CoreResponse::PlayingFieldView(view) => perftrace("PlayingFieldView", || { CoreResponse::PlayingFieldView(view) => perftrace("PlayingFieldView", || {
@ -44,10 +44,17 @@ fn main() {
.unwrap(), .unwrap(),
); );
let user_home = std::env::var("HOME").expect("the user's home directory isn't set"); let config_path = std::env::var("CONFIG")
let mut config_path = std::path::PathBuf::from(user_home); .and_then(|config| Ok(std::path::PathBuf::from(config)))
.or({
std::env::var("HOME").and_then(|base| {
let mut config_path = std::path::PathBuf::from(base);
config_path.push(".config"); config_path.push(".config");
config_path.push("kifu"); config_path.push("kifu");
Ok(config_path)
})
})
.expect("no config path could be found");
let core = CoreApp::new(config_path); let core = CoreApp::new(config_path);
@ -87,7 +94,7 @@ fn main() {
} }
}); });
api.dispatch(CoreRequest::NewGame); api.dispatch(CoreRequest::Home);
} }
}); });

View File

@ -0,0 +1,42 @@
use glib::Object;
use gtk::{glib, prelude::*, subclass::prelude::*};
use kifu_core::ui::GamePreviewElement;
#[derive(Default)]
pub struct GamePreviewPrivate;
#[glib::object_subclass]
impl ObjectSubclass for GamePreviewPrivate {
const NAME: &'static str = "GamePreview";
type Type = GamePreview;
type ParentType = gtk::Box;
}
impl ObjectImpl for GamePreviewPrivate {}
impl WidgetImpl for GamePreviewPrivate {}
impl BoxImpl for GamePreviewPrivate {}
glib::wrapper! {
pub struct GamePreview(ObjectSubclass<GamePreviewPrivate>) @extends gtk::Box, gtk::Widget, @implements gtk::Orientable;
}
impl GamePreview {
pub fn new(element: GamePreviewElement) -> GamePreview {
let s: Self = Object::builder().build();
s.set_orientation(gtk::Orientation::Horizontal);
println!("game_preview: {:?}", element);
let black_player = match element.black_rank {
Some(rank) => format!("{} ({})", element.black_player, rank.to_string()),
None => element.black_player,
};
let white_player = match element.white_rank {
Some(rank) => format!("{} ({})", element.white_player, rank.to_string()),
None => element.white_player,
};
s.append(&gtk::Label::new(Some(&black_player)));
s.append(&gtk::Label::new(Some(&white_player)));
s
}
}

View File

@ -1,8 +1,9 @@
use crate::ui::GamePreview;
use crate::CoreApi; use crate::CoreApi;
use glib::Object; use glib::Object;
use gtk::{glib, prelude::*, subclass::prelude::*}; use gtk::{glib, prelude::*, subclass::prelude::*};
use kifu_core::{ use kifu_core::{
ui::{NewGameView, PlayerElement}, ui::{HomeView, PlayerElement},
CoreRequest, CreateGameRequest, HotseatPlayerRequest, PlayerInfoRequest, CoreRequest, CreateGameRequest, HotseatPlayerRequest, PlayerInfoRequest,
}; };
use std::{cell::RefCell, rc::Rc}; use std::{cell::RefCell, rc::Rc};
@ -82,12 +83,12 @@ impl PlayerDataEntry {
} }
} }
pub struct NewGamePrivate { pub struct HomePrivate {
black_player: Rc<RefCell<Option<PlayerDataEntry>>>, black_player: Rc<RefCell<Option<PlayerDataEntry>>>,
white_player: Rc<RefCell<Option<PlayerDataEntry>>>, white_player: Rc<RefCell<Option<PlayerDataEntry>>>,
} }
impl Default for NewGamePrivate { impl Default for HomePrivate {
fn default() -> Self { fn default() -> Self {
Self { Self {
black_player: Rc::new(RefCell::new(None)), black_player: Rc::new(RefCell::new(None)),
@ -97,22 +98,22 @@ impl Default for NewGamePrivate {
} }
#[glib::object_subclass] #[glib::object_subclass]
impl ObjectSubclass for NewGamePrivate { impl ObjectSubclass for HomePrivate {
const NAME: &'static str = "NewGame"; const NAME: &'static str = "Home";
type Type = NewGame; type Type = Home;
type ParentType = gtk::Grid; type ParentType = gtk::Grid;
} }
impl ObjectImpl for NewGamePrivate {} impl ObjectImpl for HomePrivate {}
impl WidgetImpl for NewGamePrivate {} impl WidgetImpl for HomePrivate {}
impl GridImpl for NewGamePrivate {} impl GridImpl for HomePrivate {}
glib::wrapper! { glib::wrapper! {
pub struct NewGame(ObjectSubclass<NewGamePrivate>) @extends gtk::Grid, gtk::Widget; pub struct Home(ObjectSubclass<HomePrivate>) @extends gtk::Grid, gtk::Widget;
} }
impl NewGame { impl Home {
pub fn new(api: CoreApi, view: NewGameView) -> NewGame { pub fn new(api: CoreApi, view: HomeView) -> Home {
let s: Self = Object::builder().build(); let s: Self = Object::builder().build();
let black_player = PlayerDataEntry::new(view.black_player); let black_player = PlayerDataEntry::new(view.black_player);
@ -138,6 +139,12 @@ impl NewGame {
} }
}); });
let game_list = gtk::Box::new(gtk::Orientation::Vertical, 0);
s.attach(&game_list, 1, 3, 2, 1);
view.games
.iter()
.for_each(|game_preview| game_list.append(&GamePreview::new(game_preview.clone())));
s s
} }
} }

View File

@ -1,14 +1,17 @@
mod player_card;
pub use player_card::PlayerCard;
mod chat; mod chat;
pub use chat::Chat; pub use chat::Chat;
mod game_preview;
pub use game_preview::GamePreview;
mod player_card;
pub use player_card::PlayerCard;
mod playing_field; mod playing_field;
pub use playing_field::PlayingField; pub use playing_field::PlayingField;
mod new_game; mod home;
pub use new_game::NewGame; pub use home::Home;
mod board; mod board;
pub use board::Board; pub use board::Board;