use masonry::peniko::Blob;
use masonry_winit::app::{EventLoopBuilder, MasonryUserEvent, NewWindow, WindowId};
use tokio::runtime::Runtime as TokioRuntime;
use std::iter::Once;
use std::sync::Arc;
use masonry::core::DefaultProperties;
use masonry::theme::default_property_set;
use winit::error::EventLoopError;
use xilem_core::map_state;
use crate::window_options::WindowCallbacks;
use crate::{MasonryDriver, WidgetView, WindowOptions, WindowView};
#[must_use = "A Xilem app does nothing unless ran."]
pub struct Xilem<State, Logic> {
state: State,
logic: Logic,
runtime: Arc<TokioRuntime>,
default_properties: Option<DefaultProperties>,
fonts: Vec<Blob<u8>>,
}
pub struct ExitOnClose<S> {
state: S,
running: bool,
}
impl<S> AppState for ExitOnClose<S> {
fn keep_running(&self) -> bool {
self.running
}
}
impl<State>
Xilem<
ExitOnClose<State>,
Box<dyn FnMut(&mut ExitOnClose<State>) -> Once<WindowView<ExitOnClose<State>>>>,
>
{
pub fn new_simple<View>(
state: State,
logic: impl FnMut(&mut State) -> View + 'static,
window_options: WindowOptions<State>,
) -> Self
where
View: WidgetView<State>,
State: 'static,
{
Self::new_simple_with_tokio(
state,
logic,
window_options,
Arc::new(TokioRuntime::new().unwrap()),
)
}
pub fn new_simple_with_tokio<View>(
state: State,
mut logic: impl FnMut(&mut State) -> View + 'static,
window_options: WindowOptions<State>,
tokio_rt: Arc<TokioRuntime>,
) -> Self
where
View: WidgetView<State>,
State: 'static,
{
let window_id = WindowId::next();
let callbacks = Arc::new(window_options.callbacks);
Xilem::new_inner(
ExitOnClose {
state,
running: true,
},
Box::new(move |ExitOnClose { state, .. }| {
let callbacks = callbacks.clone();
let on_close = move |wrapper: &mut ExitOnClose<_>| {
wrapper.running = false;
if let Some(on_close) = &callbacks.on_close {
on_close(&mut wrapper.state);
}
};
std::iter::once(
crate::window(
window_id,
String::new(),
map_state(logic(state), |wrapper: &mut ExitOnClose<_>| {
&mut wrapper.state
}),
)
.with_options(|_| WindowOptions {
reactive: window_options.reactive.clone(),
initial: window_options.initial.clone(),
callbacks: WindowCallbacks {
on_close: Some(Box::new(on_close)),
},
}),
)
}),
tokio_rt,
)
}
}
pub trait AppState {
fn keep_running(&self) -> bool;
}
impl<State, Logic, WindowIter> Xilem<State, Logic>
where
State: AppState + 'static,
Logic: FnMut(&mut State) -> WindowIter + 'static,
WindowIter: Iterator<Item = WindowView<State>>,
{
pub fn new(state: State, logic: Logic) -> Self
where
State: AppState,
{
Self::new_inner(state, logic, Arc::new(TokioRuntime::new().unwrap()))
}
fn new_inner(state: State, logic: Logic, runtime: Arc<TokioRuntime>) -> Self {
Self {
state,
logic,
runtime,
default_properties: None,
fonts: Vec::new(),
}
}
pub fn with_font(mut self, data: impl Into<Blob<u8>>) -> Self {
self.fonts.push(data.into());
self
}
pub fn with_default_properties(mut self, default_properties: DefaultProperties) -> Self {
self.default_properties = Some(default_properties);
self
}
pub fn run_in(mut self, mut event_loop: EventLoopBuilder) -> Result<(), EventLoopError> {
let event_loop = event_loop.build()?;
let proxy = event_loop.create_proxy();
let default_properties = self
.default_properties
.take()
.unwrap_or_else(default_property_set);
let (driver, windows) =
self.into_driver_and_windows(move |event| proxy.send_event(event).map_err(|err| err.0));
masonry_winit::app::run_with(event_loop, windows, driver, default_properties)
}
pub fn into_driver_and_windows(
self,
proxy: impl Fn(MasonryUserEvent) -> Result<(), MasonryUserEvent> + Send + Sync + 'static,
) -> (MasonryDriver<State, Logic>, Vec<NewWindow>) {
MasonryDriver::new(self.state, self.logic, proxy, self.runtime, self.fonts)
}
}