use zng_layout::unit::PxSize;
use zng_var::Var;
use crate::{
update::WidgetUpdates,
widget::{
WIDGET, WidgetHandlesCtx,
node::{IntoUiNode, UiNodeImpl},
},
};
use super::{UiNode, UiNodeListObserver};
pub struct WhenUiNodeBuilder {
default: UiNode,
conditions: Vec<(Var<bool>, UiNode)>,
}
impl WhenUiNodeBuilder {
pub fn new(default: impl IntoUiNode) -> Self {
Self {
default: default.into_node(),
conditions: vec![],
}
}
pub fn push(&mut self, condition: Var<bool>, node: impl IntoUiNode) {
self.conditions.push((condition, node.into_node()));
}
pub fn build(self) -> UiNode {
UiNode::new(WhenUiNode {
default: self.default,
conditions: self.conditions,
current: usize::MAX,
wgt_handles: WidgetHandlesCtx::new(),
})
}
}
struct WhenUiNode {
default: UiNode,
conditions: Vec<(Var<bool>, UiNode)>,
current: usize,
wgt_handles: WidgetHandlesCtx,
}
impl WhenUiNode {
fn child_mut_with_handles(&mut self) -> (&mut UiNode, &mut WidgetHandlesCtx) {
let child = if self.current == usize::MAX {
&mut self.default
} else {
&mut self.conditions[self.current].1
};
(child, &mut self.wgt_handles)
}
fn child_ref(&self) -> &UiNode {
if self.current == usize::MAX {
&self.default
} else {
&self.conditions[self.current].1
}
}
fn change_child(&mut self, new: usize) {
{
let (child, wgt_handles) = self.child_mut_with_handles();
WIDGET.with_handles(wgt_handles, || child.deinit());
wgt_handles.clear();
}
self.current = new;
{
let (child, wgt_handles) = self.child_mut_with_handles();
WIDGET.with_handles(wgt_handles, || child.init());
}
WIDGET.update_info().layout().render();
}
fn with<R>(&mut self, f: impl FnOnce(&mut UiNode) -> R) -> R {
let (child, wgt_handles) = self.child_mut_with_handles();
WIDGET.with_handles(wgt_handles, || f(child))
}
fn update_when(&mut self) -> bool {
let mut any = false;
for (i, (c, _)) in self.conditions.iter().enumerate() {
if i < self.current {
if c.get() {
self.change_child(i);
return true;
}
} else if i == self.current {
if c.get() {
any = true;
break;
}
} else if c.get() {
self.change_child(i);
return true;
}
}
if !any && self.current != usize::MAX {
self.change_child(usize::MAX);
return true;
}
false
}
}
impl UiNodeImpl for WhenUiNode {
fn children_len(&self) -> usize {
self.child_ref().0.children_len()
}
fn with_child(&mut self, index: usize, visitor: &mut dyn FnMut(&mut UiNode)) {
self.child_mut_with_handles().0.0.with_child(index, visitor);
}
fn init(&mut self) {
self.current = usize::MAX;
for (i, (c, _)) in self.conditions.iter().enumerate() {
if self.current == usize::MAX && c.get() {
self.current = i;
}
WIDGET.sub_var(c);
}
self.with(|c| c.0.init());
}
fn deinit(&mut self) {
self.with(|c| c.0.deinit());
self.wgt_handles.clear();
}
fn info(&mut self, info: &mut crate::widget::info::WidgetInfoBuilder) {
self.with(|c| c.0.info(info));
}
fn update(&mut self, updates: &WidgetUpdates) {
if !self.update_when() {
self.with(|c| c.0.update(updates));
}
}
fn update_list(&mut self, updates: &WidgetUpdates, observer: &mut dyn UiNodeListObserver) {
if self.update_when() {
observer.reset();
} else {
self.with(|c| c.0.update_list(updates, observer));
}
}
fn measure(&mut self, wm: &mut crate::widget::info::WidgetMeasure) -> PxSize {
self.with(|c| c.0.measure(wm))
}
fn layout(&mut self, wl: &mut crate::widget::info::WidgetLayout) -> PxSize {
self.with(|c| c.0.layout(wl))
}
fn render(&mut self, frame: &mut crate::render::FrameBuilder) {
self.with(|c| c.0.render(frame))
}
fn render_update(&mut self, update: &mut crate::render::FrameUpdate) {
self.with(|c| c.0.render_update(update))
}
fn is_list(&self) -> bool {
self.child_ref().0.is_list()
}
fn for_each_child(&mut self, visitor: &mut dyn FnMut(usize, &mut UiNode)) {
self.child_mut_with_handles().0.0.for_each_child(visitor);
}
fn try_for_each_child(
&mut self,
visitor: &mut dyn FnMut(usize, &mut UiNode) -> std::ops::ControlFlow<zng_var::BoxAnyVarValue>,
) -> std::ops::ControlFlow<zng_var::BoxAnyVarValue> {
self.child_mut_with_handles().0.0.try_for_each_child(visitor)
}
fn par_each_child(&mut self, visitor: &(dyn Fn(usize, &mut UiNode) + Sync)) {
self.child_mut_with_handles().0.0.par_each_child(visitor);
}
fn par_fold_reduce(
&mut self,
identity: zng_var::BoxAnyVarValue,
fold: &(dyn Fn(zng_var::BoxAnyVarValue, usize, &mut UiNode) -> zng_var::BoxAnyVarValue + Sync),
reduce: &(dyn Fn(zng_var::BoxAnyVarValue, zng_var::BoxAnyVarValue) -> zng_var::BoxAnyVarValue + Sync),
) -> zng_var::BoxAnyVarValue {
self.child_mut_with_handles().0.0.par_fold_reduce(identity, fold, reduce)
}
fn measure_list(
&mut self,
wm: &mut crate::widget::info::WidgetMeasure,
measure: &(dyn Fn(usize, &mut UiNode, &mut crate::widget::info::WidgetMeasure) -> PxSize + Sync),
fold_size: &(dyn Fn(PxSize, PxSize) -> PxSize + Sync),
) -> PxSize {
self.with(|c| c.0.measure_list(wm, measure, fold_size))
}
fn layout_list(
&mut self,
wl: &mut crate::widget::info::WidgetLayout,
layout: &(dyn Fn(usize, &mut UiNode, &mut crate::widget::info::WidgetLayout) -> PxSize + Sync),
fold_size: &(dyn Fn(PxSize, PxSize) -> PxSize + Sync),
) -> PxSize {
self.with(|c| c.0.layout_list(wl, layout, fold_size))
}
fn render_list(
&mut self,
frame: &mut crate::render::FrameBuilder,
render: &(dyn Fn(usize, &mut UiNode, &mut crate::render::FrameBuilder) + Sync),
) {
self.with(|c| c.0.render_list(frame, render))
}
fn render_update_list(
&mut self,
update: &mut crate::render::FrameUpdate,
render_update: &(dyn Fn(usize, &mut UiNode, &mut crate::render::FrameUpdate) + Sync),
) {
self.with(|c| c.0.render_update_list(update, render_update))
}
fn as_widget(&mut self) -> Option<&mut dyn super::WidgetUiNodeImpl> {
self.child_mut_with_handles().0.0.as_widget()
}
}