From db6fb33c9686fc2ab7d4a7af632c9082a5fdbfec Mon Sep 17 00:00:00 2001 From: Robert Date: Sun, 5 May 2024 16:27:08 +0200 Subject: [PATCH 1/2] Data model picker - Implemented search --- .../DataModelPicker/DataModelPicker.cs | 91 ++++++++++++++++--- .../Shared/DataModelEventViewModel.cs | 11 ++- .../Shared/DataModelListItemViewModel.cs | 7 ++ .../Shared/DataModelListViewModel.cs | 12 +++ .../Shared/DataModelPropertiesViewModel.cs | 14 ++- .../Shared/DataModelPropertyViewModel.cs | 12 +++ .../Shared/DataModelUpdateConfiguration.cs | 13 ++- .../Shared/DataModelVisualizationViewModel.cs | 9 +- .../Styles/Controls/DataModelPicker.axaml | 86 +++++++++++++----- .../Tabs/DataModel/DataModelDebugViewModel.cs | 2 +- 10 files changed, 215 insertions(+), 42 deletions(-) diff --git a/src/Artemis.UI.Shared/Controls/DataModelPicker/DataModelPicker.cs b/src/Artemis.UI.Shared/Controls/DataModelPicker/DataModelPicker.cs index e3168eaba..996dc842c 100644 --- a/src/Artemis.UI.Shared/Controls/DataModelPicker/DataModelPicker.cs +++ b/src/Artemis.UI.Shared/Controls/DataModelPicker/DataModelPicker.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; @@ -69,12 +70,19 @@ public class DataModelPicker : TemplatedControl public static readonly StyledProperty IsEventPickerProperty = AvaloniaProperty.Register(nameof(IsEventPicker)); + private readonly ObservableCollection _searchResults = []; private TextBlock? _currentPathDescription; private TextBlock? _currentPathDisplay; private MaterialIcon? _currentPathIcon; private TreeView? _dataModelTreeView; + private ListBox? _searchListBox; + private TextBox? _searchBox; + private Panel? _searchContainer; + private StackPanel? _searchEmpty; private DispatcherTimer? _updateTimer; + private string? _lastSearch; + private bool _firstUpdate; static DataModelPicker() { @@ -200,7 +208,14 @@ private void Update(object? sender, EventArgs e) if (DataModelUIService == null) return; - DataModelViewModel?.Update(DataModelUIService, new DataModelUpdateConfiguration(!IsEventPicker)); + DataModelViewModel?.Update(DataModelUIService, new DataModelUpdateConfiguration(!IsEventPicker, !string.IsNullOrEmpty(_searchBox?.Text))); + ApplySearch(); + + if (_firstUpdate) + { + _firstUpdate = false; + UpdateCurrentPath(true); + } } private void GetDataModel() @@ -229,14 +244,15 @@ private void DataModelOnUpdateRequested(object? sender, EventArgs e) if (DataModelUIService == null || DataModelViewModel == null) return; - DataModelViewModel.Update(DataModelUIService, new DataModelUpdateConfiguration(IsEventPicker)); + DataModelViewModel.Update(DataModelUIService, new DataModelUpdateConfiguration(!IsEventPicker, !string.IsNullOrEmpty(_searchBox?.Text))); DataModelViewModel.ApplyTypeFilter(true, FilterTypes?.ToArray() ?? Type.EmptyTypes); + ApplySearch(); } - private void DataModelTreeViewOnSelectionChanged(object? sender, SelectionChangedEventArgs e) + private void TreeViewOnSelectionChanged(object? sender, SelectionChangedEventArgs e) { - // Multi-select isn't a think so grab the first one - object? selected = _dataModelTreeView?.SelectedItems[0]; + // Multi-select isn't a thing so grab the first one + object? selected = e.AddedItems[0]; if (selected == null) return; @@ -255,12 +271,48 @@ private void DataModelTreeViewOnDoubleTapped(object? sender, RoutedEventArgs e) treeViewItem.IsExpanded = !treeViewItem.IsExpanded; } + private void ApplySearch() + { + if (DataModelViewModel == null || string.IsNullOrWhiteSpace(_searchBox?.Text)) + { + if (_dataModelTreeView != null) + _dataModelTreeView.IsVisible = true; + if (_searchContainer != null) + _searchContainer.IsVisible = false; + return; + } + + if (_dataModelTreeView != null) + _dataModelTreeView.IsVisible = false; + if (_searchContainer != null) + _searchContainer.IsVisible = true; + + if (_searchBox.Text != _lastSearch) + { + _searchResults.Clear(); + foreach (DataModelVisualizationViewModel searchResult in DataModelViewModel.GetSearchResults(_searchBox.Text).DistinctBy(r => r.Path).Take(20)) + _searchResults.Add(searchResult); + _lastSearch = _searchBox.Text; + } + + if (_searchListBox != null) + { + _searchListBox.IsVisible = _searchResults.Count > 0; + _searchListBox.SelectedItem = _searchResults.FirstOrDefault(r => r.DataModelPath?.Path == DataModelPath?.Path); + } + + if (_searchEmpty != null) + _searchEmpty.IsVisible = _searchResults.Count == 0; + + + } + private void UpdateCurrentPath(bool selectCurrentPath) { - if (DataModelPath == null) + if (DataModelPath == null || _dataModelTreeView == null) return; - if (_dataModelTreeView != null && selectCurrentPath) + if (selectCurrentPath) { // Expand the path DataModel? start = DataModelPath.Target; @@ -286,7 +338,7 @@ private void UpdateCurrentPath(bool selectCurrentPath) protected override void OnApplyTemplate(TemplateAppliedEventArgs e) { if (_dataModelTreeView != null) - _dataModelTreeView.SelectionChanged -= DataModelTreeViewOnSelectionChanged; + _dataModelTreeView.SelectionChanged -= TreeViewOnSelectionChanged; if (_dataModelTreeView != null) _dataModelTreeView.DoubleTapped -= DataModelTreeViewOnDoubleTapped; @@ -294,11 +346,22 @@ protected override void OnApplyTemplate(TemplateAppliedEventArgs e) _currentPathDisplay = e.NameScope.Find("CurrentPathDisplay"); _currentPathDescription = e.NameScope.Find("CurrentPathDescription"); _dataModelTreeView = e.NameScope.Find("DataModelTreeView"); + _searchBox = e.NameScope.Find("SearchBox"); + _searchContainer = e.NameScope.Find("SearchContainer"); + _searchListBox = e.NameScope.Find("SearchListBox"); + _searchEmpty = e.NameScope.Find("SearchEmpty"); if (_dataModelTreeView != null) - _dataModelTreeView.SelectionChanged += DataModelTreeViewOnSelectionChanged; - if (_dataModelTreeView != null) + { + _dataModelTreeView.SelectionChanged += TreeViewOnSelectionChanged; _dataModelTreeView.DoubleTapped += DataModelTreeViewOnDoubleTapped; + } + + if (_searchListBox != null) + { + _searchListBox.ItemsSource = _searchResults; + _searchListBox.SelectionChanged += TreeViewOnSelectionChanged; + } } #region Overrides of Visual @@ -307,17 +370,23 @@ protected override void OnApplyTemplate(TemplateAppliedEventArgs e) protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) { GetDataModel(); - UpdateCurrentPath(true); + _updateTimer = new DispatcherTimer(TimeSpan.FromMilliseconds(200), DispatcherPriority.Background, Update); _updateTimer.Start(); + _firstUpdate = true; } /// protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) { DataModelViewModel?.Dispose(); + _updateTimer?.Stop(); _updateTimer = null; + + _lastSearch = null; + if (_searchBox != null) + _searchBox.Text = null; } #endregion diff --git a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelEventViewModel.cs b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelEventViewModel.cs index b3c16c860..312d95a36 100644 --- a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelEventViewModel.cs +++ b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelEventViewModel.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using Artemis.Core; using Artemis.Core.Modules; @@ -41,13 +42,21 @@ public override void Update(IDataModelUIService dataModelUIService, DataModelUpd } // Only update children if the parent is expanded - if (Parent != null && !Parent.IsRootViewModel && !Parent.IsVisualizationExpanded) + if (Parent != null && !Parent.IsRootViewModel && !Parent.IsVisualizationExpanded && (configuration == null || !configuration.UpdateAllChildren)) return; foreach (DataModelVisualizationViewModel dataModelVisualizationViewModel in Children) dataModelVisualizationViewModel.Update(dataModelUIService, configuration); } + /// + public override IEnumerable GetSearchResults(string search) + { + if (PropertyDescription?.Name != null && PropertyDescription.Name.Contains(search, StringComparison.OrdinalIgnoreCase)) + return [this]; + return []; + } + /// /// Always returns for data model events /// diff --git a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListItemViewModel.cs b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListItemViewModel.cs index 0a553339c..fd7a7a74f 100644 --- a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListItemViewModel.cs +++ b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListItemViewModel.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using Artemis.UI.Shared.Services; using ReactiveUI; @@ -62,6 +63,12 @@ public object? DisplayValue internal set => this.RaiseAndSetIfChanged(ref _displayValue, value); } + /// + public override IEnumerable GetSearchResults(string search) + { + return []; + } + /// public override object? GetCurrentValue() { diff --git a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListViewModel.cs b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListViewModel.cs index 0d0ca66dd..b04d61d2b 100644 --- a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListViewModel.cs +++ b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListViewModel.cs @@ -1,5 +1,6 @@ using System; using System.Collections; +using System.Collections.Generic; using System.Collections.ObjectModel; using Artemis.Core; using Artemis.Core.Modules; @@ -119,6 +120,17 @@ public override void Update(IDataModelUIService dataModelUIService, DataModelUpd CountDisplay = $"{ListChildren.Count} {(ListChildren.Count == 1 ? "item" : "items")}"; } + /// + public override IEnumerable GetSearchResults(string search) + { + if (PropertyDescription?.Name != null && PropertyDescription.Name.Contains(search, StringComparison.OrdinalIgnoreCase)) + return [this]; + if (PropertyDescription?.Description != null && PropertyDescription.Description.Contains(search, StringComparison.OrdinalIgnoreCase)) + return [this]; + + return []; + } + /// public override string ToString() { diff --git a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelPropertiesViewModel.cs b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelPropertiesViewModel.cs index fe1d226c4..ca03c5c26 100644 --- a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelPropertiesViewModel.cs +++ b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelPropertiesViewModel.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; using Artemis.Core; using Artemis.Core.Modules; using Artemis.UI.Shared.Services; @@ -52,14 +54,20 @@ public override void Update(IDataModelUIService dataModelUIService, DataModelUpd // Always populate properties PopulateProperties(dataModelUIService, configuration); - // Only update children if the parent is expanded - if (Parent != null && !Parent.IsRootViewModel && !Parent.IsVisualizationExpanded) + // Only update children if the parent is expanded or when searching + if (Parent != null && !Parent.IsRootViewModel && !Parent.IsVisualizationExpanded && (configuration == null || !configuration.UpdateAllChildren)) return; - + foreach (DataModelVisualizationViewModel dataModelVisualizationViewModel in Children) dataModelVisualizationViewModel.Update(dataModelUIService, configuration); } + /// + public override IEnumerable GetSearchResults(string search) + { + return Children.SelectMany(c => c.GetSearchResults(search)); + } + /// public override object? GetCurrentValue() { diff --git a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelPropertyViewModel.cs b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelPropertyViewModel.cs index a68ee78a2..69e60578d 100644 --- a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelPropertyViewModel.cs +++ b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelPropertyViewModel.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using Artemis.Core; using Artemis.Core.Modules; using Artemis.UI.Shared.Services; @@ -71,6 +72,17 @@ public override void Update(IDataModelUIService dataModelUIService, DataModelUpd DisplayViewModel?.UpdateValue(DisplayValue); } + /// + public override IEnumerable GetSearchResults(string search) + { + if (PropertyDescription?.Name != null && PropertyDescription.Name.Contains(search, StringComparison.OrdinalIgnoreCase)) + return [this]; + if (DisplayValue != null && DisplayValue.ToString()?.Contains(search, StringComparison.OrdinalIgnoreCase) == true) + return [this]; + + return []; + } + /// public override string ToString() { diff --git a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelUpdateConfiguration.cs b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelUpdateConfiguration.cs index 1cf9dcce7..f4f30e082 100644 --- a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelUpdateConfiguration.cs +++ b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelUpdateConfiguration.cs @@ -8,14 +8,21 @@ public class DataModelUpdateConfiguration /// /// Creates a new instance of the class /// - /// A boolean indicating whether or not event children should be created - public DataModelUpdateConfiguration(bool createEventChildren) + /// A boolean indicating whether event children should be created + /// A boolean indicating whether all children should be updated + public DataModelUpdateConfiguration(bool createEventChildren, bool updateAllChildren) { CreateEventChildren = createEventChildren; + UpdateAllChildren = updateAllChildren; } /// - /// Gets a boolean indicating whether or not event children should be created + /// Gets a boolean indicating whether event children should be created /// public bool CreateEventChildren { get; } + + /// + /// Gets a boolean indicating whether all children should be updated + /// + public bool UpdateAllChildren { get; } } \ No newline at end of file diff --git a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelVisualizationViewModel.cs b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelVisualizationViewModel.cs index 77f429408..95d07a35b 100644 --- a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelVisualizationViewModel.cs +++ b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelVisualizationViewModel.cs @@ -144,6 +144,13 @@ public bool IsVisualizationExpanded /// The configuration to apply while updating public abstract void Update(IDataModelUIService dataModelUIService, DataModelUpdateConfiguration? configuration); + /// + /// Gets the search results for the provided search string + /// + /// The search string + /// The search results + public abstract IEnumerable GetSearchResults(string search); + /// /// Gets the current value of the property being visualized /// @@ -281,7 +288,7 @@ internal void PopulateProperties(IDataModelUIService dataModelUIService, DataMod foreach (PropertyInfo propertyInfo in modelType.GetProperties(BindingFlags.Public | BindingFlags.Instance).OrderBy(t => t.MetadataToken)) { string childPath = AppendToPath(propertyInfo.Name); - + if (!ShouldIncludePath(childPath, propertyInfo)) continue; diff --git a/src/Artemis.UI.Shared/Styles/Controls/DataModelPicker.axaml b/src/Artemis.UI.Shared/Styles/Controls/DataModelPicker.axaml index 0175ae880..18df7201a 100644 --- a/src/Artemis.UI.Shared/Styles/Controls/DataModelPicker.axaml +++ b/src/Artemis.UI.Shared/Styles/Controls/DataModelPicker.axaml @@ -11,38 +11,43 @@