1use std::{
2 any::{Any, TypeId},
3 collections::{HashMap, hash_map},
4 fmt, mem,
5 ops::{self, ControlFlow},
6 thread::ThreadId,
7};
8
9use crate::{
10 APP,
11 handler::{Handler, HandlerExt},
12 shortcut::CommandShortcutExt,
13 widget::info::{WidgetInfo, WidgetPath},
14 window::{WINDOWS_APP, WindowId},
15};
16
17use super::*;
18
19#[macro_export]
152macro_rules! command {
153 ($(
154 $(#[$attr:meta])*
155 $vis:vis static $COMMAND:ident $({ $($meta_ident:ident $(!)? : $meta_init:expr),* $(,)? };)? $(;)?
156 )+) => {
157 $(
158 $crate::__command! {
159 $(#[$attr])*
160 $vis static $COMMAND $({
161 $($meta_ident: $meta_init,)+
162 })? ;
163 }
164 )+
165 }
166}
167#[doc(inline)]
168pub use command;
169
170use zng_state_map::{OwnedStateMap, StateId, StateMapMut, StateValue};
171use zng_task::parking_lot::Mutex;
172use zng_txt::Txt;
173use zng_unique_id::{static_id, unique_id_64};
174use zng_var::{Var, VarHandles, VarValue, const_var, impl_from_and_into_var, var};
175
176#[doc(hidden)]
177pub use zng_app_context::app_local;
178
179#[doc(hidden)]
180pub use pastey::paste;
181
182#[doc(hidden)]
183#[macro_export]
184macro_rules! __command {
185 (
186 $(#[$attr:meta])*
187 $vis:vis static $COMMAND:ident { l10n: $l10n_arg:expr, $($meta_ident:ident : $meta_init:expr),* $(,)? };
188 ) => {
189 $(#[$attr])*
190 $(#[doc = concat!("* `", stringify!($meta_ident), "`")])+
196 $vis static $COMMAND: $crate::event::Command = {
199 fn __meta_init__(cmd: $crate::event::Command) {
200 let __l10n_arg = $l10n_arg;
201 $crate::event::paste! {$(
202 cmd.[<init_ $meta_ident>]($meta_init);
203 $crate::event::init_meta_l10n(
204 std::env!("CARGO_PKG_NAME"),
205 std::env!("CARGO_PKG_VERSION"),
206 &__l10n_arg,
207 cmd,
208 stringify!($meta_ident),
209 &cmd.$meta_ident()
210 );
211 )*}
212 }
213 $crate::event::app_local! {
214 static EVENT: $crate::event::EventData = $crate::event::EventData::new::<$crate::event::CommandArgs>();
215 static DATA: $crate::event::CommandData = $crate::event::CommandData::new(__meta_init__, stringify!($COMMAND));
216 }
217 $crate::event::Command::new(&EVENT, &DATA)
218 };
219 };
220 (
221 $(#[$attr:meta])*
222 $vis:vis static $COMMAND:ident { $($meta_ident:ident : $meta_init:expr),* $(,)? };
223 ) => {
224 $(#[$attr])*
225 $(#[doc = concat!("* `", stringify!($meta_ident), "`")])+
231 $vis static $COMMAND: $crate::event::Command = {
232 fn __meta_init__(cmd: $crate::event::Command) {
233 $crate::event::paste! {$(
234 cmd.[<init_ $meta_ident>]($meta_init);
235 )*}
236 }
237 $crate::event::app_local! {
238 static EVENT: $crate::event::EventData = $crate::event::EventData::new::<$crate::event::CommandArgs>();
239 static DATA: $crate::event::CommandData = $crate::event::CommandData::new(__meta_init__, stringify!($COMMAND));
240 }
241 $crate::event::Command::new(&EVENT, &DATA)
242 };
243 };
244 (
245 $(#[$attr:meta])*
246 $vis:vis static $COMMAND:ident;
247 ) => {
248 $(#[$attr])*
249 $vis static $COMMAND: $crate::event::Command = {
250 fn __meta_init__(_: $crate::event::Command) {
251 }
252 $crate::event::app_local! {
253 static EVENT: $crate::event::EventData = $crate::event::EventData::new::<$crate::event::CommandArgs>();
254 static DATA: $crate::event::CommandData = $crate::event::CommandData::new(__meta_init__, stringify!($COMMAND));
255 }
256 $crate::event::Command::new(&EVENT, &DATA)
257 };
258 };
259}
260
261#[doc(hidden)]
262pub fn init_meta_l10n(
263 pkg_name: &'static str,
264 pkg_version: &'static str,
265 l10n_arg: &dyn Any,
266 cmd: Command,
267 meta_name: &'static str,
268 meta_value: &dyn Any,
269) {
270 if let Some(txt) = meta_value.downcast_ref::<CommandMetaVar<Txt>>() {
271 let mut l10n_file = "";
272
273 if let Some(&enabled) = l10n_arg.downcast_ref::<bool>() {
274 if !enabled {
275 return;
276 }
277 } else if let Some(&file) = l10n_arg.downcast_ref::<&'static str>() {
278 l10n_file = file;
279 } else {
280 tracing::error!("unknown l10n value in {:?}", cmd.event());
281 return;
282 }
283
284 EVENTS_L10N.init_meta_l10n([pkg_name, pkg_version, l10n_file], cmd, meta_name, txt.clone());
285 }
286}
287
288#[derive(Clone, Copy)]
327pub struct Command {
328 event: Event<CommandArgs>,
329 local: &'static AppLocal<CommandData>,
330 scope: CommandScope,
331}
332struct CommandDbg {
333 static_name: &'static str,
334 scope: CommandScope,
335 state: Option<[usize; 2]>,
336}
337impl CommandDbg {
338 fn new(static_name: &'static str, scope: CommandScope) -> Self {
339 Self {
340 static_name,
341 scope,
342 state: None,
343 }
344 }
345}
346impl fmt::Debug for CommandDbg {
347 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
348 if f.alternate() {
349 let mut d = f.debug_struct("Command");
350 d.field("static_name", &self.static_name).field("scope", &self.scope);
351 if let Some([has, enabled]) = &self.state {
352 d.field("handle_count", has);
353 d.field("enabled_count", enabled);
354 }
355
356 d.finish_non_exhaustive()
357 } else {
358 write!(f, "{}", self.static_name)?;
359 match self.scope {
360 CommandScope::App => Ok(()),
361 CommandScope::Window(id) => write!(f, "({id})"),
362 CommandScope::Widget(id) => write!(f, "({id})"),
363 }
364 }
365 }
366}
367impl fmt::Debug for Command {
368 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
369 let dbg = if let Some(d) = self.local.try_read() {
370 let mut dbg = CommandDbg::new(d.static_name, self.scope);
371 dbg.state = Some([d.handle_count, d.enabled_count]);
372 dbg
373 } else {
374 CommandDbg::new("<locked>", self.scope)
375 };
376 fmt::Debug::fmt(&dbg, f)
377 }
378}
379impl Command {
380 #[doc(hidden)]
381 pub const fn new(event_local: &'static AppLocal<EventData>, command_local: &'static AppLocal<CommandData>) -> Self {
382 Command {
383 event: Event::new(event_local),
384 local: command_local,
385 scope: CommandScope::App,
386 }
387 }
388
389 pub fn subscribe(&self, enabled: bool) -> CommandHandle {
396 self.local.write().subscribe(*self, enabled, None)
397 }
398
399 pub fn subscribe_wgt(&self, enabled: bool, target: WidgetId) -> CommandHandle {
407 self.local.write().subscribe(*self, enabled, Some(target))
408 }
409
410 pub fn event(&self) -> Event<CommandArgs> {
412 self.event
413 }
414
415 pub fn scope(&self) -> CommandScope {
417 self.scope
418 }
419
420 pub fn scoped(mut self, scope: impl Into<CommandScope>) -> Command {
422 self.scope = scope.into();
423 self
424 }
425
426 pub fn with_meta<R>(&self, visit: impl FnOnce(&mut CommandMeta) -> R) -> R {
435 fn init_meta(self_: &Command) -> parking_lot::MappedRwLockReadGuard<'static, CommandData> {
437 {
438 let mut write = self_.local.write();
439 match write.meta_init.clone() {
440 MetaInit::Init(init) => {
441 let lock = Arc::new((std::thread::current().id(), Mutex::new(())));
442 write.meta_init = MetaInit::Initing(lock.clone());
443 let _init_guard = lock.1.lock();
444 drop(write);
445 init(self_.scoped(CommandScope::App));
446 self_.local.write().meta_init = MetaInit::Inited;
447 }
448 MetaInit::Initing(l) => {
449 drop(write);
450 if l.0 != std::thread::current().id() {
451 let _wait = l.1.lock();
452 }
453 }
454 MetaInit::Inited => {}
455 }
456 }
457
458 if !matches!(self_.scope, CommandScope::App) {
459 let mut write = self_.local.write();
460 write.scopes.entry(self_.scope).or_default();
461 }
462 self_.local.read()
463 }
464 let local_read = init_meta(self);
465 let mut meta_lock = local_read.meta.lock();
466
467 match self.scope {
468 CommandScope::App => visit(&mut CommandMeta {
469 meta: meta_lock.borrow_mut(),
470 scope: None,
471 }),
472 scope => {
473 let scope = local_read.scopes.get(&scope).unwrap();
474 visit(&mut CommandMeta {
475 meta: meta_lock.borrow_mut(),
476 scope: Some(scope.meta.lock().borrow_mut()),
477 })
478 }
479 }
480 }
481
482 pub fn has_handlers(&self) -> Var<bool> {
484 match self.scope {
485 CommandScope::App => self.local.read().has_handlers.read_only(),
486 scope => {
487 let mut write = self.local.write();
491 let entry = write.scopes.entry(scope).or_default();
492
493 entry.observer_count += 1;
495 let observer = var(entry.has_handlers.get());
496 entry.has_handlers.set_bind(&observer).perm();
497
498 let command = *self;
500 observer
501 .hook_drop(move || {
502 if !APP.is_started() {
503 return;
504 }
505 let mut write = command.local.write();
506 let mut entry = match write.scopes.entry(scope) {
507 hash_map::Entry::Occupied(e) => e,
508 hash_map::Entry::Vacant(_) => unreachable!(),
509 };
510 entry.get_mut().observer_count -= 1;
511 if entry.get().observer_count == 0 && entry.get().handle_count == 0 {
512 entry.remove();
513 EVENTS.unregister_command(command, write.static_name);
514 }
515 })
516 .perm();
517
518 observer.read_only()
519 }
520 }
521 }
522
523 pub fn is_enabled(&self) -> Var<bool> {
525 match self.scope {
526 CommandScope::App => self.local.read().is_enabled.read_only(),
527 scope => {
528 let mut write = self.local.write();
529 let entry = write.scopes.entry(scope).or_default();
530
531 entry.observer_count += 1;
532 let observer = var(entry.is_enabled.get());
533 entry.is_enabled.set_bind(&observer).perm();
534
535 let command = *self;
536 observer
537 .hook_drop(move || {
538 if !APP.is_started() {
539 return;
540 }
541
542 let mut write = command.local.write();
543 let mut entry = match write.scopes.entry(scope) {
544 hash_map::Entry::Occupied(e) => e,
545 hash_map::Entry::Vacant(_) => unreachable!(),
546 };
547 entry.get_mut().observer_count -= 1;
548 if entry.get().observer_count == 0 && entry.get().handle_count == 0 {
549 entry.remove();
550 EVENTS.unregister_command(command, write.static_name);
551 }
552 })
553 .perm();
554
555 observer.read_only()
556 }
557 }
558 }
559
560 pub fn visit_scopes<T>(&self, mut visitor: impl FnMut(Command) -> ControlFlow<T>) -> Option<T> {
564 let read = self.local.read();
565 for &scope in read.scopes.keys() {
566 match visitor(self.scoped(scope)) {
567 ControlFlow::Continue(_) => continue,
568 ControlFlow::Break(r) => return Some(r),
569 }
570 }
571 None
572 }
573
574 pub fn notify(&self) {
576 self.event.notify(CommandArgs::now(
577 None,
578 self.scope,
579 self.scope.search_target(),
580 self.is_enabled().get(),
581 ))
582 }
583
584 pub fn notify_descendants(&self, parent: &WidgetInfo) {
586 self.visit_scopes::<()>(|parse_cmd| {
587 if let CommandScope::Widget(id) = parse_cmd.scope()
588 && let Some(scope) = parent.tree().get(id)
589 && scope.is_descendant(parent)
590 {
591 parse_cmd.notify();
592 }
593 ControlFlow::Continue(())
594 });
595 }
596
597 pub fn notify_param(&self, param: impl Any + Send + Sync) {
599 self.event.notify(CommandArgs::now(
600 CommandParam::new(param),
601 self.scope,
602 self.scope.search_target(),
603 self.is_enabled().get(),
604 ));
605 }
606
607 pub fn notify_linked(&self, propagation: EventPropagationHandle, param: Option<CommandParam>) {
609 self.event.notify(CommandArgs::new(
610 crate::INSTANT.now(),
611 propagation,
612 param,
613 self.scope,
614 self.scope.search_target(),
615 self.is_enabled().get(),
616 ))
617 }
618
619 pub fn each_update(&self, direct_scope_only: bool, ignore_propagation: bool, mut handler: impl FnMut(&CommandArgs)) {
625 self.event.each_update(ignore_propagation, move |args| {
626 if args.scope_matches(direct_scope_only, self.scope) {
627 handler(args);
628 }
629 });
630 }
631
632 pub fn latest_update<O>(
636 &self,
637 direct_scope_only: bool,
638 ignore_propagation: bool,
639 handler: impl FnOnce(&CommandArgs) -> O,
640 ) -> Option<O> {
641 let mut r = None;
642 self.event.latest_update(ignore_propagation, |args| {
643 if args.scope_matches(direct_scope_only, self.scope) {
644 r = Some(handler(args));
645 }
646 });
647 r
648 }
649
650 pub fn has_update(&self, direct_scope_only: bool, ignore_propagation: bool) -> bool {
654 self.latest_update(direct_scope_only, ignore_propagation, |_| true).unwrap_or(false)
655 }
656
657 pub fn on_pre_event(
668 &self,
669 init_enabled: bool,
670 direct_scope_only: bool,
671 ignore_propagation: bool,
672 handler: Handler<CommandArgs>,
673 ) -> CommandHandle {
674 let (mut handle, handler) = self.event_handler(init_enabled, direct_scope_only, handler);
675 handle._handles.push(self.event().on_pre_event(ignore_propagation, handler));
676 handle
677 }
678
679 pub fn on_event(
690 &self,
691 init_enabled: bool,
692 direct_scope_only: bool,
693 ignore_propagation: bool,
694 handler: Handler<CommandArgs>,
695 ) -> CommandHandle {
696 let (mut handle, handler) = self.event_handler(init_enabled, direct_scope_only, handler);
697 handle._handles.push(self.event().on_event(ignore_propagation, handler));
698 handle
699 }
700
701 fn event_handler(
702 &self,
703 init_enabled: bool,
704 direct_scope_only: bool,
705 handler: Handler<CommandArgs>,
706 ) -> (CommandHandle, Handler<CommandArgs>) {
707 let handle = self.subscribe(init_enabled);
708 let local_enabled = handle.enabled().clone();
709 let handler = if direct_scope_only {
710 let scope = self.scope();
711 handler.filtered(move |a| a.scope == scope && local_enabled.get())
712 } else {
713 match self.scope() {
714 CommandScope::App => handler.filtered(move |_| local_enabled.get()),
715 CommandScope::Window(id) => {
716 handler.filtered(move |a| a.target.as_ref().map(|t| t.window_id() == id).unwrap_or(false) && local_enabled.get())
717 }
718 CommandScope::Widget(id) => {
719 handler.filtered(move |a| a.target.as_ref().map(|t| t.contains(id)).unwrap_or(false) && local_enabled.get())
720 }
721 }
722 };
723 (handle, handler)
724 }
725
726 pub fn on_pre_event_with_enabled(
731 &self,
732 init_enabled: bool,
733 direct_scope_only: bool,
734 ignore_propagation: bool,
735 handler: Handler<(CommandArgs, Var<bool>)>,
736 ) -> CommandHandle {
737 let (mut handle, handler) = self.event_handler_with_enabled(init_enabled, direct_scope_only, handler);
738 handle._handles.push(self.event().on_pre_event(ignore_propagation, handler));
739 handle
740 }
741
742 pub fn on_event_with_enabled(
747 &self,
748 init_enabled: bool,
749 direct_scope_only: bool,
750 ignore_propagation: bool,
751 handler: Handler<(CommandArgs, Var<bool>)>,
752 ) -> CommandHandle {
753 let (mut handle, handler) = self.event_handler_with_enabled(init_enabled, direct_scope_only, handler);
754 handle._handles.push(self.event().on_event(ignore_propagation, handler));
755 handle
756 }
757
758 fn event_handler_with_enabled(
759 &self,
760 init_enabled: bool,
761 direct_scope_only: bool,
762 mut handler: Handler<(CommandArgs, Var<bool>)>,
763 ) -> (CommandHandle, Handler<CommandArgs>) {
764 let handle = self.subscribe(init_enabled);
765 let local_enabled = handle.enabled().clone();
766
767 let r: Handler<CommandArgs>;
768 if direct_scope_only {
769 let scope = self.scope();
770 r = Box::new(move |a: &CommandArgs| {
771 if a.scope == scope {
772 handler(&(a.clone(), local_enabled.clone()))
773 } else {
774 HandlerResult::Done
775 }
776 });
777 } else {
778 match self.scope() {
779 CommandScope::App => r = Box::new(move |a: &CommandArgs| handler(&(a.clone(), local_enabled.clone()))),
780 CommandScope::Window(id) => {
781 r = Box::new(move |a: &CommandArgs| {
782 if a.target.as_ref().map(|t| t.window_id() == id).unwrap_or(false) {
783 handler(&(a.clone(), local_enabled.clone()))
784 } else {
785 HandlerResult::Done
786 }
787 })
788 }
789 CommandScope::Widget(id) => {
790 r = Box::new(move |a: &CommandArgs| {
791 if a.target.as_ref().map(|t| t.contains(id)).unwrap_or(false) {
792 handler(&(a.clone(), local_enabled.clone()))
793 } else {
794 HandlerResult::Done
795 }
796 })
797 }
798 }
799 };
800 (handle, r)
801 }
802
803 pub fn static_name(&self) -> &'static str {
805 self.local.read().static_name
806 }
807}
808impl ops::Deref for Command {
809 type Target = Event<CommandArgs>;
810
811 fn deref(&self) -> &Self::Target {
812 &self.event
813 }
814}
815impl PartialEq for Command {
816 fn eq(&self, other: &Self) -> bool {
817 self.event == other.event && self.scope == other.scope
818 }
819}
820impl Eq for Command {}
821impl std::hash::Hash for Command {
822 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
823 std::hash::Hash::hash(&self.event.as_any(), state);
824 std::hash::Hash::hash(&self.scope, state);
825 }
826}
827
828#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
832pub enum CommandScope {
833 App,
835 Window(WindowId),
840 Widget(WidgetId),
842}
843impl CommandScope {
844 pub fn search_target(self) -> Option<WidgetPath> {
846 match self {
847 CommandScope::App => None,
848 CommandScope::Window(id) => WINDOWS_APP.widget_tree(id).map(|t| t.root().path()),
849 CommandScope::Widget(id) => WINDOWS_APP.widget_info(id).map(|w| w.path()),
850 }
851 }
852}
853impl_from_and_into_var! {
854 fn from(id: WidgetId) -> CommandScope {
855 CommandScope::Widget(id)
856 }
857 fn from(id: WindowId) -> CommandScope {
858 CommandScope::Window(id)
859 }
860 fn from(widget_name: &'static str) -> CommandScope {
862 WidgetId::named(widget_name).into()
863 }
864 fn from(widget_name: Txt) -> CommandScope {
866 WidgetId::named(widget_name).into()
867 }
868}
869
870event_args! {
871 pub struct CommandArgs {
873 pub param: Option<CommandParam>,
875
876 pub scope: CommandScope,
878
879 pub target: Option<WidgetPath>,
885
886 pub enabled: bool,
894
895 ..
896
897 fn is_in_target(&self, id: WidgetId) -> bool {
899 match self.scope {
900 CommandScope::App => true,
901 _ => match &self.target {
902 Some(t) => t.contains(id),
903 None => false,
904 },
905 }
906 }
907
908 fn validate(&self) -> Result<(), Txt> {
910 if let Some(t) = &self.target {
911 match self.scope {
912 CommandScope::App => return Err("args for app scope cannot have a `target`".into()),
913 CommandScope::Window(id) => {
914 if id != t.window_id() || t.widgets_path().len() > 1 {
915 return Err("args for window scope must only `target` that window root widget".into());
916 }
917 }
918 CommandScope::Widget(id) => {
919 if id != t.widget_id() {
920 return Err("args for widget scope must only `target` that widget".into());
921 }
922 }
923 }
924 }
925 Ok(())
926 }
927 }
928}
929impl CommandArgs {
930 pub fn param<T: Any>(&self) -> Option<&T> {
932 self.param.as_ref().and_then(|p| p.downcast_ref::<T>())
933 }
934
935 pub fn enabled_param<T: Any>(&self) -> Option<&T> {
940 if self.enabled { self.param::<T>() } else { None }
941 }
942
943 pub fn disabled_param<T: Any>(&self) -> Option<&T> {
948 if !self.enabled { self.param::<T>() } else { None }
949 }
950
951 pub fn scope_matches(&self, direct_only: bool, scope: CommandScope) -> bool {
955 if direct_only {
956 self.scope == scope
957 } else {
958 match (scope, self.scope) {
959 (CommandScope::App, _) => true,
960 (CommandScope::Window(scope_id), CommandScope::Window(args_id)) => scope_id == args_id,
961 (CommandScope::Window(scope_id), CommandScope::Widget(args_id)) => {
962 if let Some(t) = &self.target {
964 t.window_id() == scope_id && t.contains(args_id)
965 } else if let Some(info) = WINDOWS_APP.widget_tree(scope_id) {
966 info.contains(args_id)
967 } else {
968 false
969 }
970 }
971 (CommandScope::Widget(scope_id), CommandScope::Widget(args_id)) => {
972 if let Some(t) = &self.target {
974 t.widgets_path().iter().position(|i| *i == scope_id).unwrap_or(usize::MAX)
975 <= t.widgets_path().iter().position(|i| *i == args_id).unwrap_or(usize::MAX)
976 } else {
977 false
978 }
979 }
980 _ => false,
981 }
982 }
983 }
984}
985
986#[must_use = "command handle indicates presence of a subscriber"]
993pub struct CommandHandle {
994 command: Option<Command>,
995 local_enabled: Var<bool>,
996 _handles: VarHandles,
998}
999impl Clone for CommandHandle {
1003 fn clone(&self) -> Self {
1004 match self.command {
1005 Some(c) => c.subscribe(self.local_enabled.get()),
1006 None => Self::dummy(),
1007 }
1008 }
1009}
1010impl CommandHandle {
1011 pub fn command(&self) -> Option<Command> {
1013 self.command
1014 }
1015
1016 pub fn enabled(&self) -> &Var<bool> {
1020 &self.local_enabled
1021 }
1022
1023 pub fn dummy() -> Self {
1025 CommandHandle {
1026 command: None,
1027 local_enabled: const_var(false),
1028 _handles: VarHandles::dummy(),
1029 }
1030 }
1031
1032 fn new(cmd: Command, event_handle: VarHandle, enabled: bool) -> Self {
1033 let mut r = Self {
1034 command: Some(cmd),
1035 local_enabled: var(enabled),
1036 _handles: VarHandles::dummy(),
1037 };
1038
1039 let mut last_applied = enabled;
1041 r._handles.push(r.local_enabled.hook(move |args| {
1042 let _hold = &event_handle;
1043 let enabled = *args.value();
1044 if last_applied != enabled {
1045 Self::update_enabled(cmd, enabled);
1046 last_applied = enabled;
1047 }
1048 true
1049 }));
1050
1051 r
1052 }
1053
1054 fn update_enabled(command: Command, enabled: bool) {
1055 let mut write = command.local.write();
1056 match command.scope {
1057 CommandScope::App => {
1058 if enabled {
1059 write.enabled_count += 1;
1060 if write.enabled_count == 1 {
1061 modify_is_enabled(&write.is_enabled, command);
1062 }
1063 tracing::trace!(
1064 "command handle {:?} enabled, count: {:?}",
1065 CommandDbg::new(write.static_name, command.scope),
1066 write.enabled_count
1067 );
1068 } else {
1069 write.enabled_count = match write.enabled_count.checked_sub(1) {
1070 Some(c) => c,
1071 None => {
1072 #[cfg(debug_assertions)]
1073 panic!("handle for {} was disabled when enabled_count was already zero", write.static_name);
1074 #[cfg(not(debug_assertions))]
1075 0
1076 }
1077 };
1078 if write.enabled_count == 0 {
1079 modify_is_enabled(&write.is_enabled, command);
1080 }
1081 tracing::trace!(
1082 "command handle {:?} disabled, count: {:?}",
1083 CommandDbg::new(write.static_name, command.scope),
1084 write.enabled_count
1085 );
1086 }
1087 }
1088 scope => {
1089 let write = &mut *write;
1090 if let Some(data) = write.scopes.get_mut(&scope) {
1091 if enabled {
1092 data.enabled_count += 1;
1093 if data.enabled_count == 1 {
1094 modify_is_enabled(&data.is_enabled, command);
1095 }
1096 tracing::trace!(
1097 "command handle {:?} enabled, count: {:?}",
1098 CommandDbg::new(write.static_name, command.scope),
1099 data.enabled_count
1100 );
1101 } else {
1102 data.enabled_count = match data.enabled_count.checked_sub(1) {
1103 Some(c) => c,
1104 None => {
1105 #[cfg(debug_assertions)]
1106 panic!(
1107 "handle for {:?} was disabled when enabled_count was already zero",
1108 CommandDbg::new(write.static_name, scope)
1109 );
1110 #[cfg(not(debug_assertions))]
1111 0
1112 }
1113 };
1114 if data.enabled_count == 0 {
1115 modify_is_enabled(&data.is_enabled, command);
1116 }
1117 tracing::trace!(
1118 "command handle {:?} enabled, count: {:?}",
1119 CommandDbg::new(write.static_name, command.scope),
1120 data.enabled_count
1121 );
1122 }
1123 }
1124 }
1125 }
1126 }
1127
1128 pub fn is_dummy(&self) -> bool {
1130 self.command.is_none()
1131 }
1132
1133 pub fn perm(mut self) {
1138 mem::replace(&mut self._handles, VarHandles::dummy()).perm();
1140 self.command = None;
1142 }
1143}
1144impl fmt::Debug for CommandHandle {
1145 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1146 f.debug_struct("CommandHandle")
1147 .field("command", &self.command)
1148 .field("enabled", &self.local_enabled.get())
1149 .finish_non_exhaustive()
1150 }
1151}
1152impl Drop for CommandHandle {
1153 fn drop(&mut self) {
1154 if let Some(command) = self.command.take()
1155 && APP.is_started()
1156 {
1157 let mut write = command.local.write();
1158 match command.scope {
1159 CommandScope::App => {
1160 write.handle_count = match write.handle_count.checked_sub(1) {
1161 Some(c) => c,
1162 None => {
1163 #[cfg(debug_assertions)]
1164 panic!("handle for {} was dropped when handle_count was already zero", write.static_name);
1165 #[cfg(not(debug_assertions))]
1166 0
1167 }
1168 };
1169 if write.handle_count == 0 {
1170 modify_has_handlers(&write.has_handlers, command);
1171 }
1172
1173 if self.local_enabled.get() {
1174 write.enabled_count = match write.enabled_count.checked_sub(1) {
1175 Some(c) => c,
1176 None => {
1177 #[cfg(debug_assertions)]
1178 panic!(
1179 "handle for enabled {} was dropped when enabled_count was already zero",
1180 write.static_name
1181 );
1182 #[cfg(not(debug_assertions))]
1183 0
1184 }
1185 };
1186
1187 if write.enabled_count == 0 {
1188 modify_is_enabled(&write.is_enabled, command);
1189 }
1190 }
1191
1192 tracing::trace!(
1193 "unsubscribe from {:?}, handle_count: {:?}, enabled_count: {:?}",
1194 CommandDbg::new(write.static_name, command.scope),
1195 write.handle_count,
1196 write.enabled_count
1197 );
1198 }
1199 scope => {
1200 let write = &mut *write;
1201 if let hash_map::Entry::Occupied(mut entry) = write.scopes.entry(scope) {
1202 let data = entry.get_mut();
1203
1204 data.handle_count = match data.handle_count.checked_sub(1) {
1205 Some(c) => c,
1206 None => {
1207 #[cfg(debug_assertions)]
1208 panic!(
1209 "handle for {:?} was dropped when handle_count was already zero",
1210 CommandDbg::new(write.static_name, scope)
1211 );
1212 #[cfg(not(debug_assertions))]
1213 0
1214 }
1215 };
1216
1217 if self.local_enabled.get() {
1218 data.enabled_count = match data.enabled_count.checked_sub(1) {
1219 Some(c) => c,
1220 None => {
1221 #[cfg(debug_assertions)]
1222 panic!(
1223 "handle for enabled {:?} was dropped when enabled_count was already zero",
1224 CommandDbg::new(write.static_name, scope)
1225 );
1226 #[cfg(not(debug_assertions))]
1227 0
1228 }
1229 };
1230
1231 if data.enabled_count == 0 {
1232 modify_is_enabled(&data.is_enabled, command);
1233 }
1234 }
1235
1236 tracing::trace!(
1237 "unsubscribe from {:?}, handle_count: {:?}, enabled_count: {:?}",
1238 CommandDbg::new(write.static_name, command.scope),
1239 data.handle_count,
1240 data.enabled_count
1241 );
1242
1243 if data.handle_count == 0 {
1244 modify_has_handlers(&data.has_handlers, command);
1245 if data.observer_count == 0 {
1246 entry.remove();
1247 EVENTS.unregister_command(command, write.static_name);
1248 }
1249 }
1250 }
1251 }
1252 }
1253 }
1254 }
1255}
1256impl Default for CommandHandle {
1257 fn default() -> Self {
1258 Self::dummy()
1259 }
1260}
1261
1262#[derive(Clone)]
1264#[non_exhaustive]
1265pub struct CommandParam(pub Arc<dyn Any + Send + Sync>);
1266impl PartialEq for CommandParam {
1267 fn eq(&self, other: &Self) -> bool {
1268 Arc::ptr_eq(&self.0, &other.0)
1269 }
1270}
1271impl Eq for CommandParam {}
1272impl CommandParam {
1273 pub fn new(param: impl Any + Send + Sync + 'static) -> Self {
1277 let p: &dyn Any = ¶m;
1278 if let Some(p) = p.downcast_ref::<Self>() {
1279 p.clone()
1280 } else if let Some(p) = p.downcast_ref::<Arc<dyn Any + Send + Sync>>() {
1281 CommandParam(p.clone())
1282 } else {
1283 CommandParam(Arc::new(param))
1284 }
1285 }
1286
1287 pub fn type_id(&self) -> TypeId {
1289 self.0.type_id()
1290 }
1291
1292 pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
1294 self.0.downcast_ref()
1295 }
1296
1297 pub fn is<T: Any>(&self) -> bool {
1299 self.0.is::<T>()
1300 }
1301}
1302impl fmt::Debug for CommandParam {
1303 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1304 f.debug_tuple("CommandParam").field(&self.0.type_id()).finish()
1305 }
1306}
1307zng_var::impl_from_and_into_var! {
1308 fn from(param: CommandParam) -> Option<CommandParam>;
1309}
1310
1311#[rustfmt::skip] unique_id_64! {
1313 pub struct CommandMetaVarId<T: (StateValue + VarValue)>: StateId;
1319}
1320zng_unique_id::impl_unique_id_bytemuck!(CommandMetaVarId<T: (StateValue + VarValue)>);
1321impl<T: StateValue + VarValue> CommandMetaVarId<T> {
1322 fn app(self) -> StateId<Var<T>> {
1323 let id = self.get();
1324 StateId::from_raw(id)
1325 }
1326
1327 fn scope(self) -> StateId<Var<T>> {
1328 let id = self.get();
1329 StateId::from_raw(id)
1330 }
1331}
1332
1333impl<T: StateValue + VarValue> fmt::Debug for CommandMetaVarId<T> {
1334 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1335 #[cfg(debug_assertions)]
1336 let t = pretty_type_name::pretty_type_name::<T>();
1337 #[cfg(not(debug_assertions))]
1338 let t = "$T";
1339
1340 if f.alternate() {
1341 writeln!(f, "CommandMetaVarId<{t} {{")?;
1342 writeln!(f, " id: {},", self.get())?;
1343 writeln!(f, " sequential: {}", self.sequential())?;
1344 writeln!(f, "}}")
1345 } else {
1346 write!(f, "CommandMetaVarId<{t}>({})", self.sequential())
1347 }
1348 }
1349}
1350
1351pub struct CommandMeta<'a> {
1416 meta: StateMapMut<'a, CommandMetaState>,
1417 scope: Option<StateMapMut<'a, CommandMetaState>>,
1418}
1419impl CommandMeta<'_> {
1420 pub fn get_or_insert<T, F>(&mut self, id: impl Into<StateId<T>>, init: F) -> T
1426 where
1427 T: StateValue + Clone,
1428 F: FnOnce() -> T,
1429 {
1430 let id = id.into();
1431 if let Some(scope) = &mut self.scope {
1432 if let Some(value) = scope.get(id) {
1433 value.clone()
1434 } else if let Some(value) = self.meta.get(id) {
1435 value.clone()
1436 } else {
1437 let value = init();
1438 let r = value.clone();
1439 scope.set(id, value);
1440 r
1441 }
1442 } else {
1443 self.meta.entry(id).or_insert_with(init).clone()
1444 }
1445 }
1446
1447 pub fn get_or_default<T>(&mut self, id: impl Into<StateId<T>>) -> T
1453 where
1454 T: StateValue + Clone + Default,
1455 {
1456 self.get_or_insert(id, Default::default)
1457 }
1458
1459 pub fn get<T>(&self, id: impl Into<StateId<T>>) -> Option<T>
1463 where
1464 T: StateValue + Clone,
1465 {
1466 let id = id.into();
1467 if let Some(scope) = &self.scope {
1468 scope.get(id).or_else(|| self.meta.get(id))
1469 } else {
1470 self.meta.get(id)
1471 }
1472 .cloned()
1473 }
1474
1475 pub fn set<T>(&mut self, id: impl Into<StateId<T>>, value: impl Into<T>)
1479 where
1480 T: StateValue + Clone,
1481 {
1482 if let Some(scope) = &mut self.scope {
1483 scope.set(id, value);
1484 } else {
1485 self.meta.set(id, value);
1486 }
1487 }
1488
1489 pub fn init<T>(&mut self, id: impl Into<StateId<T>>, value: impl Into<T>)
1493 where
1494 T: StateValue + Clone,
1495 {
1496 self.meta.entry(id).or_insert(value);
1497 }
1498
1499 pub fn get_var_or_insert<T, F>(&mut self, id: impl Into<CommandMetaVarId<T>>, init: F) -> CommandMetaVar<T>
1505 where
1506 T: StateValue + VarValue,
1507 F: FnOnce() -> T,
1508 {
1509 let id = id.into();
1510 if let Some(scope) = &mut self.scope {
1511 let meta = &mut self.meta;
1512 scope
1513 .entry(id.scope())
1514 .or_insert_with(|| {
1515 let var = meta.entry(id.app()).or_insert_with(|| var(init())).clone();
1516 var.cow()
1517 })
1518 .clone()
1519 } else {
1520 self.meta.entry(id.app()).or_insert_with(|| var(init())).clone()
1521 }
1522 }
1523
1524 pub fn get_var<T>(&self, id: impl Into<CommandMetaVarId<T>>) -> Option<CommandMetaVar<T>>
1526 where
1527 T: StateValue + VarValue,
1528 {
1529 let id = id.into();
1530 if let Some(scope) = &self.scope {
1531 let meta = &self.meta;
1532 scope.get(id.scope()).cloned().or_else(|| meta.get(id.app()).cloned())
1533 } else {
1534 self.meta.get(id.app()).cloned()
1535 }
1536 }
1537
1538 pub fn get_var_or_default<T>(&mut self, id: impl Into<CommandMetaVarId<T>>) -> CommandMetaVar<T>
1542 where
1543 T: StateValue + VarValue + Default,
1544 {
1545 self.get_var_or_insert(id, Default::default)
1546 }
1547
1548 pub fn init_var<T>(&mut self, id: impl Into<CommandMetaVarId<T>>, value: impl Into<T>)
1552 where
1553 T: StateValue + VarValue,
1554 {
1555 self.meta.entry(id.into().app()).or_insert_with(|| var(value.into()));
1556 }
1557}
1558
1559pub type CommandMetaVar<T> = Var<T>;
1566
1567pub type ReadOnlyCommandMetaVar<T> = Var<T>;
1573
1574pub trait CommandNameExt {
1576 fn name(self) -> CommandMetaVar<Txt>;
1578
1579 fn init_name(self, name: impl Into<Txt>) -> Self;
1581
1582 fn name_with_shortcut(self) -> Var<Txt>
1588 where
1589 Self: crate::shortcut::CommandShortcutExt;
1590}
1591static_id! {
1592 static ref COMMAND_NAME_ID: CommandMetaVarId<Txt>;
1593}
1594impl CommandNameExt for Command {
1595 fn name(self) -> CommandMetaVar<Txt> {
1596 self.with_meta(|m| {
1597 m.get_var_or_insert(*COMMAND_NAME_ID, || {
1598 let name = self.static_name();
1599 let name = name.strip_suffix("_CMD").unwrap_or(name);
1600 let mut title = String::with_capacity(name.len());
1601 let mut lower = false;
1602 for c in name.chars() {
1603 if c == '_' {
1604 if !title.ends_with(' ') {
1605 title.push(' ');
1606 }
1607 lower = false;
1608 } else if lower {
1609 for l in c.to_lowercase() {
1610 title.push(l);
1611 }
1612 } else {
1613 title.push(c);
1614 lower = true;
1615 }
1616 }
1617 Txt::from(title)
1618 })
1619 })
1620 }
1621
1622 fn init_name(self, name: impl Into<Txt>) -> Self {
1623 self.with_meta(|m| m.init_var(*COMMAND_NAME_ID, name.into()));
1624 self
1625 }
1626
1627 fn name_with_shortcut(self) -> Var<Txt>
1628 where
1629 Self: crate::shortcut::CommandShortcutExt,
1630 {
1631 crate::var::merge_var!(self.name(), self.shortcut(), |name, shortcut| {
1632 if shortcut.is_empty() {
1633 name.clone()
1634 } else {
1635 zng_txt::formatx!("{name} ({})", shortcut[0])
1636 }
1637 })
1638 }
1639}
1640
1641impl Command {
1643 #[doc(hidden)]
1644 pub fn init_init(self, init: impl FnOnce(Self)) {
1645 init(self)
1646 }
1647 #[doc(hidden)]
1648 pub fn init(self) {}
1649}
1650
1651pub trait CommandInfoExt {
1653 fn info(self) -> CommandMetaVar<Txt>;
1655
1656 fn init_info(self, info: impl Into<Txt>) -> Self;
1658}
1659static_id! {
1660 static ref COMMAND_INFO_ID: CommandMetaVarId<Txt>;
1661}
1662impl CommandInfoExt for Command {
1663 fn info(self) -> CommandMetaVar<Txt> {
1664 self.with_meta(|m| m.get_var_or_insert(*COMMAND_INFO_ID, Txt::default))
1665 }
1666
1667 fn init_info(self, info: impl Into<Txt>) -> Self {
1668 self.with_meta(|m| m.init_var(*COMMAND_INFO_ID, info.into()));
1669 self
1670 }
1671}
1672
1673enum CommandMetaState {}
1674
1675#[derive(Clone)]
1676enum MetaInit {
1677 Init(fn(Command)),
1678 Initing(Arc<(ThreadId, Mutex<()>)>),
1680 Inited,
1681}
1682
1683#[doc(hidden)]
1684pub struct CommandData {
1685 static_name: &'static str,
1686
1687 meta_init: MetaInit,
1688 meta: Mutex<OwnedStateMap<CommandMetaState>>,
1689
1690 handle_count: usize,
1691 enabled_count: usize,
1692 registered: bool,
1693
1694 has_handlers: Var<bool>,
1696 is_enabled: Var<bool>,
1698
1699 scopes: HashMap<CommandScope, ScopedValue>,
1700}
1701impl CommandData {
1702 pub fn new(meta_init: fn(Command), static_name: &'static str) -> Self {
1703 CommandData {
1704 static_name,
1705 meta_init: MetaInit::Init(meta_init),
1706 meta: Mutex::new(OwnedStateMap::new()),
1707
1708 handle_count: 0,
1709 enabled_count: 0,
1710 registered: false,
1711
1712 has_handlers: var(false),
1713 is_enabled: var(false),
1714
1715 scopes: HashMap::default(),
1716 }
1717 }
1718
1719 fn subscribe(&mut self, command: Command, enabled: bool, mut target: Option<WidgetId>) -> CommandHandle {
1720 match command.scope {
1721 CommandScope::App => {
1722 if !mem::replace(&mut self.registered, true) {
1723 EVENTS.register_command(command, self.static_name);
1724 }
1725
1726 self.handle_count += 1;
1727 if enabled {
1728 self.enabled_count += 1;
1729 }
1730
1731 if self.handle_count == 1 {
1732 modify_has_handlers(&self.has_handlers, command);
1733 }
1734 if self.enabled_count == 1 {
1735 modify_is_enabled(&self.is_enabled, command);
1736 }
1737
1738 tracing::trace!(
1739 "subscribe to {:?}, handle_count: {:?}, enabled_count: {:?}",
1740 CommandDbg::new(self.static_name, command.scope),
1741 self.handle_count,
1742 self.enabled_count
1743 );
1744 }
1745 scope => {
1746 let data = self.scopes.entry(scope).or_default();
1747
1748 if !mem::replace(&mut data.registered, true) {
1749 EVENTS.register_command(command, self.static_name);
1750 }
1751
1752 data.handle_count += 1;
1753 if enabled {
1754 data.enabled_count += 1;
1755 }
1756
1757 if data.handle_count == 1 {
1758 modify_has_handlers(&data.has_handlers, command);
1759 }
1760 if data.enabled_count == 1 {
1761 modify_is_enabled(&data.is_enabled, command);
1762 }
1763
1764 tracing::trace!(
1765 "subscribe to {:?}, handle_count: {:?}, enabled_count: {:?}",
1766 CommandDbg::new(self.static_name, command.scope),
1767 data.handle_count,
1768 data.enabled_count
1769 );
1770
1771 if let CommandScope::Widget(id) = scope {
1772 target = Some(id);
1773 }
1774 }
1775 };
1776
1777 CommandHandle::new(
1778 command,
1779 target
1780 .map(|t| command.event.subscribe(UpdateOp::Update, t))
1781 .unwrap_or_else(VarHandle::dummy),
1782 enabled,
1783 )
1784 }
1785}
1786
1787fn modify_is_enabled(is_enabled: &Var<bool>, command: Command) {
1795 is_enabled.modify(move |a| {
1796 let cmd = command.local.read();
1797 let enabled = match command.scope {
1798 CommandScope::App => cmd.enabled_count > 0,
1799 scope => match cmd.scopes.get(&scope) {
1800 Some(s) => s.enabled_count > 0,
1801 None => false,
1802 },
1803 };
1804 a.set(enabled);
1805 });
1806}
1807fn modify_has_handlers(has_handlers: &Var<bool>, command: Command) {
1809 has_handlers.modify(move |a| {
1810 let cmd = command.local.read();
1811 let has = match command.scope {
1812 CommandScope::App => cmd.handle_count > 0,
1813 scope => match cmd.scopes.get(&scope) {
1814 Some(s) => s.handle_count > 0,
1815 None => false,
1816 },
1817 };
1818 a.set(has);
1819 });
1820}
1821
1822struct ScopedValue {
1823 observer_count: usize,
1824 handle_count: usize,
1825 enabled_count: usize,
1826 is_enabled: Var<bool>,
1827 has_handlers: Var<bool>,
1828 meta: Mutex<OwnedStateMap<CommandMetaState>>,
1829 registered: bool,
1830}
1831impl Default for ScopedValue {
1832 fn default() -> Self {
1833 ScopedValue {
1834 observer_count: 0,
1835 is_enabled: var(false),
1836 has_handlers: var(false),
1837 handle_count: 0,
1838 enabled_count: 0,
1839 meta: Mutex::new(OwnedStateMap::default()),
1840 registered: false,
1841 }
1842 }
1843}
1844
1845#[cfg(test)]
1846mod tests {
1847 use crate::APP;
1848
1849 use super::*;
1850
1851 command! {
1852 static FOO_CMD;
1853 }
1854
1855 #[test]
1856 fn parameter_none() {
1857 let _ = CommandArgs::now(None, CommandScope::App, None, true);
1858 }
1859
1860 #[test]
1861 fn enabled_not_scoped() {
1862 let mut app = APP.minimal().run_headless(false);
1863
1864 assert!(!FOO_CMD.has_handlers().get());
1865
1866 let handle = FOO_CMD.subscribe(true);
1867 app.update(false).assert_wait();
1868 assert!(FOO_CMD.is_enabled().get());
1869
1870 handle.enabled().set(false);
1871 app.update(false).assert_wait();
1872
1873 assert!(FOO_CMD.has_handlers().get());
1874 assert!(!FOO_CMD.is_enabled().get());
1875
1876 handle.enabled().set(true);
1877 app.update(false).assert_wait();
1878
1879 assert!(FOO_CMD.is_enabled().get());
1880
1881 drop(handle);
1882 app.update(false).assert_wait();
1883
1884 assert!(!FOO_CMD.has_handlers().get());
1885 assert!(!FOO_CMD.is_enabled().get());
1886 }
1887
1888 #[test]
1889 fn enabled_scoped() {
1890 let mut app = APP.minimal().run_headless(false);
1891
1892 let cmd = FOO_CMD;
1893 let cmd_scoped = FOO_CMD.scoped(WindowId::named("enabled_scoped"));
1894 app.update(false).assert_wait();
1895 assert!(!cmd.has_handlers().get());
1896 assert!(!cmd_scoped.has_handlers().get());
1897
1898 let handle_scoped = cmd_scoped.subscribe(true);
1899 app.update(false).assert_wait();
1900
1901 assert!(!cmd.has_handlers().get());
1902 assert!(cmd_scoped.is_enabled().get());
1903
1904 handle_scoped.enabled().set(false);
1905 app.update(false).assert_wait();
1906
1907 assert!(!cmd.has_handlers().get());
1908 assert!(!cmd_scoped.is_enabled().get());
1909 assert!(cmd_scoped.has_handlers().get());
1910
1911 handle_scoped.enabled().set(true);
1912 app.update(false).assert_wait();
1913
1914 assert!(!cmd.has_handlers().get());
1915 assert!(cmd_scoped.is_enabled().get());
1916
1917 drop(handle_scoped);
1918 app.update(false).assert_wait();
1919
1920 assert!(!cmd.has_handlers().get());
1921 assert!(!cmd_scoped.has_handlers().get());
1922 }
1923
1924 #[test]
1925 fn has_handlers_not_scoped() {
1926 let mut app = APP.minimal().run_headless(false);
1927
1928 assert!(!FOO_CMD.has_handlers().get());
1929
1930 let handle = FOO_CMD.subscribe(false);
1931 app.update(false).assert_wait();
1932
1933 assert!(FOO_CMD.has_handlers().get());
1934
1935 drop(handle);
1936 app.update(false).assert_wait();
1937
1938 assert!(!FOO_CMD.has_handlers().get());
1939 }
1940
1941 #[test]
1942 fn has_handlers_scoped() {
1943 let mut app = APP.minimal().run_headless(false);
1944
1945 let cmd = FOO_CMD;
1946 let cmd_scoped = FOO_CMD.scoped(WindowId::named("has_handlers_scoped"));
1947 app.update(false).assert_wait();
1948
1949 assert!(!cmd.has_handlers().get());
1950 assert!(!cmd_scoped.has_handlers().get());
1951
1952 let handle = cmd_scoped.subscribe(false);
1953 app.update(false).assert_wait();
1954
1955 assert!(!cmd.has_handlers().get());
1956 assert!(cmd_scoped.has_handlers().get());
1957
1958 drop(handle);
1959 app.update(false).assert_wait();
1960
1961 assert!(!cmd.has_handlers().get());
1962 assert!(!cmd_scoped.has_handlers().get());
1963 }
1964
1965 }