diff --git a/flake.lock b/flake.lock index fad6c0a..5c66945 100644 --- a/flake.lock +++ b/flake.lock @@ -17,11 +17,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1678972866, - "narHash": "sha256-YV8BcNWfNVgS449B6hFYFUg4kwVIQMNehZP+FNDs1LY=", + "lastModified": 1680122840, + "narHash": "sha256-zCQ/9iFHzCW5JMYkkHMwgK1/1/kTMgCMHq4THPINpAU=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "cd34d6ed7ba7d5c4e44b04a53dc97edb52f2766c", + "rev": "a575c243c23e2851b78c00e9fa245232926ec32f", "type": "github" }, "original": { @@ -52,7 +52,7 @@ "nixpkgs": "nixpkgs_2" }, "locked": { - "narHash": "sha256-03Opt2yu4E/AIFjvlgib0/nhMn6B4B/t/nvwS2bzOGw=", + "narHash": "sha256-o28gi3WKSsVeXg3wDSR2kGpawrDO5lzGG4eUsLTPglw=", "type": "tarball", "url": "https://github.com/oxalica/rust-overlay/archive/master.tar.gz" }, @@ -70,11 +70,11 @@ }, "unstable": { "locked": { - "lastModified": 1678898370, - "narHash": "sha256-xTICr1j+uat5hk9FyuPOFGxpWHdJRibwZC+ATi0RbtE=", + "lastModified": 1680125544, + "narHash": "sha256-mlqo1r+TZUOuypWdrZHluxWL+E5WzXlUXNZ9Y0WLDFU=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "ac718d02867a84b42522a0ece52d841188208f2c", + "rev": "9a6aabc4740790ef3bbb246b86d029ccf6759658", "type": "github" }, "original": { diff --git a/kifu/kifu-core/src/types.rs b/kifu/kifu-core/src/types.rs index a15f37e..e37992a 100644 --- a/kifu/kifu-core/src/types.rs +++ b/kifu/kifu-core/src/types.rs @@ -1,17 +1,26 @@ use std::time::Duration; -#[derive(Clone, Debug)] +#[derive(Clone, Copy, Debug)] pub enum Color { Black, White, } -#[derive(Clone, Debug)] +#[derive(Clone, Copy, Debug)] pub struct Size { pub width: u8, pub height: u8, } +impl Default for Size { + fn default() -> Self { + Self { + width: 19, + height: 19, + } + } +} + pub(crate) struct AppState { game: Option, } diff --git a/kifu/kifu-core/src/ui/mod.rs b/kifu/kifu-core/src/ui/mod.rs index 1948529..e7b40c0 100644 --- a/kifu/kifu-core/src/ui/mod.rs +++ b/kifu/kifu-core/src/ui/mod.rs @@ -2,4 +2,4 @@ mod playing_field; pub use playing_field::{playing_field, PlayingFieldView}; mod types; -pub use types::{ChatElement, GameBoardElement, PlayerCardElement, TextFieldElement}; +pub use types::{ChatElement, GobanElement, PlayerCardElement, StoneElement, TextFieldElement}; diff --git a/kifu/kifu-core/src/ui/playing_field.rs b/kifu/kifu-core/src/ui/playing_field.rs index 437e07c..8787338 100644 --- a/kifu/kifu-core/src/ui/playing_field.rs +++ b/kifu/kifu-core/src/ui/playing_field.rs @@ -3,7 +3,7 @@ use crate::ui::types; #[derive(Clone, Debug)] pub struct PlayingFieldView { - pub board: types::GameBoardElement, + pub board: types::GobanElement, pub player_card_black: types::PlayerCardElement, pub player_card_white: types::PlayerCardElement, pub chat: types::ChatElement, @@ -12,16 +12,7 @@ pub struct PlayingFieldView { } pub fn playing_field() -> PlayingFieldView { - let mut spaces = Vec::new(); - (0..19).for_each(|_| spaces.push(Vec::new())); - - let board = types::GameBoardElement { - size: Size { - width: 19, - height: 19, - }, - spaces, - }; + let board = types::GobanElement::default(); let player_card_black = types::PlayerCardElement { color: Color::Black, diff --git a/kifu/kifu-core/src/ui/types.rs b/kifu/kifu-core/src/ui/types.rs index cb6b690..05092f3 100644 --- a/kifu/kifu-core/src/ui/types.rs +++ b/kifu/kifu-core/src/ui/types.rs @@ -12,10 +12,49 @@ pub struct StoneElement { pub jitter: Jitter, } +impl StoneElement { + pub fn new(color: Color) -> Self { + Self { + color, + jitter: Jitter { x: 0, y: 0 }, + } + } +} + #[derive(Clone, Debug)] -pub struct GameBoardElement { +pub struct GobanElement { pub size: Size, - pub spaces: Vec>, + pub spaces: Vec>, +} + +impl GobanElement { + pub fn new(size: Size) -> Self { + Self { + size: size.clone(), + spaces: (0..((size.width as usize) * (size.height as usize))) + .map(|_| None) + .collect::>>(), + } + } + + 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 { + fn default() -> Self { + Self::new(Size::default()) + } } #[derive(Clone, Debug)] diff --git a/kifu/kifu-gtk/Cargo.lock b/kifu/kifu-gtk/Cargo.lock index c2e0105..7e5c456 100644 --- a/kifu/kifu-gtk/Cargo.lock +++ b/kifu/kifu-gtk/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "anyhow" version = "1.0.70" @@ -14,12 +20,36 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "bit_field" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" + [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bumpalo" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" + +[[package]] +name = "bytemuck" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + [[package]] name = "bytes" version = "1.4.0" @@ -66,6 +96,92 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + +[[package]] +name = "exr" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdd2162b720141a91a054640662d3edce3d50a944a50ffca5313cd951abb35b4" +dependencies = [ + "bit_field", + "flume", + "half", + "lebe", + "miniz_oxide", + "rayon-core", + "smallvec", + "zune-inflate", +] + [[package]] name = "field-offset" version = "0.3.5" @@ -76,6 +192,29 @@ dependencies = [ "rustc_version", ] +[[package]] +name = "flate2" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "flume" +version = "0.10.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577" +dependencies = [ + "futures-core", + "futures-sink", + "nanorand", + "pin-project", + "spin", +] + [[package]] name = "futures-channel" version = "0.3.27" @@ -119,6 +258,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "futures-sink" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" + [[package]] name = "futures-task" version = "0.3.27" @@ -199,6 +344,29 @@ dependencies = [ "system-deps", ] +[[package]] +name = "getrandom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "gif" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045" +dependencies = [ + "color_quant", + "weezl", +] + [[package]] name = "gio" version = "0.17.4" @@ -255,6 +423,12 @@ dependencies = [ "thiserror", ] +[[package]] +name = "glib-build-tools" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f8480c9ba9cc06aa8d5baf446037f8dc237bee127e9b62080c4db7e293d8ea0" + [[package]] name = "glib-macros" version = "0.17.5" @@ -402,6 +576,15 @@ dependencies = [ "system-deps", ] +[[package]] +name = "half" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b4af3693f1b705df946e9fe5631932443781d0aabb423b62fcd4d73f6d2fd0" +dependencies = [ + "crunchy", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -423,6 +606,25 @@ dependencies = [ "libc", ] +[[package]] +name = "image" +version = "0.24.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527909aa81e20ac3a44803521443a765550f09b5130c2c2fa1ea59c2f8f50a3a" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "exr", + "gif", + "jpeg-decoder", + "num-rational", + "num-traits", + "png", + "qoi", + "tiff", +] + [[package]] name = "indexmap" version = "1.9.2" @@ -433,6 +635,24 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "jpeg-decoder" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e" +dependencies = [ + "rayon", +] + +[[package]] +name = "js-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "kifu-core" version = "0.1.0" @@ -445,13 +665,22 @@ name = "kifu-gtk" version = "0.1.0" dependencies = [ "cairo-rs", + "gio", "glib", + "glib-build-tools", "gtk4", + "image", "kifu-core", "screenplay", "tokio", ] +[[package]] +name = "lebe" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" + [[package]] name = "libc" version = "0.2.140" @@ -492,6 +721,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "miniz_oxide" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +dependencies = [ + "adler", +] + [[package]] name = "mio" version = "0.8.6" @@ -504,6 +742,45 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "nanorand" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" +dependencies = [ + "getrandom", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + [[package]] name = "num_cpus" version = "1.15.0" @@ -569,6 +846,26 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "pin-project" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "pin-project-lite" version = "0.2.9" @@ -587,6 +884,18 @@ version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +[[package]] +name = "png" +version = "0.17.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d708eaf860a19b19ce538740d2b4bdeeb8337fa53f7738455e706623ad5c638" +dependencies = [ + "bitflags", + "crc32fast", + "flate2", + "miniz_oxide", +] + [[package]] name = "proc-macro-crate" version = "1.3.1" @@ -630,6 +939,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "qoi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] + [[package]] name = "quote" version = "1.0.26" @@ -639,6 +957,28 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rayon" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + [[package]] name = "redox_syscall" version = "0.2.16" @@ -691,6 +1031,12 @@ dependencies = [ "libc", ] +[[package]] +name = "simd-adler32" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "238abfbb77c1915110ad968465608b68e869e0772622c9656714e73e5a1a522f" + [[package]] name = "slab" version = "0.4.8" @@ -716,6 +1062,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "spin" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0959fd6f767df20b231736396e4f602171e00d95205676286e79d4a4eb67bef" +dependencies = [ + "lock_api", +] + [[package]] name = "syn" version = "1.0.109" @@ -771,6 +1126,17 @@ dependencies = [ "syn 2.0.2", ] +[[package]] +name = "tiff" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7449334f9ff2baf290d55d73983a7d6fa15e01198faef72af07e2a8db851e471" +dependencies = [ + "flate2", + "jpeg-decoder", + "weezl", +] + [[package]] name = "tokio" version = "1.26.0" @@ -852,6 +1218,66 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasm-bindgen" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 1.0.109", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" + +[[package]] +name = "weezl" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" + [[package]] name = "winapi" version = "0.3.9" @@ -948,3 +1374,12 @@ checksum = "23d020b441f92996c80d94ae9166e8501e59c7bb56121189dc9eab3bd8216966" dependencies = [ "memchr", ] + +[[package]] +name = "zune-inflate" +version = "0.2.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "440a08fd59c6442e4b846ea9b10386c38307eae728b216e1ab2c305d1c9daaf8" +dependencies = [ + "simd-adler32", +] diff --git a/kifu/kifu-gtk/Cargo.toml b/kifu/kifu-gtk/Cargo.toml index 1f7487f..fa13690 100644 --- a/kifu/kifu-gtk/Cargo.toml +++ b/kifu/kifu-gtk/Cargo.toml @@ -10,12 +10,17 @@ screenplay = [] [dependencies] cairo-rs = { version = "0.17" } +gio = { version = "0.17" } glib = { version = "0.17" } gtk = { version = "0.6", package = "gtk4" } +image = { version = "0.24" } kifu-core = { path = "../kifu-core" } tokio = { version = "1.26", features = [ "full" ] } screenplay = { path = "../../screenplay" } +[build-dependencies] +glib-build-tools = "0.17" + [[bin]] name = "kifu-gtk" path = "src/main.rs" @@ -24,3 +29,4 @@ path = "src/main.rs" name = "screenplay" path = "src/bin/screenplay.rs" required-features = [ "screenplay" ] + diff --git a/kifu/kifu-gtk/build.rs b/kifu/kifu-gtk/build.rs new file mode 100644 index 0000000..18b1e1e --- /dev/null +++ b/kifu/kifu-gtk/build.rs @@ -0,0 +1,7 @@ +fn main() { + glib_build_tools::compile_resources( + &["resources"], + "resources/resources.gresources.xml", + "com.luminescent-dreams.kifu-gtk.gresource", + ); +} diff --git a/kifu/kifu-gtk/resources/resources.gresources.xml b/kifu/kifu-gtk/resources/resources.gresources.xml new file mode 100644 index 0000000..31ca0a0 --- /dev/null +++ b/kifu/kifu-gtk/resources/resources.gresources.xml @@ -0,0 +1,6 @@ + + + + wood_texture.jpg + + diff --git a/kifu/kifu-gtk/resources/wood_texture.jpg b/kifu/kifu-gtk/resources/wood_texture.jpg new file mode 100644 index 0000000..a586b16 Binary files /dev/null and b/kifu/kifu-gtk/resources/wood_texture.jpg differ diff --git a/kifu/kifu-gtk/src/bin/screenplay.rs b/kifu/kifu-gtk/src/bin/screenplay.rs index 28f16cb..ab461e4 100644 --- a/kifu/kifu-gtk/src/bin/screenplay.rs +++ b/kifu/kifu-gtk/src/bin/screenplay.rs @@ -7,6 +7,9 @@ use kifu_gtk::ui::{playing_field_view, Chat, PlayerCard, PlayingField}; use screenplay::{Screen, Screenplay}; fn main() { + gio::resources_register_include!("com.luminescent-dreams.kifu-gtk.gresource") + .expect("Failed to register resources"); + let app = gtk::Application::builder() .application_id("com.luminescent-dreams.kifu-gtk.screenplay") .build(); diff --git a/kifu/kifu-gtk/src/main.rs b/kifu/kifu-gtk/src/main.rs index 617ab45..a12ed30 100644 --- a/kifu/kifu-gtk/src/main.rs +++ b/kifu/kifu-gtk/src/main.rs @@ -1,5 +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, @@ -27,6 +29,9 @@ impl CoreApi { } fn main() { + gio::resources_register_include!("com.luminescent-dreams.kifu-gtk.gresource") + .expect("Failed to register resources"); + let runtime = Arc::new( tokio::runtime::Builder::new_multi_thread() .enable_all() @@ -62,8 +67,14 @@ fn main() { let window = gtk::ApplicationWindow::new(app); window.present(); - gtk_rx.attach(None, |message| { + 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)); + } + } Continue(true) }); diff --git a/kifu/kifu-gtk/src/ui/goban.rs b/kifu/kifu-gtk/src/ui/goban.rs index 6c30670..2580500 100644 --- a/kifu/kifu-gtk/src/ui/goban.rs +++ b/kifu/kifu-gtk/src/ui/goban.rs @@ -1,30 +1,231 @@ +use gio::resources_lookup_data; use glib::Object; -use gtk::{prelude::*, subclass::prelude::*}; +use gtk::{ + gdk_pixbuf::{InterpType, Pixbuf}, + prelude::*, + subclass::prelude::*, +}; +use image::io::Reader as ImageReader; +use kifu_core::{ui::GobanElement, Color}; +use std::{cell::RefCell, io::Cursor, rc::Rc}; -#[derive(Default)] -pub struct GobanPrivate; +const WIDTH: i32 = 800; +const HEIGHT: i32 = 800; + +#[derive(Clone, Default, PartialEq)] +struct Addr { + row: u8, + column: u8, +} + +pub struct GobanPrivate { + drawing_area: gtk::DrawingArea, + + current_player: Rc>, + goban: Rc>, + cursor_location: Rc>, +} #[glib::object_subclass] impl ObjectSubclass for GobanPrivate { const NAME: &'static str = "Goban"; type Type = Goban; - type ParentType = gtk::DrawingArea; + type ParentType = gtk::Grid; + + fn new() -> GobanPrivate { + GobanPrivate { + drawing_area: Default::default(), + current_player: Rc::new(RefCell::new(Color::Black)), + goban: Default::default(), + cursor_location: Default::default(), + } + } } -impl ObjectImpl for GobanPrivate {} +impl ObjectImpl for GobanPrivate { + fn constructed(&self) { + self.drawing_area.set_width_request(WIDTH); + self.drawing_area.set_height_request(HEIGHT); + + let goban = self.goban.clone(); + let cursor_location = self.cursor_location.clone(); + let current_player = self.current_player.clone(); + + let wood_texture = resources_lookup_data( + "/com/luminescent-dreams/kifu-gtk/wood_texture.jpg", + gio::ResourceLookupFlags::NONE, + ) + .unwrap(); + let background = ImageReader::new(Cursor::new(wood_texture)) + .with_guessed_format() + .unwrap() + .decode(); + let background = background.map(|background| { + Pixbuf::from_bytes( + &glib::Bytes::from(background.as_bytes()), + gtk::gdk_pixbuf::Colorspace::Rgb, + false, + 8, + background.width() as i32, + background.height() as i32, + background.to_rgb8().sample_layout().height_stride as i32, + ) + .scale_simple(WIDTH, HEIGHT, InterpType::Nearest) + }); + + self.drawing_area + .set_draw_func(move |_, context, width, height| { + let goban = goban.borrow(); + match background { + Ok(Some(ref background)) => { + context.set_source_pixbuf(&background, 0., 0.); + context.paint().expect("paint should succeed"); + } + Ok(None) | Err(_) => context.set_source_rgb(0.7, 0.7, 0.7), + }; + let _ = context.paint(); + + 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 pen = Pen { + x_offset: 20., + y_offset: 20., + 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); + 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); + let _ = context.stroke(); + }); + + context.set_source_rgb(0.1, 0.1, 0.0); + vec![3, 9, 15].into_iter().for_each(|col| { + vec![3, 9, 15].into_iter().for_each(|row| { + pen.star_point(context, col, row); + }); + }); + + (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); + } + }; + }) + }); + + let cursor = cursor_location.borrow(); + pen.ghost_stone(context, cursor.row, cursor.column, *current_player.borrow()); + }); + + let motion_controller = gtk::EventControllerMotion::new(); + { + let goban = self.goban.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 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 addr = Addr { + column: ((x.round() - 20.) / hspace_between).round() as u8, + row: ((y.round() - 20.) / vspace_between).round() as u8, + }; + + if *cursor != addr { + *cursor = addr; + drawing_area.queue_draw(); + } + }); + } + + self.drawing_area.add_controller(motion_controller); + } +} impl WidgetImpl for GobanPrivate {} -impl DrawingAreaImpl for GobanPrivate {} +impl GridImpl for GobanPrivate {} glib::wrapper! { - pub struct Goban(ObjectSubclass) @extends gtk::DrawingArea, gtk::Widget; + pub struct Goban(ObjectSubclass) @extends gtk::Grid, gtk::Widget; } impl Goban { pub fn new() -> Self { let s: Self = Object::builder().build(); - s.set_width_request(1024); - s.set_height_request(768); - + s.attach(&s.imp().drawing_area, 1, 1, 1, 1); s } + + pub fn set_board(&self, goban: GobanElement) { + *self.imp().goban.borrow_mut() = goban; + self.imp().drawing_area.queue_draw(); + } + + pub fn set_current_player(&self, color: Color) { + *self.imp().current_player.borrow_mut() = color; + } +} + +struct Pen { + x_offset: f64, + y_offset: f64, + hspace_between: f64, + vspace_between: f64, +} + +impl Pen { + fn star_point(&self, context: &cairo::Context, row: u8, col: u8) { + context.arc( + self.x_offset + (col as f64) * self.hspace_between, + self.y_offset + (row as f64) * self.vspace_between, + 10., + 0., + 2. * std::f64::consts::PI, + ); + let _ = context.fill(); + } + + fn stone(&self, context: &cairo::Context, row: u8, col: u8, color: Color) { + match color { + 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(); + } + + fn ghost_stone(&self, context: &cairo::Context, row: u8, col: u8, color: Color) { + match color { + 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), + }; + 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(); + } } diff --git a/kifu/kifu-gtk/src/ui/playing_field.rs b/kifu/kifu-gtk/src/ui/playing_field.rs index 0dc4be6..7ca1589 100644 --- a/kifu/kifu-gtk/src/ui/playing_field.rs +++ b/kifu/kifu-gtk/src/ui/playing_field.rs @@ -2,7 +2,10 @@ use crate::ui::{Chat, Goban, PlayerCard}; use glib::Object; use gtk::{prelude::*, subclass::prelude::*}; use kifu_core::{ - ui::{ChatElement, GameBoardElement, PlayerCardElement, PlayingFieldView, TextFieldElement}, + ui::{ + ChatElement, GobanElement, PlayerCardElement, PlayingFieldView, StoneElement, + TextFieldElement, + }, Color, Size, }; use std::{cell::RefCell, rc::Rc}; @@ -10,7 +13,7 @@ use std::{cell::RefCell, rc::Rc}; /* #[derive(Clone, Debug)] pub struct PlayingFieldView { - pub board: types::GameBoardElement, + pub board: types::GobanElement, pub player_card_black: types::PlayerCardElement, pub player_card_white: types::PlayerCardElement, pub chat: types::ChatElement, @@ -19,13 +22,24 @@ pub struct PlayingFieldView { } */ -#[derive(Default)] pub struct PlayingFieldPrivate { + goban: Goban, player_card_white: Rc>>, player_card_black: Rc>>, chat: Rc>>, } +impl Default for PlayingFieldPrivate { + fn default() -> 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)), + } + } +} + #[glib::object_subclass] impl ObjectSubclass for PlayingFieldPrivate { const NAME: &'static str = "PlayingField"; @@ -34,6 +48,7 @@ impl ObjectSubclass for PlayingFieldPrivate { 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)), @@ -53,12 +68,13 @@ impl PlayingField { pub fn new(view: PlayingFieldView) -> PlayingField { let s: Self = Object::builder().build(); - let goban = Goban::new(); 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.attach(&goban, 1, 1, 1, 2); + s.imp().goban.set_board(view.board); + + s.attach(&s.imp().goban, 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); @@ -66,6 +82,7 @@ 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 } @@ -73,17 +90,9 @@ impl PlayingField { #[cfg(feature = "screenplay")] pub fn playing_field_view() -> PlayingFieldView { - let mut spaces = Vec::new(); - (0..19).for_each(|_| spaces.push(Vec::new())); - - let board = GameBoardElement { - size: Size { - width: 19, - height: 19, - }, - spaces, - }; - + 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 player_card_black = PlayerCardElement { color: Color::Black, name: "Savanni".to_owned(),