Create, populate, and start using the Application Context
This commit is contained in:
parent
ff1d117e8c
commit
4114e64b8e
|
@ -2709,6 +2709,8 @@ dependencies = [
|
||||||
"libadwaita",
|
"libadwaita",
|
||||||
"pango",
|
"pango",
|
||||||
"sgf",
|
"sgf",
|
||||||
|
"sys-locale",
|
||||||
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -13,8 +13,8 @@ adw = { version = "0.5", package = "libadwaita", features = [ "v1_2"
|
||||||
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" }
|
||||||
fluent = { version = "0.16" }
|
|
||||||
fluent-ergonomics = { path = "../../fluent-ergonomics" }
|
fluent-ergonomics = { path = "../../fluent-ergonomics" }
|
||||||
|
fluent = { version = "0.16" }
|
||||||
gio = { version = "0.18" }
|
gio = { version = "0.18" }
|
||||||
glib = { version = "0.18" }
|
glib = { version = "0.18" }
|
||||||
gtk = { version = "0.7", package = "gtk4", features = [ "v4_8" ] }
|
gtk = { version = "0.7", package = "gtk4", features = [ "v4_8" ] }
|
||||||
|
@ -23,6 +23,8 @@ kifu-core = { path = "../core" }
|
||||||
l10n = { path = "../../l10n" }
|
l10n = { path = "../../l10n" }
|
||||||
pango = { version = "*" }
|
pango = { version = "*" }
|
||||||
sgf = { path = "../../sgf" }
|
sgf = { path = "../../sgf" }
|
||||||
|
sys-locale = { version = "0.3" }
|
||||||
|
thiserror = { version = "1" }
|
||||||
tokio = { version = "1.26", features = [ "full" ] }
|
tokio = { version = "1.26", features = [ "full" ] }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
use async_std::task::yield_now;
|
||||||
|
use kifu_core::{Core, CoreRequest, Observable};
|
||||||
|
use l10n::{NonEmptyList, L10N};
|
||||||
|
use std::{rc::Rc, sync::Arc};
|
||||||
|
use tokio::runtime::Runtime;
|
||||||
|
|
||||||
mod messages {
|
mod messages {
|
||||||
include!(env!("KIFU_GTK_MESSAGES"));
|
include!(env!("KIFU_GTK_MESSAGES"));
|
||||||
}
|
}
|
||||||
|
@ -7,11 +13,6 @@ pub mod ui;
|
||||||
mod view_models;
|
mod view_models;
|
||||||
mod views;
|
mod views;
|
||||||
|
|
||||||
use async_std::task::yield_now;
|
|
||||||
use kifu_core::{Core, CoreRequest, CoreResponse, Observable};
|
|
||||||
use std::{rc::Rc, sync::Arc};
|
|
||||||
use tokio::runtime::Runtime;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct CoreApi {
|
pub struct CoreApi {
|
||||||
pub rt: Arc<Runtime>,
|
pub rt: Arc<Runtime>,
|
||||||
|
@ -43,6 +44,45 @@ where
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// AppContext is our global store for things that need to be available everywhere. Developers
|
||||||
|
/// should never pass this around directly, but should instead pass it around by traits. This is to
|
||||||
|
/// provide systems such as:
|
||||||
|
///
|
||||||
|
/// - Feature Flags
|
||||||
|
/// - L10N
|
||||||
|
pub struct AppContext {
|
||||||
|
pub l10n: L10N,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum AppContextInitError {}
|
||||||
|
|
||||||
|
impl AppContext {
|
||||||
|
pub fn new() -> Result<Self, AppContextInitError> {
|
||||||
|
let mut locale_list = NonEmptyList::new("en-US");
|
||||||
|
|
||||||
|
let user_locale = sys_locale::get_locale().unwrap();
|
||||||
|
println!("user locale: {}", user_locale);
|
||||||
|
if locale_list.find(|l| *l == user_locale).is_none() {
|
||||||
|
locale_list.push(&user_locale);
|
||||||
|
}
|
||||||
|
let mut l10n = l10n::L10N::new("messages".into());
|
||||||
|
l10n.set_locales(locale_list);
|
||||||
|
|
||||||
|
Ok(Self { l10n })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ProvidesL10N {
|
||||||
|
fn l10n(&self) -> &L10N;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProvidesL10N for AppContext {
|
||||||
|
fn l10n(&self) -> &L10N {
|
||||||
|
&self.l10n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// LocalObserver creates a task on the current thread which watches the specified observer for notifications and calls the handler function with each one.
|
/// LocalObserver creates a task on the current thread which watches the specified observer for notifications and calls the handler function with each one.
|
||||||
///
|
///
|
||||||
/// The LocalObserver starts a task which listens for notifications during the constructor. When the observer goes out of scope, it will make a point of aborting the task. This combination means that anything which uses the observer can create it, hold on to a reference of it, and then drop it when done, and not have to do anything else with the observer object.
|
/// The LocalObserver starts a task which listens for notifications during the constructor. When the observer goes out of scope, it will make a point of aborting the task. This combination means that anything which uses the observer can create it, hold on to a reference of it, and then drop it when done, and not have to do anything else with the observer object.
|
||||||
|
|
|
@ -3,8 +3,9 @@ use kifu_core::{Config, ConfigOption, Core, CoreRequest, CoreResponse, DatabaseP
|
||||||
use kifu_gtk::{
|
use kifu_gtk::{
|
||||||
perftrace,
|
perftrace,
|
||||||
ui::{AppWindow, ConfigurationPage, Home, PlayingField},
|
ui::{AppWindow, ConfigurationPage, Home, PlayingField},
|
||||||
CoreApi,
|
AppContext, CoreApi,
|
||||||
};
|
};
|
||||||
|
use l10n::NonEmptyList;
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
const APP_ID_DEV: &str = "com.luminescent-dreams.kifu-gtk.dev";
|
const APP_ID_DEV: &str = "com.luminescent-dreams.kifu-gtk.dev";
|
||||||
|
@ -104,7 +105,9 @@ fn main() {
|
||||||
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 ctx = AppContext::new().unwrap();
|
||||||
|
let app_window = AppWindow::new(app, &ctx);
|
||||||
|
|
||||||
let api = CoreApi {
|
let api = CoreApi {
|
||||||
rt: runtime.clone(),
|
rt: runtime.clone(),
|
||||||
|
|
|
@ -4,7 +4,7 @@ use glib::IsA;
|
||||||
use gtk::STYLE_PROVIDER_PRIORITY_USER;
|
use gtk::STYLE_PROVIDER_PRIORITY_USER;
|
||||||
use l10n::{L10N, NonEmptyList};
|
use l10n::{L10N, NonEmptyList};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use crate::messages;
|
use crate::{ProvidesL10N, messages};
|
||||||
|
|
||||||
mod chat;
|
mod chat;
|
||||||
pub use chat::Chat;
|
pub use chat::Chat;
|
||||||
|
@ -40,7 +40,7 @@ pub struct AppWindow {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AppWindow {
|
impl AppWindow {
|
||||||
pub fn new(app: &adw::Application) -> Self {
|
pub fn new(app: &adw::Application, ctx: &impl ProvidesL10N) -> Self {
|
||||||
let window = adw::ApplicationWindow::builder()
|
let window = adw::ApplicationWindow::builder()
|
||||||
.application(app)
|
.application(app)
|
||||||
.width_request(800)
|
.width_request(800)
|
||||||
|
@ -78,10 +78,8 @@ impl AppWindow {
|
||||||
header.pack_end(&hamburger);
|
header.pack_end(&hamburger);
|
||||||
|
|
||||||
let content = adw::Bin::builder().css_classes(vec!["content"]).build();
|
let content = adw::Bin::builder().css_classes(vec!["content"]).build();
|
||||||
let mut l10n = L10N::new(PathBuf::from("resources"));
|
|
||||||
l10n.set_locales(NonEmptyList::from_iter(vec!["en-US", "eo"]).unwrap());
|
|
||||||
content.set_child(Some(
|
content.set_child(Some(
|
||||||
&adw::StatusPage::builder().title(l10n.tr(messages::NothingHere)).build(),
|
&adw::StatusPage::builder().title(ctx.l10n().tr(messages::NothingHere)).build(),
|
||||||
));
|
));
|
||||||
|
|
||||||
let layout = gtk::Box::builder()
|
let layout = gtk::Box::builder()
|
||||||
|
|
|
@ -22,7 +22,7 @@ pub enum NonEmptyListError {
|
||||||
pub struct NonEmptyList<A>(Vec<A>);
|
pub struct NonEmptyList<A>(Vec<A>);
|
||||||
|
|
||||||
impl<A> NonEmptyList<A> {
|
impl<A> NonEmptyList<A> {
|
||||||
fn new(elem: A) -> Self {
|
pub fn new(elem: A) -> Self {
|
||||||
Self(vec![elem])
|
Self(vec![elem])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,6 +37,14 @@ impl<A> NonEmptyList<A> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn push(&mut self, item: A) {
|
||||||
|
self.0.push(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find(&self, f: impl Fn(&A) -> bool) -> Option<&A> {
|
||||||
|
self.0.iter().find(|item| f(*item))
|
||||||
|
}
|
||||||
|
|
||||||
fn first(&self) -> &A {
|
fn first(&self) -> &A {
|
||||||
&self.0[0]
|
&self.0[0]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue