From cc3ad372e6d8dbb8b6877112cbe820906bd219e1 Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Sun, 20 Aug 2023 21:37:40 -0400 Subject: [PATCH 1/7] Flip totally to a libadwaita program --- Cargo.lock | 1 + kifu/gtk/Cargo.toml | 1 + kifu/gtk/src/main.rs | 28 +++++++++++++++++++++++----- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index de43e70..1967a17 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1348,6 +1348,7 @@ dependencies = [ "gtk4", "image", "kifu-core", + "libadwaita", "sgf", "tokio", ] diff --git a/kifu/gtk/Cargo.toml b/kifu/gtk/Cargo.toml index 21918ab..9e60660 100644 --- a/kifu/gtk/Cargo.toml +++ b/kifu/gtk/Cargo.toml @@ -9,6 +9,7 @@ screenplay = [] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +adw = { version = "0.4", package = "libadwaita", features = [ "v1_2" ] } cairo-rs = { version = "0.17" } gio = { version = "0.17" } glib = { version = "0.17" } diff --git a/kifu/gtk/src/main.rs b/kifu/gtk/src/main.rs index 499acf0..47b67f0 100644 --- a/kifu/gtk/src/main.rs +++ b/kifu/gtk/src/main.rs @@ -1,3 +1,4 @@ +use adw::prelude::AdwApplicationWindowExt; use gtk::prelude::*; use kifu_core::{CoreApp, CoreRequest, CoreResponse}; use kifu_gtk::{ @@ -7,14 +8,14 @@ use kifu_gtk::{ }; use std::sync::{Arc, RwLock}; -fn handle_response(api: CoreApi, window: gtk::ApplicationWindow, message: CoreResponse) { +fn handle_response(api: CoreApi, window: adw::ApplicationWindow, message: CoreResponse) { let playing_field = Arc::new(RwLock::new(None)); match message { CoreResponse::HomeView(view) => perftrace("HomeView", || { let api = api.clone(); let home = Home::new(api, view); - window.set_child(Some(&home)); + window.set_content(Some(&home)); }), CoreResponse::PlayingFieldView(view) => perftrace("PlayingFieldView", || { let api = api.clone(); @@ -23,7 +24,7 @@ fn handle_response(api: CoreApi, window: gtk::ApplicationWindow, message: CoreRe if playing_field.is_none() { perftrace("creating a new playing field", || { let field = PlayingField::new(api, view); - window.set_child(Some(&field)); + window.set_content(Some(&field)); *playing_field = Some(field); }) } else { @@ -65,10 +66,25 @@ fn main() { } }); - let app = gtk::Application::builder() + let app = adw::Application::builder() .application_id("com.luminescent-dreams.kifu-gtk") + .resource_base_path("/com/luminescent-dreams/kifu-gtk") .build(); + let action_config = gio::SimpleAction::new("show-config", None); + action_config.connect_activate(|_, _| { + println!("trigger the configuration menu"); + }); + app.add_action(&action_config); + + app.connect_startup(|app| { + println!("connect_startup"); + let menu_model = gio::Menu::new(); + menu_model.append(Some("Quit"), None); + menu_model.append(Some("Configuration"), Some("show-config")); + app.set_menubar(Some(&menu_model)); + }); + app.connect_activate({ let runtime = runtime.clone(); move |app| { @@ -81,9 +97,11 @@ fn main() { core: core.clone(), }; - let window = gtk::ApplicationWindow::new(app); + let window = adw::ApplicationWindow::new(app); window.present(); + window.set_show_menubar(true); + gtk_rx.attach(None, { let api = api.clone(); move |message| { -- 2.44.1 From ff13ff3c0e3f0ed875402834a1f0c4c5971501f8 Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Sun, 20 Aug 2023 22:12:00 -0400 Subject: [PATCH 2/7] Update to a libadwaita app --- kifu/gtk/src/main.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/kifu/gtk/src/main.rs b/kifu/gtk/src/main.rs index 47b67f0..8423550 100644 --- a/kifu/gtk/src/main.rs +++ b/kifu/gtk/src/main.rs @@ -1,5 +1,4 @@ -use adw::prelude::AdwApplicationWindowExt; -use gtk::prelude::*; +use adw::prelude::*; use kifu_core::{CoreApp, CoreRequest, CoreResponse}; use kifu_gtk::{ perftrace, @@ -97,11 +96,13 @@ fn main() { core: core.clone(), }; - let window = adw::ApplicationWindow::new(app); + let window = adw::ApplicationWindow::builder() + .application(app) + .width_request(800) + .height_request(500) + .build(); window.present(); - window.set_show_menubar(true); - gtk_rx.attach(None, { let api = api.clone(); move |message| { -- 2.44.1 From 793cd67218beaf098ee0a38eec4e23ffe2569781 Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Wed, 23 Aug 2023 15:57:09 -0400 Subject: [PATCH 3/7] Add a header bar and content field for applications --- kifu/core/src/database.rs | 13 +++-- kifu/core/src/ui/elements/game_preview.rs | 1 + kifu/gtk/src/main.rs | 14 ++++-- kifu/gtk/src/ui/game_preview.rs | 2 +- kifu/gtk/src/ui/layout.rs | 61 +++++++++++++++++++++++ kifu/gtk/src/ui/mod.rs | 3 ++ sgf/src/go.rs | 3 +- 7 files changed, 86 insertions(+), 11 deletions(-) create mode 100644 kifu/gtk/src/ui/layout.rs diff --git a/kifu/core/src/database.rs b/kifu/core/src/database.rs index cad6a5c..c7e9cc2 100644 --- a/kifu/core/src/database.rs +++ b/kifu/core/src/database.rs @@ -39,11 +39,16 @@ impl Database { .unwrap() .read_to_string(&mut buffer) .unwrap(); - for sgf in parse_sgf(&buffer).unwrap() { - match sgf { - Game::Go(game) => games.push(game), - Game::Unsupported(_) => {} + match parse_sgf(&buffer) { + Ok(sgfs) => { + for sgf in sgfs { + match sgf { + Game::Go(game) => games.push(game), + Game::Unsupported(_) => {} + } + } } + Err(err) => println!("Error parsing {:?}: {:?}", entry.path(), err), } } } diff --git a/kifu/core/src/ui/elements/game_preview.rs b/kifu/core/src/ui/elements/game_preview.rs index b8660f6..fbd00ea 100644 --- a/kifu/core/src/ui/elements/game_preview.rs +++ b/kifu/core/src/ui/elements/game_preview.rs @@ -50,6 +50,7 @@ impl GamePreviewElement { Some(GameResult::Draw) => "Draw".to_owned(), Some(GameResult::Black(ref win)) => format!("Black by {}", format_win(win)), Some(GameResult::White(ref win)) => format!("White by {}", format_win(win)), + Some(GameResult::Unknown(ref text)) => format!("Unknown: {}", text), None => "".to_owned(), }; diff --git a/kifu/gtk/src/main.rs b/kifu/gtk/src/main.rs index 8423550..f9725e9 100644 --- a/kifu/gtk/src/main.rs +++ b/kifu/gtk/src/main.rs @@ -2,19 +2,19 @@ use adw::prelude::*; use kifu_core::{CoreApp, CoreRequest, CoreResponse}; use kifu_gtk::{ perftrace, - ui::{Home, PlayingField}, + ui::{Home, Layout, PlayingField}, CoreApi, }; use std::sync::{Arc, RwLock}; -fn handle_response(api: CoreApi, window: adw::ApplicationWindow, message: CoreResponse) { +fn handle_response(api: CoreApi, layout: Layout, message: CoreResponse) { let playing_field = Arc::new(RwLock::new(None)); match message { CoreResponse::HomeView(view) => perftrace("HomeView", || { let api = api.clone(); let home = Home::new(api, view); - window.set_content(Some(&home)); + layout.set_content(&home); }), CoreResponse::PlayingFieldView(view) => perftrace("PlayingFieldView", || { let api = api.clone(); @@ -23,7 +23,7 @@ fn handle_response(api: CoreApi, window: adw::ApplicationWindow, message: CoreRe if playing_field.is_none() { perftrace("creating a new playing field", || { let field = PlayingField::new(api, view); - window.set_content(Some(&field)); + layout.set_content(&field); *playing_field = Some(field); }) } else { @@ -101,13 +101,17 @@ fn main() { .width_request(800) .height_request(500) .build(); + + let layout = Layout::new(); + window.set_content(Some(&layout)); + window.present(); gtk_rx.attach(None, { let api = api.clone(); move |message| { perftrace("handle_response", || { - handle_response(api.clone(), window.clone(), message) + handle_response(api.clone(), layout.clone(), message) }); Continue(true) } diff --git a/kifu/gtk/src/ui/game_preview.rs b/kifu/gtk/src/ui/game_preview.rs index feafa96..ff87214 100644 --- a/kifu/gtk/src/ui/game_preview.rs +++ b/kifu/gtk/src/ui/game_preview.rs @@ -31,7 +31,7 @@ impl GamePreview { let s: Self = Object::builder().build(); s.set_orientation(gtk::Orientation::Horizontal); s.set_homogeneous(true); - s.set_hexpand(true); + s.set_hexpand(false); s.append(&s.imp().date); s.append(&s.imp().title); diff --git a/kifu/gtk/src/ui/layout.rs b/kifu/gtk/src/ui/layout.rs new file mode 100644 index 0000000..16cb406 --- /dev/null +++ b/kifu/gtk/src/ui/layout.rs @@ -0,0 +1,61 @@ +use glib::Object; +use gtk::{prelude::*, subclass::prelude::*}; +use std::{cell::RefCell, rc::Rc}; + +// Deprecated even from day 1. We want to use ToolbarView as soon as it's available in the versions +// of Libadwaita available in NixOS. +pub struct LayoutPrivate { + pub header: adw::HeaderBar, + pub content: Rc>, +} + +impl Default for LayoutPrivate { + fn default() -> Self { + let header = adw::HeaderBar::builder() + .title_widget(>k::Label::new(Some("Placeholder Title"))) + .show_start_title_buttons(true) + .show_end_title_buttons(true) + .build(); + let content = adw::StatusPage::builder().title("Nothing here").build(); + + Self { + header, + content: Rc::new(RefCell::new(content.into())), + } + } +} + +#[glib::object_subclass] +impl ObjectSubclass for LayoutPrivate { + const NAME: &'static str = "Layout"; + type Type = Layout; + type ParentType = gtk::Box; +} + +impl ObjectImpl for LayoutPrivate {} +impl WidgetImpl for LayoutPrivate {} +impl BoxImpl for LayoutPrivate {} + +glib::wrapper! { + pub struct Layout(ObjectSubclass) @extends gtk::Box, gtk::Widget, @implements gtk::Orientable; +} + +impl Layout { + pub fn new() -> Self { + let s: Self = Object::builder().build(); + s.set_orientation(gtk::Orientation::Vertical); + s.set_homogeneous(false); + s.append(&s.imp().header); + s.append(&*s.imp().content.borrow()); + + s + } + + pub fn set_content(&self, content: &impl IsA) { + let mut widget = self.imp().content.borrow_mut(); + + self.remove(&*widget); + *widget = content.clone().upcast::(); + self.append(&*widget); + } +} diff --git a/kifu/gtk/src/ui/mod.rs b/kifu/gtk/src/ui/mod.rs index ae70aa6..565384b 100644 --- a/kifu/gtk/src/ui/mod.rs +++ b/kifu/gtk/src/ui/mod.rs @@ -7,6 +7,9 @@ pub use game_preview::GamePreview; mod library; pub use library::Library; +mod layout; +pub use layout::Layout; + mod player_card; pub use player_card::PlayerCard; diff --git a/sgf/src/go.rs b/sgf/src/go.rs index 283ba2d..0fc3db0 100644 --- a/sgf/src/go.rs +++ b/sgf/src/go.rs @@ -242,6 +242,7 @@ pub enum GameResult { Draw, Black(Win), White(Win), + Unknown(String), } impl TryFrom<&str> for GameResult { @@ -256,7 +257,7 @@ impl TryFrom<&str> for GameResult { let res = match parts[0].to_ascii_lowercase().as_str() { "b" => GameResult::Black, "w" => GameResult::White, - _ => panic!("unknown result format"), + _ => return Ok(GameResult::Unknown(parts[0].to_owned())), }; match parts[1].to_ascii_lowercase().as_str() { "r" | "resign" => Ok(res(Win::Resignation)), -- 2.44.1 From 3998538e8856bf6e7a12a7b2db86a6193f28a673 Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Wed, 23 Aug 2023 17:31:34 -0400 Subject: [PATCH 4/7] Set up the hamburger menu --- kifu/gtk/src/ui/layout.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/kifu/gtk/src/ui/layout.rs b/kifu/gtk/src/ui/layout.rs index 16cb406..f669c2a 100644 --- a/kifu/gtk/src/ui/layout.rs +++ b/kifu/gtk/src/ui/layout.rs @@ -16,6 +16,17 @@ impl Default for LayoutPrivate { .show_start_title_buttons(true) .show_end_title_buttons(true) .build(); + + let app_menu = gio::Menu::new(); + app_menu.append(Some("Configuration"), Some("show-config")); + + let hamburger = gtk::MenuButton::builder() + .icon_name("open-menu-symbolic") + .build(); + hamburger.set_menu_model(Some(&app_menu)); + + header.pack_end(&hamburger); + let content = adw::StatusPage::builder().title("Nothing here").build(); Self { -- 2.44.1 From 0bf6e079a2d0d0f053cd9f0e0251ebfa2f978145 Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Wed, 23 Aug 2023 17:51:51 -0400 Subject: [PATCH 5/7] Set up the configuration action --- kifu/gtk/src/main.rs | 8 -------- kifu/gtk/src/ui/layout.rs | 5 ++--- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/kifu/gtk/src/main.rs b/kifu/gtk/src/main.rs index f9725e9..0ff75e6 100644 --- a/kifu/gtk/src/main.rs +++ b/kifu/gtk/src/main.rs @@ -76,14 +76,6 @@ fn main() { }); app.add_action(&action_config); - app.connect_startup(|app| { - println!("connect_startup"); - let menu_model = gio::Menu::new(); - menu_model.append(Some("Quit"), None); - menu_model.append(Some("Configuration"), Some("show-config")); - app.set_menubar(Some(&menu_model)); - }); - app.connect_activate({ let runtime = runtime.clone(); move |app| { diff --git a/kifu/gtk/src/ui/layout.rs b/kifu/gtk/src/ui/layout.rs index f669c2a..868e9f0 100644 --- a/kifu/gtk/src/ui/layout.rs +++ b/kifu/gtk/src/ui/layout.rs @@ -13,12 +13,11 @@ impl Default for LayoutPrivate { fn default() -> Self { let header = adw::HeaderBar::builder() .title_widget(>k::Label::new(Some("Placeholder Title"))) - .show_start_title_buttons(true) - .show_end_title_buttons(true) .build(); let app_menu = gio::Menu::new(); - app_menu.append(Some("Configuration"), Some("show-config")); + let menu_item = gio::MenuItem::new(Some("Configuration"), Some("app.show-config")); + app_menu.append_item(&menu_item); let hamburger = gtk::MenuButton::builder() .icon_name("open-menu-symbolic") -- 2.44.1 From 5439e2ac045002a2255e4b1a5cdd36824c52cd43 Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Thu, 24 Aug 2023 20:24:41 -0400 Subject: [PATCH 6/7] Set up a configuration UI --- kifu/core/src/api.rs | 7 ++++- kifu/core/src/ui/configuration.rs | 24 ++++++++++++++ kifu/core/src/ui/elements/action.rs | 10 ------ kifu/core/src/ui/elements/mod.rs | 30 +++++++++++++++++- kifu/core/src/ui/mod.rs | 5 ++- kifu/gtk/src/main.rs | 38 +++++++++++++++++----- kifu/gtk/src/ui/config.rs | 49 +++++++++++++++++++++++++++++ kifu/gtk/src/ui/layout.rs | 2 +- kifu/gtk/src/ui/mod.rs | 3 ++ 9 files changed, 147 insertions(+), 21 deletions(-) create mode 100644 kifu/core/src/ui/configuration.rs delete mode 100644 kifu/core/src/ui/elements/action.rs create mode 100644 kifu/gtk/src/ui/config.rs diff --git a/kifu/core/src/api.rs b/kifu/core/src/api.rs index fead3e7..9ac63a0 100644 --- a/kifu/core/src/api.rs +++ b/kifu/core/src/api.rs @@ -1,6 +1,6 @@ use crate::{ types::{AppState, Config, DatabasePath, GameState, Player, Rank}, - ui::{home, playing_field, HomeView, PlayingFieldView}, + ui::{configuration, home, playing_field, ConfigurationView, HomeView, PlayingFieldView}, }; use serde::{Deserialize, Serialize}; use std::sync::{Arc, RwLock}; @@ -12,6 +12,7 @@ use typeshare::typeshare; pub enum CoreRequest { CreateGame(CreateGameRequest), Home, + OpenConfiguration, PlayingField, PlayStone(PlayStoneRequest), StartGame, @@ -57,6 +58,7 @@ impl From for Player { #[typeshare] #[serde(tag = "type", content = "content")] pub enum CoreResponse { + ConfigurationView(ConfigurationView), HomeView(HomeView), PlayingFieldView(PlayingFieldView), } @@ -116,6 +118,9 @@ impl CoreApp { CoreRequest::Home => { CoreResponse::HomeView(home(self.state.read().unwrap().database.all_games())) } + CoreRequest::OpenConfiguration => { + CoreResponse::ConfigurationView(configuration(&self.config)) + } CoreRequest::PlayingField => { let app_state = self.state.read().unwrap(); let game = app_state.game.as_ref().unwrap(); diff --git a/kifu/core/src/ui/configuration.rs b/kifu/core/src/ui/configuration.rs new file mode 100644 index 0000000..c15f408 --- /dev/null +++ b/kifu/core/src/ui/configuration.rs @@ -0,0 +1,24 @@ +use crate::{ + types::{Config, DatabasePath}, + ui::Field, +}; +use serde::{Deserialize, Serialize}; +use typeshare::typeshare; + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[typeshare] +pub struct ConfigurationView { + pub library: Field<()>, +} + +pub fn configuration(config: &Config) -> ConfigurationView { + let path: Option = config.get(); + ConfigurationView { + library: Field { + id: "library-path-field".to_owned(), + label: "Library".to_owned(), + value: path.map(|path| path.to_string_lossy().into_owned()), + action: (), + }, + } +} diff --git a/kifu/core/src/ui/elements/action.rs b/kifu/core/src/ui/elements/action.rs deleted file mode 100644 index 69c5514..0000000 --- a/kifu/core/src/ui/elements/action.rs +++ /dev/null @@ -1,10 +0,0 @@ -use serde::{Deserialize, Serialize}; -use typeshare::typeshare; - -#[derive(Clone, Debug, Serialize, Deserialize)] -#[typeshare] -pub struct Action { - pub id: String, - pub label: String, - pub action: A, -} diff --git a/kifu/core/src/ui/elements/mod.rs b/kifu/core/src/ui/elements/mod.rs index 0a6140f..61d07b4 100644 --- a/kifu/core/src/ui/elements/mod.rs +++ b/kifu/core/src/ui/elements/mod.rs @@ -1,3 +1,31 @@ -pub mod action; +use serde::{Deserialize, Serialize}; +use typeshare::typeshare; + pub mod game_preview; pub mod menu; + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[typeshare] +pub struct Action { + pub id: String, + pub label: String, + pub action: A, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[typeshare] +pub struct Toggle { + pub id: String, + pub label: String, + pub value: bool, + pub action: A, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[typeshare] +pub struct Field { + pub id: String, + pub label: String, + pub value: Option, + pub action: A, +} diff --git a/kifu/core/src/ui/mod.rs b/kifu/core/src/ui/mod.rs index 7233ec5..9c63d14 100644 --- a/kifu/core/src/ui/mod.rs +++ b/kifu/core/src/ui/mod.rs @@ -1,5 +1,8 @@ +mod configuration; +pub use configuration::{configuration, ConfigurationView}; + mod elements; -pub use elements::{action::Action, game_preview::GamePreviewElement, menu::Menu}; +pub use elements::{game_preview::GamePreviewElement, menu::Menu, Action, Field, Toggle}; mod playing_field; pub use playing_field::{playing_field, PlayingFieldView}; diff --git a/kifu/gtk/src/main.rs b/kifu/gtk/src/main.rs index 0ff75e6..9cf9198 100644 --- a/kifu/gtk/src/main.rs +++ b/kifu/gtk/src/main.rs @@ -2,7 +2,7 @@ use adw::prelude::*; use kifu_core::{CoreApp, CoreRequest, CoreResponse}; use kifu_gtk::{ perftrace, - ui::{Home, Layout, PlayingField}, + ui::{ConfigurationPage, Home, Layout, PlayingField}, CoreApi, }; use std::sync::{Arc, RwLock}; @@ -10,6 +10,27 @@ use std::sync::{Arc, RwLock}; fn handle_response(api: CoreApi, layout: Layout, message: CoreResponse) { let playing_field = Arc::new(RwLock::new(None)); match message { + CoreResponse::ConfigurationView(view) => perftrace("ConfigurationView", || { + /* + let config_group = adw::PreferencesGroup::builder().build(); + config_group.add( + &adw::EntryRow::builder() + .name("library") + .title("Library path") + .build(), + ); + let config_page = adw::PreferencesPage::new(); + config_page.set_name(Some("preferences")); + config_page.add(&config_group); + */ + + let config_page = ConfigurationPage::new(view); + + let window = adw::PreferencesWindow::new(); + window.add(&config_page); + window.set_visible_page(&config_page); + window.present(); + }), CoreResponse::HomeView(view) => perftrace("HomeView", || { let api = api.clone(); @@ -70,12 +91,6 @@ fn main() { .resource_base_path("/com/luminescent-dreams/kifu-gtk") .build(); - let action_config = gio::SimpleAction::new("show-config", None); - action_config.connect_activate(|_, _| { - println!("trigger the configuration menu"); - }); - app.add_action(&action_config); - app.connect_activate({ let runtime = runtime.clone(); move |app| { @@ -88,6 +103,15 @@ fn main() { core: core.clone(), }; + let action_config = gio::SimpleAction::new("show-config", None); + action_config.connect_activate({ + let api = api.clone(); + move |_, _| { + api.dispatch(CoreRequest::OpenConfiguration); + } + }); + app.add_action(&action_config); + let window = adw::ApplicationWindow::builder() .application(app) .width_request(800) diff --git a/kifu/gtk/src/ui/config.rs b/kifu/gtk/src/ui/config.rs new file mode 100644 index 0000000..b854839 --- /dev/null +++ b/kifu/gtk/src/ui/config.rs @@ -0,0 +1,49 @@ +use adw::{prelude::*, subclass::prelude::*}; +use glib::Object; +use kifu_core::ui::ConfigurationView; + +#[derive(Default)] +pub struct ConfigurationPagePrivate {} + +#[glib::object_subclass] +impl ObjectSubclass for ConfigurationPagePrivate { + const NAME: &'static str = "Configuration"; + type Type = ConfigurationPage; + type ParentType = adw::PreferencesPage; +} + +impl ObjectImpl for ConfigurationPagePrivate {} +impl WidgetImpl for ConfigurationPagePrivate {} +impl PreferencesPageImpl for ConfigurationPagePrivate {} + +glib::wrapper! { + pub struct ConfigurationPage(ObjectSubclass) + @extends adw::PreferencesPage, gtk::Widget, + @implements gtk::Orientable; +} + +impl ConfigurationPage { + pub fn new(view: ConfigurationView) -> Self { + let s: Self = Object::builder().build(); + + let group = adw::PreferencesGroup::builder().build(); + + let library_entry = &adw::EntryRow::builder() + .name("library-path") + .title(view.library.label) + .show_apply_button(true) + .build(); + if let Some(path) = view.library.value { + library_entry.set_text(&path); + } + library_entry.connect_apply(|entry| { + println!("Set the library path to {}", entry.text()); + }); + + group.add(library_entry); + + s.add(&group); + + s + } +} diff --git a/kifu/gtk/src/ui/layout.rs b/kifu/gtk/src/ui/layout.rs index 868e9f0..5057bfc 100644 --- a/kifu/gtk/src/ui/layout.rs +++ b/kifu/gtk/src/ui/layout.rs @@ -12,7 +12,7 @@ pub struct LayoutPrivate { impl Default for LayoutPrivate { fn default() -> Self { let header = adw::HeaderBar::builder() - .title_widget(>k::Label::new(Some("Placeholder Title"))) + .title_widget(>k::Label::new(Some("Kifu"))) .build(); let app_menu = gio::Menu::new(); diff --git a/kifu/gtk/src/ui/mod.rs b/kifu/gtk/src/ui/mod.rs index 565384b..de1c5fe 100644 --- a/kifu/gtk/src/ui/mod.rs +++ b/kifu/gtk/src/ui/mod.rs @@ -1,6 +1,9 @@ mod chat; pub use chat::Chat; +mod config; +pub use config::ConfigurationPage; + mod game_preview; pub use game_preview::GamePreview; -- 2.44.1 From 784f3ff7f4808988ba101c78e2761509e66a2961 Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Thu, 24 Aug 2023 20:52:27 -0400 Subject: [PATCH 7/7] Be able to update the library path in the core --- kifu/core/src/api.rs | 48 +++++++++++++++++++++++---------------- kifu/core/src/lib.rs | 3 ++- kifu/core/src/types.rs | 2 +- kifu/gtk/src/main.rs | 18 ++++----------- kifu/gtk/src/ui/config.rs | 11 +++++---- 5 files changed, 43 insertions(+), 39 deletions(-) diff --git a/kifu/core/src/api.rs b/kifu/core/src/api.rs index 9ac63a0..76d5264 100644 --- a/kifu/core/src/api.rs +++ b/kifu/core/src/api.rs @@ -1,15 +1,19 @@ use crate::{ - types::{AppState, Config, DatabasePath, GameState, Player, Rank}, + types::{AppState, Config, ConfigOption, DatabasePath, GameState, Player, Rank}, ui::{configuration, home, playing_field, ConfigurationView, HomeView, PlayingFieldView}, }; use serde::{Deserialize, Serialize}; -use std::sync::{Arc, RwLock}; +use std::{ + path::PathBuf, + sync::{Arc, RwLock}, +}; use typeshare::typeshare; #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[typeshare] #[serde(tag = "type", content = "content")] pub enum CoreRequest { + ChangeSetting(ChangeSettingRequest), CreateGame(CreateGameRequest), Home, OpenConfiguration, @@ -18,6 +22,13 @@ pub enum CoreRequest { StartGame, } +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[typeshare] +#[serde(tag = "type", content = "content")] +pub enum ChangeSettingRequest { + LibraryPath(String), +} + #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] #[typeshare] pub struct PlayStoneRequest { @@ -61,11 +72,12 @@ pub enum CoreResponse { ConfigurationView(ConfigurationView), HomeView(HomeView), PlayingFieldView(PlayingFieldView), + UpdatedConfigurationView(ConfigurationView), } #[derive(Clone, Debug)] pub struct CoreApp { - config: Config, + config: Arc>, state: Arc>, } @@ -76,25 +88,23 @@ impl CoreApp { let db_path: DatabasePath = config.get().unwrap(); let state = Arc::new(RwLock::new(AppState::new(db_path))); - Self { config, state } + Self { + config: Arc::new(RwLock::new(config)), + state, + } } pub async fn dispatch(&self, request: CoreRequest) -> CoreResponse { 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::ChangeSetting(request) => match request { + ChangeSettingRequest::LibraryPath(path) => { + let mut config = self.config.write().unwrap(); + config.set(ConfigOption::DatabasePath(DatabasePath(PathBuf::from( + path, + )))); + CoreResponse::UpdatedConfigurationView(configuration(&config)) + } + }, CoreRequest::CreateGame(create_request) => { let mut app_state = self.state.write().unwrap(); let white_player = { @@ -119,7 +129,7 @@ impl CoreApp { CoreResponse::HomeView(home(self.state.read().unwrap().database.all_games())) } CoreRequest::OpenConfiguration => { - CoreResponse::ConfigurationView(configuration(&self.config)) + CoreResponse::ConfigurationView(configuration(&self.config.read().unwrap())) } CoreRequest::PlayingField => { let app_state = self.state.read().unwrap(); diff --git a/kifu/core/src/lib.rs b/kifu/core/src/lib.rs index b817d92..ceda4df 100644 --- a/kifu/core/src/lib.rs +++ b/kifu/core/src/lib.rs @@ -3,7 +3,8 @@ extern crate config_derive; mod api; pub use api::{ - CoreApp, CoreRequest, CoreResponse, CreateGameRequest, HotseatPlayerRequest, PlayerInfoRequest, + ChangeSettingRequest, CoreApp, CoreRequest, CoreResponse, CreateGameRequest, + HotseatPlayerRequest, PlayerInfoRequest, }; mod board; diff --git a/kifu/core/src/types.rs b/kifu/core/src/types.rs index 28ff8f3..5754c42 100644 --- a/kifu/core/src/types.rs +++ b/kifu/core/src/types.rs @@ -16,7 +16,7 @@ define_config! { } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ConfigOption)] -pub struct DatabasePath(PathBuf); +pub struct DatabasePath(pub PathBuf); impl std::ops::Deref for DatabasePath { type Target = PathBuf; diff --git a/kifu/gtk/src/main.rs b/kifu/gtk/src/main.rs index 9cf9198..3217134 100644 --- a/kifu/gtk/src/main.rs +++ b/kifu/gtk/src/main.rs @@ -11,20 +11,7 @@ fn handle_response(api: CoreApi, layout: Layout, message: CoreResponse) { let playing_field = Arc::new(RwLock::new(None)); match message { CoreResponse::ConfigurationView(view) => perftrace("ConfigurationView", || { - /* - let config_group = adw::PreferencesGroup::builder().build(); - config_group.add( - &adw::EntryRow::builder() - .name("library") - .title("Library path") - .build(), - ); - let config_page = adw::PreferencesPage::new(); - config_page.set_name(Some("preferences")); - config_page.add(&config_group); - */ - - let config_page = ConfigurationPage::new(view); + let config_page = ConfigurationPage::new(api, view); let window = adw::PreferencesWindow::new(); window.add(&config_page); @@ -51,6 +38,9 @@ fn handle_response(api: CoreApi, layout: Layout, message: CoreResponse) { playing_field.as_ref().map(|field| field.update_view(view)); } }), + CoreResponse::UpdatedConfigurationView(view) => perftrace("UpdatedConfiguration", || { + println!("updated configuration: {:?}", view); + }), } } diff --git a/kifu/gtk/src/ui/config.rs b/kifu/gtk/src/ui/config.rs index b854839..7dcb80d 100644 --- a/kifu/gtk/src/ui/config.rs +++ b/kifu/gtk/src/ui/config.rs @@ -1,6 +1,7 @@ +use crate::CoreApi; use adw::{prelude::*, subclass::prelude::*}; use glib::Object; -use kifu_core::ui::ConfigurationView; +use kifu_core::{ui::ConfigurationView, ChangeSettingRequest, CoreRequest}; #[derive(Default)] pub struct ConfigurationPagePrivate {} @@ -23,7 +24,7 @@ glib::wrapper! { } impl ConfigurationPage { - pub fn new(view: ConfigurationView) -> Self { + pub fn new(api: CoreApi, view: ConfigurationView) -> Self { let s: Self = Object::builder().build(); let group = adw::PreferencesGroup::builder().build(); @@ -36,8 +37,10 @@ impl ConfigurationPage { if let Some(path) = view.library.value { library_entry.set_text(&path); } - library_entry.connect_apply(|entry| { - println!("Set the library path to {}", entry.text()); + library_entry.connect_apply(move |entry| { + api.dispatch(CoreRequest::ChangeSetting( + ChangeSettingRequest::LibraryPath(entry.text().into()), + )); }); group.add(library_entry); -- 2.44.1