diff --git a/babylonia-terminal-gui/src/lib.rs b/babylonia-terminal-gui/src/lib.rs index cdd834c..e076d8b 100644 --- a/babylonia-terminal-gui/src/lib.rs +++ b/babylonia-terminal-gui/src/lib.rs @@ -1,112 +1,11 @@ -use babylonia_terminal_sdk::game_state::GameState; use log::debug; -use manager::run_game; -use relm4::{ - gtk::{ - self, - prelude::{BoxExt, ButtonExt, GtkWindowExt, OrientableExt, WidgetExt}, - }, - loading_widgets::LoadingWidgets, - prelude::{AsyncComponent, AsyncComponentParts, SimpleAsyncComponent}, - view, ComponentParts, RelmWidgetExt, SimpleComponent, -}; -use relm4::{Component, RelmApp}; +use relm4::RelmApp; mod manager; +mod ui; pub fn run() { debug!("Start GUI!"); let app = RelmApp::new("moe.celica.BabyloniaTerminal").with_args(vec![]); - app.run_async::(None); -} - -#[derive(Debug)] -pub enum MainWindowMsg { - RunGame, -} - -struct MainWindow { - game_state: GameState, - is_game_running: bool, -} - -impl MainWindow { - fn new(game_state: GameState) -> Self { - MainWindow { - game_state, - is_game_running: false, - } - } -} - -#[relm4::component(async)] -impl SimpleAsyncComponent for MainWindow { - type Input = MainWindowMsg; - - type Output = (); - - type Init = Option; - - view! { - #[root] - gtk::Window { - gtk::Box { - set_orientation: gtk::Orientation::Vertical, - set_spacing: 5, - set_margin_all: 5, - - #[name(start_button)] - gtk::Button { - set_label: "Start game", - connect_clicked => MainWindowMsg::RunGame, - }, - } - } - } - - async fn init( - game_state: Self::Init, - root: Self::Root, - sender: relm4::AsyncComponentSender, - ) -> AsyncComponentParts { - let model; - if game_state.is_none() { - model = MainWindow::new( - babylonia_terminal_sdk::game_state::GameState::get_current_state() - .await - .unwrap(), - ); - } else { - model = MainWindow::new(game_state.unwrap()); - } - - let widgets = view_output!(); - - AsyncComponentParts { model, widgets } - } - - fn init_loading_widgets(root: Self::Root) -> Option { - view! { - #[local] - root { - set_title: Some("Babylonia Terminal"), - set_default_width: 700, - set_default_height: 300, - - #[name(spinner)] - gtk::Spinner { - start: (), - set_halign: gtk::Align::Center, - } - } - } - - Some(LoadingWidgets::new(root, spinner)) - } - - async fn update(&mut self, message: Self::Input, _sender: relm4::AsyncComponentSender) { - match message { - MainWindowMsg::RunGame => run_game().await, - } - } + ui::run(app); } diff --git a/babylonia-terminal-gui/src/manager.rs b/babylonia-terminal-gui/src/manager.rs index 63c8495..f19e818 100644 --- a/babylonia-terminal-gui/src/manager.rs +++ b/babylonia-terminal-gui/src/manager.rs @@ -3,9 +3,14 @@ use babylonia_terminal_sdk::{ game_manager::GameManager, }; use log::error; -use relm4::tokio::sync::OnceCell; +use relm4::{ + tokio::{self, sync::OnceCell}, + Worker, +}; use wincompatlib::prelude::Proton; +use crate::ui; + static PROTON: OnceCell = OnceCell::const_new(); pub async fn get_proton() -> Proton { @@ -31,3 +36,38 @@ pub async fn run_game() { GameManager::start_game(&proton, game_dir.unwrap(), None, false).await; } + +#[derive(Debug)] +pub enum HandleGameProcessMsg { + RunGame, +} + +pub struct HandleGameProcess; + +impl Worker for HandleGameProcess { + type Init = (); + + type Input = HandleGameProcessMsg; + + type Output = ui::MainWindowMsg; + + fn init(_init: Self::Init, _sender: relm4::ComponentSender) -> Self { + Self + } + + fn update(&mut self, message: Self::Input, sender: relm4::ComponentSender) { + match message { + HandleGameProcessMsg::RunGame => { + tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .unwrap() + .block_on(async { + sender.output(ui::MainWindowMsg::SetIsGameRunning(true)); + run_game().await; + sender.output(ui::MainWindowMsg::SetIsGameRunning(false)); + }); + } + } + } +} diff --git a/babylonia-terminal-gui/src/ui/mod.rs b/babylonia-terminal-gui/src/ui/mod.rs new file mode 100644 index 0000000..843acb5 --- /dev/null +++ b/babylonia-terminal-gui/src/ui/mod.rs @@ -0,0 +1,118 @@ +use std::convert::identity; + +use crate::manager; +use babylonia_terminal_sdk::game_state::GameState; + +use relm4::{ + gtk::{ + self, + prelude::{BoxExt, ButtonExt, GtkWindowExt, OrientableExt, WidgetExt}, + }, + loading_widgets::LoadingWidgets, + prelude::{AsyncComponentParts, SimpleAsyncComponent}, + view, Component, RelmApp, RelmWidgetExt, WorkerController, +}; + +pub fn run(app: RelmApp) { + app.run_async::(None); +} + +#[derive(Debug)] +pub enum MainWindowMsg { + SetIsGameRunning(bool), +} + +struct MainWindow { + game_handler: WorkerController, + game_state: GameState, + is_game_running: bool, +} + +impl MainWindow { + fn new(game_state: GameState, sender: relm4::AsyncComponentSender) -> Self { + MainWindow { + game_state, + is_game_running: false, + game_handler: manager::HandleGameProcess::builder() + .detach_worker(()) + .forward(sender.input_sender(), identity), + } + } +} + +#[relm4::component(async)] +impl SimpleAsyncComponent for MainWindow { + type Input = MainWindowMsg; + + type Output = (); + + type Init = Option; + + view! { + #[root] + gtk::Window { + gtk::Box { + set_orientation: gtk::Orientation::Vertical, + set_spacing: 5, + set_margin_all: 5, + + #[name(start_button)] + gtk::Button { + #[watch] + set_sensitive: !model.is_game_running, + set_label: "Start game", + connect_clicked[sender = model.game_handler.sender().clone()] => move |_| { + sender.send(manager::HandleGameProcessMsg::RunGame).unwrap(); + }, + }, + } + } + } + + async fn init( + game_state: Self::Init, + root: Self::Root, + sender: relm4::AsyncComponentSender, + ) -> AsyncComponentParts { + let model; + if game_state.is_none() { + model = MainWindow::new( + babylonia_terminal_sdk::game_state::GameState::get_current_state() + .await + .unwrap(), + sender, + ); + } else { + model = MainWindow::new(game_state.unwrap(), sender); + } + + let widgets = view_output!(); + + AsyncComponentParts { model, widgets } + } + + fn init_loading_widgets(root: Self::Root) -> Option { + view! { + #[local] + root { + set_title: Some("Babylonia Terminal"), + set_default_width: 700, + set_default_height: 300, + + #[name(spinner)] + gtk::Spinner { + start: (), + set_halign: gtk::Align::Center, + } + } + } + + Some(LoadingWidgets::new(root, spinner)) + } + + async fn update(&mut self, message: Self::Input, _sender: relm4::AsyncComponentSender) { + match message { + MainWindowMsg::SetIsGameRunning(value) => self.is_game_running = value, + } + } +}