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]
143macro_rules! command {
144 ($(
145 $(#[$attr:meta])*
146 $vis:vis static $COMMAND:ident $(=> |$cmd:ident|$custom_meta_init:expr ;)? $(= { $($meta_ident:ident $(!)? : $meta_init:expr),* $(,)? };)? $(;)?
147 )+) => {
148 $(
149 $crate::__command! {
150 $(#[$attr])*
151 $vis static $COMMAND $(=> |$cmd|$custom_meta_init)? $(= {
152 $($meta_ident: $meta_init,)+
153 })? ;
154 }
155 )+
156 }
157}
158#[doc(inline)]
159pub use command;
160
161use zng_app_context::AppId;
162use zng_state_map::{OwnedStateMap, StateId, StateMapMut, StateValue};
163use zng_txt::Txt;
164use zng_unique_id::{static_id, unique_id_64};
165use zng_var::{Var, VarValue, impl_from_and_into_var, var};
166
167#[doc(hidden)]
168pub use zng_app_context::app_local;
169
170#[doc(hidden)]
171pub use pastey::paste;
172
173#[doc(hidden)]
174#[macro_export]
175macro_rules! __command {
176 (
177 $(#[$attr:meta])*
178 $vis:vis static $COMMAND:ident => |$cmd:ident| $meta_init:expr;
179 ) => {
180 $(#[$attr])*
181 $vis static $COMMAND: $crate::event::Command = {
182 fn __meta_init__($cmd: $crate::event::Command) {
183 $meta_init
184 }
185 $crate::event::app_local! {
186 static EVENT: $crate::event::EventData = const { $crate::event::EventData::new(std::stringify!($COMMAND)) };
187 static DATA: $crate::event::CommandData = $crate::event::CommandData::new(__meta_init__);
188 }
189 $crate::event::Command::new(&EVENT, &DATA)
190 };
191 };
192 (
193 $(#[$attr:meta])*
194 $vis:vis static $COMMAND:ident = { l10n: $l10n_arg:expr, $($meta_ident:ident : $meta_init:expr),* $(,)? };
195 ) => {
196 $crate::event::paste! {
197 $crate::__command! {
198 $(#[$attr])*
199 $(#[doc = concat!("<tr> <td>", stringify!($meta_ident), "</td> <td>", stringify!($meta_init), "</td> </tr>")])+
208 $vis static $COMMAND => |cmd| {
214 let __l10n_arg = $l10n_arg;
215 $(
216 cmd.[<init_ $meta_ident>]($meta_init);
217 $crate::event::init_meta_l10n(std::env!("CARGO_PKG_NAME"), std::env!("CARGO_PKG_VERSION"), &__l10n_arg, cmd, stringify!($meta_ident), &cmd.$meta_ident());
218 )*
219 };
220 }
221 }
222 };
223 (
224 $(#[$attr:meta])*
225 $vis:vis static $COMMAND:ident = { $($meta_ident:ident : $meta_init:expr),* $(,)? };
226 ) => {
227 $crate::event::paste! {
228 $crate::__command! {
229 $(#[$attr])*
230 $(#[doc = concat!("<tr> <td>", stringify!($meta_ident), "</td> <td>", stringify!($meta_init), "</td> </tr>")])+
239 $vis static $COMMAND => |cmd| {
243 $(
244 cmd.[<init_ $meta_ident>]($meta_init);
245 )*
246 };
247 }
248 }
249 };
250 (
251 $(#[$attr:meta])*
252 $vis:vis static $COMMAND:ident;
253 ) => {
254 $crate::__command! {
255 $(#[$attr])*
256 $vis static $COMMAND => |_cmd|{};
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().as_any().name());
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}
332impl fmt::Debug for Command {
333 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
334 if f.alternate() {
335 f.debug_struct("Command")
336 .field("event", &self.event)
337 .field("scope", &self.scope)
338 .finish_non_exhaustive()
339 } else {
340 write!(f, "{}", self.event.name())?;
341 match self.scope {
342 CommandScope::App => Ok(()),
343 CommandScope::Window(id) => write!(f, "({id})"),
344 CommandScope::Widget(id) => write!(f, "({id})"),
345 }
346 }
347 }
348}
349impl Command {
350 #[doc(hidden)]
351 pub const fn new(event_local: &'static AppLocal<EventData>, command_local: &'static AppLocal<CommandData>) -> Self {
352 Command {
353 event: Event::new(event_local),
354 local: command_local,
355 scope: CommandScope::App,
356 }
357 }
358
359 pub fn subscribe(&self, enabled: bool) -> CommandHandle {
366 let mut evs = EVENTS_SV.write();
367 self.local.write().subscribe(&mut evs, *self, enabled, None)
368 }
369
370 pub fn subscribe_wgt(&self, enabled: bool, target: WidgetId) -> CommandHandle {
378 let mut evs = EVENTS_SV.write();
379 self.local.write().subscribe(&mut evs, *self, enabled, Some(target))
380 }
381
382 pub fn event(&self) -> Event<CommandArgs> {
384 self.event
385 }
386
387 pub fn scope(&self) -> CommandScope {
389 self.scope
390 }
391
392 pub fn scoped(mut self, scope: impl Into<CommandScope>) -> Command {
394 self.scope = scope.into();
395 self
396 }
397
398 pub fn with_meta<R>(&self, visit: impl FnOnce(&mut CommandMeta) -> R) -> R {
407 fn init_meta(self_: &Command) -> parking_lot::MappedRwLockReadGuard<'static, CommandData> {
409 {
410 let mut write = self_.local.write();
411 match write.meta_init.clone() {
412 MetaInit::Init(init) => {
413 let lock = Arc::new((std::thread::current().id(), Mutex::new(())));
414 write.meta_init = MetaInit::Initing(lock.clone());
415 let _init_guard = lock.1.lock();
416 drop(write);
417 init(*self_);
418 self_.local.write().meta_init = MetaInit::Inited;
419 }
420 MetaInit::Initing(l) => {
421 drop(write);
422 if l.0 != std::thread::current().id() {
423 let _wait = l.1.lock();
424 }
425 }
426 MetaInit::Inited => {}
427 }
428 }
429
430 if !matches!(self_.scope, CommandScope::App) {
431 let mut write = self_.local.write();
432 write.scopes.entry(self_.scope).or_default();
433 }
434 self_.local.read()
435 }
436 let local_read = init_meta(self);
437 let mut meta_lock = local_read.meta.lock();
438
439 match self.scope {
440 CommandScope::App => visit(&mut CommandMeta {
441 meta: meta_lock.borrow_mut(),
442 scope: None,
443 }),
444 scope => {
445 let scope = local_read.scopes.get(&scope).unwrap();
446 visit(&mut CommandMeta {
447 meta: meta_lock.borrow_mut(),
448 scope: Some(scope.meta.lock().borrow_mut()),
449 })
450 }
451 }
452 }
453
454 pub fn has(&self, update: &EventUpdate) -> bool {
456 self.on(update).is_some()
457 }
458
459 pub fn on<'a>(&self, update: &'a EventUpdate) -> Option<&'a CommandArgs> {
461 self.event.on(update).filter(|a| a.scope == self.scope)
462 }
463
464 pub fn on_unhandled<'a>(&self, update: &'a EventUpdate) -> Option<&'a CommandArgs> {
466 self.event
467 .on(update)
468 .filter(|a| a.scope == self.scope && !a.propagation().is_stopped())
469 }
470
471 pub fn handle<R>(&self, update: &EventUpdate, handler: impl FnOnce(&CommandArgs) -> R) -> Option<R> {
474 if let Some(args) = self.on(update) {
475 args.handle(handler)
476 } else {
477 None
478 }
479 }
480
481 pub fn has_handlers(&self) -> Var<bool> {
483 let mut write = self.local.write();
484 match self.scope {
485 CommandScope::App => write.has_handlers.read_only(),
486 scope => write.scopes.entry(scope).or_default().has_handlers.read_only(),
487 }
488 }
489
490 pub fn is_enabled(&self) -> Var<bool> {
492 let mut write = self.local.write();
493 match self.scope {
494 CommandScope::App => write.is_enabled.read_only(),
495 scope => write.scopes.entry(scope).or_default().is_enabled.read_only(),
496 }
497 }
498
499 pub fn has_handlers_value(&self) -> bool {
501 let read = self.local.read();
502 match self.scope {
503 CommandScope::App => read.handle_count > 0,
504 scope => read.scopes.get(&scope).map(|l| l.handle_count > 0).unwrap_or(false),
505 }
506 }
507
508 pub fn is_enabled_value(&self) -> bool {
510 let read = self.local.read();
511 match self.scope {
512 CommandScope::App => read.enabled_count > 0,
513 scope => read.scopes.get(&scope).map(|l| l.enabled_count > 0).unwrap_or(false),
514 }
515 }
516
517 pub fn visit_scopes<T>(&self, mut visitor: impl FnMut(Command) -> ControlFlow<T>) -> Option<T> {
521 let read = self.local.read();
522 for &scope in read.scopes.keys() {
523 match visitor(self.scoped(scope)) {
524 ControlFlow::Continue(_) => continue,
525 ControlFlow::Break(r) => return Some(r),
526 }
527 }
528 None
529 }
530
531 pub fn notify(&self) {
533 self.event.notify(CommandArgs::now(None, self.scope, self.is_enabled_value()))
534 }
535
536 pub fn notify_descendants(&self, parent: &WidgetInfo) {
538 self.visit_scopes::<()>(|parse_cmd| {
539 if let CommandScope::Widget(id) = parse_cmd.scope()
540 && let Some(scope) = parent.tree().get(id)
541 && scope.is_descendant(parent)
542 {
543 parse_cmd.notify();
544 }
545 ControlFlow::Continue(())
546 });
547 }
548
549 pub fn notify_param(&self, param: impl Any + Send + Sync) {
551 self.event
552 .notify(CommandArgs::now(CommandParam::new(param), self.scope, self.is_enabled_value()));
553 }
554
555 pub fn notify_linked(&self, propagation: EventPropagationHandle, param: Option<CommandParam>) {
557 self.event.notify(CommandArgs::new(
558 crate::INSTANT.now(),
559 propagation,
560 param,
561 self.scope,
562 self.is_enabled_value(),
563 ))
564 }
565
566 pub fn new_update(&self) -> EventUpdate {
568 self.event.new_update(CommandArgs::now(None, self.scope, self.is_enabled_value()))
569 }
570
571 pub fn new_update_param(&self, param: impl Any + Send + Sync) -> EventUpdate {
573 self.event
574 .new_update(CommandArgs::now(CommandParam::new(param), self.scope, self.is_enabled_value()))
575 }
576
577 pub fn on_pre_event<H>(&self, enabled: bool, handler: H) -> EventHandle
585 where
586 H: AppHandler<AppCommandArgs>,
587 {
588 self.event().on_pre_event(CmdAppHandler {
589 handler,
590 handle: Arc::new(self.subscribe(enabled)),
591 })
592 }
593
594 pub fn on_event<H>(&self, enabled: bool, handler: H) -> EventHandle
602 where
603 H: AppHandler<AppCommandArgs>,
604 {
605 self.event().on_event(CmdAppHandler {
606 handler,
607 handle: Arc::new(self.subscribe(enabled)),
608 })
609 }
610
611 #[must_use]
613 pub(crate) fn update_state(&self) -> bool {
614 let mut write = self.local.write();
615 if let CommandScope::App = self.scope {
616 let has_handlers = write.handle_count > 0;
617 if has_handlers != write.has_handlers.get() {
618 write.has_handlers.set(has_handlers);
619 }
620 let is_enabled = has_handlers && write.enabled_count > 0;
621 if is_enabled != write.is_enabled.get() {
622 write.is_enabled.set(is_enabled);
623 }
624 true
625 } else if let hash_map::Entry::Occupied(entry) = write.scopes.entry(self.scope) {
626 let scope = entry.get();
627
628 if scope.handle_count == 0 && scope.has_handlers.strong_count() == 1 && scope.is_enabled.strong_count() == 1 {
629 entry.remove();
630 return false;
631 }
632
633 let has_handlers = scope.handle_count > 0;
634 if has_handlers != scope.has_handlers.get() {
635 scope.has_handlers.set(has_handlers);
636 }
637 let is_enabled = has_handlers && scope.enabled_count > 0;
638 if is_enabled != scope.is_enabled.get() {
639 scope.is_enabled.set(is_enabled);
640 }
641 true
642 } else {
643 false
644 }
645 }
646}
647impl Deref for Command {
648 type Target = Event<CommandArgs>;
649
650 fn deref(&self) -> &Self::Target {
651 &self.event
652 }
653}
654impl PartialEq for Command {
655 fn eq(&self, other: &Self) -> bool {
656 self.event == other.event && self.scope == other.scope
657 }
658}
659impl Eq for Command {}
660impl std::hash::Hash for Command {
661 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
662 std::hash::Hash::hash(&self.event.as_any(), state);
663 std::hash::Hash::hash(&self.scope, state);
664 }
665}
666
667struct CmdAppHandler<H> {
668 handler: H,
669 handle: Arc<CommandHandle>,
670}
671impl<H: AppHandler<AppCommandArgs>> AppHandler<CommandArgs> for CmdAppHandler<H> {
672 fn event(&mut self, args: &CommandArgs, handler_args: &AppHandlerArgs) {
673 let args = AppCommandArgs {
674 args: args.clone(),
675 handle: self.handle.clone(),
676 };
677 self.handler.event(&args, handler_args);
678 }
679}
680
681#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
685pub enum CommandScope {
686 App,
688 Window(WindowId),
693 Widget(WidgetId),
695}
696impl_from_and_into_var! {
697 fn from(id: WidgetId) -> CommandScope {
698 CommandScope::Widget(id)
699 }
700 fn from(id: WindowId) -> CommandScope {
701 CommandScope::Window(id)
702 }
703 fn from(widget_name: &'static str) -> CommandScope {
705 WidgetId::named(widget_name).into()
706 }
707 fn from(widget_name: Txt) -> CommandScope {
709 WidgetId::named(widget_name).into()
710 }
711}
712
713event_args! {
714 pub struct CommandArgs {
716 pub param: Option<CommandParam>,
718
719 pub scope: CommandScope,
721
722 pub enabled: bool,
727
728 ..
729
730 fn delivery_list(&self, list: &mut UpdateDeliveryList) {
733 match self.scope {
734 CommandScope::Widget(id) => list.search_widget(id),
735 CommandScope::Window(id) => list.insert_window(id),
736 CommandScope::App => list.search_all(),
737 }
738 }
739 }
740}
741impl CommandArgs {
742 pub fn param<T: Any>(&self) -> Option<&T> {
744 self.param.as_ref().and_then(|p| p.downcast_ref::<T>())
745 }
746
747 pub fn enabled_param<T: Any>(&self) -> Option<&T> {
752 if self.enabled { self.param::<T>() } else { None }
753 }
754
755 pub fn disabled_param<T: Any>(&self) -> Option<&T> {
760 if !self.enabled { self.param::<T>() } else { None }
761 }
762
763 pub fn handle_enabled<F, R>(&self, local_handle: &CommandHandle, handler: F) -> Option<R>
771 where
772 F: FnOnce(&Self) -> R,
773 {
774 if self.propagation().is_stopped() || !self.enabled || !local_handle.is_enabled() {
775 None
776 } else {
777 let r = handler(self);
778 self.propagation().stop();
779 Some(r)
780 }
781 }
782}
783
784#[non_exhaustive]
786#[derive(Debug, Clone)]
787pub struct AppCommandArgs {
788 pub args: CommandArgs,
790 pub handle: Arc<CommandHandle>,
792}
793impl ops::Deref for AppCommandArgs {
794 type Target = CommandArgs;
795
796 fn deref(&self) -> &Self::Target {
797 &self.args
798 }
799}
800impl AnyEventArgs for AppCommandArgs {
801 fn clone_any(&self) -> Box<dyn AnyEventArgs> {
802 Box::new(self.clone())
803 }
804
805 fn as_any(&self) -> &dyn Any {
806 self
807 }
808
809 fn timestamp(&self) -> crate::DInstant {
810 self.args.timestamp()
811 }
812
813 fn delivery_list(&self, list: &mut UpdateDeliveryList) {
814 self.args.delivery_list(list)
815 }
816
817 fn propagation(&self) -> &EventPropagationHandle {
818 self.args.propagation()
819 }
820}
821impl EventArgs for AppCommandArgs {}
822
823pub struct CommandHandle {
830 command: Option<Command>,
831 local_enabled: AtomicBool,
832 app_id: Option<AppId>,
833 _event_handle: EventHandle,
834}
835impl CommandHandle {
836 pub fn command(&self) -> Option<Command> {
838 self.command
839 }
840
841 pub fn set_enabled(&self, enabled: bool) {
845 if let Some(command) = self.command
846 && self.local_enabled.swap(enabled, Ordering::Relaxed) != enabled
847 {
848 if self.app_id != APP.id() {
849 return;
850 }
851
852 UpdatesTrace::log_var(std::any::type_name::<bool>());
853
854 let mut write = command.local.write();
855 match command.scope {
856 CommandScope::App => {
857 if enabled {
858 write.enabled_count += 1;
859 } else {
860 write.enabled_count -= 1;
861 }
862 }
863 scope => {
864 if let Some(data) = write.scopes.get_mut(&scope) {
865 if enabled {
866 data.enabled_count += 1;
867 } else {
868 data.enabled_count -= 1;
869 }
870 }
871 }
872 }
873 }
874 }
875
876 pub fn is_enabled(&self) -> bool {
878 self.local_enabled.load(Ordering::Relaxed)
879 }
880
881 pub fn dummy() -> Self {
883 CommandHandle {
884 command: None,
885 app_id: None,
886 local_enabled: AtomicBool::new(false),
887 _event_handle: EventHandle::dummy(),
888 }
889 }
890
891 pub fn is_dummy(&self) -> bool {
893 self.command.is_none()
894 }
895}
896impl fmt::Debug for CommandHandle {
897 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
898 f.debug_struct("CommandHandle")
899 .field("command", &self.command)
900 .field("local_enabled", &self.local_enabled.load(Ordering::Relaxed))
901 .finish()
902 }
903}
904impl Drop for CommandHandle {
905 fn drop(&mut self) {
906 if let Some(command) = self.command {
907 if self.app_id != APP.id() {
908 return;
909 }
910
911 let mut write = command.local.write();
912 match command.scope {
913 CommandScope::App => {
914 write.handle_count -= 1;
915 if self.local_enabled.load(Ordering::Relaxed) {
916 write.enabled_count -= 1;
917 }
918 }
919 scope => {
920 if let Some(data) = write.scopes.get_mut(&scope) {
921 data.handle_count -= 1;
922 if self.local_enabled.load(Ordering::Relaxed) {
923 data.enabled_count -= 1;
924 }
925 }
926 }
927 }
928 }
929 }
930}
931impl Default for CommandHandle {
932 fn default() -> Self {
933 Self::dummy()
934 }
935}
936
937#[derive(Clone)]
939#[non_exhaustive]
940pub struct CommandParam(pub Arc<dyn Any + Send + Sync>);
941impl PartialEq for CommandParam {
942 fn eq(&self, other: &Self) -> bool {
943 Arc::ptr_eq(&self.0, &other.0)
944 }
945}
946impl Eq for CommandParam {}
947impl CommandParam {
948 pub fn new(param: impl Any + Send + Sync + 'static) -> Self {
952 let p: &dyn Any = ¶m;
953 if let Some(p) = p.downcast_ref::<Self>() {
954 p.clone()
955 } else if let Some(p) = p.downcast_ref::<Arc<dyn Any + Send + Sync>>() {
956 CommandParam(p.clone())
957 } else {
958 CommandParam(Arc::new(param))
959 }
960 }
961
962 pub fn type_id(&self) -> TypeId {
964 self.0.type_id()
965 }
966
967 pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
969 self.0.downcast_ref()
970 }
971
972 pub fn is<T: Any>(&self) -> bool {
974 self.0.is::<T>()
975 }
976}
977impl fmt::Debug for CommandParam {
978 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
979 f.debug_tuple("CommandParam").field(&self.0.type_id()).finish()
980 }
981}
982zng_var::impl_from_and_into_var! {
983 fn from(param: CommandParam) -> Option<CommandParam>;
984}
985
986#[rustfmt::skip] unique_id_64! {
988 pub struct CommandMetaVarId<T: (StateValue + VarValue)>: StateId;
994}
995zng_unique_id::impl_unique_id_bytemuck!(CommandMetaVarId<T: (StateValue + VarValue)>);
996impl<T: StateValue + VarValue> CommandMetaVarId<T> {
997 fn app(self) -> StateId<Var<T>> {
998 let id = self.get();
999 StateId::from_raw(id)
1000 }
1001
1002 fn scope(self) -> StateId<Var<T>> {
1003 let id = self.get();
1004 StateId::from_raw(id)
1005 }
1006}
1007
1008impl<T: StateValue + VarValue> fmt::Debug for CommandMetaVarId<T> {
1009 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1010 #[cfg(debug_assertions)]
1011 let t = pretty_type_name::pretty_type_name::<T>();
1012 #[cfg(not(debug_assertions))]
1013 let t = "$T";
1014
1015 if f.alternate() {
1016 writeln!(f, "CommandMetaVarId<{t} {{")?;
1017 writeln!(f, " id: {},", self.get())?;
1018 writeln!(f, " sequential: {}", self.sequential())?;
1019 writeln!(f, "}}")
1020 } else {
1021 write!(f, "CommandMetaVarId<{t}>({})", self.sequential())
1022 }
1023 }
1024}
1025
1026pub struct CommandMeta<'a> {
1091 meta: StateMapMut<'a, CommandMetaState>,
1092 scope: Option<StateMapMut<'a, CommandMetaState>>,
1093}
1094impl CommandMeta<'_> {
1095 pub fn get_or_insert<T, F>(&mut self, id: impl Into<StateId<T>>, init: F) -> T
1101 where
1102 T: StateValue + Clone,
1103 F: FnOnce() -> T,
1104 {
1105 let id = id.into();
1106 if let Some(scope) = &mut self.scope {
1107 if let Some(value) = scope.get(id) {
1108 value.clone()
1109 } else if let Some(value) = self.meta.get(id) {
1110 value.clone()
1111 } else {
1112 let value = init();
1113 let r = value.clone();
1114 scope.set(id, value);
1115 r
1116 }
1117 } else {
1118 self.meta.entry(id).or_insert_with(init).clone()
1119 }
1120 }
1121
1122 pub fn get_or_default<T>(&mut self, id: impl Into<StateId<T>>) -> T
1128 where
1129 T: StateValue + Clone + Default,
1130 {
1131 self.get_or_insert(id, Default::default)
1132 }
1133
1134 pub fn get<T>(&self, id: impl Into<StateId<T>>) -> Option<T>
1138 where
1139 T: StateValue + Clone,
1140 {
1141 let id = id.into();
1142 if let Some(scope) = &self.scope {
1143 scope.get(id).or_else(|| self.meta.get(id))
1144 } else {
1145 self.meta.get(id)
1146 }
1147 .cloned()
1148 }
1149
1150 pub fn set<T>(&mut self, id: impl Into<StateId<T>>, value: impl Into<T>)
1154 where
1155 T: StateValue + Clone,
1156 {
1157 if let Some(scope) = &mut self.scope {
1158 scope.set(id, value);
1159 } else {
1160 self.meta.set(id, value);
1161 }
1162 }
1163
1164 pub fn init<T>(&mut self, id: impl Into<StateId<T>>, value: impl Into<T>)
1168 where
1169 T: StateValue + Clone,
1170 {
1171 self.meta.entry(id).or_insert(value);
1172 }
1173
1174 pub fn get_var_or_insert<T, F>(&mut self, id: impl Into<CommandMetaVarId<T>>, init: F) -> CommandMetaVar<T>
1180 where
1181 T: StateValue + VarValue,
1182 F: FnOnce() -> T,
1183 {
1184 let id = id.into();
1185 if let Some(scope) = &mut self.scope {
1186 let meta = &mut self.meta;
1187 scope
1188 .entry(id.scope())
1189 .or_insert_with(|| {
1190 let var = meta.entry(id.app()).or_insert_with(|| var(init())).clone();
1191 var.cow()
1192 })
1193 .clone()
1194 } else {
1195 self.meta.entry(id.app()).or_insert_with(|| var(init())).clone()
1196 }
1197 }
1198
1199 pub fn get_var<T>(&self, id: impl Into<CommandMetaVarId<T>>) -> Option<CommandMetaVar<T>>
1201 where
1202 T: StateValue + VarValue,
1203 {
1204 let id = id.into();
1205 if let Some(scope) = &self.scope {
1206 let meta = &self.meta;
1207 scope.get(id.scope()).cloned().or_else(|| meta.get(id.app()).cloned())
1208 } else {
1209 self.meta.get(id.app()).cloned()
1210 }
1211 }
1212
1213 pub fn get_var_or_default<T>(&mut self, id: impl Into<CommandMetaVarId<T>>) -> CommandMetaVar<T>
1217 where
1218 T: StateValue + VarValue + Default,
1219 {
1220 self.get_var_or_insert(id, Default::default)
1221 }
1222
1223 pub fn init_var<T>(&mut self, id: impl Into<CommandMetaVarId<T>>, value: impl Into<T>)
1227 where
1228 T: StateValue + VarValue,
1229 {
1230 self.meta.entry(id.into().app()).or_insert_with(|| var(value.into()));
1231 }
1232}
1233
1234pub type CommandMetaVar<T> = Var<T>;
1241
1242pub type ReadOnlyCommandMetaVar<T> = Var<T>;
1248
1249pub trait CommandNameExt {
1251 fn name(self) -> CommandMetaVar<Txt>;
1253
1254 fn init_name(self, name: impl Into<Txt>) -> Self;
1256
1257 fn name_with_shortcut(self) -> Var<Txt>
1263 where
1264 Self: crate::shortcut::CommandShortcutExt;
1265}
1266static_id! {
1267 static ref COMMAND_NAME_ID: CommandMetaVarId<Txt>;
1268}
1269impl CommandNameExt for Command {
1270 fn name(self) -> CommandMetaVar<Txt> {
1271 self.with_meta(|m| {
1272 m.get_var_or_insert(*COMMAND_NAME_ID, || {
1273 let name = self.event.name();
1274 let name = name.strip_suffix("_CMD").unwrap_or(name);
1275 let mut title = String::with_capacity(name.len());
1276 let mut lower = false;
1277 for c in name.chars() {
1278 if c == '_' {
1279 if !title.ends_with(' ') {
1280 title.push(' ');
1281 }
1282 lower = false;
1283 } else if lower {
1284 for l in c.to_lowercase() {
1285 title.push(l);
1286 }
1287 } else {
1288 title.push(c);
1289 lower = true;
1290 }
1291 }
1292 Txt::from(title)
1293 })
1294 })
1295 }
1296
1297 fn init_name(self, name: impl Into<Txt>) -> Self {
1298 self.with_meta(|m| m.init_var(*COMMAND_NAME_ID, name.into()));
1299 self
1300 }
1301
1302 fn name_with_shortcut(self) -> Var<Txt>
1303 where
1304 Self: crate::shortcut::CommandShortcutExt,
1305 {
1306 crate::var::merge_var!(self.name(), self.shortcut(), |name, shortcut| {
1307 if shortcut.is_empty() {
1308 name.clone()
1309 } else {
1310 zng_txt::formatx!("{name} ({})", shortcut[0])
1311 }
1312 })
1313 }
1314}
1315
1316pub trait CommandInfoExt {
1318 fn info(self) -> CommandMetaVar<Txt>;
1320
1321 fn init_info(self, info: impl Into<Txt>) -> Self;
1323}
1324static_id! {
1325 static ref COMMAND_INFO_ID: CommandMetaVarId<Txt>;
1326}
1327impl CommandInfoExt for Command {
1328 fn info(self) -> CommandMetaVar<Txt> {
1329 self.with_meta(|m| m.get_var_or_insert(*COMMAND_INFO_ID, Txt::default))
1330 }
1331
1332 fn init_info(self, info: impl Into<Txt>) -> Self {
1333 self.with_meta(|m| m.init_var(*COMMAND_INFO_ID, info.into()));
1334 self
1335 }
1336}
1337
1338enum CommandMetaState {}
1339
1340#[derive(Clone)]
1341enum MetaInit {
1342 Init(fn(Command)),
1343 Initing(Arc<(ThreadId, Mutex<()>)>),
1345 Inited,
1346}
1347
1348#[doc(hidden)]
1349pub struct CommandData {
1350 meta_init: MetaInit,
1351 meta: Mutex<OwnedStateMap<CommandMetaState>>,
1352
1353 handle_count: usize,
1354 enabled_count: usize,
1355 registered: bool,
1356
1357 has_handlers: Var<bool>,
1358 is_enabled: Var<bool>,
1359
1360 scopes: HashMap<CommandScope, ScopedValue>,
1361}
1362impl CommandData {
1363 pub fn new(meta_init: fn(Command)) -> Self {
1364 CommandData {
1365 meta_init: MetaInit::Init(meta_init),
1366 meta: Mutex::new(OwnedStateMap::new()),
1367
1368 handle_count: 0,
1369 enabled_count: 0,
1370 registered: false,
1371
1372 has_handlers: var(false),
1373 is_enabled: var(false),
1374
1375 scopes: HashMap::default(),
1376 }
1377 }
1378
1379 fn subscribe(&mut self, events: &mut EventsService, command: Command, enabled: bool, mut target: Option<WidgetId>) -> CommandHandle {
1380 match command.scope {
1381 CommandScope::App => {
1382 if !mem::replace(&mut self.registered, true) {
1383 events.register_command(command);
1384 }
1385
1386 self.handle_count += 1;
1387 if enabled {
1388 self.enabled_count += 1;
1389 }
1390 }
1391 scope => {
1392 let data = self.scopes.entry(scope).or_default();
1393
1394 if !mem::replace(&mut data.registered, true) {
1395 events.register_command(command);
1396 }
1397
1398 data.handle_count += 1;
1399 if enabled {
1400 data.enabled_count += 1;
1401 }
1402
1403 if let CommandScope::Widget(id) = scope {
1404 target = Some(id);
1405 }
1406 }
1407 };
1408
1409 CommandHandle {
1410 command: Some(command),
1411 app_id: APP.id(),
1412 local_enabled: AtomicBool::new(enabled),
1413 _event_handle: target.map(|t| command.event.subscribe(t)).unwrap_or_else(EventHandle::dummy),
1414 }
1415 }
1416}
1417
1418struct ScopedValue {
1419 handle_count: usize,
1420 enabled_count: usize,
1421 is_enabled: Var<bool>,
1422 has_handlers: Var<bool>,
1423 meta: Mutex<OwnedStateMap<CommandMetaState>>,
1424 registered: bool,
1425}
1426impl Default for ScopedValue {
1427 fn default() -> Self {
1428 ScopedValue {
1429 is_enabled: var(false),
1430 has_handlers: var(false),
1431 handle_count: 0,
1432 enabled_count: 0,
1433 meta: Mutex::new(OwnedStateMap::default()),
1434 registered: false,
1435 }
1436 }
1437}
1438
1439#[cfg(test)]
1440mod tests {
1441 use super::*;
1442
1443 command! {
1444 static FOO_CMD;
1445 }
1446
1447 #[test]
1448 fn parameter_none() {
1449 let _ = CommandArgs::now(None, CommandScope::App, true);
1450 }
1451
1452 #[test]
1453 fn enabled() {
1454 let _app = APP.minimal().run_headless(false);
1455
1456 assert!(!FOO_CMD.has_handlers_value());
1457
1458 let handle = FOO_CMD.subscribe(true);
1459 assert!(FOO_CMD.is_enabled_value());
1460
1461 handle.set_enabled(false);
1462 assert!(FOO_CMD.has_handlers_value());
1463 assert!(!FOO_CMD.is_enabled_value());
1464
1465 handle.set_enabled(true);
1466 assert!(FOO_CMD.is_enabled_value());
1467
1468 drop(handle);
1469 assert!(!FOO_CMD.has_handlers_value());
1470 }
1471
1472 #[test]
1473 fn enabled_scoped() {
1474 let _app = APP.minimal().run_headless(false);
1475
1476 let cmd = FOO_CMD;
1477 let cmd_scoped = FOO_CMD.scoped(WindowId::named("enabled_scoped"));
1478 assert!(!cmd.has_handlers_value());
1479 assert!(!cmd_scoped.has_handlers_value());
1480
1481 let handle_scoped = cmd_scoped.subscribe(true);
1482 assert!(!cmd.has_handlers_value());
1483 assert!(cmd_scoped.is_enabled_value());
1484
1485 handle_scoped.set_enabled(false);
1486 assert!(!cmd.has_handlers_value());
1487 assert!(!cmd_scoped.is_enabled_value());
1488 assert!(cmd_scoped.has_handlers_value());
1489
1490 handle_scoped.set_enabled(true);
1491 assert!(!cmd.has_handlers_value());
1492 assert!(cmd_scoped.is_enabled_value());
1493
1494 drop(handle_scoped);
1495 assert!(!cmd.has_handlers_value());
1496 assert!(!cmd_scoped.has_handlers_value());
1497 }
1498
1499 #[test]
1500 fn has_handlers() {
1501 let _app = APP.minimal().run_headless(false);
1502
1503 assert!(!FOO_CMD.has_handlers_value());
1504
1505 let handle = FOO_CMD.subscribe(false);
1506 assert!(FOO_CMD.has_handlers_value());
1507
1508 drop(handle);
1509 assert!(!FOO_CMD.has_handlers_value());
1510 }
1511
1512 #[test]
1513 fn has_handlers_scoped() {
1514 let _app = APP.minimal().run_headless(false);
1515
1516 let cmd = FOO_CMD;
1517 let cmd_scoped = FOO_CMD.scoped(WindowId::named("has_handlers_scoped"));
1518
1519 assert!(!cmd.has_handlers_value());
1520 assert!(!cmd_scoped.has_handlers_value());
1521
1522 let handle = cmd_scoped.subscribe(false);
1523
1524 assert!(!cmd.has_handlers_value());
1525 assert!(cmd_scoped.has_handlers_value());
1526
1527 drop(handle);
1528
1529 assert!(!cmd.has_handlers_value());
1530 assert!(!cmd_scoped.has_handlers_value());
1531 }
1532
1533 }