cranpose-ui 0.9.0

UI primitives for Cranpose
Documentation
use std::cell::{Cell, RefCell};

use cranpose_core::NodeId;

thread_local! {
    static ARMED_LOGS: Cell<usize> = const { Cell::new(0) };
}

const MAX_PENDING: usize = 4096;

pub(crate) struct SemanticsLayoutLog {
    armed: Cell<bool>,
    epoch: Cell<u64>,
    sync: Cell<u64>,
    pending: RefCell<Vec<NodeId>>,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) struct LayoutSync {
    pub(crate) epoch: u64,
    pub(crate) continuous: bool,
}

impl Default for SemanticsLayoutLog {
    fn default() -> Self {
        Self {
            armed: Cell::new(false),
            epoch: Cell::new(0),
            sync: Cell::new(1),
            pending: RefCell::new(Vec::new()),
        }
    }
}

impl SemanticsLayoutLog {
    fn set_armed(&self, armed: bool) {
        if self.armed.replace(armed) == armed {
            return;
        }
        let _ = ARMED_LOGS.try_with(|logs| {
            logs.set(if armed {
                logs.get() + 1
            } else {
                logs.get().saturating_sub(1)
            });
        });
    }

    fn record(&self, node: NodeId, stamp: &mut u64) {
        let sync = self.sync.get();
        if !self.armed.get() || *stamp == sync {
            return;
        }
        *stamp = sync;
        let mut pending = self.pending.borrow_mut();
        if pending.len() < MAX_PENDING {
            pending.push(node);
            return;
        }
        pending.clear();
        drop(pending);
        self.epoch.set(self.epoch.get().wrapping_add(1));
        self.set_armed(false);
    }

    pub(crate) fn begin_sync(&self, changed: &mut Vec<NodeId>) -> LayoutSync {
        let continuous = self.armed.get();
        changed.clear();
        std::mem::swap(&mut *self.pending.borrow_mut(), changed);
        self.sync.set(self.sync.get().wrapping_add(1));
        self.set_armed(true);
        LayoutSync {
            epoch: self.epoch.get(),
            continuous,
        }
    }
}

impl Drop for SemanticsLayoutLog {
    fn drop(&mut self) {
        self.set_armed(false);
    }
}

pub(crate) fn record_layout_change(node: NodeId, stamp: &mut u64) {
    if ARMED_LOGS.try_with(Cell::get).unwrap_or(0) == 0 {
        return;
    }
    crate::render_state::with_current_semantics_layout_log(|log| log.record(node, stamp));
}

#[cfg(test)]
#[path = "tests/semantics_layout_log_tests.rs"]
mod tests;