using DynamicData; using ReactiveUI; using System; using System.Linq; using System.Reactive.Linq; using System.Windows.Input; namespace NodeNetwork.Toolkit.ContextMenu { /// /// A data type containing a command, parameter and display properties. /// public class LabeledCommand : ReactiveObject { #region Label /// /// The label that is displayed in the menu /// public string Label { get => _label; set => this.RaiseAndSetIfChanged(ref _label, value); } private string _label = ""; #endregion #region Visible /// /// Should the command be displayed in the menu? /// public bool Visible { get => _visible; set => this.RaiseAndSetIfChanged(ref _visible, value); } private bool _visible = true; #endregion #region Command /// /// The command to be executed. /// public ICommand Command { get => _command; set => this.RaiseAndSetIfChanged(ref _command, value); } private ICommand _command = null; #endregion #region CommandParameter /// /// The parameter to be passed to the command on execution. /// public object CommandParameter { get => _commandParameter; set => this.RaiseAndSetIfChanged(ref _commandParameter, value); } private object _commandParameter = null; #endregion } /// /// A viewmodel for a context menu in which the entries can be filtered by the user based on a searchquery. /// public class SearchableContextMenuViewModel : ReactiveObject { static SearchableContextMenuViewModel() { NNViewRegistrar.AddRegistration(() => new SearchableContextMenuView(), typeof(IViewFor)); } /// /// List of all the available commands in the menu. /// public ISourceList Commands { get; } = new SourceList(); /// /// List of commands that are actually visible in the menu. /// This list is based on Commands and SearchQuery. /// public IObservableList VisibleCommands { get; } #region SearchQuery /// /// The current search string that is used to filter Nodes into VisibleNodes. /// public string SearchQuery { get => _searchQuery; set => this.RaiseAndSetIfChanged(ref _searchQuery, value); } private string _searchQuery = ""; #endregion #region MaxItemsDisplayed /// /// Only the first MaxItemsDisplayed items from Commands that match the query are displayed. /// public int MaxItemsDisplayed { get => _maxItemsDisplayed; set => this.RaiseAndSetIfChanged(ref _maxItemsDisplayed, value); } private int _maxItemsDisplayed = int.MaxValue; #endregion public SearchableContextMenuViewModel() { var onQueryChanged = this.WhenAnyValue(vm => vm.SearchQuery, vm => vm.MaxItemsDisplayed) .Throttle(TimeSpan.FromMilliseconds(70), RxApp.MainThreadScheduler) .Publish(); onQueryChanged.Connect(); VisibleCommands = Commands.Connect() .AutoRefreshOnObservable(_ => onQueryChanged) .AutoRefresh(cmd => cmd.Label) .AutoRefresh(cmd => cmd.Visible) .Filter(cmd => cmd.Visible && (cmd.Label ?? "").ToUpper().Contains(SearchQuery?.ToUpper() ?? "")) .Top(MaxItemsDisplayed) .AsObservableList(); } } }