use std::{fmt, mem, ops};
use zng_layout::unit::PxSize;
use crate::{
render::{FrameBuilder, FrameUpdate},
update::WidgetUpdates,
widget::info::{WidgetInfoBuilder, WidgetLayout, WidgetMeasure},
};
use super::*;
#[non_exhaustive]
pub enum UiNodeOp<'a> {
Init,
Deinit,
Info {
info: &'a mut WidgetInfoBuilder,
},
Update {
updates: &'a WidgetUpdates,
},
Measure {
wm: &'a mut WidgetMeasure,
desired_size: &'a mut PxSize,
},
Layout {
wl: &'a mut WidgetLayout,
final_size: &'a mut PxSize,
},
Render {
frame: &'a mut FrameBuilder,
},
RenderUpdate {
update: &'a mut FrameUpdate,
},
}
impl<'a> UiNodeOp<'a> {
pub fn mtd(&self) -> UiNodeMethod {
match self {
UiNodeOp::Init => UiNodeMethod::Init,
UiNodeOp::Deinit => UiNodeMethod::Deinit,
UiNodeOp::Info { .. } => UiNodeMethod::Info,
UiNodeOp::Update { .. } => UiNodeMethod::Update,
UiNodeOp::Measure { .. } => UiNodeMethod::Measure,
UiNodeOp::Layout { .. } => UiNodeMethod::Layout,
UiNodeOp::Render { .. } => UiNodeMethod::Render,
UiNodeOp::RenderUpdate { .. } => UiNodeMethod::RenderUpdate,
}
}
pub fn reborrow(&mut self) -> UiNodeOp<'_> {
match self {
UiNodeOp::Init => UiNodeOp::Init,
UiNodeOp::Deinit => UiNodeOp::Deinit,
UiNodeOp::Info { info } => UiNodeOp::Info { info },
UiNodeOp::Update { updates } => UiNodeOp::Update { updates },
UiNodeOp::Measure { wm, desired_size } => UiNodeOp::Measure { wm, desired_size },
UiNodeOp::Layout { wl, final_size } => UiNodeOp::Layout { wl, final_size },
UiNodeOp::Render { frame } => UiNodeOp::Render { frame },
UiNodeOp::RenderUpdate { update } => UiNodeOp::RenderUpdate { update },
}
}
}
impl fmt::Debug for UiNodeOp<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Update { updates } => f.debug_struct("Update").field("updates", updates).finish(),
op => write!(f, "{}", op.mtd()),
}
}
}
#[derive(Clone, Copy, serde::Serialize, serde::Deserialize)]
#[non_exhaustive]
pub enum UiNodeMethod {
Init,
Deinit,
Info,
Update,
UpdateList,
Measure,
MeasureList,
Layout,
LayoutList,
Render,
RenderList,
RenderUpdate,
RenderUpdateList,
}
impl UiNodeMethod {
pub fn enum_name(self) -> &'static str {
match self {
UiNodeMethod::Init => "Init",
UiNodeMethod::Deinit => "Deinit",
UiNodeMethod::Info => "Info",
UiNodeMethod::Update => "Update",
UiNodeMethod::UpdateList => "UpdateList",
UiNodeMethod::Measure => "Measure",
UiNodeMethod::MeasureList => "MeasureList",
UiNodeMethod::Layout => "Layout",
UiNodeMethod::LayoutList => "LayoutList",
UiNodeMethod::Render => "Render",
UiNodeMethod::RenderList => "RenderList",
UiNodeMethod::RenderUpdate => "RenderUpdate",
UiNodeMethod::RenderUpdateList => "RenderUpdateList",
}
}
pub fn mtd_name(self) -> &'static str {
match self {
UiNodeMethod::Init => "init",
UiNodeMethod::Deinit => "deinit",
UiNodeMethod::Info => "info",
UiNodeMethod::Update => "update",
UiNodeMethod::UpdateList => "update_list",
UiNodeMethod::Measure => "measure",
UiNodeMethod::MeasureList => "measure_list",
UiNodeMethod::Layout => "layout",
UiNodeMethod::LayoutList => "layout_list",
UiNodeMethod::Render => "render",
UiNodeMethod::RenderList => "render_list",
UiNodeMethod::RenderUpdate => "render_update",
UiNodeMethod::RenderUpdateList => "render_update_list",
}
}
}
impl fmt::Debug for UiNodeMethod {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
impl fmt::Display for UiNodeMethod {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
write!(f, "{}", self.enum_name())
} else {
write!(f, "{}", self.mtd_name())
}
}
}
pub fn match_node(child: impl IntoUiNode, closure: impl FnMut(&mut MatchNodeChild, UiNodeOp) + Send + 'static) -> UiNode {
match_node_impl(child.into_node(), closure)
}
fn match_node_impl(child: UiNode, closure: impl FnMut(&mut MatchNodeChild, UiNodeOp) + Send + 'static) -> UiNode {
struct MatchNode<F> {
child: MatchNodeChild,
closure: F,
}
impl<F: FnMut(&mut MatchNodeChild, UiNodeOp) + Send + 'static> UiNodeImpl for MatchNode<F> {
fn children_len(&self) -> usize {
1
}
fn with_child(&mut self, index: usize, visitor: &mut dyn FnMut(&mut UiNode)) {
if index == 0 {
visitor(&mut self.child.node)
}
}
fn init(&mut self) {
self.child.delegated = false;
(self.closure)(&mut self.child, UiNodeOp::Init);
if !mem::take(&mut self.child.delegated) {
self.child.node.0.init();
}
}
fn deinit(&mut self) {
self.child.delegated = false;
(self.closure)(&mut self.child, UiNodeOp::Deinit);
if !mem::take(&mut self.child.delegated) {
self.child.node.0.deinit();
}
}
fn info(&mut self, info: &mut WidgetInfoBuilder) {
self.child.delegated = false;
(self.closure)(&mut self.child, UiNodeOp::Info { info });
if !mem::take(&mut self.child.delegated) {
self.child.node.0.info(info);
}
}
fn update(&mut self, updates: &WidgetUpdates) {
self.child.delegated = false;
(self.closure)(&mut self.child, UiNodeOp::Update { updates });
if !mem::take(&mut self.child.delegated) {
self.child.node.0.update(updates);
}
}
fn measure(&mut self, wm: &mut WidgetMeasure) -> PxSize {
self.child.delegated = false;
let mut size = PxSize::zero();
(self.closure)(
&mut self.child,
UiNodeOp::Measure {
wm,
desired_size: &mut size,
},
);
if !mem::take(&mut self.child.delegated) {
if size != PxSize::zero() {
tracing::error!("measure changed size without flagging delegated");
return size;
}
self.child.node.0.measure(wm)
} else {
size
}
}
fn layout(&mut self, wl: &mut WidgetLayout) -> PxSize {
self.child.delegated = false;
let mut size = PxSize::zero();
(self.closure)(&mut self.child, UiNodeOp::Layout { wl, final_size: &mut size });
if !mem::take(&mut self.child.delegated) {
if size != PxSize::zero() {
tracing::error!("layout changed size without flagging delegated");
return size;
}
self.child.node.0.layout(wl)
} else {
size
}
}
fn render(&mut self, frame: &mut FrameBuilder) {
self.child.delegated = false;
(self.closure)(&mut self.child, UiNodeOp::Render { frame });
if !mem::take(&mut self.child.delegated) {
self.child.node.0.render(frame);
}
}
fn render_update(&mut self, update: &mut FrameUpdate) {
self.child.delegated = false;
(self.closure)(&mut self.child, UiNodeOp::RenderUpdate { update });
if !mem::take(&mut self.child.delegated) {
self.child.node.0.render_update(update);
}
}
fn is_list(&self) -> bool {
false
}
fn for_each_child(&mut self, visitor: &mut dyn FnMut(usize, &mut UiNode)) {
visitor(0, &mut self.child.node)
}
fn try_for_each_child(
&mut self,
visitor: &mut dyn FnMut(usize, &mut UiNode) -> ControlFlow<BoxAnyVarValue>,
) -> ControlFlow<BoxAnyVarValue> {
visitor(0, &mut self.child.node)
}
fn par_each_child(&mut self, visitor: &(dyn Fn(usize, &mut UiNode) + Sync)) {
visitor(0, &mut self.child.node)
}
fn par_fold_reduce(
&mut self,
identity: BoxAnyVarValue,
fold: &(dyn Fn(BoxAnyVarValue, usize, &mut UiNode) -> BoxAnyVarValue + Sync),
_: &(dyn Fn(BoxAnyVarValue, BoxAnyVarValue) -> BoxAnyVarValue + Sync),
) -> BoxAnyVarValue {
fold(identity, 0, &mut self.child.node)
}
fn update_list(&mut self, updates: &WidgetUpdates, _: &mut dyn UiNodeListObserver) {
self.update(updates);
}
fn measure_list(
&mut self,
wm: &mut WidgetMeasure,
_: &(dyn Fn(usize, &mut UiNode, &mut WidgetMeasure) -> PxSize + Sync),
_: &(dyn Fn(PxSize, PxSize) -> PxSize + Sync),
) -> PxSize {
self.measure(wm)
}
fn layout_list(
&mut self,
wl: &mut WidgetLayout,
_: &(dyn Fn(usize, &mut UiNode, &mut WidgetLayout) -> PxSize + Sync),
_: &(dyn Fn(PxSize, PxSize) -> PxSize + Sync),
) -> PxSize {
self.layout(wl)
}
fn render_list(&mut self, frame: &mut FrameBuilder, _: &(dyn Fn(usize, &mut UiNode, &mut FrameBuilder) + Sync)) {
self.render(frame)
}
fn render_update_list(&mut self, update: &mut FrameUpdate, _: &(dyn Fn(usize, &mut UiNode, &mut FrameUpdate) + Sync)) {
self.render_update(update);
}
fn as_widget(&mut self) -> Option<&mut dyn WidgetUiNodeImpl> {
None
}
}
MatchNode {
child: MatchNodeChild {
node: child,
delegated: false,
},
closure,
}
.into_node()
}
pub struct MatchNodeChild {
node: UiNode,
delegated: bool,
}
impl MatchNodeChild {
#[inline(always)]
pub fn delegated(&mut self) {
self.delegated = true;
}
#[inline(always)]
pub fn has_delegated(&self) -> bool {
self.delegated
}
#[inline(always)]
pub fn node(&mut self) -> &mut UiNode {
&mut self.node
}
#[inline(always)]
pub fn node_impl<U: UiNodeImpl>(&mut self) -> &mut U {
self.node.downcast_mut::<U>().unwrap()
}
#[inline(always)]
pub fn init(&mut self) {
self.node.0.init();
self.delegated = true;
}
#[inline(always)]
pub fn deinit(&mut self) {
self.node.0.deinit();
self.delegated = true;
}
#[inline(always)]
pub fn info(&mut self, info: &mut WidgetInfoBuilder) {
self.node.0.info(info);
self.delegated = true;
}
#[inline(always)]
pub fn update(&mut self, updates: &WidgetUpdates) {
self.node.0.update(updates);
self.delegated = true;
}
#[inline(always)]
pub fn update_list(&mut self, updates: &WidgetUpdates, observer: &mut dyn UiNodeListObserver) {
self.node.0.update_list(updates, observer);
self.delegated = true;
}
#[inline(always)]
#[must_use]
pub fn measure(&mut self, wm: &mut WidgetMeasure) -> PxSize {
self.delegated = true;
self.node.0.measure(wm)
}
#[inline(always)]
#[must_use]
pub fn measure_list(
&mut self,
wm: &mut WidgetMeasure,
measure: impl Fn(usize, &mut UiNode, &mut WidgetMeasure) -> PxSize + Sync,
fold_size: impl Fn(PxSize, PxSize) -> PxSize + Sync,
) -> PxSize {
self.delegated = true;
self.node.0.measure_list(wm, &measure, &fold_size)
}
#[inline(always)]
#[must_use]
pub fn layout(&mut self, wl: &mut WidgetLayout) -> PxSize {
self.delegated = true;
self.node.0.layout(wl)
}
#[inline(always)]
#[must_use]
pub fn layout_list(
&mut self,
wl: &mut WidgetLayout,
layout: impl Fn(usize, &mut UiNode, &mut WidgetLayout) -> PxSize + Sync,
fold_size: impl Fn(PxSize, PxSize) -> PxSize + Sync,
) -> PxSize {
self.delegated = true;
self.node.0.layout_list(wl, &layout, &fold_size)
}
#[inline(always)]
pub fn render(&mut self, frame: &mut FrameBuilder) {
self.node.0.render(frame);
self.delegated = true;
}
#[inline(always)]
pub fn render_list(&mut self, frame: &mut FrameBuilder, render: impl Fn(usize, &mut UiNode, &mut FrameBuilder) + Sync) {
self.node.render_list(frame, render);
self.delegated = true;
}
#[inline(always)]
pub fn render_update(&mut self, update: &mut FrameUpdate) {
self.node.0.render_update(update);
self.delegated = true;
}
#[inline(always)]
pub fn render_update_list(&mut self, update: &mut FrameUpdate, render_update: impl Fn(usize, &mut UiNode, &mut FrameUpdate) + Sync) {
self.node.render_update_list(update, render_update);
}
#[inline(always)]
pub fn op(&mut self, op: UiNodeOp) {
self.node.op(op);
self.delegated = true;
}
}
pub fn match_node_leaf(closure: impl FnMut(UiNodeOp) + Send + 'static) -> UiNode {
struct MatchNodeLeaf<F> {
closure: F,
}
impl<F: FnMut(UiNodeOp) + Send + 'static> UiNodeImpl for MatchNodeLeaf<F> {
fn children_len(&self) -> usize {
0
}
fn with_child(&mut self, _: usize, _: &mut dyn FnMut(&mut UiNode)) {}
fn init(&mut self) {
(self.closure)(UiNodeOp::Init);
}
fn deinit(&mut self) {
(self.closure)(UiNodeOp::Deinit);
}
fn info(&mut self, info: &mut WidgetInfoBuilder) {
(self.closure)(UiNodeOp::Info { info });
}
fn update(&mut self, updates: &WidgetUpdates) {
(self.closure)(UiNodeOp::Update { updates });
}
fn measure(&mut self, wm: &mut WidgetMeasure) -> PxSize {
let mut size = PxSize::zero();
(self.closure)(UiNodeOp::Measure {
wm,
desired_size: &mut size,
});
size
}
fn layout(&mut self, wl: &mut WidgetLayout) -> PxSize {
let mut size = PxSize::zero();
(self.closure)(UiNodeOp::Layout { wl, final_size: &mut size });
size
}
fn render(&mut self, frame: &mut FrameBuilder) {
(self.closure)(UiNodeOp::Render { frame });
}
fn render_update(&mut self, update: &mut FrameUpdate) {
(self.closure)(UiNodeOp::RenderUpdate { update });
}
fn is_list(&self) -> bool {
false
}
fn for_each_child(&mut self, _: &mut dyn FnMut(usize, &mut UiNode)) {}
fn try_for_each_child(
&mut self,
_: &mut dyn FnMut(usize, &mut UiNode) -> ControlFlow<BoxAnyVarValue>,
) -> ControlFlow<BoxAnyVarValue> {
ControlFlow::Continue(())
}
fn par_each_child(&mut self, _: &(dyn Fn(usize, &mut UiNode) + Sync)) {}
fn par_fold_reduce(
&mut self,
identity: BoxAnyVarValue,
_: &(dyn Fn(BoxAnyVarValue, usize, &mut UiNode) -> BoxAnyVarValue + Sync),
_: &(dyn Fn(BoxAnyVarValue, BoxAnyVarValue) -> BoxAnyVarValue + Sync),
) -> BoxAnyVarValue {
identity
}
fn update_list(&mut self, updates: &WidgetUpdates, _: &mut dyn UiNodeListObserver) {
self.update(updates);
}
fn measure_list(
&mut self,
wm: &mut WidgetMeasure,
_: &(dyn Fn(usize, &mut UiNode, &mut WidgetMeasure) -> PxSize + Sync),
_: &(dyn Fn(PxSize, PxSize) -> PxSize + Sync),
) -> PxSize {
self.measure(wm)
}
fn layout_list(
&mut self,
wl: &mut WidgetLayout,
_: &(dyn Fn(usize, &mut UiNode, &mut WidgetLayout) -> PxSize + Sync),
_: &(dyn Fn(PxSize, PxSize) -> PxSize + Sync),
) -> PxSize {
self.layout(wl)
}
fn render_list(&mut self, frame: &mut FrameBuilder, _: &(dyn Fn(usize, &mut UiNode, &mut FrameBuilder) + Sync)) {
self.render(frame);
}
fn render_update_list(&mut self, update: &mut FrameUpdate, _: &(dyn Fn(usize, &mut UiNode, &mut FrameUpdate) + Sync)) {
self.render_update(update);
}
fn as_widget(&mut self) -> Option<&mut dyn WidgetUiNodeImpl> {
None
}
}
UiNode::new(MatchNodeLeaf { closure })
}
pub fn match_widget(child: impl IntoUiNode, closure: impl FnMut(&mut MatchWidgetChild, UiNodeOp) + Send + 'static) -> UiNode {
struct MatchWidget<F> {
child: MatchWidgetChild,
closure: F,
}
impl<F: FnMut(&mut MatchWidgetChild, UiNodeOp) + Send + 'static> UiNodeImpl for MatchWidget<F> {
fn children_len(&self) -> usize {
1
}
fn with_child(&mut self, index: usize, visitor: &mut dyn FnMut(&mut UiNode)) {
if index == 0 {
visitor(&mut self.child.node)
}
}
fn init(&mut self) {
self.child.0.delegated = false;
(self.closure)(&mut self.child, UiNodeOp::Init);
if !mem::take(&mut self.child.0.delegated) {
self.child.0.node.0.init();
}
}
fn deinit(&mut self) {
self.child.0.delegated = false;
(self.closure)(&mut self.child, UiNodeOp::Deinit);
if !mem::take(&mut self.child.0.delegated) {
self.child.0.node.0.deinit();
}
}
fn info(&mut self, info: &mut WidgetInfoBuilder) {
self.child.0.delegated = false;
(self.closure)(&mut self.child, UiNodeOp::Info { info });
if !mem::take(&mut self.child.0.delegated) {
self.child.0.node.0.info(info);
} else {
#[cfg(debug_assertions)]
if self
.child
.0
.node
.as_widget()
.map(|mut w| {
w.with_context(crate::widget::WidgetUpdateMode::Ignore, || {
WIDGET.pending_update().contains(crate::update::UpdateFlags::INFO)
})
})
.unwrap_or(false)
{
tracing::warn!(target: "match_widget-pending", "pending info build after info delegated in {:?}", WIDGET.id());
}
}
}
fn update(&mut self, updates: &WidgetUpdates) {
self.child.0.delegated = false;
(self.closure)(&mut self.child, UiNodeOp::Update { updates });
if !mem::take(&mut self.child.0.delegated) {
self.child.0.node.0.update(updates);
}
}
fn measure(&mut self, wm: &mut WidgetMeasure) -> PxSize {
self.child.0.delegated = false;
let mut size = PxSize::zero();
(self.closure)(
&mut self.child,
UiNodeOp::Measure {
wm,
desired_size: &mut size,
},
);
if !mem::take(&mut self.child.0.delegated) {
if size != PxSize::zero() {
tracing::error!("measure changed size without flagging delegated in {:?}", WIDGET.id());
return size;
}
self.child.0.node.0.measure(wm)
} else {
size
}
}
fn layout(&mut self, wl: &mut WidgetLayout) -> PxSize {
self.child.0.delegated = false;
let mut size = PxSize::zero();
(self.closure)(&mut self.child, UiNodeOp::Layout { wl, final_size: &mut size });
if !mem::take(&mut self.child.0.delegated) {
if size != PxSize::zero() {
tracing::error!("layout changed size without flagging delegated in {:?}", WIDGET.id());
return size;
}
self.child.0.node.0.layout(wl)
} else {
#[cfg(debug_assertions)]
if self
.child
.0
.node
.as_widget()
.map(|mut w| {
w.with_context(crate::widget::WidgetUpdateMode::Ignore, || {
WIDGET.pending_update().contains(crate::update::UpdateFlags::LAYOUT)
})
})
.unwrap_or(false)
{
tracing::warn!(target: "match_widget-pending", "pending layout after layout delegated in {:?}", WIDGET.id());
}
size
}
}
fn render(&mut self, frame: &mut FrameBuilder) {
self.child.0.delegated = false;
(self.closure)(&mut self.child, UiNodeOp::Render { frame });
if !mem::take(&mut self.child.0.delegated) {
self.child.0.node.0.render(frame);
} else {
#[cfg(debug_assertions)]
if self
.child
.0
.node
.as_widget()
.map(|mut w| {
w.with_context(crate::widget::WidgetUpdateMode::Ignore, || {
WIDGET.pending_update().contains(crate::update::UpdateFlags::RENDER)
})
})
.unwrap_or(false)
{
tracing::warn!(target: "match_widget-pending", "pending render after render delegated in {:?}", WIDGET.id());
}
}
}
fn render_update(&mut self, update: &mut FrameUpdate) {
self.child.0.delegated = false;
(self.closure)(&mut self.child, UiNodeOp::RenderUpdate { update });
if !mem::take(&mut self.child.0.delegated) {
self.child.0.node.0.render_update(update);
} else {
#[cfg(debug_assertions)]
if self
.child
.0
.node
.as_widget()
.map(|mut w| {
w.with_context(crate::widget::WidgetUpdateMode::Ignore, || {
WIDGET.pending_update().contains(crate::update::UpdateFlags::RENDER_UPDATE)
})
})
.unwrap_or(false)
{
tracing::warn!(target: "match_widget-pending", "pending render_update after render_update delegated in {:?}", WIDGET.id());
}
}
}
fn is_list(&self) -> bool {
false
}
fn for_each_child(&mut self, visitor: &mut dyn FnMut(usize, &mut UiNode)) {
visitor(0, &mut self.child.node)
}
fn try_for_each_child(
&mut self,
visitor: &mut dyn FnMut(usize, &mut UiNode) -> ControlFlow<BoxAnyVarValue>,
) -> ControlFlow<BoxAnyVarValue> {
visitor(0, &mut self.child.node)
}
fn par_each_child(&mut self, visitor: &(dyn Fn(usize, &mut UiNode) + Sync)) {
visitor(0, &mut self.child.node)
}
fn par_fold_reduce(
&mut self,
identity: BoxAnyVarValue,
fold: &(dyn Fn(BoxAnyVarValue, usize, &mut UiNode) -> BoxAnyVarValue + Sync),
_: &(dyn Fn(BoxAnyVarValue, BoxAnyVarValue) -> BoxAnyVarValue + Sync),
) -> BoxAnyVarValue {
fold(identity, 0, &mut self.child.node)
}
fn update_list(&mut self, updates: &WidgetUpdates, _: &mut dyn UiNodeListObserver) {
self.update(updates);
}
fn measure_list(
&mut self,
wm: &mut WidgetMeasure,
_: &(dyn Fn(usize, &mut UiNode, &mut WidgetMeasure) -> PxSize + Sync),
_: &(dyn Fn(PxSize, PxSize) -> PxSize + Sync),
) -> PxSize {
self.measure(wm)
}
fn layout_list(
&mut self,
wl: &mut WidgetLayout,
_: &(dyn Fn(usize, &mut UiNode, &mut WidgetLayout) -> PxSize + Sync),
_: &(dyn Fn(PxSize, PxSize) -> PxSize + Sync),
) -> PxSize {
self.layout(wl)
}
fn render_list(&mut self, frame: &mut FrameBuilder, _: &(dyn Fn(usize, &mut UiNode, &mut FrameBuilder) + Sync)) {
self.render(frame);
}
fn render_update_list(&mut self, update: &mut FrameUpdate, _: &(dyn Fn(usize, &mut UiNode, &mut FrameUpdate) + Sync)) {
self.render_update(update);
}
fn as_widget(&mut self) -> Option<&mut dyn WidgetUiNodeImpl> {
self.child.node.0.as_widget()
}
}
MatchWidget {
child: MatchWidgetChild(MatchNodeChild {
node: child.into_node(),
delegated: false,
}),
closure,
}
.into_node()
}
pub struct MatchWidgetChild(MatchNodeChild);
impl ops::Deref for MatchWidgetChild {
type Target = MatchNodeChild;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl ops::DerefMut for MatchWidgetChild {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}