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()));
        }
    }
}