Update the build environment and some architectural elements of the Kifu app #210
|
@ -9,6 +9,11 @@ use std::{
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
sync::{Arc, RwLock},
|
sync::{Arc, RwLock},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub trait Observable<T> {
|
||||||
|
fn subscribe(&self) -> Receiver<T>;
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub enum CoreRequest {
|
pub enum CoreRequest {
|
||||||
ChangeSetting(ChangeSettingRequest),
|
ChangeSetting(ChangeSettingRequest),
|
||||||
|
@ -92,15 +97,6 @@ impl Core {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn subscribe(&self) -> Receiver<CoreNotification> {
|
|
||||||
let mut subscribers = self.subscribers.write().unwrap();
|
|
||||||
|
|
||||||
let (sender, receiver) = async_std::channel::unbounded();
|
|
||||||
subscribers.push(sender);
|
|
||||||
|
|
||||||
receiver
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
pub async fn dispatch(&self, request: CoreRequest) -> CoreResponse {
|
pub async fn dispatch(&self, request: CoreRequest) -> CoreResponse {
|
||||||
match request {
|
match request {
|
||||||
|
@ -168,3 +164,14 @@ impl Core {
|
||||||
|
|
||||||
// pub async fn run(&self) {}
|
// pub async fn run(&self) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Observable<CoreNotification> for Core {
|
||||||
|
fn subscribe(&self) -> Receiver<CoreNotification> {
|
||||||
|
let mut subscribers = self.subscribers.write().unwrap();
|
||||||
|
|
||||||
|
let (sender, receiver) = async_std::channel::unbounded();
|
||||||
|
subscribers.push(sender);
|
||||||
|
|
||||||
|
receiver
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ extern crate config_derive;
|
||||||
mod api;
|
mod api;
|
||||||
pub use api::{
|
pub use api::{
|
||||||
ChangeSettingRequest, Core, CoreNotification, CoreRequest, CoreResponse, CreateGameRequest,
|
ChangeSettingRequest, Core, CoreNotification, CoreRequest, CoreResponse, CreateGameRequest,
|
||||||
HotseatPlayerRequest, PlayerInfoRequest,
|
HotseatPlayerRequest, Observable, PlayerInfoRequest,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod board;
|
mod board;
|
||||||
|
|
|
@ -3,14 +3,12 @@ pub mod ui;
|
||||||
mod view_models;
|
mod view_models;
|
||||||
mod views;
|
mod views;
|
||||||
|
|
||||||
|
|
||||||
use kifu_core::{Core, CoreRequest, CoreResponse};
|
use kifu_core::{Core, CoreRequest, CoreResponse};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::{spawn, runtime::Runtime};
|
use tokio::{runtime::Runtime, spawn};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct CoreApi {
|
pub struct CoreApi {
|
||||||
pub notification_tx: async_channel::Sender<CoreResponse>,
|
|
||||||
pub rt: Arc<Runtime>,
|
pub rt: Arc<Runtime>,
|
||||||
pub core: Core,
|
pub core: Core,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use adw::prelude::*;
|
use adw::prelude::*;
|
||||||
use kifu_core::{Core, CoreRequest, CoreResponse, DatabasePath, Config, ConfigOption};
|
use kifu_core::{Config, ConfigOption, Core, CoreRequest, CoreResponse, DatabasePath};
|
||||||
use kifu_gtk::{
|
use kifu_gtk::{
|
||||||
perftrace,
|
perftrace,
|
||||||
ui::{AppWindow, ConfigurationPage, Home, PlayingField},
|
ui::{AppWindow, ConfigurationPage, Home, PlayingField},
|
||||||
|
@ -104,16 +104,9 @@ fn main() {
|
||||||
app.connect_activate({
|
app.connect_activate({
|
||||||
let runtime = runtime.clone();
|
let runtime = runtime.clone();
|
||||||
move |app| {
|
move |app| {
|
||||||
let (notification_tx, notification_rx) = async_channel::unbounded::<CoreResponse>();
|
|
||||||
/*
|
|
||||||
let (gtk_tx, gtk_rx) =
|
|
||||||
gtk::glib::MainContext::channel::<CoreResponse>(gtk::glib::Priority::DEFAULT);
|
|
||||||
*/
|
|
||||||
|
|
||||||
let app_window = AppWindow::new(app);
|
let app_window = AppWindow::new(app);
|
||||||
|
|
||||||
let api = CoreApi {
|
let api = CoreApi {
|
||||||
notification_tx,
|
|
||||||
rt: runtime.clone(),
|
rt: runtime.clone(),
|
||||||
core: core.clone(),
|
core: core.clone(),
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,13 +14,12 @@ General Public License for more details.
|
||||||
You should have received a copy of the GNU General Public License along with Kifu. If not, see <https://www.gnu.org/licenses/>.
|
You should have received a copy of the GNU General Public License along with Kifu. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use async_channel::Receiver;
|
use async_std::{
|
||||||
use async_std::task::{spawn, yield_now, JoinHandle};
|
channel::Receiver,
|
||||||
use kifu_core::{Color, Core, CoreNotification, Goban, Player};
|
task::{spawn, yield_now, JoinHandle},
|
||||||
use std::{
|
|
||||||
sync::{Arc, RwLock},
|
|
||||||
time::Duration,
|
|
||||||
};
|
};
|
||||||
|
use kifu_core::{Color, Core, CoreNotification, Goban, Observable, Player};
|
||||||
|
use std::{cell::RefCell, rc::Rc, time::Duration};
|
||||||
|
|
||||||
pub struct GameState {
|
pub struct GameState {
|
||||||
goban: Goban,
|
goban: Goban,
|
||||||
|
@ -41,9 +40,8 @@ struct GameViewModelPrivate {
|
||||||
/// The Game View Model manages the current state of the game. It shows the two player cards, the board, the current capture count, the current player, and it maintains the UI for the clock (bearing in mind that the real clock is managed in the core). This view model should only be created once the details of the game, whether a game in progress or a new game (this view model won't know the difference) is known.
|
/// The Game View Model manages the current state of the game. It shows the two player cards, the board, the current capture count, the current player, and it maintains the UI for the clock (bearing in mind that the real clock is managed in the core). This view model should only be created once the details of the game, whether a game in progress or a new game (this view model won't know the difference) is known.
|
||||||
pub struct GameViewModel {
|
pub struct GameViewModel {
|
||||||
core: Core,
|
core: Core,
|
||||||
notification_handler: JoinHandle<()>,
|
|
||||||
widget: gtk::Box,
|
widget: gtk::Box,
|
||||||
data: Arc<RwLock<GameViewModelPrivate>>,
|
data: Rc<RefCell<GameViewModelPrivate>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GameViewModelPrivate {
|
impl GameViewModelPrivate {
|
||||||
|
@ -52,20 +50,32 @@ impl GameViewModelPrivate {
|
||||||
|
|
||||||
impl GameViewModel {
|
impl GameViewModel {
|
||||||
pub fn new(white: Player, black: Player, game: GameState, core: Core) -> Self {
|
pub fn new(white: Player, black: Player, game: GameState, core: Core) -> Self {
|
||||||
let data = Arc::new(RwLock::new(GameViewModelPrivate {
|
let data = Rc::new(RefCell::new(GameViewModelPrivate {
|
||||||
white,
|
white,
|
||||||
black,
|
black,
|
||||||
state: game,
|
state: game,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let handler = spawn({
|
let s = Self {
|
||||||
let core = core.clone();
|
core,
|
||||||
let data = data.clone();
|
widget: gtk::Box::new(gtk::Orientation::Horizontal, 0),
|
||||||
async move {
|
data,
|
||||||
let notifications = core.subscribe();
|
};
|
||||||
|
|
||||||
|
let notifications = s.core.subscribe();
|
||||||
|
let data = s.data.clone();
|
||||||
|
glib::spawn_future_local(Self::listen(notifications, data));
|
||||||
|
|
||||||
|
s
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn listen(
|
||||||
|
notifications: Receiver<CoreNotification>,
|
||||||
|
data: Rc<RefCell<GameViewModelPrivate>>,
|
||||||
|
) {
|
||||||
loop {
|
loop {
|
||||||
match notifications.recv().await {
|
match notifications.recv().await {
|
||||||
Ok(msg) => data.write().unwrap().handle(msg),
|
Ok(msg) => data.borrow_mut().handle(msg),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
unimplemented!("Should display an error message in the UI: {}", err)
|
unimplemented!("Should display an error message in the UI: {}", err)
|
||||||
}
|
}
|
||||||
|
@ -73,13 +83,4 @@ impl GameViewModel {
|
||||||
yield_now().await;
|
yield_now().await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
Self {
|
|
||||||
core,
|
|
||||||
notification_handler: handler,
|
|
||||||
widget: gtk::Box::new(gtk::Orientation::Horizontal, 0),
|
|
||||||
data,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue