1use std::{any::Any, sync::Arc};
6
7use crate::WidgetFn;
8use zng_app::{
9 event::{AnyEventArgs, Command, CommandArgs, CommandHandle, CommandScope, Event, EventArgs, EventPropagationHandle},
10 handler::{Handler, HandlerExt as _},
11 render::{FrameBuilder, FrameValueKey},
12 update::WidgetUpdates,
13 widget::{
14 VarLayout, WIDGET,
15 border::{BORDER, BORDER_ALIGN_VAR, BORDER_OVER_VAR},
16 info::Interactivity,
17 node::*,
18 },
19 window::WINDOW,
20};
21use zng_app_context::{ContextLocal, LocalContext};
22use zng_layout::{
23 context::LAYOUT,
24 unit::{PxConstraints2d, PxCornerRadius, PxPoint, PxRect, PxSideOffsets, PxSize, PxVector, SideOffsets},
25};
26use zng_state_map::{StateId, StateMapRef, StateValue};
27use zng_var::*;
28
29#[doc(hidden)]
30pub use pastey::paste;
31
32#[doc(hidden)]
33pub mod __macro_util {
34 pub use zng_app::{
35 event::CommandArgs,
36 handler::{Handler, hn},
37 widget::{
38 node::{IntoUiNode, UiNode},
39 property,
40 },
41 };
42 pub use zng_var::{IntoVar, context_var};
43}
44
45pub fn with_context_var<T: VarValue>(child: impl IntoUiNode, context_var: ContextVar<T>, value: impl IntoVar<T>) -> UiNode {
135 let value = value.into_var();
136 let mut actual_value = None;
137 let mut id = None;
138
139 match_node(child, move |child, op| {
140 let mut is_deinit = false;
141 match &op {
142 UiNodeOp::Init => {
143 id = Some(ContextInitHandle::new());
144 actual_value = Some(Arc::new(value.current_context().into()));
145 }
146 UiNodeOp::Deinit => {
147 is_deinit = true;
148 }
149 _ => {}
150 }
151
152 context_var.with_context(id.clone().expect("node not inited"), &mut actual_value, || child.op(op));
153
154 if is_deinit {
155 id = None;
156 actual_value = None;
157 }
158 })
159}
160
161pub fn with_context_var_init<T: VarValue>(
170 child: impl IntoUiNode,
171 var: ContextVar<T>,
172 mut init_value: impl FnMut() -> Var<T> + Send + 'static,
173) -> UiNode {
174 let mut id = None;
175 let mut value = None;
176 match_node(child, move |child, op| {
177 let mut is_deinit = false;
178 match &op {
179 UiNodeOp::Init => {
180 id = Some(ContextInitHandle::new());
181 value = Some(Arc::new(init_value().current_context().into()));
182 }
183 UiNodeOp::Deinit => {
184 is_deinit = true;
185 }
186 _ => {}
187 }
188
189 var.with_context(id.clone().expect("node not inited"), &mut value, || child.op(op));
190
191 if is_deinit {
192 id = None;
193 value = None;
194 }
195 })
196}
197
198#[macro_export]
305macro_rules! event_property {
306 ($(
307 $(#[$on_event_attrs:meta])*
308 $vis:vis fn $event:ident {
309 event: $EVENT:path,
310 args: $Args:path
311 $(, filter: $filter:expr)?
312 $(, widget_impl: $Wgt:ty)?
313 $(, with: $with:expr)?
314 $(,)?
315 }
316 )+) => {$(
317 $crate::__event_property! {
318 done {
319 sig { $(#[$on_event_attrs])* $vis fn $event { event: $EVENT, args: $Args, } }
320 }
321
322 $(filter: $filter,)?
323 $(widget_impl: $Wgt,)?
324 $(with: $with,)?
325 }
326 )+};
327}
328
329#[doc(hidden)]
330#[macro_export]
331macro_rules! __event_property {
332 (
334 done {
335 $($done:tt)+
336 }
337 filter: $filter:expr,
338 $($rest:tt)*
339 ) => {
340 $crate::__event_property! {
341 done {
342 $($done)+
343 filter { $filter }
344 }
345 $($rest)*
346 }
347 };
348 (
350 done {
351 $($done:tt)+
352 }
353 widget_impl: $Wgt:ty,
354 $($rest:tt)*
355 ) => {
356 $crate::__event_property! {
357 done {
358 $($done)+
359 widget_impl { , widget_impl($Wgt) }
360 }
361 $($rest)*
362 }
363 };
364 (
366 done {
367 $($done:tt)+
368 }
369 with: $with:expr,
370 ) => {
371 $crate::__event_property! {
372 done {
373 $($done)+
374 with { $with }
375 }
376 }
377 };
378 (
380 done {
381 sig { $($sig:tt)+ }
382 }
383 ) => {
384 $crate::__event_property! {
385 done {
386 sig { $($sig)+ }
387 filter { |_args| true }
388 widget_impl { }
389 with { }
390 }
391 }
392 };
393 (
395 done {
396 sig { $($sig:tt)+ }
397 filter { $($filter:tt)+ }
398 }
399 ) => {
400 $crate::__event_property! {
401 done {
402 sig { $($sig)+ }
403 filter { $($filter)+ }
404 widget_impl { }
405 with { }
406 }
407 }
408 };
409 (
411 done {
412 sig { $($sig:tt)+ }
413 widget_impl { $($widget_impl:tt)+ }
414 }
415 ) => {
416 $crate::__event_property! {
417 done {
418 sig { $($sig)+ }
419 filter { |_args| true }
420 widget_impl { $($widget_impl)+ }
421 with { }
422 }
423 }
424 };
425 (
427 done {
428 sig { $($sig:tt)+ }
429 with { $($with:tt)+ }
430 }
431 ) => {
432 $crate::__event_property! {
433 done {
434 sig { $($sig)+ }
435 filter { |_args| true }
436 widget_impl { }
437 with { $($with)+ }
438 }
439 }
440 };
441 (
443 done {
444 sig { $($sig:tt)+ }
445 filter { $($filter:tt)+ }
446 widget_impl { $($widget_impl:tt)+ }
447 }
448 ) => {
449 $crate::__event_property! {
450 done {
451 sig { $($sig)+ }
452 filter { $($filter)+ }
453 widget_impl { $($widget_impl)+ }
454 with { }
455 }
456 }
457 };
458 (
460 done {
461 sig { $($sig:tt)+ }
462 filter { $($filter:tt)+ }
463 with { $($with:tt)+ }
464 }
465 ) => {
466 $crate::__event_property! {
467 done {
468 sig { $($sig)+ }
469 filter { $($filter)+ }
470 widget_impl { }
471 with { $($with)+ }
472 }
473 }
474 };
475 (
477 done {
478 sig { $(#[$on_event_attrs:meta])* $vis:vis fn $event:ident { event: $EVENT:path, args: $Args:path, } }
479 filter { $filter:expr }
480 widget_impl { $($widget_impl:tt)* }
481 with { $($with:expr)? }
482 }
483 ) => {
484 $crate::node::paste! {
485 $(#[$on_event_attrs])*
486 #[doc = "You can preview this event using [`on_pre_"$event "`](fn.on_pre_"$event ".html)."]
490 #[$crate::node::__macro_util::property(
496 EVENT,
497 default( $crate::node::__macro_util::hn!(|_|{}) )
498 $($widget_impl)*
499 )]
500 $vis fn [<on_ $event>](
501 child: impl $crate::node::__macro_util::IntoUiNode,
502 handler: $crate::node::__macro_util::Handler<$Args>,
503 ) -> $crate::node::__macro_util::UiNode {
504 $crate::__event_property!(=> with($crate::node::on_event(child, $EVENT, $filter, handler), false, $($with)?))
505 }
506
507 #[doc = "Preview [`on_"$event "`](fn.on_"$event ".html) event."]
508 #[$crate::node::__macro_util::property(
519 EVENT,
520 default( $crate::node::__macro_util::hn!(|_|{}) )
521 $($widget_impl)*
522 )]
523 $vis fn [<on_pre_ $event>](
524 child: impl $crate::node::__macro_util::IntoUiNode,
525 handler: $crate::node::__macro_util::Handler<$Args>,
526 ) -> $crate::node::__macro_util::UiNode {
527 $crate::__event_property!(=> with($crate::node::on_pre_event(child, $EVENT, $filter, handler), true, $($with)?))
528 }
529 }
530 };
531
532 (=> with($child:expr, $preview:expr,)) => { $child };
533 (=> with($child:expr, $preview:expr, $with:expr)) => { ($with)($child, $preview) };
534}
535
536pub fn on_event<C, A, F>(child: C, event: Event<A>, filter: F, handler: Handler<A>) -> UiNode
566where
567 C: IntoUiNode,
568 A: EventArgs,
569 F: FnMut(&A) -> bool + Send + 'static,
570{
571 on_event_impl(child.into_node(), event, filter, handler)
572}
573fn on_event_impl<A, F>(child: UiNode, event: Event<A>, mut filter: F, handler: Handler<A>) -> UiNode
574where
575 A: EventArgs,
576 F: FnMut(&A) -> bool + Send + 'static,
577{
578 let mut handler = handler.into_wgt_runner();
579 match_node(child, move |child, op| match op {
580 UiNodeOp::Init => {
581 WIDGET.sub_event(&event);
582 }
583 UiNodeOp::Deinit => {
584 handler.deinit();
585 }
586 UiNodeOp::Event { update } => {
587 child.event(update);
588
589 if let Some(args) = event.on(update)
590 && !args.propagation().is_stopped()
591 && filter(args)
592 {
593 #[cfg(all(debug_assertions, not(target_arch = "wasm32")))]
594 let t = std::time::Instant::now();
595 #[cfg(all(debug_assertions, target_arch = "wasm32"))]
596 let t = web_time::Instant::now();
597
598 handler.event(args);
599
600 #[cfg(debug_assertions)]
601 {
602 let t = t.elapsed();
603 if t > std::time::Duration::from_millis(300) {
604 tracing::warn!(
605 "event handler for `{}` in {:?} blocked for {t:?}, consider using `async_hn!`",
606 event.as_any().name(),
607 WIDGET.id()
608 );
609 }
610 }
611 }
612 }
613 UiNodeOp::Update { updates } => {
614 child.update(updates);
615 handler.update();
616 }
617 _ => {}
618 })
619}
620
621pub fn on_pre_event<C, A, F>(child: C, event: Event<A>, filter: F, handler: Handler<A>) -> UiNode
651where
652 C: IntoUiNode,
653 A: EventArgs,
654 F: FnMut(&A) -> bool + Send + 'static,
655{
656 on_pre_event_impl(child.into_node(), event, filter, handler)
657}
658fn on_pre_event_impl<A, F>(child: UiNode, event: Event<A>, mut filter: F, handler: Handler<A>) -> UiNode
659where
660 A: EventArgs,
661 F: FnMut(&A) -> bool + Send + 'static,
662{
663 let mut handler = handler.into_wgt_runner();
664 match_node(child, move |_, op| match op {
665 UiNodeOp::Init => {
666 WIDGET.sub_event(&event);
667 }
668 UiNodeOp::Deinit => {
669 handler.deinit();
670 }
671 UiNodeOp::Event { update } => {
672 if let Some(args) = event.on(update)
673 && !args.propagation().is_stopped()
674 && filter(args)
675 {
676 #[cfg(debug_assertions)]
677 let t = std::time::Instant::now();
678
679 handler.event(args);
680
681 #[cfg(debug_assertions)]
682 {
683 let t = t.elapsed();
684 if t > std::time::Duration::from_millis(300) {
685 tracing::warn!(
686 "preview event handler for `{}` in {:?} blocked for {t:?}, consider using `async_hn!`",
687 event.as_any().name(),
688 WIDGET.id()
689 );
690 }
691 }
692 }
693 }
694 UiNodeOp::Update { .. } => {
695 handler.update();
696 }
697 _ => {}
698 })
699}
700
701#[doc(hidden)]
702#[macro_export]
703macro_rules! __command_property {
704 (
706 $(#[$on_cmd_attrs:meta])*
707 $vis:vis fn $command:ident {
708 cmd { $cmd_init:expr }
709 widget_impl { $($widget_impl:tt)* }
710 enabled_var { $enabled_var:expr }
711 generate_can_property: false
712 }
713 ) => { $crate::node::paste! {
714 $(#[$on_cmd_attrs])*
715 #[doc = "You can preview this command event using [`on_pre_"$command "`](fn.on_pre_"$command ".html)."]
719 #[$crate::node::__macro_util::property(EVENT, default( $crate::node::__macro_util::hn!(|_|{}) ))]
725 $vis fn [<on_ $command>](
726 child: impl $crate::node::__macro_util::IntoUiNode,
727 handler: $crate::node::__macro_util::Handler<$crate::node::__macro_util::CommandArgs>,
728 ) -> $crate::node::__macro_util::UiNode {
729 $crate::node::on_command(child, || $cmd_init, || $enabled_var, handler)
730 }
731
732 #[doc = "Preview [`on_"$command "`](fn.on_"$command ".html) command."]
733 #[$crate::node::__macro_util::property(EVENT, default( $crate::node::__macro_util::hn!(|_|{}) ) $($widget_impl)*)]
744 $vis fn [<on_pre_ $command>](
745 child: impl $crate::node::__macro_util::IntoUiNode,
746 handler: $crate::node::__macro_util::Handler<$crate::node::__macro_util::CommandArgs>,
747 ) -> $crate::node::__macro_util::UiNode {
748 $crate::node::on_pre_command(child, || $cmd_init, || $enabled_var, handler)
749 }
750 } };
751 (
752 $(#[$on_cmd_attrs:meta])*
753 $vis:vis fn $command:ident {
754 cmd { $cmd_init:expr }
755 widget_impl { $($widget_impl:tt)* }
756 generate_can_property: true
757 }
758 ) => { $crate::node::paste! {
759 $(#[$on_cmd_attrs])*
760 #[doc = "You can preview this command event using [`on_pre_"$command "`](fn.on_pre_"$command ".html)."]
764 #[doc = "You can use the [`can_"$command "`] context property to disable the command handle."]
773 #[$crate::node::__macro_util::property(EVENT, default( $crate::node::__macro_util::hn!(|_|{}) ))]
775 $vis fn [<on_ $command>](
776 child: impl $crate::node::__macro_util::IntoUiNode,
777 handler: $crate::node::__macro_util::Handler<$crate::node::__macro_util::CommandArgs>,
778 ) -> $crate::node::__macro_util::UiNode {
779 $crate::node::on_command(child, || $cmd_init, || $crate::node::__macro_util::IntoVar::into_var(self::[<CAN_ $command:upper _VAR>]), handler)
780 }
781
782 #[doc = "Preview [`on_"$command "`](fn.on_"$command ".html) command."]
783 #[doc = "You can use the [`can_"$command "`](fn@can_"$command ") context property to disable the command handle."]
797 #[$crate::node::__macro_util::property(EVENT, default( $crate::node::__macro_util::hn!(|_|{}) ) $($widget_impl)*)]
798 $vis fn [<on_pre_ $command>](
799 child: impl $crate::node::__macro_util::IntoUiNode,
800 handler: $crate::node::__macro_util::Handler<$crate::node::__macro_util::CommandArgs>,
801 ) -> $crate::node::__macro_util::UiNode {
802 $crate::node::on_pre_command(child, || $cmd_init, || $crate::node::__macro_util::IntoVar::into_var(self::[<CAN_ $command:upper _VAR>]), handler)
803 }
804
805 $crate::node::__macro_util::context_var! {
806 #[doc = "Enable/disable [`on_"$command "`](fn@on_"$command ") and [`on_pre_"$command "`](fn@on_pre_"$command ") command handles."]
807 #[doc = "Enabled by default, use the [`can_"$command "`](fn@can_" $command ") property to set."]
809 $vis static [<CAN_ $command:upper _VAR>]: bool = true;
810 }
811
812 #[doc = "Defines if [`on_"$command "`](fn@on_"$command ") and [`on_pre_"$command "`](fn@on_pre_"$command ") command handles"]
813 #[doc = "Sets the [`CAN_"$command:upper "_VAR`]."]
818 #[$crate::node::__macro_util::property(CONTEXT, default(true) $($widget_impl)*)]
819 $vis fn [<can_ $command>](
820 child: impl $crate::node::__macro_util::IntoUiNode,
821 enabled: impl $crate::node::__macro_util::IntoVar<bool>,
822 ) -> $crate::node::__macro_util::UiNode {
823 $crate::node::with_context_var(child, self::[<CAN_ $command:upper _VAR>], enabled)
824 }
825 } };
826
827 (
829 $(#[$on_cmd_attrs:meta])*
830 $vis:vis fn $command:ident {
831 cmd { $cmd_init:expr }
832 enabled { $enabled_var:expr }
833 widget_impl_ty { $Wgt:ty }
834 }
835 ) => {
836 $crate::__command_property! {
837 $(#[$on_cmd_attrs])*
838 $vis fn $command {
839 cmd { $cmd_init }
840 widget_impl { , widget_impl($Wgt) }
841 enabled_var { $enabled_var }
842 generate_can_property: false
843 }
844 }
845 };
846
847 (
849 $(#[$on_cmd_attrs:meta])*
850 $vis:vis fn $command:ident {
851 cmd { $cmd_init:expr }
852 widget_impl_ty { $Wgt:ty }
853 }
854 ) => {
855 $crate::node::paste! {
856 $crate::__command_property! {
857 $(#[$on_cmd_attrs])*
858 $vis fn $command {
859 cmd { $cmd_init }
860 widget_impl { , widget_impl($Wgt) }
861 generate_can_property: true
862 }
863 }
864 }
865 };
866
867 (
869 $(#[$on_cmd_attrs:meta])*
870 $vis:vis fn $command:ident {
871 cmd { $cmd_init:expr }
872 enabled { $enabled_var:expr }
873 }
874 ) => {
875 $crate::__command_property! {
876 $(#[$on_cmd_attrs])*
877 $vis fn $command {
878 cmd { $cmd_init }
879 widget_impl { }
880 enabled_var { $enabled_var }
881 generate_can_property: false
882 }
883 }
884 };
885
886 (
888 $(#[$on_cmd_attrs:meta])*
889 $vis:vis fn $command:ident {
890 cmd { $cmd_init:expr }
891 }
892 ) => {
893 $crate::__command_property! {
894 $(#[$on_cmd_attrs])*
895 $vis fn $command {
896 cmd { $cmd_init }
897 widget_impl { }
898 generate_can_property: true
899 }
900 }
901 };
902}
903
904#[macro_export]
1000macro_rules! command_property {
1001 ($(
1002 $(#[$on_cmd_attrs:meta])*
1003 $vis:vis fn $command:ident {
1004 cmd: $cmd_init:expr
1005 $(, enabled: $enabled_var:expr)?
1006 $(, widget_impl: $Wgt:ty)?
1007 $(,)?
1008 }
1009 )+) => {$(
1010 $crate::__command_property! {
1011 $(#[$on_cmd_attrs])*
1012 $vis fn $command {
1013 cmd { $cmd_init }
1014 $( enabled { $enabled_var } )?
1015 $( widget_impl_ty { $Wgt } )?
1016 }
1017 }
1018 )+};
1019}
1020
1021pub fn on_command<U, CB, EB>(child: U, command_builder: CB, enabled_builder: EB, handler: Handler<CommandArgs>) -> UiNode
1058where
1059 U: IntoUiNode,
1060 CB: FnMut() -> Command + Send + 'static,
1061 EB: FnMut() -> Var<bool> + Send + 'static,
1062{
1063 on_command_impl(child.into_node(), command_builder, enabled_builder, handler)
1064}
1065fn on_command_impl<CB, EB>(child: UiNode, mut command_builder: CB, mut enabled_builder: EB, handler: Handler<CommandArgs>) -> UiNode
1066where
1067 CB: FnMut() -> Command + Send + 'static,
1068 EB: FnMut() -> Var<bool> + Send + 'static,
1069{
1070 let mut handler = handler.into_wgt_runner();
1071 let mut enabled = None;
1072 let mut handle = CommandHandle::dummy();
1073 let mut win_handle = CommandHandle::dummy();
1074 let mut command = NIL_CMD;
1075 let mut last_propagation = EventPropagationHandle::new();
1076
1077 match_node(child, move |child, op| match op {
1078 UiNodeOp::Init => {
1079 child.init();
1080
1081 let e = enabled_builder();
1082 WIDGET.sub_var(&e);
1083 let is_enabled = e.get();
1084 enabled = Some(e);
1085
1086 command = command_builder();
1087
1088 let id = WIDGET.id();
1089 handle = command.subscribe_wgt(is_enabled, id);
1090 if CommandScope::Widget(id) == command.scope() && WIDGET.parent_id().is_none() {
1091 win_handle = command.scoped(WINDOW.id()).subscribe_wgt(is_enabled, id);
1093 }
1094 }
1095 UiNodeOp::Deinit => {
1096 child.deinit();
1097
1098 enabled = None;
1099 handle = CommandHandle::dummy();
1100 win_handle = CommandHandle::dummy();
1101 command = NIL_CMD;
1102 handler.deinit();
1103 }
1104
1105 UiNodeOp::Event { update } => {
1106 child.event(update);
1107
1108 if command.event().has(update) {
1109 if win_handle.is_dummy() {
1110 if let Some(args) = command.on_unhandled(update) {
1111 handler.event(args);
1112 }
1113 } else if let Some(args) = command
1114 .on_unhandled(update)
1115 .or_else(|| command.scoped(WINDOW.id()).on_unhandled(update))
1116 && &last_propagation != args.propagation()
1117 {
1118 handler.event(args);
1119 last_propagation = args.propagation().clone();
1120 }
1121 }
1122 }
1123 UiNodeOp::Update { updates } => {
1124 child.update(updates);
1125
1126 handler.update();
1127
1128 if let Some(enabled) = enabled.as_ref().expect("node not inited").get_new() {
1129 handle.set_enabled(enabled);
1130 win_handle.set_enabled(enabled);
1131 }
1132 }
1133
1134 _ => {}
1135 })
1136}
1137
1138zng_app::event::command! {
1139 static NIL_CMD;
1140}
1141
1142pub fn on_pre_command<U, CB, EB>(child: U, command_builder: CB, enabled_builder: EB, handler: Handler<CommandArgs>) -> UiNode
1151where
1152 U: IntoUiNode,
1153 CB: FnMut() -> Command + Send + 'static,
1154 EB: FnMut() -> Var<bool> + Send + 'static,
1155{
1156 on_pre_command_impl(child.into_node(), command_builder, enabled_builder, handler)
1157}
1158fn on_pre_command_impl<CB, EB>(child: UiNode, mut command_builder: CB, mut enabled_builder: EB, handler: Handler<CommandArgs>) -> UiNode
1159where
1160 CB: FnMut() -> Command + Send + 'static,
1161 EB: FnMut() -> Var<bool> + Send + 'static,
1162{
1163 let mut handler = handler.into_wgt_runner();
1164 let mut enabled = None;
1165 let mut handle = CommandHandle::dummy();
1166 let mut win_handle = CommandHandle::dummy();
1167 let mut command = NIL_CMD;
1168 let mut last_propagation = EventPropagationHandle::new();
1169
1170 match_node(child, move |child, op| match op {
1171 UiNodeOp::Init => {
1172 child.init();
1173
1174 let e = enabled_builder();
1175 WIDGET.sub_var(&e);
1176 let is_enabled = e.get();
1177 enabled = Some(e);
1178
1179 command = command_builder();
1180
1181 let id = WIDGET.id();
1182 handle = command.subscribe_wgt(is_enabled, id);
1183 if CommandScope::Widget(id) == command.scope() && WIDGET.parent_id().is_none() {
1184 win_handle = command.scoped(WINDOW.id()).subscribe_wgt(is_enabled, id);
1186 }
1187 }
1188 UiNodeOp::Deinit => {
1189 child.deinit();
1190
1191 enabled = None;
1192 handle = CommandHandle::dummy();
1193 win_handle = CommandHandle::dummy();
1194 command = NIL_CMD;
1195 handler.deinit();
1196 }
1197
1198 UiNodeOp::Event { update } => {
1199 if command.event().has(update) {
1200 if win_handle.is_dummy() {
1201 if let Some(args) = command.on_unhandled(update) {
1202 handler.event(args);
1203 }
1204 } else if let Some(args) = command
1205 .on_unhandled(update)
1206 .or_else(|| command.scoped(WINDOW.id()).on_unhandled(update))
1207 && &last_propagation != args.propagation()
1208 {
1209 handler.event(args);
1210 last_propagation = args.propagation().clone();
1211 }
1212 }
1213 }
1214 UiNodeOp::Update { .. } => {
1215 handler.update();
1216
1217 if let Some(enabled) = enabled.as_ref().expect("on_pre_command not initialized").get_new() {
1218 handle.set_enabled(enabled);
1219 win_handle.set_enabled(enabled);
1220 }
1221 }
1222
1223 _ => {}
1224 })
1225}
1226
1227pub fn validate_getter_var<T: VarValue>(_var: &Var<T>) {
1229 #[cfg(debug_assertions)]
1230 if _var.capabilities().is_always_read_only() {
1231 tracing::error!(
1232 "`is_`, `has_` or `get_` property inited with read-only var in `{}`",
1233 WIDGET.trace_id()
1234 );
1235 }
1236}
1237
1238pub fn event_state<A: EventArgs, S: VarValue>(
1242 child: impl IntoUiNode,
1243 state: impl IntoVar<S>,
1244 default: S,
1245 event: Event<A>,
1246 mut on_event: impl FnMut(&A) -> Option<S> + Send + 'static,
1247) -> UiNode {
1248 let state = state.into_var();
1249 match_node(child, move |_, op| match op {
1250 UiNodeOp::Init => {
1251 validate_getter_var(&state);
1252 WIDGET.sub_event(&event);
1253 state.set(default.clone());
1254 }
1255 UiNodeOp::Deinit => {
1256 state.set(default.clone());
1257 }
1258 UiNodeOp::Event { update } => {
1259 if let Some(args) = event.on(update)
1260 && let Some(s) = on_event(args)
1261 {
1262 state.set(s);
1263 }
1264 }
1265 _ => {}
1266 })
1267}
1268
1269#[expect(clippy::too_many_arguments)]
1274pub fn event_state2<A0, A1, S0, S1, S>(
1275 child: impl IntoUiNode,
1276 state: impl IntoVar<S>,
1277 default: S,
1278 event0: Event<A0>,
1279 default0: S0,
1280 mut on_event0: impl FnMut(&A0) -> Option<S0> + Send + 'static,
1281 event1: Event<A1>,
1282 default1: S1,
1283 mut on_event1: impl FnMut(&A1) -> Option<S1> + Send + 'static,
1284 mut merge: impl FnMut(S0, S1) -> Option<S> + Send + 'static,
1285) -> UiNode
1286where
1287 A0: EventArgs,
1288 A1: EventArgs,
1289 S0: VarValue,
1290 S1: VarValue,
1291 S: VarValue,
1292{
1293 let state = state.into_var();
1294 let partial_default = (default0, default1);
1295 let mut partial = partial_default.clone();
1296
1297 match_node(child, move |child, op| match op {
1298 UiNodeOp::Init => {
1299 validate_getter_var(&state);
1300 WIDGET.sub_event(&event0).sub_event(&event1);
1301
1302 partial = partial_default.clone();
1303 state.set(default.clone());
1304 }
1305 UiNodeOp::Deinit => {
1306 state.set(default.clone());
1307 }
1308 UiNodeOp::Event { update } => {
1309 let mut updated = false;
1310 if let Some(args) = event0.on(update) {
1311 if let Some(state) = on_event0(args)
1312 && partial.0 != state
1313 {
1314 partial.0 = state;
1315 updated = true;
1316 }
1317 } else if let Some(args) = event1.on(update)
1318 && let Some(state) = on_event1(args)
1319 && partial.1 != state
1320 {
1321 partial.1 = state;
1322 updated = true;
1323 }
1324 child.event(update);
1325
1326 if updated && let Some(value) = merge(partial.0.clone(), partial.1.clone()) {
1327 state.set(value);
1328 }
1329 }
1330 _ => {}
1331 })
1332}
1333
1334#[expect(clippy::too_many_arguments)]
1339pub fn event_state3<A0, A1, A2, S0, S1, S2, S>(
1340 child: impl IntoUiNode,
1341 state: impl IntoVar<S>,
1342 default: S,
1343 event0: Event<A0>,
1344 default0: S0,
1345 mut on_event0: impl FnMut(&A0) -> Option<S0> + Send + 'static,
1346 event1: Event<A1>,
1347 default1: S1,
1348 mut on_event1: impl FnMut(&A1) -> Option<S1> + Send + 'static,
1349 event2: Event<A2>,
1350 default2: S2,
1351 mut on_event2: impl FnMut(&A2) -> Option<S2> + Send + 'static,
1352 mut merge: impl FnMut(S0, S1, S2) -> Option<S> + Send + 'static,
1353) -> UiNode
1354where
1355 A0: EventArgs,
1356 A1: EventArgs,
1357 A2: EventArgs,
1358 S0: VarValue,
1359 S1: VarValue,
1360 S2: VarValue,
1361 S: VarValue,
1362{
1363 let state = state.into_var();
1364 let partial_default = (default0, default1, default2);
1365 let mut partial = partial_default.clone();
1366
1367 match_node(child, move |child, op| match op {
1368 UiNodeOp::Init => {
1369 validate_getter_var(&state);
1370 WIDGET.sub_event(&event0).sub_event(&event1).sub_event(&event2);
1371
1372 partial = partial_default.clone();
1373 state.set(default.clone());
1374 }
1375 UiNodeOp::Deinit => {
1376 state.set(default.clone());
1377 }
1378 UiNodeOp::Event { update } => {
1379 let mut updated = false;
1380 if let Some(args) = event0.on(update) {
1381 if let Some(state) = on_event0(args)
1382 && partial.0 != state
1383 {
1384 partial.0 = state;
1385 updated = true;
1386 }
1387 } else if let Some(args) = event1.on(update) {
1388 if let Some(state) = on_event1(args)
1389 && partial.1 != state
1390 {
1391 partial.1 = state;
1392 updated = true;
1393 }
1394 } else if let Some(args) = event2.on(update)
1395 && let Some(state) = on_event2(args)
1396 && partial.2 != state
1397 {
1398 partial.2 = state;
1399 updated = true;
1400 }
1401 child.event(update);
1402
1403 if updated && let Some(value) = merge(partial.0.clone(), partial.1.clone(), partial.2.clone()) {
1404 state.set(value);
1405 }
1406 }
1407 _ => {}
1408 })
1409}
1410
1411#[expect(clippy::too_many_arguments)]
1416pub fn event_state4<A0, A1, A2, A3, S0, S1, S2, S3, S>(
1417 child: impl IntoUiNode,
1418 state: impl IntoVar<S>,
1419 default: S,
1420 event0: Event<A0>,
1421 default0: S0,
1422 mut on_event0: impl FnMut(&A0) -> Option<S0> + Send + 'static,
1423 event1: Event<A1>,
1424 default1: S1,
1425 mut on_event1: impl FnMut(&A1) -> Option<S1> + Send + 'static,
1426 event2: Event<A2>,
1427 default2: S2,
1428 mut on_event2: impl FnMut(&A2) -> Option<S2> + Send + 'static,
1429 event3: Event<A3>,
1430 default3: S3,
1431 mut on_event3: impl FnMut(&A3) -> Option<S3> + Send + 'static,
1432 mut merge: impl FnMut(S0, S1, S2, S3) -> Option<S> + Send + 'static,
1433) -> UiNode
1434where
1435 A0: EventArgs,
1436 A1: EventArgs,
1437 A2: EventArgs,
1438 A3: EventArgs,
1439 S0: VarValue,
1440 S1: VarValue,
1441 S2: VarValue,
1442 S3: VarValue,
1443 S: VarValue,
1444{
1445 let state = state.into_var();
1446 let partial_default = (default0, default1, default2, default3);
1447 let mut partial = partial_default.clone();
1448
1449 match_node(child, move |child, op| match op {
1450 UiNodeOp::Init => {
1451 validate_getter_var(&state);
1452 WIDGET.sub_event(&event0).sub_event(&event1).sub_event(&event2).sub_event(&event3);
1453
1454 partial = partial_default.clone();
1455 state.set(default.clone());
1456 }
1457 UiNodeOp::Deinit => {
1458 state.set(default.clone());
1459 }
1460 UiNodeOp::Event { update } => {
1461 let mut updated = false;
1462 if let Some(args) = event0.on(update) {
1463 if let Some(state) = on_event0(args)
1464 && partial.0 != state
1465 {
1466 partial.0 = state;
1467 updated = true;
1468 }
1469 } else if let Some(args) = event1.on(update) {
1470 if let Some(state) = on_event1(args)
1471 && partial.1 != state
1472 {
1473 partial.1 = state;
1474 updated = true;
1475 }
1476 } else if let Some(args) = event2.on(update) {
1477 if let Some(state) = on_event2(args)
1478 && partial.2 != state
1479 {
1480 partial.2 = state;
1481 updated = true;
1482 }
1483 } else if let Some(args) = event3.on(update)
1484 && let Some(state) = on_event3(args)
1485 && partial.3 != state
1486 {
1487 partial.3 = state;
1488 updated = true;
1489 }
1490 child.event(update);
1491
1492 if updated && let Some(value) = merge(partial.0.clone(), partial.1.clone(), partial.2.clone(), partial.3.clone()) {
1493 state.set(value);
1494 }
1495 }
1496 _ => {}
1497 })
1498}
1499
1500pub fn bind_state<T: VarValue>(child: impl IntoUiNode, source: impl IntoVar<T>, state: impl IntoVar<T>) -> UiNode {
1505 let source = source.into_var();
1506 let state = state.into_var();
1507 let mut _binding = VarHandle::dummy();
1508
1509 match_node(child, move |_, op| match op {
1510 UiNodeOp::Init => {
1511 validate_getter_var(&state);
1512 state.set_from(&source);
1513 _binding = source.bind(&state);
1514 }
1515 UiNodeOp::Deinit => {
1516 _binding = VarHandle::dummy();
1517 }
1518 _ => {}
1519 })
1520}
1521
1522pub fn bind_state_init<T>(child: impl IntoUiNode, source: impl Fn() -> Var<T> + Send + 'static, state: impl IntoVar<T>) -> UiNode
1528where
1529 T: VarValue,
1530{
1531 let state = state.into_var();
1532 let mut _source_var = None;
1533 let mut _binding = VarHandle::dummy();
1534
1535 match_node(child, move |_, op| match op {
1536 UiNodeOp::Init => {
1537 validate_getter_var(&state);
1538 let source = source();
1539 state.set_from(&source);
1540 _binding = source.bind(&state);
1541 _source_var = Some(source);
1542 }
1543 UiNodeOp::Deinit => {
1544 _binding = VarHandle::dummy();
1545 _source_var = None;
1546 }
1547 _ => {}
1548 })
1549}
1550
1551pub fn widget_state_is_state(
1556 child: impl IntoUiNode,
1557 predicate: impl Fn(StateMapRef<WIDGET>) -> bool + Send + 'static,
1558 deinit: impl Fn(StateMapRef<WIDGET>) -> bool + Send + 'static,
1559 state: impl IntoVar<bool>,
1560) -> UiNode {
1561 let state = state.into_var();
1562
1563 match_node(child, move |child, op| match op {
1564 UiNodeOp::Init => {
1565 validate_getter_var(&state);
1566 child.init();
1567 let s = WIDGET.with_state(&predicate);
1568 if s != state.get() {
1569 state.set(s);
1570 }
1571 }
1572 UiNodeOp::Deinit => {
1573 child.deinit();
1574 let s = WIDGET.with_state(&deinit);
1575 if s != state.get() {
1576 state.set(s);
1577 }
1578 }
1579 UiNodeOp::Update { updates } => {
1580 child.update(updates);
1581 let s = WIDGET.with_state(&predicate);
1582 if s != state.get() {
1583 state.set(s);
1584 }
1585 }
1586 _ => {}
1587 })
1588}
1589
1590pub fn widget_state_get_state<T: VarValue>(
1595 child: impl IntoUiNode,
1596 get_new: impl Fn(StateMapRef<WIDGET>, &T) -> Option<T> + Send + 'static,
1597 get_deinit: impl Fn(StateMapRef<WIDGET>, &T) -> Option<T> + Send + 'static,
1598 state: impl IntoVar<T>,
1599) -> UiNode {
1600 let state = state.into_var();
1601 match_node(child, move |child, op| match op {
1602 UiNodeOp::Init => {
1603 validate_getter_var(&state);
1604 child.init();
1605 let new = state.with(|s| WIDGET.with_state(|w| get_new(w, s)));
1606 if let Some(new) = new {
1607 state.set(new);
1608 }
1609 }
1610 UiNodeOp::Deinit => {
1611 child.deinit();
1612
1613 let new = state.with(|s| WIDGET.with_state(|w| get_deinit(w, s)));
1614 if let Some(new) = new {
1615 state.set(new);
1616 }
1617 }
1618 UiNodeOp::Update { updates } => {
1619 child.update(updates);
1620 let new = state.with(|s| WIDGET.with_state(|w| get_new(w, s)));
1621 if let Some(new) = new {
1622 state.set(new);
1623 }
1624 }
1625 _ => {}
1626 })
1627}
1628
1629pub fn fill_node(content: impl IntoUiNode) -> UiNode {
1635 let mut clip_bounds = PxSize::zero();
1636 let mut clip_corners = PxCornerRadius::zero();
1637
1638 let mut offset = PxVector::zero();
1639 let offset_key = FrameValueKey::new_unique();
1640 let mut define_frame = false;
1641
1642 match_node(content, move |child, op| match op {
1643 UiNodeOp::Init => {
1644 WIDGET.sub_var_layout(&BORDER_ALIGN_VAR);
1645 define_frame = false;
1646 offset = PxVector::zero();
1647 }
1648 UiNodeOp::Measure { desired_size, .. } => {
1649 let offsets = BORDER.inner_offsets();
1650 let align = BORDER_ALIGN_VAR.get();
1651
1652 let our_offsets = offsets * align;
1653 let size_offset = offsets - our_offsets;
1654
1655 let size_increase = PxSize::new(size_offset.horizontal(), size_offset.vertical());
1656
1657 *desired_size = LAYOUT.constraints().fill_size() + size_increase;
1658 }
1659 UiNodeOp::Layout { wl, final_size } => {
1660 let (bounds, corners) = BORDER.fill_bounds();
1665
1666 let mut new_offset = bounds.origin.to_vector();
1667
1668 if clip_bounds != bounds.size || clip_corners != corners {
1669 clip_bounds = bounds.size;
1670 clip_corners = corners;
1671 WIDGET.render();
1672 }
1673
1674 let (_, branch_offset) = LAYOUT.with_constraints(PxConstraints2d::new_exact_size(bounds.size), || {
1675 wl.with_branch_child(|wl| child.layout(wl))
1676 });
1677 new_offset += branch_offset;
1678
1679 if offset != new_offset {
1680 offset = new_offset;
1681
1682 if define_frame {
1683 WIDGET.render_update();
1684 } else {
1685 define_frame = true;
1686 WIDGET.render();
1687 }
1688 }
1689
1690 *final_size = bounds.size;
1691 }
1692 UiNodeOp::Render { frame } => {
1693 let mut render = |frame: &mut FrameBuilder| {
1694 let bounds = PxRect::from_size(clip_bounds);
1695 frame.push_clips(
1696 |c| {
1697 if clip_corners != PxCornerRadius::zero() {
1698 c.push_clip_rounded_rect(bounds, clip_corners, false, false);
1699 } else {
1700 c.push_clip_rect(bounds, false, false);
1701 }
1702
1703 if let Some(inline) = WIDGET.bounds().inline() {
1704 for r in inline.negative_space().iter() {
1705 c.push_clip_rect(*r, true, false);
1706 }
1707 }
1708 },
1709 |f| child.render(f),
1710 );
1711 };
1712
1713 if define_frame {
1714 frame.push_reference_frame(offset_key.into(), offset_key.bind(offset.into(), false), true, false, |frame| {
1715 render(frame);
1716 });
1717 } else {
1718 render(frame);
1719 }
1720 }
1721 UiNodeOp::RenderUpdate { update } => {
1722 if define_frame {
1723 update.with_transform(offset_key.update(offset.into(), false), false, |update| {
1724 child.render_update(update);
1725 });
1726 } else {
1727 child.render_update(update);
1728 }
1729 }
1730 _ => {}
1731 })
1732}
1733
1734pub fn border_node(child: impl IntoUiNode, border_offsets: impl IntoVar<SideOffsets>, border_visual: impl IntoUiNode) -> UiNode {
1739 let offsets = border_offsets.into_var();
1740 let mut render_offsets = PxSideOffsets::zero();
1741 let mut border_rect = PxRect::zero();
1742
1743 match_node(ui_vec![child, border_visual], move |children, op| match op {
1744 UiNodeOp::Init => {
1745 WIDGET.sub_var_layout(&offsets).sub_var_render(&BORDER_OVER_VAR);
1746 }
1747 UiNodeOp::Measure { wm, desired_size } => {
1748 let offsets = offsets.layout();
1749 *desired_size = BORDER.measure_border(offsets, || {
1750 LAYOUT.with_sub_size(PxSize::new(offsets.horizontal(), offsets.vertical()), || {
1751 children.node().with_child(0, |n| wm.measure_block(n))
1752 })
1753 });
1754 children.delegated();
1755 }
1756 UiNodeOp::Layout { wl, final_size } => {
1757 let offsets = offsets.layout();
1765 if render_offsets != offsets {
1766 render_offsets = offsets;
1767 WIDGET.render();
1768 }
1769
1770 let parent_offsets = BORDER.inner_offsets();
1771 let origin = PxPoint::new(parent_offsets.left, parent_offsets.top);
1772 if border_rect.origin != origin {
1773 border_rect.origin = origin;
1774 WIDGET.render();
1775 }
1776
1777 BORDER.layout_border(offsets, || {
1779 wl.translate(PxVector::new(offsets.left, offsets.top));
1780
1781 let taken_size = PxSize::new(offsets.horizontal(), offsets.vertical());
1782 border_rect.size = LAYOUT.with_sub_size(taken_size, || children.node().with_child(0, |n| n.layout(wl)));
1783
1784 LAYOUT.with_constraints(PxConstraints2d::new_exact_size(border_rect.size), || {
1786 BORDER.with_border_layout(border_rect, offsets, || {
1787 children.node().with_child(1, |n| n.layout(wl));
1788 });
1789 });
1790 });
1791 children.delegated();
1792
1793 *final_size = border_rect.size;
1794 }
1795 UiNodeOp::Render { frame } => {
1796 if BORDER_OVER_VAR.get() {
1797 children.node().with_child(0, |c| c.render(frame));
1798 BORDER.with_border_layout(border_rect, render_offsets, || {
1799 children.node().with_child(1, |c| c.render(frame));
1800 });
1801 } else {
1802 BORDER.with_border_layout(border_rect, render_offsets, || {
1803 children.node().with_child(1, |c| c.render(frame));
1804 });
1805 children.node().with_child(0, |c| c.render(frame));
1806 }
1807 children.delegated();
1808 }
1809 UiNodeOp::RenderUpdate { update } => {
1810 children.node().with_child(0, |c| c.render_update(update));
1811 BORDER.with_border_layout(border_rect, render_offsets, || {
1812 children.node().with_child(1, |c| c.render_update(update));
1813 });
1814 children.delegated();
1815 }
1816 _ => {}
1817 })
1818}
1819
1820pub fn with_context_local<T: Any + Send + Sync + 'static>(
1826 child: impl IntoUiNode,
1827 context: &'static ContextLocal<T>,
1828 value: impl Into<T>,
1829) -> UiNode {
1830 let mut value = Some(Arc::new(value.into()));
1831
1832 match_node(child, move |child, op| {
1833 context.with_context(&mut value, || child.op(op));
1834 })
1835}
1836
1837pub fn with_context_local_init<T: Any + Send + Sync + 'static>(
1846 child: impl IntoUiNode,
1847 context: &'static ContextLocal<T>,
1848 init_value: impl FnMut() -> T + Send + 'static,
1849) -> UiNode {
1850 with_context_local_init_impl(child.into_node(), context, init_value)
1851}
1852fn with_context_local_init_impl<T: Any + Send + Sync + 'static>(
1853 child: UiNode,
1854 context: &'static ContextLocal<T>,
1855 mut init_value: impl FnMut() -> T + Send + 'static,
1856) -> UiNode {
1857 let mut value = None;
1858
1859 match_node(child, move |child, op| {
1860 let mut is_deinit = false;
1861 match &op {
1862 UiNodeOp::Init => {
1863 value = Some(Arc::new(init_value()));
1864 }
1865 UiNodeOp::Deinit => {
1866 is_deinit = true;
1867 }
1868 _ => {}
1869 }
1870
1871 context.with_context(&mut value, || child.op(op));
1872
1873 if is_deinit {
1874 value = None;
1875 }
1876 })
1877}
1878
1879pub fn with_context_blend(mut ctx: LocalContext, over: bool, child: impl IntoUiNode) -> UiNode {
1908 match_widget(child, move |c, op| {
1909 if let UiNodeOp::Init = op {
1910 let init_app = LocalContext::current_app();
1911 ctx.with_context_blend(over, || {
1912 let ctx_app = LocalContext::current_app();
1913 assert_eq!(init_app, ctx_app);
1914 c.op(op)
1915 });
1916 } else {
1917 ctx.with_context_blend(over, || c.op(op));
1918 }
1919 })
1920}
1921
1922pub fn with_widget_state<U, I, T>(child: U, id: impl Into<StateId<T>>, default: I, value: impl IntoVar<T>) -> UiNode
1964where
1965 U: IntoUiNode,
1966 I: Fn() -> T + Send + 'static,
1967 T: StateValue + VarValue,
1968{
1969 with_widget_state_impl(child.into_node(), id.into(), default, value.into_var())
1970}
1971fn with_widget_state_impl<I, T>(child: UiNode, id: impl Into<StateId<T>>, default: I, value: impl IntoVar<T>) -> UiNode
1972where
1973 I: Fn() -> T + Send + 'static,
1974 T: StateValue + VarValue,
1975{
1976 let id = id.into();
1977 let value = value.into_var();
1978
1979 match_node(child, move |child, op| match op {
1980 UiNodeOp::Init => {
1981 child.init();
1982 WIDGET.sub_var(&value);
1983 WIDGET.set_state(id, value.get());
1984 }
1985 UiNodeOp::Deinit => {
1986 child.deinit();
1987 WIDGET.set_state(id, default());
1988 }
1989 UiNodeOp::Update { updates } => {
1990 child.update(updates);
1991 if let Some(v) = value.get_new() {
1992 WIDGET.set_state(id, v);
1993 }
1994 }
1995 _ => {}
1996 })
1997}
1998
1999pub fn with_widget_state_modify<U, S, V, I, M>(child: U, id: impl Into<StateId<S>>, value: impl IntoVar<V>, default: I, modify: M) -> UiNode
2007where
2008 U: IntoUiNode,
2009 S: StateValue,
2010 V: VarValue,
2011 I: Fn() -> S + Send + 'static,
2012 M: FnMut(&mut S, &V) + Send + 'static,
2013{
2014 with_widget_state_modify_impl(child.into_node(), id.into(), value.into_var(), default, modify)
2015}
2016fn with_widget_state_modify_impl<S, V, I, M>(
2017 child: UiNode,
2018 id: impl Into<StateId<S>>,
2019 value: impl IntoVar<V>,
2020 default: I,
2021 mut modify: M,
2022) -> UiNode
2023where
2024 S: StateValue,
2025 V: VarValue,
2026 I: Fn() -> S + Send + 'static,
2027 M: FnMut(&mut S, &V) + Send + 'static,
2028{
2029 let id = id.into();
2030 let value = value.into_var();
2031
2032 match_node(child, move |child, op| match op {
2033 UiNodeOp::Init => {
2034 child.init();
2035
2036 WIDGET.sub_var(&value);
2037
2038 value.with(|v| {
2039 WIDGET.with_state_mut(|mut s| {
2040 modify(s.entry(id).or_insert_with(&default), v);
2041 })
2042 })
2043 }
2044 UiNodeOp::Deinit => {
2045 child.deinit();
2046
2047 WIDGET.set_state(id, default());
2048 }
2049 UiNodeOp::Update { updates } => {
2050 child.update(updates);
2051 value.with_new(|v| {
2052 WIDGET.with_state_mut(|mut s| {
2053 modify(s.req_mut(id), v);
2054 })
2055 });
2056 }
2057 _ => {}
2058 })
2059}
2060
2061pub fn interactive_node(child: impl IntoUiNode, interactive: impl IntoVar<bool>) -> UiNode {
2073 let interactive = interactive.into_var();
2074
2075 match_node(child, move |child, op| match op {
2076 UiNodeOp::Init => {
2077 WIDGET.sub_var_info(&interactive);
2078 }
2079 UiNodeOp::Info { info } => {
2080 if interactive.get() {
2081 child.info(info);
2082 } else if let Some(mut wgt) = child.node().as_widget() {
2083 let id = wgt.id();
2084 info.push_interactivity_filter(move |args| {
2086 if args.info.id() == id {
2087 Interactivity::BLOCKED
2088 } else {
2089 Interactivity::ENABLED
2090 }
2091 });
2092 child.info(info);
2093 } else {
2094 let block_range = info.with_children_range(|info| child.info(info));
2095 if !block_range.is_empty() {
2096 let id = WIDGET.id();
2099 info.push_interactivity_filter(move |args| {
2100 if let Some(parent) = args.info.parent()
2101 && parent.id() == id
2102 {
2103 for (i, item) in parent.children().enumerate() {
2105 if item == args.info {
2106 return if !block_range.contains(&i) {
2107 Interactivity::ENABLED
2108 } else {
2109 Interactivity::BLOCKED
2110 };
2111 } else if i >= block_range.end {
2112 break;
2113 }
2114 }
2115 }
2116 Interactivity::ENABLED
2117 });
2118 }
2119 }
2120 }
2121 _ => {}
2122 })
2123}
2124
2125pub fn with_index_node(
2129 child: impl IntoUiNode,
2130 panel_list_id: impl Into<StateId<PanelListRange>>,
2131 mut update: impl FnMut(Option<usize>) + Send + 'static,
2132) -> UiNode {
2133 let panel_list_id = panel_list_id.into();
2134 let mut version = None;
2135 match_node(child, move |_, op| match op {
2136 UiNodeOp::Deinit => {
2137 update(None);
2138 version = None;
2139 }
2140 UiNodeOp::Update { .. } => {
2141 let info = WIDGET.info();
2143 if let Some(parent) = info.parent()
2144 && let Some(mut c) = PanelListRange::update(&parent, panel_list_id, &mut version)
2145 {
2146 let id = info.id();
2147 let p = c.position(|w| w.id() == id);
2148 update(p);
2149 }
2150 }
2151 _ => {}
2152 })
2153}
2154
2155pub fn with_rev_index_node(
2159 child: impl IntoUiNode,
2160 panel_list_id: impl Into<StateId<PanelListRange>>,
2161 mut update: impl FnMut(Option<usize>) + Send + 'static,
2162) -> UiNode {
2163 let panel_list_id = panel_list_id.into();
2164 let mut version = None;
2165 match_node(child, move |_, op| match op {
2166 UiNodeOp::Deinit => {
2167 update(None);
2168 version = None;
2169 }
2170 UiNodeOp::Update { .. } => {
2171 let info = WIDGET.info();
2172 if let Some(parent) = info.parent()
2173 && let Some(c) = PanelListRange::update(&parent, panel_list_id, &mut version)
2174 {
2175 let id = info.id();
2176 let p = c.rev().position(|w| w.id() == id);
2177 update(p);
2178 }
2179 }
2180 _ => {}
2181 })
2182}
2183
2184pub fn with_index_len_node(
2191 child: impl IntoUiNode,
2192 panel_list_id: impl Into<StateId<PanelListRange>>,
2193 mut update: impl FnMut(Option<(usize, usize)>) + Send + 'static,
2194) -> UiNode {
2195 let panel_list_id = panel_list_id.into();
2196 let mut version = None;
2197 match_node(child, move |_, op| match op {
2198 UiNodeOp::Deinit => {
2199 update(None);
2200 version = None;
2201 }
2202 UiNodeOp::Update { .. } => {
2203 let info = WIDGET.info();
2204 if let Some(parent) = info.parent()
2205 && let Some(mut iter) = PanelListRange::update(&parent, panel_list_id, &mut version)
2206 {
2207 let id = info.id();
2208 let mut p = 0;
2209 let mut count = 0;
2210 for c in &mut iter {
2211 if c.id() == id {
2212 p = count;
2213 count += 1 + iter.count();
2214 break;
2215 } else {
2216 count += 1;
2217 }
2218 }
2219 update(Some((p, count)));
2220 }
2221 }
2222 _ => {}
2223 })
2224}
2225
2226pub fn presenter<D: VarValue>(data: impl IntoVar<D>, wgt_fn: impl IntoVar<WidgetFn<D>>) -> UiNode {
2236 let data = data.into_var();
2237 let wgt_fn = wgt_fn.into_var();
2238
2239 match_node(UiNode::nil(), move |c, op| match op {
2240 UiNodeOp::Init => {
2241 WIDGET.sub_var(&data).sub_var(&wgt_fn);
2242 *c.node() = wgt_fn.get()(data.get());
2243 }
2244 UiNodeOp::Deinit => {
2245 c.deinit();
2246 *c.node() = UiNode::nil();
2247 }
2248 UiNodeOp::Update { .. } => {
2249 if data.is_new() || wgt_fn.is_new() {
2250 c.node().deinit();
2251 *c.node() = wgt_fn.get()(data.get());
2252 c.node().init();
2253 c.delegated();
2254 WIDGET.update_info().layout().render();
2255 }
2256 }
2257 _ => {}
2258 })
2259}
2260
2261pub fn presenter_opt<D: VarValue>(data: impl IntoVar<Option<D>>, wgt_fn: impl IntoVar<WidgetFn<D>>) -> UiNode {
2267 let data = data.into_var();
2268 let wgt_fn = wgt_fn.into_var();
2269
2270 match_node(UiNode::nil(), move |c, op| match op {
2271 UiNodeOp::Init => {
2272 WIDGET.sub_var(&data).sub_var(&wgt_fn);
2273 if let Some(data) = data.get() {
2274 *c.node() = wgt_fn.get()(data);
2275 }
2276 }
2277 UiNodeOp::Deinit => {
2278 c.deinit();
2279 *c.node() = UiNode::nil();
2280 }
2281 UiNodeOp::Update { .. } => {
2282 if data.is_new() || wgt_fn.is_new() {
2283 if let Some(data) = data.get() {
2284 c.node().deinit();
2285 *c.node() = wgt_fn.get()(data);
2286 c.node().init();
2287 c.delegated();
2288 WIDGET.update_info().layout().render();
2289 } else if !c.node().is_nil() {
2290 c.node().deinit();
2291 *c.node() = UiNode::nil();
2292 c.delegated();
2293 WIDGET.update_info().layout().render();
2294 }
2295 }
2296 }
2297 _ => {}
2298 })
2299}
2300
2301pub fn list_presenter<D: VarValue>(list: impl IntoVar<ObservableVec<D>>, item_fn: impl IntoVar<WidgetFn<D>>) -> UiNode {
2307 ListPresenter {
2308 list: list.into_var(),
2309 item_fn: item_fn.into_var(),
2310 view: ui_vec![],
2311 _e: std::marker::PhantomData,
2312 }
2313 .into_node()
2314}
2315
2316pub fn list_presenter_from_iter<D, L>(list: impl IntoVar<L>, item_fn: impl IntoVar<WidgetFn<D>>) -> UiNode
2322where
2323 D: VarValue,
2324 L: IntoIterator<Item = D> + VarValue,
2325{
2326 ListPresenterFromIter {
2327 list: list.into_var(),
2328 item_fn: item_fn.into_var(),
2329 view: ui_vec![],
2330 _e: std::marker::PhantomData,
2331 }
2332 .into_node()
2333}
2334
2335struct ListPresenter<D>
2336where
2337 D: VarValue,
2338{
2339 list: Var<ObservableVec<D>>,
2340 item_fn: Var<WidgetFn<D>>,
2341 view: UiVec,
2342 _e: std::marker::PhantomData<D>,
2343}
2344
2345impl<D> UiNodeImpl for ListPresenter<D>
2346where
2347 D: VarValue,
2348{
2349 fn children_len(&self) -> usize {
2350 self.view.len()
2351 }
2352
2353 fn with_child(&mut self, index: usize, visitor: &mut dyn FnMut(&mut UiNode)) {
2354 self.view.with_child(index, visitor)
2355 }
2356
2357 fn is_list(&self) -> bool {
2358 true
2359 }
2360
2361 fn for_each_child(&mut self, visitor: &mut dyn FnMut(usize, &mut UiNode)) {
2362 self.view.for_each_child(visitor);
2363 }
2364
2365 fn try_for_each_child(
2366 &mut self,
2367 visitor: &mut dyn FnMut(usize, &mut UiNode) -> std::ops::ControlFlow<BoxAnyVarValue>,
2368 ) -> std::ops::ControlFlow<BoxAnyVarValue> {
2369 self.view.try_for_each_child(visitor)
2370 }
2371
2372 fn par_each_child(&mut self, visitor: &(dyn Fn(usize, &mut UiNode) + Sync)) {
2373 self.view.par_each_child(visitor);
2374 }
2375
2376 fn par_fold_reduce(
2377 &mut self,
2378 identity: BoxAnyVarValue,
2379 fold: &(dyn Fn(BoxAnyVarValue, usize, &mut UiNode) -> BoxAnyVarValue + Sync),
2380 reduce: &(dyn Fn(BoxAnyVarValue, BoxAnyVarValue) -> BoxAnyVarValue + Sync),
2381 ) -> BoxAnyVarValue {
2382 self.view.par_fold_reduce(identity, fold, reduce)
2383 }
2384
2385 fn init(&mut self) {
2386 debug_assert!(self.view.is_empty());
2387 self.view.clear();
2388
2389 WIDGET.sub_var(&self.list).sub_var(&self.item_fn);
2390
2391 let e_fn = self.item_fn.get();
2392 self.list.with(|l| {
2393 for el in l.iter() {
2394 let child = e_fn(el.clone());
2395 self.view.push(child);
2396 }
2397 });
2398
2399 self.view.init();
2400 }
2401
2402 fn deinit(&mut self) {
2403 self.view.deinit();
2404 self.view.clear();
2405 }
2406
2407 fn update(&mut self, updates: &WidgetUpdates) {
2408 self.update_list(updates, &mut ());
2409 }
2410
2411 fn update_list(&mut self, updates: &WidgetUpdates, observer: &mut dyn UiNodeListObserver) {
2412 let mut need_reset = self.item_fn.is_new();
2413
2414 let is_new = self
2415 .list
2416 .with_new(|l| {
2417 need_reset |= l.changes().is_empty() || l.changes() == [VecChange::Clear];
2418
2419 if need_reset {
2420 return;
2421 }
2422
2423 self.view.update_list(updates, observer);
2425
2426 let e_fn = self.item_fn.get();
2427
2428 for change in l.changes() {
2429 match change {
2430 VecChange::Insert { index, count } => {
2431 for i in *index..(*index + count) {
2432 let mut el = e_fn(l[i].clone());
2433 el.init();
2434 self.view.insert(i, el);
2435 observer.inserted(i);
2436 }
2437 }
2438 VecChange::Remove { index, count } => {
2439 let mut count = *count;
2440 let index = *index;
2441 while count > 0 {
2442 count -= 1;
2443
2444 let mut el = self.view.remove(index);
2445 el.deinit();
2446 observer.removed(index);
2447 }
2448 }
2449 VecChange::Move { from_index, to_index } => {
2450 let el = self.view.remove(*from_index);
2451 self.view.insert(*to_index, el);
2452 observer.moved(*from_index, *to_index);
2453 }
2454 VecChange::Clear => unreachable!(),
2455 }
2456 }
2457 })
2458 .is_some();
2459
2460 if !need_reset && !is_new && self.list.with(|l| l.len() != self.view.len()) {
2461 need_reset = true;
2462 }
2463
2464 if need_reset {
2465 self.view.deinit();
2466 self.view.clear();
2467
2468 let e_fn = self.item_fn.get();
2469 self.list.with(|l| {
2470 for el in l.iter() {
2471 let child = e_fn(el.clone());
2472 self.view.push(child);
2473 }
2474 });
2475
2476 self.view.init();
2477 } else if !is_new {
2478 self.view.update_list(updates, observer);
2479 }
2480 }
2481
2482 fn info(&mut self, info: &mut zng_app::widget::info::WidgetInfoBuilder) {
2483 self.view.info(info);
2484 }
2485
2486 fn event(&mut self, update: &zng_app::update::EventUpdate) {
2487 self.view.event(update);
2488 }
2489
2490 fn measure(&mut self, wm: &mut zng_app::widget::info::WidgetMeasure) -> PxSize {
2491 self.view.measure(wm)
2492 }
2493
2494 fn measure_list(
2495 &mut self,
2496 wm: &mut zng_app::widget::info::WidgetMeasure,
2497 measure: &(dyn Fn(usize, &mut UiNode, &mut zng_app::widget::info::WidgetMeasure) -> PxSize + Sync),
2498 fold_size: &(dyn Fn(PxSize, PxSize) -> PxSize + Sync),
2499 ) -> PxSize {
2500 self.view.measure_list(wm, measure, fold_size)
2501 }
2502
2503 fn layout(&mut self, wl: &mut zng_app::widget::info::WidgetLayout) -> PxSize {
2504 self.view.layout(wl)
2505 }
2506
2507 fn layout_list(
2508 &mut self,
2509 wl: &mut zng_app::widget::info::WidgetLayout,
2510 layout: &(dyn Fn(usize, &mut UiNode, &mut zng_app::widget::info::WidgetLayout) -> PxSize + Sync),
2511 fold_size: &(dyn Fn(PxSize, PxSize) -> PxSize + Sync),
2512 ) -> PxSize {
2513 self.view.layout_list(wl, layout, fold_size)
2514 }
2515
2516 fn render(&mut self, frame: &mut FrameBuilder) {
2517 self.view.render(frame);
2518 }
2519
2520 fn render_list(&mut self, frame: &mut FrameBuilder, render: &(dyn Fn(usize, &mut UiNode, &mut FrameBuilder) + Sync)) {
2521 self.view.render_list(frame, render);
2522 }
2523
2524 fn render_update(&mut self, update: &mut zng_app::render::FrameUpdate) {
2525 self.view.render_update(update);
2526 }
2527
2528 fn render_update_list(
2529 &mut self,
2530 update: &mut zng_app::render::FrameUpdate,
2531 render_update: &(dyn Fn(usize, &mut UiNode, &mut zng_app::render::FrameUpdate) + Sync),
2532 ) {
2533 self.view.render_update_list(update, render_update);
2534 }
2535
2536 fn as_widget(&mut self) -> Option<&mut dyn WidgetUiNodeImpl> {
2537 None
2538 }
2539}
2540
2541struct ListPresenterFromIter<D, L>
2542where
2543 D: VarValue,
2544 L: IntoIterator<Item = D> + VarValue,
2545{
2546 list: Var<L>,
2547 item_fn: Var<WidgetFn<D>>,
2548 view: UiVec,
2549 _e: std::marker::PhantomData<(D, L)>,
2550}
2551
2552impl<D, L> UiNodeImpl for ListPresenterFromIter<D, L>
2553where
2554 D: VarValue,
2555 L: IntoIterator<Item = D> + VarValue,
2556{
2557 fn children_len(&self) -> usize {
2558 self.view.len()
2559 }
2560
2561 fn with_child(&mut self, index: usize, visitor: &mut dyn FnMut(&mut UiNode)) {
2562 self.view.with_child(index, visitor)
2563 }
2564
2565 fn for_each_child(&mut self, visitor: &mut dyn FnMut(usize, &mut UiNode)) {
2566 self.view.for_each_child(visitor)
2567 }
2568
2569 fn try_for_each_child(
2570 &mut self,
2571 visitor: &mut dyn FnMut(usize, &mut UiNode) -> std::ops::ControlFlow<BoxAnyVarValue>,
2572 ) -> std::ops::ControlFlow<BoxAnyVarValue> {
2573 self.view.try_for_each_child(visitor)
2574 }
2575
2576 fn par_each_child(&mut self, visitor: &(dyn Fn(usize, &mut UiNode) + Sync)) {
2577 self.view.par_each_child(visitor);
2578 }
2579
2580 fn par_fold_reduce(
2581 &mut self,
2582 identity: BoxAnyVarValue,
2583 fold: &(dyn Fn(BoxAnyVarValue, usize, &mut UiNode) -> BoxAnyVarValue + Sync),
2584 reduce: &(dyn Fn(BoxAnyVarValue, BoxAnyVarValue) -> BoxAnyVarValue + Sync),
2585 ) -> BoxAnyVarValue {
2586 self.view.par_fold_reduce(identity, fold, reduce)
2587 }
2588
2589 fn is_list(&self) -> bool {
2590 true
2591 }
2592
2593 fn init(&mut self) {
2594 debug_assert!(self.view.is_empty());
2595 self.view.clear();
2596
2597 WIDGET.sub_var(&self.list).sub_var(&self.item_fn);
2598
2599 let e_fn = self.item_fn.get();
2600
2601 self.view.extend(self.list.get().into_iter().map(&*e_fn));
2602 self.view.init();
2603 }
2604
2605 fn deinit(&mut self) {
2606 self.view.deinit();
2607 self.view.clear();
2608 }
2609
2610 fn update(&mut self, updates: &WidgetUpdates) {
2611 self.update_list(updates, &mut ())
2612 }
2613 fn update_list(&mut self, updates: &WidgetUpdates, observer: &mut dyn UiNodeListObserver) {
2614 if self.list.is_new() || self.item_fn.is_new() {
2615 self.view.deinit();
2616 self.view.clear();
2617 let e_fn = self.item_fn.get();
2618 self.view.extend(self.list.get().into_iter().map(&*e_fn));
2619 self.view.init();
2620 observer.reset();
2621 } else {
2622 self.view.update_list(updates, observer);
2623 }
2624 }
2625
2626 fn info(&mut self, info: &mut zng_app::widget::info::WidgetInfoBuilder) {
2627 self.view.info(info)
2628 }
2629
2630 fn event(&mut self, update: &zng_app::update::EventUpdate) {
2631 self.view.event(update);
2632 }
2633
2634 fn measure(&mut self, wm: &mut zng_app::widget::info::WidgetMeasure) -> PxSize {
2635 self.view.measure(wm)
2636 }
2637
2638 fn measure_list(
2639 &mut self,
2640 wm: &mut zng_app::widget::info::WidgetMeasure,
2641 measure: &(dyn Fn(usize, &mut UiNode, &mut zng_app::widget::info::WidgetMeasure) -> PxSize + Sync),
2642 fold_size: &(dyn Fn(PxSize, PxSize) -> PxSize + Sync),
2643 ) -> PxSize {
2644 self.view.measure_list(wm, measure, fold_size)
2645 }
2646
2647 fn layout(&mut self, wl: &mut zng_app::widget::info::WidgetLayout) -> PxSize {
2648 self.view.layout(wl)
2649 }
2650
2651 fn layout_list(
2652 &mut self,
2653 wl: &mut zng_app::widget::info::WidgetLayout,
2654 layout: &(dyn Fn(usize, &mut UiNode, &mut zng_app::widget::info::WidgetLayout) -> PxSize + Sync),
2655 fold_size: &(dyn Fn(PxSize, PxSize) -> PxSize + Sync),
2656 ) -> PxSize {
2657 self.view.layout_list(wl, layout, fold_size)
2658 }
2659
2660 fn render(&mut self, frame: &mut FrameBuilder) {
2661 self.view.render(frame);
2662 }
2663
2664 fn render_list(&mut self, frame: &mut FrameBuilder, render: &(dyn Fn(usize, &mut UiNode, &mut FrameBuilder) + Sync)) {
2665 self.view.render_list(frame, render);
2666 }
2667
2668 fn render_update(&mut self, update: &mut zng_app::render::FrameUpdate) {
2669 self.view.render_update(update);
2670 }
2671
2672 fn render_update_list(
2673 &mut self,
2674 update: &mut zng_app::render::FrameUpdate,
2675 render_update: &(dyn Fn(usize, &mut UiNode, &mut zng_app::render::FrameUpdate) + Sync),
2676 ) {
2677 self.view.render_update_list(update, render_update);
2678 }
2679
2680 fn as_widget(&mut self) -> Option<&mut dyn WidgetUiNodeImpl> {
2681 None
2682 }
2683}
2684
2685pub trait VarPresent<D: VarValue> {
2687 fn present(&self, wgt_fn: impl IntoVar<WidgetFn<D>>) -> UiNode;
2689}
2690impl<D: VarValue> VarPresent<D> for Var<D> {
2691 fn present(&self, wgt_fn: impl IntoVar<WidgetFn<D>>) -> UiNode {
2692 presenter(self.clone(), wgt_fn)
2693 }
2694}
2695
2696pub trait VarPresentOpt<D: VarValue> {
2698 fn present_opt(&self, wgt_fn: impl IntoVar<WidgetFn<D>>) -> UiNode;
2700}
2701impl<D: VarValue> VarPresentOpt<D> for Var<Option<D>> {
2702 fn present_opt(&self, wgt_fn: impl IntoVar<WidgetFn<D>>) -> UiNode {
2703 presenter_opt(self.clone(), wgt_fn)
2704 }
2705}
2706
2707pub trait VarPresentList<D: VarValue> {
2709 fn present_list(&self, wgt_fn: impl IntoVar<WidgetFn<D>>) -> UiNode;
2711}
2712impl<D: VarValue> VarPresentList<D> for Var<ObservableVec<D>> {
2713 fn present_list(&self, wgt_fn: impl IntoVar<WidgetFn<D>>) -> UiNode {
2714 list_presenter(self.clone(), wgt_fn)
2715 }
2716}
2717
2718pub trait VarPresentListFromIter<D: VarValue, L: IntoIterator<Item = D> + VarValue> {
2720 fn present_list_from_iter(&self, wgt_fn: impl IntoVar<WidgetFn<D>>) -> UiNode;
2722}
2723impl<D: VarValue, L: IntoIterator<Item = D> + VarValue> VarPresentListFromIter<D, L> for Var<L> {
2724 fn present_list_from_iter(&self, wgt_fn: impl IntoVar<WidgetFn<D>>) -> UiNode {
2725 list_presenter_from_iter(self.clone(), wgt_fn)
2726 }
2727}
2728
2729pub trait VarPresentData<D: VarValue> {
2731 fn present_data(&self, data: impl IntoVar<D>) -> UiNode;
2733}
2734impl<D: VarValue> VarPresentData<D> for Var<WidgetFn<D>> {
2735 fn present_data(&self, data: impl IntoVar<D>) -> UiNode {
2736 presenter(data, self.clone())
2737 }
2738}
2739
2740#[doc(inline)]
2741pub use crate::command_property;
2742#[doc(inline)]
2743pub use crate::event_property;