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, Expression, ToSourceKind, Variables};
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    variables: Variables,
32}
33
34impl<G: GlobalEventHandler> Builder<G> {
35    /// Create a new runtime builder with a receiver.
36    /// Use this if the `Emitter` was created outside of the runtime.
37    pub(super) fn with_receiver(
38        message_receiver: flume::Receiver<ViewMessage>,
39        emitter: Emitter,
40        document: Document,
41        size: Size,
42        global_event_handler: G,
43    ) -> Self {
44        let mut factory = Factory::new();
45        register_default_widgets(&mut factory);
46
47        Self {
48            factory,
49            document,
50            component_registry: ComponentRegistry::new(),
51            emitter,
52            message_receiver,
53            fps: 30,
54            size,
55            global_event_handler,
56            hot_reload: true,
57            function_table: FunctionTable::new(),
58            variables: Variables::new(),
59        }
60    }
61
62    /// Create a new runtime builder
63    pub(super) fn new(document: Document, size: Size, global_event_handler: G) -> Self {
64        let (tx, rx) = flume::unbounded();
65        let emitter = Emitter::from(tx);
66        Self::with_receiver(rx, emitter.clone(), document, size, global_event_handler)
67    }
68
69    /// Enable/Disable hot reloading
70    pub fn hot_reload(&mut self, value: bool) {
71        self.hot_reload = value;
72    }
73
74    /// Register a new widget
75    pub fn register_widget<T: Widget + Default + 'static>(&mut self, ident: &'static str) {
76        self.factory.register_default::<T>(ident);
77    }
78
79    /// Set the expected frame rate
80    pub fn fps(&mut self, fps: u32) {
81        self.fps = fps;
82    }
83
84    /// Returns an [Emitter] to send messages to components
85    pub fn emitter(&self) -> Emitter {
86        self.emitter.clone()
87    }
88
89    /// Registers a component as a template-only component.
90    ///
91    /// This component has no state or reacts to any events
92    pub fn template(&mut self, ident: impl Into<String>, template: impl ToSourceKind) -> Result<()> {
93        self.prototype(ident, template, || (), || ())
94    }
95
96    /// Registers a [Component] with the runtime.
97    /// This returns a unique [ComponentId] that is used to send messages to the component.
98    ///
99    /// A component can only be used once in a template.
100    /// If you want multiple instances, register the component as a prototype instead,
101    /// see [RuntimeBuilder::prototype].
102    pub fn component<C: Component>(
103        &mut self,
104        ident: impl Into<String>,
105        template: impl ToSourceKind,
106        component: C,
107        state: C::State,
108    ) -> Result<ComponentId<C::Message>> {
109        let id = self.document.add_component(ident, template.to_source_kind())?;
110        self.component_registry.add_component(id, component, state);
111        Ok(id.into())
112    }
113
114    /// Registers a [Component] with the runtime as long as the component and the associated state
115    /// implements the `Default` trait.
116    /// This returns a unique [ComponentId] that is used to send messages to the component.
117    pub fn default<C>(
118        &mut self,
119        ident: impl Into<String>,
120        template: impl ToSourceKind,
121    ) -> Result<ComponentId<C::Message>>
122    where
123        C: Component + Default,
124        C::State: Default,
125    {
126        let component = C::default();
127        let state = C::State::default();
128        let id = self.document.add_component(ident, template.to_source_kind())?;
129        self.component_registry.add_component(id, component, state);
130        Ok(id.into())
131    }
132
133    /// Registers a [Component] as a prototype with the [Runtime],
134    /// which allows for multiple instances of the component to exist the templates.
135    pub fn prototype<FC, FS, C>(
136        &mut self,
137        ident: impl Into<String>,
138        template: impl ToSourceKind,
139        proto: FC,
140        state: FS,
141    ) -> Result<()>
142    where
143        FC: 'static + Fn() -> C,
144        FS: 'static + FnMut() -> C::State,
145        C: Component + 'static,
146    {
147        let id = self.document.add_component(ident, template.to_source_kind())?;
148        self.component_registry.add_prototype(id, proto, state);
149        Ok(())
150    }
151
152    /// Assign a new event handler (make sure not to forget to add some mechanism to stop the
153    /// runtime)
154    pub fn with_global_event_handler<Eh>(self, global_event_handler: Eh) -> Builder<Eh>
155    where
156        Eh: Fn(Event, &mut TabIndex<'_, '_>, &mut DeferredComponents) -> Option<Event>,
157    {
158        Builder {
159            factory: self.factory,
160            document: self.document,
161            component_registry: self.component_registry,
162            emitter: self.emitter,
163            message_receiver: self.message_receiver,
164            fps: self.fps,
165            size: self.size,
166            global_event_handler,
167            hot_reload: self.hot_reload,
168            function_table: self.function_table,
169            variables: Variables::new(),
170        }
171    }
172
173    pub fn register_global(&mut self, key: impl Into<String>, value: impl Into<Expression>) -> Result<()> {
174        self.variables.define_global(key, value).map_err(|e| e.to_error(None))?;
175        Ok(())
176    }
177
178    pub fn finish<F, B>(mut self, backend: &mut B, mut f: F) -> Result<()>
179    where
180        F: FnMut(&mut Runtime<G>, &mut B) -> Result<()>,
181        B: Backend,
182    {
183        #[cfg(feature = "profile")]
184        let _puffin_server = {
185            let server_addr = format!("127.0.0.1:{}", puffin_http::DEFAULT_PORT);
186            let server = puffin_http::Server::new(&server_addr).unwrap();
187            puffin::set_scopes_on(true);
188            server
189        };
190
191        let blueprint = loop {
192            match self.document.compile(&mut self.variables) {
193                Ok(val) => break val,
194                // This can only show template errors.
195                // Widget errors doesn't become available until after the first tick.
196                Err(error) => match show_error(error.into(), backend, &self.document) {
197                    Ok(()) => return Err(Error::Stop),
198                    Err(Error::Reload) if self.hot_reload => {
199                        _ = self.document.reload_templates();
200                    }
201                    err => err?,
202                },
203            }
204        };
205
206        let watcher = self.set_watcher(self.hot_reload)?;
207
208        let mut inst = Runtime::new(
209            blueprint,
210            self.variables,
211            self.component_registry,
212            self.document,
213            self.factory,
214            self.message_receiver,
215            self.emitter,
216            watcher,
217            self.size,
218            self.fps,
219            self.global_event_handler,
220            self.function_table,
221            self.hot_reload,
222        );
223
224        f(&mut inst, backend)
225    }
226
227    fn set_watcher(&mut self, hot_reload: bool) -> Result<Option<RecommendedWatcher>> {
228        if !hot_reload {
229            return Ok(None);
230        }
231
232        let paths = self
233            .document
234            .template_paths()
235            .filter_map(|p| p.canonicalize().ok())
236            .collect::<Vec<_>>();
237
238        let mut watcher = recommended_watcher(move |event: std::result::Result<NotifyEvent, _>| match event {
239            Ok(event) => match event.kind {
240                notify::EventKind::Create(_) | notify::EventKind::Remove(_) | notify::EventKind::Modify(_) => {
241                    if paths.iter().any(|p| event.paths.contains(p)) {
242                        REBUILD.store(true, Ordering::Relaxed);
243                    }
244                }
245                notify::EventKind::Any | notify::EventKind::Access(_) | notify::EventKind::Other => (),
246            },
247            Err(_err) => (),
248        })?;
249
250        for path in self.document.template_paths() {
251            let path = path.canonicalize().unwrap();
252
253            if let Some(parent) = path.parent() {
254                watcher.watch(parent, RecursiveMode::NonRecursive)?;
255            }
256        }
257
258        Ok(Some(watcher))
259    }
260
261    pub fn register_function(&mut self, ident: impl Into<String>, f: impl Into<Function>) -> Result<()> {
262        Ok(self.function_table.insert(ident, f)?)
263    }
264}