anathema_runtime/
builder.rs

1use std::sync::atomic::Ordering;
2
3use anathema_default_widgets::register_default_widgets;
4use anathema_geometry::Size;
5use anathema_templates::{Document, ToSourceKind};
6use anathema_widgets::components::deferred::DeferredComponents;
7use anathema_widgets::components::events::Event;
8use anathema_widgets::components::{Component, ComponentId, ComponentRegistry, Emitter, ViewMessage};
9use anathema_widgets::tabindex::TabIndex;
10use anathema_widgets::{Factory, Widget};
11use notify::{Event as NotifyEvent, RecommendedWatcher, RecursiveMode, Watcher, recommended_watcher};
12
13use crate::REBUILD;
14pub use crate::error::{Error, Result};
15use crate::events::GlobalEventHandler;
16use crate::runtime::Runtime;
17
18fn error_doc() -> Document {
19    let template = "text 'you goofed up'";
20    Document::new(template)
21}
22
23pub struct Builder<G> {
24    factory: Factory,
25    document: Document,
26    component_registry: ComponentRegistry,
27    emitter: Emitter,
28    message_receiver: flume::Receiver<ViewMessage>,
29    fps: u32,
30    size: Size,
31    global_event_handler: G,
32}
33
34impl<G: GlobalEventHandler> Builder<G> {
35    /// Create a new runtime builder
36    pub(super) fn new(document: Document, size: Size, global_event_handler: G) -> Self {
37        let mut factory = Factory::new();
38        register_default_widgets(&mut factory);
39
40        let (tx, message_receiver) = flume::unbounded();
41        let emitter = tx.into();
42
43        Self {
44            factory,
45            document,
46            component_registry: ComponentRegistry::new(),
47            emitter,
48            message_receiver,
49            fps: 30,
50            size,
51            global_event_handler,
52        }
53    }
54
55    pub fn register_widget<T: Widget + Default + 'static>(&mut self, ident: &'static str) {
56        self.factory.register_default::<T>(ident);
57    }
58
59    pub fn fps(&mut self, fps: u32) {
60        self.fps = fps;
61    }
62
63    /// Returns an [Emitter] to send messages to components
64    pub fn emitter(&self) -> Emitter {
65        self.emitter.clone()
66    }
67
68    /// Registers a component as a template-only component.
69    ///
70    /// This component has no state or reacts to any events
71    pub fn template(&mut self, ident: impl Into<String>, template: impl ToSourceKind) -> Result<()> {
72        self.prototype(ident, template, || (), || ())
73    }
74
75    /// Registers a [Component] with the runtime.
76    /// This returns a unique [ComponentId] that is used to send messages to the component.
77    ///
78    /// A component can only be used once in a template.
79    /// If you want multiple instances, register the component as a prototype instead,
80    /// see [RuntimeBuilder::prototype].
81    pub fn component<C: Component>(
82        &mut self,
83        ident: impl Into<String>,
84        template: impl ToSourceKind,
85        component: C,
86        state: C::State,
87    ) -> Result<ComponentId<C::Message>> {
88        let id = self.document.add_component(ident, template.to_source_kind())?;
89        self.component_registry.add_component(id, component, state);
90        Ok(id.into())
91    }
92
93    /// Registers a [Component] with the runtime as long as the component and the associated state
94    /// implements the `Default` trait.
95    /// This returns a unique [ComponentId] that is used to send messages to the component.
96    pub fn default<C>(
97        &mut self,
98        ident: impl Into<String>,
99        template: impl ToSourceKind,
100    ) -> Result<ComponentId<C::Message>>
101    where
102        C: Component + Default,
103        C::State: Default,
104    {
105        let component = C::default();
106        let state = C::State::default();
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] as a prototype with the [Runtime],
113    /// which allows for multiple instances of the component to exist the templates.
114    pub fn prototype<FC, FS, C>(
115        &mut self,
116        ident: impl Into<String>,
117        template: impl ToSourceKind,
118        proto: FC,
119        state: FS,
120    ) -> Result<()>
121    where
122        FC: 'static + Fn() -> C,
123        FS: 'static + FnMut() -> C::State,
124        C: Component + 'static,
125    {
126        let id = self.document.add_component(ident, template.to_source_kind())?;
127        self.component_registry.add_prototype(id, proto, state);
128        Ok(())
129    }
130
131    pub fn with_global_event_handler<Eh>(self, global_event_handler: Eh) -> Builder<Eh>
132    where
133        Eh: Fn(Event, &mut TabIndex<'_, '_>, &mut DeferredComponents) -> Option<Event>,
134    {
135        Builder {
136            factory: self.factory,
137            document: self.document,
138            component_registry: self.component_registry,
139            emitter: self.emitter,
140            message_receiver: self.message_receiver,
141            fps: self.fps,
142            size: self.size,
143            global_event_handler,
144        }
145    }
146
147    pub fn finish<F>(mut self, mut f: F) -> Result<()>
148    where
149        F: FnMut(&mut Runtime<G>) -> Result<()>,
150    {
151        #[cfg(feature = "profile")]
152        let _puffin_server = {
153            let server_addr = format!("127.0.0.1:{}", puffin_http::DEFAULT_PORT);
154            let server = puffin_http::Server::new(&server_addr).unwrap();
155            puffin::set_scopes_on(true);
156            server
157        };
158
159        let watcher = self.set_watcher()?;
160
161        let mut inst = Runtime::new(
162            self.component_registry,
163            self.document,
164            error_doc(),
165            self.factory,
166            self.message_receiver,
167            self.emitter,
168            Some(watcher),
169            self.size,
170            self.fps,
171            self.global_event_handler,
172        )?;
173
174        // TODO: this enables hot reload,
175        //       however with this enabled the `with_frame` function
176        //       on the runtime will repeat
177        loop {
178            match f(&mut inst) {
179                Ok(()) => (),
180                e => match e {
181                    Ok(_) => continue,
182                    Err(Error::Stop) => break Ok(()),
183                    Err(Error::Template(_error)) => todo!(),
184                    Err(Error::Widget(err)) => panic!("this should not panic in the future: {err}"),
185                    Err(e) => break Err(e),
186                },
187            }
188            match inst.reload() {
189                Ok(()) => continue,
190                Err(Error::Stop) => todo!(),
191                Err(Error::Template(_error)) => todo!(),
192                Err(Error::Widget(_error)) => todo!(),
193                Err(e) => break Err(e),
194            }
195        }
196    }
197
198    fn set_watcher(&mut self) -> Result<RecommendedWatcher> {
199        let paths = self
200            .document
201            .template_paths()
202            .filter_map(|p| p.canonicalize().ok())
203            .collect::<Vec<_>>();
204
205        let mut watcher = recommended_watcher(move |event: std::result::Result<NotifyEvent, _>| match event {
206            Ok(event) => match event.kind {
207                notify::EventKind::Create(_) | notify::EventKind::Remove(_) | notify::EventKind::Modify(_) => {
208                    if paths.iter().any(|p| event.paths.contains(p)) {
209                        REBUILD.store(true, Ordering::Relaxed);
210                    }
211                }
212                notify::EventKind::Any | notify::EventKind::Access(_) | notify::EventKind::Other => (),
213            },
214            Err(_err) => (),
215        })?;
216
217        for path in self.document.template_paths() {
218            let path = path.canonicalize().unwrap();
219
220            if let Some(parent) = path.parent() {
221                watcher.watch(parent, RecursiveMode::NonRecursive)?;
222            }
223        }
224
225        Ok(watcher)
226    }
227}