1use std::sync::atomic::Ordering;
2
3use anathema_backend::Backend;
4use anathema_default_widgets::register_default_widgets;
5use anathema_geometry::Size;
6use anathema_templates::{Document, ToSourceKind};
7use anathema_value_resolver::{Function, FunctionTable};
8use anathema_widgets::components::deferred::DeferredComponents;
9use anathema_widgets::components::events::Event;
10use anathema_widgets::components::{Component, ComponentId, ComponentRegistry, Emitter, ViewMessage};
11use anathema_widgets::tabindex::TabIndex;
12use anathema_widgets::{Factory, Widget};
13use notify::{Event as NotifyEvent, RecommendedWatcher, RecursiveMode, Watcher, recommended_watcher};
14
15use crate::REBUILD;
16pub use crate::error::{Error, Result};
17use crate::events::GlobalEventHandler;
18use crate::runtime::{Runtime, show_error};
19
20pub struct Builder<G> {
21 factory: Factory,
22 document: Document,
23 component_registry: ComponentRegistry,
24 emitter: Emitter,
25 message_receiver: flume::Receiver<ViewMessage>,
26 fps: u32,
27 size: Size,
28 global_event_handler: G,
29 hot_reload: bool,
30 function_table: FunctionTable,
31}
32
33impl<G: GlobalEventHandler> Builder<G> {
34 pub(super) fn new(document: Document, size: Size, global_event_handler: G) -> Self {
36 let mut factory = Factory::new();
37 register_default_widgets(&mut factory);
38
39 let (tx, message_receiver) = flume::unbounded();
40 let emitter = tx.into();
41
42 Self {
43 factory,
44 document,
45 component_registry: ComponentRegistry::new(),
46 emitter,
47 message_receiver,
48 fps: 30,
49 size,
50 global_event_handler,
51 hot_reload: true,
52 function_table: FunctionTable::new(),
53 }
54 }
55
56 pub fn disable_hot_reload(&mut self) {
58 self.hot_reload = false;
59 }
60
61 pub fn register_widget<T: Widget + Default + 'static>(&mut self, ident: &'static str) {
63 self.factory.register_default::<T>(ident);
64 }
65
66 pub fn fps(&mut self, fps: u32) {
68 self.fps = fps;
69 }
70
71 pub fn emitter(&self) -> Emitter {
73 self.emitter.clone()
74 }
75
76 pub fn template(&mut self, ident: impl Into<String>, template: impl ToSourceKind) -> Result<()> {
80 self.prototype(ident, template, || (), || ())
81 }
82
83 pub fn component<C: Component>(
90 &mut self,
91 ident: impl Into<String>,
92 template: impl ToSourceKind,
93 component: C,
94 state: C::State,
95 ) -> Result<ComponentId<C::Message>> {
96 let id = self.document.add_component(ident, template.to_source_kind())?;
97 self.component_registry.add_component(id, component, state);
98 Ok(id.into())
99 }
100
101 pub fn default<C>(
105 &mut self,
106 ident: impl Into<String>,
107 template: impl ToSourceKind,
108 ) -> Result<ComponentId<C::Message>>
109 where
110 C: Component + Default,
111 C::State: Default,
112 {
113 let component = C::default();
114 let state = C::State::default();
115 let id = self.document.add_component(ident, template.to_source_kind())?;
116 self.component_registry.add_component(id, component, state);
117 Ok(id.into())
118 }
119
120 pub fn prototype<FC, FS, C>(
123 &mut self,
124 ident: impl Into<String>,
125 template: impl ToSourceKind,
126 proto: FC,
127 state: FS,
128 ) -> Result<()>
129 where
130 FC: 'static + Fn() -> C,
131 FS: 'static + FnMut() -> C::State,
132 C: Component + 'static,
133 {
134 let id = self.document.add_component(ident, template.to_source_kind())?;
135 self.component_registry.add_prototype(id, proto, state);
136 Ok(())
137 }
138
139 pub fn with_global_event_handler<Eh>(self, global_event_handler: Eh) -> Builder<Eh>
142 where
143 Eh: Fn(Event, &mut TabIndex<'_, '_>, &mut DeferredComponents) -> Option<Event>,
144 {
145 Builder {
146 factory: self.factory,
147 document: self.document,
148 component_registry: self.component_registry,
149 emitter: self.emitter,
150 message_receiver: self.message_receiver,
151 fps: self.fps,
152 size: self.size,
153 global_event_handler,
154 hot_reload: self.hot_reload,
155 function_table: self.function_table,
156 }
157 }
158
159 pub fn finish<F, B>(mut self, backend: &mut B, mut f: F) -> Result<()>
160 where
161 F: FnMut(&mut Runtime<G>, &mut B) -> Result<()>,
162 B: Backend,
163 {
164 #[cfg(feature = "profile")]
165 let _puffin_server = {
166 let server_addr = format!("127.0.0.1:{}", puffin_http::DEFAULT_PORT);
167 let server = puffin_http::Server::new(&server_addr).unwrap();
168 puffin::set_scopes_on(true);
169 server
170 };
171
172 let (blueprint, globals) = loop {
173 match self.document.compile() {
174 Ok(val) => break val,
175 Err(error) => {
176 show_error(error, backend, &mut self.document)?;
177 }
178 }
179 };
180
181 let watcher = self.set_watcher(self.hot_reload)?;
182
183 let mut inst = Runtime::new(
184 blueprint,
185 globals,
186 self.component_registry,
187 self.document,
188 self.factory,
189 self.message_receiver,
190 self.emitter,
191 watcher,
192 self.size,
193 self.fps,
194 self.global_event_handler,
195 self.function_table,
196 );
197
198 loop {
203 match f(&mut inst, backend) {
204 Ok(()) => (),
205 e => match e {
206 Ok(_) => continue,
207 Err(Error::Stop) => break Ok(()),
208 Err(Error::Template(error)) => match show_error(error, backend, &mut inst.document) {
209 Ok(_) => continue,
210 Err(err) => panic!("error console failed: {err}"),
211 },
212 Err(Error::Widget(err)) => panic!("this should not panic in the future: {err}"),
213 Err(e) => break Err(e),
214 },
215 }
216
217 if !self.hot_reload {
218 break Ok(());
219 }
220
221 match inst.reload() {
222 Ok(()) => continue,
223 Err(Error::Stop) => todo!(),
224 Err(Error::Template(error)) => match show_error(error, backend, &mut inst.document) {
225 Ok(_) => continue,
226 Err(err) => panic!("error console failed: {err}"),
227 },
228 Err(Error::Widget(_error)) => todo!(),
229 Err(e) => break Err(e),
230 }
231 }
232 }
233
234 fn set_watcher(&mut self, hot_reload: bool) -> Result<Option<RecommendedWatcher>> {
235 if !hot_reload {
236 return Ok(None);
237 }
238
239 let paths = self
240 .document
241 .template_paths()
242 .filter_map(|p| p.canonicalize().ok())
243 .collect::<Vec<_>>();
244
245 let mut watcher = recommended_watcher(move |event: std::result::Result<NotifyEvent, _>| match event {
246 Ok(event) => match event.kind {
247 notify::EventKind::Create(_) | notify::EventKind::Remove(_) | notify::EventKind::Modify(_) => {
248 if paths.iter().any(|p| event.paths.contains(p)) {
249 REBUILD.store(true, Ordering::Relaxed);
250 }
251 }
252 notify::EventKind::Any | notify::EventKind::Access(_) | notify::EventKind::Other => (),
253 },
254 Err(_err) => (),
255 })?;
256
257 for path in self.document.template_paths() {
258 let path = path.canonicalize().unwrap();
259
260 if let Some(parent) = path.parent() {
261 watcher.watch(parent, RecursiveMode::NonRecursive)?;
262 }
263 }
264
265 Ok(Some(watcher))
266 }
267
268 pub fn register_function(&mut self, ident: impl Into<String>, f: impl Into<Function>) -> Result<()> {
269 Ok(self.function_table.insert(ident, f)?)
270 }
271}