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(std::env!("CARGO_PKG_NAME"), std::env!("CARGO_PKG_VERSION"), &__l10n_arg, cmd, stringify!($meta_ident), &cmd.$meta_ident());
204 )*}
205 }
206 $crate::event::app_local! {
207 static EVENT: $crate::event::EventData = $crate::event::EventData::new::<$crate::event::CommandArgs>();
208 static DATA: $crate::event::CommandData = $crate::event::CommandData::new(__meta_init__, stringify!($COMMAND));
209 }
210 $crate::event::Command::new(&EVENT, &DATA)
211 };
212 };
213 (
214 $(#[$attr:meta])*
215 $vis:vis static $COMMAND:ident { $($meta_ident:ident : $meta_init:expr),* $(,)? };
216 ) => {
217 $(#[$attr])*
218 $(#[doc = concat!("* `", stringify!($meta_ident), "`")])+
224 $vis static $COMMAND: $crate::event::Command = {
225 fn __meta_init__(cmd: $crate::event::Command) {
226 $crate::event::paste! {$(
227 cmd.[<init_ $meta_ident>]($meta_init);
228 )*}
229 }
230 $crate::event::app_local! {
231 static EVENT: $crate::event::EventData = $crate::event::EventData::new::<$crate::event::CommandArgs>();
232 static DATA: $crate::event::CommandData = $crate::event::CommandData::new(__meta_init__, stringify!($COMMAND));
233 }
234 $crate::event::Command::new(&EVENT, &DATA)
235 };
236 };
237 (
238 $(#[$attr:meta])*
239 $vis:vis static $COMMAND:ident;
240 ) => {
241 $(#[$attr])*
242 $vis static $COMMAND: $crate::event::Command = {
243 fn __meta_init__(_: $crate::event::Command) {
244 }
245 $crate::event::app_local! {
246 static EVENT: $crate::event::EventData = $crate::event::EventData::new::<$crate::event::CommandArgs>();
247 static DATA: $crate::event::CommandData = $crate::event::CommandData::new(__meta_init__, stringify!($COMMAND));
248 }
249 $crate::event::Command::new(&EVENT, &DATA)
250 };
251 };
252}
253
254#[doc(hidden)]
255pub fn init_meta_l10n(
256 pkg_name: &'static str,
257 pkg_version: &'static str,
258 l10n_arg: &dyn Any,
259 cmd: Command,
260 meta_name: &'static str,
261 meta_value: &dyn Any,
262) {
263 if let Some(txt) = meta_value.downcast_ref::<CommandMetaVar<Txt>>() {
264 let mut l10n_file = "";
265
266 if let Some(&enabled) = l10n_arg.downcast_ref::<bool>() {
267 if !enabled {
268 return;
269 }
270 } else if let Some(&file) = l10n_arg.downcast_ref::<&'static str>() {
271 l10n_file = file;
272 } else {
273 tracing::error!("unknown l10n value in {:?}", cmd.event());
274 return;
275 }
276
277 EVENTS_L10N.init_meta_l10n([pkg_name, pkg_version, l10n_file], cmd, meta_name, txt.clone());
278 }
279}
280
281#[derive(Clone, Copy)]
320pub struct Command {
321 event: Event<CommandArgs>,
322 local: &'static AppLocal<CommandData>,
323 scope: CommandScope,
324}
325struct CommandDbg {
326 static_name: &'static str,
327 scope: CommandScope,
328 state: Option<[usize; 2]>,
329}
330impl CommandDbg {
331 fn new(static_name: &'static str, scope: CommandScope) -> Self {
332 Self {
333 static_name,
334 scope,
335 state: None,
336 }
337 }
338}
339impl fmt::Debug for CommandDbg {
340 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
341 if f.alternate() {
342 let mut d = f.debug_struct("Command");
343 d.field("static_name", &self.static_name).field("scope", &self.scope);
344 if let Some([has, enabled]) = &self.state {
345 d.field("handle_count", has);
346 d.field("enabled_count", enabled);
347 }
348
349 d.finish_non_exhaustive()
350 } else {
351 write!(f, "{}", self.static_name)?;
352 match self.scope {
353 CommandScope::App => Ok(()),
354 CommandScope::Window(id) => write!(f, "({id})"),
355 CommandScope::Widget(id) => write!(f, "({id})"),
356 }
357 }
358 }
359}
360impl fmt::Debug for Command {
361 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
362 let dbg = if let Some(d) = self.local.try_read() {
363 let mut dbg = CommandDbg::new(d.static_name, self.scope);
364 dbg.state = Some([d.handle_count, d.enabled_count]);
365 dbg
366 } else {
367 CommandDbg::new("<locked>", self.scope)
368 };
369 fmt::Debug::fmt(&dbg, f)
370 }
371}
372impl Command {
373 #[doc(hidden)]
374 pub const fn new(event_local: &'static AppLocal<EventData>, command_local: &'static AppLocal<CommandData>) -> Self {
375 Command {
376 event: Event::new(event_local),
377 local: command_local,
378 scope: CommandScope::App,
379 }
380 }
381
382 pub fn subscribe(&self, enabled: bool) -> CommandHandle {
389 self.local.write().subscribe(*self, enabled, None)
390 }
391
392 pub fn subscribe_wgt(&self, enabled: bool, target: WidgetId) -> CommandHandle {
400 self.local.write().subscribe(*self, enabled, Some(target))
401 }
402
403 pub fn event(&self) -> Event<CommandArgs> {
405 self.event
406 }
407
408 pub fn scope(&self) -> CommandScope {
410 self.scope
411 }
412
413 pub fn scoped(mut self, scope: impl Into<CommandScope>) -> Command {
415 self.scope = scope.into();
416 self
417 }
418
419 pub fn with_meta<R>(&self, visit: impl FnOnce(&mut CommandMeta) -> R) -> R {
428 fn init_meta(self_: &Command) -> parking_lot::MappedRwLockReadGuard<'static, CommandData> {
430 {
431 let mut write = self_.local.write();
432 match write.meta_init.clone() {
433 MetaInit::Init(init) => {
434 let lock = Arc::new((std::thread::current().id(), Mutex::new(())));
435 write.meta_init = MetaInit::Initing(lock.clone());
436 let _init_guard = lock.1.lock();
437 drop(write);
438 init(*self_);
439 self_.local.write().meta_init = MetaInit::Inited;
440 }
441 MetaInit::Initing(l) => {
442 drop(write);
443 if l.0 != std::thread::current().id() {
444 let _wait = l.1.lock();
445 }
446 }
447 MetaInit::Inited => {}
448 }
449 }
450
451 if !matches!(self_.scope, CommandScope::App) {
452 let mut write = self_.local.write();
453 write.scopes.entry(self_.scope).or_default();
454 }
455 self_.local.read()
456 }
457 let local_read = init_meta(self);
458 let mut meta_lock = local_read.meta.lock();
459
460 match self.scope {
461 CommandScope::App => visit(&mut CommandMeta {
462 meta: meta_lock.borrow_mut(),
463 scope: None,
464 }),
465 scope => {
466 let scope = local_read.scopes.get(&scope).unwrap();
467 visit(&mut CommandMeta {
468 meta: meta_lock.borrow_mut(),
469 scope: Some(scope.meta.lock().borrow_mut()),
470 })
471 }
472 }
473 }
474
475 pub fn has_handlers(&self) -> Var<bool> {
477 let mut write = self.local.write();
478 match self.scope {
479 CommandScope::App => write.has_handlers.read_only(),
480 scope => write.scopes.entry(scope).or_default().has_handlers.read_only(),
481 }
482 }
483
484 pub fn is_enabled(&self) -> Var<bool> {
486 let mut write = self.local.write();
487 match self.scope {
488 CommandScope::App => write.is_enabled.read_only(),
489 scope => write.scopes.entry(scope).or_default().is_enabled.read_only(),
490 }
491 }
492
493 pub fn visit_scopes<T>(&self, mut visitor: impl FnMut(Command) -> ControlFlow<T>) -> Option<T> {
497 let read = self.local.read();
498 for &scope in read.scopes.keys() {
499 match visitor(self.scoped(scope)) {
500 ControlFlow::Continue(_) => continue,
501 ControlFlow::Break(r) => return Some(r),
502 }
503 }
504 None
505 }
506
507 pub fn notify(&self) {
509 self.event.notify(CommandArgs::now(
510 None,
511 self.scope,
512 self.scope.search_target(),
513 self.is_enabled().get(),
514 ))
515 }
516
517 pub fn notify_descendants(&self, parent: &WidgetInfo) {
519 self.visit_scopes::<()>(|parse_cmd| {
520 if let CommandScope::Widget(id) = parse_cmd.scope()
521 && let Some(scope) = parent.tree().get(id)
522 && scope.is_descendant(parent)
523 {
524 parse_cmd.notify();
525 }
526 ControlFlow::Continue(())
527 });
528 }
529
530 pub fn notify_param(&self, param: impl Any + Send + Sync) {
532 self.event.notify(CommandArgs::now(
533 CommandParam::new(param),
534 self.scope,
535 self.scope.search_target(),
536 self.is_enabled().get(),
537 ));
538 }
539
540 pub fn notify_linked(&self, propagation: EventPropagationHandle, param: Option<CommandParam>) {
542 self.event.notify(CommandArgs::new(
543 crate::INSTANT.now(),
544 propagation,
545 param,
546 self.scope,
547 self.scope.search_target(),
548 self.is_enabled().get(),
549 ))
550 }
551
552 pub fn each_update(&self, direct_scope_only: bool, ignore_propagation: bool, mut handler: impl FnMut(&CommandArgs)) {
558 self.event.each_update(ignore_propagation, move |args| {
559 if args.scope_matches(direct_scope_only, self.scope) {
560 handler(args);
561 }
562 });
563 }
564
565 pub fn latest_update<O>(
569 &self,
570 direct_scope_only: bool,
571 ignore_propagation: bool,
572 handler: impl FnOnce(&CommandArgs) -> O,
573 ) -> Option<O> {
574 let mut r = None;
575 self.event.latest_update(ignore_propagation, |args| {
576 if args.scope_matches(direct_scope_only, self.scope) {
577 r = Some(handler(args));
578 }
579 });
580 r
581 }
582
583 pub fn has_update(&self, direct_scope_only: bool, ignore_propagation: bool) -> bool {
587 self.latest_update(direct_scope_only, ignore_propagation, |_| true).unwrap_or(false)
588 }
589
590 pub fn on_pre_event(
601 &self,
602 init_enabled: bool,
603 direct_scope_only: bool,
604 ignore_propagation: bool,
605 handler: Handler<CommandArgs>,
606 ) -> CommandHandle {
607 let (mut handle, handler) = self.event_handler(init_enabled, direct_scope_only, handler);
608 handle._handles.push(self.event().on_pre_event(ignore_propagation, handler));
609 handle
610 }
611
612 pub fn on_event(
623 &self,
624 init_enabled: bool,
625 direct_scope_only: bool,
626 ignore_propagation: bool,
627 handler: Handler<CommandArgs>,
628 ) -> CommandHandle {
629 let (mut handle, handler) = self.event_handler(init_enabled, direct_scope_only, handler);
630 handle._handles.push(self.event().on_event(ignore_propagation, handler));
631 handle
632 }
633
634 fn event_handler(
635 &self,
636 init_enabled: bool,
637 direct_scope_only: bool,
638 handler: Handler<CommandArgs>,
639 ) -> (CommandHandle, Handler<CommandArgs>) {
640 let handle = self.subscribe(init_enabled);
641 let local_enabled = handle.enabled().clone();
642 let handler = if direct_scope_only {
643 let scope = self.scope();
644 handler.filtered(move |a| a.scope == scope && local_enabled.get())
645 } else {
646 match self.scope() {
647 CommandScope::App => handler.filtered(move |_| local_enabled.get()),
648 CommandScope::Window(id) => {
649 handler.filtered(move |a| a.target.as_ref().map(|t| t.window_id() == id).unwrap_or(false) && local_enabled.get())
650 }
651 CommandScope::Widget(id) => {
652 handler.filtered(move |a| a.target.as_ref().map(|t| t.contains(id)).unwrap_or(false) && local_enabled.get())
653 }
654 }
655 };
656 (handle, handler)
657 }
658
659 pub fn on_pre_event_with_enabled(
664 &self,
665 init_enabled: bool,
666 direct_scope_only: bool,
667 ignore_propagation: bool,
668 handler: Handler<(CommandArgs, Var<bool>)>,
669 ) -> CommandHandle {
670 let (mut handle, handler) = self.event_handler_with_enabled(init_enabled, direct_scope_only, handler);
671 handle._handles.push(self.event().on_pre_event(ignore_propagation, handler));
672 handle
673 }
674
675 pub fn on_event_with_enabled(
680 &self,
681 init_enabled: bool,
682 direct_scope_only: bool,
683 ignore_propagation: bool,
684 handler: Handler<(CommandArgs, Var<bool>)>,
685 ) -> CommandHandle {
686 let (mut handle, handler) = self.event_handler_with_enabled(init_enabled, direct_scope_only, handler);
687 handle._handles.push(self.event().on_event(ignore_propagation, handler));
688 handle
689 }
690
691 fn event_handler_with_enabled(
692 &self,
693 init_enabled: bool,
694 direct_scope_only: bool,
695 mut handler: Handler<(CommandArgs, Var<bool>)>,
696 ) -> (CommandHandle, Handler<CommandArgs>) {
697 let handle = self.subscribe(init_enabled);
698 let local_enabled = handle.enabled().clone();
699
700 let r: Handler<CommandArgs>;
701 if direct_scope_only {
702 let scope = self.scope();
703 r = Box::new(move |a: &CommandArgs| {
704 if a.scope == scope {
705 handler(&(a.clone(), local_enabled.clone()))
706 } else {
707 HandlerResult::Done
708 }
709 });
710 } else {
711 match self.scope() {
712 CommandScope::App => r = Box::new(move |a: &CommandArgs| handler(&(a.clone(), local_enabled.clone()))),
713 CommandScope::Window(id) => {
714 r = Box::new(move |a: &CommandArgs| {
715 if a.target.as_ref().map(|t| t.window_id() == id).unwrap_or(false) {
716 handler(&(a.clone(), local_enabled.clone()))
717 } else {
718 HandlerResult::Done
719 }
720 })
721 }
722 CommandScope::Widget(id) => {
723 r = Box::new(move |a: &CommandArgs| {
724 if a.target.as_ref().map(|t| t.contains(id)).unwrap_or(false) {
725 handler(&(a.clone(), local_enabled.clone()))
726 } else {
727 HandlerResult::Done
728 }
729 })
730 }
731 }
732 };
733 (handle, r)
734 }
735
736 pub fn static_name(&self) -> &'static str {
738 self.local.read().static_name
739 }
740}
741impl ops::Deref for Command {
742 type Target = Event<CommandArgs>;
743
744 fn deref(&self) -> &Self::Target {
745 &self.event
746 }
747}
748impl PartialEq for Command {
749 fn eq(&self, other: &Self) -> bool {
750 self.event == other.event && self.scope == other.scope
751 }
752}
753impl Eq for Command {}
754impl std::hash::Hash for Command {
755 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
756 std::hash::Hash::hash(&self.event.as_any(), state);
757 std::hash::Hash::hash(&self.scope, state);
758 }
759}
760
761#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
765pub enum CommandScope {
766 App,
768 Window(WindowId),
773 Widget(WidgetId),
775}
776impl CommandScope {
777 pub fn search_target(self) -> Option<WidgetPath> {
779 match self {
780 CommandScope::App => None,
781 CommandScope::Window(id) => WINDOWS_APP.widget_tree(id).map(|t| t.root().path()),
782 CommandScope::Widget(id) => WINDOWS_APP.widget_info(id).map(|w| w.path()),
783 }
784 }
785}
786impl_from_and_into_var! {
787 fn from(id: WidgetId) -> CommandScope {
788 CommandScope::Widget(id)
789 }
790 fn from(id: WindowId) -> CommandScope {
791 CommandScope::Window(id)
792 }
793 fn from(widget_name: &'static str) -> CommandScope {
795 WidgetId::named(widget_name).into()
796 }
797 fn from(widget_name: Txt) -> CommandScope {
799 WidgetId::named(widget_name).into()
800 }
801}
802
803event_args! {
804 pub struct CommandArgs {
806 pub param: Option<CommandParam>,
808
809 pub scope: CommandScope,
811
812 pub target: Option<WidgetPath>,
818
819 pub enabled: bool,
827
828 ..
829
830 fn is_in_target(&self, id: WidgetId) -> bool {
832 match self.scope {
833 CommandScope::App => true,
834 _ => match &self.target {
835 Some(t) => t.contains(id),
836 None => false,
837 },
838 }
839 }
840
841 fn validate(&self) -> Result<(), Txt> {
843 if let Some(t) = &self.target {
844 match self.scope {
845 CommandScope::App => return Err("args for app scope cannot have a `target`".into()),
846 CommandScope::Window(id) => {
847 if id != t.window_id() || t.widgets_path().len() > 1 {
848 return Err("args for window scope must only `target` that window root widget".into());
849 }
850 }
851 CommandScope::Widget(id) => {
852 if id != t.widget_id() {
853 return Err("args for widget scope must only `target` that widget".into());
854 }
855 }
856 }
857 }
858 Ok(())
859 }
860 }
861}
862impl CommandArgs {
863 pub fn param<T: Any>(&self) -> Option<&T> {
865 self.param.as_ref().and_then(|p| p.downcast_ref::<T>())
866 }
867
868 pub fn enabled_param<T: Any>(&self) -> Option<&T> {
873 if self.enabled { self.param::<T>() } else { None }
874 }
875
876 pub fn disabled_param<T: Any>(&self) -> Option<&T> {
881 if !self.enabled { self.param::<T>() } else { None }
882 }
883
884 pub fn scope_matches(&self, direct_only: bool, scope: CommandScope) -> bool {
888 if direct_only {
889 self.scope == scope
890 } else {
891 match (scope, self.scope) {
892 (CommandScope::App, _) => true,
893 (CommandScope::Window(scope_id), CommandScope::Window(args_id)) => scope_id == args_id,
894 (CommandScope::Window(scope_id), CommandScope::Widget(args_id)) => {
895 if let Some(t) = &self.target {
897 t.window_id() == scope_id && t.contains(args_id)
898 } else if let Some(info) = WINDOWS_APP.widget_tree(scope_id) {
899 info.contains(args_id)
900 } else {
901 false
902 }
903 }
904 (CommandScope::Widget(scope_id), CommandScope::Widget(args_id)) => {
905 if let Some(t) = &self.target {
907 t.widgets_path().iter().position(|i| *i == scope_id).unwrap_or(usize::MAX)
908 < t.widgets_path().iter().position(|i| *i == args_id).unwrap_or(usize::MAX)
909 } else {
910 todo!()
911 }
912 }
913 _ => false,
914 }
915 }
916 }
917}
918
919pub struct CommandHandle {
926 command: Option<Command>,
927 local_enabled: Var<bool>,
928 _handles: VarHandles,
930}
931impl Clone for CommandHandle {
935 fn clone(&self) -> Self {
936 match self.command {
937 Some(c) => c.subscribe(self.local_enabled.get()),
938 None => Self::dummy(),
939 }
940 }
941}
942impl CommandHandle {
943 pub fn command(&self) -> Option<Command> {
945 self.command
946 }
947
948 pub fn enabled(&self) -> &Var<bool> {
952 &self.local_enabled
953 }
954
955 pub fn dummy() -> Self {
957 CommandHandle {
958 command: None,
959 local_enabled: const_var(false),
960 _handles: VarHandles::dummy(),
961 }
962 }
963
964 fn new(cmd: Command, event_handle: VarHandle, enabled: bool) -> Self {
965 let mut r = Self {
966 command: Some(cmd),
967 local_enabled: var(enabled),
968 _handles: VarHandles::dummy(),
969 };
970
971 let mut last_applied = enabled;
973 r._handles.push(r.local_enabled.hook(move |args| {
974 let _hold = &event_handle;
975 let enabled = *args.value();
976 if last_applied != enabled {
977 Self::update_enabled(cmd, enabled);
978 last_applied = enabled;
979 }
980 true
981 }));
982
983 r
984 }
985
986 fn update_enabled(command: Command, enabled: bool) {
987 let mut write = command.local.write();
988 match command.scope {
989 CommandScope::App => {
990 if enabled {
991 write.enabled_count += 1;
992 if write.enabled_count == 1 {
993 write.is_enabled.set(true);
994 }
995 tracing::trace!(
996 "command handle {:?} enabled, count: {:?}",
997 CommandDbg::new(write.static_name, command.scope),
998 write.enabled_count
999 );
1000 } else {
1001 write.enabled_count = match write.enabled_count.checked_sub(1) {
1002 Some(c) => c,
1003 None => {
1004 #[cfg(debug_assertions)]
1005 panic!("handle for {} was disabled when enabled_count was already zero", write.static_name);
1006 #[cfg(not(debug_assertions))]
1007 0
1008 }
1009 };
1010 if write.enabled_count == 0 {
1011 write.is_enabled.set(false);
1012 }
1013 tracing::trace!(
1014 "command handle {:?} disabled, count: {:?}",
1015 CommandDbg::new(write.static_name, command.scope),
1016 write.enabled_count
1017 );
1018 }
1019 }
1020 scope => {
1021 let write = &mut *write;
1022 if let Some(data) = write.scopes.get_mut(&scope) {
1023 if enabled {
1024 data.enabled_count += 1;
1025 if data.enabled_count == 1 {
1026 data.is_enabled.set(true);
1027 }
1028 tracing::trace!(
1029 "command handle {:?} enabled, count: {:?}",
1030 CommandDbg::new(write.static_name, command.scope),
1031 data.enabled_count
1032 );
1033 } else {
1034 data.enabled_count = match data.enabled_count.checked_sub(1) {
1035 Some(c) => c,
1036 None => {
1037 #[cfg(debug_assertions)]
1038 panic!(
1039 "handle for {:?} was disabled when enabled_count was already zero",
1040 CommandDbg::new(write.static_name, scope)
1041 );
1042 #[cfg(not(debug_assertions))]
1043 0
1044 }
1045 };
1046 if data.enabled_count == 0 {
1047 data.is_enabled.set(false);
1048 }
1049 tracing::trace!(
1050 "command handle {:?} enabled, count: {:?}",
1051 CommandDbg::new(write.static_name, command.scope),
1052 data.enabled_count
1053 );
1054 }
1055 }
1056 }
1057 }
1058 }
1059
1060 pub fn is_dummy(&self) -> bool {
1062 self.command.is_none()
1063 }
1064
1065 pub fn perm(mut self) {
1070 mem::replace(&mut self._handles, VarHandles::dummy()).perm();
1072 self.command = None;
1074 }
1075}
1076impl fmt::Debug for CommandHandle {
1077 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1078 f.debug_struct("CommandHandle")
1079 .field("command", &self.command)
1080 .field("enabled", &self.local_enabled.get())
1081 .finish_non_exhaustive()
1082 }
1083}
1084impl Drop for CommandHandle {
1085 fn drop(&mut self) {
1086 if let Some(command) = self.command.take()
1087 && APP.is_started()
1088 {
1089 let mut write = command.local.write();
1090 match command.scope {
1091 CommandScope::App => {
1092 write.handle_count = match write.handle_count.checked_sub(1) {
1093 Some(c) => c,
1094 None => {
1095 #[cfg(debug_assertions)]
1096 panic!("handle for {} was dropped when handle_count was already zero", write.static_name);
1097 #[cfg(not(debug_assertions))]
1098 0
1099 }
1100 };
1101 if write.handle_count == 0 {
1102 write.has_handlers.set(false);
1103 }
1104
1105 if self.local_enabled.get() {
1106 write.enabled_count = match write.enabled_count.checked_sub(1) {
1107 Some(c) => c,
1108 None => {
1109 #[cfg(debug_assertions)]
1110 panic!(
1111 "handle for enabled {} was dropped when enabled_count was already zero",
1112 write.static_name
1113 );
1114 #[cfg(not(debug_assertions))]
1115 0
1116 }
1117 };
1118
1119 if write.enabled_count == 0 {
1120 write.is_enabled.set(false);
1121 }
1122 }
1123
1124 tracing::trace!(
1125 "unsubscribe to {:?}, handle_count: {:?}, enabled_count: {:?}",
1126 CommandDbg::new(write.static_name, command.scope),
1127 write.handle_count,
1128 write.enabled_count
1129 );
1130 }
1131 scope => {
1132 let write = &mut *write;
1133 if let hash_map::Entry::Occupied(mut entry) = write.scopes.entry(scope) {
1134 let data = entry.get_mut();
1135
1136 data.handle_count = match data.handle_count.checked_sub(1) {
1137 Some(c) => c,
1138 None => {
1139 #[cfg(debug_assertions)]
1140 panic!(
1141 "handle for {:?} was dropped when handle_count was already zero",
1142 CommandDbg::new(write.static_name, scope)
1143 );
1144 #[cfg(not(debug_assertions))]
1145 0
1146 }
1147 };
1148
1149 if self.local_enabled.get() {
1150 data.enabled_count = match data.enabled_count.checked_sub(1) {
1151 Some(c) => c,
1152 None => {
1153 #[cfg(debug_assertions)]
1154 panic!(
1155 "handle for enabled {:?} was dropped when enabled_count was already zero",
1156 CommandDbg::new(write.static_name, scope)
1157 );
1158 #[cfg(not(debug_assertions))]
1159 0
1160 }
1161 };
1162
1163 if data.enabled_count == 0 {
1164 data.is_enabled.set(false);
1165 }
1166 }
1167
1168 tracing::trace!(
1169 "unsubscribe to {:?}, handle_count: {:?}, enabled_count: {:?}",
1170 CommandDbg::new(write.static_name, command.scope),
1171 data.handle_count,
1172 data.enabled_count
1173 );
1174
1175 if data.handle_count == 0 {
1176 data.has_handlers.set(false);
1177 entry.remove();
1178 EVENTS.unregister_command(command);
1179 }
1180 }
1181 }
1182 }
1183 }
1184 }
1185}
1186impl Default for CommandHandle {
1187 fn default() -> Self {
1188 Self::dummy()
1189 }
1190}
1191
1192#[derive(Clone)]
1194#[non_exhaustive]
1195pub struct CommandParam(pub Arc<dyn Any + Send + Sync>);
1196impl PartialEq for CommandParam {
1197 fn eq(&self, other: &Self) -> bool {
1198 Arc::ptr_eq(&self.0, &other.0)
1199 }
1200}
1201impl Eq for CommandParam {}
1202impl CommandParam {
1203 pub fn new(param: impl Any + Send + Sync + 'static) -> Self {
1207 let p: &dyn Any = ¶m;
1208 if let Some(p) = p.downcast_ref::<Self>() {
1209 p.clone()
1210 } else if let Some(p) = p.downcast_ref::<Arc<dyn Any + Send + Sync>>() {
1211 CommandParam(p.clone())
1212 } else {
1213 CommandParam(Arc::new(param))
1214 }
1215 }
1216
1217 pub fn type_id(&self) -> TypeId {
1219 self.0.type_id()
1220 }
1221
1222 pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
1224 self.0.downcast_ref()
1225 }
1226
1227 pub fn is<T: Any>(&self) -> bool {
1229 self.0.is::<T>()
1230 }
1231}
1232impl fmt::Debug for CommandParam {
1233 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1234 f.debug_tuple("CommandParam").field(&self.0.type_id()).finish()
1235 }
1236}
1237zng_var::impl_from_and_into_var! {
1238 fn from(param: CommandParam) -> Option<CommandParam>;
1239}
1240
1241#[rustfmt::skip] unique_id_64! {
1243 pub struct CommandMetaVarId<T: (StateValue + VarValue)>: StateId;
1249}
1250zng_unique_id::impl_unique_id_bytemuck!(CommandMetaVarId<T: (StateValue + VarValue)>);
1251impl<T: StateValue + VarValue> CommandMetaVarId<T> {
1252 fn app(self) -> StateId<Var<T>> {
1253 let id = self.get();
1254 StateId::from_raw(id)
1255 }
1256
1257 fn scope(self) -> StateId<Var<T>> {
1258 let id = self.get();
1259 StateId::from_raw(id)
1260 }
1261}
1262
1263impl<T: StateValue + VarValue> fmt::Debug for CommandMetaVarId<T> {
1264 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1265 #[cfg(debug_assertions)]
1266 let t = pretty_type_name::pretty_type_name::<T>();
1267 #[cfg(not(debug_assertions))]
1268 let t = "$T";
1269
1270 if f.alternate() {
1271 writeln!(f, "CommandMetaVarId<{t} {{")?;
1272 writeln!(f, " id: {},", self.get())?;
1273 writeln!(f, " sequential: {}", self.sequential())?;
1274 writeln!(f, "}}")
1275 } else {
1276 write!(f, "CommandMetaVarId<{t}>({})", self.sequential())
1277 }
1278 }
1279}
1280
1281pub struct CommandMeta<'a> {
1346 meta: StateMapMut<'a, CommandMetaState>,
1347 scope: Option<StateMapMut<'a, CommandMetaState>>,
1348}
1349impl CommandMeta<'_> {
1350 pub fn get_or_insert<T, F>(&mut self, id: impl Into<StateId<T>>, init: F) -> T
1356 where
1357 T: StateValue + Clone,
1358 F: FnOnce() -> T,
1359 {
1360 let id = id.into();
1361 if let Some(scope) = &mut self.scope {
1362 if let Some(value) = scope.get(id) {
1363 value.clone()
1364 } else if let Some(value) = self.meta.get(id) {
1365 value.clone()
1366 } else {
1367 let value = init();
1368 let r = value.clone();
1369 scope.set(id, value);
1370 r
1371 }
1372 } else {
1373 self.meta.entry(id).or_insert_with(init).clone()
1374 }
1375 }
1376
1377 pub fn get_or_default<T>(&mut self, id: impl Into<StateId<T>>) -> T
1383 where
1384 T: StateValue + Clone + Default,
1385 {
1386 self.get_or_insert(id, Default::default)
1387 }
1388
1389 pub fn get<T>(&self, id: impl Into<StateId<T>>) -> Option<T>
1393 where
1394 T: StateValue + Clone,
1395 {
1396 let id = id.into();
1397 if let Some(scope) = &self.scope {
1398 scope.get(id).or_else(|| self.meta.get(id))
1399 } else {
1400 self.meta.get(id)
1401 }
1402 .cloned()
1403 }
1404
1405 pub fn set<T>(&mut self, id: impl Into<StateId<T>>, value: impl Into<T>)
1409 where
1410 T: StateValue + Clone,
1411 {
1412 if let Some(scope) = &mut self.scope {
1413 scope.set(id, value);
1414 } else {
1415 self.meta.set(id, value);
1416 }
1417 }
1418
1419 pub fn init<T>(&mut self, id: impl Into<StateId<T>>, value: impl Into<T>)
1423 where
1424 T: StateValue + Clone,
1425 {
1426 self.meta.entry(id).or_insert(value);
1427 }
1428
1429 pub fn get_var_or_insert<T, F>(&mut self, id: impl Into<CommandMetaVarId<T>>, init: F) -> CommandMetaVar<T>
1435 where
1436 T: StateValue + VarValue,
1437 F: FnOnce() -> T,
1438 {
1439 let id = id.into();
1440 if let Some(scope) = &mut self.scope {
1441 let meta = &mut self.meta;
1442 scope
1443 .entry(id.scope())
1444 .or_insert_with(|| {
1445 let var = meta.entry(id.app()).or_insert_with(|| var(init())).clone();
1446 var.cow()
1447 })
1448 .clone()
1449 } else {
1450 self.meta.entry(id.app()).or_insert_with(|| var(init())).clone()
1451 }
1452 }
1453
1454 pub fn get_var<T>(&self, id: impl Into<CommandMetaVarId<T>>) -> Option<CommandMetaVar<T>>
1456 where
1457 T: StateValue + VarValue,
1458 {
1459 let id = id.into();
1460 if let Some(scope) = &self.scope {
1461 let meta = &self.meta;
1462 scope.get(id.scope()).cloned().or_else(|| meta.get(id.app()).cloned())
1463 } else {
1464 self.meta.get(id.app()).cloned()
1465 }
1466 }
1467
1468 pub fn get_var_or_default<T>(&mut self, id: impl Into<CommandMetaVarId<T>>) -> CommandMetaVar<T>
1472 where
1473 T: StateValue + VarValue + Default,
1474 {
1475 self.get_var_or_insert(id, Default::default)
1476 }
1477
1478 pub fn init_var<T>(&mut self, id: impl Into<CommandMetaVarId<T>>, value: impl Into<T>)
1482 where
1483 T: StateValue + VarValue,
1484 {
1485 self.meta.entry(id.into().app()).or_insert_with(|| var(value.into()));
1486 }
1487}
1488
1489pub type CommandMetaVar<T> = Var<T>;
1496
1497pub type ReadOnlyCommandMetaVar<T> = Var<T>;
1503
1504pub trait CommandNameExt {
1506 fn name(self) -> CommandMetaVar<Txt>;
1508
1509 fn init_name(self, name: impl Into<Txt>) -> Self;
1511
1512 fn name_with_shortcut(self) -> Var<Txt>
1518 where
1519 Self: crate::shortcut::CommandShortcutExt;
1520}
1521static_id! {
1522 static ref COMMAND_NAME_ID: CommandMetaVarId<Txt>;
1523}
1524impl CommandNameExt for Command {
1525 fn name(self) -> CommandMetaVar<Txt> {
1526 self.with_meta(|m| {
1527 m.get_var_or_insert(*COMMAND_NAME_ID, || {
1528 let name = self.static_name();
1529 let name = name.strip_suffix("_CMD").unwrap_or(name);
1530 let mut title = String::with_capacity(name.len());
1531 let mut lower = false;
1532 for c in name.chars() {
1533 if c == '_' {
1534 if !title.ends_with(' ') {
1535 title.push(' ');
1536 }
1537 lower = false;
1538 } else if lower {
1539 for l in c.to_lowercase() {
1540 title.push(l);
1541 }
1542 } else {
1543 title.push(c);
1544 lower = true;
1545 }
1546 }
1547 Txt::from(title)
1548 })
1549 })
1550 }
1551
1552 fn init_name(self, name: impl Into<Txt>) -> Self {
1553 self.with_meta(|m| m.init_var(*COMMAND_NAME_ID, name.into()));
1554 self
1555 }
1556
1557 fn name_with_shortcut(self) -> Var<Txt>
1558 where
1559 Self: crate::shortcut::CommandShortcutExt,
1560 {
1561 crate::var::merge_var!(self.name(), self.shortcut(), |name, shortcut| {
1562 if shortcut.is_empty() {
1563 name.clone()
1564 } else {
1565 zng_txt::formatx!("{name} ({})", shortcut[0])
1566 }
1567 })
1568 }
1569}
1570
1571impl Command {
1573 #[doc(hidden)]
1574 pub fn init_init(self, init: impl FnOnce(Self)) {
1575 init(self)
1576 }
1577 #[doc(hidden)]
1578 pub fn init(self) {}
1579}
1580
1581pub trait CommandInfoExt {
1583 fn info(self) -> CommandMetaVar<Txt>;
1585
1586 fn init_info(self, info: impl Into<Txt>) -> Self;
1588}
1589static_id! {
1590 static ref COMMAND_INFO_ID: CommandMetaVarId<Txt>;
1591}
1592impl CommandInfoExt for Command {
1593 fn info(self) -> CommandMetaVar<Txt> {
1594 self.with_meta(|m| m.get_var_or_insert(*COMMAND_INFO_ID, Txt::default))
1595 }
1596
1597 fn init_info(self, info: impl Into<Txt>) -> Self {
1598 self.with_meta(|m| m.init_var(*COMMAND_INFO_ID, info.into()));
1599 self
1600 }
1601}
1602
1603enum CommandMetaState {}
1604
1605#[derive(Clone)]
1606enum MetaInit {
1607 Init(fn(Command)),
1608 Initing(Arc<(ThreadId, Mutex<()>)>),
1610 Inited,
1611}
1612
1613#[doc(hidden)]
1614pub struct CommandData {
1615 static_name: &'static str,
1616
1617 meta_init: MetaInit,
1618 meta: Mutex<OwnedStateMap<CommandMetaState>>,
1619
1620 handle_count: usize,
1621 enabled_count: usize,
1622 registered: bool,
1623
1624 has_handlers: Var<bool>,
1625 is_enabled: Var<bool>,
1626
1627 scopes: HashMap<CommandScope, ScopedValue>,
1628}
1629impl CommandData {
1630 pub fn new(meta_init: fn(Command), static_name: &'static str) -> Self {
1631 CommandData {
1632 static_name,
1633 meta_init: MetaInit::Init(meta_init),
1634 meta: Mutex::new(OwnedStateMap::new()),
1635
1636 handle_count: 0,
1637 enabled_count: 0,
1638 registered: false,
1639
1640 has_handlers: var(false),
1641 is_enabled: var(false),
1642
1643 scopes: HashMap::default(),
1644 }
1645 }
1646
1647 fn subscribe(&mut self, command: Command, enabled: bool, mut target: Option<WidgetId>) -> CommandHandle {
1648 match command.scope {
1649 CommandScope::App => {
1650 if !mem::replace(&mut self.registered, true) {
1651 EVENTS.register_command(command);
1652 }
1653
1654 self.handle_count += 1;
1655 if enabled {
1656 self.enabled_count += 1;
1657 }
1658
1659 if self.handle_count == 1 {
1660 self.has_handlers.set(true);
1661 }
1662 if self.enabled_count == 1 {
1663 self.is_enabled.set(true);
1664 }
1665
1666 tracing::trace!(
1667 "subscribe to {:?}, handle_count: {:?}, enabled_count: {:?}",
1668 CommandDbg::new(self.static_name, command.scope),
1669 self.handle_count,
1670 self.enabled_count
1671 );
1672 }
1673 scope => {
1674 let data = self.scopes.entry(scope).or_default();
1675
1676 if !mem::replace(&mut data.registered, true) {
1677 EVENTS.register_command(command);
1678 }
1679
1680 data.handle_count += 1;
1681 if enabled {
1682 data.enabled_count += 1;
1683 }
1684
1685 if data.handle_count == 1 {
1686 data.has_handlers.set(true);
1687 }
1688 if data.enabled_count == 1 {
1689 data.is_enabled.set(true);
1690 }
1691
1692 tracing::trace!(
1693 "subscribe to {:?}, handle_count: {:?}, enabled_count: {:?}",
1694 CommandDbg::new(self.static_name, command.scope),
1695 self.handle_count,
1696 self.enabled_count
1697 );
1698
1699 if let CommandScope::Widget(id) = scope {
1700 target = Some(id);
1701 }
1702 }
1703 };
1704
1705 CommandHandle::new(
1706 command,
1707 target
1708 .map(|t| command.event.subscribe(UpdateOp::Update, t))
1709 .unwrap_or_else(VarHandle::dummy),
1710 enabled,
1711 )
1712 }
1713}
1714
1715struct ScopedValue {
1716 handle_count: usize,
1717 enabled_count: usize,
1718 is_enabled: Var<bool>,
1719 has_handlers: Var<bool>,
1720 meta: Mutex<OwnedStateMap<CommandMetaState>>,
1721 registered: bool,
1722}
1723impl Default for ScopedValue {
1724 fn default() -> Self {
1725 ScopedValue {
1726 is_enabled: var(false),
1727 has_handlers: var(false),
1728 handle_count: 0,
1729 enabled_count: 0,
1730 meta: Mutex::new(OwnedStateMap::default()),
1731 registered: false,
1732 }
1733 }
1734}
1735
1736#[cfg(test)]
1737mod tests {
1738 use crate::APP;
1739
1740 use super::*;
1741
1742 command! {
1743 static FOO_CMD;
1744 }
1745
1746 #[test]
1747 fn parameter_none() {
1748 let _ = CommandArgs::now(None, CommandScope::App, None, true);
1749 }
1750
1751 #[test]
1752 fn enabled_not_scoped() {
1753 let mut app = APP.minimal().run_headless(false);
1754
1755 assert!(!FOO_CMD.has_handlers().get());
1756
1757 let handle = FOO_CMD.subscribe(true);
1758 app.update(false).assert_wait();
1759 assert!(FOO_CMD.is_enabled().get());
1760
1761 handle.enabled().set(false);
1762 app.update(false).assert_wait();
1763
1764 assert!(FOO_CMD.has_handlers().get());
1765 assert!(!FOO_CMD.is_enabled().get());
1766
1767 handle.enabled().set(true);
1768 app.update(false).assert_wait();
1769
1770 assert!(FOO_CMD.is_enabled().get());
1771
1772 drop(handle);
1773 app.update(false).assert_wait();
1774
1775 assert!(!FOO_CMD.has_handlers().get());
1776 assert!(!FOO_CMD.is_enabled().get());
1777 }
1778
1779 #[test]
1780 fn enabled_scoped() {
1781 let mut app = APP.minimal().run_headless(false);
1782
1783 let cmd = FOO_CMD;
1784 let cmd_scoped = FOO_CMD.scoped(WindowId::named("enabled_scoped"));
1785 app.update(false).assert_wait();
1786 assert!(!cmd.has_handlers().get());
1787 assert!(!cmd_scoped.has_handlers().get());
1788
1789 let handle_scoped = cmd_scoped.subscribe(true);
1790 app.update(false).assert_wait();
1791
1792 assert!(!cmd.has_handlers().get());
1793 assert!(cmd_scoped.is_enabled().get());
1794
1795 handle_scoped.enabled().set(false);
1796 app.update(false).assert_wait();
1797
1798 assert!(!cmd.has_handlers().get());
1799 assert!(!cmd_scoped.is_enabled().get());
1800 assert!(cmd_scoped.has_handlers().get());
1801
1802 handle_scoped.enabled().set(true);
1803 app.update(false).assert_wait();
1804
1805 assert!(!cmd.has_handlers().get());
1806 assert!(cmd_scoped.is_enabled().get());
1807
1808 drop(handle_scoped);
1809 app.update(false).assert_wait();
1810
1811 assert!(!cmd.has_handlers().get());
1812 assert!(!cmd_scoped.has_handlers().get());
1813 }
1814
1815 #[test]
1816 fn has_handlers() {
1817 let mut app = APP.minimal().run_headless(false);
1818
1819 assert!(!FOO_CMD.has_handlers().get());
1820
1821 let handle = FOO_CMD.subscribe(false);
1822 app.update(false).assert_wait();
1823
1824 assert!(FOO_CMD.has_handlers().get());
1825
1826 drop(handle);
1827 app.update(false).assert_wait();
1828
1829 assert!(!FOO_CMD.has_handlers().get());
1830 }
1831
1832 #[test]
1833 fn has_handlers_scoped() {
1834 let mut app = APP.minimal().run_headless(false);
1835
1836 let cmd = FOO_CMD;
1837 let cmd_scoped = FOO_CMD.scoped(WindowId::named("has_handlers_scoped"));
1838 app.update(false).assert_wait();
1839
1840 assert!(!cmd.has_handlers().get());
1841 assert!(!cmd_scoped.has_handlers().get());
1842
1843 let handle = cmd_scoped.subscribe(false);
1844 app.update(false).assert_wait();
1845
1846 assert!(!cmd.has_handlers().get());
1847 assert!(cmd_scoped.has_handlers().get());
1848
1849 drop(handle);
1850 app.update(false).assert_wait();
1851
1852 assert!(!cmd.has_handlers().get());
1853 assert!(!cmd_scoped.has_handlers().get());
1854 }
1855
1856 }