use crate::{handler::WidgetHandler, widget::node::IntoUiNode};
use std::{
any::{Any, TypeId},
collections::{HashMap, hash_map},
fmt, ops,
sync::Arc,
};
#[doc(hidden)]
pub use zng_var::{var_getter, var_state};
#[macro_export]
macro_rules! source_location {
() => {
$crate::widget::builder::SourceLocation::new(std::file!(), std::line!(), std::column!())
};
}
#[doc(inline)]
pub use crate::source_location;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
#[non_exhaustive]
pub struct SourceLocation {
pub file: &'static str,
pub line: u32,
pub column: u32,
}
impl SourceLocation {
#[doc(hidden)]
pub fn new(file: &'static str, line: u32, column: u32) -> Self {
Self { file, line, column }
}
}
impl fmt::Display for SourceLocation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}:{}:{}", self.file, self.line, self.column)
}
}
#[doc(hidden)]
pub struct WgtInfo;
impl WidgetExt for WgtInfo {
fn ext_property__(&mut self, _: Box<dyn PropertyArgs>) {
panic!("WgtInfo is for extracting info only")
}
fn ext_property_unset__(&mut self, _: PropertyId) {
panic!("WgtInfo is for extracting info only")
}
}
#[macro_export]
macro_rules! property_id {
($($tt:tt)*) => {
$crate::widget::property_meta!($($tt)*).id()
}
}
#[doc(inline)]
pub use crate::property_id;
#[macro_export]
macro_rules! property_info {
($($property:ident)::+ <$($generics:ty),*>) => {
$crate::widget::property_meta!($($property)::+).info::<$($generics),*>()
};
($($tt:tt)*) => {
$crate::widget::property_meta!($($tt)*).info()
}
}
#[doc(inline)]
pub use crate::property_info;
#[macro_export]
macro_rules! property_input_types {
($($tt:tt)*) => {
$crate::widget::property_meta!($($tt)*).input_types()
}
}
#[doc(inline)]
pub use crate::property_input_types;
#[macro_export]
macro_rules! property_args {
($($property:ident)::+ = $($value:tt)*) => {
{
$crate::widget::builder::PropertyArgsGetter! {
$($property)::+ = $($value)*
}
}
};
($($property:ident)::+ ::<$($generics:ty),*> = $($value:tt)*) => {
{
$crate::widget::builder::PropertyArgsGetter! {
$($property)::+ ::<$($generics),*> = $($value)*
}
}
};
($property:ident $(;)?) => {
{
$crate::widget::builder::PropertyArgsGetter! {
$property
}
}
}
}
#[doc(inline)]
pub use crate::property_args;
#[macro_export]
macro_rules! widget_type {
($($widget:ident)::+) => {
$($widget)::+::widget_type()
};
}
use parking_lot::Mutex;
#[doc(inline)]
pub use widget_type;
use zng_app_context::context_local;
use zng_app_proc_macros::widget;
use zng_txt::{Txt, formatx};
use zng_unique_id::{IdEntry, IdMap, IdSet, unique_id_32};
use zng_var::{
AnyVar, AnyVarValue, AnyWhenVarBuilder, ContextInitHandle, IntoValue, IntoVar, Var, VarValue, WeakContextInitHandle, any_const_var,
const_var, contextual_var, impl_from_and_into_var,
};
use super::{
base::{WidgetBase, WidgetExt},
node::{ArcNode, FillUiNode, UiNode, WhenUiNodeBuilder, with_new_context_init_id},
};
#[doc(hidden)]
#[widget($crate::widget::builder::PropertyArgsGetter)]
pub struct PropertyArgsGetter(WidgetBase);
impl PropertyArgsGetter {
pub fn widget_build(&mut self) -> Box<dyn PropertyArgs> {
let mut wgt = self.widget_take();
if !wgt.p.items.is_empty() {
if wgt.p.items.len() > 1 {
tracing::error!("properties ignored, `property_args!` only collects args for first property");
}
match wgt.p.items.remove(0).item {
WidgetItem::Property { args, .. } => args,
WidgetItem::Intrinsic { .. } => unreachable!(),
}
} else if wgt.unset.is_empty() {
panic!("missing property");
} else {
panic!("cannot use `unset!` in `property_args!`")
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize)]
pub struct NestPosition {
pub group: NestGroup,
pub index: u16,
}
impl NestPosition {
pub const INTRINSIC_INDEX: u16 = u16::MAX / 3;
pub const PROPERTY_INDEX: u16 = Self::INTRINSIC_INDEX * 2;
pub fn property(group: NestGroup) -> Self {
NestPosition {
group,
index: Self::PROPERTY_INDEX,
}
}
pub fn intrinsic(group: NestGroup) -> Self {
NestPosition {
group,
index: Self::INTRINSIC_INDEX,
}
}
}
impl fmt::Debug for NestPosition {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
struct IndexName(u16);
impl fmt::Debug for IndexName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.0 {
NestPosition::INTRINSIC_INDEX => write!(f, "INTRINSIC_INDEX"),
NestPosition::PROPERTY_INDEX => write!(f, "PROPERTY_INDEX"),
i => write!(f, "{i}"),
}
}
}
f.debug_struct("NestPosition")
.field("group", &self.group)
.field("index", &IndexName(self.index))
.finish()
}
}
macro_rules! nest_group_items {
() => {
pub const WIDGET: NestGroup = NestGroup(0);
pub const CONTEXT: NestGroup = NestGroup(NestGroup::NEXT_GROUP);
pub const EVENT: NestGroup = NestGroup(NestGroup::CONTEXT.0 + NestGroup::NEXT_GROUP);
pub const LAYOUT: NestGroup = NestGroup(NestGroup::EVENT.0 + NestGroup::NEXT_GROUP);
pub const SIZE: NestGroup = NestGroup(NestGroup::LAYOUT.0 + NestGroup::NEXT_GROUP);
pub const WIDGET_INNER: NestGroup = NestGroup(NestGroup::SIZE.0 + NestGroup::NEXT_GROUP);
pub const BORDER: NestGroup = NestGroup(NestGroup::WIDGET_INNER.0 + NestGroup::NEXT_GROUP);
pub const FILL: NestGroup = NestGroup(NestGroup::BORDER.0 + NestGroup::NEXT_GROUP);
pub const CHILD_CONTEXT: NestGroup = NestGroup(NestGroup::FILL.0 + NestGroup::NEXT_GROUP);
pub const CHILD_LAYOUT: NestGroup = NestGroup(NestGroup::CHILD_CONTEXT.0 + NestGroup::NEXT_GROUP);
pub const CHILD: NestGroup = NestGroup(u16::MAX);
};
}
#[doc(hidden)]
pub mod nest_group_items {
use super::NestGroup;
nest_group_items!();
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct NestGroup(u16);
impl NestGroup {
const NEXT_GROUP: u16 = u16::MAX / 10;
nest_group_items!();
pub const ITEMS: [Self; 11] = [
Self::WIDGET,
Self::CONTEXT,
Self::EVENT,
Self::LAYOUT,
Self::SIZE,
Self::WIDGET_INNER,
Self::BORDER,
Self::FILL,
Self::CHILD_CONTEXT,
Self::CHILD_LAYOUT,
Self::CHILD,
];
fn exact_name(self) -> &'static str {
if self.0 == Self::WIDGET.0 {
"WIDGET"
} else if self.0 == Self::CONTEXT.0 {
"CONTEXT"
} else if self.0 == Self::EVENT.0 {
"EVENT"
} else if self.0 == Self::LAYOUT.0 {
"LAYOUT"
} else if self.0 == Self::SIZE.0 {
"SIZE"
} else if self.0 == Self::WIDGET_INNER.0 {
"WIDGET_INNER"
} else if self.0 == Self::BORDER.0 {
"BORDER"
} else if self.0 == Self::FILL.0 {
"FILL"
} else if self.0 == Self::CHILD_CONTEXT.0 {
"CHILD_CONTEXT"
} else if self.0 == Self::CHILD_LAYOUT.0 {
"CHILD_LAYOUT"
} else if self.0 == Self::CHILD.0 {
"CHILD"
} else {
""
}
}
pub fn name(self) -> Txt {
let name = self.exact_name();
if name.is_empty() {
let closest = Self::ITEMS.into_iter().min_by_key(|i| (self.0 as i32 - i.0 as i32).abs()).unwrap();
let diff = self.0 as i32 - closest.0 as i32;
let name = closest.exact_name();
debug_assert!(!name.is_empty());
formatx!("{closest}{diff:+}")
} else {
Txt::from_static(name)
}
}
}
impl fmt::Debug for NestGroup {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
write!(f, "NestGroup::")?;
}
write!(f, "{}", self.name())
}
}
impl fmt::Display for NestGroup {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name())
}
}
impl ops::Add<i16> for NestGroup {
type Output = Self;
fn add(self, rhs: i16) -> Self::Output {
let r = (self.0 as i32) + rhs as i32;
Self(r.clamp(0, u16::MAX as i32) as u16)
}
}
impl ops::Sub<i16> for NestGroup {
type Output = Self;
fn sub(self, rhs: i16) -> Self::Output {
let r = (self.0 as i32) - rhs as i32;
Self(r.clamp(0, u16::MAX as i32) as u16)
}
}
impl ops::AddAssign<i16> for NestGroup {
fn add_assign(&mut self, rhs: i16) {
*self = *self + rhs;
}
}
impl ops::SubAssign<i16> for NestGroup {
fn sub_assign(&mut self, rhs: i16) {
*self = *self - rhs;
}
}
#[test]
fn nest_group_spacing() {
let mut expected = NestGroup::NEXT_GROUP;
for g in &NestGroup::ITEMS[1..NestGroup::ITEMS.len() - 1] {
assert_eq!(expected, g.0);
expected += NestGroup::NEXT_GROUP;
}
assert_eq!(expected, (u16::MAX / 10) * 10); }
#[derive(serde::Deserialize)]
#[serde(untagged)]
enum NestGroupSerde<'s> {
Named(&'s str),
Unnamed(u16),
}
impl serde::Serialize for NestGroup {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
if serializer.is_human_readable() {
self.name().serialize(serializer)
} else {
self.0.serialize(serializer)
}
}
}
impl<'de> serde::Deserialize<'de> for NestGroup {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::de::Error;
match NestGroupSerde::deserialize(deserializer)? {
NestGroupSerde::Named(n) => match n.parse() {
Ok(g) => Ok(g),
Err(e) => Err(D::Error::custom(e)),
},
NestGroupSerde::Unnamed(i) => Ok(NestGroup(i)),
}
}
}
impl std::str::FromStr for NestGroup {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut name = s;
let mut add = 0i16;
if let Some((n, a)) = s.split_once('+') {
add = a.parse().map_err(|e| format!("{e}"))?;
name = n;
} else if let Some((n, s)) = s.split_once('-') {
add = -s.parse().map_err(|e| format!("{e}"))?;
name = n;
}
match name {
"WIDGET" => Ok(NestGroup::WIDGET + add),
"CONTEXT" => Ok(NestGroup::CONTEXT + add),
"EVENT" => Ok(NestGroup::EVENT + add),
"LAYOUT" => Ok(NestGroup::LAYOUT + add),
"SIZE" => Ok(NestGroup::SIZE + add),
"BORDER" => Ok(NestGroup::BORDER + add),
"FILL" => Ok(NestGroup::FILL + add),
"CHILD_CONTEXT" => Ok(NestGroup::CHILD_CONTEXT + add),
"CHILD_LAYOUT" => Ok(NestGroup::CHILD_LAYOUT + add),
"CHILD" => Ok(NestGroup::CHILD + add),
ukn => Err(format!("unknown nest group {ukn:?}")),
}
}
}
#[derive(PartialEq, Eq, Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
pub enum InputKind {
Var,
Value,
UiNode,
WidgetHandler,
}
#[derive(Clone)]
pub struct ArcWidgetHandler<A: Clone + 'static>(Arc<Mutex<dyn WidgetHandler<A>>>);
impl<A: Clone + 'static> ArcWidgetHandler<A> {
pub fn new(handler: impl WidgetHandler<A>) -> Self {
Self(Arc::new(Mutex::new(handler)))
}
}
impl<A: Clone + 'static> WidgetHandler<A> for ArcWidgetHandler<A> {
#[inline(always)]
fn event(&mut self, args: &A) -> bool {
self.0.lock().event(args)
}
#[inline(always)]
fn update(&mut self) -> bool {
self.0.lock().update()
}
}
pub trait AnyArcWidgetHandler: Any {
fn as_any(&self) -> &dyn Any;
fn into_any(self: Box<Self>) -> Box<dyn Any>;
fn clone_boxed(&self) -> Box<dyn AnyArcWidgetHandler>;
}
impl<A: Clone + 'static> AnyArcWidgetHandler for ArcWidgetHandler<A> {
fn clone_boxed(&self) -> Box<dyn AnyArcWidgetHandler> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn Any {
self
}
fn into_any(self: Box<Self>) -> Box<dyn Any> {
self
}
}
pub struct AnyWhenArcWidgetHandlerBuilder {
default: Box<dyn AnyArcWidgetHandler>,
conditions: Vec<(Var<bool>, Box<dyn AnyArcWidgetHandler>)>,
}
impl AnyWhenArcWidgetHandlerBuilder {
pub fn new(default: Box<dyn AnyArcWidgetHandler>) -> Self {
Self {
default,
conditions: vec![],
}
}
pub fn push(&mut self, condition: Var<bool>, handler: Box<dyn AnyArcWidgetHandler>) {
self.conditions.push((condition, handler));
}
pub fn build<A: Clone + 'static>(self) -> ArcWidgetHandler<A> {
match self.default.into_any().downcast::<ArcWidgetHandler<A>>() {
Ok(default) => {
let mut conditions = Vec::with_capacity(self.conditions.len());
for (c, h) in self.conditions {
match h.into_any().downcast::<ArcWidgetHandler<A>>() {
Ok(h) => conditions.push((c, *h)),
Err(_) => continue,
}
}
ArcWidgetHandler::new(WhenWidgetHandler {
default: *default,
conditions,
})
}
Err(_) => panic!("unexpected build type in widget handler when builder"),
}
}
}
struct WhenWidgetHandler<A: Clone + 'static> {
default: ArcWidgetHandler<A>,
conditions: Vec<(Var<bool>, ArcWidgetHandler<A>)>,
}
impl<A: Clone + 'static> WidgetHandler<A> for WhenWidgetHandler<A> {
fn event(&mut self, args: &A) -> bool {
for (c, h) in &mut self.conditions {
if c.get() {
return h.event(args);
}
}
self.default.event(args)
}
fn update(&mut self) -> bool {
let mut pending = self.default.update();
for (_, h) in &mut self.conditions {
pending |= h.update();
}
pending
}
}
pub type PropertyBuildActions = Vec<Vec<Box<dyn AnyPropertyBuildAction>>>;
pub type PropertyBuildActionsWhenData = Vec<Vec<Option<WhenBuildActionData>>>;
#[non_exhaustive]
pub struct PropertyNewArgs {
pub args: Vec<Box<dyn Any>>,
pub build_actions: PropertyBuildActions,
pub build_actions_when_data: PropertyBuildActionsWhenData,
}
#[derive(Debug, Clone)]
pub struct PropertyInfo {
pub group: NestGroup,
pub capture: bool,
pub id: PropertyId,
pub name: &'static str,
pub location: SourceLocation,
pub default: Option<fn() -> Box<dyn PropertyArgs>>,
pub new: fn(PropertyNewArgs) -> Box<dyn PropertyArgs>,
pub inputs: Box<[PropertyInput]>,
#[doc(hidden)] pub _non_exhaustive: (),
}
impl PropertyInfo {
pub fn input_idx(&self, name: &str) -> Option<usize> {
self.inputs.iter().position(|i| i.name == name)
}
}
#[derive(Debug, Clone)]
pub struct PropertyInput {
pub name: &'static str,
pub kind: InputKind,
pub ty: TypeId,
pub ty_name: &'static str,
#[doc(hidden)] pub _non_exhaustive: (),
}
impl PropertyInput {
pub fn display_ty_name(&self) -> Txt {
pretty_type_name::pretty_type_name_str(self.ty_name).into()
}
}
pub trait PropertyArgs: Send + Sync {
fn clone_boxed(&self) -> Box<dyn PropertyArgs>;
fn property(&self) -> PropertyInfo;
fn var(&self, i: usize) -> &AnyVar {
panic_input(&self.property(), i, InputKind::Var)
}
fn value(&self, i: usize) -> &dyn AnyVarValue {
panic_input(&self.property(), i, InputKind::Value)
}
fn ui_node(&self, i: usize) -> &ArcNode {
panic_input(&self.property(), i, InputKind::UiNode)
}
fn widget_handler(&self, i: usize) -> &dyn AnyArcWidgetHandler {
panic_input(&self.property(), i, InputKind::WidgetHandler)
}
fn instantiate(&self, child: UiNode) -> UiNode;
}
impl dyn PropertyArgs + '_ {
pub fn id(&self) -> PropertyId {
self.property().id
}
pub fn downcast_value<T>(&self, i: usize) -> &T
where
T: VarValue,
{
self.value(i).downcast_ref::<T>().expect("cannot downcast value to type")
}
pub fn downcast_var<T>(&self, i: usize) -> Var<T>
where
T: VarValue,
{
self.var(i)
.clone()
.downcast::<T>()
.unwrap_or_else(|_| panic!("cannot downcast var to type"))
}
pub fn downcast_handler<A>(&self, i: usize) -> &ArcWidgetHandler<A>
where
A: 'static + Clone,
{
self.widget_handler(i)
.as_any()
.downcast_ref::<ArcWidgetHandler<A>>()
.expect("cannot downcast handler to type")
}
pub fn live_debug(&self, i: usize) -> Var<Txt> {
let p = self.property();
match p.inputs[i].kind {
InputKind::Var => self.var(i).map_debug(false),
InputKind::Value => const_var(formatx!("{:?}", self.value(i))),
InputKind::UiNode => const_var(Txt::from_static("UiNode")),
InputKind::WidgetHandler => const_var(formatx!("<impl WidgetHandler<{}>>", p.inputs[i].display_ty_name())),
}
}
pub fn debug(&self, i: usize) -> Txt {
let p = self.property();
match p.inputs[i].kind {
InputKind::Var => formatx!("{:?}", self.var(i).get()),
InputKind::Value => formatx!("{:?}", self.value(i)),
InputKind::UiNode => Txt::from_static("UiNode"),
InputKind::WidgetHandler => formatx!("<impl WidgetHandler<{}>>", p.inputs[i].display_ty_name()),
}
}
pub fn new_build(
&self,
build_actions: PropertyBuildActions,
build_actions_when_data: PropertyBuildActionsWhenData,
) -> Box<dyn PropertyArgs> {
let p = self.property();
let mut args: Vec<Box<dyn Any>> = Vec::with_capacity(p.inputs.len());
for (i, input) in p.inputs.iter().enumerate() {
match input.kind {
InputKind::Var => args.push(Box::new(self.var(i).clone())),
InputKind::Value => args.push(Box::new(self.value(i).clone_boxed())),
InputKind::UiNode => args.push(Box::new(self.ui_node(i).clone())),
InputKind::WidgetHandler => args.push(self.widget_handler(i).clone_boxed().into_any()),
}
}
(p.new)(PropertyNewArgs {
args,
build_actions,
build_actions_when_data,
})
}
}
#[doc(hidden)]
pub fn panic_input(info: &PropertyInfo, i: usize, kind: InputKind) -> ! {
if i > info.inputs.len() {
panic!("index out of bounds, the input len is {}, but the index is {i}", info.inputs.len())
} else if info.inputs[i].kind != kind {
panic!(
"invalid input request `{:?}`, but `{}` is `{:?}`",
kind, info.inputs[i].name, info.inputs[i].kind
)
} else {
panic!("invalid input `{}`", info.inputs[i].name)
}
}
#[doc(hidden)]
pub fn var_to_args<T: VarValue>(var: impl IntoVar<T>) -> Var<T> {
var.into_var()
}
#[doc(hidden)]
pub fn value_to_args<T: VarValue>(value: impl IntoValue<T>) -> T {
value.into()
}
#[doc(hidden)]
pub fn ui_node_to_args(node: impl IntoUiNode) -> ArcNode {
ArcNode::new(node)
}
#[doc(hidden)]
pub fn widget_handler_to_args<A: Clone + 'static>(handler: impl WidgetHandler<A>) -> ArcWidgetHandler<A> {
ArcWidgetHandler::new(handler)
}
#[doc(hidden)]
pub fn iter_input_build_actions<'a>(
actions: &'a PropertyBuildActions,
data: &'a PropertyBuildActionsWhenData,
index: usize,
) -> impl Iterator<Item = (&'a dyn AnyPropertyBuildAction, &'a [Option<WhenBuildActionData>])> {
let mut actions = actions.iter();
let mut data = data.iter();
std::iter::from_fn(move || {
let action = &*actions.next()?[index];
let data = if let Some(data) = data.next() { &data[..] } else { &[] };
Some((action, data))
})
}
fn apply_build_actions<'a, I: Any + Send>(
mut item: I,
mut actions: impl Iterator<Item = (&'a dyn AnyPropertyBuildAction, &'a [Option<WhenBuildActionData>])>,
) -> I {
if let Some((action, data)) = actions.next() {
let action = action
.as_any()
.downcast_ref::<PropertyBuildAction<I>>()
.expect("property build action type did not match expected var type");
item = action.build(PropertyBuildActionArgs {
input: item,
when_conditions_data: data,
});
}
item
}
#[doc(hidden)]
pub fn new_dyn_var<'a, T: VarValue>(
inputs: &mut std::vec::IntoIter<Box<dyn Any>>,
actions: impl Iterator<Item = (&'a dyn AnyPropertyBuildAction, &'a [Option<WhenBuildActionData>])>,
) -> Var<T> {
let item = inputs.next().expect("missing input");
let item = match item.downcast::<AnyWhenVarBuilder>() {
Ok(builder) => builder.into_typed::<T>().build(),
Err(item) => {
let any = *item.downcast::<AnyVar>().expect("input did not match expected var types");
any.downcast::<T>().expect("input did not match expected var types")
}
};
apply_build_actions(item, actions)
}
#[doc(hidden)]
pub fn new_dyn_ui_node<'a>(
inputs: &mut std::vec::IntoIter<Box<dyn Any>>,
actions: impl Iterator<Item = (&'a dyn AnyPropertyBuildAction, &'a [Option<WhenBuildActionData>])>,
) -> ArcNode {
let item = inputs.next().expect("missing input");
let item = match item.downcast::<WhenUiNodeBuilder>() {
Ok(builder) => ArcNode::new(builder.build()),
Err(item) => *item.downcast::<ArcNode>().expect("input did not match expected UiNode types"),
};
apply_build_actions(item, actions)
}
#[doc(hidden)]
pub fn new_dyn_widget_handler<'a, A: Clone + 'static>(
inputs: &mut std::vec::IntoIter<Box<dyn Any>>,
actions: impl Iterator<Item = (&'a dyn AnyPropertyBuildAction, &'a [Option<WhenBuildActionData>])>,
) -> ArcWidgetHandler<A> {
let item = inputs.next().expect("missing input");
let item = match item.downcast::<AnyWhenArcWidgetHandlerBuilder>() {
Ok(builder) => builder.build(),
Err(item) => *item
.downcast::<ArcWidgetHandler<A>>()
.expect("input did not match expected WidgetHandler types"),
};
apply_build_actions(item, actions)
}
#[doc(hidden)]
pub fn new_dyn_other<'a, T: Any + Send>(
inputs: &mut std::vec::IntoIter<Box<dyn Any>>,
actions: impl Iterator<Item = (&'a dyn AnyPropertyBuildAction, &'a [Option<WhenBuildActionData>])>,
) -> T {
let item = *inputs
.next()
.expect("missing input")
.downcast::<T>()
.expect("input did not match expected var type");
apply_build_actions(item, actions)
}
#[derive(Clone, PartialEq)]
pub struct UiNodeInWhenExprError;
impl fmt::Debug for UiNodeInWhenExprError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{self}")
}
}
impl fmt::Display for UiNodeInWhenExprError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "cannot ref `UiNode` in when expression, only var and value properties allowed")
}
}
impl std::error::Error for UiNodeInWhenExprError {}
#[derive(Clone, PartialEq)]
pub struct WidgetHandlerInWhenExprError;
impl fmt::Debug for WidgetHandlerInWhenExprError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{self}")
}
}
impl fmt::Display for WidgetHandlerInWhenExprError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"cannot ref `impl WidgetHandler<A>` in when expression, only var and value properties allowed"
)
}
}
impl std::error::Error for WidgetHandlerInWhenExprError {}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)]
pub struct Importance(pub u32);
impl Importance {
pub const WIDGET: Importance = Importance(1000);
pub const INSTANCE: Importance = Importance(1000 * 10);
}
impl_from_and_into_var! {
fn from(imp: u32) -> Importance {
Importance(imp)
}
}
unique_id_32! {
pub struct PropertyId;
}
zng_unique_id::impl_unique_id_bytemuck!(PropertyId);
impl fmt::Debug for PropertyId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("PropertyId").field(&self.get()).finish()
}
}
#[derive(Clone, Copy, Debug)]
#[non_exhaustive]
pub struct WidgetType {
pub type_id: TypeId,
pub path: &'static str,
pub location: SourceLocation,
}
impl WidgetType {
#[doc(hidden)]
pub fn new(type_id: TypeId, path: &'static str, location: SourceLocation) -> Self {
Self { type_id, path, location }
}
pub fn name(&self) -> &'static str {
self.path.rsplit_once(':').map(|(_, n)| n).unwrap_or(self.path)
}
}
impl PartialEq for WidgetType {
fn eq(&self, other: &Self) -> bool {
self.type_id == other.type_id
}
}
impl Eq for WidgetType {}
impl std::hash::Hash for WidgetType {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.type_id.hash(state);
}
}
#[derive(Clone, Copy, Debug)]
pub enum WhenInputMember {
Named(&'static str),
Index(usize),
}
#[derive(Clone)]
pub struct WhenInput {
pub property: PropertyId,
pub member: WhenInputMember,
pub var: WhenInputVar,
pub property_default: Option<fn() -> Box<dyn PropertyArgs>>,
#[doc(hidden)] pub _non_exhaustive: (),
}
impl fmt::Debug for WhenInput {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("WhenInput")
.field("property", &self.property)
.field("member", &self.member)
.finish_non_exhaustive()
}
}
context_local! {
static WHEN_INPUT_CONTEXT_INIT_ID: ContextInitHandle = ContextInitHandle::no_context();
}
#[derive(Clone)]
pub struct WhenInputVar {
var: Arc<Mutex<Vec<(WeakContextInitHandle, AnyVar)>>>,
}
impl WhenInputVar {
pub fn new<T: VarValue>() -> (Self, Var<T>) {
let arc = Arc::new(Mutex::new(vec![]));
(
WhenInputVar { var: arc.clone() },
contextual_var(move || {
let mut data = arc.lock();
let current_id = WHEN_INPUT_CONTEXT_INIT_ID.get();
let current_id = current_id.downgrade();
let mut r = None;
data.retain(|(id, val)| {
let retain = id.is_alive();
if retain && id == ¤t_id {
r = Some(val.clone());
}
retain
});
match r {
Some(r) => r,
None => {
if !data.is_empty() {
let last = data.len() - 1;
let last = &data[last];
tracing::error!(
"when input not inited for context {:?}, using value from {:?} to avoid crash",
current_id,
last.0
);
last.1.clone()
} else {
panic!("when input not inited for context {current_id:?}")
}
}
}
.downcast()
.expect("incorrect when input var type")
}),
)
}
fn set(&self, handle: WeakContextInitHandle, var: AnyVar) {
let mut data = self.var.lock();
if let Some(i) = data.iter().position(|(i, _)| i == &handle) {
data[i].1 = var;
} else {
data.push((handle, var));
}
}
}
type WhenBuildActionData = Arc<dyn Any + Send + Sync>;
type WhenBuildDefaultAction = Arc<dyn Fn() -> Vec<Box<dyn AnyPropertyBuildAction>> + Send + Sync>;
#[derive(Clone)]
#[non_exhaustive]
pub struct WhenBuildAction {
pub data: WhenBuildActionData,
pub default_action: Option<WhenBuildDefaultAction>,
}
impl WhenBuildAction {
pub fn new<D, F>(data: D, default_action: F) -> Self
where
D: Any + Send + Sync + 'static,
F: Fn() -> Vec<Box<dyn AnyPropertyBuildAction>> + Send + Sync + 'static,
{
Self {
data: Arc::new(data),
default_action: Some(Arc::new(default_action)),
}
}
pub fn new_no_default(data: impl Any + Send + Sync + 'static) -> Self {
Self {
data: Arc::new(data),
default_action: None,
}
}
}
#[derive(Clone)]
#[non_exhaustive]
pub struct WhenInfo {
pub inputs: Box<[WhenInput]>,
pub state: Var<bool>,
pub assigns: Vec<Box<dyn PropertyArgs>>,
pub build_action_data: Vec<((PropertyId, &'static str), WhenBuildAction)>,
pub expr: &'static str,
pub location: SourceLocation,
}
impl fmt::Debug for WhenInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
struct DebugBuildActions<'a>(&'a WhenInfo);
impl fmt::Debug for DebugBuildActions<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries(self.0.build_action_data.iter().map(|(k, _)| k)).finish()
}
}
f.debug_struct("WhenInfo")
.field("inputs", &self.inputs)
.field("state", &self.state.get_debug(false))
.field("assigns", &self.assigns)
.field("build_action_data", &DebugBuildActions(self))
.field("expr", &self.expr)
.finish()
}
}
impl Clone for Box<dyn PropertyArgs> {
fn clone(&self) -> Self {
PropertyArgs::clone_boxed(&**self)
}
}
impl fmt::Debug for &dyn PropertyArgs {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("dyn PropertyArgs")
.field("property", &self.property())
.finish_non_exhaustive()
}
}
impl fmt::Debug for Box<dyn PropertyArgs> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("dyn PropertyArgs")
.field("property", &self.property())
.finish_non_exhaustive()
}
}
#[derive(Clone)]
struct WidgetItemPositioned {
position: NestPosition,
insert_idx: u32,
item: WidgetItem,
}
impl WidgetItemPositioned {
fn sort_key(&self) -> (NestPosition, u32) {
(self.position, self.insert_idx)
}
}
#[derive(Clone, Debug)]
struct WhenItemPositioned {
importance: Importance,
insert_idx: u32,
when: WhenInfo,
}
impl WhenItemPositioned {
fn sort_key(&self) -> (Importance, u32) {
(self.importance, self.insert_idx)
}
}
enum WidgetItem {
Property {
importance: Importance,
args: Box<dyn PropertyArgs>,
captured: bool,
},
Intrinsic {
name: &'static str,
new: Box<dyn FnOnce(UiNode) -> UiNode + Send + Sync>,
},
}
impl Clone for WidgetItem {
fn clone(&self) -> Self {
match self {
Self::Property {
importance,
args,
captured,
} => Self::Property {
importance: *importance,
captured: *captured,
args: args.clone(),
},
Self::Intrinsic { .. } => unreachable!("only WidgetBuilder clones, and it does not insert intrinsic"),
}
}
}
type PropertyBuildActionsMap = HashMap<(PropertyId, &'static str), (Importance, Vec<Box<dyn AnyPropertyBuildAction>>)>;
type PropertyBuildActionsVec = Vec<((PropertyId, &'static str), (Importance, Vec<Box<dyn AnyPropertyBuildAction>>))>;
pub struct WidgetBuilder {
widget_type: WidgetType,
insert_idx: u32,
p: WidgetBuilderProperties,
unset: HashMap<PropertyId, Importance>,
whens: Vec<WhenItemPositioned>,
when_insert_idx: u32,
p_build_actions: PropertyBuildActionsMap,
p_build_actions_unset: HashMap<(PropertyId, &'static str), Importance>,
build_actions: Vec<Arc<Mutex<dyn FnMut(&mut WidgetBuilding) + Send>>>,
custom_build: Option<Arc<Mutex<dyn FnMut(WidgetBuilder) -> UiNode + Send>>>,
}
impl Clone for WidgetBuilder {
fn clone(&self) -> Self {
Self {
widget_type: self.widget_type,
p: WidgetBuilderProperties { items: self.items.clone() },
p_build_actions: self.p_build_actions.clone(),
insert_idx: self.insert_idx,
unset: self.unset.clone(),
p_build_actions_unset: self.p_build_actions_unset.clone(),
whens: self.whens.clone(),
when_insert_idx: self.when_insert_idx,
build_actions: self.build_actions.clone(),
custom_build: self.custom_build.clone(),
}
}
}
impl fmt::Debug for WidgetBuilder {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
struct PropertiesDebug<'a>(&'a WidgetBuilderProperties);
impl fmt::Debug for PropertiesDebug<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries(self.0.properties()).finish()
}
}
f.debug_struct("WidgetBuilder")
.field("widget_type", &self.widget_type)
.field("properties", &PropertiesDebug(&self.p))
.field("unset", &self.unset)
.field("whens", &self.whens)
.field("build_actions.len", &self.build_actions.len())
.field("is_custom_build", &self.is_custom_build())
.finish()
}
}
impl WidgetBuilder {
pub fn new(widget: WidgetType) -> Self {
Self {
widget_type: widget,
p: WidgetBuilderProperties { items: Default::default() },
insert_idx: 0,
unset: Default::default(),
whens: Default::default(),
p_build_actions: Default::default(),
p_build_actions_unset: Default::default(),
when_insert_idx: 0,
build_actions: Default::default(),
custom_build: Default::default(),
}
}
pub fn widget_type(&self) -> WidgetType {
self.widget_type
}
pub fn push_property(&mut self, importance: Importance, args: Box<dyn PropertyArgs>) {
let pos = NestPosition::property(args.property().group);
self.push_property_positioned(importance, pos, args);
}
pub fn push_property_positioned(&mut self, importance: Importance, position: NestPosition, args: Box<dyn PropertyArgs>) {
self.push_property_positioned_impl(importance, position, args, false)
}
fn push_property_positioned_impl(
&mut self,
importance: Importance,
position: NestPosition,
args: Box<dyn PropertyArgs>,
captured: bool,
) {
let insert_idx = self.insert_idx;
self.insert_idx = insert_idx.wrapping_add(1);
let property_id = args.id();
if let Some(i) = self.p.property_index(property_id) {
match &self.p.items[i].item {
WidgetItem::Property { importance: imp, .. } => {
if *imp <= importance {
self.p.items[i] = WidgetItemPositioned {
position,
insert_idx,
item: WidgetItem::Property {
importance,
args,
captured,
},
};
}
}
WidgetItem::Intrinsic { .. } => unreachable!(),
}
} else {
if let Some(imp) = self.unset.get(&property_id)
&& *imp >= importance
{
return; }
self.p.items.push(WidgetItemPositioned {
position,
insert_idx,
item: WidgetItem::Property {
importance,
args,
captured,
},
});
}
}
pub fn push_when(&mut self, importance: Importance, mut when: WhenInfo) {
let insert_idx = self.when_insert_idx;
self.when_insert_idx = insert_idx.wrapping_add(1);
when.assigns.retain(|a| {
if let Some(imp) = self.unset.get(&a.id()) {
*imp < importance
} else {
true
}
});
if !when.assigns.is_empty() {
self.whens.push(WhenItemPositioned {
importance,
insert_idx,
when,
});
}
}
pub fn push_unset(&mut self, importance: Importance, property_id: PropertyId) {
let check;
match self.unset.entry(property_id) {
hash_map::Entry::Occupied(mut e) => {
let i = e.get_mut();
check = *i < importance;
*i = importance;
}
hash_map::Entry::Vacant(e) => {
check = true;
e.insert(importance);
}
}
if check {
if let Some(i) = self.p.property_index(property_id) {
match &self.p.items[i].item {
WidgetItem::Property { importance: imp, .. } => {
if *imp <= importance {
self.p.items.swap_remove(i);
}
}
WidgetItem::Intrinsic { .. } => unreachable!(),
}
}
self.whens.retain_mut(|w| {
if w.importance <= importance {
w.when.assigns.retain(|a| a.id() != property_id);
!w.when.assigns.is_empty()
} else {
true
}
});
}
}
pub fn push_property_build_action(
&mut self,
property_id: PropertyId,
action_name: &'static str,
importance: Importance,
input_actions: Vec<Box<dyn AnyPropertyBuildAction>>,
) {
match self.p_build_actions.entry((property_id, action_name)) {
hash_map::Entry::Occupied(mut e) => {
if e.get().0 < importance {
e.insert((importance, input_actions));
}
}
hash_map::Entry::Vacant(e) => {
if let Some(imp) = self.p_build_actions_unset.get(&(property_id, action_name))
&& *imp >= importance
{
return;
}
e.insert((importance, input_actions));
}
}
}
pub fn push_unset_property_build_action(&mut self, property_id: PropertyId, action_name: &'static str, importance: Importance) {
let mut check = false;
match self.p_build_actions_unset.entry((property_id, action_name)) {
hash_map::Entry::Occupied(mut e) => {
if *e.get() < importance {
e.insert(importance);
check = true;
}
}
hash_map::Entry::Vacant(e) => {
e.insert(importance);
check = true;
}
}
if check {
self.p_build_actions.retain(|_, (imp, _)| *imp > importance);
}
}
pub fn clear_property_build_actions(&mut self) {
self.p_build_actions.clear();
}
pub fn push_build_action(&mut self, action: impl FnMut(&mut WidgetBuilding) + Send + 'static) {
self.build_actions.push(Arc::new(Mutex::new(action)))
}
pub fn clear_build_actions(&mut self) {
self.build_actions.clear();
}
pub fn is_custom_build(&self) -> bool {
self.custom_build.is_some()
}
pub fn set_custom_build(&mut self, build: impl FnMut(WidgetBuilder) -> UiNode + Send + 'static) {
self.custom_build = Some(Arc::new(Mutex::new(build)));
}
pub fn clear_custom_build(&mut self) {
self.custom_build = None;
}
pub fn extend(&mut self, other: WidgetBuilder) {
self.extend_important(other, Importance(0));
}
pub fn extend_important(&mut self, other: WidgetBuilder, min_importance: Importance) {
for (id, imp) in other.unset {
if imp >= min_importance {
self.push_unset(imp, id);
}
}
for ((id, name), imp) in other.p_build_actions_unset {
if imp >= min_importance {
self.push_unset_property_build_action(id, name, imp);
}
}
for WidgetItemPositioned { position, item, .. } in other.p.items {
match item {
WidgetItem::Property {
importance,
args,
captured,
} => {
if importance >= min_importance {
self.push_property_positioned_impl(importance, position, args, captured);
}
}
WidgetItem::Intrinsic { .. } => unreachable!(),
}
}
for w in other.whens {
if w.importance >= min_importance {
self.push_when(w.importance, w.when);
}
}
for ((id, name), (imp, action)) in other.p_build_actions {
if imp >= min_importance {
self.push_property_build_action(id, name, imp, action);
}
}
for act in other.build_actions {
self.build_actions.push(act);
}
if let Some(c) = other.custom_build {
self.custom_build = Some(c);
}
}
pub fn has_properties(&self) -> bool {
!self.p.items.is_empty()
}
pub fn has_unsets(&self) -> bool {
!self.unset.is_empty()
}
pub fn has_whens(&self) -> bool {
!self.whens.is_empty()
}
pub fn split_off(&mut self, properties: impl IntoIterator<Item = PropertyId>, out: &mut WidgetBuilder) {
self.split_off_impl(properties.into_iter().collect(), out)
}
fn split_off_impl(&mut self, properties: IdSet<PropertyId>, out: &mut WidgetBuilder) {
let mut found = 0;
let mut i = 0;
while i < self.items.len() && found < properties.len() {
match &self.items[i].item {
WidgetItem::Property { args, .. } if properties.contains(&args.id()) => match self.items.swap_remove(i) {
WidgetItemPositioned {
position,
item: WidgetItem::Property { importance, args, .. },
..
} => {
out.push_property_positioned(importance, position, args);
found += 1;
}
_ => unreachable!(),
},
_ => {
i += 1;
continue;
}
}
}
i = 0;
while i < self.whens.len() {
let mut ai = 0;
let mut moved_assigns = vec![];
while ai < self.whens[i].when.assigns.len() {
if properties.contains(&self.whens[i].when.assigns[ai].id()) {
let args = self.whens[i].when.assigns.remove(ai);
moved_assigns.push(args);
} else {
ai += 1;
}
}
if !moved_assigns.is_empty() {
let out_imp;
let out_when;
if self.whens[i].when.assigns.is_empty() {
let WhenItemPositioned { importance, mut when, .. } = self.whens.remove(i);
when.assigns = moved_assigns;
out_imp = importance;
out_when = when;
} else {
let WhenItemPositioned { importance, when, .. } = &self.whens[i];
out_imp = *importance;
out_when = WhenInfo {
inputs: when.inputs.clone(),
state: when.state.clone(),
assigns: moved_assigns,
build_action_data: when.build_action_data.clone(),
expr: when.expr,
location: when.location,
};
i += 1;
};
for input in out_when.inputs.iter() {
if let Some(i) = self.property_index(input.property) {
match &self.items[i] {
WidgetItemPositioned {
position,
item: WidgetItem::Property { importance, args, .. },
..
} => {
out.push_property_positioned(*importance, *position, args.clone());
}
_ => unreachable!(),
}
}
}
out.push_when(out_imp, out_when);
} else {
i += 1;
}
}
for id in properties {
if let Some(imp) = self.unset.remove(&id) {
out.push_unset(imp, id);
}
}
}
pub fn build(self) -> UiNode {
if let Some(custom) = self.custom_build.clone() {
match custom.try_lock() {
Some(mut c) => c(self),
None => self.default_build(),
}
} else {
self.default_build()
}
}
pub fn default_build(self) -> UiNode {
#[cfg(feature = "inspector")]
let builder = self.clone();
let mut building = WidgetBuilding {
#[cfg(feature = "inspector")]
builder: Some(builder),
#[cfg(feature = "trace_widget")]
trace_widget: true,
#[cfg(feature = "trace_wgt_item")]
trace_wgt_item: true,
widget_type: self.widget_type,
p: self.p,
child: None,
};
let mut p_build_actions = self.p_build_actions.into_iter().collect();
let mut when_init_context_handle = None;
if !self.whens.is_empty() {
let handle = ContextInitHandle::new();
building.build_whens(self.whens, handle.downgrade(), &mut p_build_actions);
when_init_context_handle = Some(handle);
}
if !p_build_actions.is_empty() {
building.build_p_actions(p_build_actions);
}
for action in self.build_actions {
(action.lock())(&mut building);
}
building.build(when_init_context_handle)
}
}
impl ops::Deref for WidgetBuilder {
type Target = WidgetBuilderProperties;
fn deref(&self) -> &Self::Target {
&self.p
}
}
impl ops::DerefMut for WidgetBuilder {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.p
}
}
pub struct WidgetBuilding {
#[cfg(feature = "inspector")]
builder: Option<WidgetBuilder>,
#[cfg(feature = "trace_widget")]
trace_widget: bool,
#[cfg(feature = "trace_wgt_item")]
trace_wgt_item: bool,
widget_type: WidgetType,
p: WidgetBuilderProperties,
child: Option<UiNode>,
}
impl WidgetBuilding {
pub fn widget_type(&self) -> WidgetType {
self.widget_type
}
pub fn has_child(&self) -> bool {
self.child.is_some()
}
pub fn set_child(&mut self, node: impl IntoUiNode) {
self.child = Some(node.into_node());
}
#[cfg(feature = "inspector")]
pub fn disable_inspector(&mut self) {
self.builder = None;
}
#[cfg(feature = "trace_widget")]
pub fn disable_trace_widget(&mut self) {
self.trace_widget = false;
}
#[cfg(feature = "trace_wgt_item")]
pub fn disable_trace_wgt_item(&mut self) {
self.trace_wgt_item = false;
}
pub fn push_intrinsic(
&mut self,
group: NestGroup,
name: &'static str,
intrinsic: impl FnOnce(UiNode) -> UiNode + Send + Sync + 'static,
) {
self.push_intrinsic_positioned(NestPosition::intrinsic(group), name, intrinsic)
}
pub fn push_intrinsic_positioned(
&mut self,
position: NestPosition,
name: &'static str,
intrinsic: impl FnOnce(UiNode) -> UiNode + Send + Sync + 'static,
) {
self.items.push(WidgetItemPositioned {
position,
insert_idx: u32::MAX,
item: WidgetItem::Intrinsic {
name,
new: Box::new(intrinsic),
},
});
}
pub fn remove_property(&mut self, property_id: PropertyId) -> Option<BuilderProperty> {
if let Some(i) = self.property_index(property_id) {
match self.items.swap_remove(i) {
WidgetItemPositioned {
position,
item:
WidgetItem::Property {
importance,
args,
captured,
},
..
} => Some(BuilderProperty {
importance,
position,
args,
captured,
}),
_ => unreachable!(),
}
} else {
None
}
}
pub fn capture_property(&mut self, property_id: PropertyId) -> Option<BuilderPropertyRef<'_>> {
self.capture_property_impl(property_id)
}
pub fn capture_var<T>(&mut self, property_id: PropertyId) -> Option<Var<T>>
where
T: VarValue,
{
let p = self.capture_property(property_id)?;
let var = p.args.downcast_var::<T>(0).clone();
Some(var)
}
pub fn capture_var_or_else<T, F>(&mut self, property_id: PropertyId, or_else: impl FnOnce() -> F) -> Var<T>
where
T: VarValue,
F: IntoVar<T>,
{
match self.capture_var::<T>(property_id) {
Some(var) => var,
None => or_else().into_var(),
}
}
pub fn capture_var_or_default<T>(&mut self, property_id: PropertyId) -> Var<T>
where
T: VarValue + Default,
{
self.capture_var_or_else(property_id, T::default)
}
pub fn capture_ui_node(&mut self, property_id: PropertyId) -> Option<UiNode> {
let p = self.capture_property(property_id)?;
let node = p.args.ui_node(0).take_on_init();
Some(node)
}
pub fn capture_ui_node_or_else(&mut self, property_id: PropertyId, or_else: impl FnOnce() -> UiNode) -> UiNode {
match self.capture_ui_node(property_id) {
Some(u) => u,
None => or_else(),
}
}
pub fn capture_ui_node_or_nil(&mut self, property_id: PropertyId) -> UiNode {
self.capture_ui_node_or_else(property_id, UiNode::nil)
}
pub fn capture_widget_handler<A: Clone + 'static>(&mut self, property_id: PropertyId) -> Option<ArcWidgetHandler<A>> {
let p = self.capture_property(property_id)?;
let handler = p.args.downcast_handler::<A>(0).clone();
Some(handler)
}
fn build_whens(
&mut self,
mut whens: Vec<WhenItemPositioned>,
when_init_context_id: WeakContextInitHandle,
build_actions: &mut PropertyBuildActionsVec,
) {
whens.sort_unstable_by_key(|w| w.sort_key());
struct Input<'a> {
input: &'a WhenInput,
item_idx: usize,
}
let mut inputs = vec![];
struct Assign {
item_idx: usize,
builder: Vec<Box<dyn Any>>,
when_count: usize,
actions_data: HashMap<&'static str, (Vec<Option<WhenBuildActionData>>, Option<WhenBuildDefaultAction>)>,
}
let mut assigns = IdMap::default();
'when: for WhenItemPositioned { when, .. } in whens.iter().rev() {
let valid_inputs = inputs.len();
let valid_items = self.p.items.len();
for input in when.inputs.iter() {
if let Some(i) = self.property_index(input.property) {
inputs.push(Input { input, item_idx: i })
} else if let Some(default) = input.property_default {
let args = default();
self.p.items.push(WidgetItemPositioned {
position: NestPosition::property(args.property().group),
insert_idx: u32::MAX,
item: WidgetItem::Property {
importance: Importance::WIDGET,
args,
captured: false,
},
});
inputs.push(Input {
input,
item_idx: self.p.items.len() - 1,
});
} else {
inputs.truncate(valid_inputs);
self.p.items.truncate(valid_items);
continue 'when;
}
}
let mut any_assign = false;
'assign: for assign in when.assigns.iter() {
let id = assign.id();
let assign_info;
let i;
if let Some(idx) = self.property_index(id) {
assign_info = assign.property();
i = idx;
} else if let Some(default) = assign.property().default {
let args = default();
assign_info = args.property();
i = self.p.items.len();
self.p.items.push(WidgetItemPositioned {
position: NestPosition::property(args.property().group),
insert_idx: u32::MAX,
item: WidgetItem::Property {
importance: Importance::WIDGET,
args,
captured: false,
},
});
} else {
tracing::warn!(
"property `{}` ignored, it is only set in `when` block and has no default value",
assign.property().name
);
continue;
}
any_assign = true;
let default_args = match &self.items[i].item {
WidgetItem::Property { args, .. } => args,
WidgetItem::Intrinsic { .. } => unreachable!(),
};
let info = default_args.property();
for (default_info, assign_info) in info.inputs.iter().zip(assign_info.inputs.iter()) {
if default_info.ty != assign_info.ty {
continue 'assign;
}
}
let entry = match assigns.entry(id) {
IdEntry::Occupied(e) => e.into_mut(),
IdEntry::Vacant(e) => e.insert(Assign {
item_idx: i,
builder: info
.inputs
.iter()
.enumerate()
.map(|(i, input)| match input.kind {
InputKind::Var => Box::new(AnyWhenVarBuilder::new(default_args.var(i).clone())) as _,
InputKind::UiNode => Box::new(WhenUiNodeBuilder::new(default_args.ui_node(i).take_on_init())) as _,
InputKind::WidgetHandler => {
Box::new(AnyWhenArcWidgetHandlerBuilder::new(default_args.widget_handler(i).clone_boxed())) as _
}
InputKind::Value => panic!("can only assign vars in when blocks"),
})
.collect(),
when_count: 0,
actions_data: Default::default(),
}),
};
entry.when_count += 1;
for (i, (input, entry)) in info.inputs.iter().zip(entry.builder.iter_mut()).enumerate() {
match input.kind {
InputKind::Var => {
let entry = entry.downcast_mut::<AnyWhenVarBuilder>().unwrap();
let value = assign.var(i).clone();
entry.push(when.state.clone(), value);
}
InputKind::UiNode => {
let entry = entry.downcast_mut::<WhenUiNodeBuilder>().unwrap();
let node = assign.ui_node(i).take_on_init();
entry.push(when.state.clone(), node);
}
InputKind::WidgetHandler => {
let entry = entry.downcast_mut::<AnyWhenArcWidgetHandlerBuilder>().unwrap();
let handler = assign.widget_handler(i).clone_boxed();
entry.push(when.state.clone(), handler);
}
InputKind::Value => panic!("cannot assign `Value` in when blocks"),
}
}
for ((property_id, action_key), action) in &when.build_action_data {
if *property_id == id {
match entry.actions_data.entry(*action_key) {
hash_map::Entry::Occupied(mut e) => {
let e = e.get_mut();
for _ in e.0.len()..(entry.when_count - 1) {
e.0.push(None);
}
e.0.push(Some(action.data.clone()));
if action.default_action.is_some() && e.1.is_none() {
e.1.clone_from(&action.default_action);
}
}
hash_map::Entry::Vacant(e) => {
let mut a = Vec::with_capacity(entry.when_count);
for _ in 0..(entry.when_count - 1) {
a.push(None);
}
a.push(Some(action.data.clone()));
e.insert((a, action.default_action.clone()));
}
}
}
}
}
if !any_assign {
inputs.truncate(valid_inputs);
self.p.items.truncate(valid_items);
}
}
for Input { input, item_idx } in inputs {
let args = match &self.items[item_idx].item {
WidgetItem::Property { args, .. } => args,
WidgetItem::Intrinsic { .. } => unreachable!(),
};
let info = args.property();
let member_i = match input.member {
WhenInputMember::Named(name) => info.input_idx(name).expect("when ref named input not found"),
WhenInputMember::Index(i) => i,
};
let actual = match info.inputs[member_i].kind {
InputKind::Var => args.var(member_i).clone(),
InputKind::Value => any_const_var(args.value(member_i).clone_boxed()),
_ => panic!("can only ref var or values in when expr"),
};
input.var.set(when_init_context_id.clone(), actual);
}
for (
_,
Assign {
item_idx,
builder,
when_count,
mut actions_data,
},
) in assigns
{
let args = match &mut self.items[item_idx].item {
WidgetItem::Property { args, .. } => args,
WidgetItem::Intrinsic { .. } => unreachable!(),
};
let mut actions = vec![];
let mut b_actions_data = vec![];
if !build_actions.is_empty() {
let p_id = args.id();
while let Some(i) = build_actions.iter().position(|((id, _), _)| *id == p_id) {
let ((_, action_key), (_, a)) = build_actions.swap_remove(i);
actions.push(a);
if let Some(data) = actions_data.remove(action_key) {
let mut data = data.clone();
for _ in data.0.len()..when_count {
data.0.push(None);
}
b_actions_data.push(data.0);
}
}
}
for (_, (mut data, default)) in actions_data {
if let Some(default) = default {
let action = default();
for _ in data.len()..when_count {
data.push(None);
}
actions.push(action);
b_actions_data.push(data);
}
}
*args = (args.property().new)(PropertyNewArgs {
args: builder,
build_actions: actions,
build_actions_when_data: b_actions_data,
});
}
}
fn build_p_actions(&mut self, mut build_actions: PropertyBuildActionsVec) {
while !build_actions.is_empty() {
let ((p_id, _), (_, a)) = build_actions.swap_remove(0);
let mut actions = vec![a];
while let Some(i) = build_actions.iter().position(|((id, _), _)| *id == p_id) {
let (_, (_, a)) = build_actions.swap_remove(i);
actions.push(a);
}
if let Some(i) = self.property_index(p_id) {
match &mut self.items[i].item {
WidgetItem::Property { args, .. } => *args = args.new_build(actions, vec![]),
WidgetItem::Intrinsic { .. } => unreachable!(),
}
}
}
}
fn build(mut self, when_init_context_handle: Option<ContextInitHandle>) -> UiNode {
self.items.sort_unstable_by_key(|b| b.sort_key());
#[cfg(feature = "inspector")]
let mut inspector_items = Vec::with_capacity(self.p.items.len());
let mut node = self.child.take().unwrap_or_else(|| FillUiNode.into_node());
for WidgetItemPositioned { position, item, .. } in self.p.items.into_iter().rev() {
match item {
WidgetItem::Property { args, captured, .. } => {
if !captured {
#[cfg(debug_assertions)]
{
let p = args.property();
if p.capture {
tracing::error!(
"capture only property `{}` is not captured in `{}!`, it will have no effect",
p.name,
self.widget_type.name()
);
}
}
node = args.instantiate(node);
#[cfg(feature = "trace_wgt_item")]
if self.trace_wgt_item {
let name = args.property().name;
node = node.trace(move |mtd| crate::update::UpdatesTrace::property_span(name, mtd.mtd_name()));
}
}
#[cfg(feature = "inspector")]
{
if args.property().inputs.iter().any(|i| matches!(i.kind, InputKind::Var)) {
node = crate::widget::inspector::actualize_var_info(node, args.id());
}
inspector_items.push(crate::widget::inspector::InstanceItem::Property { args, captured });
}
}
#[allow(unused_variables)] WidgetItem::Intrinsic { new, name } => {
node = new(node);
#[cfg(feature = "trace_wgt_item")]
if self.trace_wgt_item {
node = node.trace(move |mtd| crate::update::UpdatesTrace::intrinsic_span(name, mtd.mtd_name()));
}
#[cfg(feature = "inspector")]
inspector_items.push(crate::widget::inspector::InstanceItem::Intrinsic {
group: position.group,
name,
});
#[cfg(not(feature = "inspector"))]
let _ = position;
}
}
}
#[cfg(feature = "inspector")]
if let Some(builder) = self.builder {
node = crate::widget::inspector::insert_widget_builder_info(
node,
crate::widget::inspector::InspectorInfo {
builder,
items: inspector_items.into_boxed_slice(),
actual_vars: crate::widget::inspector::InspectorActualVars::default(),
},
);
}
#[cfg(feature = "trace_widget")]
if self.trace_widget {
let name = self.widget_type.name();
node = node.trace(move |op| crate::update::UpdatesTrace::widget_span(crate::widget::WIDGET.id(), name, op.mtd_name()));
}
node = with_new_context_init_id(node);
if let Some(handle) = when_init_context_handle {
let mut handle = Some(Arc::new(handle));
node = crate::widget::node::match_node(node, move |c, op| {
WHEN_INPUT_CONTEXT_INIT_ID.with_context(&mut handle, || c.op(op));
});
}
node
}
}
impl ops::Deref for WidgetBuilding {
type Target = WidgetBuilderProperties;
fn deref(&self) -> &Self::Target {
&self.p
}
}
impl ops::DerefMut for WidgetBuilding {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.p
}
}
#[derive(Debug)]
#[non_exhaustive]
pub struct BuilderProperty {
pub importance: Importance,
pub position: NestPosition,
pub args: Box<dyn PropertyArgs>,
pub captured: bool,
}
#[derive(Debug)]
#[non_exhaustive]
pub struct BuilderPropertyRef<'a> {
pub importance: Importance,
pub position: NestPosition,
pub args: &'a dyn PropertyArgs,
pub captured: bool,
}
#[derive(Debug)]
#[non_exhaustive]
pub struct BuilderPropertyMut<'a> {
pub importance: &'a mut Importance,
pub position: &'a mut NestPosition,
pub args: &'a mut Box<dyn PropertyArgs>,
pub captured: &'a mut bool,
}
pub struct WidgetBuilderProperties {
items: Vec<WidgetItemPositioned>,
}
impl WidgetBuilderProperties {
pub fn property(&self, property_id: PropertyId) -> Option<BuilderPropertyRef<'_>> {
match self.property_index(property_id) {
Some(i) => match &self.items[i].item {
WidgetItem::Property {
importance,
args,
captured,
} => Some(BuilderPropertyRef {
importance: *importance,
position: self.items[i].position,
args: &**args,
captured: *captured,
}),
WidgetItem::Intrinsic { .. } => unreachable!(),
},
None => None,
}
}
pub fn property_mut(&mut self, property_id: PropertyId) -> Option<BuilderPropertyMut<'_>> {
match self.property_index(property_id) {
Some(i) => match &mut self.items[i] {
WidgetItemPositioned {
position,
item:
WidgetItem::Property {
importance,
args,
captured,
},
..
} => Some(BuilderPropertyMut {
importance,
position,
args,
captured,
}),
_ => unreachable!(),
},
None => None,
}
}
pub fn properties(&self) -> impl Iterator<Item = BuilderPropertyRef<'_>> {
self.items.iter().filter_map(|it| match &it.item {
WidgetItem::Intrinsic { .. } => None,
WidgetItem::Property {
importance,
args,
captured,
} => Some(BuilderPropertyRef {
importance: *importance,
position: it.position,
args: &**args,
captured: *captured,
}),
})
}
pub fn properties_mut(&mut self) -> impl Iterator<Item = BuilderPropertyMut<'_>> {
self.items.iter_mut().filter_map(|it| match &mut it.item {
WidgetItem::Intrinsic { .. } => None,
WidgetItem::Property {
importance,
args,
captured,
} => Some(BuilderPropertyMut {
importance,
position: &mut it.position,
args,
captured,
}),
})
}
pub fn capture_value<T>(&mut self, property_id: PropertyId) -> Option<T>
where
T: VarValue,
{
let p = self.capture_property_impl(property_id)?;
let value = p.args.downcast_value::<T>(0).clone();
Some(value)
}
pub fn capture_value_or_else<T>(&mut self, property_id: PropertyId, or_else: impl FnOnce() -> T) -> T
where
T: VarValue,
{
match self.capture_value(property_id) {
Some(v) => v,
None => or_else(),
}
}
pub fn capture_value_or_default<T>(&mut self, property_id: PropertyId) -> T
where
T: VarValue + Default,
{
self.capture_value_or_else(property_id, T::default)
}
fn capture_property_impl(&mut self, property_id: PropertyId) -> Option<BuilderPropertyRef<'_>> {
if let Some(i) = self.property_index(property_id) {
match &mut self.items[i] {
WidgetItemPositioned {
position,
item:
WidgetItem::Property {
importance,
args,
captured,
},
..
} => {
*captured = true;
Some(BuilderPropertyRef {
importance: *importance,
position: *position,
args: &**args,
captured: *captured,
})
}
_ => unreachable!(),
}
} else {
None
}
}
fn property_index(&self, property_id: PropertyId) -> Option<usize> {
self.items.iter().position(|it| match &it.item {
WidgetItem::Property { args, .. } => args.id() == property_id,
WidgetItem::Intrinsic { .. } => false,
})
}
}
pub trait AnyPropertyBuildAction: crate::private::Sealed + Any + Send + Sync {
fn as_any(&self) -> &dyn Any;
fn clone_boxed(&self) -> Box<dyn AnyPropertyBuildAction>;
}
#[non_exhaustive]
pub struct PropertyBuildActionArgs<'a, I: Any + Send> {
pub input: I,
pub when_conditions_data: &'a [Option<WhenBuildActionData>],
}
pub struct PropertyBuildAction<I: Any + Send>(Arc<Mutex<dyn FnMut(PropertyBuildActionArgs<I>) -> I + Send>>);
impl<I: Any + Send> crate::private::Sealed for PropertyBuildAction<I> {}
impl<I: Any + Send> Clone for PropertyBuildAction<I> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<I: Any + Send> AnyPropertyBuildAction for PropertyBuildAction<I> {
fn clone_boxed(&self) -> Box<dyn AnyPropertyBuildAction> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn Any {
self
}
}
impl<I: Any + Send> PropertyBuildAction<I> {
pub fn new(build: impl FnMut(PropertyBuildActionArgs<I>) -> I + Send + 'static) -> Self {
Self(Arc::new(Mutex::new(build)))
}
pub fn no_op() -> Self {
Self::new(|i| i.input)
}
pub fn build(&self, args: PropertyBuildActionArgs<I>) -> I {
(self.0.lock())(args)
}
}
impl Clone for Box<dyn AnyPropertyBuildAction> {
fn clone(&self) -> Self {
self.clone_boxed()
}
}
pub struct PropertyInputTypes<Tuple>(std::marker::PhantomData<Tuple>);
impl<Tuple> PropertyInputTypes<Tuple> {
pub const fn unit() -> Self {
Self(std::marker::PhantomData)
}
}
impl<Tuple> Clone for PropertyInputTypes<Tuple> {
fn clone(&self) -> Self {
*self
}
}
impl<Tuple> Copy for PropertyInputTypes<Tuple> {}
unsafe impl<Tuple> Send for PropertyInputTypes<Tuple> {}
unsafe impl<Tuple> Sync for PropertyInputTypes<Tuple> {}