use std::collections::VecDeque;
use std::sync::atomic::Ordering;
use std::time::{Duration, Instant};
use anathema_backend::{Backend, WidgetCycle};
use anathema_geometry::Size;
use anathema_state::{Changes, StateId, States, clear_all_changes, clear_all_subs, drain_changes};
use anathema_store::tree::root_node;
use anathema_templates::blueprints::Blueprint;
use anathema_templates::{Document, Variables};
use anathema_value_resolver::{AttributeStorage, FunctionTable, Scope};
use anathema_widgets::components::deferred::{CommandKind, DeferredComponents};
use anathema_widgets::components::events::{Event, EventType};
use anathema_widgets::components::{
AnyComponentContext, AssociatedEvents, ComponentKind, ComponentRegistry, Emitter, MessageReceiver, Recipient,
ViewMessage,
};
use anathema_widgets::layout::{LayoutCtx, Viewport};
use anathema_widgets::query::Children;
use anathema_widgets::tabindex::{Index, TabIndex};
use anathema_widgets::{
Component, Components, Factory, FloatingWidgets, GlyphMap, WidgetContainer, WidgetId, WidgetKind, WidgetTree,
eval_blueprint, update_widget,
};
use flume::Receiver;
use notify::RecommendedWatcher;
pub(crate) use self::error::show_error;
use crate::builder::Builder;
pub use crate::error::Result;
use crate::events::GlobalEventHandler;
use crate::{Error, REBUILD};
mod error;
mod testing;
pub struct Runtime<G> {
pub(super) blueprint: Blueprint,
pub(super) variables: Variables,
pub(super) factory: Factory,
pub(super) states: States,
pub(super) component_registry: ComponentRegistry,
pub(super) components: Components,
pub(super) document: Document,
pub(super) floating_widgets: FloatingWidgets,
pub(super) assoc_events: AssociatedEvents,
pub(super) glyph_map: GlyphMap,
pub(super) changes: Changes,
pub(super) viewport: Viewport,
pub(super) emitter: Emitter,
pub(super) sleep_micros: u64,
pub(super) message_receiver: flume::Receiver<ViewMessage>,
pub(super) dt: Instant,
pub(super) _watcher: Option<RecommendedWatcher>,
pub(super) deferred_components: DeferredComponents,
pub(super) global_event_handler: G,
pub(super) function_table: FunctionTable,
pub(super) hot_reload: bool,
}
impl Runtime<()> {
pub fn builder<B: Backend>(doc: Document, backend: &B) -> Builder<()> {
Builder::new(doc, backend.size(), ())
}
pub fn with_receiver<B: Backend>(
message_receiver: MessageReceiver,
emitter: Emitter,
doc: Document,
backend: &B,
) -> Builder<()> {
Builder::with_receiver(message_receiver, emitter, doc, backend.size(), ())
}
}
impl<G: GlobalEventHandler> Runtime<G> {
pub(crate) fn new(
blueprint: Blueprint,
variables: Variables,
component_registry: ComponentRegistry,
document: Document,
factory: Factory,
message_receiver: Receiver<ViewMessage>,
emitter: Emitter,
watcher: Option<RecommendedWatcher>,
size: Size,
fps: u32,
global_event_handler: G,
function_table: FunctionTable,
hot_reload: bool,
) -> Self {
let sleep_micros: u64 = ((1.0 / fps as f64) * 1000.0 * 1000.0) as u64;
Self {
component_registry,
components: Components::new(),
document,
factory,
states: States::new(),
floating_widgets: FloatingWidgets::empty(),
assoc_events: AssociatedEvents::new(),
glyph_map: GlyphMap::empty(),
blueprint,
variables,
changes: Changes::empty(),
viewport: Viewport::new(size),
message_receiver,
emitter,
dt: Instant::now(),
_watcher: watcher,
deferred_components: DeferredComponents::new(),
sleep_micros,
global_event_handler,
function_table,
hot_reload,
}
}
pub fn with_frame<B, F>(&mut self, backend: &mut B, mut f: F) -> Result<()>
where
B: Backend,
F: FnMut(&mut B, Frame<'_, '_, G>) -> Result<()>,
{
let mut tree = WidgetTree::empty();
let mut attribute_storage = AttributeStorage::empty();
let mut frame = self.next_frame(&mut tree, &mut attribute_storage)?;
frame.init_tree()?;
f(backend, frame)
}
pub fn run<B: Backend>(&mut self, backend: &mut B) -> Result<()> {
let sleep_micros = self.sleep_micros;
loop {
let res = self.with_frame(backend, |backend, mut frame| {
_ = frame.tick(backend);
TabIndex::new(&mut frame.tabindex, frame.tree.view()).next();
if let Some(current) = frame.tabindex.as_ref() {
frame.with_component(current.widget_id, current.state_id, |comp, children, ctx| {
comp.dyn_component.any_focus(children, ctx)
});
}
loop {
if REBUILD.swap(false, Ordering::Relaxed) {
frame.force_unmount_return();
backend.clear();
break Err(Error::Reload);
}
match frame.tick(backend) {
Ok(_duration) => (),
Err(err) => match err {
err @ (Error::Template(_) | Error::Widget(_)) => {
match show_error(err, backend, frame.document) {
Err(Error::Stop) => return Err(Error::Stop),
Err(Error::Reload) => continue,
_ => unreachable!("show_error only return stop or rebuild"),
}
}
err => return Err(err),
},
}
if frame.layout_ctx.stop_runtime {
return Err(Error::Stop);
}
frame.present(backend);
frame.cleanup();
std::thread::sleep(Duration::from_micros(sleep_micros));
}
});
match res {
Ok(()) => panic!(),
Err(e) => match e {
Error::Template(_) | Error::Widget(_) => {
unreachable!("these error variants are handled inside the tick loop")
}
Error::Stop => return Err(Error::Stop),
Error::Reload => loop {
match self.reload() {
Ok(()) => break,
Err(e) => {
if let Err(Error::Stop) = show_error(e, backend, &self.document) {
return Err(Error::Stop);
}
}
}
},
e => return Err(e),
},
}
if !self.hot_reload {
break;
}
}
Ok(())
}
pub fn next_frame<'frame, 'bp>(
&'bp mut self,
tree: &'frame mut WidgetTree<'bp>,
attribute_storage: &'frame mut AttributeStorage<'bp>,
) -> Result<Frame<'frame, 'bp, G>> {
let layout_ctx = LayoutCtx::new(
&self.variables,
&self.factory,
&mut self.states,
attribute_storage,
&mut self.components,
&mut self.component_registry,
&mut self.floating_widgets,
&mut self.glyph_map,
&mut self.viewport,
&self.function_table,
);
let inst = Frame {
document: &self.document,
blueprint: &self.blueprint,
tree,
layout_ctx,
changes: &mut self.changes,
sleep_micros: self.sleep_micros,
assoc_events: &mut self.assoc_events,
deferred_components: &mut self.deferred_components,
emitter: &self.emitter,
message_receiver: &self.message_receiver,
dt: &mut self.dt,
needs_layout: true,
post_cycle_events: VecDeque::new(),
global_event_handler: &self.global_event_handler,
tabindex: None,
};
Ok(inst)
}
pub(super) fn reload(&mut self) -> Result<()> {
clear_all_changes();
clear_all_subs();
self.components = Components::new();
self.floating_widgets = FloatingWidgets::empty();
self.document.reload_templates()?;
let (blueprint, variables) = self.document.compile()?;
self.blueprint = blueprint;
self.variables = variables;
Ok(())
}
}
pub struct Frame<'rt, 'bp, G> {
document: &'bp Document,
blueprint: &'bp Blueprint,
pub tree: &'rt mut WidgetTree<'bp>,
deferred_components: &'rt mut DeferredComponents,
layout_ctx: LayoutCtx<'rt, 'bp>,
changes: &'rt mut Changes,
assoc_events: &'rt mut AssociatedEvents,
sleep_micros: u64,
emitter: &'rt Emitter,
message_receiver: &'rt flume::Receiver<ViewMessage>,
dt: &'rt mut Instant,
needs_layout: bool,
post_cycle_events: VecDeque<Event>,
global_event_handler: &'rt G,
pub tabindex: Option<Index>,
}
impl<'rt, 'bp, G: GlobalEventHandler> Frame<'rt, 'bp, G> {
pub fn force_unmount_return(mut self) {
for i in 0..self.layout_ctx.components.len() {
let Some((widget_id, state_id)) = self.layout_ctx.components.get_ticking(i) else { continue };
let event = Event::Unmount;
self.send_event_to_component(event, widget_id, state_id);
}
self.return_state_and_component();
}
pub fn handle_global_event(&mut self, event: Event) -> Option<Event> {
let mut tabindex = TabIndex::new(&mut self.tabindex, self.tree.view());
let event = self
.global_event_handler
.handle(event, &mut tabindex, self.deferred_components);
if tabindex.changed {
let prev = tabindex.consume();
if let Some(prev) = prev {
self.with_component(prev.widget_id, prev.state_id, |comp, children, ctx| {
comp.dyn_component.any_blur(children, ctx)
});
}
if let Some(current) = self.tabindex.as_ref() {
self.with_component(current.widget_id, current.state_id, |comp, children, ctx| {
comp.dyn_component.any_focus(children, ctx)
});
}
}
event
}
pub fn event(&mut self, event: Event) {
#[cfg(feature = "profile")]
puffin::profile_function!();
let Some(event) = self.handle_global_event(event) else { return };
if let Event::Stop = event {
self.layout_ctx.stop_runtime = true;
return;
}
match event {
Event::Noop => (),
Event::Stop => todo!(),
Event::Blur | Event::Focus | Event::Key(_) => {
let Some(Index {
widget_id, state_id, ..
}) = self.tabindex
else {
return;
};
self.send_event_to_component(event, widget_id, state_id);
}
Event::Mouse(_) | Event::Resize(_) => {
for i in 0..self.layout_ctx.components.len() {
let Some((widget_id, state_id)) = self.layout_ctx.components.get(i) else { continue };
self.send_event_to_component(event, widget_id, state_id);
}
}
Event::Tick(_) | Event::Mount | Event::Unmount => panic!("this event should never be sent to the runtime"),
}
}
pub fn init_tree(&mut self) -> Result<()> {
let mut ctx = self.layout_ctx.eval_ctx(None);
eval_blueprint(
self.blueprint,
&mut ctx,
&Scope::root(),
root_node(),
&mut self.tree.view(),
)?;
Ok(())
}
pub fn tick<B: Backend>(&mut self, backend: &mut B) -> Result<Duration> {
#[cfg(feature = "profile")]
puffin::GlobalProfiler::lock().new_frame();
let now = Instant::now();
self.init_new_components();
let elapsed = self.handle_messages(now);
self.poll_events(elapsed, now, backend);
self.drain_deferred_commands();
self.drain_assoc_events();
self.apply_changes()?;
self.apply_changes()?;
self.tick_components(self.dt.elapsed());
self.cycle(backend)?;
self.post_cycle_events();
*self.dt = Instant::now();
match self.layout_ctx.stop_runtime {
false => Ok(now.elapsed()),
true => Err(Error::Stop),
}
}
pub fn present<B: Backend>(&mut self, backend: &mut B) -> Duration {
#[cfg(feature = "profile")]
puffin::profile_function!();
let now = Instant::now();
backend.render(self.layout_ctx.glyph_map);
backend.clear();
now.elapsed()
}
pub fn cleanup(&mut self) {
#[cfg(feature = "profile")]
puffin::profile_function!();
for key in self.tree.drain_removed() {
self.layout_ctx.attribute_storage.try_remove(key);
self.layout_ctx.floating_widgets.try_remove(key);
self.layout_ctx.components.try_remove(key);
if let Some(Index { widget_id, .. }) = self.tabindex {
if widget_id == key {
self.tabindex.take();
}
}
}
}
fn handle_messages(&mut self, fps_now: Instant) -> Duration {
#[cfg(feature = "profile")]
puffin::profile_function!();
while let Ok(msg) = self.message_receiver.try_recv() {
let (widget_id, state_id) = match msg.recipient() {
Recipient::ComponentId(id) => {
let val = self
.layout_ctx
.components
.get_by_component_id(id)
.map(|e| (e.widget_id, e.state_id));
let Some(id_and_state) = val else { continue };
id_and_state
}
Recipient::WidgetId(id) => {
let state_id = self.layout_ctx.components.get_by_widget_id(id).map(|(_, state)| state);
let Some(state_id) = state_id else { continue };
(id, state_id)
}
};
self.with_component(widget_id, state_id, |comp, elements, ctx| {
comp.dyn_component.any_message(elements, ctx, msg.payload())
});
if fps_now.elapsed().as_micros() as u64 >= self.sleep_micros / 2 {
break;
}
}
fps_now.elapsed()
}
fn poll_events<B: Backend>(&mut self, remaining: Duration, fps_now: Instant, backend: &mut B) {
while let Some(event) = backend.next_event(remaining) {
if let Event::Resize(size) = event {
self.layout_ctx.viewport.resize(size);
self.needs_layout = true;
backend.resize(size, self.layout_ctx.glyph_map);
}
if let Event::Stop = event {
self.layout_ctx.stop_runtime = true;
break;
}
match event.into() {
EventType::PreCycle => {
self.event(event);
}
EventType::PostCycle => {
self.post_cycle_events.push_back(event);
}
}
if fps_now.elapsed().as_micros() as u64 > self.sleep_micros {
break;
}
}
}
fn drain_deferred_commands(&mut self) {
#[cfg(feature = "profile")]
puffin::profile_function!();
let commands = self.deferred_components.drain().collect::<Vec<_>>();
for mut cmd in commands {
for index in 0..self.layout_ctx.components.len() {
let Some((widget_id, state_id)) = self.layout_ctx.components.get(index) else { continue };
let Some(comp) = self.tree.get_ref(widget_id) else { continue };
let WidgetContainer {
kind: WidgetKind::Component(comp),
..
} = comp
else {
continue;
};
let attributes = self.layout_ctx.attribute_storage.get(widget_id);
if !cmd.filter_component(comp, attributes) {
continue;
}
if let CommandKind::Focus = cmd.kind {
if let Some(index) = self.tabindex.as_ref() {
if index.widget_id == widget_id {
continue;
}
}
let new_index = self
.with_component(widget_id, state_id, |comp, children, ctx| {
if comp.dyn_component.any_accept_focus() {
let index = Index {
path: children.parent_path().into(),
index: comp.tabindex,
widget_id,
state_id,
};
comp.dyn_component.any_focus(children, ctx);
Some(index)
} else {
None
}
})
.flatten();
if let Some(index) = new_index {
if let Some(old) = self.tabindex.replace(index) {
self.with_component(old.widget_id, old.state_id, |comp, children, ctx| {
comp.dyn_component.any_blur(children, ctx)
});
}
}
}
if let CommandKind::SendMessage(msg) = cmd.kind {
self.with_component(widget_id, state_id, |comp, children, ctx| {
comp.dyn_component.any_message(children, ctx, msg);
});
break;
}
}
}
}
fn drain_assoc_events(&mut self) {
#[cfg(feature = "profile")]
puffin::profile_function!();
while let Some(assoc_event) = self.assoc_events.next() {
let mut parent = assoc_event.parent;
let external_ident = self.document.strings.get_ref_unchecked(assoc_event.external());
let internal_ident = self.document.strings.get_ref_unchecked(assoc_event.internal());
let sender = self.document.strings.get_ref_unchecked(assoc_event.sender);
let sender_id = assoc_event.sender_id;
let mut event = assoc_event.to_event(internal_ident, external_ident, sender, sender_id);
loop {
let Some((widget_id, state_id)) = self.layout_ctx.components.get_by_widget_id(parent.into()) else {
return;
};
let stop_propagation = self
.with_component(widget_id, state_id, |comp, children, ctx| {
let next_parent = ctx.parent();
comp.dyn_component.any_component_event(children, ctx, &mut event);
parent = match next_parent {
Some(p) => p,
None => return true,
};
event.should_stop_propagation()
})
.unwrap_or(true);
if stop_propagation {
break;
}
}
}
}
fn cycle<B: Backend>(&mut self, backend: &mut B) -> Result<()> {
#[cfg(feature = "profile")]
puffin::profile_function!();
let mut cycle = WidgetCycle::new(backend, self.tree.view(), self.layout_ctx.viewport.constraints());
cycle.run(&mut self.layout_ctx, self.needs_layout)?;
self.needs_layout = false;
Ok(())
}
fn apply_changes(&mut self) -> Result<()> {
#[cfg(feature = "profile")]
puffin::profile_function!();
drain_changes(self.changes);
if self.changes.is_empty() {
return Ok(());
}
self.needs_layout = true;
let mut tree = self.tree.view();
self.changes.iter().try_for_each(|(sub, change)| {
sub.iter().try_for_each(|value_id| {
let widget_id = value_id.key();
if let Some(widget) = tree.get_mut(widget_id) {
if let WidgetKind::Element(element) = &mut widget.kind {
element.invalidate_cache();
}
}
if !tree.contains(widget_id) {
return Result::Ok(());
}
tree.with_value_mut(widget_id, |_path, widget, tree| {
update_widget(widget, value_id, change, tree, &mut self.layout_ctx)
})
.unwrap_or(Ok(()))?;
Ok(())
})?;
Result::Ok(())
})?;
self.changes.clear();
Ok(())
}
fn send_event_to_component(&mut self, event: Event, widget_id: WidgetId, state_id: StateId) {
self.with_component(widget_id, state_id, |comp, elements, ctx| {
comp.dyn_component.any_event(elements, ctx, event);
});
}
fn with_component<F, U>(&mut self, widget_id: WidgetId, state_id: StateId, f: F) -> Option<U>
where
F: FnOnce(&mut Component<'_>, Children<'_, '_>, AnyComponentContext<'_, '_>) -> U,
{
let mut tree = self.tree.view();
tree.with_value_mut(widget_id, |_path, container, children| {
let WidgetKind::Component(component) = &mut container.kind else {
panic!("this is always a component")
};
let Some(state) = self.layout_ctx.states.get_mut(state_id) else {
panic!("a component always has a state")
};
self.layout_ctx
.attribute_storage
.with_mut(widget_id, |attributes, storage| {
let elements = Children::new(children, storage, &mut self.needs_layout);
let ctx = AnyComponentContext::new(
component.parent.map(Into::into),
component.name_id,
widget_id,
state_id,
component.assoc_functions,
self.assoc_events,
self.deferred_components,
attributes,
Some(state),
self.emitter,
self.layout_ctx.viewport,
&mut self.layout_ctx.stop_runtime,
&self.document.strings,
);
f(component, elements, ctx)
})
})?
}
fn tick_components(&mut self, dt: Duration) {
#[cfg(feature = "profile")]
puffin::profile_function!();
for i in 0..self.layout_ctx.components.len() {
let Some((widget_id, state_id)) = self.layout_ctx.components.get_ticking(i) else { continue };
let event = Event::Tick(dt);
self.send_event_to_component(event, widget_id, state_id);
}
}
fn init_new_components(&mut self) {
while let Some((widget_id, state_id)) = self.layout_ctx.new_components.pop() {
self.with_component(widget_id, state_id, |comp, elements, ctx| {
comp.dyn_component.any_event(elements, ctx, Event::Mount);
});
}
}
fn return_state_and_component(self) {
let mut tree = WidgetTree::empty();
std::mem::swap(&mut tree, self.tree);
for (_, widget) in tree.values().into_iter() {
let WidgetKind::Component(comp) = widget.kind else { continue };
let ComponentKind::Instance = comp.kind else { continue };
let state = self.layout_ctx.states.remove(comp.state_id).take();
self.layout_ctx
.component_registry
.return_component(comp.component_id, comp.dyn_component, state);
}
}
fn post_cycle_events(&mut self) {
while let Some(event) = self.post_cycle_events.pop_front() {
self.event(event);
}
}
}