concoct 0.18.0

Cross-platform UI framework
Documentation
use crate::{macros::trace, Runtime, Scope, Tree, ViewBuilder};
use slotmap::{DefaultKey, Key};
use std::{
    any::{self, Any},
    mem,
};

pub struct Node<V, B, F> {
    pub(crate) view: V,
    pub(crate) body: Option<B>,
    pub(crate) builder: F,
    pub(crate) scope: Option<Scope>,
    pub(crate) key: Option<DefaultKey>,
}

impl<V, B, F> Node<V, B, F> {
    fn name(&self) -> &'static str {
        let name = any::type_name::<V>();
        name.split('<')
            .next()
            .unwrap_or(name)
            .split("::")
            .last()
            .unwrap_or(name)
    }
}

impl<V, B, F> Tree for Node<V, B, F>
where
    V: ViewBuilder,
    B: Tree + 'static,
    F: FnMut(&'static V) -> B + 'static,
{
    unsafe fn build(&mut self) {
        let cx = Runtime::current();
        let mut cx_ref = cx.inner.borrow_mut();

        if let Some(key) = self.key {
            let mut scope = self.scope.as_ref().unwrap().inner.borrow_mut();
            for (name, value) in cx_ref.contexts.iter() {
                if !scope.contexts.contains_key(name) {
                    scope.contexts.insert(*name, value.clone());
                }
            }
            drop(scope);

            cx_ref.node = Some(key);
            cx_ref.scope = Some(self.scope.clone().unwrap());
            drop(cx_ref);

            trace!("rebuilding from {:?}: {}", key.data(), self.name());

            let view = unsafe { mem::transmute(&self.view) };
            let body = (self.builder)(view);

            let parent_contexts = {
                let mut cx_ref = cx.inner.borrow_mut();
                let mut scope = self.scope.as_ref().unwrap().inner.borrow_mut();
                scope.hook_idx = 0;
                mem::replace(&mut cx_ref.contexts, scope.contexts.clone())
            };

            let mut last_body = mem::replace(&mut self.body, Some(body)).unwrap();
            self.body.as_mut().unwrap().rebuild(&mut last_body, true);

            let mut cx_ref = cx.inner.borrow_mut();
            cx_ref.contexts = parent_contexts;
        } else {
            let key = cx_ref.nodes.insert(self as _);
            self.key = Some(key);

            let scope = Scope::default();
            scope.inner.borrow_mut().contexts = cx_ref.contexts.clone();
            self.scope = Some(scope);

            cx_ref.node = Some(key);
            cx_ref.scope = Some(self.scope.clone().unwrap());
            drop(cx_ref);

            trace!("building {:?}: {}", key.data(), self.name());

            let view = unsafe { mem::transmute(&self.view) };
            let body = (self.builder)(view);

            let parent_contexts = {
                let mut cx_ref = cx.inner.borrow_mut();
                let mut scope = self.scope.as_ref().unwrap().inner.borrow_mut();
                scope.hook_idx = 0;
                mem::replace(&mut cx_ref.contexts, scope.contexts.clone())
            };

            self.body = Some(body);
            self.body.as_mut().unwrap().build();

            let mut cx_ref = cx.inner.borrow_mut();
            cx_ref.contexts = parent_contexts;
        }
    }

    unsafe fn rebuild(&mut self, last: &mut dyn Any, is_changed: bool) {
        let last = (*last).downcast_mut::<Self>().unwrap();
        let cx = Runtime::current();
        let mut cx_ref = cx.inner.borrow_mut();

        let key = last.key.unwrap();
        self.key = Some(key);
        self.scope = last.scope.clone();

        if is_changed {
            let mut scope = self.scope.as_ref().unwrap().inner.borrow_mut();
            for (name, value) in cx_ref.contexts.iter() {
                if !scope.contexts.contains_key(name) {
                    scope.contexts.insert(*name, value.clone());
                }
            }
            drop(scope);

            cx_ref.node = Some(key);
            cx_ref.scope = Some(self.scope.clone().unwrap());
            drop(cx_ref);

            trace!("rebuilding {:?}: {}", key.data(), self.name());

            let view = unsafe { mem::transmute(&self.view) };
            let body = (self.builder)(view);

            let parent_contexts = {
                let mut cx_ref = cx.inner.borrow_mut();
                let mut scope = self.scope.as_ref().unwrap().inner.borrow_mut();
                scope.hook_idx = 0;
                mem::replace(&mut cx_ref.contexts, scope.contexts.clone())
            };

            self.body = Some(body);

            self.body
                .as_mut()
                .unwrap()
                .rebuild(last.body.as_mut().unwrap(), is_changed);

            let mut cx_ref = cx.inner.borrow_mut();
            cx_ref.contexts = parent_contexts;
        } else {
            trace!("skipping {:?}: {}", self.key.unwrap().data(), self.name());

            self.body = last.body.take();
        }
    }

    unsafe fn remove(&mut self) {
        let cx = Runtime::current();
        let mut cx_ref = cx.inner.borrow_mut();
        let key = self.key.unwrap();
        cx_ref.nodes.remove(key);
        drop(cx_ref);

        trace!("removing {:?}: {}", key.data(), self.name());

        for dropper in &mut self.scope.as_ref().unwrap().inner.borrow_mut().droppers {
            dropper()
        }

        self.body.as_mut().unwrap().remove();
    }
}