diff --git a/src/Lanceur.Core/MacroAttribute.cs b/src/Lanceur.Core/MacroAttribute.cs index 06cc6bcf..fbf252ac 100644 --- a/src/Lanceur.Core/MacroAttribute.cs +++ b/src/Lanceur.Core/MacroAttribute.cs @@ -11,8 +11,9 @@ public class MacroAttribute : Attribute #region Constructors - public MacroAttribute(string name) + public MacroAttribute(string name, bool isVisible = true) { + IsVisible = isVisible; _name = name.Trim().Replace("@", "").ToUpper(); } @@ -20,6 +21,12 @@ public MacroAttribute(string name) #region Properties + /// + /// Indicates whether this macro should appear in the list of macros + /// This is meant to create some macro for "privileged" people. That's + /// some debugging tools for the developer. + /// + public bool IsVisible { get; } public string Name => $"@{_name}@"; #endregion Properties diff --git a/src/Lanceur.Infra/Utils/MacroLister.cs b/src/Lanceur.Infra/Utils/MacroLister.cs new file mode 100644 index 00000000..9222e54a --- /dev/null +++ b/src/Lanceur.Infra/Utils/MacroLister.cs @@ -0,0 +1,48 @@ +using Lanceur.Core; +using System.Reflection; + +namespace Lanceur.Infra.Utils; + +public class MacroLister +{ + #region Fields + + private readonly object _source; + private MacroAttribute[] _macros; + + #endregion Fields + + #region Constructors + + public MacroLister(object source) + { + _source = source; + } + + #endregion Constructors + + #region Methods + + private MacroAttribute[] GetMacroAttributes() + { + return Assembly.GetAssembly(_source.GetType())!.GetTypes() + .SelectMany(t => t.GetCustomAttributes()) + .ToArray(); + } + + public IEnumerable GetAttributes() + { + _macros ??= GetMacroAttributes(); + return _macros.ToArray(); + } + + public IEnumerable GetVisibleAttributes() + { + _macros ??= GetMacroAttributes(); + + return _macros.Where(t => t.IsVisible) + .ToArray(); ; + } + + #endregion Methods +} \ No newline at end of file diff --git a/src/Lanceur/Macros/Development/DebugMacro.cs b/src/Lanceur/Macros/Development/DebugMacro.cs index 1e4a7a93..da2053d9 100644 --- a/src/Lanceur/Macros/Development/DebugMacro.cs +++ b/src/Lanceur/Macros/Development/DebugMacro.cs @@ -15,7 +15,7 @@ namespace Lanceur.Macros.Development { - [Macro("debug"), Description("Provides some debugging tools. But it is more an easter egg than something else")] + [Macro("debug", isVisible: false), Description("Provides some debugging tools. But it is more an easter egg than something else")] public class DebugMacro : MacroQueryResult { #region Fields diff --git a/src/Lanceur/Macros/Development/FixIconMacro.cs b/src/Lanceur/Macros/Development/FixIconMacro.cs index 53f53d24..7edf50e8 100644 --- a/src/Lanceur/Macros/Development/FixIconMacro.cs +++ b/src/Lanceur/Macros/Development/FixIconMacro.cs @@ -9,7 +9,7 @@ namespace Lanceur.Macros.Development; -[Macro("fixicon"), Description("Fix icons when alias is a directory or an hyperlink")] +[Macro("fixicon", isVisible: false), Description("Fix icons when alias is a directory or an hyperlink")] public class FixIconMacro : MacroQueryResult { #region Properties diff --git a/src/Lanceur/Views/KeywordsView.xaml b/src/Lanceur/Views/KeywordsView.xaml index 6432d4e6..b6debb05 100644 --- a/src/Lanceur/Views/KeywordsView.xaml +++ b/src/Lanceur/Views/KeywordsView.xaml @@ -122,9 +122,12 @@ - vm.MacroCollection, v => v.BoxFileName.ItemsSource).DisposeWith(d); this.OneWayBind(ViewModel, vm => vm.Aliases, v => v.Aliases.ItemsSource).DisposeWith(d); this.OneWayBind(ViewModel, vm => vm.BusyMessage, v => v.BusyMessage.Text).DisposeWith(d); this.OneWayBind(ViewModel, vm => vm.IsBusy, v => v.BusyControl.Visibility).DisposeWith(d); diff --git a/src/Lanceur/Views/KeywordsViewModel.cs b/src/Lanceur/Views/KeywordsViewModel.cs index 1b74ab9d..4a40e5d1 100644 --- a/src/Lanceur/Views/KeywordsViewModel.cs +++ b/src/Lanceur/Views/KeywordsViewModel.cs @@ -17,6 +17,7 @@ using ReactiveUI.Validation.Helpers; using Splat; using System; +using System.Collections; using System.Collections.ObjectModel; using System.Linq; using System.Reactive; @@ -24,12 +25,14 @@ using System.Reactive.Disposables; using System.Reactive.Linq; using System.Threading.Tasks; -using System.Windows.Media.TextFormatting; using DynamicData; using Humanizer; using Lanceur.Core.BusinessLogic; using Lanceur.SharedKernel.Utils; using Lanceur.Utils; +using System.Reflection; +using Lanceur.Core; +using Lanceur.Infra.Utils; namespace Lanceur.Views { @@ -45,6 +48,7 @@ public class KeywordsViewModel : RoutableViewModel, IValidatableViewModel, IActi private readonly IPackagedAppSearchService _packagedAppSearchService; private readonly ISchedulerProvider _schedulers; private readonly IThumbnailManager _thumbnailManager; + private readonly MacroLister _macroLister; #endregion Fields @@ -72,6 +76,7 @@ public KeywordsViewModel( ConfirmRemove = Interactions.YesNoQuestion(_schedulers.MainThreadScheduler); AskLuaEditor = new(); + _macroLister = new MacroLister(this); this.WhenActivated(Activate); } @@ -88,6 +93,8 @@ public KeywordsViewModel( [Reactive] public AliasQueryResult AliasToCreate { get; set; } + [Reactive] public ObservableCollection MacroCollection { get; set; } + public Interaction AskLuaEditor { get; } [Reactive] public string BusyMessage { get; set; } @@ -309,8 +316,14 @@ private void SetupValidations(CompositeDisposable d) { var validateFileNameExists = this.WhenAnyValue( x => x.SelectedAlias.FileName, - x => !x.IsNullOrEmpty() - ); + x => + { + if (x.IsNullOrEmpty()) return false; + if (!x.StartsWith('@')) return true; + + return x.StartsWith('@') && _macroLister.GetAttributes().Any(a => a.Name == x.ToUpper()); + }); + var validateNameExists = this.WhenAnyValue( x => x.SelectedAlias.SynonymsToAdd, x => _aliasService.SelectNames(x.SplitCsv()) @@ -319,7 +332,7 @@ private void SetupValidations(CompositeDisposable d) ValidationFileName = this.ValidationRule( vm => vm.SelectedAlias.FileName, validateFileNameExists, - "The path to the file shouldn't be empty." + "Either the path to the file is empty or the macro does not exist." ); ValidationFileName.DisposeWith(d); @@ -357,6 +370,12 @@ internal void Activate(CompositeDisposable d) SetupValidations(d); SetupCommands(_schedulers.MainThreadScheduler, _notify, d); SetupBindings(_schedulers.MainThreadScheduler, d); + + //load macros + var macros = _macroLister.GetVisibleAttributes() + .Select(x => x.Name) + .ToArray(); + MacroCollection = new(macros); } public void HydrateSelectedAlias() => _aliasService.HydrateAlias(SelectedAlias);