freya-core 0.4.0-rc.7

Reactivity runtime, tree management, accessibility integration, rendering pipeline and more, for Freya
Documentation
use std::{
    cell::RefCell,
    rc::Rc,
    sync::atomic::AtomicU64,
};

use rustc_hash::FxHashMap;

use crate::{
    prelude::{
        Task,
        TaskId,
    },
    reactive_context::ReactiveContext,
    runner::Message,
    scope::ScopeStorage,
    scope_id::ScopeId,
};

// TODO: rendering flag.
pub struct CurrentContext {
    pub scope_id: ScopeId,
    pub scopes_storages: Rc<RefCell<FxHashMap<ScopeId, ScopeStorage>>>,

    pub tasks: Rc<RefCell<FxHashMap<TaskId, Rc<RefCell<Task>>>>>,
    pub task_id_counter: Rc<AtomicU64>,
    pub sender: futures_channel::mpsc::UnboundedSender<Message>,
}

impl CurrentContext {
    pub fn run_with_reactive<T>(new_context: Self, run: impl FnOnce() -> T) -> T {
        let reactive_context = CURRENT_CONTEXT.with_borrow_mut(|context| {
            let reactive_context = {
                let scope_storages = new_context.scopes_storages.borrow();
                let scope_storage = scope_storages.get(&new_context.scope_id).unwrap();
                scope_storage.reactive_context.clone()
            };
            context.replace(new_context);
            reactive_context
        });
        let res = ReactiveContext::run(reactive_context, run);
        CURRENT_CONTEXT.with_borrow_mut(|context| context.take());
        res
    }

    pub fn run<T>(new_context: Self, run: impl FnOnce() -> T) -> T {
        CURRENT_CONTEXT.with_borrow_mut(|context| {
            context.replace(new_context);
        });
        let res = run();
        CURRENT_CONTEXT.with_borrow_mut(|context| context.take());
        res
    }

    pub fn with<T>(with: impl FnOnce(&CurrentContext) -> T) -> T {
        CURRENT_CONTEXT.with(|context| with(context.borrow().as_ref().expect("Your trying to access Freya's current context outside of it, you might be in a separate thread or async task that is not integrated with Freya.")))
    }

    pub fn try_with<T>(with: impl FnOnce(&CurrentContext) -> T) -> Option<T> {
        CURRENT_CONTEXT
            .try_with(|context| {
                if let Ok(context) = context.try_borrow()
                    && let Some(context) = context.as_ref()
                {
                    Some(with(context))
                } else {
                    None
                }
            })
            .ok()
            .flatten()
    }
}

thread_local! {
    static CURRENT_CONTEXT: RefCell<Option<CurrentContext>> = const { RefCell::new(None) }
}