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 parking_lot::Mutex;
171use zng_state_map::{OwnedStateMap, StateId, StateMapMut, StateValue};
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);
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);
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 todo!()
978 }
979 }
980 _ => false,
981 }
982 }
983 }
984}
985
986pub struct CommandHandle {
993 command: Option<Command>,
994 local_enabled: Var<bool>,
995 _handles: VarHandles,
997}
998impl Clone for CommandHandle {
1002 fn clone(&self) -> Self {
1003 match self.command {
1004 Some(c) => c.subscribe(self.local_enabled.get()),
1005 None => Self::dummy(),
1006 }
1007 }
1008}
1009impl CommandHandle {
1010 pub fn command(&self) -> Option<Command> {
1012 self.command
1013 }
1014
1015 pub fn enabled(&self) -> &Var<bool> {
1019 &self.local_enabled
1020 }
1021
1022 pub fn dummy() -> Self {
1024 CommandHandle {
1025 command: None,
1026 local_enabled: const_var(false),
1027 _handles: VarHandles::dummy(),
1028 }
1029 }
1030
1031 fn new(cmd: Command, event_handle: VarHandle, enabled: bool) -> Self {
1032 let mut r = Self {
1033 command: Some(cmd),
1034 local_enabled: var(enabled),
1035 _handles: VarHandles::dummy(),
1036 };
1037
1038 let mut last_applied = enabled;
1040 r._handles.push(r.local_enabled.hook(move |args| {
1041 let _hold = &event_handle;
1042 let enabled = *args.value();
1043 if last_applied != enabled {
1044 Self::update_enabled(cmd, enabled);
1045 last_applied = enabled;
1046 }
1047 true
1048 }));
1049
1050 r
1051 }
1052
1053 fn update_enabled(command: Command, enabled: bool) {
1054 let mut write = command.local.write();
1055 match command.scope {
1056 CommandScope::App => {
1057 if enabled {
1058 write.enabled_count += 1;
1059 if write.enabled_count == 1 {
1060 write.is_enabled.set(true);
1061 }
1062 tracing::trace!(
1063 "command handle {:?} enabled, count: {:?}",
1064 CommandDbg::new(write.static_name, command.scope),
1065 write.enabled_count
1066 );
1067 } else {
1068 write.enabled_count = match write.enabled_count.checked_sub(1) {
1069 Some(c) => c,
1070 None => {
1071 #[cfg(debug_assertions)]
1072 panic!("handle for {} was disabled when enabled_count was already zero", write.static_name);
1073 #[cfg(not(debug_assertions))]
1074 0
1075 }
1076 };
1077 if write.enabled_count == 0 {
1078 write.is_enabled.set(false);
1079 }
1080 tracing::trace!(
1081 "command handle {:?} disabled, count: {:?}",
1082 CommandDbg::new(write.static_name, command.scope),
1083 write.enabled_count
1084 );
1085 }
1086 }
1087 scope => {
1088 let write = &mut *write;
1089 if let Some(data) = write.scopes.get_mut(&scope) {
1090 if enabled {
1091 data.enabled_count += 1;
1092 if data.enabled_count == 1 {
1093 data.is_enabled.set(true);
1094 }
1095 tracing::trace!(
1096 "command handle {:?} enabled, count: {:?}",
1097 CommandDbg::new(write.static_name, command.scope),
1098 data.enabled_count
1099 );
1100 } else {
1101 data.enabled_count = match data.enabled_count.checked_sub(1) {
1102 Some(c) => c,
1103 None => {
1104 #[cfg(debug_assertions)]
1105 panic!(
1106 "handle for {:?} was disabled when enabled_count was already zero",
1107 CommandDbg::new(write.static_name, scope)
1108 );
1109 #[cfg(not(debug_assertions))]
1110 0
1111 }
1112 };
1113 if data.enabled_count == 0 {
1114 data.is_enabled.set(false);
1115 }
1116 tracing::trace!(
1117 "command handle {:?} enabled, count: {:?}",
1118 CommandDbg::new(write.static_name, command.scope),
1119 data.enabled_count
1120 );
1121 }
1122 }
1123 }
1124 }
1125 }
1126
1127 pub fn is_dummy(&self) -> bool {
1129 self.command.is_none()
1130 }
1131
1132 pub fn perm(mut self) {
1137 mem::replace(&mut self._handles, VarHandles::dummy()).perm();
1139 self.command = None;
1141 }
1142}
1143impl fmt::Debug for CommandHandle {
1144 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1145 f.debug_struct("CommandHandle")
1146 .field("command", &self.command)
1147 .field("enabled", &self.local_enabled.get())
1148 .finish_non_exhaustive()
1149 }
1150}
1151impl Drop for CommandHandle {
1152 fn drop(&mut self) {
1153 if let Some(command) = self.command.take()
1154 && APP.is_started()
1155 {
1156 let mut write = command.local.write();
1157 match command.scope {
1158 CommandScope::App => {
1159 write.handle_count = match write.handle_count.checked_sub(1) {
1160 Some(c) => c,
1161 None => {
1162 #[cfg(debug_assertions)]
1163 panic!("handle for {} was dropped when handle_count was already zero", write.static_name);
1164 #[cfg(not(debug_assertions))]
1165 0
1166 }
1167 };
1168 if write.handle_count == 0 {
1169 write.has_handlers.set(false);
1170 }
1171
1172 if self.local_enabled.get() {
1173 write.enabled_count = match write.enabled_count.checked_sub(1) {
1174 Some(c) => c,
1175 None => {
1176 #[cfg(debug_assertions)]
1177 panic!(
1178 "handle for enabled {} was dropped when enabled_count was already zero",
1179 write.static_name
1180 );
1181 #[cfg(not(debug_assertions))]
1182 0
1183 }
1184 };
1185
1186 if write.enabled_count == 0 {
1187 write.is_enabled.set(false);
1188 }
1189 }
1190
1191 tracing::trace!(
1192 "unsubscribe to {:?}, handle_count: {:?}, enabled_count: {:?}",
1193 CommandDbg::new(write.static_name, command.scope),
1194 write.handle_count,
1195 write.enabled_count
1196 );
1197 }
1198 scope => {
1199 let write = &mut *write;
1200 if let hash_map::Entry::Occupied(mut entry) = write.scopes.entry(scope) {
1201 let data = entry.get_mut();
1202
1203 data.handle_count = match data.handle_count.checked_sub(1) {
1204 Some(c) => c,
1205 None => {
1206 #[cfg(debug_assertions)]
1207 panic!(
1208 "handle for {:?} was dropped when handle_count was already zero",
1209 CommandDbg::new(write.static_name, scope)
1210 );
1211 #[cfg(not(debug_assertions))]
1212 0
1213 }
1214 };
1215
1216 if self.local_enabled.get() {
1217 data.enabled_count = match data.enabled_count.checked_sub(1) {
1218 Some(c) => c,
1219 None => {
1220 #[cfg(debug_assertions)]
1221 panic!(
1222 "handle for enabled {:?} was dropped when enabled_count was already zero",
1223 CommandDbg::new(write.static_name, scope)
1224 );
1225 #[cfg(not(debug_assertions))]
1226 0
1227 }
1228 };
1229
1230 if data.enabled_count == 0 {
1231 data.is_enabled.set(false);
1232 }
1233 }
1234
1235 tracing::trace!(
1236 "unsubscribe to {:?}, handle_count: {:?}, enabled_count: {:?}",
1237 CommandDbg::new(write.static_name, command.scope),
1238 data.handle_count,
1239 data.enabled_count
1240 );
1241
1242 if data.handle_count == 0 {
1243 data.has_handlers.set(false);
1244 if data.observer_count == 0 {
1245 entry.remove();
1246 EVENTS.unregister_command(command);
1247 }
1248 }
1249 }
1250 }
1251 }
1252 }
1253 }
1254}
1255impl Default for CommandHandle {
1256 fn default() -> Self {
1257 Self::dummy()
1258 }
1259}
1260
1261#[derive(Clone)]
1263#[non_exhaustive]
1264pub struct CommandParam(pub Arc<dyn Any + Send + Sync>);
1265impl PartialEq for CommandParam {
1266 fn eq(&self, other: &Self) -> bool {
1267 Arc::ptr_eq(&self.0, &other.0)
1268 }
1269}
1270impl Eq for CommandParam {}
1271impl CommandParam {
1272 pub fn new(param: impl Any + Send + Sync + 'static) -> Self {
1276 let p: &dyn Any = ¶m;
1277 if let Some(p) = p.downcast_ref::<Self>() {
1278 p.clone()
1279 } else if let Some(p) = p.downcast_ref::<Arc<dyn Any + Send + Sync>>() {
1280 CommandParam(p.clone())
1281 } else {
1282 CommandParam(Arc::new(param))
1283 }
1284 }
1285
1286 pub fn type_id(&self) -> TypeId {
1288 self.0.type_id()
1289 }
1290
1291 pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
1293 self.0.downcast_ref()
1294 }
1295
1296 pub fn is<T: Any>(&self) -> bool {
1298 self.0.is::<T>()
1299 }
1300}
1301impl fmt::Debug for CommandParam {
1302 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1303 f.debug_tuple("CommandParam").field(&self.0.type_id()).finish()
1304 }
1305}
1306zng_var::impl_from_and_into_var! {
1307 fn from(param: CommandParam) -> Option<CommandParam>;
1308}
1309
1310#[rustfmt::skip] unique_id_64! {
1312 pub struct CommandMetaVarId<T: (StateValue + VarValue)>: StateId;
1318}
1319zng_unique_id::impl_unique_id_bytemuck!(CommandMetaVarId<T: (StateValue + VarValue)>);
1320impl<T: StateValue + VarValue> CommandMetaVarId<T> {
1321 fn app(self) -> StateId<Var<T>> {
1322 let id = self.get();
1323 StateId::from_raw(id)
1324 }
1325
1326 fn scope(self) -> StateId<Var<T>> {
1327 let id = self.get();
1328 StateId::from_raw(id)
1329 }
1330}
1331
1332impl<T: StateValue + VarValue> fmt::Debug for CommandMetaVarId<T> {
1333 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1334 #[cfg(debug_assertions)]
1335 let t = pretty_type_name::pretty_type_name::<T>();
1336 #[cfg(not(debug_assertions))]
1337 let t = "$T";
1338
1339 if f.alternate() {
1340 writeln!(f, "CommandMetaVarId<{t} {{")?;
1341 writeln!(f, " id: {},", self.get())?;
1342 writeln!(f, " sequential: {}", self.sequential())?;
1343 writeln!(f, "}}")
1344 } else {
1345 write!(f, "CommandMetaVarId<{t}>({})", self.sequential())
1346 }
1347 }
1348}
1349
1350pub struct CommandMeta<'a> {
1415 meta: StateMapMut<'a, CommandMetaState>,
1416 scope: Option<StateMapMut<'a, CommandMetaState>>,
1417}
1418impl CommandMeta<'_> {
1419 pub fn get_or_insert<T, F>(&mut self, id: impl Into<StateId<T>>, init: F) -> T
1425 where
1426 T: StateValue + Clone,
1427 F: FnOnce() -> T,
1428 {
1429 let id = id.into();
1430 if let Some(scope) = &mut self.scope {
1431 if let Some(value) = scope.get(id) {
1432 value.clone()
1433 } else if let Some(value) = self.meta.get(id) {
1434 value.clone()
1435 } else {
1436 let value = init();
1437 let r = value.clone();
1438 scope.set(id, value);
1439 r
1440 }
1441 } else {
1442 self.meta.entry(id).or_insert_with(init).clone()
1443 }
1444 }
1445
1446 pub fn get_or_default<T>(&mut self, id: impl Into<StateId<T>>) -> T
1452 where
1453 T: StateValue + Clone + Default,
1454 {
1455 self.get_or_insert(id, Default::default)
1456 }
1457
1458 pub fn get<T>(&self, id: impl Into<StateId<T>>) -> Option<T>
1462 where
1463 T: StateValue + Clone,
1464 {
1465 let id = id.into();
1466 if let Some(scope) = &self.scope {
1467 scope.get(id).or_else(|| self.meta.get(id))
1468 } else {
1469 self.meta.get(id)
1470 }
1471 .cloned()
1472 }
1473
1474 pub fn set<T>(&mut self, id: impl Into<StateId<T>>, value: impl Into<T>)
1478 where
1479 T: StateValue + Clone,
1480 {
1481 if let Some(scope) = &mut self.scope {
1482 scope.set(id, value);
1483 } else {
1484 self.meta.set(id, value);
1485 }
1486 }
1487
1488 pub fn init<T>(&mut self, id: impl Into<StateId<T>>, value: impl Into<T>)
1492 where
1493 T: StateValue + Clone,
1494 {
1495 self.meta.entry(id).or_insert(value);
1496 }
1497
1498 pub fn get_var_or_insert<T, F>(&mut self, id: impl Into<CommandMetaVarId<T>>, init: F) -> CommandMetaVar<T>
1504 where
1505 T: StateValue + VarValue,
1506 F: FnOnce() -> T,
1507 {
1508 let id = id.into();
1509 if let Some(scope) = &mut self.scope {
1510 let meta = &mut self.meta;
1511 scope
1512 .entry(id.scope())
1513 .or_insert_with(|| {
1514 let var = meta.entry(id.app()).or_insert_with(|| var(init())).clone();
1515 var.cow()
1516 })
1517 .clone()
1518 } else {
1519 self.meta.entry(id.app()).or_insert_with(|| var(init())).clone()
1520 }
1521 }
1522
1523 pub fn get_var<T>(&self, id: impl Into<CommandMetaVarId<T>>) -> Option<CommandMetaVar<T>>
1525 where
1526 T: StateValue + VarValue,
1527 {
1528 let id = id.into();
1529 if let Some(scope) = &self.scope {
1530 let meta = &self.meta;
1531 scope.get(id.scope()).cloned().or_else(|| meta.get(id.app()).cloned())
1532 } else {
1533 self.meta.get(id.app()).cloned()
1534 }
1535 }
1536
1537 pub fn get_var_or_default<T>(&mut self, id: impl Into<CommandMetaVarId<T>>) -> CommandMetaVar<T>
1541 where
1542 T: StateValue + VarValue + Default,
1543 {
1544 self.get_var_or_insert(id, Default::default)
1545 }
1546
1547 pub fn init_var<T>(&mut self, id: impl Into<CommandMetaVarId<T>>, value: impl Into<T>)
1551 where
1552 T: StateValue + VarValue,
1553 {
1554 self.meta.entry(id.into().app()).or_insert_with(|| var(value.into()));
1555 }
1556}
1557
1558pub type CommandMetaVar<T> = Var<T>;
1565
1566pub type ReadOnlyCommandMetaVar<T> = Var<T>;
1572
1573pub trait CommandNameExt {
1575 fn name(self) -> CommandMetaVar<Txt>;
1577
1578 fn init_name(self, name: impl Into<Txt>) -> Self;
1580
1581 fn name_with_shortcut(self) -> Var<Txt>
1587 where
1588 Self: crate::shortcut::CommandShortcutExt;
1589}
1590static_id! {
1591 static ref COMMAND_NAME_ID: CommandMetaVarId<Txt>;
1592}
1593impl CommandNameExt for Command {
1594 fn name(self) -> CommandMetaVar<Txt> {
1595 self.with_meta(|m| {
1596 m.get_var_or_insert(*COMMAND_NAME_ID, || {
1597 let name = self.static_name();
1598 let name = name.strip_suffix("_CMD").unwrap_or(name);
1599 let mut title = String::with_capacity(name.len());
1600 let mut lower = false;
1601 for c in name.chars() {
1602 if c == '_' {
1603 if !title.ends_with(' ') {
1604 title.push(' ');
1605 }
1606 lower = false;
1607 } else if lower {
1608 for l in c.to_lowercase() {
1609 title.push(l);
1610 }
1611 } else {
1612 title.push(c);
1613 lower = true;
1614 }
1615 }
1616 Txt::from(title)
1617 })
1618 })
1619 }
1620
1621 fn init_name(self, name: impl Into<Txt>) -> Self {
1622 self.with_meta(|m| m.init_var(*COMMAND_NAME_ID, name.into()));
1623 self
1624 }
1625
1626 fn name_with_shortcut(self) -> Var<Txt>
1627 where
1628 Self: crate::shortcut::CommandShortcutExt,
1629 {
1630 crate::var::merge_var!(self.name(), self.shortcut(), |name, shortcut| {
1631 if shortcut.is_empty() {
1632 name.clone()
1633 } else {
1634 zng_txt::formatx!("{name} ({})", shortcut[0])
1635 }
1636 })
1637 }
1638}
1639
1640impl Command {
1642 #[doc(hidden)]
1643 pub fn init_init(self, init: impl FnOnce(Self)) {
1644 init(self)
1645 }
1646 #[doc(hidden)]
1647 pub fn init(self) {}
1648}
1649
1650pub trait CommandInfoExt {
1652 fn info(self) -> CommandMetaVar<Txt>;
1654
1655 fn init_info(self, info: impl Into<Txt>) -> Self;
1657}
1658static_id! {
1659 static ref COMMAND_INFO_ID: CommandMetaVarId<Txt>;
1660}
1661impl CommandInfoExt for Command {
1662 fn info(self) -> CommandMetaVar<Txt> {
1663 self.with_meta(|m| m.get_var_or_insert(*COMMAND_INFO_ID, Txt::default))
1664 }
1665
1666 fn init_info(self, info: impl Into<Txt>) -> Self {
1667 self.with_meta(|m| m.init_var(*COMMAND_INFO_ID, info.into()));
1668 self
1669 }
1670}
1671
1672enum CommandMetaState {}
1673
1674#[derive(Clone)]
1675enum MetaInit {
1676 Init(fn(Command)),
1677 Initing(Arc<(ThreadId, Mutex<()>)>),
1679 Inited,
1680}
1681
1682#[doc(hidden)]
1683pub struct CommandData {
1684 static_name: &'static str,
1685
1686 meta_init: MetaInit,
1687 meta: Mutex<OwnedStateMap<CommandMetaState>>,
1688
1689 handle_count: usize,
1690 enabled_count: usize,
1691 registered: bool,
1692
1693 has_handlers: Var<bool>,
1694 is_enabled: Var<bool>,
1695
1696 scopes: HashMap<CommandScope, ScopedValue>,
1697}
1698impl CommandData {
1699 pub fn new(meta_init: fn(Command), static_name: &'static str) -> Self {
1700 CommandData {
1701 static_name,
1702 meta_init: MetaInit::Init(meta_init),
1703 meta: Mutex::new(OwnedStateMap::new()),
1704
1705 handle_count: 0,
1706 enabled_count: 0,
1707 registered: false,
1708
1709 has_handlers: var(false),
1710 is_enabled: var(false),
1711
1712 scopes: HashMap::default(),
1713 }
1714 }
1715
1716 fn subscribe(&mut self, command: Command, enabled: bool, mut target: Option<WidgetId>) -> CommandHandle {
1717 match command.scope {
1718 CommandScope::App => {
1719 if !mem::replace(&mut self.registered, true) {
1720 EVENTS.register_command(command);
1721 }
1722
1723 self.handle_count += 1;
1724 if enabled {
1725 self.enabled_count += 1;
1726 }
1727
1728 if self.handle_count == 1 {
1729 self.has_handlers.set(true);
1730 }
1731 if self.enabled_count == 1 {
1732 self.is_enabled.set(true);
1733 }
1734
1735 tracing::trace!(
1736 "subscribe to {:?}, handle_count: {:?}, enabled_count: {:?}",
1737 CommandDbg::new(self.static_name, command.scope),
1738 self.handle_count,
1739 self.enabled_count
1740 );
1741 }
1742 scope => {
1743 let data = self.scopes.entry(scope).or_default();
1744
1745 if !mem::replace(&mut data.registered, true) {
1746 EVENTS.register_command(command);
1747 }
1748
1749 data.handle_count += 1;
1750 if enabled {
1751 data.enabled_count += 1;
1752 }
1753
1754 if data.handle_count == 1 {
1755 data.has_handlers.set(true);
1756 }
1757 if data.enabled_count == 1 {
1758 data.is_enabled.set(true);
1759 }
1760
1761 tracing::trace!(
1762 "subscribe to {:?}, handle_count: {:?}, enabled_count: {:?}",
1763 CommandDbg::new(self.static_name, command.scope),
1764 data.handle_count,
1765 data.enabled_count
1766 );
1767
1768 if let CommandScope::Widget(id) = scope {
1769 target = Some(id);
1770 }
1771 }
1772 };
1773
1774 CommandHandle::new(
1775 command,
1776 target
1777 .map(|t| command.event.subscribe(UpdateOp::Update, t))
1778 .unwrap_or_else(VarHandle::dummy),
1779 enabled,
1780 )
1781 }
1782}
1783
1784struct ScopedValue {
1785 observer_count: usize,
1786 handle_count: usize,
1787 enabled_count: usize,
1788 is_enabled: Var<bool>,
1789 has_handlers: Var<bool>,
1790 meta: Mutex<OwnedStateMap<CommandMetaState>>,
1791 registered: bool,
1792}
1793impl Default for ScopedValue {
1794 fn default() -> Self {
1795 ScopedValue {
1796 observer_count: 0,
1797 is_enabled: var(false),
1798 has_handlers: var(false),
1799 handle_count: 0,
1800 enabled_count: 0,
1801 meta: Mutex::new(OwnedStateMap::default()),
1802 registered: false,
1803 }
1804 }
1805}
1806
1807#[cfg(test)]
1808mod tests {
1809 use crate::APP;
1810
1811 use super::*;
1812
1813 command! {
1814 static FOO_CMD;
1815 }
1816
1817 #[test]
1818 fn parameter_none() {
1819 let _ = CommandArgs::now(None, CommandScope::App, None, true);
1820 }
1821
1822 #[test]
1823 fn enabled_not_scoped() {
1824 let mut app = APP.minimal().run_headless(false);
1825
1826 assert!(!FOO_CMD.has_handlers().get());
1827
1828 let handle = FOO_CMD.subscribe(true);
1829 app.update(false).assert_wait();
1830 assert!(FOO_CMD.is_enabled().get());
1831
1832 handle.enabled().set(false);
1833 app.update(false).assert_wait();
1834
1835 assert!(FOO_CMD.has_handlers().get());
1836 assert!(!FOO_CMD.is_enabled().get());
1837
1838 handle.enabled().set(true);
1839 app.update(false).assert_wait();
1840
1841 assert!(FOO_CMD.is_enabled().get());
1842
1843 drop(handle);
1844 app.update(false).assert_wait();
1845
1846 assert!(!FOO_CMD.has_handlers().get());
1847 assert!(!FOO_CMD.is_enabled().get());
1848 }
1849
1850 #[test]
1851 fn enabled_scoped() {
1852 let mut app = APP.minimal().run_headless(false);
1853
1854 let cmd = FOO_CMD;
1855 let cmd_scoped = FOO_CMD.scoped(WindowId::named("enabled_scoped"));
1856 app.update(false).assert_wait();
1857 assert!(!cmd.has_handlers().get());
1858 assert!(!cmd_scoped.has_handlers().get());
1859
1860 let handle_scoped = cmd_scoped.subscribe(true);
1861 app.update(false).assert_wait();
1862
1863 assert!(!cmd.has_handlers().get());
1864 assert!(cmd_scoped.is_enabled().get());
1865
1866 handle_scoped.enabled().set(false);
1867 app.update(false).assert_wait();
1868
1869 assert!(!cmd.has_handlers().get());
1870 assert!(!cmd_scoped.is_enabled().get());
1871 assert!(cmd_scoped.has_handlers().get());
1872
1873 handle_scoped.enabled().set(true);
1874 app.update(false).assert_wait();
1875
1876 assert!(!cmd.has_handlers().get());
1877 assert!(cmd_scoped.is_enabled().get());
1878
1879 drop(handle_scoped);
1880 app.update(false).assert_wait();
1881
1882 assert!(!cmd.has_handlers().get());
1883 assert!(!cmd_scoped.has_handlers().get());
1884 }
1885
1886 #[test]
1887 fn has_handlers_not_scoped() {
1888 let mut app = APP.minimal().run_headless(false);
1889
1890 assert!(!FOO_CMD.has_handlers().get());
1891
1892 let handle = FOO_CMD.subscribe(false);
1893 app.update(false).assert_wait();
1894
1895 assert!(FOO_CMD.has_handlers().get());
1896
1897 drop(handle);
1898 app.update(false).assert_wait();
1899
1900 assert!(!FOO_CMD.has_handlers().get());
1901 }
1902
1903 #[test]
1904 fn has_handlers_scoped() {
1905 let mut app = APP.minimal().run_headless(false);
1906
1907 let cmd = FOO_CMD;
1908 let cmd_scoped = FOO_CMD.scoped(WindowId::named("has_handlers_scoped"));
1909 app.update(false).assert_wait();
1910
1911 assert!(!cmd.has_handlers().get());
1912 assert!(!cmd_scoped.has_handlers().get());
1913
1914 let handle = cmd_scoped.subscribe(false);
1915 app.update(false).assert_wait();
1916
1917 assert!(!cmd.has_handlers().get());
1918 assert!(cmd_scoped.has_handlers().get());
1919
1920 drop(handle);
1921 app.update(false).assert_wait();
1922
1923 assert!(!cmd.has_handlers().get());
1924 assert!(!cmd_scoped.has_handlers().get());
1925 }
1926
1927 }