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