1use std::{
6 any::{Any, TypeId},
7 sync::Arc,
8};
9
10use zng_app::{
11 event::{Command, CommandArgs, CommandHandle, CommandScope, Event, EventArgs},
12 handler::WidgetHandler,
13 render::{FrameBuilder, FrameValueKey},
14 update::WidgetUpdates,
15 widget::{
16 VarLayout, WIDGET, WidgetUpdateMode,
17 border::{BORDER, BORDER_ALIGN_VAR, BORDER_OVER_VAR},
18 info::Interactivity,
19 node::*,
20 },
21 window::WINDOW,
22};
23use zng_app_context::{ContextLocal, LocalContext};
24use zng_layout::{
25 context::LAYOUT,
26 unit::{PxConstraints2d, PxCornerRadius, PxPoint, PxRect, PxSideOffsets, PxSize, PxVector, SideOffsets},
27};
28use zng_state_map::{StateId, StateMapRef, StateValue};
29use zng_var::{types::VecChange, *};
30
31#[doc(hidden)]
32pub use pastey::paste;
33
34#[doc(hidden)]
35pub use zng_app;
36
37pub fn with_context_var<T: VarValue>(child: impl UiNode, context_var: ContextVar<T>, value: impl IntoVar<T>) -> impl UiNode {
120 let value = value.into_var();
121 let mut actual_value = None;
122 let mut id = None;
123
124 match_node(child, move |child, op| {
125 let mut is_deinit = false;
126 match &op {
127 UiNodeOp::Init => {
128 id = Some(ContextInitHandle::new());
129 actual_value = Some(Arc::new(value.clone().actual_var().boxed()));
130 }
131 UiNodeOp::Deinit => {
132 is_deinit = true;
133 }
134 _ => {}
135 }
136
137 context_var.with_context(id.clone().expect("node not inited"), &mut actual_value, || child.op(op));
138
139 if is_deinit {
140 id = None;
141 actual_value = None;
142 }
143 })
144}
145
146pub fn with_context_var_init<T: VarValue>(
155 child: impl UiNode,
156 var: ContextVar<T>,
157 mut init_value: impl FnMut() -> BoxedVar<T> + Send + 'static,
158) -> impl UiNode {
159 let mut id = None;
160 let mut value = None;
161 match_node(child, move |child, op| {
162 let mut is_deinit = false;
163 match &op {
164 UiNodeOp::Init => {
165 id = Some(ContextInitHandle::new());
166 value = Some(Arc::new(init_value().actual_var()));
167 }
168 UiNodeOp::Deinit => {
169 is_deinit = true;
170 }
171 _ => {}
172 }
173
174 var.with_context(id.clone().expect("node not inited"), &mut value, || child.op(op));
175
176 if is_deinit {
177 id = None;
178 value = None;
179 }
180 })
181}
182
183#[macro_export]
290macro_rules! event_property {
291 ($(
292 $(#[$on_event_attrs:meta])*
293 $vis:vis fn $event:ident {
294 event: $EVENT:path,
295 args: $Args:path
296 $(, filter: $filter:expr)?
297 $(, widget_impl: $Wgt:ty)?
298 $(, with: $with:expr)?
299 $(,)?
300 }
301 )+) => {$(
302 $crate::__event_property! {
303 done {
304 sig { $(#[$on_event_attrs])* $vis fn $event { event: $EVENT, args: $Args, } }
305 }
306
307 $(filter: $filter,)?
308 $(widget_impl: $Wgt,)?
309 $(with: $with,)?
310 }
311 )+};
312}
313
314#[doc(hidden)]
315#[macro_export]
316macro_rules! __event_property {
317 (
319 done {
320 $($done:tt)+
321 }
322 filter: $filter:expr,
323 $($rest:tt)*
324 ) => {
325 $crate::__event_property! {
326 done {
327 $($done)+
328 filter { $filter }
329 }
330 $($rest)*
331 }
332 };
333 (
335 done {
336 $($done:tt)+
337 }
338 widget_impl: $Wgt:ty,
339 $($rest:tt)*
340 ) => {
341 $crate::__event_property! {
342 done {
343 $($done)+
344 widget_impl { , widget_impl($Wgt) }
345 }
346 $($rest)*
347 }
348 };
349 (
351 done {
352 $($done:tt)+
353 }
354 with: $with:expr,
355 ) => {
356 $crate::__event_property! {
357 done {
358 $($done)+
359 with { $with }
360 }
361 }
362 };
363 (
365 done {
366 sig { $($sig:tt)+ }
367 }
368 ) => {
369 $crate::__event_property! {
370 done {
371 sig { $($sig)+ }
372 filter { |_args| true }
373 widget_impl { }
374 with { }
375 }
376 }
377 };
378 (
380 done {
381 sig { $($sig:tt)+ }
382 filter { $($filter:tt)+ }
383 }
384 ) => {
385 $crate::__event_property! {
386 done {
387 sig { $($sig)+ }
388 filter { $($filter)+ }
389 widget_impl { }
390 with { }
391 }
392 }
393 };
394 (
396 done {
397 sig { $($sig:tt)+ }
398 widget_impl { $($widget_impl:tt)+ }
399 }
400 ) => {
401 $crate::__event_property! {
402 done {
403 sig { $($sig)+ }
404 filter { |_args| true }
405 widget_impl { $($widget_impl)+ }
406 with { }
407 }
408 }
409 };
410 (
412 done {
413 sig { $($sig:tt)+ }
414 with { $($with:tt)+ }
415 }
416 ) => {
417 $crate::__event_property! {
418 done {
419 sig { $($sig)+ }
420 filter { |_args| true }
421 widget_impl { }
422 with { $($with)+ }
423 }
424 }
425 };
426 (
428 done {
429 sig { $($sig:tt)+ }
430 filter { $($filter:tt)+ }
431 widget_impl { $($widget_impl:tt)+ }
432 }
433 ) => {
434 $crate::__event_property! {
435 done {
436 sig { $($sig)+ }
437 filter { $($filter)+ }
438 widget_impl { $($widget_impl)+ }
439 with { }
440 }
441 }
442 };
443 (
445 done {
446 sig { $($sig:tt)+ }
447 filter { $($filter:tt)+ }
448 with { $($with:tt)+ }
449 }
450 ) => {
451 $crate::__event_property! {
452 done {
453 sig { $($sig)+ }
454 filter { $($filter)+ }
455 widget_impl { }
456 with { $($with)+ }
457 }
458 }
459 };
460 (
462 done {
463 sig { $(#[$on_event_attrs:meta])* $vis:vis fn $event:ident { event: $EVENT:path, args: $Args:path, } }
464 filter { $filter:expr }
465 widget_impl { $($widget_impl:tt)* }
466 with { $($with:expr)? }
467 }
468 ) => {
469 $crate::node::paste! {
470 $(#[$on_event_attrs])*
471 #[doc = "You can preview this event using [`on_pre_"$event "`](fn.on_pre_"$event ".html)."]
475 #[$crate::node::zng_app::widget::property(
481 EVENT,
482 default( $crate::node::zng_app::handler::hn!(|_|{}) )
483 $($widget_impl)*
484 )]
485 $vis fn [<on_ $event>](
486 child: impl $crate::node::zng_app::widget::node::UiNode,
487 handler: impl $crate::node::zng_app::handler::WidgetHandler<$Args>,
488 ) -> impl $crate::node::zng_app::widget::node::UiNode {
489 $crate::__event_property!(=> with($crate::node::on_event(child, $EVENT, $filter, handler), false, $($with)?))
490 }
491
492 #[doc = "Preview [`on_"$event "`](fn.on_"$event ".html) event."]
493 #[$crate::node::zng_app::widget::property(
504 EVENT,
505 default( $crate::node::zng_app::handler::hn!(|_|{}) )
506 $($widget_impl)*
507 )]
508 $vis fn [<on_pre_ $event>](
509 child: impl $crate::node::zng_app::widget::node::UiNode,
510 handler: impl $crate::node::zng_app::handler::WidgetHandler<$Args>,
511 ) -> impl $crate::node::zng_app::widget::node::UiNode {
512 $crate::__event_property!(=> with($crate::node::on_pre_event(child, $EVENT, $filter, handler), true, $($with)?))
513 }
514 }
515 };
516
517 (=> with($child:expr, $preview:expr,)) => { $child };
518 (=> with($child:expr, $preview:expr, $with:expr)) => { ($with)($child, $preview) };
519}
520
521pub fn on_event<C, A, F, H>(child: C, event: Event<A>, filter: F, handler: H) -> impl UiNode
551where
552 C: UiNode,
553 A: EventArgs,
554 F: FnMut(&A) -> bool + Send + 'static,
555 H: WidgetHandler<A>,
556{
557 #[cfg(feature = "dyn_closure")]
558 let filter: Box<dyn FnMut(&A) -> bool + Send> = Box::new(filter);
559 on_event_impl(child.cfg_boxed(), event, filter, handler.cfg_boxed()).cfg_boxed()
560}
561fn on_event_impl<C, A, F, H>(child: C, event: Event<A>, mut filter: F, mut handler: H) -> impl UiNode
562where
563 C: UiNode,
564 A: EventArgs,
565 F: FnMut(&A) -> bool + Send + 'static,
566 H: WidgetHandler<A>,
567{
568 match_node(child, move |child, op| match op {
569 UiNodeOp::Init => {
570 WIDGET.sub_event(&event);
571 }
572 UiNodeOp::Event { update } => {
573 child.event(update);
574
575 if let Some(args) = event.on(update) {
576 if !args.propagation().is_stopped() && filter(args) {
577 #[cfg(all(debug_assertions, not(target_arch = "wasm32")))]
578 let t = std::time::Instant::now();
579 #[cfg(all(debug_assertions, target_arch = "wasm32"))]
580 let t = web_time::Instant::now();
581
582 handler.event(args);
583
584 #[cfg(debug_assertions)]
585 {
586 let t = t.elapsed();
587 if t > std::time::Duration::from_millis(300) {
588 tracing::warn!(
589 "event handler for `{}` in {:?} blocked for {t:?}, consider using `async_hn!`",
590 event.as_any().name(),
591 WIDGET.id()
592 );
593 }
594 }
595 }
596 }
597 }
598 UiNodeOp::Update { updates } => {
599 child.update(updates);
600 handler.update();
601 }
602 _ => {}
603 })
604}
605
606pub fn on_pre_event<C, A, F, H>(child: C, event: Event<A>, filter: F, handler: H) -> impl UiNode
636where
637 C: UiNode,
638 A: EventArgs,
639 F: FnMut(&A) -> bool + Send + 'static,
640 H: WidgetHandler<A>,
641{
642 #[cfg(feature = "dyn_closure")]
643 let filter: Box<dyn FnMut(&A) -> bool + Send> = Box::new(filter);
644 on_pre_event_impl(child.cfg_boxed(), event, filter, handler.cfg_boxed()).cfg_boxed()
645}
646fn on_pre_event_impl<C, A, F, H>(child: C, event: Event<A>, mut filter: F, mut handler: H) -> impl UiNode
647where
648 C: UiNode,
649 A: EventArgs,
650 F: FnMut(&A) -> bool + Send + 'static,
651 H: WidgetHandler<A>,
652{
653 match_node(child, move |_, op| match op {
654 UiNodeOp::Init => {
655 WIDGET.sub_event(&event);
656 }
657 UiNodeOp::Event { update } => {
658 if let Some(args) = event.on(update) {
659 if !args.propagation().is_stopped() && filter(args) {
660 #[cfg(debug_assertions)]
661 let t = std::time::Instant::now();
662
663 handler.event(args);
664
665 #[cfg(debug_assertions)]
666 {
667 let t = t.elapsed();
668 if t > std::time::Duration::from_millis(300) {
669 tracing::warn!(
670 "preview event handler for `{}` in {:?} blocked for {t:?}, consider using `async_hn!`",
671 event.as_any().name(),
672 WIDGET.id()
673 );
674 }
675 }
676 }
677 }
678 }
679 UiNodeOp::Update { .. } => {
680 handler.update();
681 }
682 _ => {}
683 })
684}
685
686#[doc(hidden)]
687#[macro_export]
688macro_rules! __command_property {
689 (
690 $(#[$on_cmd_attrs:meta])*
691 $vis:vis fn $command:ident {
692 cmd { $cmd_init:expr }
693 enabled { $enabled_var:expr }
694 widget_impl { $($widget_impl:tt)* }
695 }
696 ) => { $crate::node::paste! {
697 $(#[$on_cmd_attrs])*
698 #[doc = "You can preview this command event using [`on_pre_"$command "`](fn.on_pre_"$command ".html)."]
702 #[$crate::node::zng_app::widget::property(EVENT, default( $crate::node::zng_app::handler::hn!(|_|{}) ))]
708 $vis fn [<on_ $command>](
709 child: impl $crate::node::zng_app::widget::node::UiNode,
710 handler: impl $crate::node::zng_app::handler::WidgetHandler<$crate::node::zng_app::event::CommandArgs>,
711 ) -> impl $crate::node::zng_app::widget::node::UiNode {
712 $crate::node::on_command(child, || $cmd_init, || $enabled_var, handler)
713 }
714
715 #[doc = "Preview [`on_"$command "`](fn.on_"$command ".html) command."]
716 #[$crate::node::zng_app::widget::property(EVENT, default( $crate::node::zng_app::handler::hn!(|_|{}) ) $($widget_impl)*)]
727 $vis fn [<on_pre_ $command>](
728 child: impl $crate::node::zng_app::widget::node::UiNode,
729 handler: impl $crate::node::zng_app::handler::WidgetHandler<$crate::node::zng_app::event::CommandArgs>,
730 ) -> impl $crate::node::zng_app::widget::node::UiNode {
731 $crate::node::on_pre_command(child, || $cmd_init, || $enabled_var, handler)
732 }
733 } };
734
735 (
736 $(#[$on_cmd_attrs:meta])*
737 $vis:vis fn $command:ident {
738 cmd { $cmd_init:expr }
739 enabled { $enabled_var:expr }
740 widget_impl_ty { $Wgt:ty }
741 }
742 ) => {
743 $crate::__command_property! {
744 $(#[$on_cmd_attrs])*
745 $vis fn $command {
746 cmd { $cmd_init }
747 enabled { $enabled_var }
748 widget_impl { , widget_impl($Wgt) }
749 }
750 }
751 };
752
753 (
754 $(#[$on_cmd_attrs:meta])*
755 $vis:vis fn $command:ident {
756 cmd { $cmd_init:expr }
757 widget_impl_ty { $Wgt:ty }
758 }
759 ) => {
760 $crate::__command_property! {
761 $(#[$on_cmd_attrs])*
762 $vis fn $command {
763 cmd { $cmd_init }
764 enabled { $crate::node::zng_app::var::LocalVar(true) }
765 widget_impl { , widget_impl($Wgt) }
766 }
767 }
768 };
769
770 (
771 $(#[$on_cmd_attrs:meta])*
772 $vis:vis fn $command:ident {
773 cmd { $cmd_init:expr }
774 enabled { $enabled_var:expr }
775 }
776 ) => {
777 $crate::__command_property! {
778 $(#[$on_cmd_attrs])*
779 $vis fn $command {
780 cmd { $cmd_init }
781 enabled { $enabled_var }
782 widget_impl { }
783 }
784 }
785 };
786
787 (
788 $(#[$on_cmd_attrs:meta])*
789 $vis:vis fn $command:ident {
790 cmd { $cmd_init:expr }
791 }
792 ) => {
793 $crate::__command_property! {
794 $(#[$on_cmd_attrs])*
795 $vis fn $command {
796 cmd { $cmd_init }
797 enabled { $crate::node::zng_app::var::LocalVar(true) }
798 widget_impl { }
799 }
800 }
801 };
802}
803
804#[macro_export]
878macro_rules! command_property {
879 ($(
880 $(#[$on_cmd_attrs:meta])*
881 $vis:vis fn $command:ident {
882 cmd: $cmd_init:expr
883 $(, enabled: $enabled_var:expr)?
884 $(, widget_impl: $Wgt:ty)?
885 $(,)?
886 }
887 )+) => {$(
888 $crate::__command_property! {
889 $(#[$on_cmd_attrs])*
890 $vis fn $command {
891 cmd { $cmd_init }
892 $( enabled { $enabled_var } )?
893 $( widget_impl_ty { $Wgt } )?
894 }
895 }
896 )+};
897}
898
899pub fn on_command<U, CB, E, EB, H>(child: U, command_builder: CB, enabled_builder: EB, handler: H) -> impl UiNode
936where
937 U: UiNode,
938 CB: FnMut() -> Command + Send + 'static,
939 E: Var<bool>,
940 EB: FnMut() -> E + Send + 'static,
941 H: WidgetHandler<CommandArgs>,
942{
943 #[cfg(feature = "dyn_closure")]
944 let command_builder: Box<dyn FnMut() -> Command + Send> = Box::new(command_builder);
945 #[cfg(feature = "dyn_closure")]
946 let enabled_builder: Box<dyn FnMut() -> E + Send> = Box::new(enabled_builder);
947
948 on_command_impl(child.boxed(), command_builder, enabled_builder, handler.cfg_boxed()).cfg_boxed()
949}
950fn on_command_impl<U, CB, E, EB, H>(child: U, mut command_builder: CB, mut enabled_builder: EB, handler: H) -> impl UiNode
951where
952 U: UiNode,
953 CB: FnMut() -> Command + Send + 'static,
954 E: Var<bool>,
955 EB: FnMut() -> E + Send + 'static,
956 H: WidgetHandler<CommandArgs>,
957{
958 let mut enabled = None;
959 let mut handle = CommandHandle::dummy();
960 let mut win_handle = CommandHandle::dummy();
961 let mut command = NIL_CMD;
962
963 let mut handler = handler.cfg_boxed();
964
965 match_node(child, move |child, op| match op {
966 UiNodeOp::Init => {
967 child.init();
968
969 let e = enabled_builder();
970 WIDGET.sub_var(&e);
971 let is_enabled = e.get();
972 enabled = Some(e);
973
974 command = command_builder();
975
976 let id = WIDGET.id();
977 handle = command.subscribe_wgt(is_enabled, id);
978 if CommandScope::Widget(id) == command.scope() && WIDGET.parent_id().is_none() {
979 win_handle = command.scoped(WINDOW.id()).subscribe_wgt(is_enabled, id);
981 }
982 }
983 UiNodeOp::Deinit => {
984 child.deinit();
985
986 enabled = None;
987 handle = CommandHandle::dummy();
988 win_handle = CommandHandle::dummy();
989 command = NIL_CMD;
990 }
991
992 UiNodeOp::Event { update } => {
993 child.event(update);
994
995 if let Some(args) = command.on_unhandled(update) {
996 handler.event(args);
997 } else if !win_handle.is_dummy() {
998 if let Some(args) = command.scoped(WINDOW.id()).on_unhandled(update) {
999 handler.event(args);
1000 }
1001 }
1002 }
1003 UiNodeOp::Update { updates } => {
1004 child.update(updates);
1005
1006 handler.update();
1007
1008 if let Some(enabled) = enabled.as_ref().expect("node not inited").get_new() {
1009 handle.set_enabled(enabled);
1010 win_handle.set_enabled(enabled);
1011 }
1012 }
1013
1014 _ => {}
1015 })
1016}
1017
1018zng_app::event::command! {
1019 static NIL_CMD;
1020}
1021
1022pub fn on_pre_command<U, CB, E, EB, H>(child: U, command_builder: CB, enabled_builder: EB, handler: H) -> impl UiNode
1031where
1032 U: UiNode,
1033 CB: FnMut() -> Command + Send + 'static,
1034 E: Var<bool>,
1035 EB: FnMut() -> E + Send + 'static,
1036 H: WidgetHandler<CommandArgs>,
1037{
1038 #[cfg(feature = "dyn_closure")]
1039 let command_builder: Box<dyn FnMut() -> Command + Send> = Box::new(command_builder);
1040 #[cfg(feature = "dyn_closure")]
1041 let enabled_builder: Box<dyn FnMut() -> E + Send> = Box::new(enabled_builder);
1042
1043 on_pre_command_impl(child.cfg_boxed(), command_builder, enabled_builder, handler.cfg_boxed()).cfg_boxed()
1044}
1045fn on_pre_command_impl<U, CB, E, EB, H>(child: U, mut command_builder: CB, mut enabled_builder: EB, handler: H) -> impl UiNode
1046where
1047 U: UiNode,
1048 CB: FnMut() -> Command + Send + 'static,
1049 E: Var<bool>,
1050 EB: FnMut() -> E + Send + 'static,
1051 H: WidgetHandler<CommandArgs>,
1052{
1053 let mut handler = handler.cfg_boxed();
1054
1055 let mut enabled = None;
1056 let mut handle = CommandHandle::dummy();
1057 let mut win_handle = CommandHandle::dummy();
1058 let mut command = NIL_CMD;
1059
1060 match_node(child, move |child, op| match op {
1061 UiNodeOp::Init => {
1062 child.init();
1063
1064 let e = enabled_builder();
1065 WIDGET.sub_var(&e);
1066 let is_enabled = e.get();
1067 enabled = Some(e);
1068
1069 command = command_builder();
1070
1071 let id = WIDGET.id();
1072 handle = command.subscribe_wgt(is_enabled, id);
1073 if CommandScope::Widget(id) == command.scope() && WIDGET.parent_id().is_none() {
1074 win_handle = command.scoped(WINDOW.id()).subscribe_wgt(is_enabled, id);
1076 }
1077 }
1078 UiNodeOp::Deinit => {
1079 child.deinit();
1080
1081 enabled = None;
1082 handle = CommandHandle::dummy();
1083 win_handle = CommandHandle::dummy();
1084 command = NIL_CMD;
1085 }
1086
1087 UiNodeOp::Event { update } => {
1088 if let Some(args) = command.on_unhandled(update) {
1089 handler.event(args);
1090 } else if !win_handle.is_dummy() {
1091 if let Some(args) = command.scoped(WINDOW.id()).on_unhandled(update) {
1092 handler.event(args);
1093 }
1094 }
1095 }
1096 UiNodeOp::Update { .. } => {
1097 handler.update();
1098
1099 if let Some(enabled) = enabled.as_ref().expect("on_pre_command not initialized").get_new() {
1100 handle.set_enabled(enabled);
1101 win_handle.set_enabled(enabled);
1102 }
1103 }
1104
1105 _ => {}
1106 })
1107}
1108
1109pub fn validate_getter_var<T: VarValue>(_var: &impl Var<T>) {
1111 #[cfg(debug_assertions)]
1112 if _var.capabilities().is_always_read_only() {
1113 tracing::error!(
1114 "`is_`, `has_` or `get_` property inited with read-only var in `{}`",
1115 WIDGET.trace_id()
1116 );
1117 }
1118}
1119
1120pub fn event_state<A: EventArgs, S: VarValue>(
1124 child: impl UiNode,
1125 state: impl IntoVar<S>,
1126 default: S,
1127 event: Event<A>,
1128 mut on_event: impl FnMut(&A) -> Option<S> + Send + 'static,
1129) -> impl UiNode {
1130 let state = state.into_var();
1131 match_node(child, move |_, op| match op {
1132 UiNodeOp::Init => {
1133 validate_getter_var(&state);
1134 WIDGET.sub_event(&event);
1135 let _ = state.set(default.clone());
1136 }
1137 UiNodeOp::Deinit => {
1138 let _ = state.set(default.clone());
1139 }
1140 UiNodeOp::Event { update } => {
1141 if let Some(args) = event.on(update) {
1142 if let Some(s) = on_event(args) {
1143 let _ = state.set(s);
1144 }
1145 }
1146 }
1147 _ => {}
1148 })
1149}
1150
1151#[expect(clippy::too_many_arguments)]
1156pub fn event_state2<A0, A1, S0, S1, S>(
1157 child: impl UiNode,
1158 state: impl IntoVar<S>,
1159 default: S,
1160 event0: Event<A0>,
1161 default0: S0,
1162 mut on_event0: impl FnMut(&A0) -> Option<S0> + Send + 'static,
1163 event1: Event<A1>,
1164 default1: S1,
1165 mut on_event1: impl FnMut(&A1) -> Option<S1> + Send + 'static,
1166 mut merge: impl FnMut(S0, S1) -> Option<S> + Send + 'static,
1167) -> impl UiNode
1168where
1169 A0: EventArgs,
1170 A1: EventArgs,
1171 S0: VarValue,
1172 S1: VarValue,
1173 S: VarValue,
1174{
1175 let state = state.into_var();
1176 let partial_default = (default0, default1);
1177 let mut partial = partial_default.clone();
1178
1179 match_node(child, move |child, op| match op {
1180 UiNodeOp::Init => {
1181 validate_getter_var(&state);
1182 WIDGET.sub_event(&event0).sub_event(&event1);
1183
1184 partial = partial_default.clone();
1185 let _ = state.set(default.clone());
1186 }
1187 UiNodeOp::Deinit => {
1188 let _ = state.set(default.clone());
1189 }
1190 UiNodeOp::Event { update } => {
1191 let mut updated = false;
1192 if let Some(args) = event0.on(update) {
1193 if let Some(state) = on_event0(args) {
1194 if partial.0 != state {
1195 partial.0 = state;
1196 updated = true;
1197 }
1198 }
1199 } else if let Some(args) = event1.on(update) {
1200 if let Some(state) = on_event1(args) {
1201 if partial.1 != state {
1202 partial.1 = state;
1203 updated = true;
1204 }
1205 }
1206 }
1207 child.event(update);
1208
1209 if updated {
1210 if let Some(value) = merge(partial.0.clone(), partial.1.clone()) {
1211 let _ = state.set(value);
1212 }
1213 }
1214 }
1215 _ => {}
1216 })
1217}
1218
1219#[expect(clippy::too_many_arguments)]
1224pub fn event_state3<A0, A1, A2, S0, S1, S2, S>(
1225 child: impl UiNode,
1226 state: impl IntoVar<S>,
1227 default: S,
1228 event0: Event<A0>,
1229 default0: S0,
1230 mut on_event0: impl FnMut(&A0) -> Option<S0> + Send + 'static,
1231 event1: Event<A1>,
1232 default1: S1,
1233 mut on_event1: impl FnMut(&A1) -> Option<S1> + Send + 'static,
1234 event2: Event<A2>,
1235 default2: S2,
1236 mut on_event2: impl FnMut(&A2) -> Option<S2> + Send + 'static,
1237 mut merge: impl FnMut(S0, S1, S2) -> Option<S> + Send + 'static,
1238) -> impl UiNode
1239where
1240 A0: EventArgs,
1241 A1: EventArgs,
1242 A2: EventArgs,
1243 S0: VarValue,
1244 S1: VarValue,
1245 S2: VarValue,
1246 S: VarValue,
1247{
1248 let state = state.into_var();
1249 let partial_default = (default0, default1, default2);
1250 let mut partial = partial_default.clone();
1251
1252 match_node(child, move |child, op| match op {
1253 UiNodeOp::Init => {
1254 validate_getter_var(&state);
1255 WIDGET.sub_event(&event0).sub_event(&event1).sub_event(&event2);
1256
1257 partial = partial_default.clone();
1258 let _ = state.set(default.clone());
1259 }
1260 UiNodeOp::Deinit => {
1261 let _ = state.set(default.clone());
1262 }
1263 UiNodeOp::Event { update } => {
1264 let mut updated = false;
1265 if let Some(args) = event0.on(update) {
1266 if let Some(state) = on_event0(args) {
1267 if partial.0 != state {
1268 partial.0 = state;
1269 updated = true;
1270 }
1271 }
1272 } else if let Some(args) = event1.on(update) {
1273 if let Some(state) = on_event1(args) {
1274 if partial.1 != state {
1275 partial.1 = state;
1276 updated = true;
1277 }
1278 }
1279 } else if let Some(args) = event2.on(update) {
1280 if let Some(state) = on_event2(args) {
1281 if partial.2 != state {
1282 partial.2 = state;
1283 updated = true;
1284 }
1285 }
1286 }
1287 child.event(update);
1288
1289 if updated {
1290 if let Some(value) = merge(partial.0.clone(), partial.1.clone(), partial.2.clone()) {
1291 let _ = state.set(value);
1292 }
1293 }
1294 }
1295 _ => {}
1296 })
1297}
1298
1299#[expect(clippy::too_many_arguments)]
1304pub fn event_state4<A0, A1, A2, A3, S0, S1, S2, S3, S>(
1305 child: impl UiNode,
1306 state: impl IntoVar<S>,
1307 default: S,
1308 event0: Event<A0>,
1309 default0: S0,
1310 mut on_event0: impl FnMut(&A0) -> Option<S0> + Send + 'static,
1311 event1: Event<A1>,
1312 default1: S1,
1313 mut on_event1: impl FnMut(&A1) -> Option<S1> + Send + 'static,
1314 event2: Event<A2>,
1315 default2: S2,
1316 mut on_event2: impl FnMut(&A2) -> Option<S2> + Send + 'static,
1317 event3: Event<A3>,
1318 default3: S3,
1319 mut on_event3: impl FnMut(&A3) -> Option<S3> + Send + 'static,
1320 mut merge: impl FnMut(S0, S1, S2, S3) -> Option<S> + Send + 'static,
1321) -> impl UiNode
1322where
1323 A0: EventArgs,
1324 A1: EventArgs,
1325 A2: EventArgs,
1326 A3: EventArgs,
1327 S0: VarValue,
1328 S1: VarValue,
1329 S2: VarValue,
1330 S3: VarValue,
1331 S: VarValue,
1332{
1333 let state = state.into_var();
1334 let partial_default = (default0, default1, default2, default3);
1335 let mut partial = partial_default.clone();
1336
1337 match_node(child, move |child, op| match op {
1338 UiNodeOp::Init => {
1339 validate_getter_var(&state);
1340 WIDGET.sub_event(&event0).sub_event(&event1).sub_event(&event2).sub_event(&event3);
1341
1342 partial = partial_default.clone();
1343 let _ = state.set(default.clone());
1344 }
1345 UiNodeOp::Deinit => {
1346 let _ = state.set(default.clone());
1347 }
1348 UiNodeOp::Event { update } => {
1349 let mut updated = false;
1350 if let Some(args) = event0.on(update) {
1351 if let Some(state) = on_event0(args) {
1352 if partial.0 != state {
1353 partial.0 = state;
1354 updated = true;
1355 }
1356 }
1357 } else if let Some(args) = event1.on(update) {
1358 if let Some(state) = on_event1(args) {
1359 if partial.1 != state {
1360 partial.1 = state;
1361 updated = true;
1362 }
1363 }
1364 } else if let Some(args) = event2.on(update) {
1365 if let Some(state) = on_event2(args) {
1366 if partial.2 != state {
1367 partial.2 = state;
1368 updated = true;
1369 }
1370 }
1371 } else if let Some(args) = event3.on(update) {
1372 if let Some(state) = on_event3(args) {
1373 if partial.3 != state {
1374 partial.3 = state;
1375 updated = true;
1376 }
1377 }
1378 }
1379 child.event(update);
1380
1381 if updated {
1382 if let Some(value) = merge(partial.0.clone(), partial.1.clone(), partial.2.clone(), partial.3.clone()) {
1383 let _ = state.set(value);
1384 }
1385 }
1386 }
1387 _ => {}
1388 })
1389}
1390
1391pub fn bind_state<T: VarValue>(child: impl UiNode, source: impl IntoVar<T>, state: impl IntoVar<T>) -> impl UiNode {
1396 let source = source.into_var();
1397 let state = state.into_var();
1398 let mut _binding = VarHandle::dummy();
1399
1400 match_node(child, move |_, op| match op {
1401 UiNodeOp::Init => {
1402 validate_getter_var(&state);
1403 let _ = state.set_from(&source);
1404 _binding = source.bind(&state);
1405 }
1406 UiNodeOp::Deinit => {
1407 _binding = VarHandle::dummy();
1408 }
1409 _ => {}
1410 })
1411}
1412
1413pub fn bind_state_init<T, V>(child: impl UiNode, source: impl Fn() -> V + Send + 'static, state: impl IntoVar<T>) -> impl UiNode
1419where
1420 T: VarValue,
1421 V: Var<T>,
1422{
1423 let state = state.into_var();
1424 let mut _source_var = None;
1425 let mut _binding = VarHandle::dummy();
1426
1427 match_node(child, move |_, op| match op {
1428 UiNodeOp::Init => {
1429 validate_getter_var(&state);
1430 let source = source();
1431 let _ = state.set_from(&source);
1432 _binding = source.bind(&state);
1433 _source_var = Some(source);
1434 }
1435 UiNodeOp::Deinit => {
1436 _binding = VarHandle::dummy();
1437 _source_var = None;
1438 }
1439 _ => {}
1440 })
1441}
1442
1443pub fn widget_state_is_state(
1448 child: impl UiNode,
1449 predicate: impl Fn(StateMapRef<WIDGET>) -> bool + Send + 'static,
1450 deinit: impl Fn(StateMapRef<WIDGET>) -> bool + Send + 'static,
1451 state: impl IntoVar<bool>,
1452) -> impl UiNode {
1453 let state = state.into_var();
1454
1455 match_node(child, move |child, op| match op {
1456 UiNodeOp::Init => {
1457 validate_getter_var(&state);
1458 child.init();
1459 let s = WIDGET.with_state(&predicate);
1460 if s != state.get() {
1461 let _ = state.set(s);
1462 }
1463 }
1464 UiNodeOp::Deinit => {
1465 child.deinit();
1466 let s = WIDGET.with_state(&deinit);
1467 if s != state.get() {
1468 let _ = state.set(s);
1469 }
1470 }
1471 UiNodeOp::Update { updates } => {
1472 child.update(updates);
1473 let s = WIDGET.with_state(&predicate);
1474 if s != state.get() {
1475 let _ = state.set(s);
1476 }
1477 }
1478 _ => {}
1479 })
1480}
1481
1482pub fn widget_state_get_state<T: VarValue>(
1487 child: impl UiNode,
1488 get_new: impl Fn(StateMapRef<WIDGET>, &T) -> Option<T> + Send + 'static,
1489 get_deinit: impl Fn(StateMapRef<WIDGET>, &T) -> Option<T> + Send + 'static,
1490 state: impl IntoVar<T>,
1491) -> impl UiNode {
1492 let state = state.into_var();
1493 match_node(child, move |child, op| match op {
1494 UiNodeOp::Init => {
1495 validate_getter_var(&state);
1496 child.init();
1497 let new = state.with(|s| WIDGET.with_state(|w| get_new(w, s)));
1498 if let Some(new) = new {
1499 let _ = state.set(new);
1500 }
1501 }
1502 UiNodeOp::Deinit => {
1503 child.deinit();
1504
1505 let new = state.with(|s| WIDGET.with_state(|w| get_deinit(w, s)));
1506 if let Some(new) = new {
1507 let _ = state.set(new);
1508 }
1509 }
1510 UiNodeOp::Update { updates } => {
1511 child.update(updates);
1512 let new = state.with(|s| WIDGET.with_state(|w| get_new(w, s)));
1513 if let Some(new) = new {
1514 let _ = state.set(new);
1515 }
1516 }
1517 _ => {}
1518 })
1519}
1520
1521pub fn fill_node(content: impl UiNode) -> impl UiNode {
1527 let mut clip_bounds = PxSize::zero();
1528 let mut clip_corners = PxCornerRadius::zero();
1529
1530 let mut offset = PxVector::zero();
1531 let offset_key = FrameValueKey::new_unique();
1532 let mut define_frame = false;
1533
1534 match_node(content, move |child, op| match op {
1535 UiNodeOp::Init => {
1536 WIDGET.sub_var_layout(&BORDER_ALIGN_VAR);
1537 define_frame = false;
1538 offset = PxVector::zero();
1539 }
1540 UiNodeOp::Measure { desired_size, .. } => {
1541 let offsets = BORDER.inner_offsets();
1542 let align = BORDER_ALIGN_VAR.get();
1543
1544 let our_offsets = offsets * align;
1545 let size_offset = offsets - our_offsets;
1546
1547 let size_increase = PxSize::new(size_offset.horizontal(), size_offset.vertical());
1548
1549 *desired_size = LAYOUT.constraints().fill_size() + size_increase;
1550 }
1551 UiNodeOp::Layout { wl, final_size } => {
1552 let (bounds, corners) = BORDER.fill_bounds();
1557
1558 let mut new_offset = bounds.origin.to_vector();
1559
1560 if clip_bounds != bounds.size || clip_corners != corners {
1561 clip_bounds = bounds.size;
1562 clip_corners = corners;
1563 WIDGET.render();
1564 }
1565
1566 let (_, branch_offset) = LAYOUT.with_constraints(PxConstraints2d::new_exact_size(bounds.size), || {
1567 wl.with_branch_child(|wl| child.layout(wl))
1568 });
1569 new_offset += branch_offset;
1570
1571 if offset != new_offset {
1572 offset = new_offset;
1573
1574 if define_frame {
1575 WIDGET.render_update();
1576 } else {
1577 define_frame = true;
1578 WIDGET.render();
1579 }
1580 }
1581
1582 *final_size = bounds.size;
1583 }
1584 UiNodeOp::Render { frame } => {
1585 let mut render = |frame: &mut FrameBuilder| {
1586 let bounds = PxRect::from_size(clip_bounds);
1587 frame.push_clips(
1588 |c| {
1589 if clip_corners != PxCornerRadius::zero() {
1590 c.push_clip_rounded_rect(bounds, clip_corners, false, false);
1591 } else {
1592 c.push_clip_rect(bounds, false, false);
1593 }
1594
1595 if let Some(inline) = WIDGET.bounds().inline() {
1596 for r in inline.negative_space().iter() {
1597 c.push_clip_rect(*r, true, false);
1598 }
1599 }
1600 },
1601 |f| child.render(f),
1602 );
1603 };
1604
1605 if define_frame {
1606 frame.push_reference_frame(offset_key.into(), offset_key.bind(offset.into(), false), true, false, |frame| {
1607 render(frame);
1608 });
1609 } else {
1610 render(frame);
1611 }
1612 }
1613 UiNodeOp::RenderUpdate { update } => {
1614 if define_frame {
1615 update.with_transform(offset_key.update(offset.into(), false), false, |update| {
1616 child.render_update(update);
1617 });
1618 } else {
1619 child.render_update(update);
1620 }
1621 }
1622 _ => {}
1623 })
1624}
1625
1626pub fn border_node(child: impl UiNode, border_offsets: impl IntoVar<SideOffsets>, border_visual: impl UiNode) -> impl UiNode {
1631 let offsets = border_offsets.into_var();
1632 let mut render_offsets = PxSideOffsets::zero();
1633 let mut border_rect = PxRect::zero();
1634
1635 match_node_list(ui_vec![child, border_visual], move |children, op| match op {
1636 UiNodeOp::Init => {
1637 WIDGET.sub_var_layout(&offsets).sub_var_render(&BORDER_OVER_VAR);
1638 }
1639 UiNodeOp::Measure { wm, desired_size } => {
1640 let offsets = offsets.layout();
1641 *desired_size = BORDER.measure_border(offsets, || {
1642 LAYOUT.with_sub_size(PxSize::new(offsets.horizontal(), offsets.vertical()), || {
1643 children.with_node(0, |n| wm.measure_block(n))
1644 })
1645 });
1646 }
1647 UiNodeOp::Layout { wl, final_size } => {
1648 let offsets = offsets.layout();
1656 if render_offsets != offsets {
1657 render_offsets = offsets;
1658 WIDGET.render();
1659 }
1660
1661 let parent_offsets = BORDER.inner_offsets();
1662 let origin = PxPoint::new(parent_offsets.left, parent_offsets.top);
1663 if border_rect.origin != origin {
1664 border_rect.origin = origin;
1665 WIDGET.render();
1666 }
1667
1668 BORDER.layout_border(offsets, || {
1670 wl.translate(PxVector::new(offsets.left, offsets.top));
1671
1672 let taken_size = PxSize::new(offsets.horizontal(), offsets.vertical());
1673 border_rect.size = LAYOUT.with_sub_size(taken_size, || children.with_node(0, |n| n.layout(wl)));
1674
1675 LAYOUT.with_constraints(PxConstraints2d::new_exact_size(border_rect.size), || {
1677 BORDER.with_border_layout(border_rect, offsets, || {
1678 children.with_node(1, |n| n.layout(wl));
1679 });
1680 });
1681 });
1682
1683 *final_size = border_rect.size;
1684 }
1685 UiNodeOp::Render { frame } => {
1686 if BORDER_OVER_VAR.get() {
1687 children.with_node(0, |c| c.render(frame));
1688 BORDER.with_border_layout(border_rect, render_offsets, || {
1689 children.with_node(1, |c| c.render(frame));
1690 });
1691 } else {
1692 BORDER.with_border_layout(border_rect, render_offsets, || {
1693 children.with_node(1, |c| c.render(frame));
1694 });
1695 children.with_node(0, |c| c.render(frame));
1696 }
1697 }
1698 UiNodeOp::RenderUpdate { update } => {
1699 children.with_node(0, |c| c.render_update(update));
1700 BORDER.with_border_layout(border_rect, render_offsets, || {
1701 children.with_node(1, |c| c.render_update(update));
1702 })
1703 }
1704 _ => {}
1705 })
1706}
1707
1708pub fn with_context_local<T: Any + Send + Sync + 'static>(
1714 child: impl UiNode,
1715 context: &'static ContextLocal<T>,
1716 value: impl Into<T>,
1717) -> impl UiNode {
1718 let mut value = Some(Arc::new(value.into()));
1719
1720 match_node(child, move |child, op| {
1721 context.with_context(&mut value, || child.op(op));
1722 })
1723}
1724
1725pub fn with_context_local_init<T: Any + Send + Sync + 'static>(
1734 child: impl UiNode,
1735 context: &'static ContextLocal<T>,
1736 init_value: impl FnMut() -> T + Send + 'static,
1737) -> impl UiNode {
1738 #[cfg(feature = "dyn_closure")]
1739 let init_value: Box<dyn FnMut() -> T + Send> = Box::new(init_value);
1740 with_context_local_init_impl(child.cfg_boxed(), context, init_value).cfg_boxed()
1741}
1742fn with_context_local_init_impl<T: Any + Send + Sync + 'static>(
1743 child: impl UiNode,
1744 context: &'static ContextLocal<T>,
1745 mut init_value: impl FnMut() -> T + Send + 'static,
1746) -> impl UiNode {
1747 let mut value = None;
1748
1749 match_node(child, move |child, op| {
1750 let mut is_deinit = false;
1751 match &op {
1752 UiNodeOp::Init => {
1753 value = Some(Arc::new(init_value()));
1754 }
1755 UiNodeOp::Deinit => {
1756 is_deinit = true;
1757 }
1758 _ => {}
1759 }
1760
1761 context.with_context(&mut value, || child.op(op));
1762
1763 if is_deinit {
1764 value = None;
1765 }
1766 })
1767}
1768
1769pub fn with_context_blend(mut ctx: LocalContext, over: bool, child: impl UiNode) -> impl UiNode {
1799 match_widget(child, move |c, op| {
1800 if let UiNodeOp::Init = op {
1801 let init_app = LocalContext::current_app();
1802 ctx.with_context_blend(over, || {
1803 let ctx_app = LocalContext::current_app();
1804 assert_eq!(init_app, ctx_app);
1805 c.op(op)
1806 });
1807 } else {
1808 ctx.with_context_blend(over, || c.op(op));
1809 }
1810 })
1811}
1812
1813pub fn with_widget_state<U, I, T>(child: U, id: impl Into<StateId<T>>, default: I, value: impl IntoVar<T>) -> impl UiNode
1850where
1851 U: UiNode,
1852 I: Fn() -> T + Send + 'static,
1853 T: StateValue + VarValue,
1854{
1855 #[cfg(feature = "dyn_closure")]
1856 let default: Box<dyn Fn() -> T + Send> = Box::new(default);
1857 with_widget_state_impl(child.cfg_boxed(), id.into(), default, value.into_var()).cfg_boxed()
1858}
1859fn with_widget_state_impl<U, I, T>(child: U, id: impl Into<StateId<T>>, default: I, value: impl IntoVar<T>) -> impl UiNode
1860where
1861 U: UiNode,
1862 I: Fn() -> T + Send + 'static,
1863 T: StateValue + VarValue,
1864{
1865 let id = id.into();
1866 let value = value.into_var();
1867
1868 match_node(child, move |child, op| match op {
1869 UiNodeOp::Init => {
1870 child.init();
1871 WIDGET.sub_var(&value);
1872 WIDGET.set_state(id, value.get());
1873 }
1874 UiNodeOp::Deinit => {
1875 child.deinit();
1876 WIDGET.set_state(id, default());
1877 }
1878 UiNodeOp::Update { updates } => {
1879 child.update(updates);
1880 if let Some(v) = value.get_new() {
1881 WIDGET.set_state(id, v);
1882 }
1883 }
1884 _ => {}
1885 })
1886}
1887
1888pub fn with_widget_state_modify<U, S, V, I, M>(
1896 child: U,
1897 id: impl Into<StateId<S>>,
1898 value: impl IntoVar<V>,
1899 default: I,
1900 modify: M,
1901) -> impl UiNode
1902where
1903 U: UiNode,
1904 S: StateValue,
1905 V: VarValue,
1906 I: Fn() -> S + Send + 'static,
1907 M: FnMut(&mut S, &V) + Send + 'static,
1908{
1909 #[cfg(feature = "dyn_closure")]
1910 let default: Box<dyn Fn() -> S + Send> = Box::new(default);
1911 #[cfg(feature = "dyn_closure")]
1912 let modify: Box<dyn FnMut(&mut S, &V) + Send> = Box::new(modify);
1913
1914 with_widget_state_modify_impl(child.cfg_boxed(), id.into(), value.into_var(), default, modify)
1915}
1916fn with_widget_state_modify_impl<U, S, V, I, M>(
1917 child: U,
1918 id: impl Into<StateId<S>>,
1919 value: impl IntoVar<V>,
1920 default: I,
1921 mut modify: M,
1922) -> impl UiNode
1923where
1924 U: UiNode,
1925 S: StateValue,
1926 V: VarValue,
1927 I: Fn() -> S + Send + 'static,
1928 M: FnMut(&mut S, &V) + Send + 'static,
1929{
1930 let id = id.into();
1931 let value = value.into_var();
1932
1933 match_node(child, move |child, op| match op {
1934 UiNodeOp::Init => {
1935 child.init();
1936
1937 WIDGET.sub_var(&value);
1938
1939 value.with(|v| {
1940 WIDGET.with_state_mut(|mut s| {
1941 modify(s.entry(id).or_insert_with(&default), v);
1942 })
1943 })
1944 }
1945 UiNodeOp::Deinit => {
1946 child.deinit();
1947
1948 WIDGET.set_state(id, default());
1949 }
1950 UiNodeOp::Update { updates } => {
1951 child.update(updates);
1952 value.with_new(|v| {
1953 WIDGET.with_state_mut(|mut s| {
1954 modify(s.req_mut(id), v);
1955 })
1956 });
1957 }
1958 _ => {}
1959 })
1960}
1961
1962pub fn interactive_node(child: impl UiNode, interactive: impl IntoVar<bool>) -> impl UiNode {
1974 let interactive = interactive.into_var();
1975
1976 match_node(child, move |child, op| match op {
1977 UiNodeOp::Init => {
1978 WIDGET.sub_var_info(&interactive);
1979 }
1980 UiNodeOp::Info { info } => {
1981 if interactive.get() {
1982 child.info(info);
1983 } else if let Some(id) = child.with_context(WidgetUpdateMode::Ignore, || WIDGET.id()) {
1984 info.push_interactivity_filter(move |args| {
1986 if args.info.id() == id {
1987 Interactivity::BLOCKED
1988 } else {
1989 Interactivity::ENABLED
1990 }
1991 });
1992 child.info(info);
1993 } else {
1994 let block_range = info.with_children_range(|info| child.info(info));
1995 if !block_range.is_empty() {
1996 let id = WIDGET.id();
1999 info.push_interactivity_filter(move |args| {
2000 if let Some(parent) = args.info.parent() {
2001 if parent.id() == id {
2002 for (i, item) in parent.children().enumerate() {
2004 if item == args.info {
2005 return if !block_range.contains(&i) {
2006 Interactivity::ENABLED
2007 } else {
2008 Interactivity::BLOCKED
2009 };
2010 } else if i >= block_range.end {
2011 break;
2012 }
2013 }
2014 }
2015 }
2016 Interactivity::ENABLED
2017 });
2018 }
2019 }
2020 }
2021 _ => {}
2022 })
2023}
2024
2025pub fn with_index_node(
2029 child: impl UiNode,
2030 panel_list_id: impl Into<StateId<PanelListRange>>,
2031 mut update: impl FnMut(Option<usize>) + Send + 'static,
2032) -> impl UiNode {
2033 let panel_list_id = panel_list_id.into();
2034 let mut version = None;
2035 match_node(child, move |_, op| match op {
2036 UiNodeOp::Deinit => {
2037 update(None);
2038 version = None;
2039 }
2040 UiNodeOp::Update { .. } => {
2041 let info = WIDGET.info();
2043 if let Some(parent) = info.parent() {
2044 if let Some(mut c) = PanelListRange::update(&parent, panel_list_id, &mut version) {
2045 let id = info.id();
2046 let p = c.position(|w| w.id() == id);
2047 update(p);
2048 }
2049 }
2050 }
2051 _ => {}
2052 })
2053}
2054
2055pub fn with_rev_index_node(
2059 child: impl UiNode,
2060 panel_list_id: impl Into<StateId<PanelListRange>>,
2061 mut update: impl FnMut(Option<usize>) + Send + 'static,
2062) -> impl UiNode {
2063 let panel_list_id = panel_list_id.into();
2064 let mut version = None;
2065 match_node(child, move |_, op| match op {
2066 UiNodeOp::Deinit => {
2067 update(None);
2068 version = None;
2069 }
2070 UiNodeOp::Update { .. } => {
2071 let info = WIDGET.info();
2072 if let Some(parent) = info.parent() {
2073 if let Some(c) = PanelListRange::update(&parent, panel_list_id, &mut version) {
2074 let id = info.id();
2075 let p = c.rev().position(|w| w.id() == id);
2076 update(p);
2077 }
2078 }
2079 }
2080 _ => {}
2081 })
2082}
2083
2084pub fn with_index_len_node(
2091 child: impl UiNode,
2092 panel_list_id: impl Into<StateId<PanelListRange>>,
2093 mut update: impl FnMut(Option<(usize, usize)>) + Send + 'static,
2094) -> impl UiNode {
2095 let panel_list_id = panel_list_id.into();
2096 let mut version = None;
2097 match_node(child, move |_, op| match op {
2098 UiNodeOp::Deinit => {
2099 update(None);
2100 version = None;
2101 }
2102 UiNodeOp::Update { .. } => {
2103 let info = WIDGET.info();
2104 if let Some(parent) = info.parent() {
2105 if let Some(mut iter) = PanelListRange::update(&parent, panel_list_id, &mut version) {
2106 let id = info.id();
2107 let mut p = 0;
2108 let mut count = 0;
2109 for c in &mut iter {
2110 if c.id() == id {
2111 p = count;
2112 count += 1 + iter.count();
2113 break;
2114 } else {
2115 count += 1;
2116 }
2117 }
2118 update(Some((p, count)));
2119 }
2120 }
2121 }
2122 _ => {}
2123 })
2124}
2125
2126pub fn presenter<D: VarValue>(data: impl IntoVar<D>, wgt_fn: impl IntoVar<WidgetFn<D>>) -> impl UiNode {
2133 let data = data.into_var();
2134 let wgt_fn = wgt_fn.into_var();
2135
2136 match_node(NilUiNode.boxed(), move |c, op| match op {
2137 UiNodeOp::Init => {
2138 WIDGET.sub_var(&data).sub_var(&wgt_fn);
2139 *c.child() = wgt_fn.get()(data.get());
2140 }
2141 UiNodeOp::Deinit => {
2142 c.deinit();
2143 *c.child() = NilUiNode.boxed();
2144 }
2145 UiNodeOp::Update { .. } => {
2146 if data.is_new() || wgt_fn.is_new() {
2147 c.child().deinit();
2148 *c.child() = wgt_fn.get()(data.get());
2149 c.child().init();
2150 c.delegated();
2151 WIDGET.update_info().layout().render();
2152 }
2153 }
2154 _ => {}
2155 })
2156}
2157
2158pub fn presenter_opt<D: VarValue>(data: impl IntoVar<Option<D>>, wgt_fn: impl IntoVar<WidgetFn<D>>) -> impl UiNode {
2162 let data = data.into_var();
2163 let wgt_fn = wgt_fn.into_var();
2164
2165 match_node(NilUiNode.boxed(), move |c, op| match op {
2166 UiNodeOp::Init => {
2167 WIDGET.sub_var(&data).sub_var(&wgt_fn);
2168 if let Some(data) = data.get() {
2169 *c.child() = wgt_fn.get()(data);
2170 }
2171 }
2172 UiNodeOp::Deinit => {
2173 c.deinit();
2174 *c.child() = NilUiNode.boxed();
2175 }
2176 UiNodeOp::Update { .. } => {
2177 if data.is_new() || wgt_fn.is_new() {
2178 if let Some(data) = data.get() {
2179 c.child().deinit();
2180 *c.child() = wgt_fn.get()(data);
2181 c.child().init();
2182 c.delegated();
2183 WIDGET.update_info().layout().render();
2184 } else if c.child().actual_type_id() != TypeId::of::<NilUiNode>() {
2185 c.child().deinit();
2186 *c.child() = NilUiNode.boxed();
2187 c.delegated();
2188 WIDGET.update_info().layout().render();
2189 }
2190 }
2191 }
2192 _ => {}
2193 })
2194}
2195
2196pub fn list_presenter<D: VarValue>(list: impl IntoVar<ObservableVec<D>>, item_fn: impl IntoVar<WidgetFn<D>>) -> impl UiNodeList {
2200 ListPresenter {
2201 list: list.into_var(),
2202 item_fn: item_fn.into_var(),
2203 view: vec![],
2204 _e: std::marker::PhantomData,
2205 }
2206}
2207
2208struct ListPresenter<D: VarValue, L: Var<ObservableVec<D>>, E: Var<WidgetFn<D>>> {
2209 list: L,
2210 item_fn: E,
2211 view: Vec<BoxedUiNode>,
2212 _e: std::marker::PhantomData<D>,
2213}
2214
2215impl<D, L, E> UiNodeList for ListPresenter<D, L, E>
2216where
2217 D: VarValue,
2218 L: Var<ObservableVec<D>>,
2219 E: Var<WidgetFn<D>>,
2220{
2221 fn with_node<R, F>(&mut self, index: usize, f: F) -> R
2222 where
2223 F: FnOnce(&mut BoxedUiNode) -> R,
2224 {
2225 self.view.with_node(index, f)
2226 }
2227
2228 fn for_each<F>(&mut self, f: F)
2229 where
2230 F: FnMut(usize, &mut BoxedUiNode),
2231 {
2232 self.view.for_each(f)
2233 }
2234
2235 fn par_each<F>(&mut self, f: F)
2236 where
2237 F: Fn(usize, &mut BoxedUiNode) + Send + Sync,
2238 {
2239 self.view.par_each(f)
2240 }
2241
2242 fn par_fold_reduce<T, I, F, R>(&mut self, identity: I, fold: F, reduce: R) -> T
2243 where
2244 T: Send + 'static,
2245 I: Fn() -> T + Send + Sync,
2246 F: Fn(T, usize, &mut BoxedUiNode) -> T + Send + Sync,
2247 R: Fn(T, T) -> T + Send + Sync,
2248 {
2249 self.view.par_fold_reduce(identity, fold, reduce)
2250 }
2251
2252 fn len(&self) -> usize {
2253 self.view.len()
2254 }
2255
2256 fn boxed(self) -> BoxedUiNodeList {
2257 Box::new(self)
2258 }
2259
2260 fn drain_into(&mut self, vec: &mut Vec<BoxedUiNode>) {
2261 self.view.drain_into(vec);
2262 tracing::warn!("drained `list_presenter`, now out of sync with data");
2263 }
2264
2265 fn init_all(&mut self) {
2266 debug_assert!(self.view.is_empty());
2267 self.view.clear();
2268
2269 WIDGET.sub_var(&self.list).sub_var(&self.item_fn);
2270
2271 let e_fn = self.item_fn.get();
2272 self.list.with(|l| {
2273 for el in l.iter() {
2274 let child = e_fn(el.clone());
2275 self.view.push(child);
2276 }
2277 });
2278
2279 self.view.init_all();
2280 }
2281
2282 fn deinit_all(&mut self) {
2283 self.view.deinit_all();
2284 self.view.clear();
2285 }
2286
2287 fn update_all(&mut self, updates: &WidgetUpdates, observer: &mut dyn UiNodeListObserver) {
2288 let mut need_reset = self.item_fn.is_new();
2289
2290 let is_new = self
2291 .list
2292 .with_new(|l| {
2293 need_reset |= l.changes().is_empty() || l.changes() == [VecChange::Clear];
2294
2295 if need_reset {
2296 return;
2297 }
2298
2299 self.view.update_all(updates, observer);
2301
2302 let e_fn = self.item_fn.get();
2303
2304 for change in l.changes() {
2305 match change {
2306 VecChange::Insert { index, count } => {
2307 for i in *index..(*index + count) {
2308 let mut el = e_fn(l[i].clone());
2309 el.init();
2310 self.view.insert(i, el);
2311 observer.inserted(i);
2312 }
2313 }
2314 VecChange::Remove { index, count } => {
2315 let mut count = *count;
2316 let index = *index;
2317 while count > 0 {
2318 count -= 1;
2319
2320 let mut el = self.view.remove(index);
2321 el.deinit();
2322 observer.removed(index);
2323 }
2324 }
2325 VecChange::Move { from_index, to_index } => {
2326 let el = self.view.remove(*from_index);
2327 self.view.insert(*to_index, el);
2328 observer.moved(*from_index, *to_index);
2329 }
2330 VecChange::Clear => unreachable!(),
2331 }
2332 }
2333 })
2334 .is_some();
2335
2336 if !need_reset && !is_new && self.list.with(|l| l.len() != self.view.len()) {
2337 need_reset = true;
2338 }
2339
2340 if need_reset {
2341 self.view.deinit_all();
2342 self.view.clear();
2343
2344 let e_fn = self.item_fn.get();
2345 self.list.with(|l| {
2346 for el in l.iter() {
2347 let child = e_fn(el.clone());
2348 self.view.push(child);
2349 }
2350 });
2351
2352 self.view.init_all();
2353 } else if !is_new {
2354 self.view.update_all(updates, observer);
2355 }
2356 }
2357}
2358
2359use crate::WidgetFn;
2360#[doc(inline)]
2361pub use crate::command_property;
2362#[doc(inline)]
2363pub use crate::event_property;