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