use std::{
cell::{Cell, OnceCell, RefCell},
num::NonZeroU32,
rc::{Rc, Weak},
};
use cranpose_core::{
NodeId,
collections::{FixedHashMap, fixed_hash_map},
};
use cranpose_ui_graphics::{DrawTextMeasurer, VectorPath};
use cranpose_ui_layout::MeasurePolicy;
use crate::{
announce::Announcement,
environment_locals::EnvironmentLocals,
layout::policies::SpareFlexChildren,
modifier::{Modifier, ModifierNodeSlices},
render_state::{AppContext, AppContextId},
selection_container::RegistrarInner,
text::{draw_scope_text::DrawnTextStyles, shared_plain_text::SharedPlainText},
text_modifier_node::RecentTextStyles,
unhandled_keys::KeyHandler,
};
type IconPaths = Vec<(&'static str, Option<Rc<VectorPath>>)>;
pub(crate) struct ThreadCells {
pub(crate) current_draw_node: Cell<Option<NodeId>>,
pub(crate) next_app_context_id: Cell<NonZeroU32>,
pub(crate) keyboard_focus_visible: Cell<bool>,
pub(crate) clean_slot_skips: Cell<u64>,
pub(crate) retained_measures: Cell<u64>,
pub(crate) moved_scene_node_takes: Cell<u64>,
pub(crate) last_modifier_revision: Cell<u64>,
pub(crate) modal_counting_contexts: Cell<usize>,
pub(crate) z_index_nodes: Cell<usize>,
pub(crate) z_order_reasons: Cell<usize>,
pub(crate) last_unhandled_key_handler_id: Cell<u64>,
pub(crate) idle_prefetch_passes: Cell<bool>,
}
const _: () = assert!(!std::mem::needs_drop::<ThreadCells>());
impl ThreadCells {
const fn new() -> Self {
Self {
current_draw_node: Cell::new(None),
next_app_context_id: Cell::new(NonZeroU32::MIN),
keyboard_focus_visible: Cell::new(false),
clean_slot_skips: Cell::new(0),
retained_measures: Cell::new(0),
moved_scene_node_takes: Cell::new(0),
last_modifier_revision: Cell::new(0),
modal_counting_contexts: Cell::new(0),
z_index_nodes: Cell::new(0),
z_order_reasons: Cell::new(0),
last_unhandled_key_handler_id: Cell::new(0),
idle_prefetch_passes: Cell::new(false),
}
}
}
pub(crate) struct UiThreadState {
pub(crate) environment: EnvironmentLocals,
pub(crate) left_in_focus_move:
RefCell<Option<Vec<Rc<crate::focus_within::FocusWithinObserver>>>>,
pub(crate) app_context_stack: RefCell<Vec<Weak<AppContext>>>,
pub(crate) app_contexts: RefCell<FixedHashMap<AppContextId, Weak<AppContext>>>,
pub(crate) active_selection: RefCell<Weak<RegistrarInner>>,
pub(crate) recent_text_styles: RefCell<RecentTextStyles>,
pub(crate) drawn_text_styles: RefCell<DrawnTextStyles>,
pub(crate) shared_plain_texts: RefCell<cranpose_ui_graphics::ContentPool<SharedPlainText>>,
pub(crate) icon_paths: RefCell<IconPaths>,
pub(crate) pending_announcements: RefCell<Vec<Announcement>>,
pub(crate) unhandled_key_handlers: RefCell<Vec<(u64, KeyHandler)>>,
pub(crate) fill_width: OnceCell<Modifier>,
pub(crate) fill_height: OnceCell<Modifier>,
pub(crate) fill_size: OnceCell<Modifier>,
pub(crate) clip_to_bounds: OnceCell<Modifier>,
pub(crate) empty_slices: OnceCell<Rc<ModifierNodeSlices>>,
pub(crate) spare_slices: Cell<Option<ModifierNodeSlices>>,
pub(crate) empty_measure_policy: OnceCell<Rc<dyn MeasurePolicy>>,
pub(crate) draw_text_measurer: OnceCell<Rc<dyn DrawTextMeasurer>>,
pub(crate) spare_flex_children: SpareFlexChildren,
}
impl UiThreadState {
const fn new() -> Self {
Self {
environment: EnvironmentLocals::new(),
left_in_focus_move: RefCell::new(None),
app_context_stack: RefCell::new(Vec::new()),
app_contexts: RefCell::new(fixed_hash_map()),
active_selection: RefCell::new(Weak::new()),
recent_text_styles: RefCell::new(RecentTextStyles::new()),
drawn_text_styles: RefCell::new(DrawnTextStyles::new()),
shared_plain_texts: RefCell::new(cranpose_ui_graphics::ContentPool::new()),
icon_paths: RefCell::new(Vec::new()),
pending_announcements: RefCell::new(Vec::new()),
unhandled_key_handlers: RefCell::new(Vec::new()),
fill_width: OnceCell::new(),
fill_height: OnceCell::new(),
fill_size: OnceCell::new(),
clip_to_bounds: OnceCell::new(),
empty_slices: OnceCell::new(),
spare_slices: Cell::new(None),
empty_measure_policy: OnceCell::new(),
draw_text_measurer: OnceCell::new(),
spare_flex_children: SpareFlexChildren::new(),
}
}
}
thread_local! {
static CELLS: ThreadCells = const { ThreadCells::new() };
static STATE: UiThreadState = const { UiThreadState::new() };
}
pub(crate) fn cells<R>(f: impl FnOnce(&ThreadCells) -> R) -> R {
CELLS.with(f)
}
pub(crate) fn with<R>(f: impl FnOnce(&UiThreadState) -> R) -> R {
cranpose_core::with_or_fresh(&STATE, UiThreadState::new, f)
}
pub(crate) fn try_with<R>(f: impl FnOnce(&UiThreadState) -> R) -> Option<R> {
STATE.try_with(f).ok()
}
pub(crate) fn with_environment<R>(f: impl FnOnce(&EnvironmentLocals) -> R) -> R {
with(|state| f(&state.environment))
}