use super::effects::Executor;
use crate::drawing::DrawList;
use crate::schema::WidgetKind;
use crate::spans::TextSpans;
use crate::writer::{AttrValue, NodeWriter};
use std::any::Any;
use std::cell::RefCell;
use std::collections::HashMap;
use std::future::Future;
use std::pin::Pin;
use std::rc::Rc;
use std::sync::{Arc, Mutex};
pub(crate) type GroupId = u32;
const TAKEN: GroupId = u32::MAX;
pub(crate) const ROOT: GroupId = 0;
pub(crate) struct Group {
pub(crate) key: u64,
pub(crate) parent: GroupId,
pub(crate) depth: u32,
pub(crate) generation: u32,
pub(crate) alive: bool,
pub(crate) slots: Vec<Box<dyn Any>>,
pub(crate) children: Vec<GroupId>,
pub(crate) node: Option<u32>,
pub(crate) scope: Option<Box<Scope>>,
}
pub(crate) struct Scope {
pub(crate) params: Vec<Option<Box<dyn Any>>>,
pub(crate) restart: Option<Rc<dyn Fn()>>,
pub(crate) invalid: bool,
pub(crate) params_changed: bool,
pub(crate) reads: Vec<u64>,
pub(crate) previous_reads: Vec<u64>,
}
impl Scope {
fn new() -> Self {
Self {
params: Vec::new(),
restart: None,
invalid: false,
params_changed: false,
reads: Vec::new(),
previous_reads: Vec::new(),
}
}
}
struct Frame {
group: GroupId,
old: Vec<GroupId>,
cursor: usize,
new: Vec<GroupId>,
slot: usize,
seen: Vec<(u64, u32)>,
forced: Option<GroupId>,
standin: bool,
scope: bool,
skipped: bool,
}
#[derive(Clone, Copy, Debug)]
pub(crate) struct NodeContext {
pub(crate) parent: u32,
pub(crate) last: Option<u32>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum Observer {
Scope(GroupId, u32),
Derived(u64),
}
#[derive(Clone, Debug, PartialEq)]
pub(crate) enum OwnedAttr {
None,
Text(String),
Float(f64),
Int(i64),
Bool(bool),
Draw(DrawList),
Spans(TextSpans),
Bytes(Vec<u8>),
}
impl OwnedAttr {
pub(crate) fn as_value(&self) -> AttrValue<'_> {
match self {
Self::None => AttrValue::None,
Self::Text(text) => AttrValue::Text(text),
Self::Float(value) => AttrValue::Float(*value),
Self::Int(value) => AttrValue::Int(*value),
Self::Bool(value) => AttrValue::Bool(*value),
Self::Draw(list) => AttrValue::Bytes(list.as_bytes()),
Self::Spans(spans) => AttrValue::Bytes(spans.as_bytes()),
Self::Bytes(bytes) => AttrValue::Bytes(bytes),
}
}
fn differs(&self, next: &Self) -> bool {
match (self, next) {
(Self::Float(left), Self::Float(right)) => left != right,
_ => self != next,
}
}
}
#[derive(Clone)]
pub(crate) enum EventCallback {
Unit(Rc<RefCell<dyn FnMut()>>),
Text(Rc<RefCell<dyn FnMut(String)>>),
Key(Rc<RefCell<dyn FnMut(crate::KeyEvent)>>),
Value(Rc<RefCell<dyn FnMut(f64)>>),
Range(Rc<RefCell<dyn FnMut(crate::RangeRequest)>>),
Files(Rc<RefCell<dyn FnMut(crate::FileDrop)>>),
Pointer(Rc<RefCell<dyn FnMut(crate::PointerEvent)>>),
}
pub(crate) struct HandlerEntry {
pub(crate) node: u32,
pub(crate) callback: EventCallback,
}
pub(crate) struct NodeState {
pub(crate) node: u32,
pub(crate) attrs: Vec<(&'static str, OwnedAttr)>,
pub(crate) handlers: Vec<(&'static str, u64)>,
}
pub(crate) enum Effect {
Run(Box<dyn FnOnce()>),
Launch {
handle: Rc<super::effects::LaunchHandle>,
start: Box<dyn FnOnce() -> Pin<Box<dyn Future<Output = ()>>>>,
},
}
#[derive(Default)]
pub(crate) struct RuntimeSignal {
pub(crate) dirty: Mutex<Vec<u64>>,
pub(crate) woken: Mutex<Vec<usize>>,
}
impl RuntimeSignal {
pub(crate) fn push_dirty(&self, id: u64) {
self.dirty
.lock()
.unwrap_or_else(|poison| poison.into_inner())
.push(id);
}
pub(crate) fn push_woken(&self, id: usize) {
self.woken
.lock()
.unwrap_or_else(|poison| poison.into_inner())
.push(id);
}
}
pub(crate) struct Composer {
pub(crate) groups: Vec<Group>,
free: Vec<GroupId>,
frames: Vec<Frame>,
pub(crate) scope_stack: Vec<GroupId>,
pub(crate) node_stack: Vec<NodeContext>,
pub(crate) derived_stack: Vec<u64>,
pub(crate) writer: NodeWriter,
pub(crate) handlers: HashMap<u64, HandlerEntry>,
pub(crate) observers: HashMap<u64, Vec<Observer>>,
pub(crate) derived_deps: HashMap<u64, Vec<u64>>,
pub(crate) invalid: Vec<GroupId>,
pub(crate) signal: Arc<RuntimeSignal>,
pub(crate) disposals: Vec<Box<dyn FnOnce()>>,
pub(crate) effects: Vec<Effect>,
pub(crate) graveyard: Vec<Box<dyn Any>>,
pub(crate) executor: Executor,
pub(crate) ambient: super::state::Ambient,
vec_pool: Vec<Vec<GroupId>>,
attr_pool: Vec<Vec<(&'static str, OwnedAttr)>>,
seen_pool: Vec<Vec<(u64, u32)>>,
pub(crate) dirty_scratch: Vec<u64>,
}
impl Composer {
pub(crate) fn new() -> Self {
let root = Group {
key: 0,
parent: ROOT,
depth: 0,
generation: 0,
alive: true,
slots: Vec::new(),
children: Vec::new(),
node: None,
scope: Some(Box::new(Scope::new())),
};
Self {
groups: vec![root],
free: Vec::new(),
frames: Vec::with_capacity(64),
scope_stack: Vec::with_capacity(32),
node_stack: Vec::with_capacity(32),
derived_stack: Vec::new(),
writer: NodeWriter::new(),
handlers: HashMap::with_capacity(64),
observers: HashMap::with_capacity(64),
derived_deps: HashMap::new(),
invalid: Vec::with_capacity(16),
signal: Arc::new(RuntimeSignal::default()),
disposals: Vec::new(),
effects: Vec::new(),
graveyard: Vec::new(),
executor: Executor::default(),
ambient: super::state::Ambient::new(),
vec_pool: Vec::new(),
attr_pool: Vec::new(),
seen_pool: Vec::new(),
dirty_scratch: Vec::new(),
}
}
pub(crate) fn composing(&self) -> bool {
!self.frames.is_empty()
}
fn take_vec(&mut self) -> Vec<GroupId> {
self.vec_pool.pop().unwrap_or_default()
}
fn give_vec(&mut self, mut vec: Vec<GroupId>) {
vec.clear();
self.vec_pool.push(vec);
}
pub(crate) fn take_attrs(&mut self) -> Vec<(&'static str, OwnedAttr)> {
self.attr_pool.pop().unwrap_or_default()
}
fn allocate_group(&mut self, key: u64, parent: GroupId) -> GroupId {
let depth = self.groups[parent as usize].depth + 1;
if let Some(id) = self.free.pop() {
let group = &mut self.groups[id as usize];
group.key = key;
group.parent = parent;
group.depth = depth;
group.generation = group.generation.wrapping_add(1);
group.alive = true;
group.node = None;
group.scope = None;
return id;
}
let id = self.groups.len() as GroupId;
self.groups.push(Group {
key,
parent,
depth,
generation: 0,
alive: true,
slots: Vec::new(),
children: Vec::new(),
node: None,
scope: None,
});
id
}
pub(crate) fn begin_root(&mut self) {
let old = std::mem::take(&mut self.groups[ROOT as usize].children);
let new = self.take_vec();
let seen = self.seen_pool.pop().unwrap_or_default();
self.frames.push(Frame {
group: ROOT,
old,
cursor: 0,
new,
slot: 0,
seen,
forced: None,
standin: false,
scope: true,
skipped: false,
});
self.begin_scope_reads(ROOT);
self.scope_stack.push(ROOT);
self.node_stack.push(NodeContext {
parent: crate::writer::PLACEHOLDER_NODE,
last: None,
});
}
pub(crate) fn end_root(&mut self) {
self.node_stack.pop();
self.end_group();
}
pub(crate) fn start_group(&mut self, key: u64) -> GroupId {
let frame = self
.frames
.last_mut()
.expect("a group is always inside the root");
if let Some(forced) = frame.forced.take() {
return self.push_existing(forced);
}
let occurrence = match frame.seen.iter_mut().find(|(seen, _)| *seen == key) {
Some((_, count)) => {
*count += 1;
*count
}
None => {
frame.seen.push((key, 0));
0
}
};
let identity = if occurrence == 0 {
key
} else {
mix(key, u64::from(occurrence))
};
let parent = frame.group;
let mut matched = None;
if let Some(&candidate) = frame.old.get(frame.cursor) {
if candidate != TAKEN && self.groups[candidate as usize].key == identity {
matched = Some(frame.cursor);
}
}
if matched.is_none() {
matched = frame.old.iter().position(|&candidate| {
candidate != TAKEN && self.groups[candidate as usize].key == identity
});
}
let group = match matched {
Some(position) => {
let group = frame.old[position];
frame.old[position] = TAKEN;
frame.cursor = position + 1;
group
}
None => self.allocate_group(identity, parent),
};
self.push_existing(group)
}
fn push_existing(&mut self, group: GroupId) -> GroupId {
let old = std::mem::take(&mut self.groups[group as usize].children);
let new = self.take_vec();
let seen = self.seen_pool.pop().unwrap_or_default();
self.frames.push(Frame {
group,
old,
cursor: 0,
new,
slot: 0,
seen,
forced: None,
standin: false,
scope: false,
skipped: false,
});
group
}
pub(crate) fn end_group(&mut self) {
let Some(mut frame) = self.frames.pop() else {
return;
};
let mut seen = std::mem::take(&mut frame.seen);
seen.clear();
self.seen_pool.push(seen);
if frame.standin {
self.give_vec(frame.old);
self.give_vec(frame.new);
return;
}
let group = frame.group;
if frame.scope {
self.end_scope_reads(group, frame.skipped);
if self.scope_stack.last() == Some(&group) {
self.scope_stack.pop();
}
}
for index in 0..frame.old.len() {
let child = frame.old[index];
if child != TAKEN {
self.dispose_group(child, true);
}
}
let slots = &mut self.groups[group as usize].slots;
if frame.slot < slots.len() {
let gone: Vec<Box<dyn Any>> = slots.drain(frame.slot..).collect();
for slot in gone {
self.forget_slot(slot);
}
}
let new = std::mem::take(&mut frame.new);
let old_buffer = std::mem::take(&mut frame.old);
self.groups[group as usize].children = new;
self.give_vec(old_buffer);
if let Some(parent) = self.frames.last_mut() {
if !parent.standin {
parent.new.push(group);
}
}
}
pub(crate) fn skip_to_group_end(&mut self) {
let Some(frame) = self.frames.last_mut() else {
return;
};
frame.skipped = true;
let group = frame.group;
let old = std::mem::take(&mut frame.old);
for child in old.iter().copied() {
if child != TAKEN {
frame.new.push(child);
}
}
frame.old = old;
frame.old.clear();
frame.slot = self.groups[group as usize].slots.len();
let mut nodes = Vec::new();
self.top_nodes(group, &mut nodes);
for node in nodes {
self.place(node);
}
}
fn top_nodes(&self, group: GroupId, into: &mut Vec<u32>) {
let data = &self.groups[group as usize];
if let Some(node) = data.node {
into.push(node);
return;
}
for &child in &data.children {
self.top_nodes(child, into);
}
}
pub(crate) fn remembered<T: Clone + 'static>(&mut self) -> Option<T> {
let frame = self.frames.last_mut()?;
let group = frame.group;
let index = frame.slot;
let slot = self.groups[group as usize].slots.get(index)?;
let value = slot.downcast_ref::<T>()?.clone();
self.frames.last_mut()?.slot += 1;
Some(value)
}
pub(crate) fn remembered_if<T: Clone + 'static>(
&mut self,
accept: impl FnOnce(&T) -> bool,
) -> Option<T> {
let frame = self.frames.last()?;
let group = frame.group;
let index = frame.slot;
let value = self.groups[group as usize]
.slots
.get(index)?
.downcast_ref::<T>()?;
if !accept(value) {
return None;
}
let value = value.clone();
self.frames.last_mut()?.slot += 1;
Some(value)
}
pub(crate) fn remember_new<T: 'static>(&mut self, value: T) {
let Some(frame) = self.frames.last_mut() else {
return;
};
let group = frame.group;
let index = frame.slot;
frame.slot += 1;
let slots = &mut self.groups[group as usize].slots;
if index < slots.len() {
let old = std::mem::replace(&mut slots[index], Box::new(value));
self.forget_slot(old);
} else {
slots.push(Box::new(value));
}
}
pub(crate) fn slot_mut<T: 'static>(&mut self) -> Option<&mut T> {
let frame = self.frames.last_mut()?;
let group = frame.group;
let index = frame.slot;
let has = self.groups[group as usize]
.slots
.get(index)
.is_some_and(|slot| slot.is::<T>());
if !has {
return None;
}
self.frames.last_mut()?.slot += 1;
self.groups[group as usize].slots[index].downcast_mut::<T>()
}
pub(crate) fn peek_slot_mut<T: 'static>(&mut self) -> Option<&mut T> {
let frame = self.frames.last()?;
let group = frame.group;
let index = frame.slot;
self.groups[group as usize]
.slots
.get_mut(index)?
.downcast_mut::<T>()
}
fn forget_slot(&mut self, mut slot: Box<dyn Any>) {
if let Some(state) = slot.downcast_mut::<NodeState>() {
for (_, handler) in state.handlers.drain(..) {
if let Some(entry) = self.handlers.remove(&handler) {
self.graveyard.push(Box::new(entry.callback));
}
}
let attrs = std::mem::take(&mut state.attrs);
self.graveyard.push(Box::new(attrs));
} else if let Some(effect) = slot.downcast_mut::<super::effects::EffectSlot>() {
effect.dispose(self);
}
self.graveyard.push(slot);
}
fn dispose_group(&mut self, group: GroupId, top: bool) {
let mut top = top;
if let Some(node) = self.groups[group as usize].node {
if top {
self.writer.remove_node(node);
}
top = false;
}
let children = std::mem::take(&mut self.groups[group as usize].children);
for &child in &children {
self.dispose_group(child, top);
}
self.give_vec(children);
let slots = std::mem::take(&mut self.groups[group as usize].slots);
for slot in slots.into_iter().rev() {
self.forget_slot(slot);
}
if let Some(scope) = self.groups[group as usize].scope.take() {
let generation = self.groups[group as usize].generation;
for read in scope.reads.iter().chain(scope.previous_reads.iter()) {
self.unobserve(*read, Observer::Scope(group, generation));
}
self.graveyard.push(scope);
}
let data = &mut self.groups[group as usize];
data.alive = false;
data.node = None;
data.generation = data.generation.wrapping_add(1);
self.free.push(group);
}
pub(crate) fn start_restart_group(&mut self, key: u64) -> GroupId {
let group = self.start_group(key);
if self.groups[group as usize].scope.is_none() {
self.groups[group as usize].scope = Some(Box::new(Scope::new()));
}
if let Some(frame) = self.frames.last_mut() {
frame.scope = true;
}
self.begin_scope_reads(group);
self.scope_stack.push(group);
group
}
fn begin_scope_reads(&mut self, group: GroupId) {
if let Some(scope) = self.groups[group as usize].scope.as_mut() {
std::mem::swap(&mut scope.reads, &mut scope.previous_reads);
scope.reads.clear();
scope.params_changed = false;
}
}
fn end_scope_reads(&mut self, group: GroupId, skipped: bool) {
let generation = self.groups[group as usize].generation;
let Some(scope) = self.groups[group as usize].scope.as_mut() else {
return;
};
if skipped {
std::mem::swap(&mut scope.reads, &mut scope.previous_reads);
scope.previous_reads.clear();
return;
}
scope.invalid = false;
let previous = std::mem::take(&mut scope.previous_reads);
let stale: Vec<u64> = previous
.iter()
.copied()
.filter(|read| !scope.reads.contains(read))
.collect();
let mut previous = previous;
previous.clear();
if let Some(scope) = self.groups[group as usize].scope.as_mut() {
scope.previous_reads = previous;
}
for read in stale {
self.unobserve(read, Observer::Scope(group, generation));
}
}
pub(crate) fn must_run(&self, group: GroupId) -> bool {
match self.groups[group as usize].scope.as_ref() {
Some(scope) => scope.invalid || scope.restart.is_none(),
None => true,
}
}
pub(crate) fn param_changed<T: PartialEq + Clone + 'static>(
&mut self,
group: GroupId,
index: usize,
value: &T,
) -> bool {
let Some(scope) = self.groups[group as usize].scope.as_mut() else {
return true;
};
if scope.params.len() <= index {
scope.params.resize_with(index + 1, || None);
}
let changed = match scope.params[index]
.as_mut()
.and_then(|stored| stored.downcast_mut::<T>())
{
Some(stored) => {
if stored == value {
false
} else {
*stored = value.clone();
true
}
}
None => {
scope.params[index] = Some(Box::new(value.clone()));
true
}
};
if changed {
scope.params_changed = true;
}
changed
}
pub(crate) fn wants_restart(&self, group: GroupId) -> bool {
self.groups[group as usize]
.scope
.as_ref()
.is_some_and(|scope| scope.restart.is_none() || scope.params_changed)
}
pub(crate) fn set_restart(&mut self, group: GroupId, restart: Rc<dyn Fn()>) {
if let Some(scope) = self.groups[group as usize].scope.as_mut() {
if let Some(old) = scope.restart.replace(restart) {
self.graveyard.push(Box::new(old));
}
}
}
pub(crate) fn invalidate(&mut self, group: GroupId, generation: u32) {
let Some(data) = self.groups.get_mut(group as usize) else {
return;
};
if !data.alive || data.generation != generation {
return;
}
let restartable = group == ROOT
|| data
.scope
.as_ref()
.is_some_and(|scope| scope.restart.is_some());
if !restartable {
let mut ancestor = data.parent;
loop {
let candidate = &self.groups[ancestor as usize];
if ancestor == ROOT
|| candidate
.scope
.as_ref()
.is_some_and(|scope| scope.restart.is_some())
{
let generation = candidate.generation;
return self.invalidate(ancestor, generation);
}
ancestor = candidate.parent;
}
}
if let Some(scope) = data.scope.as_mut() {
if !scope.invalid {
scope.invalid = true;
self.invalid.push(group);
}
}
}
pub(crate) fn take_invalid(&mut self) -> Vec<GroupId> {
let mut invalid = std::mem::take(&mut self.invalid);
invalid.sort_by_key(|group| (self.groups[*group as usize].depth, *group));
invalid.dedup();
invalid
}
pub(crate) fn prepare_restart(&mut self, group: GroupId) -> Option<Rc<dyn Fn()>> {
if group == ROOT {
return None;
}
let data = &self.groups[group as usize];
if !data.alive {
return None;
}
let parent = data.parent;
let scope = data.scope.as_ref()?;
if !scope.invalid {
return None;
}
let restart = scope.restart.clone()?;
let context = self.context_before(group);
let new = self.take_vec();
let old = self.take_vec();
let seen = self.seen_pool.pop().unwrap_or_default();
self.frames.push(Frame {
group: parent,
old,
cursor: 0,
new,
slot: 0,
seen,
forced: Some(group),
standin: true,
scope: false,
skipped: false,
});
self.node_stack.push(context);
Some(restart)
}
pub(crate) fn root_invalid(&self) -> bool {
self.groups[ROOT as usize]
.scope
.as_ref()
.is_some_and(|scope| scope.invalid)
}
pub(crate) fn finish_restart(&mut self) {
self.node_stack.pop();
self.end_group();
}
fn context_before(&self, group: GroupId) -> NodeContext {
let mut current = group;
loop {
if current == ROOT {
return NodeContext {
parent: crate::writer::PLACEHOLDER_NODE,
last: None,
};
}
let parent = self.groups[current as usize].parent;
let siblings = &self.groups[parent as usize].children;
if let Some(position) = siblings.iter().position(|&child| child == current) {
for &sibling in siblings[..position].iter().rev() {
if let Some(node) = self.last_top_node(sibling) {
return NodeContext {
parent: self.container_of(parent),
last: Some(node),
};
}
}
}
if self.groups[parent as usize].node.is_some() || parent == ROOT {
return NodeContext {
parent: self.container_of(parent),
last: None,
};
}
current = parent;
}
}
fn container_of(&self, group: GroupId) -> u32 {
let mut current = group;
loop {
if let Some(node) = self.groups[current as usize].node {
return node;
}
if current == ROOT {
return crate::writer::PLACEHOLDER_NODE;
}
current = self.groups[current as usize].parent;
}
}
fn last_top_node(&self, group: GroupId) -> Option<u32> {
let data = &self.groups[group as usize];
if let Some(node) = data.node {
return Some(node);
}
data.children
.iter()
.rev()
.find_map(|&child| self.last_top_node(child))
}
pub(crate) fn record_read(&mut self, state: u64) -> bool {
let observer = if let Some(&derived) = self.derived_stack.last() {
let deps = self.derived_deps.entry(derived).or_default();
if !deps.contains(&state) {
deps.push(state);
}
Observer::Derived(derived)
} else if self.composing() {
let Some(group) = self.reading_scope() else {
return false;
};
let generation = self.groups[group as usize].generation;
if let Some(scope) = self.groups[group as usize].scope.as_mut() {
if !scope.reads.contains(&state) {
scope.reads.push(state);
}
}
Observer::Scope(group, generation)
} else {
return false;
};
let fresh = !self.observers.contains_key(&state);
let list = self.observers.entry(state).or_default();
if !list.contains(&observer) {
list.push(observer);
}
fresh
}
fn reading_scope(&self) -> Option<GroupId> {
self.scope_stack.iter().rev().copied().find(|&group| {
group == ROOT
|| self.groups[group as usize]
.scope
.as_ref()
.is_some_and(|scope| scope.restart.is_some())
})
}
pub(crate) fn unobserve(&mut self, state: u64, observer: Observer) {
if let Some(list) = self.observers.get_mut(&state) {
list.retain(|existing| *existing != observer);
}
}
pub(crate) fn place(&mut self, node: u32) {
let Some(context) = self.node_stack.last_mut() else {
return;
};
let (parent, last) = (context.parent, context.last);
context.last = Some(node);
self.writer.place_after(parent, node, last);
}
pub(crate) fn update_node(
&mut self,
widget: WidgetKind,
attrs: Vec<(&'static str, OwnedAttr)>,
listeners: Vec<(&'static str, EventCallback)>,
) -> u32 {
let group = self.frames.last().map_or(ROOT, |frame| frame.group);
let mut attrs = attrs;
let node = if let Some(state) = self.slot_mut::<NodeState>() {
let node = state.node;
let mut previous = std::mem::take(&mut state.attrs);
let mut handlers = std::mem::take(&mut state.handlers);
for (name, _) in &previous {
if !attrs.iter().any(|(new_name, _)| new_name == name) {
self.writer.set_attribute(node, name, &AttrValue::None);
}
}
for (name, value) in &attrs {
let changed = match previous.iter().find(|(old_name, _)| old_name == name) {
Some((_, old)) => old.differs(value),
None => true,
};
if !changed {
continue;
}
if widget == WidgetKind::Text && *name == "text" {
if let (Some((_, OwnedAttr::Text(old))), OwnedAttr::Text(new)) = (
previous.iter().find(|(old_name, _)| *old_name == "text"),
value,
) {
let plain = !attrs
.iter()
.any(|(name, value)| *name == "spans" && *value != OwnedAttr::None);
if plain
&& !old.is_empty()
&& new.len() > old.len()
&& new.starts_with(old.as_str())
{
self.writer
.batch_mut()
.append_text_node(node, &new[old.len()..]);
continue;
}
}
}
self.writer.set_attribute(node, name, &value.as_value());
}
for (name, callback) in listeners {
match handlers.iter().find(|(existing, _)| *existing == name) {
Some(&(_, id)) => {
if let Some(entry) = self.handlers.get_mut(&id) {
let old = std::mem::replace(&mut entry.callback, callback);
self.graveyard.push(Box::new(old));
}
}
None => {
let id = self.writer.allocate_handler_id();
self.writer.set_listener(node, name, id);
self.handlers.insert(id, HandlerEntry { node, callback });
handlers.push((name, id));
}
}
}
previous.clear();
self.attr_pool.push(previous);
let index = self.frames.last().map_or(0, |frame| frame.slot) - 1;
if let Some(state) =
self.groups[group as usize].slots[index].downcast_mut::<NodeState>()
{
std::mem::swap(&mut state.attrs, &mut attrs);
state.handlers = handlers;
}
node
} else {
let node = self.writer.allocate_node(widget);
let mut handlers = Vec::with_capacity(listeners.len());
for (name, value) in &attrs {
self.writer.set_attribute(node, name, &value.as_value());
}
for (name, callback) in listeners {
let id = self.writer.allocate_handler_id();
self.writer.set_listener(node, name, id);
self.handlers.insert(id, HandlerEntry { node, callback });
handlers.push((name, id));
}
self.remember_new(NodeState {
node,
attrs: std::mem::take(&mut attrs),
handlers,
});
node
};
attrs.clear();
self.attr_pool.push(attrs);
self.groups[group as usize].node = Some(node);
self.place(node);
node
}
pub(crate) fn push_node_context(&mut self, parent: u32) {
self.node_stack.push(NodeContext { parent, last: None });
}
pub(crate) fn pop_node_context(&mut self) {
self.node_stack.pop();
}
pub(crate) fn push_effect(&mut self, effect: Effect) {
self.effects.push(effect);
}
pub(crate) fn clear_walk(&mut self) {
self.frames.clear();
self.scope_stack.clear();
self.node_stack.clear();
self.derived_stack.clear();
}
pub(crate) fn current_group(&self) -> Option<GroupId> {
self.frames.last().map(|frame| frame.group)
}
}
pub(crate) fn mix(key: u64, occurrence: u64) -> u64 {
let mut value = key ^ occurrence.wrapping_mul(0x9E37_79B9_7F4A_7C15);
value = (value ^ (value >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
value = (value ^ (value >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
value ^ (value >> 31)
}
pub const fn call_site(path: &str, ordinal: u32) -> u64 {
let bytes = path.as_bytes();
let mut hash = 0xcbf2_9ce4_8422_2325_u64;
let mut index = 0;
while index < bytes.len() {
hash ^= bytes[index] as u64;
hash = hash.wrapping_mul(0x100_0000_01b3);
index += 1;
}
hash ^= ordinal as u64;
hash.wrapping_mul(0x100_0000_01b3)
}