use std::{
cell::{Cell, RefCell},
hash::{Hash, Hasher},
rc::Rc,
};
use cranpose_core::PackedU64;
use cranpose_ui_layout::Constraints;
use super::layout_node::{DescendantDirt, LayoutDirt};
use crate::layout::MeasuredNode;
#[derive(Clone)]
struct MeasurementCacheEntry {
constraints: Constraints,
measured: Rc<MeasuredNode>,
hold: Option<cranpose_ui_layout::ConstraintsHold>,
ignores_child_sizes: bool,
}
#[derive(Clone, Copy, Debug)]
pub enum IntrinsicKind {
MinWidth(f32),
MaxWidth(f32),
MinHeight(f32),
MaxHeight(f32),
}
impl IntrinsicKind {
fn discriminant(&self) -> u8 {
match self {
Self::MinWidth(_) => 0,
Self::MaxWidth(_) => 1,
Self::MinHeight(_) => 2,
Self::MaxHeight(_) => 3,
}
}
fn value_bits(&self) -> u32 {
match self {
Self::MinWidth(value)
| Self::MaxWidth(value)
| Self::MinHeight(value)
| Self::MaxHeight(value) => value.to_bits(),
}
}
}
impl PartialEq for IntrinsicKind {
fn eq(&self, other: &Self) -> bool {
self.discriminant() == other.discriminant() && self.value_bits() == other.value_bits()
}
}
impl Eq for IntrinsicKind {}
impl Hash for IntrinsicKind {
fn hash<H: Hasher>(&self, state: &mut H) {
self.discriminant().hash(state);
self.value_bits().hash(state);
}
}
#[derive(Default)]
struct NodeCacheState {
epoch: PackedU64,
measurement: Option<MeasurementCacheEntry>,
spare: Option<Rc<MeasuredNode>>,
intrinsics: Vec<(IntrinsicKind, f32)>,
intrinsics_read: bool,
}
impl NodeCacheState {
fn retire_measurement(&mut self) {
if let Some(entry) = self.measurement.take() {
self.spare = Some(entry.measured);
}
}
}
#[derive(Default)]
pub(crate) struct NodeCache {
cache: RefCell<NodeCacheState>,
dirt: Cell<LayoutDirt>,
}
impl NodeCache {
pub(crate) fn dirt(&self) -> LayoutDirt {
self.dirt.get()
}
pub(super) fn set_dirt(&self, dirt: LayoutDirt) {
self.dirt.set(dirt);
}
fn update_dirt(&self, update: impl FnOnce(LayoutDirt) -> LayoutDirt) {
self.set_dirt(update(self.dirt()));
}
pub(crate) fn mark_needs_measure(&self) {
self.update_dirt(|dirt| LayoutDirt {
measure: true,
layout: true,
..dirt
});
}
pub(crate) fn mark_needs_layout(&self) {
self.update_dirt(|dirt| LayoutDirt {
layout: true,
..dirt
});
}
pub(crate) fn clear_needs_measure(&self) {
self.update_dirt(|dirt| LayoutDirt {
measure: false,
..dirt
});
}
pub(crate) fn mark_descendant_needs_layout(&self, measure: bool) {
self.update_dirt(|dirt| LayoutDirt {
descendant: dirt.descendant.marked(measure),
..dirt
});
if measure {
self.forget_intrinsics();
}
}
pub(crate) fn take_layout_dirt(&self) -> LayoutDirt {
let dirt = self.dirt();
self.clear_layout_dirt();
dirt
}
pub(crate) fn restore_dirt(&self, dirt: LayoutDirt) {
self.set_dirt(dirt);
}
pub(crate) fn clear_layout_dirt(&self) {
self.set_dirt(LayoutDirt {
layout: false,
descendant: DescendantDirt::default(),
..self.dirt()
});
}
pub(crate) fn clear(&self) {
let mut state = self.cache.borrow_mut();
state.retire_measurement();
state.intrinsics.clear();
state.intrinsics_read = false;
state.epoch = PackedU64::default();
}
pub(crate) fn activate(&self, epoch: u64) {
let mut state = self.cache.borrow_mut();
if state.epoch.get() != epoch {
state.retire_measurement();
state.intrinsics.clear();
state.intrinsics_read = false;
state.epoch = PackedU64::new(epoch);
}
}
pub(crate) fn held_measurement(&self) -> Option<Rc<MeasuredNode>> {
let state = self.cache.borrow();
state
.measurement
.as_ref()
.map(|entry| &entry.measured)
.or(state.spare.as_ref())
.cloned()
}
pub(crate) fn take_previous_measurement(&self) -> Option<Rc<MeasuredNode>> {
let mut state = self.cache.borrow_mut();
state
.spare
.take()
.or_else(|| state.measurement.take().map(|entry| entry.measured))
}
pub(crate) fn forget_intrinsics(&self) {
self.cache.borrow_mut().intrinsics.clear();
}
pub(crate) fn epoch(&self) -> u64 {
self.cache.borrow().epoch.get()
}
pub(crate) fn get_measurement(&self, constraints: Constraints) -> Option<Rc<MeasuredNode>> {
let state = self.cache.borrow();
state
.measurement
.as_ref()
.filter(|entry| {
entry.constraints == constraints
|| entry.hold.is_some_and(|hold| hold.contains(constraints))
})
.map(|entry| Rc::clone(&entry.measured))
}
pub(crate) fn measured_constraints(&self) -> Option<Constraints> {
self.cache
.borrow()
.measurement
.as_ref()
.map(|entry| entry.constraints)
}
pub(crate) fn has_intrinsics(&self) -> bool {
self.cache.borrow().intrinsics_read
}
pub(crate) fn hold_of(
&self,
measured: &Rc<MeasuredNode>,
) -> Option<cranpose_ui_layout::ConstraintsHold> {
self.cache
.borrow()
.measurement
.as_ref()
.filter(|entry| Rc::ptr_eq(&entry.measured, measured))
.and_then(|entry| entry.hold)
}
pub(crate) fn store_measurement(&self, constraints: Constraints, measured: Rc<MeasuredNode>) {
let mut state = self.cache.borrow_mut();
let (hold, ignores_child_sizes) = state
.measurement
.as_ref()
.filter(|entry| Rc::ptr_eq(&entry.measured, &measured))
.map_or((None, false), |entry| {
(entry.hold, entry.ignores_child_sizes)
});
state.measurement = Some(MeasurementCacheEntry {
constraints,
measured,
hold,
ignores_child_sizes,
});
}
pub(crate) fn store_held_measurement(
&self,
constraints: Constraints,
measured: Rc<MeasuredNode>,
hold: Option<cranpose_ui_layout::ConstraintsHold>,
ignores_child_sizes: bool,
) {
self.cache.borrow_mut().measurement = Some(MeasurementCacheEntry {
constraints,
measured,
hold,
ignores_child_sizes,
});
}
pub(crate) fn store_resized_measurement(
&self,
constraints: Constraints,
measured: Rc<MeasuredNode>,
) {
self.store_held_measurement(constraints, measured, None, true);
}
pub(crate) fn read_measurement<R>(
&self,
read: impl FnOnce(&MeasuredNode, bool) -> R,
) -> Option<R> {
let state = self.cache.borrow();
state
.measurement
.as_ref()
.map(|entry| read(&entry.measured, entry.ignores_child_sizes))
}
pub(crate) fn ignores_child_sizes_of(&self, measured: &Rc<MeasuredNode>) -> bool {
self.cache
.borrow()
.measurement
.as_ref()
.is_some_and(|entry| entry.ignores_child_sizes && Rc::ptr_eq(&entry.measured, measured))
}
pub(crate) fn get_intrinsic(&self, kind: &IntrinsicKind) -> Option<f32> {
let state = self.cache.borrow();
state
.intrinsics
.iter()
.find(|(stored_kind, _)| stored_kind == kind)
.map(|(_, value)| *value)
}
pub(crate) fn store_intrinsic(&self, kind: IntrinsicKind, value: f32) {
let mut state = self.cache.borrow_mut();
state.intrinsics_read = true;
if let Some((_, existing)) = state
.intrinsics
.iter_mut()
.find(|(stored_kind, _)| stored_kind == &kind)
{
*existing = value;
} else {
state.intrinsics.push((kind, value));
}
}
}