Set up the settings user interface #225
|
@ -1,13 +1,12 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
database::Database,
|
database::Database,
|
||||||
types::{AppState, Config, ConfigOption, DatabasePath, GameState, Player, Rank},
|
types::{AppState, Config, ConfigOption, LibraryPath, GameState, Player, Rank},
|
||||||
ui::{configuration, home, playing_field, ConfigurationView, HomeView, PlayingFieldView},
|
|
||||||
};
|
};
|
||||||
use async_std::channel::{Receiver, Sender};
|
use async_std::channel::{Receiver, Sender};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{
|
use std::{
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
sync::{Arc, RwLock},
|
sync::{Arc, RwLock, RwLockReadGuard},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait Observable<T> {
|
pub trait Observable<T> {
|
||||||
|
@ -62,6 +61,7 @@ impl From<HotseatPlayerRequest> for Player {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub enum CoreResponse {
|
pub enum CoreResponse {
|
||||||
ConfigurationView(ConfigurationView),
|
ConfigurationView(ConfigurationView),
|
||||||
|
@ -69,6 +69,7 @@ pub enum CoreResponse {
|
||||||
PlayingFieldView(PlayingFieldView),
|
PlayingFieldView(PlayingFieldView),
|
||||||
UpdatedConfigurationView(ConfigurationView),
|
UpdatedConfigurationView(ConfigurationView),
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub enum CoreNotification {
|
pub enum CoreNotification {
|
||||||
|
@ -79,7 +80,7 @@ pub enum CoreNotification {
|
||||||
pub struct Core {
|
pub struct Core {
|
||||||
// config: Arc<RwLock<Config>>,
|
// config: Arc<RwLock<Config>>,
|
||||||
// state: Arc<RwLock<AppState>>,
|
// state: Arc<RwLock<AppState>>,
|
||||||
database: Arc<RwLock<Option<Database>>>,
|
library: Arc<RwLock<Option<Database>>>,
|
||||||
subscribers: Arc<RwLock<Vec<Sender<CoreNotification>>>>,
|
subscribers: Arc<RwLock<Vec<Sender<CoreNotification>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,11 +93,15 @@ impl Core {
|
||||||
Self {
|
Self {
|
||||||
// config: Arc::new(RwLock::new(config)),
|
// config: Arc::new(RwLock::new(config)),
|
||||||
// state,
|
// state,
|
||||||
database: Arc::new(RwLock::new(None)),
|
library: Arc::new(RwLock::new(None)),
|
||||||
subscribers: Arc::new(RwLock::new(vec![])),
|
subscribers: Arc::new(RwLock::new(vec![])),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn library<'a>(&'a self) -> RwLockReadGuard<'_, Option<Database>> {
|
||||||
|
self.library.read().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
pub async fn dispatch(&self, request: CoreRequest) -> CoreResponse {
|
pub async fn dispatch(&self, request: CoreRequest) -> CoreResponse {
|
||||||
match request {
|
match request {
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
extern crate config_derive;
|
extern crate config_derive;
|
||||||
|
|
||||||
mod api;
|
mod api;
|
||||||
pub use api::{
|
pub use api::{Core, CoreNotification, Observable};
|
||||||
ChangeSettingRequest, Core, CoreNotification, CoreRequest, CoreResponse, CreateGameRequest,
|
|
||||||
HotseatPlayerRequest, Observable, PlayerInfoRequest,
|
|
||||||
};
|
|
||||||
|
|
||||||
mod board;
|
mod board;
|
||||||
pub use board::*;
|
pub use board::*;
|
||||||
|
@ -12,6 +9,5 @@ pub use board::*;
|
||||||
mod database;
|
mod database;
|
||||||
|
|
||||||
mod types;
|
mod types;
|
||||||
pub use types::{BoardError, Color, Config, ConfigOption, DatabasePath, Player, Rank, Size};
|
pub use types::{BoardError, Color, Config, ConfigOption, LibraryPath, Player, Rank, Size};
|
||||||
|
|
||||||
pub mod ui;
|
|
||||||
|
|
|
@ -10,21 +10,21 @@ use std::{path::PathBuf, time::Duration};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
define_config! {
|
define_config! {
|
||||||
DatabasePath(DatabasePath),
|
LibraryPath(LibraryPath),
|
||||||
Me(Me),
|
Me(Me),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ConfigOption)]
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ConfigOption)]
|
||||||
pub struct DatabasePath(pub PathBuf);
|
pub struct LibraryPath(pub PathBuf);
|
||||||
|
|
||||||
impl std::ops::Deref for DatabasePath {
|
impl std::ops::Deref for LibraryPath {
|
||||||
type Target = PathBuf;
|
type Target = PathBuf;
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<String> for DatabasePath {
|
impl From<String> for LibraryPath {
|
||||||
fn from(s: String) -> Self {
|
fn from(s: String) -> Self {
|
||||||
Self(PathBuf::from(s))
|
Self(PathBuf::from(s))
|
||||||
}
|
}
|
||||||
|
@ -78,7 +78,7 @@ pub struct AppState {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AppState {
|
impl AppState {
|
||||||
pub fn new(database_path: DatabasePath) -> Self {
|
pub fn new(database_path: LibraryPath) -> Self {
|
||||||
Self {
|
Self {
|
||||||
game: Some(GameState::default()),
|
game: Some(GameState::default()),
|
||||||
database: Database::open_path(database_path.to_path_buf()).unwrap(),
|
database: Database::open_path(database_path.to_path_buf()).unwrap(),
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
use crate::{
|
|
||||||
types::{Config, DatabasePath},
|
|
||||||
ui::Field,
|
|
||||||
};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
||||||
pub struct ConfigurationView {
|
|
||||||
pub library: Field<()>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn configuration(config: &Config) -> ConfigurationView {
|
|
||||||
let path: Option<DatabasePath> = 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: (),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -9,7 +9,7 @@ screenplay = []
|
||||||
# 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]
|
||||||
adw = { version = "0.5", package = "libadwaita", features = [ "v1_2" ] }
|
adw = { version = "0.5", package = "libadwaita", features = [ "v1_4" ] }
|
||||||
async-channel = { version = "2" }
|
async-channel = { version = "2" }
|
||||||
async-std = { version = "1" }
|
async-std = { version = "1" }
|
||||||
cairo-rs = { version = "0.18" }
|
cairo-rs = { version = "0.18" }
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<schemalist>
|
<schemalist>
|
||||||
<schema id="com.luminescent-dreams.kifu-gtk.dev" path="/com/luminescent-dreams/kifu-gtk/dev/">
|
<schema id="com.luminescent-dreams.kifu-gtk.dev" path="/com/luminescent-dreams/kifu-gtk/dev/">
|
||||||
<key name="database-path" type="s">
|
<key name="library-path" type="s">
|
||||||
<default>""</default>
|
<default>""</default>
|
||||||
<summary>Path to the directory of games</summary>
|
<summary>Path to the directory of games</summary>
|
||||||
</key>
|
</key>
|
||||||
|
@ -10,4 +10,4 @@
|
||||||
<summary>Language override, use system settings if empty</summary>
|
<summary>Language override, use system settings if empty</summary>
|
||||||
</key>
|
</key>
|
||||||
</schema>
|
</schema>
|
||||||
</schemalist>
|
</schemalist>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<schemalist>
|
<schemalist>
|
||||||
<schema id="com.luminescent-dreams.kifu-gtk" path="/com/luminescent-dreams/kifu-gtk/">
|
<schema id="com.luminescent-dreams.kifu-gtk" path="/com/luminescent-dreams/kifu-gtk/">
|
||||||
<key name="database-path" type="s">
|
<key name="library-path" type="s">
|
||||||
<default>""</default>
|
<default>""</default>
|
||||||
<summary>Path to the directory of games</summary>
|
<summary>Path to the directory of games</summary>
|
||||||
</key>
|
</key>
|
||||||
|
@ -10,4 +10,4 @@
|
||||||
<summary>Language override, use system settings if empty</summary>
|
<summary>Language override, use system settings if empty</summary>
|
||||||
</key>
|
</key>
|
||||||
</schema>
|
</schema>
|
||||||
</schemalist>
|
</schemalist>
|
||||||
|
|
|
@ -0,0 +1,175 @@
|
||||||
|
/*
|
||||||
|
Copyright 2024, Savanni D'Gerinel <savanni@luminescent-dreams.com>
|
||||||
|
|
||||||
|
This file is part of Kifu.
|
||||||
|
|
||||||
|
Kifu is free software: you can redistribute it and/or modify it under the terms of the GNU
|
||||||
|
General Public License as published by the Free Software Foundation, either version 3 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
Kifu is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
|
||||||
|
even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use adw::prelude::*;
|
||||||
|
use gio::resources_lookup_data;
|
||||||
|
use glib::IsA;
|
||||||
|
use gtk::STYLE_PROVIDER_PRIORITY_USER;
|
||||||
|
use kifu_core::{Config, Core};
|
||||||
|
|
||||||
|
use crate::view_models::SettingsViewModel;
|
||||||
|
|
||||||
|
enum AppView {
|
||||||
|
Settings(SettingsViewModel),
|
||||||
|
Home,
|
||||||
|
}
|
||||||
|
|
||||||
|
// An application window should generally contain
|
||||||
|
// - an overlay widget
|
||||||
|
// - the main content in a stack on the bottom panel of the overlay
|
||||||
|
// - the settings and the about page in bins atop the overlay
|
||||||
|
pub struct AppWindow {
|
||||||
|
pub window: adw::ApplicationWindow,
|
||||||
|
header: adw::HeaderBar,
|
||||||
|
|
||||||
|
// content is a stack which contains the view models for the application. These are the main
|
||||||
|
// elements that users want to interact with: the home page, the game library, a review, a game
|
||||||
|
// itself, perhaps also chat rooms and player lists on other networks. stack contains the
|
||||||
|
// widgets that need to be rendered. The two of these work together in order to ensure that
|
||||||
|
// we can maintain the state of previous views. Since the two of these work together, they are
|
||||||
|
// a candidate for extraction into a new widget or a new struct.
|
||||||
|
stack: adw::NavigationView,
|
||||||
|
content: Vec<AppView>,
|
||||||
|
|
||||||
|
// Overlays are for transient content, such as about and settings, which can be accessed from
|
||||||
|
// anywhere but shouldn't be part of the main application flow.
|
||||||
|
panel_overlay: gtk::Overlay,
|
||||||
|
core: Core,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AppWindow {
|
||||||
|
pub fn new(app: &adw::Application, core: Core) -> Self {
|
||||||
|
/*
|
||||||
|
|
||||||
|
let stylesheet = String::from_utf8(
|
||||||
|
resources_lookup_data(
|
||||||
|
"/com/luminescent-dreams/kifu-gtk/style.css",
|
||||||
|
gio::ResourceLookupFlags::NONE,
|
||||||
|
)
|
||||||
|
.expect("stylesheet should just be available")
|
||||||
|
.to_vec(),
|
||||||
|
)
|
||||||
|
.expect("to parse stylesheet");
|
||||||
|
|
||||||
|
let provider = gtk::CssProvider::new();
|
||||||
|
provider.load_from_data(&stylesheet);
|
||||||
|
let context = window.style_context();
|
||||||
|
context.add_provider(&provider, STYLE_PROVIDER_PRIORITY_USER);
|
||||||
|
|
||||||
|
let header = setup_header();
|
||||||
|
|
||||||
|
let current_view = match config.get::<Database>() {
|
||||||
|
Some(_) => AppView::Home,
|
||||||
|
None => AppView::Config(SettingsViewModel::new(core.clone())),
|
||||||
|
};
|
||||||
|
|
||||||
|
let content = adw::Bin::builder().css_classes(vec!["content"]).build();
|
||||||
|
content.set_child(Some(
|
||||||
|
));
|
||||||
|
|
||||||
|
|
||||||
|
window.set_content(Some(&layout));
|
||||||
|
*/
|
||||||
|
|
||||||
|
let window = Self::setup_window(app);
|
||||||
|
let header = Self::setup_header();
|
||||||
|
let panel_overlay = Self::setup_panel_overlay();
|
||||||
|
let (stack, content) = Self::setup_content(core.clone());
|
||||||
|
|
||||||
|
let layout = gtk::Box::builder()
|
||||||
|
.orientation(gtk::Orientation::Vertical)
|
||||||
|
.build();
|
||||||
|
layout.append(&header);
|
||||||
|
layout.append(&panel_overlay);
|
||||||
|
panel_overlay.set_child(Some(&stack));
|
||||||
|
|
||||||
|
window.set_content(Some(&layout));
|
||||||
|
|
||||||
|
Self {
|
||||||
|
window,
|
||||||
|
header,
|
||||||
|
stack,
|
||||||
|
content,
|
||||||
|
panel_overlay,
|
||||||
|
core,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_window(app: &adw::Application) -> adw::ApplicationWindow {
|
||||||
|
let window = adw::ApplicationWindow::builder()
|
||||||
|
.application(app)
|
||||||
|
.width_request(800)
|
||||||
|
.height_request(500)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
window
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_header() -> adw::HeaderBar {
|
||||||
|
let header = adw::HeaderBar::builder()
|
||||||
|
.title_widget(>k::Label::new(Some("Kifu")))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let app_menu = gio::Menu::new();
|
||||||
|
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")
|
||||||
|
.build();
|
||||||
|
hamburger.set_menu_model(Some(&app_menu));
|
||||||
|
|
||||||
|
header.pack_end(&hamburger);
|
||||||
|
header
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_panel_overlay() -> gtk::Overlay {
|
||||||
|
gtk::Overlay::new()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_content(core: Core) -> (adw::NavigationView, Vec<AppView>) {
|
||||||
|
let stack = adw::NavigationView::new();
|
||||||
|
let mut content = Vec::new();
|
||||||
|
|
||||||
|
let nothing_page = adw::StatusPage::builder().title("Nothing here").build();
|
||||||
|
let _ = stack.push(
|
||||||
|
&adw::NavigationPage::builder()
|
||||||
|
.can_pop(false)
|
||||||
|
.title("Kifu")
|
||||||
|
.child(¬hing_page)
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
content.push(AppView::Home);
|
||||||
|
|
||||||
|
/*
|
||||||
|
match *core.library() {
|
||||||
|
Some(_) => {
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let settings_vm = SettingsViewModel::new(core.clone());
|
||||||
|
let _ = stack.push(&adw::NavigationPage::new(&settings_vm.widget, "Settings"));
|
||||||
|
content.push(AppView::Settings(settings_vm));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
(stack, content)
|
||||||
|
}
|
||||||
|
|
||||||
|
// pub fn set_content(content: &impl IsA<gtk::Widget>) -> adw::ViewStack {
|
||||||
|
// self.content.set_child(Some(content));
|
||||||
|
// }
|
||||||
|
}
|
|
@ -1,10 +1,29 @@
|
||||||
|
/*
|
||||||
|
Copyright 2024, Savanni D'Gerinel <savanni@luminescent-dreams.com>
|
||||||
|
|
||||||
|
This file is part of Kifu.
|
||||||
|
|
||||||
|
Kifu is free software: you can redistribute it and/or modify it under the terms of the GNU
|
||||||
|
General Public License as published by the Free Software Foundation, either version 3 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
Kifu is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
|
||||||
|
even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
pub mod ui;
|
pub mod ui;
|
||||||
|
|
||||||
|
mod app_window;
|
||||||
|
pub use app_window::AppWindow;
|
||||||
|
|
||||||
mod view_models;
|
mod view_models;
|
||||||
mod views;
|
mod views;
|
||||||
|
|
||||||
use async_std::task::yield_now;
|
use async_std::task::yield_now;
|
||||||
use kifu_core::{Core, CoreRequest, CoreResponse, Observable};
|
use kifu_core::{Core, Observable};
|
||||||
use std::{rc::Rc, sync::Arc};
|
use std::{rc::Rc, sync::Arc};
|
||||||
use tokio::runtime::Runtime;
|
use tokio::runtime::Runtime;
|
||||||
|
|
||||||
|
@ -15,6 +34,7 @@ pub struct CoreApi {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CoreApi {
|
impl CoreApi {
|
||||||
|
/*
|
||||||
pub fn dispatch(&self, request: CoreRequest) {
|
pub fn dispatch(&self, request: CoreRequest) {
|
||||||
/*
|
/*
|
||||||
spawn({
|
spawn({
|
||||||
|
@ -26,6 +46,7 @@ impl CoreApi {
|
||||||
});
|
});
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn perftrace<F, A>(trace_name: &str, f: F) -> A
|
pub fn perftrace<F, A>(trace_name: &str, f: F) -> A
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
use adw::prelude::*;
|
use adw::prelude::*;
|
||||||
use kifu_core::{Config, ConfigOption, Core, CoreRequest, CoreResponse, DatabasePath};
|
use gio::ActionEntry;
|
||||||
|
use kifu_core::{Config, ConfigOption, Core};
|
||||||
use kifu_gtk::{
|
use kifu_gtk::{
|
||||||
perftrace,
|
perftrace,
|
||||||
ui::{AppWindow, ConfigurationPage, Home, PlayingField},
|
// ui::{ConfigurationPage, Home, PlayingField},
|
||||||
|
AppWindow,
|
||||||
CoreApi,
|
CoreApi,
|
||||||
};
|
};
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
|
@ -12,6 +14,7 @@ const APP_ID_PROD: &str = "com.luminescent-dreams.kifu-gtk";
|
||||||
|
|
||||||
const RESOURCE_BASE_PATH: &str = "/com/luminescent-dreams/kifu-gtk/";
|
const RESOURCE_BASE_PATH: &str = "/com/luminescent-dreams/kifu-gtk/";
|
||||||
|
|
||||||
|
/*
|
||||||
fn handle_response(api: CoreApi, app_window: &AppWindow, message: CoreResponse) {
|
fn handle_response(api: CoreApi, app_window: &AppWindow, message: CoreResponse) {
|
||||||
let playing_field = Arc::new(RwLock::new(None));
|
let playing_field = Arc::new(RwLock::new(None));
|
||||||
match message {
|
match message {
|
||||||
|
@ -48,6 +51,26 @@ fn handle_response(api: CoreApi, app_window: &AppWindow, message: CoreResponse)
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
fn load_config(app_id: &str) -> Config {
|
||||||
|
let settings = gio::Settings::new(app_id);
|
||||||
|
let db_path: String = settings.string("database-path").into();
|
||||||
|
let mut config = Config::new();
|
||||||
|
config.set(ConfigOption::LibraryPath(db_path.into()));
|
||||||
|
config
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_app_configuration_action(app: &adw::Application) {
|
||||||
|
println!("setup_app_configuration_action");
|
||||||
|
let action = ActionEntry::builder("show_config")
|
||||||
|
.activate(|_app: &adw::Application, _, _| {
|
||||||
|
println!("show configuration window");
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
app.add_action_entries([action]);
|
||||||
|
println!("setup_app_configuration_action complete");
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
gio::resources_register_include!("com.luminescent-dreams.kifu-gtk.gresource")
|
gio::resources_register_include!("com.luminescent-dreams.kifu-gtk.gresource")
|
||||||
|
@ -59,10 +82,7 @@ fn main() {
|
||||||
APP_ID_PROD
|
APP_ID_PROD
|
||||||
};
|
};
|
||||||
|
|
||||||
let settings = gio::Settings::new(app_id);
|
let config = load_config(&app_id);
|
||||||
let db_path: String = settings.string("database-path").into();
|
|
||||||
let mut config = Config::new();
|
|
||||||
config.set(ConfigOption::DatabasePath(db_path.into()));
|
|
||||||
|
|
||||||
let runtime = Arc::new(
|
let runtime = Arc::new(
|
||||||
tokio::runtime::Builder::new_multi_thread()
|
tokio::runtime::Builder::new_multi_thread()
|
||||||
|
@ -101,16 +121,20 @@ fn main() {
|
||||||
.resource_base_path("/com/luminescent-dreams/kifu-gtk")
|
.resource_base_path("/com/luminescent-dreams/kifu-gtk")
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
|
||||||
app.connect_activate({
|
app.connect_activate({
|
||||||
let runtime = runtime.clone();
|
let runtime = runtime.clone();
|
||||||
move |app| {
|
move |app| {
|
||||||
let app_window = AppWindow::new(app);
|
let app_window = AppWindow::new(app, core.clone());
|
||||||
|
|
||||||
|
setup_app_configuration_action(app);
|
||||||
|
|
||||||
let api = CoreApi {
|
let api = CoreApi {
|
||||||
rt: runtime.clone(),
|
rt: runtime.clone(),
|
||||||
core: core.clone(),
|
core: core.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
let action_config = gio::SimpleAction::new("show-config", None);
|
let action_config = gio::SimpleAction::new("show-config", None);
|
||||||
action_config.connect_activate({
|
action_config.connect_activate({
|
||||||
let api = api.clone();
|
let api = api.clone();
|
||||||
|
@ -119,6 +143,7 @@ fn main() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
app.add_action(&action_config);
|
app.add_action(&action_config);
|
||||||
|
*/
|
||||||
|
|
||||||
app_window.window.present();
|
app_window.window.present();
|
||||||
|
|
||||||
|
@ -134,7 +159,7 @@ fn main() {
|
||||||
});
|
});
|
||||||
*/
|
*/
|
||||||
|
|
||||||
api.dispatch(CoreRequest::Home);
|
// api.dispatch(CoreRequest::Home);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,100 +1,27 @@
|
||||||
use adw::prelude::*;
|
// mod chat;
|
||||||
use gio::resources_lookup_data;
|
// pub use chat::Chat;
|
||||||
use glib::IsA;
|
|
||||||
use gtk::STYLE_PROVIDER_PRIORITY_USER;
|
|
||||||
|
|
||||||
mod chat;
|
// mod config;
|
||||||
pub use chat::Chat;
|
// pub use config::ConfigurationPage;
|
||||||
|
|
||||||
mod config;
|
// mod game_preview;
|
||||||
pub use config::ConfigurationPage;
|
// pub use game_preview::GamePreview;
|
||||||
|
|
||||||
mod game_preview;
|
// mod library;
|
||||||
pub use game_preview::GamePreview;
|
// pub use library::Library;
|
||||||
|
|
||||||
mod library;
|
// mod player_card;
|
||||||
pub use library::Library;
|
// pub use player_card::PlayerCard;
|
||||||
|
|
||||||
mod player_card;
|
// mod playing_field;
|
||||||
pub use player_card::PlayerCard;
|
// pub use playing_field::PlayingField;
|
||||||
|
|
||||||
mod playing_field;
|
// mod home;
|
||||||
pub use playing_field::PlayingField;
|
// pub use home::Home;
|
||||||
|
|
||||||
mod home;
|
// mod board;
|
||||||
pub use home::Home;
|
// pub use board::Board;
|
||||||
|
|
||||||
mod board;
|
|
||||||
pub use board::Board;
|
|
||||||
|
|
||||||
#[cfg(feature = "screenplay")]
|
#[cfg(feature = "screenplay")]
|
||||||
pub use playing_field::playing_field_view;
|
pub use playing_field::playing_field_view;
|
||||||
|
|
||||||
pub struct AppWindow {
|
|
||||||
pub window: adw::ApplicationWindow,
|
|
||||||
pub header: adw::HeaderBar,
|
|
||||||
pub content: adw::Bin,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AppWindow {
|
|
||||||
pub fn new(app: &adw::Application) -> Self {
|
|
||||||
let window = adw::ApplicationWindow::builder()
|
|
||||||
.application(app)
|
|
||||||
.width_request(800)
|
|
||||||
.height_request(500)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let stylesheet = String::from_utf8(
|
|
||||||
resources_lookup_data(
|
|
||||||
"/com/luminescent-dreams/kifu-gtk/style.css",
|
|
||||||
gio::ResourceLookupFlags::NONE,
|
|
||||||
)
|
|
||||||
.expect("stylesheet should just be available")
|
|
||||||
.to_vec(),
|
|
||||||
)
|
|
||||||
.expect("to parse stylesheet");
|
|
||||||
|
|
||||||
let provider = gtk::CssProvider::new();
|
|
||||||
provider.load_from_data(&stylesheet);
|
|
||||||
let context = window.style_context();
|
|
||||||
context.add_provider(&provider, STYLE_PROVIDER_PRIORITY_USER);
|
|
||||||
|
|
||||||
let header = adw::HeaderBar::builder()
|
|
||||||
.title_widget(>k::Label::new(Some("Kifu")))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let app_menu = gio::Menu::new();
|
|
||||||
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")
|
|
||||||
.build();
|
|
||||||
hamburger.set_menu_model(Some(&app_menu));
|
|
||||||
|
|
||||||
header.pack_end(&hamburger);
|
|
||||||
|
|
||||||
let content = adw::Bin::builder().css_classes(vec!["content"]).build();
|
|
||||||
content.set_child(Some(
|
|
||||||
&adw::StatusPage::builder().title("Nothing here").build(),
|
|
||||||
));
|
|
||||||
|
|
||||||
let layout = gtk::Box::builder()
|
|
||||||
.orientation(gtk::Orientation::Vertical)
|
|
||||||
.build();
|
|
||||||
layout.append(&header);
|
|
||||||
layout.append(&content);
|
|
||||||
|
|
||||||
window.set_content(Some(&layout));
|
|
||||||
|
|
||||||
Self {
|
|
||||||
window,
|
|
||||||
header,
|
|
||||||
content,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_content(&self, content: &impl IsA<gtk::Widget>) {
|
|
||||||
self.content.set_child(Some(content));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -14,17 +14,24 @@ 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 crate::LocalObserver;
|
use crate::{views, LocalObserver, views::SettingsView};
|
||||||
use kifu_core::{Core, CoreNotification};
|
use kifu_core::{Core, CoreNotification};
|
||||||
|
|
||||||
|
/// SettingsViewModel
|
||||||
|
///
|
||||||
|
/// Listens for messages from the core, and serves as intermediary between the Settings UI and the
|
||||||
|
/// core. Because it needs to respond to events from the core, it owns the widget, which allows it
|
||||||
|
/// to tell the widget to update after certain events.
|
||||||
pub struct SettingsViewModel {
|
pub struct SettingsViewModel {
|
||||||
core: Core,
|
core: Core,
|
||||||
|
// Technically, Settings doesn't care about any events from Core. We will keep this around for
|
||||||
|
// now as reference, until something which does care shows up.
|
||||||
notification_observer: LocalObserver<CoreNotification>,
|
notification_observer: LocalObserver<CoreNotification>,
|
||||||
widget: gtk::Box,
|
pub widget: views::SettingsView,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SettingsViewModel {
|
impl SettingsViewModel {
|
||||||
fn new(core: Core) -> Self {
|
pub fn new(core: Core) -> Self {
|
||||||
let notification_observer = LocalObserver::new(&core, |msg| {
|
let notification_observer = LocalObserver::new(&core, |msg| {
|
||||||
println!("SettingsViewModel called with message: {:?}", msg)
|
println!("SettingsViewModel called with message: {:?}", msg)
|
||||||
});
|
});
|
||||||
|
@ -32,7 +39,7 @@ impl SettingsViewModel {
|
||||||
Self {
|
Self {
|
||||||
core,
|
core,
|
||||||
notification_observer,
|
notification_observer,
|
||||||
widget: gtk::Box::new(gtk::Orientation::Horizontal, 0),
|
widget: SettingsView::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
mod settings;
|
||||||
|
pub use settings::SettingsView;
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
Copyright 2024, Savanni D'Gerinel <savanni@luminescent-dreams.com>
|
||||||
|
|
||||||
|
This file is part of Kifu.
|
||||||
|
|
||||||
|
Kifu is free software: you can redistribute it and/or modify it under the terms of the GNU
|
||||||
|
General Public License as published by the Free Software Foundation, either version 3 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
Kifu is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
|
||||||
|
even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use glib::Object;
|
||||||
|
use gtk::{prelude::*, subclass::prelude::*};
|
||||||
|
|
||||||
|
pub struct SettingsPrivate {}
|
||||||
|
|
||||||
|
#[glib::object_subclass]
|
||||||
|
impl ObjectSubclass for SettingsPrivate {
|
||||||
|
const NAME: &'static str = "Settings";
|
||||||
|
type Type = SettingsView;
|
||||||
|
type ParentType = gtk::Box;
|
||||||
|
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ObjectImpl for SettingsPrivate {}
|
||||||
|
impl WidgetImpl for SettingsPrivate {}
|
||||||
|
impl BoxImpl for SettingsPrivate {}
|
||||||
|
|
||||||
|
glib::wrapper! {
|
||||||
|
pub struct SettingsView(ObjectSubclass<SettingsPrivate>) @extends gtk::Box, gtk::Widget, @implements gtk::Orientable;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SettingsView {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let s: Self = Object::builder().build();
|
||||||
|
s
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue