use crate::ext_event::{ExtEventHost, ExtEventSink};
use crate::kurbo::Size;
use crate::shell::{Application, Error as PlatformError, WindowBuilder, WindowHandle};
use crate::win_handler::{AppHandler, AppState};
use crate::window::WindowId;
use crate::{
theme, AppDelegate, Data, DruidHandler, Env, LocalizedString, MenuDesc, Widget, WidgetExt,
};
type EnvSetupFn<T> = dyn FnOnce(&mut Env, &T);
pub struct AppLauncher<T> {
windows: Vec<WindowDesc<T>>,
env_setup: Option<Box<EnvSetupFn<T>>>,
delegate: Option<Box<dyn AppDelegate<T>>>,
ext_event_host: ExtEventHost,
}
pub struct WindowDesc<T> {
pub(crate) root: Box<dyn Widget<T>>,
pub(crate) title: LocalizedString<T>,
pub(crate) size: Option<Size>,
pub(crate) min_size: Option<Size>,
pub(crate) menu: Option<MenuDesc<T>>,
pub(crate) resizable: bool,
pub(crate) show_titlebar: bool,
pub id: WindowId,
}
impl<T: Data> AppLauncher<T> {
pub fn with_window(window: WindowDesc<T>) -> Self {
AppLauncher {
windows: vec![window],
env_setup: None,
delegate: None,
ext_event_host: ExtEventHost::new(),
}
}
pub fn configure_env(mut self, f: impl Fn(&mut Env, &T) + '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 get_external_handle(&self) -> ExtEventSink {
self.ext_event_host.make_sink()
}
#[deprecated(since = "0.5.0", note = "Use WidgetExt::debug_paint_layout instead.")]
pub fn debug_paint_layout(self) -> Self {
self
}
pub fn launch(mut self, data: T) -> Result<(), PlatformError> {
let mut env = theme::init();
if let Some(f) = self.env_setup.take() {
f(&mut env, &data);
}
let mut state = AppState::new(data, env, self.delegate.take(), self.ext_event_host);
let handler = AppHandler::new(state.clone());
let mut app = Application::new(Some(Box::new(handler)));
for desc in self.windows {
let window = desc.build_native(&mut state)?;
window.show();
}
app.run();
Ok(())
}
}
impl<T: Data> WindowDesc<T> {
pub fn new<W, F>(root: F) -> WindowDesc<T>
where
W: Widget<T> + 'static,
F: FnOnce() -> W + 'static,
{
WindowDesc {
root: root().boxed(),
title: LocalizedString::new("app-name"),
size: None,
min_size: None,
menu: MenuDesc::platform_default(),
resizable: true,
show_titlebar: true,
id: WindowId::next(),
}
}
pub fn title(mut self, title: LocalizedString<T>) -> Self {
self.title = title;
self
}
pub fn menu(mut self, menu: MenuDesc<T>) -> Self {
self.menu = Some(menu);
self
}
pub fn window_size(mut self, size: impl Into<Size>) -> Self {
self.size = Some(size.into());
self
}
pub fn with_min_size(mut self, size: impl Into<Size>) -> Self {
self.min_size = Some(size.into());
self
}
pub fn resizable(mut self, resizable: bool) -> Self {
self.resizable = resizable;
self
}
pub fn show_titlebar(mut self, show_titlebar: bool) -> Self {
self.show_titlebar = show_titlebar;
self
}
pub(crate) fn build_native(
mut self,
state: &mut AppState<T>,
) -> Result<WindowHandle, PlatformError> {
let data = state.data();
let env = state.env();
self.title.resolve(&data, &env);
let platform_menu = self.menu.as_mut().map(|m| m.build_window_menu(&data, &env));
let handler = DruidHandler::new_shared(state.clone(), self.id);
let mut builder = WindowBuilder::new();
builder.resizable(self.resizable);
builder.show_titlebar(self.show_titlebar);
builder.set_handler(Box::new(handler));
if let Some(size) = self.size {
builder.set_size(size);
}
if let Some(min_size) = self.min_size {
builder.set_min_size(min_size);
}
builder.set_title(self.title.localized_str());
if let Some(menu) = platform_menu {
builder.set_menu(menu);
}
let root = self.root;
let mut window = WindowDesc::new(|| root);
window.title = self.title;
window.menu = self.menu;
state.add_window(self.id, window);
builder.build()
}
}