use std::sync::atomic::Ordering;
use anathema_backend::Backend;
use anathema_default_widgets::register_default_widgets;
use anathema_geometry::Size;
use anathema_templates::{Document, ToSourceKind};
use anathema_value_resolver::{Function, FunctionTable};
use anathema_widgets::components::deferred::DeferredComponents;
use anathema_widgets::components::events::Event;
use anathema_widgets::components::{Component, ComponentId, ComponentRegistry, Emitter, ViewMessage};
use anathema_widgets::tabindex::TabIndex;
use anathema_widgets::{Factory, Widget};
use notify::{Event as NotifyEvent, RecommendedWatcher, RecursiveMode, Watcher, recommended_watcher};
use crate::REBUILD;
pub use crate::error::{Error, Result};
use crate::events::GlobalEventHandler;
use crate::runtime::{Runtime, show_error};
pub struct Builder<G> {
factory: Factory,
document: Document,
component_registry: ComponentRegistry,
emitter: Emitter,
message_receiver: flume::Receiver<ViewMessage>,
fps: u32,
size: Size,
global_event_handler: G,
hot_reload: bool,
function_table: FunctionTable,
}
impl<G: GlobalEventHandler> Builder<G> {
pub(super) fn with_receiver(
message_receiver: flume::Receiver<ViewMessage>,
emitter: Emitter,
document: Document,
size: Size,
global_event_handler: G,
) -> Self {
let mut factory = Factory::new();
register_default_widgets(&mut factory);
Self {
factory,
document,
component_registry: ComponentRegistry::new(),
emitter,
message_receiver,
fps: 30,
size,
global_event_handler,
hot_reload: true,
function_table: FunctionTable::new(),
}
}
pub(super) fn new(document: Document, size: Size, global_event_handler: G) -> Self {
let (tx, rx) = flume::unbounded();
let emitter = Emitter::from(tx);
Self::with_receiver(rx, emitter.clone(), document, size, global_event_handler)
}
pub fn hot_reload(&mut self, value: bool) {
self.hot_reload = value;
}
pub fn register_widget<T: Widget + Default + 'static>(&mut self, ident: &'static str) {
self.factory.register_default::<T>(ident);
}
pub fn fps(&mut self, fps: u32) {
self.fps = fps;
}
pub fn emitter(&self) -> Emitter {
self.emitter.clone()
}
pub fn template(&mut self, ident: impl Into<String>, template: impl ToSourceKind) -> Result<()> {
self.prototype(ident, template, || (), || ())
}
pub fn component<C: Component>(
&mut self,
ident: impl Into<String>,
template: impl ToSourceKind,
component: C,
state: C::State,
) -> Result<ComponentId<C::Message>> {
let id = self.document.add_component(ident, template.to_source_kind())?;
self.component_registry.add_component(id, component, state);
Ok(id.into())
}
pub fn default<C>(
&mut self,
ident: impl Into<String>,
template: impl ToSourceKind,
) -> Result<ComponentId<C::Message>>
where
C: Component + Default,
C::State: Default,
{
let component = C::default();
let state = C::State::default();
let id = self.document.add_component(ident, template.to_source_kind())?;
self.component_registry.add_component(id, component, state);
Ok(id.into())
}
pub fn prototype<FC, FS, C>(
&mut self,
ident: impl Into<String>,
template: impl ToSourceKind,
proto: FC,
state: FS,
) -> Result<()>
where
FC: 'static + Fn() -> C,
FS: 'static + FnMut() -> C::State,
C: Component + 'static,
{
let id = self.document.add_component(ident, template.to_source_kind())?;
self.component_registry.add_prototype(id, proto, state);
Ok(())
}
pub fn with_global_event_handler<Eh>(self, global_event_handler: Eh) -> Builder<Eh>
where
Eh: Fn(Event, &mut TabIndex<'_, '_>, &mut DeferredComponents) -> Option<Event>,
{
Builder {
factory: self.factory,
document: self.document,
component_registry: self.component_registry,
emitter: self.emitter,
message_receiver: self.message_receiver,
fps: self.fps,
size: self.size,
global_event_handler,
hot_reload: self.hot_reload,
function_table: self.function_table,
}
}
pub fn finish<F, B>(mut self, backend: &mut B, mut f: F) -> Result<()>
where
F: FnMut(&mut Runtime<G>, &mut B) -> Result<()>,
B: Backend,
{
#[cfg(feature = "profile")]
let _puffin_server = {
let server_addr = format!("127.0.0.1:{}", puffin_http::DEFAULT_PORT);
let server = puffin_http::Server::new(&server_addr).unwrap();
puffin::set_scopes_on(true);
server
};
let (blueprint, globals) = loop {
match self.document.compile() {
Ok(val) => break val,
Err(error) => match show_error(error.into(), backend, &self.document) {
Ok(()) => return Err(Error::Stop),
Err(Error::Reload) if self.hot_reload => {
_ = self.document.reload_templates();
}
err => err?,
},
}
};
let watcher = self.set_watcher(self.hot_reload)?;
let mut inst = Runtime::new(
blueprint,
globals,
self.component_registry,
self.document,
self.factory,
self.message_receiver,
self.emitter,
watcher,
self.size,
self.fps,
self.global_event_handler,
self.function_table,
self.hot_reload,
);
f(&mut inst, backend)
}
fn set_watcher(&mut self, hot_reload: bool) -> Result<Option<RecommendedWatcher>> {
if !hot_reload {
return Ok(None);
}
let paths = self
.document
.template_paths()
.filter_map(|p| p.canonicalize().ok())
.collect::<Vec<_>>();
let mut watcher = recommended_watcher(move |event: std::result::Result<NotifyEvent, _>| match event {
Ok(event) => match event.kind {
notify::EventKind::Create(_) | notify::EventKind::Remove(_) | notify::EventKind::Modify(_) => {
if paths.iter().any(|p| event.paths.contains(p)) {
REBUILD.store(true, Ordering::Relaxed);
}
}
notify::EventKind::Any | notify::EventKind::Access(_) | notify::EventKind::Other => (),
},
Err(_err) => (),
})?;
for path in self.document.template_paths() {
let path = path.canonicalize().unwrap();
if let Some(parent) = path.parent() {
watcher.watch(parent, RecursiveMode::NonRecursive)?;
}
}
Ok(Some(watcher))
}
pub fn register_function(&mut self, ident: impl Into<String>, f: impl Into<Function>) -> Result<()> {
Ok(self.function_table.insert(ident, f)?)
}
}