1use std::{
2 any::TypeId,
3 collections::{HashMap, hash_map},
4 mem, ops,
5 thread::ThreadId,
6};
7
8use crate::{APP, shortcut::CommandShortcutExt, update::UpdatesTrace, widget::info::WidgetInfo, window::WindowId};
9
10use super::*;
11
12#[macro_export]
137macro_rules! command {
138 ($(
139 $(#[$attr:meta])*
140 $vis:vis static $COMMAND:ident $(=> |$cmd:ident|$custom_meta_init:expr ;)? $(= { $($meta_ident:ident $(!)? : $meta_init:expr),* $(,)? };)? $(;)?
141 )+) => {
142 $(
143 $crate::__command! {
144 $(#[$attr])*
145 $vis static $COMMAND $(=> |$cmd|$custom_meta_init)? $(= {
146 $($meta_ident: $meta_init,)+
147 })? ;
148 }
149 )+
150 }
151}
152#[doc(inline)]
153pub use command;
154
155use zng_app_context::AppId;
156use zng_state_map::{OwnedStateMap, StateId, StateMapMut, StateValue};
157use zng_txt::Txt;
158use zng_unique_id::{static_id, unique_id_64};
159use zng_var::{AnyVar, ArcVar, BoxedVar, ReadOnlyArcVar, Var, VarValue, impl_from_and_into_var, types::ArcCowVar, var};
160
161#[doc(hidden)]
162pub use zng_app_context::app_local;
163
164#[doc(hidden)]
165pub use pastey::paste;
166
167#[doc(hidden)]
168#[macro_export]
169macro_rules! __command {
170 (
171 $(#[$attr:meta])*
172 $vis:vis static $COMMAND:ident => |$cmd:ident| $meta_init:expr;
173 ) => {
174 $(#[$attr])*
175 $vis static $COMMAND: $crate::event::Command = {
176 fn __meta_init__($cmd: $crate::event::Command) {
177 $meta_init
178 }
179 $crate::event::app_local! {
180 static EVENT: $crate::event::EventData = const { $crate::event::EventData::new(std::stringify!($COMMAND)) };
181 static DATA: $crate::event::CommandData = $crate::event::CommandData::new(__meta_init__);
182 }
183 $crate::event::Command::new(&EVENT, &DATA)
184 };
185 };
186 (
187 $(#[$attr:meta])*
188 $vis:vis static $COMMAND:ident = { l10n: $l10n_arg:expr, $($meta_ident:ident : $meta_init:expr),* $(,)? };
189 ) => {
190 $crate::event::paste! {
191 $crate::__command! {
192 $(#[$attr])*
193 $(#[doc = concat!("<tr> <td>", stringify!($meta_ident), "</td> <td>", stringify!($meta_init), "</td> </tr>")])+
202 $vis static $COMMAND => |cmd| {
208 let __l10n_arg = $l10n_arg;
209 $(
210 cmd.[<init_ $meta_ident>]($meta_init);
211 $crate::event::init_meta_l10n(std::env!("CARGO_PKG_NAME"), std::env!("CARGO_PKG_VERSION"), &__l10n_arg, cmd, stringify!($meta_ident), &cmd.$meta_ident());
212 )*
213 };
214 }
215 }
216 };
217 (
218 $(#[$attr:meta])*
219 $vis:vis static $COMMAND:ident = { $($meta_ident:ident : $meta_init:expr),* $(,)? };
220 ) => {
221 $crate::event::paste! {
222 $crate::__command! {
223 $(#[$attr])*
224 $(#[doc = concat!("<tr> <td>", stringify!($meta_ident), "</td> <td>", stringify!($meta_init), "</td> </tr>")])+
233 $vis static $COMMAND => |cmd| {
237 $(
238 cmd.[<init_ $meta_ident>]($meta_init);
239 )*
240 };
241 }
242 }
243 };
244 (
245 $(#[$attr:meta])*
246 $vis:vis static $COMMAND:ident;
247 ) => {
248 $crate::__command! {
249 $(#[$attr])*
250 $vis static $COMMAND => |_cmd|{};
251 }
252 };
253}
254
255#[doc(hidden)]
256pub fn init_meta_l10n(
257 pkg_name: &'static str,
258 pkg_version: &'static str,
259 l10n_arg: &dyn Any,
260 cmd: Command,
261 meta_name: &'static str,
262 meta_value: &dyn Any,
263) {
264 if let Some(txt) = meta_value.downcast_ref::<CommandMetaVar<Txt>>() {
265 let mut l10n_file = "";
266
267 if let Some(&enabled) = l10n_arg.downcast_ref::<bool>() {
268 if !enabled {
269 return;
270 }
271 } else if let Some(&file) = l10n_arg.downcast_ref::<&'static str>() {
272 l10n_file = file;
273 } else {
274 tracing::error!("unknown l10n value in {}", cmd.event().as_any().name());
275 return;
276 }
277
278 EVENTS_L10N.init_meta_l10n([pkg_name, pkg_version, l10n_file], cmd, meta_name, txt.clone());
279 }
280}
281
282#[derive(Clone, Copy)]
324pub struct Command {
325 event: Event<CommandArgs>,
326 local: &'static AppLocal<CommandData>,
327 scope: CommandScope,
328}
329impl fmt::Debug for Command {
330 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
331 if f.alternate() {
332 f.debug_struct("Command")
333 .field("event", &self.event)
334 .field("scope", &self.scope)
335 .finish_non_exhaustive()
336 } else {
337 write!(f, "{}", self.event.name())?;
338 match self.scope {
339 CommandScope::App => Ok(()),
340 CommandScope::Window(id) => write!(f, "({id})"),
341 CommandScope::Widget(id) => write!(f, "({id})"),
342 }
343 }
344 }
345}
346impl Command {
347 #[doc(hidden)]
348 pub const fn new(event_local: &'static AppLocal<EventData>, command_local: &'static AppLocal<CommandData>) -> Self {
349 Command {
350 event: Event::new(event_local),
351 local: command_local,
352 scope: CommandScope::App,
353 }
354 }
355
356 pub fn subscribe(&self, enabled: bool) -> CommandHandle {
363 let mut evs = EVENTS_SV.write();
364 self.local.write().subscribe(&mut evs, *self, enabled, None)
365 }
366
367 pub fn subscribe_wgt(&self, enabled: bool, target: WidgetId) -> CommandHandle {
375 let mut evs = EVENTS_SV.write();
376 self.local.write().subscribe(&mut evs, *self, enabled, Some(target))
377 }
378
379 pub fn event(&self) -> Event<CommandArgs> {
381 self.event
382 }
383
384 pub fn scope(&self) -> CommandScope {
386 self.scope
387 }
388
389 pub fn scoped(mut self, scope: impl Into<CommandScope>) -> Command {
391 self.scope = scope.into();
392 self
393 }
394
395 pub fn with_meta<R>(&self, visit: impl FnOnce(&mut CommandMeta) -> R) -> R {
404 {
405 let mut write = self.local.write();
406 match write.meta_init.clone() {
407 MetaInit::Init(init) => {
408 let lock = Arc::new((std::thread::current().id(), Mutex::new(())));
409 write.meta_init = MetaInit::Initing(lock.clone());
410 let _init_guard = lock.1.lock();
411 drop(write);
412 init(*self);
413 self.local.write().meta_init = MetaInit::Inited;
414 }
415 MetaInit::Initing(l) => {
416 drop(write);
417 if l.0 != std::thread::current().id() {
418 let _wait = l.1.lock();
419 }
420 }
421 MetaInit::Inited => {}
422 }
423 }
424
425 match self.scope {
426 CommandScope::App => visit(&mut CommandMeta {
427 meta: self.local.read().meta.lock().borrow_mut(),
428 scope: None,
429 }),
430 scope => {
431 {
432 let mut write = self.local.write();
433 write.scopes.entry(scope).or_default();
434 }
435
436 let read = self.local.read();
437 let scope = read.scopes.get(&scope).unwrap();
438 visit(&mut CommandMeta {
439 meta: read.meta.lock().borrow_mut(),
440 scope: Some(scope.meta.lock().borrow_mut()),
441 })
442 }
443 }
444 }
445
446 pub fn has(&self, update: &EventUpdate) -> bool {
448 self.on(update).is_some()
449 }
450
451 pub fn on<'a>(&self, update: &'a EventUpdate) -> Option<&'a CommandArgs> {
453 self.event.on(update).filter(|a| a.scope == self.scope)
454 }
455
456 pub fn on_unhandled<'a>(&self, update: &'a EventUpdate) -> Option<&'a CommandArgs> {
458 self.event
459 .on(update)
460 .filter(|a| a.scope == self.scope && !a.propagation().is_stopped())
461 }
462
463 pub fn handle<R>(&self, update: &EventUpdate, handler: impl FnOnce(&CommandArgs) -> R) -> Option<R> {
466 if let Some(args) = self.on(update) {
467 args.handle(handler)
468 } else {
469 None
470 }
471 }
472
473 pub fn has_handlers(&self) -> ReadOnlyArcVar<bool> {
475 let mut write = self.local.write();
476 match self.scope {
477 CommandScope::App => write.has_handlers.read_only(),
478 scope => write.scopes.entry(scope).or_default().has_handlers.read_only(),
479 }
480 }
481
482 pub fn is_enabled(&self) -> ReadOnlyArcVar<bool> {
484 let mut write = self.local.write();
485 match self.scope {
486 CommandScope::App => write.is_enabled.read_only(),
487 scope => write.scopes.entry(scope).or_default().is_enabled.read_only(),
488 }
489 }
490
491 pub fn has_handlers_value(&self) -> bool {
493 let read = self.local.read();
494 match self.scope {
495 CommandScope::App => read.handle_count > 0,
496 scope => read.scopes.get(&scope).map(|l| l.handle_count > 0).unwrap_or(false),
497 }
498 }
499
500 pub fn is_enabled_value(&self) -> bool {
502 let read = self.local.read();
503 match self.scope {
504 CommandScope::App => read.enabled_count > 0,
505 scope => read.scopes.get(&scope).map(|l| l.enabled_count > 0).unwrap_or(false),
506 }
507 }
508
509 pub fn visit_scopes<T>(&self, mut visitor: impl FnMut(Command) -> ControlFlow<T>) -> Option<T> {
513 let read = self.local.read();
514 for &scope in read.scopes.keys() {
515 match visitor(self.scoped(scope)) {
516 ControlFlow::Continue(_) => continue,
517 ControlFlow::Break(r) => return Some(r),
518 }
519 }
520 None
521 }
522
523 pub fn notify(&self) {
525 self.event.notify(CommandArgs::now(None, self.scope, self.is_enabled_value()))
526 }
527
528 pub fn notify_descendants(&self, parent: &WidgetInfo) {
530 self.visit_scopes::<()>(|parse_cmd| {
531 if let CommandScope::Widget(id) = parse_cmd.scope() {
532 if let Some(scope) = parent.tree().get(id) {
533 if scope.is_descendant(parent) {
534 parse_cmd.notify();
535 }
536 }
537 }
538 ControlFlow::Continue(())
539 });
540 }
541
542 pub fn notify_param(&self, param: impl Any + Send + Sync) {
544 self.event
545 .notify(CommandArgs::now(CommandParam::new(param), self.scope, self.is_enabled_value()));
546 }
547
548 pub fn notify_linked(&self, propagation: EventPropagationHandle, param: Option<CommandParam>) {
550 self.event.notify(CommandArgs::new(
551 crate::INSTANT.now(),
552 propagation,
553 param,
554 self.scope,
555 self.is_enabled_value(),
556 ))
557 }
558
559 pub fn new_update(&self) -> EventUpdate {
561 self.event.new_update(CommandArgs::now(None, self.scope, self.is_enabled_value()))
562 }
563
564 pub fn new_update_param(&self, param: impl Any + Send + Sync) -> EventUpdate {
566 self.event
567 .new_update(CommandArgs::now(CommandParam::new(param), self.scope, self.is_enabled_value()))
568 }
569
570 pub fn on_pre_event<H>(&self, enabled: bool, handler: H) -> EventHandle
578 where
579 H: AppHandler<AppCommandArgs>,
580 {
581 self.event().on_pre_event(CmdAppHandler {
582 handler,
583 handle: Arc::new(self.subscribe(enabled)),
584 })
585 }
586
587 pub fn on_event<H>(&self, enabled: bool, handler: H) -> EventHandle
595 where
596 H: AppHandler<AppCommandArgs>,
597 {
598 self.event().on_event(CmdAppHandler {
599 handler,
600 handle: Arc::new(self.subscribe(enabled)),
601 })
602 }
603
604 #[must_use]
606 pub(crate) fn update_state(&self) -> bool {
607 let mut write = self.local.write();
608 if let CommandScope::App = self.scope {
609 let has_handlers = write.handle_count > 0;
610 if has_handlers != write.has_handlers.get() {
611 write.has_handlers.set(has_handlers);
612 }
613 let is_enabled = has_handlers && write.enabled_count > 0;
614 if is_enabled != write.is_enabled.get() {
615 write.is_enabled.set(is_enabled);
616 }
617 true
618 } else if let hash_map::Entry::Occupied(entry) = write.scopes.entry(self.scope) {
619 let scope = entry.get();
620
621 if scope.handle_count == 0 && scope.has_handlers.strong_count() == 1 && scope.is_enabled.strong_count() == 1 {
622 entry.remove();
623 return false;
624 }
625
626 let has_handlers = scope.handle_count > 0;
627 if has_handlers != scope.has_handlers.get() {
628 scope.has_handlers.set(has_handlers);
629 }
630 let is_enabled = has_handlers && scope.enabled_count > 0;
631 if is_enabled != scope.is_enabled.get() {
632 scope.is_enabled.set(is_enabled);
633 }
634 true
635 } else {
636 false
637 }
638 }
639}
640impl Deref for Command {
641 type Target = Event<CommandArgs>;
642
643 fn deref(&self) -> &Self::Target {
644 &self.event
645 }
646}
647impl PartialEq for Command {
648 fn eq(&self, other: &Self) -> bool {
649 self.event == other.event && self.scope == other.scope
650 }
651}
652impl Eq for Command {}
653impl std::hash::Hash for Command {
654 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
655 std::hash::Hash::hash(&self.event.as_any(), state);
656 std::hash::Hash::hash(&self.scope, state);
657 }
658}
659
660struct CmdAppHandler<H> {
661 handler: H,
662 handle: Arc<CommandHandle>,
663}
664impl<H: AppHandler<AppCommandArgs>> AppHandler<CommandArgs> for CmdAppHandler<H> {
665 fn event(&mut self, args: &CommandArgs, handler_args: &AppHandlerArgs) {
666 let args = AppCommandArgs {
667 args: args.clone(),
668 handle: self.handle.clone(),
669 };
670 self.handler.event(&args, handler_args);
671 }
672}
673
674#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
678pub enum CommandScope {
679 App,
681 Window(WindowId),
686 Widget(WidgetId),
688}
689impl_from_and_into_var! {
690 fn from(id: WidgetId) -> CommandScope {
691 CommandScope::Widget(id)
692 }
693 fn from(id: WindowId) -> CommandScope {
694 CommandScope::Window(id)
695 }
696 fn from(widget_name: &'static str) -> CommandScope {
698 WidgetId::named(widget_name).into()
699 }
700 fn from(widget_name: Txt) -> CommandScope {
702 WidgetId::named(widget_name).into()
703 }
704}
705
706event_args! {
707 pub struct CommandArgs {
709 pub param: Option<CommandParam>,
711
712 pub scope: CommandScope,
714
715 pub enabled: bool,
720
721 ..
722
723 fn delivery_list(&self, list: &mut UpdateDeliveryList) {
726 match self.scope {
727 CommandScope::Widget(id) => list.search_widget(id),
728 CommandScope::Window(id) => list.insert_window(id),
729 CommandScope::App => list.search_all(),
730 }
731 }
732 }
733}
734impl CommandArgs {
735 pub fn param<T: Any>(&self) -> Option<&T> {
737 self.param.as_ref().and_then(|p| p.downcast_ref::<T>())
738 }
739
740 pub fn enabled_param<T: Any>(&self) -> Option<&T> {
745 if self.enabled { self.param::<T>() } else { None }
746 }
747
748 pub fn disabled_param<T: Any>(&self) -> Option<&T> {
753 if !self.enabled { self.param::<T>() } else { None }
754 }
755
756 pub fn handle_enabled<F, R>(&self, local_handle: &CommandHandle, handler: F) -> Option<R>
764 where
765 F: FnOnce(&Self) -> R,
766 {
767 if self.propagation().is_stopped() || !self.enabled || !local_handle.is_enabled() {
768 None
769 } else {
770 let r = handler(self);
771 self.propagation().stop();
772 Some(r)
773 }
774 }
775}
776
777#[non_exhaustive]
779#[derive(Debug, Clone)]
780pub struct AppCommandArgs {
781 pub args: CommandArgs,
783 pub handle: Arc<CommandHandle>,
785}
786impl ops::Deref for AppCommandArgs {
787 type Target = CommandArgs;
788
789 fn deref(&self) -> &Self::Target {
790 &self.args
791 }
792}
793impl AnyEventArgs for AppCommandArgs {
794 fn clone_any(&self) -> Box<dyn AnyEventArgs> {
795 Box::new(self.clone())
796 }
797
798 fn as_any(&self) -> &dyn Any {
799 self
800 }
801
802 fn timestamp(&self) -> crate::DInstant {
803 self.args.timestamp()
804 }
805
806 fn delivery_list(&self, list: &mut UpdateDeliveryList) {
807 self.args.delivery_list(list)
808 }
809
810 fn propagation(&self) -> &EventPropagationHandle {
811 self.args.propagation()
812 }
813}
814impl EventArgs for AppCommandArgs {}
815
816pub struct CommandHandle {
823 command: Option<Command>,
824 local_enabled: AtomicBool,
825 app_id: Option<AppId>,
826 _event_handle: EventHandle,
827}
828impl CommandHandle {
829 pub fn command(&self) -> Option<Command> {
831 self.command
832 }
833
834 pub fn set_enabled(&self, enabled: bool) {
838 if let Some(command) = self.command {
839 if self.local_enabled.swap(enabled, Ordering::Relaxed) != enabled {
840 if self.app_id != APP.id() {
841 return;
842 }
843
844 UpdatesTrace::log_var(std::any::type_name::<bool>());
845
846 let mut write = command.local.write();
847 match command.scope {
848 CommandScope::App => {
849 if enabled {
850 write.enabled_count += 1;
851 } else {
852 write.enabled_count -= 1;
853 }
854 }
855 scope => {
856 if let Some(data) = write.scopes.get_mut(&scope) {
857 if enabled {
858 data.enabled_count += 1;
859 } else {
860 data.enabled_count -= 1;
861 }
862 }
863 }
864 }
865 }
866 }
867 }
868
869 pub fn is_enabled(&self) -> bool {
871 self.local_enabled.load(Ordering::Relaxed)
872 }
873
874 pub fn dummy() -> Self {
876 CommandHandle {
877 command: None,
878 app_id: None,
879 local_enabled: AtomicBool::new(false),
880 _event_handle: EventHandle::dummy(),
881 }
882 }
883
884 pub fn is_dummy(&self) -> bool {
886 self.command.is_none()
887 }
888}
889impl fmt::Debug for CommandHandle {
890 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
891 f.debug_struct("CommandHandle")
892 .field("command", &self.command)
893 .field("local_enabled", &self.local_enabled.load(Ordering::Relaxed))
894 .finish()
895 }
896}
897impl Drop for CommandHandle {
898 fn drop(&mut self) {
899 if let Some(command) = self.command {
900 if self.app_id != APP.id() {
901 return;
902 }
903
904 let mut write = command.local.write();
905 match command.scope {
906 CommandScope::App => {
907 write.handle_count -= 1;
908 if self.local_enabled.load(Ordering::Relaxed) {
909 write.enabled_count -= 1;
910 }
911 }
912 scope => {
913 if let Some(data) = write.scopes.get_mut(&scope) {
914 data.handle_count -= 1;
915 if self.local_enabled.load(Ordering::Relaxed) {
916 data.enabled_count -= 1;
917 }
918 }
919 }
920 }
921 }
922 }
923}
924impl Default for CommandHandle {
925 fn default() -> Self {
926 Self::dummy()
927 }
928}
929
930#[derive(Clone)]
932#[non_exhaustive]
933pub struct CommandParam(pub Arc<dyn Any + Send + Sync>);
934impl PartialEq for CommandParam {
935 fn eq(&self, other: &Self) -> bool {
936 Arc::ptr_eq(&self.0, &other.0)
937 }
938}
939impl Eq for CommandParam {}
940impl CommandParam {
941 pub fn new(param: impl Any + Send + Sync + 'static) -> Self {
945 let p: &dyn Any = ¶m;
946 if let Some(p) = p.downcast_ref::<Self>() {
947 p.clone()
948 } else if let Some(p) = p.downcast_ref::<Arc<dyn Any + Send + Sync>>() {
949 CommandParam(p.clone())
950 } else {
951 CommandParam(Arc::new(param))
952 }
953 }
954
955 pub fn type_id(&self) -> TypeId {
957 self.0.type_id()
958 }
959
960 pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
962 self.0.downcast_ref()
963 }
964
965 pub fn is<T: Any>(&self) -> bool {
967 self.0.is::<T>()
968 }
969}
970impl fmt::Debug for CommandParam {
971 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
972 f.debug_tuple("CommandParam").field(&self.0.type_id()).finish()
973 }
974}
975zng_var::impl_from_and_into_var! {
976 fn from(param: CommandParam) -> Option<CommandParam>;
977}
978
979unique_id_64! {
980 pub struct CommandMetaVarId<T: (StateValue + VarValue)>: StateId;
986}
987zng_unique_id::impl_unique_id_bytemuck!(CommandMetaVarId<T: (StateValue + VarValue)>);
988impl<T: StateValue + VarValue> CommandMetaVarId<T> {
989 fn app(self) -> StateId<ArcVar<T>> {
990 let id = self.get();
991 StateId::from_raw(id)
992 }
993
994 fn scope(self) -> StateId<ArcCowVar<T, ArcVar<T>>> {
995 let id = self.get();
996 StateId::from_raw(id)
997 }
998}
999
1000impl<T: StateValue + VarValue> fmt::Debug for CommandMetaVarId<T> {
1001 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1002 #[cfg(debug_assertions)]
1003 let t = pretty_type_name::pretty_type_name::<T>();
1004 #[cfg(not(debug_assertions))]
1005 let t = "$T";
1006
1007 if f.alternate() {
1008 writeln!(f, "CommandMetaVarId<{t} {{")?;
1009 writeln!(f, " id: {},", self.get())?;
1010 writeln!(f, " sequential: {}", self.sequential())?;
1011 writeln!(f, "}}")
1012 } else {
1013 write!(f, "CommandMetaVarId<{t}>({})", self.sequential())
1014 }
1015 }
1016}
1017
1018pub struct CommandMeta<'a> {
1083 meta: StateMapMut<'a, CommandMetaState>,
1084 scope: Option<StateMapMut<'a, CommandMetaState>>,
1085}
1086impl CommandMeta<'_> {
1087 pub fn get_or_insert<T, F>(&mut self, id: impl Into<StateId<T>>, init: F) -> T
1093 where
1094 T: StateValue + Clone,
1095 F: FnOnce() -> T,
1096 {
1097 let id = id.into();
1098 if let Some(scope) = &mut self.scope {
1099 if let Some(value) = scope.get(id) {
1100 value.clone()
1101 } else if let Some(value) = self.meta.get(id) {
1102 value.clone()
1103 } else {
1104 let value = init();
1105 let r = value.clone();
1106 scope.set(id, value);
1107 r
1108 }
1109 } else {
1110 self.meta.entry(id).or_insert_with(init).clone()
1111 }
1112 }
1113
1114 pub fn get_or_default<T>(&mut self, id: impl Into<StateId<T>>) -> T
1120 where
1121 T: StateValue + Clone + Default,
1122 {
1123 self.get_or_insert(id, Default::default)
1124 }
1125
1126 pub fn get<T>(&self, id: impl Into<StateId<T>>) -> Option<T>
1130 where
1131 T: StateValue + Clone,
1132 {
1133 let id = id.into();
1134 if let Some(scope) = &self.scope {
1135 scope.get(id).or_else(|| self.meta.get(id))
1136 } else {
1137 self.meta.get(id)
1138 }
1139 .cloned()
1140 }
1141
1142 pub fn set<T>(&mut self, id: impl Into<StateId<T>>, value: impl Into<T>)
1146 where
1147 T: StateValue + Clone,
1148 {
1149 if let Some(scope) = &mut self.scope {
1150 scope.set(id, value);
1151 } else {
1152 self.meta.set(id, value);
1153 }
1154 }
1155
1156 pub fn init<T>(&mut self, id: impl Into<StateId<T>>, value: impl Into<T>)
1160 where
1161 T: StateValue + Clone,
1162 {
1163 self.meta.entry(id).or_insert(value);
1164 }
1165
1166 pub fn get_var_or_insert<T, F>(&mut self, id: impl Into<CommandMetaVarId<T>>, init: F) -> CommandMetaVar<T>
1172 where
1173 T: StateValue + VarValue,
1174 F: FnOnce() -> T,
1175 {
1176 let id = id.into();
1177 if let Some(scope) = &mut self.scope {
1178 let meta = &mut self.meta;
1179 scope
1180 .entry(id.scope())
1181 .or_insert_with(|| {
1182 let var = meta.entry(id.app()).or_insert_with(|| var(init())).clone();
1183 var.cow()
1184 })
1185 .clone()
1186 .boxed()
1187 } else {
1188 self.meta.entry(id.app()).or_insert_with(|| var(init())).clone().boxed()
1189 }
1190 }
1191
1192 pub fn get_var<T>(&self, id: impl Into<CommandMetaVarId<T>>) -> Option<CommandMetaVar<T>>
1194 where
1195 T: StateValue + VarValue,
1196 {
1197 let id = id.into();
1198 if let Some(scope) = &self.scope {
1199 let meta = &self.meta;
1200 scope
1201 .get(id.scope())
1202 .map(|c| c.clone().boxed())
1203 .or_else(|| meta.get(id.app()).map(|c| c.clone().boxed()))
1204 } else {
1205 self.meta.get(id.app()).map(|c| c.clone().boxed())
1206 }
1207 }
1208
1209 pub fn get_var_or_default<T>(&mut self, id: impl Into<CommandMetaVarId<T>>) -> CommandMetaVar<T>
1213 where
1214 T: StateValue + VarValue + Default,
1215 {
1216 self.get_var_or_insert(id, Default::default)
1217 }
1218
1219 pub fn init_var<T>(&mut self, id: impl Into<CommandMetaVarId<T>>, value: impl Into<T>)
1223 where
1224 T: StateValue + VarValue,
1225 {
1226 self.meta.entry(id.into().app()).or_insert_with(|| var(value.into()));
1227 }
1228}
1229
1230pub type CommandMetaVar<T> = BoxedVar<T>;
1240
1241pub type ReadOnlyCommandMetaVar<T> = BoxedVar<T>;
1247
1248pub trait CommandNameExt {
1250 fn name(self) -> CommandMetaVar<Txt>;
1252
1253 fn init_name(self, name: impl Into<Txt>) -> Self;
1255
1256 fn name_with_shortcut(self) -> BoxedVar<Txt>
1259 where
1260 Self: crate::shortcut::CommandShortcutExt;
1261}
1262static_id! {
1263 static ref COMMAND_NAME_ID: CommandMetaVarId<Txt>;
1264}
1265impl CommandNameExt for Command {
1266 fn name(self) -> CommandMetaVar<Txt> {
1267 self.with_meta(|m| {
1268 m.get_var_or_insert(*COMMAND_NAME_ID, || {
1269 let name = self.event.name();
1270 let name = name.strip_suffix("_CMD").unwrap_or(name);
1271 let mut title = String::with_capacity(name.len());
1272 let mut lower = false;
1273 for c in name.chars() {
1274 if c == '_' {
1275 if !title.ends_with(' ') {
1276 title.push(' ');
1277 }
1278 lower = false;
1279 } else if lower {
1280 for l in c.to_lowercase() {
1281 title.push(l);
1282 }
1283 } else {
1284 title.push(c);
1285 lower = true;
1286 }
1287 }
1288 Txt::from(title)
1289 })
1290 })
1291 }
1292
1293 fn init_name(self, name: impl Into<Txt>) -> Self {
1294 self.with_meta(|m| m.init_var(*COMMAND_NAME_ID, name.into()));
1295 self
1296 }
1297
1298 fn name_with_shortcut(self) -> BoxedVar<Txt>
1299 where
1300 Self: crate::shortcut::CommandShortcutExt,
1301 {
1302 crate::var::merge_var!(self.name(), self.shortcut(), |name, shortcut| {
1303 if shortcut.is_empty() {
1304 name.clone()
1305 } else {
1306 zng_txt::formatx!("{name} ({})", shortcut[0])
1307 }
1308 })
1309 .boxed()
1310 }
1311}
1312
1313pub trait CommandInfoExt {
1315 fn info(self) -> CommandMetaVar<Txt>;
1317
1318 fn init_info(self, info: impl Into<Txt>) -> Self;
1320}
1321static_id! {
1322 static ref COMMAND_INFO_ID: CommandMetaVarId<Txt>;
1323}
1324impl CommandInfoExt for Command {
1325 fn info(self) -> CommandMetaVar<Txt> {
1326 self.with_meta(|m| m.get_var_or_insert(*COMMAND_INFO_ID, Txt::default))
1327 }
1328
1329 fn init_info(self, info: impl Into<Txt>) -> Self {
1330 self.with_meta(|m| m.init_var(*COMMAND_INFO_ID, info.into()));
1331 self
1332 }
1333}
1334
1335enum CommandMetaState {}
1336
1337#[derive(Clone)]
1338enum MetaInit {
1339 Init(fn(Command)),
1340 Initing(Arc<(ThreadId, Mutex<()>)>),
1342 Inited,
1343}
1344
1345#[doc(hidden)]
1346pub struct CommandData {
1347 meta_init: MetaInit,
1348 meta: Mutex<OwnedStateMap<CommandMetaState>>,
1349
1350 handle_count: usize,
1351 enabled_count: usize,
1352 registered: bool,
1353
1354 has_handlers: ArcVar<bool>,
1355 is_enabled: ArcVar<bool>,
1356
1357 scopes: HashMap<CommandScope, ScopedValue>,
1358}
1359impl CommandData {
1360 pub fn new(meta_init: fn(Command)) -> Self {
1361 CommandData {
1362 meta_init: MetaInit::Init(meta_init),
1363 meta: Mutex::new(OwnedStateMap::new()),
1364
1365 handle_count: 0,
1366 enabled_count: 0,
1367 registered: false,
1368
1369 has_handlers: var(false),
1370 is_enabled: var(false),
1371
1372 scopes: HashMap::default(),
1373 }
1374 }
1375
1376 fn subscribe(&mut self, events: &mut EventsService, command: Command, enabled: bool, mut target: Option<WidgetId>) -> CommandHandle {
1377 match command.scope {
1378 CommandScope::App => {
1379 if !mem::replace(&mut self.registered, true) {
1380 events.register_command(command);
1381 }
1382
1383 self.handle_count += 1;
1384 if enabled {
1385 self.enabled_count += 1;
1386 }
1387 }
1388 scope => {
1389 let data = self.scopes.entry(scope).or_default();
1390
1391 if !mem::replace(&mut data.registered, true) {
1392 events.register_command(command);
1393 }
1394
1395 data.handle_count += 1;
1396 if enabled {
1397 data.enabled_count += 1;
1398 }
1399
1400 if let CommandScope::Widget(id) = scope {
1401 target = Some(id);
1402 }
1403 }
1404 };
1405
1406 CommandHandle {
1407 command: Some(command),
1408 app_id: APP.id(),
1409 local_enabled: AtomicBool::new(enabled),
1410 _event_handle: target.map(|t| command.event.subscribe(t)).unwrap_or_else(EventHandle::dummy),
1411 }
1412 }
1413}
1414
1415struct ScopedValue {
1416 handle_count: usize,
1417 enabled_count: usize,
1418 is_enabled: ArcVar<bool>,
1419 has_handlers: ArcVar<bool>,
1420 meta: Mutex<OwnedStateMap<CommandMetaState>>,
1421 registered: bool,
1422}
1423impl Default for ScopedValue {
1424 fn default() -> Self {
1425 ScopedValue {
1426 is_enabled: var(false),
1427 has_handlers: var(false),
1428 handle_count: 0,
1429 enabled_count: 0,
1430 meta: Mutex::new(OwnedStateMap::default()),
1431 registered: false,
1432 }
1433 }
1434}
1435
1436#[cfg(test)]
1437mod tests {
1438 use super::*;
1439
1440 command! {
1441 static FOO_CMD;
1442 }
1443
1444 #[test]
1445 fn parameter_none() {
1446 let _ = CommandArgs::now(None, CommandScope::App, true);
1447 }
1448
1449 #[test]
1450 fn enabled() {
1451 let _app = APP.minimal().run_headless(false);
1452
1453 assert!(!FOO_CMD.has_handlers_value());
1454
1455 let handle = FOO_CMD.subscribe(true);
1456 assert!(FOO_CMD.is_enabled_value());
1457
1458 handle.set_enabled(false);
1459 assert!(FOO_CMD.has_handlers_value());
1460 assert!(!FOO_CMD.is_enabled_value());
1461
1462 handle.set_enabled(true);
1463 assert!(FOO_CMD.is_enabled_value());
1464
1465 drop(handle);
1466 assert!(!FOO_CMD.has_handlers_value());
1467 }
1468
1469 #[test]
1470 fn enabled_scoped() {
1471 let _app = APP.minimal().run_headless(false);
1472
1473 let cmd = FOO_CMD;
1474 let cmd_scoped = FOO_CMD.scoped(WindowId::named("enabled_scoped"));
1475 assert!(!cmd.has_handlers_value());
1476 assert!(!cmd_scoped.has_handlers_value());
1477
1478 let handle_scoped = cmd_scoped.subscribe(true);
1479 assert!(!cmd.has_handlers_value());
1480 assert!(cmd_scoped.is_enabled_value());
1481
1482 handle_scoped.set_enabled(false);
1483 assert!(!cmd.has_handlers_value());
1484 assert!(!cmd_scoped.is_enabled_value());
1485 assert!(cmd_scoped.has_handlers_value());
1486
1487 handle_scoped.set_enabled(true);
1488 assert!(!cmd.has_handlers_value());
1489 assert!(cmd_scoped.is_enabled_value());
1490
1491 drop(handle_scoped);
1492 assert!(!cmd.has_handlers_value());
1493 assert!(!cmd_scoped.has_handlers_value());
1494 }
1495
1496 #[test]
1497 fn has_handlers() {
1498 let _app = APP.minimal().run_headless(false);
1499
1500 assert!(!FOO_CMD.has_handlers_value());
1501
1502 let handle = FOO_CMD.subscribe(false);
1503 assert!(FOO_CMD.has_handlers_value());
1504
1505 drop(handle);
1506 assert!(!FOO_CMD.has_handlers_value());
1507 }
1508
1509 #[test]
1510 fn has_handlers_scoped() {
1511 let _app = APP.minimal().run_headless(false);
1512
1513 let cmd = FOO_CMD;
1514 let cmd_scoped = FOO_CMD.scoped(WindowId::named("has_handlers_scoped"));
1515
1516 assert!(!cmd.has_handlers_value());
1517 assert!(!cmd_scoped.has_handlers_value());
1518
1519 let handle = cmd_scoped.subscribe(false);
1520
1521 assert!(!cmd.has_handlers_value());
1522 assert!(cmd_scoped.has_handlers_value());
1523
1524 drop(handle);
1525
1526 assert!(!cmd.has_handlers_value());
1527 assert!(!cmd_scoped.has_handlers_value());
1528 }
1529
1530 }