use crate::core::ObjectId;
use crate::WidgetTriggerEvent;
use super::handle::{dispatch_trigger, WindowHandle};
use super::lifecycle::AppLifecycle;
#[derive(Debug, Clone)]
pub struct AppConfig {
pub app_name: String,
pub organization: String,
pub version: String,
pub enable_i18n: bool,
pub enable_accessibility: bool,
}
impl Default for AppConfig {
fn default() -> Self {
Self {
app_name: String::new(),
organization: String::new(),
version: String::new(),
enable_i18n: true,
enable_accessibility: true,
}
}
}
impl AppConfig {
pub fn with_app_name(mut self, name: &str) -> Self {
self.app_name = name.to_owned();
self
}
pub fn with_organization(mut self, org: &str) -> Self {
self.organization = org.to_owned();
self
}
pub fn with_version(mut self, version: &str) -> Self {
self.version = version.to_owned();
self
}
pub fn with_i18n(mut self, enable: bool) -> Self {
self.enable_i18n = enable;
self
}
pub fn with_accessibility(mut self, enable: bool) -> Self {
self.enable_accessibility = enable;
self
}
}
pub struct App {
config: AppConfig,
lifecycle: AppLifecycle,
}
impl App {
pub fn new() -> Self {
Self { config: AppConfig::default(), lifecycle: AppLifecycle::new() }
}
pub fn with_config(config: AppConfig) -> Self {
Self { config, lifecycle: AppLifecycle::new() }
}
pub fn config(&self) -> &AppConfig {
&self.config
}
pub fn config_mut(&mut self) -> &mut AppConfig {
&mut self.config
}
pub fn lifecycle(&self) -> &AppLifecycle {
&self.lifecycle
}
pub fn lifecycle_mut(&mut self) -> &mut AppLifecycle {
&mut self.lifecycle
}
pub fn on_startup<F: FnOnce() + Send + 'static>(self, f: F) -> Self {
with_startup(|slot| {
*slot = Some(Box::new(f));
});
self
}
pub fn on_shutdown<F: FnOnce() + Send + 'static>(self, f: F) -> Self {
with_shutdown(|slot| {
*slot = Some(Box::new(f));
});
self
}
pub fn init(&mut self) {
trace_runtime_route("app::init");
crate::init();
self.lifecycle.transition(crate::app::lifecycle::AppLifecycleState::Foreground);
with_startup(|slot| {
if let Some(cb) = slot.take() {
cb();
}
});
}
pub fn run(&self) {
trace_runtime_route("app::run");
crate::run();
with_shutdown(|slot| {
if let Some(cb) = slot.take() {
cb();
}
});
}
pub fn quit(&mut self) {
crate::quit();
self.lifecycle.transition(crate::app::lifecycle::AppLifecycleState::Terminating);
}
pub fn run_async(&self) -> std::thread::JoinHandle<()> {
trace_runtime_route("app::run_async");
std::thread::spawn(|| {
crate::run();
with_shutdown(|slot| {
if let Some(cb) = slot.take() {
cb();
}
});
})
}
pub fn new_window(&self, title: &str, x: i32, y: i32, w: u32, h: u32) -> WindowHandle {
WindowHandle::from_raw(crate::create_window(title, x, y, w, h))
}
pub fn poll_event(&self) -> Option<WidgetTriggerEvent> {
let ev = crate::poll_widget_trigger_event();
if let Some(ref ev) = ev {
dispatch_trigger(ev.widget_id, ev.kind);
}
ev
}
pub fn poll_triggered(&self) -> Option<ObjectId> {
crate::poll_widget_triggered()
}
}
impl Default for App {
fn default() -> Self {
Self::new()
}
}
use crate::compat::Mutex;
use std::sync::OnceLock;
static STARTUP: OnceLock<Mutex<Option<Box<dyn FnOnce() + Send>>>> = OnceLock::new();
static SHUTDOWN: OnceLock<Mutex<Option<Box<dyn FnOnce() + Send>>>> = OnceLock::new();
fn with_startup<F, R>(f: F) -> R
where
F: FnOnce(&mut Option<Box<dyn FnOnce() + Send>>) -> R,
{
let lock = STARTUP.get_or_init(|| Mutex::new(None));
let mut guard = lock.lock().unwrap_or_else(|e| e.into_inner());
f(&mut guard)
}
fn with_shutdown<F, R>(f: F) -> R
where
F: FnOnce(&mut Option<Box<dyn FnOnce() + Send>>) -> R,
{
let lock = SHUTDOWN.get_or_init(|| Mutex::new(None));
let mut guard = lock.lock().unwrap_or_else(|e| e.into_inner());
f(&mut guard)
}
fn trace_runtime_route(stage: &str) {
if std::env::var("RUST_WIDGETS_TRACE_RUNTIME").ok().as_deref() == Some("1") {
log::info!("[rust_widgets.app] stage={stage}");
}
}