use std::{
cmp::Ordering,
fmt, mem,
ops::{self, ControlFlow},
sync::{
Arc,
atomic::{AtomicBool, Ordering::Relaxed},
},
};
use crate::{
render::{FrameBuilder, FrameUpdate, FrameValueKey},
update::{UPDATES, WidgetUpdates},
widget::{
WIDGET, WidgetUpdateMode,
base::{PARALLEL_VAR, Parallel},
info::{WidgetInfo, WidgetInfoBuilder, WidgetLayout, WidgetMeasure},
},
};
use parking_lot::Mutex;
use task::ParallelIteratorExt;
use zng_app_context::context_local;
use zng_layout::unit::{Factor, PxSize, PxTransform, PxVector};
use zng_state_map::StateId;
use zng_task::{self as task, rayon::prelude::*};
use zng_unique_id::static_id;
use zng_var::{animation::Transitionable, impl_from_and_into_var};
use super::*;
#[macro_export]
macro_rules! ui_vec {
() => { $crate::widget::node::UiVec::new() };
($node:expr; $n:expr) => {
{
let mut n: usize = $n;
let mut vec = $crate::widget::node::UiVec::with_capacity(n);
while n > 0 {
vec.push($node);
n -= 1;
}
vec
}
};
($($nodes:tt)+) => {
$crate::ui_vec_items! {
match { $($nodes)+ }
result { }
}
};
}
#[doc(inline)]
pub use crate::ui_vec;
#[macro_export]
#[doc(hidden)]
macro_rules! ui_vec_items {
(
match { #[$meta:meta] $($tt:tt)* }
result { $($r:tt)* }
) => {
$crate::ui_vec_items! {
match { $($tt)* }
result { $($r)* #[$meta] }
}
};
(
match { $node:expr, $($tt:tt)* }
result { $($r:tt)* }
) => {
$crate::ui_vec_items! {
match { $($tt)* }
result { $($r)* $crate::widget::node::IntoUiNode::into_node($node), }
}
};
(
match { $node:expr }
result { $($r:tt)* }
) => {
$crate::ui_vec_items! {
match { }
result { $($r)* $crate::widget::node::IntoUiNode::into_node($node) }
}
};
(
match { }
result { $($r:tt)* }
) => {
$crate::widget::node::UiVec::from(std::vec![
$($r)*
])
};
}
#[derive(Default)]
pub struct UiVec(Vec<UiNode>);
impl UiVec {
pub fn new() -> Self {
Self::default()
}
pub fn with_capacity(capacity: usize) -> Self {
Self(Vec::with_capacity(capacity))
}
pub fn push(&mut self, node: impl IntoUiNode) {
self.0.push(node.into_node())
}
pub fn insert(&mut self, index: usize, node: impl IntoUiNode) {
self.0.insert(index, node.into_node())
}
pub fn chain(self, other: impl IntoUiNode) -> UiNode {
UiNode::new(self).chain(other)
}
}
impl ops::Deref for UiVec {
type Target = Vec<UiNode>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl ops::DerefMut for UiVec {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl From<Vec<UiNode>> for UiVec {
fn from(vec: Vec<UiNode>) -> Self {
Self(vec)
}
}
impl From<UiVec> for Vec<UiNode> {
fn from(vec: UiVec) -> Self {
vec.0
}
}
impl<U: IntoUiNode> FromIterator<U> for UiVec {
fn from_iter<T: IntoIterator<Item = U>>(iter: T) -> Self {
Self(Vec::from_iter(iter.into_iter().map(IntoUiNode::into_node)))
}
}
impl IntoIterator for UiVec {
type Item = UiNode;
type IntoIter = std::vec::IntoIter<UiNode>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl IntoUiNode for Vec<UiNode> {
fn into_node(self) -> UiNode {
UiNode::new(UiVec(self))
}
}
impl IntoUiNode for Box<[UiNode]> {
fn into_node(self) -> UiNode {
UiNode::new(UiVec(self.into()))
}
}
impl UiNodeImpl for UiVec {
fn children_len(&self) -> usize {
self.len()
}
fn with_child(&mut self, index: usize, visitor: &mut dyn FnMut(&mut UiNode)) {
if index < self.len() {
visitor(&mut self[index])
}
}
fn is_list(&self) -> bool {
true
}
fn for_each_child(&mut self, visitor: &mut dyn FnMut(usize, &mut UiNode)) {
for (i, n) in self.0.iter_mut().enumerate() {
visitor(i, n)
}
}
fn try_for_each_child(
&mut self,
visitor: &mut dyn FnMut(usize, &mut UiNode) -> ControlFlow<BoxAnyVarValue>,
) -> ControlFlow<BoxAnyVarValue> {
for (i, n) in self.0.iter_mut().enumerate() {
visitor(i, n)?;
}
ControlFlow::Continue(())
}
fn par_each_child(&mut self, visitor: &(dyn Fn(usize, &mut UiNode) + Sync)) {
if self.len() >= MIN_PARALLEL {
self.par_iter_mut().enumerate().with_ctx().for_each(|(i, n)| visitor(i, n))
} else {
self.iter_mut().enumerate().for_each(|(i, n)| visitor(i, n))
}
}
fn par_fold_reduce(
&mut self,
identity: BoxAnyVarValue,
fold: &(dyn Fn(BoxAnyVarValue, usize, &mut UiNode) -> BoxAnyVarValue + Sync),
reduce: &(dyn Fn(BoxAnyVarValue, BoxAnyVarValue) -> BoxAnyVarValue + Sync),
) -> BoxAnyVarValue {
self.par_iter_mut()
.enumerate()
.with_ctx()
.fold(|| identity.clone(), move |a, (i, n)| fold(a, i, n))
.reduce(|| identity.clone(), reduce)
}
fn init(&mut self) {
if (self as &mut dyn UiNodeImpl).parallelize_hint() && PARALLEL_VAR.get().contains(Parallel::INIT) {
self.par_iter_mut().with_ctx().for_each(|n| n.init());
} else {
self.iter_mut().for_each(|n| n.init());
}
}
fn deinit(&mut self) {
if (self as &mut dyn UiNodeImpl).parallelize_hint() && PARALLEL_VAR.get().contains(Parallel::DEINIT) {
self.par_iter_mut().with_ctx().for_each(|n| n.deinit());
} else {
self.iter_mut().for_each(|n| n.deinit());
}
}
fn info(&mut self, info: &mut WidgetInfoBuilder) {
if (self as &mut dyn UiNodeImpl).parallelize_hint() && PARALLEL_VAR.get().contains(Parallel::INFO) {
let b = self
.par_iter_mut()
.with_ctx()
.fold(
|| info.parallel_split(),
|mut info, c| {
c.info(&mut info);
info
},
)
.reduce(
|| info.parallel_split(),
|mut a, b| {
a.parallel_fold(b);
a
},
);
info.parallel_fold(b);
} else {
self.iter_mut().for_each(|n| n.info(info));
}
}
fn update(&mut self, updates: &WidgetUpdates) {
if (self as &mut dyn UiNodeImpl).parallelize_hint() && PARALLEL_VAR.get().contains(Parallel::UPDATE) {
self.par_iter_mut().with_ctx().for_each(|n| n.update(updates));
} else {
self.iter_mut().for_each(|n| n.update(updates));
}
}
fn update_list(&mut self, updates: &WidgetUpdates, _: &mut dyn UiNodeListObserver) {
self.update(updates);
}
fn measure(&mut self, wm: &mut WidgetMeasure) -> PxSize {
if (self as &mut dyn UiNodeImpl).parallelize_hint() && PARALLEL_VAR.get().contains(Parallel::LAYOUT) {
let (b, desired_size) = self
.par_iter_mut()
.with_ctx()
.fold(
|| (wm.parallel_split(), PxSize::zero()),
|(mut wm, desired_size), n| {
let n_ds = n.measure(&mut wm);
(wm, desired_size.max(n_ds))
},
)
.reduce(
|| (wm.parallel_split(), PxSize::zero()),
|(mut wm, desired_size), (b_wm, b_ds)| {
wm.parallel_fold(b_wm);
(wm, desired_size.max(b_ds))
},
);
wm.parallel_fold(b);
desired_size
} else {
let mut desired_size = PxSize::zero();
self.iter_mut().for_each(|n| desired_size = desired_size.max(n.measure(wm)));
desired_size
}
}
fn measure_list(
&mut self,
wm: &mut WidgetMeasure,
measure: &(dyn Fn(usize, &mut UiNode, &mut WidgetMeasure) -> PxSize + Sync),
fold_size: &(dyn Fn(PxSize, PxSize) -> PxSize + Sync),
) -> PxSize {
if (self as &mut dyn UiNodeImpl).parallelize_hint() && PARALLEL_VAR.get().contains(Parallel::LAYOUT) {
let (b, desired_size) = self
.par_iter_mut()
.enumerate()
.with_ctx()
.fold(
|| (wm.parallel_split(), PxSize::zero()),
|(mut wm, desired_size), (i, n)| {
let n_ds = measure(i, n, &mut wm);
(wm, fold_size(desired_size, n_ds))
},
)
.reduce(
|| (wm.parallel_split(), PxSize::zero()),
|(mut wm, desired_size), (b_wm, b_ds)| {
wm.parallel_fold(b_wm);
(wm, fold_size(desired_size, b_ds))
},
);
wm.parallel_fold(b);
desired_size
} else {
let mut desired_size = PxSize::zero();
self.iter_mut()
.enumerate()
.for_each(|(i, n)| desired_size = fold_size(desired_size, measure(i, n, wm)));
desired_size
}
}
fn layout(&mut self, wl: &mut WidgetLayout) -> PxSize {
if (self as &mut dyn UiNodeImpl).parallelize_hint() && PARALLEL_VAR.get().contains(Parallel::LAYOUT) {
let (b, final_size) = self
.par_iter_mut()
.with_ctx()
.fold(
|| (wl.parallel_split(), PxSize::zero()),
|(mut wl, final_size), n| {
let n_ds = n.layout(&mut wl);
(wl, final_size.max(n_ds))
},
)
.reduce(
|| (wl.parallel_split(), PxSize::zero()),
|(mut wl, desired_size), (b_wl, b_ds)| {
wl.parallel_fold(b_wl);
(wl, desired_size.max(b_ds))
},
);
wl.parallel_fold(b);
final_size
} else {
let mut final_size = PxSize::zero();
self.iter_mut().for_each(|n| final_size = final_size.max(n.layout(wl)));
final_size
}
}
fn layout_list(
&mut self,
wl: &mut WidgetLayout,
layout: &(dyn Fn(usize, &mut UiNode, &mut WidgetLayout) -> PxSize + Sync),
fold_size: &(dyn Fn(PxSize, PxSize) -> PxSize + Sync),
) -> PxSize {
if (self as &mut dyn UiNodeImpl).parallelize_hint() && PARALLEL_VAR.get().contains(Parallel::LAYOUT) {
let (b, desired_size) = self
.par_iter_mut()
.enumerate()
.with_ctx()
.fold(
|| (wl.parallel_split(), PxSize::zero()),
|(mut wl, desired_size), (i, n)| {
let n_ds = layout(i, n, &mut wl);
(wl, fold_size(desired_size, n_ds))
},
)
.reduce(
|| (wl.parallel_split(), PxSize::zero()),
|(mut wl, desired_size), (b_wm, b_ds)| {
wl.parallel_fold(b_wm);
(wl, fold_size(desired_size, b_ds))
},
);
wl.parallel_fold(b);
desired_size
} else {
let mut desired_size = PxSize::zero();
self.iter_mut()
.enumerate()
.for_each(|(i, n)| desired_size = fold_size(desired_size, layout(i, n, wl)));
desired_size
}
}
fn render(&mut self, frame: &mut FrameBuilder) {
if (self as &mut dyn UiNodeImpl).parallelize_hint() && PARALLEL_VAR.get().contains(Parallel::RENDER) {
let mut par_start = 0;
while frame.is_outer() && par_start < self.len() {
self[par_start].render(frame);
par_start += 1;
}
let b = self[par_start..]
.par_iter_mut()
.with_ctx()
.fold(
|| frame.parallel_split(),
|mut frame, c| {
c.render(&mut frame);
frame
},
)
.reduce(
|| frame.parallel_split(),
|mut a, b| {
a.parallel_fold(b);
a
},
);
frame.parallel_fold(b);
} else {
self.iter_mut().for_each(|n| n.render(frame));
}
}
fn render_list(&mut self, frame: &mut FrameBuilder, render: &(dyn Fn(usize, &mut UiNode, &mut FrameBuilder) + Sync)) {
if (self as &mut dyn UiNodeImpl).parallelize_hint() && PARALLEL_VAR.get().contains(Parallel::RENDER) {
let mut par_start = 0;
while frame.is_outer() && par_start < self.len() {
self[par_start].render(frame);
par_start += 1;
}
let b = self[par_start..]
.par_iter_mut()
.enumerate()
.with_ctx()
.fold(
|| frame.parallel_split(),
|mut frame, (i, c)| {
render(i, c, &mut frame);
frame
},
)
.reduce(
|| frame.parallel_split(),
|mut a, b| {
a.parallel_fold(b);
a
},
);
frame.parallel_fold(b);
} else {
self.iter_mut().enumerate().for_each(|(i, n)| render(i, n, frame));
}
}
fn render_update(&mut self, update: &mut FrameUpdate) {
if (self as &mut dyn UiNodeImpl).parallelize_hint() && PARALLEL_VAR.get().contains(Parallel::RENDER) {
let mut par_start = 0;
while update.is_outer() && par_start < self.len() {
self[par_start].render_update(update);
par_start += 1;
}
let b = self[par_start..]
.par_iter_mut()
.with_ctx()
.fold(
|| update.parallel_split(),
|mut update, c| {
c.render_update(&mut update);
update
},
)
.reduce(
|| update.parallel_split(),
|mut a, b| {
a.parallel_fold(b);
a
},
);
update.parallel_fold(b);
} else {
self.iter_mut().for_each(|n| n.render_update(update));
}
}
fn render_update_list(&mut self, update: &mut FrameUpdate, render_update: &(dyn Fn(usize, &mut UiNode, &mut FrameUpdate) + Sync)) {
if (self as &mut dyn UiNodeImpl).parallelize_hint() && PARALLEL_VAR.get().contains(Parallel::RENDER) {
let mut par_start = 0;
while update.is_outer() && par_start < self.len() {
self[par_start].render_update(update);
par_start += 1;
}
let b = self[par_start..]
.par_iter_mut()
.enumerate()
.with_ctx()
.fold(
|| update.parallel_split(),
|mut update, (i, c)| {
render_update(i, c, &mut update);
update
},
)
.reduce(
|| update.parallel_split(),
|mut a, b| {
a.parallel_fold(b);
a
},
);
update.parallel_fold(b);
} else {
self.iter_mut().enumerate().for_each(|(i, n)| render_update(i, n, update));
}
}
fn as_widget(&mut self) -> Option<&mut dyn WidgetUiNodeImpl> {
None
}
}
impl UiNode {
pub fn chain(self, other: impl IntoUiNode) -> UiNode {
self.chain_impl(other.into_node())
}
fn chain_impl(mut self, mut other: UiNode) -> UiNode {
if self.is_nil() {
return other;
}
if other.is_nil() {
return self;
}
if let Some(chain) = self.downcast_mut::<ChainList>() {
if let Some(other_too) = other.downcast_mut::<ChainList>() {
chain.0.append(&mut other_too.0);
} else {
chain.0.push(other);
}
self
} else {
ChainList(ui_vec![self, other]).into_node()
}
}
pub fn sorting_by(mut self, sort: impl Fn(&mut UiNode, &mut UiNode) -> Ordering + Send + 'static) -> UiNode {
if let Some(already) = self.downcast_mut::<SortingList>() {
already.sort = Box::new(sort);
already.invalidate_sort();
self
} else {
SortingList::new(self, sort).into_node()
}
}
}
pub struct ChainList(pub UiVec);
impl ChainList {
pub fn chain(self, other: impl IntoUiNode) -> UiNode {
self.into_node().chain(other)
}
}
impl UiNodeImpl for ChainList {
fn children_len(&self) -> usize {
let mut len = 0;
for c in self.0.iter() {
if c.is_list() {
len += c.children_len();
} else {
len += 1;
}
}
len
}
fn with_child(&mut self, index: usize, visitor: &mut dyn FnMut(&mut UiNode)) {
let mut offset = 0;
for c in self.0.iter_mut() {
let next_offset = offset + if c.is_list() { c.children_len() } else { 1 };
if next_offset > index {
c.with_child(index - offset, visitor);
break;
}
offset = next_offset;
}
}
fn is_list(&self) -> bool {
true
}
fn for_each_child(&mut self, visitor: &mut dyn FnMut(usize, &mut UiNode)) {
let mut offset = 0;
for c in self.0.iter_mut() {
if c.is_list() {
c.for_each_child(|i, n| visitor(offset + i, n));
offset += c.children_len();
} else {
visitor(offset, c);
offset += 1;
}
}
}
fn try_for_each_child(
&mut self,
visitor: &mut dyn FnMut(usize, &mut UiNode) -> ControlFlow<BoxAnyVarValue>,
) -> ControlFlow<BoxAnyVarValue> {
let mut offset = 0;
for c in self.0.iter_mut() {
if c.is_list() {
let mut cf = ControlFlow::Continue(());
c.for_each_child(|i, n| cf = visitor(offset + i, n));
cf?;
offset += c.children_len();
} else {
visitor(offset, c)?;
offset += 1;
}
}
ControlFlow::Continue(())
}
fn par_each_child(&mut self, visitor: &(dyn Fn(usize, &mut UiNode) + Sync)) {
let mut offset = 0;
for c in self.0.iter_mut() {
if c.is_list() {
c.par_each_child(|i, n| visitor(offset + i, n));
offset += c.children_len();
} else {
visitor(offset, c);
offset += 1;
}
}
}
fn par_fold_reduce(
&mut self,
identity: BoxAnyVarValue,
fold: &(dyn Fn(BoxAnyVarValue, usize, &mut UiNode) -> BoxAnyVarValue + Sync),
reduce: &(dyn Fn(BoxAnyVarValue, BoxAnyVarValue) -> BoxAnyVarValue + Sync),
) -> BoxAnyVarValue {
let mut offset = 0;
let mut accumulator = identity.clone();
for c in self.0.iter_mut() {
if c.is_list() {
accumulator = c.0.par_fold_reduce(identity.clone(), &|acc, i, n| fold(acc, offset + i, n), reduce);
offset += c.children_len();
} else {
accumulator = fold(accumulator, offset, c);
offset += 1;
}
}
accumulator
}
fn init(&mut self) {
self.0.init();
}
fn deinit(&mut self) {
self.0.deinit();
}
fn info(&mut self, info: &mut WidgetInfoBuilder) {
self.0.info(info);
}
fn update(&mut self, updates: &WidgetUpdates) {
self.0.update(updates);
}
fn update_list(&mut self, updates: &WidgetUpdates, observer: &mut dyn UiNodeListObserver) {
if observer.is_reset_only() {
if (self as &mut dyn UiNodeImpl).parallelize_hint() && PARALLEL_VAR.get().contains(Parallel::UPDATE) {
let changed = self
.0
.par_iter_mut()
.with_ctx()
.map(|n| {
let mut changed = false;
n.update_list(updates, &mut changed);
changed
})
.reduce(|| false, |a, b| a || b);
if changed {
observer.reset();
}
} else {
let mut changed = false;
for c in self.0.iter_mut() {
c.update_list(updates, &mut changed);
}
if changed {
observer.reset();
}
}
} else {
let mut offset = 0;
for c in self.0.iter_mut() {
if c.is_list() {
c.0.update_list(updates, &mut OffsetUiListObserver(offset, observer));
offset += c.children_len();
} else {
c.update(updates);
offset += 1;
}
}
}
}
fn measure(&mut self, wm: &mut WidgetMeasure) -> PxSize {
self.0.measure(wm)
}
fn measure_list(
&mut self,
wm: &mut WidgetMeasure,
measure: &(dyn Fn(usize, &mut UiNode, &mut WidgetMeasure) -> PxSize + Sync),
fold_size: &(dyn Fn(PxSize, PxSize) -> PxSize + Sync),
) -> PxSize {
let mut offset = 0;
let mut accumulator = PxSize::zero();
for c in self.0.iter_mut() {
if c.is_list() {
let s = c.0.measure_list(wm, &|i, n, wm| measure(offset + i, n, wm), fold_size);
accumulator = fold_size(accumulator, s);
offset += c.children_len();
} else {
let s = measure(offset, c, wm);
accumulator = fold_size(accumulator, s);
offset += 1;
}
}
accumulator
}
fn layout(&mut self, wl: &mut WidgetLayout) -> PxSize {
self.0.layout(wl)
}
fn layout_list(
&mut self,
wl: &mut WidgetLayout,
layout: &(dyn Fn(usize, &mut UiNode, &mut WidgetLayout) -> PxSize + Sync),
fold_size: &(dyn Fn(PxSize, PxSize) -> PxSize + Sync),
) -> PxSize {
let mut offset = 0;
let mut accumulator = PxSize::zero();
for c in self.0.iter_mut() {
if c.is_list() {
let s = c.0.layout_list(wl, &|i, n, wl| layout(offset + i, n, wl), fold_size);
accumulator = fold_size(accumulator, s);
offset += c.children_len();
} else {
let s = layout(offset, c, wl);
accumulator = fold_size(accumulator, s);
offset += 1;
}
}
accumulator
}
fn render(&mut self, frame: &mut FrameBuilder) {
self.0.render(frame);
}
fn render_list(&mut self, frame: &mut FrameBuilder, render: &(dyn Fn(usize, &mut UiNode, &mut FrameBuilder) + Sync)) {
let mut offset = 0;
for c in self.0.iter_mut() {
if c.is_list() {
c.0.render_list(frame, &|i, n, frame| render(offset + i, n, frame));
offset += c.children_len();
} else {
render(offset, c, frame);
offset += 1;
}
}
}
fn render_update(&mut self, update: &mut FrameUpdate) {
self.0.render_update(update);
}
fn render_update_list(&mut self, update: &mut FrameUpdate, render_update: &(dyn Fn(usize, &mut UiNode, &mut FrameUpdate) + Sync)) {
let mut offset = 0;
for c in self.0.iter_mut() {
if c.is_list() {
c.0.render_update_list(update, &|i, n, update| render_update(offset + i, n, update));
offset += c.children_len();
} else {
render_update(offset, c, update);
offset += 1;
}
}
}
fn as_widget(&mut self) -> Option<&mut dyn WidgetUiNodeImpl> {
None
}
}
#[expect(non_camel_case_types)]
pub struct SORTING_LIST;
impl SORTING_LIST {
pub fn is_inside_list(&self) -> bool {
!SORTING_LIST_PARENT.is_default()
}
pub fn invalidate_sort(&self) {
SORTING_LIST_PARENT.get().store(true, Relaxed)
}
fn with<R>(&self, action: impl FnOnce() -> R) -> (R, bool) {
SORTING_LIST_PARENT.with_context(&mut Some(Arc::new(AtomicBool::new(false))), || {
let r = action();
(r, SORTING_LIST_PARENT.get().load(Relaxed))
})
}
}
context_local! {
static SORTING_LIST_PARENT: AtomicBool = AtomicBool::new(false);
}
pub struct SortingList {
list: UiNode,
map: Vec<usize>,
sort: Box<dyn Fn(&mut UiNode, &mut UiNode) -> Ordering + Send + 'static>,
}
impl SortingList {
pub fn new(list: impl IntoUiNode, sort: impl Fn(&mut UiNode, &mut UiNode) -> Ordering + Send + 'static) -> Self {
Self {
list: list.into_node().into_list(),
map: vec![],
sort: Box::new(sort),
}
}
fn update_map(&mut self) {
let map = &mut self.map;
let len = self.list.children_len();
if len == 0 {
map.clear();
} else if map.len() != len {
map.clear();
map.extend(0..len);
let mut taken_a = UiNode::nil();
map.sort_by(|&a, &b| {
self.list.with_child(a, |a| mem::swap(a, &mut taken_a));
let result = self.list.with_child(b, |b| (self.sort)(&mut taken_a, b));
self.list.with_child(a, |a| mem::swap(a, &mut taken_a));
result
})
}
}
pub fn list(&mut self) -> &mut UiNode {
&mut self.list
}
pub fn invalidate_sort(&mut self) {
self.map.clear()
}
fn with_map<R>(&mut self, f: impl FnOnce(&[usize], &mut UiNode) -> R) -> R {
self.update_map();
let (r, resort) = SORTING_LIST.with(|| f(&self.map, &mut self.list));
if resort {
self.invalidate_sort();
}
r
}
pub fn chain(self, other: impl IntoUiNode) -> UiNode {
self.into_node().chain(other)
}
}
impl UiNodeImpl for SortingList {
fn children_len(&self) -> usize {
self.list.children_len()
}
fn with_child(&mut self, index: usize, visitor: &mut dyn FnMut(&mut UiNode)) {
self.with_map(|map, list| {
if let Some(index) = map.get(index) {
list.0.with_child(*index, visitor)
}
})
}
fn is_list(&self) -> bool {
true
}
fn for_each_child(&mut self, visitor: &mut dyn FnMut(usize, &mut UiNode)) {
self.with_map(|map, list| {
for (i, &actual_i) in map.iter().enumerate() {
list.with_child(actual_i, |n| visitor(i, n));
}
})
}
fn try_for_each_child(
&mut self,
visitor: &mut dyn FnMut(usize, &mut UiNode) -> ControlFlow<BoxAnyVarValue>,
) -> ControlFlow<BoxAnyVarValue> {
self.with_map(|map, list| {
for (i, &actual_i) in map.iter().enumerate() {
let mut cf = ControlFlow::Continue(());
list.with_child(actual_i, |n| cf = visitor(i, n));
cf?;
}
ControlFlow::Continue(())
})
}
fn par_each_child(&mut self, visitor: &(dyn Fn(usize, &mut UiNode) + Sync)) {
self.for_each_child(&mut |i, n| visitor(i, n));
}
fn par_fold_reduce(
&mut self,
identity: BoxAnyVarValue,
fold: &(dyn Fn(BoxAnyVarValue, usize, &mut UiNode) -> BoxAnyVarValue + Sync),
_: &(dyn Fn(BoxAnyVarValue, BoxAnyVarValue) -> BoxAnyVarValue + Sync),
) -> BoxAnyVarValue {
let mut acc = Some(identity);
self.for_each_child(&mut |i, n| {
acc = Some(fold(acc.take().unwrap(), i, n));
});
acc.unwrap()
}
fn init(&mut self) {
let _ = SORTING_LIST.with(|| self.list.0.init());
self.invalidate_sort();
}
fn deinit(&mut self) {
let _ = SORTING_LIST.with(|| self.list.0.deinit());
self.invalidate_sort();
}
fn info(&mut self, info: &mut WidgetInfoBuilder) {
self.list.0.info(info);
}
fn update(&mut self, updates: &WidgetUpdates) {
self.list.0.update(updates);
}
fn update_list(&mut self, updates: &WidgetUpdates, observer: &mut dyn UiNodeListObserver) {
let mut changed = false;
let (_, resort) = SORTING_LIST.with(|| self.list.0.update_list(updates, &mut changed));
if changed || resort {
self.invalidate_sort();
observer.reset();
}
}
fn measure(&mut self, wm: &mut WidgetMeasure) -> PxSize {
self.list.0.measure(wm)
}
fn measure_list(
&mut self,
wm: &mut WidgetMeasure,
measure: &(dyn Fn(usize, &mut UiNode, &mut WidgetMeasure) -> PxSize + Sync),
fold_size: &(dyn Fn(PxSize, PxSize) -> PxSize + Sync),
) -> PxSize {
let mut acc = PxSize::zero();
self.for_each_child(&mut |i, n| {
let s = measure(i, n, wm);
acc = fold_size(acc, s);
});
acc
}
fn layout(&mut self, wl: &mut WidgetLayout) -> PxSize {
self.list.0.layout(wl)
}
fn layout_list(
&mut self,
wl: &mut WidgetLayout,
layout: &(dyn Fn(usize, &mut UiNode, &mut WidgetLayout) -> PxSize + Sync),
fold_size: &(dyn Fn(PxSize, PxSize) -> PxSize + Sync),
) -> PxSize {
let mut acc = PxSize::zero();
self.for_each_child(&mut |i, n| {
let s = layout(i, n, wl);
acc = fold_size(acc, s);
});
acc
}
fn render(&mut self, frame: &mut FrameBuilder) {
self.for_each_child(&mut |_, n| n.render(frame));
}
fn render_list(&mut self, frame: &mut FrameBuilder, render: &(dyn Fn(usize, &mut UiNode, &mut FrameBuilder) + Sync)) {
self.for_each_child(&mut |i, n| render(i, n, frame));
}
fn render_update(&mut self, update: &mut FrameUpdate) {
self.list.0.render_update(update);
}
fn render_update_list(&mut self, update: &mut FrameUpdate, render_update: &(dyn Fn(usize, &mut UiNode, &mut FrameUpdate) + Sync)) {
self.list.0.render_update_list(update, render_update);
}
fn as_widget(&mut self) -> Option<&mut dyn WidgetUiNodeImpl> {
None
}
}
pub trait UiNodeListObserver {
fn inserted(&mut self, index: usize);
fn removed(&mut self, index: usize);
fn moved(&mut self, removed_index: usize, inserted_index: usize);
fn reset(&mut self);
fn is_reset_only(&self) -> bool;
}
impl UiNodeListObserver for () {
fn is_reset_only(&self) -> bool {
true
}
fn reset(&mut self) {}
fn inserted(&mut self, _: usize) {}
fn removed(&mut self, _: usize) {}
fn moved(&mut self, _: usize, _: usize) {}
}
impl UiNodeListObserver for bool {
fn is_reset_only(&self) -> bool {
true
}
fn reset(&mut self) {
*self = true;
}
fn inserted(&mut self, _: usize) {
*self = true;
}
fn removed(&mut self, _: usize) {
*self = true;
}
fn moved(&mut self, _: usize, _: usize) {
*self = true;
}
}
pub struct OffsetUiListObserver<'o>(pub usize, pub &'o mut dyn UiNodeListObserver);
impl UiNodeListObserver for OffsetUiListObserver<'_> {
fn is_reset_only(&self) -> bool {
self.1.is_reset_only()
}
fn reset(&mut self) {
self.1.reset()
}
fn inserted(&mut self, index: usize) {
self.1.inserted(index + self.0)
}
fn removed(&mut self, index: usize) {
self.1.removed(index + self.0)
}
fn moved(&mut self, removed_index: usize, inserted_index: usize) {
self.1.moved(removed_index + self.0, inserted_index + self.0)
}
}
impl UiNodeListObserver for (&mut dyn UiNodeListObserver, &mut dyn UiNodeListObserver) {
fn is_reset_only(&self) -> bool {
self.0.is_reset_only() && self.1.is_reset_only()
}
fn reset(&mut self) {
self.0.reset();
self.1.reset();
}
fn inserted(&mut self, index: usize) {
self.0.inserted(index);
self.1.inserted(index);
}
fn removed(&mut self, index: usize) {
self.0.removed(index);
self.1.removed(index);
}
fn moved(&mut self, removed_index: usize, inserted_index: usize) {
self.0.moved(removed_index, inserted_index);
self.1.moved(removed_index, inserted_index);
}
}
pub struct EditableUiVec {
vec: UiVec,
ctrl: EditableUiVecRef,
}
impl Default for EditableUiVec {
fn default() -> Self {
Self {
vec: ui_vec![],
ctrl: EditableUiVecRef::new(true),
}
}
}
impl Drop for EditableUiVec {
fn drop(&mut self) {
self.ctrl.0.lock().alive = false;
}
}
impl EditableUiVec {
pub fn new() -> Self {
Self::default()
}
pub fn from_vec(vec: impl Into<UiVec>) -> Self {
let mut s = Self::new();
s.vec = vec.into();
s
}
pub fn reference(&self) -> EditableUiVecRef {
self.ctrl.clone()
}
pub fn chain(self, other: impl IntoUiNode) -> UiNode {
self.into_node().chain(other)
}
fn fulfill_requests(&mut self, observer: &mut dyn UiNodeListObserver) {
if let Some(r) = self.ctrl.take_requests() {
if r.clear {
self.clear();
observer.reset();
for (i, mut wgt) in r.insert {
wgt.init();
WIDGET.update_info();
if i < self.len() {
self.insert(i, wgt);
} else {
self.push(wgt);
}
}
for mut wgt in r.push {
wgt.init();
WIDGET.update_info();
self.push(wgt);
}
for (r, i) in r.move_index {
if r < self.len() {
let wgt = self.vec.remove(r);
if i < self.len() {
self.vec.insert(i, wgt);
} else {
self.vec.push(wgt);
}
WIDGET.update_info();
}
}
for (id, to) in r.move_id {
if let Some(r) = self.vec.iter_mut().position(|n| n.as_widget().map(|mut w| w.id()) == Some(id)) {
let i = to(r, self.len());
if r != i {
let wgt = self.vec.remove(r);
if i < self.len() {
self.vec.insert(i, wgt);
} else {
self.vec.push(wgt);
}
WIDGET.update_info();
}
}
}
} else {
let mut removed = false;
for mut retain in r.retain {
let mut i = 0;
self.vec.retain_mut(|n| {
let r = retain(n);
if !r {
n.deinit();
removed = true;
observer.removed(i);
} else {
i += 1;
}
r
});
}
if removed {
WIDGET.update_info();
}
for (i, mut wgt) in r.insert {
wgt.init();
WIDGET.update_info();
if i < self.len() {
self.insert(i, wgt);
observer.inserted(i);
} else {
observer.inserted(self.len());
self.push(wgt);
}
}
for mut wgt in r.push {
wgt.init();
WIDGET.update_info();
observer.inserted(self.len());
self.push(wgt);
}
for (r, i) in r.move_index {
if r < self.len() {
let wgt = self.vec.remove(r);
if i < self.len() {
self.vec.insert(i, wgt);
observer.moved(r, i);
} else {
let i = self.vec.len();
self.vec.push(wgt);
observer.moved(r, i);
}
WIDGET.update_info();
}
}
for (id, to) in r.move_id {
if let Some(r) = self.vec.iter_mut().position(|n| n.as_widget().map(|mut w| w.id()) == Some(id)) {
let i = to(r, self.len());
if r != i {
let wgt = self.vec.remove(r);
if i < self.len() {
self.vec.insert(i, wgt);
observer.moved(r, i);
} else {
let i = self.vec.len();
self.vec.push(wgt);
observer.moved(r, i);
}
WIDGET.update_info();
}
}
}
}
}
}
}
impl ops::Deref for EditableUiVec {
type Target = UiVec;
fn deref(&self) -> &Self::Target {
&self.vec
}
}
impl ops::DerefMut for EditableUiVec {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.vec
}
}
impl UiNodeImpl for EditableUiVec {
fn children_len(&self) -> usize {
self.vec.children_len()
}
fn with_child(&mut self, index: usize, visitor: &mut dyn FnMut(&mut UiNode)) {
self.vec.with_child(index, visitor);
}
fn is_list(&self) -> bool {
true
}
fn for_each_child(&mut self, visitor: &mut dyn FnMut(usize, &mut UiNode)) {
self.vec.for_each_child(visitor);
}
fn try_for_each_child(
&mut self,
visitor: &mut dyn FnMut(usize, &mut UiNode) -> ControlFlow<BoxAnyVarValue>,
) -> ControlFlow<BoxAnyVarValue> {
self.vec.try_for_each_child(visitor)
}
fn par_each_child(&mut self, visitor: &(dyn Fn(usize, &mut UiNode) + Sync)) {
self.vec.par_each_child(visitor);
}
fn par_fold_reduce(
&mut self,
identity: BoxAnyVarValue,
fold: &(dyn Fn(BoxAnyVarValue, usize, &mut UiNode) -> BoxAnyVarValue + Sync),
reduce: &(dyn Fn(BoxAnyVarValue, BoxAnyVarValue) -> BoxAnyVarValue + Sync),
) -> BoxAnyVarValue {
self.vec.par_fold_reduce(identity, fold, reduce)
}
fn init(&mut self) {
self.ctrl.0.lock().target = Some(WIDGET.id());
self.vec.init();
}
fn deinit(&mut self) {
self.ctrl.0.lock().target = None;
self.vec.deinit();
}
fn info(&mut self, info: &mut WidgetInfoBuilder) {
self.vec.info(info);
}
fn update(&mut self, updates: &WidgetUpdates) {
self.vec.update(updates);
self.fulfill_requests(&mut ());
}
fn update_list(&mut self, updates: &WidgetUpdates, observer: &mut dyn UiNodeListObserver) {
self.vec.update(updates);
self.fulfill_requests(observer);
}
fn measure(&mut self, wm: &mut WidgetMeasure) -> PxSize {
self.vec.measure(wm)
}
fn measure_list(
&mut self,
wm: &mut WidgetMeasure,
measure: &(dyn Fn(usize, &mut UiNode, &mut WidgetMeasure) -> PxSize + Sync),
fold_size: &(dyn Fn(PxSize, PxSize) -> PxSize + Sync),
) -> PxSize {
self.vec.measure_list(wm, measure, fold_size)
}
fn layout(&mut self, wl: &mut WidgetLayout) -> PxSize {
self.vec.layout(wl)
}
fn layout_list(
&mut self,
wl: &mut WidgetLayout,
layout: &(dyn Fn(usize, &mut UiNode, &mut WidgetLayout) -> PxSize + Sync),
fold_size: &(dyn Fn(PxSize, PxSize) -> PxSize + Sync),
) -> PxSize {
self.vec.layout_list(wl, layout, fold_size)
}
fn render(&mut self, frame: &mut FrameBuilder) {
self.vec.render(frame);
}
fn render_list(&mut self, frame: &mut FrameBuilder, render: &(dyn Fn(usize, &mut UiNode, &mut FrameBuilder) + Sync)) {
self.vec.render_list(frame, render);
}
fn render_update(&mut self, update: &mut FrameUpdate) {
self.vec.render_update(update);
}
fn render_update_list(&mut self, update: &mut FrameUpdate, render_update: &(dyn Fn(usize, &mut UiNode, &mut FrameUpdate) + Sync)) {
self.vec.render_update_list(update, render_update);
}
fn as_widget(&mut self) -> Option<&mut dyn WidgetUiNodeImpl> {
None
}
}
type NodeMoveToFn = fn(usize, usize) -> usize;
#[derive(Clone, Debug)]
pub struct EditableUiVecRef(Arc<Mutex<EditRequests>>);
struct EditRequests {
target: Option<WidgetId>,
insert: Vec<(usize, UiNode)>,
push: Vec<UiNode>,
retain: Vec<Box<dyn FnMut(&mut UiNode) -> bool + Send>>,
move_index: Vec<(usize, usize)>,
move_id: Vec<(WidgetId, NodeMoveToFn)>,
clear: bool,
alive: bool,
}
impl fmt::Debug for EditRequests {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("EditRequests")
.field("target", &self.target)
.field("insert.len", &self.insert.len())
.field("push.len", &self.push.len())
.field("retain.len", &self.retain.len())
.field("move_index", &self.move_index)
.field("move_id", &self.move_id)
.field("clear", &self.clear)
.field("alive", &self.alive)
.finish()
}
}
impl EditableUiVecRef {
fn new(alive: bool) -> Self {
Self(Arc::new(Mutex::new(EditRequests {
target: None,
insert: vec![],
push: vec![],
retain: vec![],
move_index: vec![],
move_id: vec![],
clear: false,
alive,
})))
}
pub fn dummy() -> Self {
Self::new(false)
}
pub fn alive(&self) -> bool {
self.0.lock().alive
}
pub fn insert(&self, index: usize, widget: impl IntoUiNode) {
self.insert_impl(index, widget.into_node());
}
fn insert_impl(&self, index: usize, widget: UiNode) {
let mut s = self.0.lock();
if !s.alive {
return;
}
s.insert.push((index, widget));
if let Some(id) = s.target {
UPDATES.update(id);
}
}
pub fn push(&self, widget: impl IntoUiNode) {
self.push_impl(widget.into_node());
}
fn push_impl(&self, widget: UiNode) {
let mut s = self.0.lock();
if !s.alive {
return;
}
s.push.push(widget);
if let Some(id) = s.target {
UPDATES.update(id);
}
}
pub fn remove(&self, id: impl Into<WidgetId>) {
fn rmv_retain(id: WidgetId) -> impl FnMut(&mut UiNode) -> bool + Send + 'static {
move |node| {
match node.as_widget() {
Some(mut wgt) => wgt.id() != id,
None => true, }
}
}
self.retain(rmv_retain(id.into()))
}
pub fn retain(&self, predicate: impl FnMut(&mut UiNode) -> bool + Send + 'static) {
let mut s = self.0.lock();
if !s.alive {
return;
}
s.retain.push(Box::new(predicate));
if let Some(id) = s.target {
UPDATES.update(id);
}
}
pub fn move_index(&self, remove_index: usize, insert_index: usize) {
if remove_index != insert_index {
let mut s = self.0.lock();
if !s.alive {
return;
}
s.move_index.push((remove_index, insert_index));
if let Some(id) = s.target {
UPDATES.update(id);
}
}
}
pub fn move_id(&self, id: impl Into<WidgetId>, get_move_to: NodeMoveToFn) {
let mut s = self.0.lock();
if !s.alive {
return;
}
s.move_id.push((id.into(), get_move_to));
if let Some(id) = s.target {
UPDATES.update(id);
}
}
pub fn clear(&self) {
let mut s = self.0.lock();
s.clear = true;
if let Some(id) = s.target {
UPDATES.update(id);
}
}
fn take_requests(&self) -> Option<EditRequests> {
let mut s = self.0.lock();
if s.clear
|| !s.insert.is_empty()
|| !s.push.is_empty()
|| !s.retain.is_empty()
|| !s.move_index.is_empty()
|| !s.move_id.is_empty()
{
let empty = EditRequests {
target: s.target,
alive: s.alive,
insert: vec![],
push: vec![],
retain: vec![],
move_index: vec![],
move_id: vec![],
clear: false,
};
Some(mem::replace(&mut *s, empty))
} else {
None
}
}
}
static_id! {
static ref Z_INDEX_ID: StateId<ZIndex>;
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Transitionable)]
pub struct ZIndex(u32);
impl ZIndex {
pub const BACK: ZIndex = ZIndex(0);
pub const DEFAULT: ZIndex = ZIndex(u32::MAX / 2);
pub const FRONT: ZIndex = ZIndex(u32::MAX);
pub fn saturating_add(self, other: impl Into<Self>) -> Self {
ZIndex(self.0.saturating_add(other.into().0))
}
pub fn saturating_sub(self, other: impl Into<Self>) -> Self {
ZIndex(self.0.saturating_sub(other.into().0))
}
}
impl Default for ZIndex {
fn default() -> Self {
ZIndex::DEFAULT
}
}
impl<Z: Into<ZIndex>> ops::Add<Z> for ZIndex {
type Output = Self;
fn add(self, rhs: Z) -> Self::Output {
self.saturating_add(rhs)
}
}
impl<Z: Into<ZIndex>> ops::AddAssign<Z> for ZIndex {
fn add_assign(&mut self, rhs: Z) {
*self = *self + rhs;
}
}
impl<Z: Into<ZIndex>> ops::Sub<Z> for ZIndex {
type Output = Self;
fn sub(self, rhs: Z) -> Self::Output {
self.saturating_sub(rhs)
}
}
impl<Z: Into<ZIndex>> ops::SubAssign<Z> for ZIndex {
fn sub_assign(&mut self, rhs: Z) {
*self = *self - rhs;
}
}
impl ops::Mul<Factor> for ZIndex {
type Output = Self;
fn mul(self, rhs: Factor) -> Self::Output {
ZIndex(self.0 * rhs)
}
}
impl ops::Div<Factor> for ZIndex {
type Output = Self;
fn div(self, rhs: Factor) -> Self::Output {
ZIndex(self.0 / rhs)
}
}
impl ops::MulAssign<Factor> for ZIndex {
fn mul_assign(&mut self, rhs: Factor) {
self.0 *= rhs;
}
}
impl ops::DivAssign<Factor> for ZIndex {
fn div_assign(&mut self, rhs: Factor) {
self.0 /= rhs;
}
}
impl fmt::Debug for ZIndex {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let z = *self;
if f.alternate() {
write!(f, "ZIndex::")?;
}
if z == Self::DEFAULT {
write!(f, "DEFAULT")
} else if z == Self::BACK {
write!(f, "BACK")
} else if z == Self::FRONT {
write!(f, "FRONT")
} else if z > Self::DEFAULT {
if z > Self::FRONT - 10000 {
write!(f, "FRONT-{}", Self::FRONT.0 - z.0)
} else {
write!(f, "DEFAULT+{}", z.0 - Self::DEFAULT.0)
}
} else if z < Self::BACK + 10000 {
write!(f, "BACK+{}", z.0 - Self::BACK.0)
} else {
write!(f, "DEFAULT-{}", Self::DEFAULT.0 - z.0)
}
}
}
impl_from_and_into_var! {
fn from(index: u32) -> ZIndex {
ZIndex(index)
}
fn from(index: ZIndex) -> u32 {
index.0
}
fn from(index: ZIndex) -> Option<ZIndex>;
}
#[derive(Default, Debug)]
struct ZIndexCtx {
panel_id: Option<WidgetId>,
resort: AtomicBool,
}
context_local! {
static Z_INDEX_CTX: ZIndexCtx = ZIndexCtx::default();
}
#[expect(non_camel_case_types)]
pub struct Z_INDEX;
impl Z_INDEX {
fn with(&self, panel_id: WidgetId, action: impl FnOnce()) -> bool {
let ctx = ZIndexCtx {
panel_id: Some(panel_id),
resort: AtomicBool::new(false),
};
Z_INDEX_CTX.with_context(&mut Some(Arc::new(ctx)), || {
action();
Z_INDEX_CTX.get().resort.load(Relaxed)
})
}
pub fn get(&self) -> ZIndex {
WIDGET.get_state(*Z_INDEX_ID).unwrap_or_default()
}
pub fn get_wgt(&self, widget: &mut UiNode) -> ZIndex {
match widget.as_widget() {
Some(mut w) => w.with_context(WidgetUpdateMode::Ignore, || self.get()),
None => ZIndex::DEFAULT,
}
}
pub fn set(&self, index: ZIndex) -> bool {
let z_ctx = Z_INDEX_CTX.get();
let valid = z_ctx.panel_id == WIDGET.parent_id() && z_ctx.panel_id.is_some();
if valid {
z_ctx.resort.store(true, Relaxed);
WIDGET.set_state(*Z_INDEX_ID, index);
}
valid
}
}
#[derive(Debug, Clone)]
pub struct PanelListRange {
range: Option<(WidgetId, WidgetId)>,
version: u8,
}
impl PanelListRange {
pub fn update(
parent: &WidgetInfo,
panel_id: impl Into<StateId<Self>>,
last_version: &mut Option<u8>,
) -> Option<crate::widget::info::iter::Children> {
let range = parent.meta().get_clone(panel_id);
if let Some(Self { range, version }) = range {
let version = Some(version);
if *last_version != version {
*last_version = version;
if let Some((s, e)) = range {
let tree = parent.tree();
if let Some(s) = tree.get(s)
&& let Some(e) = tree.get(e)
{
let parent = Some(parent);
if s.parent().as_ref() == parent && e.parent().as_ref() == parent {
return Some(crate::widget::info::iter::Children::new_range(s, e));
}
}
}
}
}
None
}
pub fn get(parent: &WidgetInfo, panel_id: impl Into<StateId<Self>>) -> Option<crate::widget::info::iter::Children> {
let range = parent.meta().get_clone(panel_id);
if let Some(Self { range: Some((s, e)), .. }) = range {
let tree = parent.tree();
if let Some(s) = tree.get(s)
&& let Some(e) = tree.get(e)
{
let parent = Some(parent);
if s.parent().as_ref() == parent && e.parent().as_ref() == parent {
return Some(crate::widget::info::iter::Children::new_range(s, e));
}
}
}
None
}
}
pub struct PanelList<D = DefaultPanelListData>
where
D: PanelListData,
{
list: UiNode,
data: Vec<Mutex<D>>,
offset_key: FrameValueKey<PxTransform>,
info_id: Option<(StateId<PanelListRange>, u8, bool)>,
z_map: Vec<u64>,
z_naturally_sorted: bool,
}
impl PanelList<DefaultPanelListData> {
pub fn new(list: impl IntoUiNode) -> Self {
Self::new_custom(list)
}
}
impl<D> PanelList<D>
where
D: PanelListData,
{
pub fn new_custom(list: impl IntoUiNode) -> Self {
Self::new_custom_impl(list.into_node())
}
fn new_custom_impl(list: UiNode) -> Self {
let list = list.into_list();
Self {
data: {
let mut d = vec![];
d.resize_with(list.children_len(), Default::default);
d
},
list,
offset_key: FrameValueKey::new_unique(),
info_id: None,
z_map: vec![],
z_naturally_sorted: false,
}
}
pub fn track_info_range(mut self, info_id: impl Into<StateId<PanelListRange>>) -> Self {
self.info_id = Some((info_id.into(), 0, true));
self
}
pub fn into_parts(self) -> (UiNode, Vec<Mutex<D>>, FrameValueKey<PxTransform>, Option<StateId<PanelListRange>>) {
(self.list, self.data, self.offset_key, self.info_id.map(|t| t.0))
}
pub fn from_parts(
list: UiNode,
data: Vec<Mutex<D>>,
offset_key: FrameValueKey<PxTransform>,
info_id: Option<StateId<PanelListRange>>,
) -> Self {
assert!(list.is_list());
assert_eq!(list.children_len(), data.len());
Self {
list,
data,
offset_key,
info_id: info_id.map(|i| (i, 0, true)),
z_map: vec![],
z_naturally_sorted: false,
}
}
pub fn info_id(&self) -> Option<StateId<PanelListRange>> {
self.info_id.as_ref().map(|t| t.0)
}
pub fn with_child<R>(&mut self, index: usize, visitor: impl FnOnce(&mut UiNode, &mut D) -> R) -> R {
let data = self.data[index].get_mut();
self.list.with_child(index, |u| visitor(u, data))
}
pub fn for_each_child(&mut self, mut visitor: impl FnMut(usize, &mut UiNode, &mut D)) {
let data = &mut self.data;
self.list.for_each_child(|i, u| visitor(i, u, data[i].get_mut()));
}
pub fn try_for_each_child<B: zng_var::VarValue>(
&mut self,
visitor: &mut dyn FnMut(usize, &mut UiNode, &mut D) -> ControlFlow<B>,
) -> ControlFlow<B> {
let data = &mut self.data;
self.list.try_for_each_child(|i, u| visitor(i, u, data[i].get_mut()))
}
pub fn par_each_child(&mut self, visitor: impl Fn(usize, &mut UiNode, &mut D) + Sync)
where
D: Sync,
{
let data = &self.data;
self.list.par_each_child(|i, u| {
visitor(
i,
u,
&mut *data[i].try_lock().expect("par_each_child called visitor twice on same index"),
)
});
}
pub fn par_fold_reduce<T>(
&mut self,
identity: T,
fold: impl Fn(T, usize, &mut UiNode, &mut D) -> T + Sync,
reduce: impl Fn(T, T) -> T + Send + Sync,
) -> T
where
T: zng_var::VarValue,
{
let data = &self.data;
self.list.par_fold_reduce(
identity,
|acc, i, n| {
fold(
acc,
i,
n,
&mut *data[i].try_lock().expect("par_fold_reduce called visitor twice on same index"),
)
},
reduce,
)
}
pub fn measure_list(
&mut self,
wm: &mut WidgetMeasure,
measure: impl Fn(usize, &mut UiNode, &mut D, &mut WidgetMeasure) -> PxSize + Sync,
fold_size: impl Fn(PxSize, PxSize) -> PxSize + Sync,
) -> PxSize {
let data = &self.data;
self.list.measure_list(
wm,
|i, n, wm| {
measure(
i,
n,
&mut *data[i].try_lock().expect("measure_list called visitor twice on same index"),
wm,
)
},
fold_size,
)
}
pub fn layout_list(
&mut self,
wl: &mut WidgetLayout,
layout: impl Fn(usize, &mut UiNode, &mut D, &mut WidgetLayout) -> PxSize + Sync,
fold_size: impl Fn(PxSize, PxSize) -> PxSize + Sync,
) -> PxSize {
let data = &self.data;
self.list.layout_list(
wl,
|i, n, wl| {
layout(
i,
n,
&mut *data[i].try_lock().expect("layout_list called visitor twice on same index"),
wl,
)
},
fold_size,
)
}
pub fn render_list(&mut self, frame: &mut FrameBuilder, render: impl Fn(usize, &mut UiNode, &mut D, &mut FrameBuilder) + Sync) {
let offset_key = self.offset_key;
if self.z_naturally_sorted {
let data = &self.data;
self.list.render_list(frame, |i, child, frame| {
let mut data = data[i].try_lock().expect("render_list called visitor twice on same index");
let offset = data.child_offset();
if data.define_reference_frame() {
frame.push_reference_frame(
(offset_key, i as u32).into(),
offset_key.bind_child(i as u32, offset.into(), false),
true,
true,
|frame| render(i, child, &mut *data, frame),
);
} else {
frame.push_child(offset, |frame| render(i, child, &mut *data, frame));
}
});
} else {
self.for_each_z_sorted(|i, child, data| {
let offset = data.child_offset();
if data.define_reference_frame() {
frame.push_reference_frame(
(offset_key, i as u32).into(),
offset_key.bind_child(i as u32, offset.into(), false),
true,
true,
|frame| render(i, child, data, frame),
);
} else {
frame.push_child(offset, |frame| render(i, child, data, frame));
}
});
}
}
pub fn render_update_list(
&mut self,
update: &mut FrameUpdate,
render_update: impl Fn(usize, &mut UiNode, &mut D, &mut FrameUpdate) + Sync,
) {
let offset_key = self.offset_key;
let data = &self.data;
self.list.render_update_list(update, |i, n, update| {
let mut data = data[i].try_lock().expect("render_update_list called visitor twice on same index");
let offset = data.child_offset();
if data.define_reference_frame() {
update.with_transform(offset_key.update_child(i as u32, offset.into(), false), true, |update| {
render_update(i, n, &mut *data, update);
});
} else {
update.with_child(offset, |update| {
render_update(i, n, &mut *data, update);
});
}
});
}
pub fn for_each_z_sorted(&mut self, mut visitor: impl FnMut(usize, &mut UiNode, &mut D)) {
if self.z_naturally_sorted {
self.for_each_child(visitor)
} else {
if self.z_map.len() != self.list.children_len() {
self.z_sort();
}
if self.z_naturally_sorted {
self.for_each_child(visitor);
} else {
for &index in self.z_map.iter() {
let index = index as usize;
let data = self.data[index].get_mut();
self.list.with_child(index, |node| visitor(index, node, data));
}
}
}
}
fn z_sort(&mut self) {
let len = self.list.children_len();
assert!(len <= u32::MAX as usize);
let mut prev_z = ZIndex::BACK;
let mut need_map = false;
let mut z_and_i = Vec::with_capacity(len);
let mut has_non_default_zs = false;
self.list.for_each_child(|i, node| {
let z = Z_INDEX.get_wgt(node);
z_and_i.push(((z.0 as u64) << 32) | i as u64);
need_map |= z < prev_z;
has_non_default_zs |= z != ZIndex::DEFAULT;
prev_z = z;
});
self.z_naturally_sorted = !need_map;
if need_map {
z_and_i.sort_unstable();
for z in &mut z_and_i {
*z &= u32::MAX as u64;
}
self.z_map = z_and_i;
} else {
self.z_map.clear();
}
}
pub fn z_map(&mut self, index: usize) -> usize {
if self.z_naturally_sorted {
return index;
}
if self.z_map.len() != self.list.children_len() {
self.z_sort();
}
if self.z_naturally_sorted {
return index;
}
self.z_map[index] as usize
}
pub fn data(&mut self, index: usize) -> &mut D {
self.data[index].get_mut()
}
pub fn commit_data(&mut self) -> PanelListDataChanges {
let mut changes = PanelListDataChanges::empty();
for data in self.data.iter_mut() {
changes |= data.get_mut().commit();
}
changes
}
pub fn offset_key(&self) -> FrameValueKey<PxTransform> {
self.offset_key
}
}
impl<D> UiNodeImpl for PanelList<D>
where
D: PanelListData,
{
fn children_len(&self) -> usize {
self.list.0.children_len()
}
fn with_child(&mut self, index: usize, visitor: &mut dyn FnMut(&mut UiNode)) {
self.list.0.with_child(index, visitor)
}
fn is_list(&self) -> bool {
true
}
fn for_each_child(&mut self, visitor: &mut dyn FnMut(usize, &mut UiNode)) {
self.list.0.for_each_child(visitor);
}
fn try_for_each_child(
&mut self,
visitor: &mut dyn FnMut(usize, &mut UiNode) -> ControlFlow<BoxAnyVarValue>,
) -> ControlFlow<BoxAnyVarValue> {
self.list.0.try_for_each_child(visitor)
}
fn par_each_child(&mut self, visitor: &(dyn Fn(usize, &mut UiNode) + Sync)) {
self.list.0.par_each_child(visitor);
}
fn par_fold_reduce(
&mut self,
identity: BoxAnyVarValue,
fold: &(dyn Fn(BoxAnyVarValue, usize, &mut UiNode) -> BoxAnyVarValue + Sync),
reduce: &(dyn Fn(BoxAnyVarValue, BoxAnyVarValue) -> BoxAnyVarValue + Sync),
) -> BoxAnyVarValue {
self.list.0.par_fold_reduce(identity, fold, reduce)
}
fn init(&mut self) {
self.z_map.clear();
let resort = Z_INDEX.with(WIDGET.id(), || self.list.0.init());
self.z_naturally_sorted = !resort;
self.data.resize_with(self.list.0.children_len(), Default::default);
}
fn deinit(&mut self) {
self.list.deinit();
}
fn info(&mut self, info: &mut WidgetInfoBuilder) {
let len = self.list.0.children_len();
if len == 0 {
return;
}
self.list.0.info(info);
if let Some((id, version, pump_update)) = &mut self.info_id {
let start = self.list.with_child(0, |c| c.as_widget().map(|mut w| w.id()));
let end = self.list.with_child(len - 1, |c| c.as_widget().map(|mut w| w.id()));
let range = match (start, end) {
(Some(s), Some(e)) => Some((s, e)),
_ => None,
};
info.set_meta(*id, PanelListRange { range, version: *version });
if mem::take(pump_update) {
self.list.for_each_child(|_, c| {
if let Some(mut w) = c.as_widget() {
w.with_context(WidgetUpdateMode::Bubble, || WIDGET.update());
}
});
}
}
}
fn update(&mut self, updates: &WidgetUpdates) {
self.update_list(updates, &mut ());
}
fn update_list(&mut self, updates: &WidgetUpdates, observer: &mut dyn UiNodeListObserver) {
let mut observer = PanelObserver {
changed: false,
data: &mut self.data,
observer,
};
let resort = Z_INDEX.with(WIDGET.id(), || self.list.update_list(updates, &mut observer));
let observer_changed = observer.changed;
if resort || (observer.changed && self.z_naturally_sorted) {
self.z_map.clear();
self.z_naturally_sorted = false;
WIDGET.render();
}
self.data.resize_with(self.list.children_len(), Default::default);
if observer_changed && let Some((_, v, u)) = &mut self.info_id {
if !*u {
*v = v.wrapping_add(1);
*u = true;
}
WIDGET.update_info();
}
}
fn measure(&mut self, wm: &mut WidgetMeasure) -> PxSize {
self.list.measure(wm)
}
fn measure_list(
&mut self,
wm: &mut WidgetMeasure,
measure: &(dyn Fn(usize, &mut UiNode, &mut WidgetMeasure) -> PxSize + Sync),
fold_size: &(dyn Fn(PxSize, PxSize) -> PxSize + Sync),
) -> PxSize {
self.list.measure_list(wm, measure, fold_size)
}
fn layout(&mut self, wl: &mut WidgetLayout) -> PxSize {
self.list.layout(wl)
}
fn layout_list(
&mut self,
wl: &mut WidgetLayout,
layout: &(dyn Fn(usize, &mut UiNode, &mut WidgetLayout) -> PxSize + Sync),
fold_size: &(dyn Fn(PxSize, PxSize) -> PxSize + Sync),
) -> PxSize {
self.list.layout_list(wl, layout, fold_size)
}
fn render(&mut self, frame: &mut FrameBuilder) {
self.render_list(frame, |_, n, _, frame| n.render(frame));
}
fn render_update(&mut self, update: &mut FrameUpdate) {
self.render_update_list(update, |_, n, _, update| n.render_update(update));
}
fn as_widget(&mut self) -> Option<&mut dyn WidgetUiNodeImpl> {
self.list.0.as_widget()
}
}
bitflags::bitflags! {
#[must_use = "|= with other item changes, call request_render"]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
#[serde(transparent)]
pub struct PanelListDataChanges: u8 {
const CHILD_OFFSET = 0b01;
const DEFINE_REFERENCE_FRAME = 0b10;
}
}
impl PanelListDataChanges {
pub fn request_render(self) {
if self.contains(Self::DEFINE_REFERENCE_FRAME) {
WIDGET.render();
} else if self.contains(Self::CHILD_OFFSET) {
WIDGET.render_update();
}
}
}
#[derive(Clone, Debug, Default)]
pub struct DefaultPanelListData {
pub child_offset: PxVector,
pub define_reference_frame: bool,
prev_child_offset: PxVector,
prev_define_reference_frame: bool,
}
impl PanelListData for DefaultPanelListData {
fn child_offset(&self) -> PxVector {
self.child_offset
}
fn define_reference_frame(&self) -> bool {
self.define_reference_frame
}
fn commit(&mut self) -> PanelListDataChanges {
let mut changes = PanelListDataChanges::empty();
if self.define_reference_frame != self.prev_define_reference_frame {
changes |= PanelListDataChanges::DEFINE_REFERENCE_FRAME;
}
if self.child_offset != self.prev_child_offset {
changes |= PanelListDataChanges::CHILD_OFFSET;
}
self.prev_define_reference_frame = self.define_reference_frame;
self.prev_child_offset = self.child_offset;
changes
}
}
pub trait PanelListData: Default + Send + Any {
fn child_offset(&self) -> PxVector;
fn define_reference_frame(&self) -> bool;
fn commit(&mut self) -> PanelListDataChanges;
}
impl PanelListData for () {
fn child_offset(&self) -> PxVector {
PxVector::zero()
}
fn define_reference_frame(&self) -> bool {
false
}
fn commit(&mut self) -> PanelListDataChanges {
PanelListDataChanges::empty()
}
}
struct PanelObserver<'d, D>
where
D: PanelListData,
{
changed: bool,
data: &'d mut Vec<Mutex<D>>,
observer: &'d mut dyn UiNodeListObserver,
}
impl<D> UiNodeListObserver for PanelObserver<'_, D>
where
D: PanelListData,
{
fn is_reset_only(&self) -> bool {
false
}
fn reset(&mut self) {
self.changed = true;
self.data.clear();
self.observer.reset();
}
fn inserted(&mut self, index: usize) {
self.changed = true;
self.data.insert(index, Default::default());
self.observer.inserted(index);
}
fn removed(&mut self, index: usize) {
self.changed = true;
self.data.remove(index);
self.observer.removed(index);
}
fn moved(&mut self, removed_index: usize, inserted_index: usize) {
self.changed = true;
let item = self.data.remove(removed_index);
self.data.insert(inserted_index, item);
self.observer.moved(removed_index, inserted_index);
}
}