anathema_runtime/
builder.rs

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    /// Create a new runtime builder
33    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    /// Disable hot reloading
54    pub fn disable_hot_reload(&mut self) {
55        self.hot_reload = false;
56    }
57
58    /// Register a new widget
59    pub fn register_widget<T: Widget + Default + 'static>(&mut self, ident: &'static str) {
60        self.factory.register_default::<T>(ident);
61    }
62
63    /// Set the expected frame rate
64    pub fn fps(&mut self, fps: u32) {
65        self.fps = fps;
66    }
67
68    /// Returns an [Emitter] to send messages to components
69    pub fn emitter(&self) -> Emitter {
70        self.emitter.clone()
71    }
72
73    /// Registers a component as a template-only component.
74    ///
75    /// This component has no state or reacts to any events
76    pub fn template(&mut self, ident: impl Into<String>, template: impl ToSourceKind) -> Result<()> {
77        self.prototype(ident, template, || (), || ())
78    }
79
80    /// Registers a [Component] with the runtime.
81    /// This returns a unique [ComponentId] that is used to send messages to the component.
82    ///
83    /// A component can only be used once in a template.
84    /// If you want multiple instances, register the component as a prototype instead,
85    /// see [RuntimeBuilder::prototype].
86    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    /// Registers a [Component] with the runtime as long as the component and the associated state
99    /// implements the `Default` trait.
100    /// This returns a unique [ComponentId] that is used to send messages to the component.
101    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    /// Registers a [Component] as a prototype with the [Runtime],
118    /// which allows for multiple instances of the component to exist the templates.
119    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    /// Assign a new event handler (make sure not to forget to add some mechanism to stop the
137    /// runtime)
138    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        // NOTE:
194        // this enables hot reload,
195        // however with this enabled the `with_frame` function
196        // on the runtime will repeat
197        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}