live.rs
raw
//! Taking a component's structure from its source file while the app runs.
//!
//! The interpreter lives above this crate, so the tree cannot own a dev mount
//! directly. It owns this trait instead: something that, when polled, may
//! rebuild the tree from a file that changed. The shell polls it on every
//! wake, which is the whole of the plumbing an application needs — no branch
//! in `main`, no per-frame call to remember.
use crate::widget::WidgetTree;
/// A source of live structure, polled by the shell.
pub trait LiveReload: 'static {
/// Apply any pending source change. Returns true if the tree was rebuilt.
fn poll(&mut self, tree: &mut WidgetTree) -> bool;
/// Install the callback that wakes the event loop from whatever thread
/// notices a change. Without one, a reload waits for the next natural
/// wakeup — input, a resize — instead of appearing as you save.
fn set_waker(&mut self, wake: Box<dyn Fn() + Send>);
}
impl WidgetTree {
/// Take structure from `source` as well as from whatever is already
/// installed.
///
/// Plural on purpose. A tree can hold more than one mounted component —
/// a sidebar and a panel from two different files is an ordinary shape —
/// and each takes its structure from its own source. A single slot would
/// mean the second mount silently stopped the first from reloading, which
/// is a trap rather than a policy.
pub fn set_live_reload(&mut self, source: impl LiveReload) {
self.live.push(Box::new(source));
}
/// Whether this tree is taking its structure from any live source.
pub fn is_live(&self) -> bool {
!self.live.is_empty()
}
/// How many live sources this tree holds — one per live mount.
pub fn live_sources(&self) -> usize {
self.live.len()
}
/// Apply any pending source change; true if the tree was rebuilt.
///
/// The hooks are moved out for the call, because rebuilding needs the
/// whole tree and they are part of it — the same borrow dance event
/// handlers do. Hooks installed *during* a poll are kept: a reload that
/// mounts something live is the newer truth, not a thing to discard.
pub fn poll_live_reload(&mut self) -> bool {
if self.live.is_empty() {
return false;
}
let mut hooks = std::mem::take(&mut self.live);
let mut reloaded = false;
for hook in &mut hooks {
reloaded |= hook.poll(self);
}
hooks.append(&mut self.live);
self.live = hooks;
reloaded
}
/// Give every live source a way to wake the event loop.
pub fn connect_live_waker(&mut self, wake: impl Fn() + Send + Clone + 'static) {
for live in &mut self.live {
live.set_waker(Box::new(wake.clone()));
}
}
}