use std::cell::RefCell;
use std::rc::Rc;
use std::sync::Arc;
use crate::kurbo::Size;
use crate::shell::{Application, Error as PlatformError, RunLoop, WindowBuilder, WindowHandle};
use crate::win_handler::AppState;
use crate::window::{Window, WindowId};
use crate::{theme, AppDelegate, Data, DruidHandler, Env, LocalizedString, MenuDesc, Widget};
type EnvSetupFn = dyn FnOnce(&mut Env);
pub struct AppLauncher<T> {
windows: Vec<WindowDesc<T>>,
env_setup: Option<Box<EnvSetupFn>>,
delegate: Option<Box<dyn AppDelegate<T>>>,
}
type WidgetBuilderFn<T> = dyn Fn() -> Box<dyn Widget<T>> + 'static;
pub struct WindowDesc<T> {
pub(crate) root_builder: Arc<WidgetBuilderFn<T>>,
pub(crate) title: Option<LocalizedString<T>>,
pub(crate) size: Option<Size>,
pub(crate) menu: Option<MenuDesc<T>>,
pub id: WindowId,
}
impl<T: Data + 'static> AppLauncher<T> {
pub fn with_window(window: WindowDesc<T>) -> Self {
AppLauncher {
windows: vec![window],
env_setup: None,
delegate: None,
}
}
pub fn configure_env(mut self, f: impl Fn(&mut Env) + 'static) -> Self {
self.env_setup = Some(Box::new(f));
self
}
pub fn delegate(mut self, delegate: impl AppDelegate<T> + 'static) -> Self {
self.delegate = Some(Box::new(delegate));
self
}
pub fn use_simple_logger(self) -> Self {
simple_logger::init().ok();
self
}
pub fn launch(mut self, data: T) -> Result<(), PlatformError> {
Application::init();
let mut main_loop = RunLoop::new();
let mut env = theme::init();
if let Some(f) = self.env_setup.take() {
f(&mut env);
}
let state = AppState::new(data, env, self.delegate.take());
for desc in self.windows {
let window = desc.build_native(&state)?;
window.show();
}
main_loop.run();
Ok(())
}
}
impl<T: Data + 'static> WindowDesc<T> {
pub fn new<W, F>(root: F) -> WindowDesc<T>
where
W: Widget<T> + 'static,
F: Fn() -> W + 'static,
{
let root_builder: Arc<WidgetBuilderFn<T>> = Arc::new(move || Box::new(root()));
WindowDesc {
root_builder,
title: None,
size: None,
menu: MenuDesc::platform_default(),
id: WindowId::next(),
}
}
pub fn title(mut self, title: LocalizedString<T>) -> Self {
self.title = Some(title);
self
}
pub fn window_size(mut self, size: impl Into<Size>) -> Self {
self.size = Some(size.into());
self
}
pub(crate) fn build_native(
&self,
state: &Rc<RefCell<AppState<T>>>,
) -> Result<WindowHandle, PlatformError> {
let mut title = self
.title
.clone()
.unwrap_or_else(|| LocalizedString::new("app-name"));
title.resolve(&state.borrow().data, &state.borrow().env);
let mut menu = self.menu.to_owned();
let platform_menu = menu
.as_mut()
.map(|m| m.build_window_menu(&state.borrow().data, &state.borrow().env));
let handler = DruidHandler::new_shared(state.clone(), self.id);
let mut builder = WindowBuilder::new();
builder.set_handler(Box::new(handler));
if let Some(size) = self.size {
builder.set_size(size);
}
builder.set_title(title.localized_str());
if let Some(menu) = platform_menu {
builder.set_menu(menu);
}
let root = (self.root_builder)();
state
.borrow_mut()
.add_window(self.id, Window::new(root, title, menu));
builder.build()
}
pub fn menu(mut self, menu: MenuDesc<T>) -> Self {
self.menu = Some(menu);
self
}
}