From 81fd48a2f77cd7b181bcd630b90c2b4e15d1b596 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Sat, 29 May 2021 01:54:53 -0400 Subject: [PATCH] Create FilterViewModel and move abstract filtering to it --- .../ViewModels/ControlDetailsViewModel.cs | 12 +- .../Diagnostics/ViewModels/FilterViewModel.cs | 124 ++++++++++++++++++ .../ViewModels/TreePageViewModel.cs | 83 +----------- 3 files changed, 131 insertions(+), 88 deletions(-) create mode 100644 src/Avalonia.Diagnostics/Diagnostics/ViewModels/FilterViewModel.cs diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs index 32592559e5..6d0345d6e0 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs @@ -357,17 +357,7 @@ namespace Avalonia.Diagnostics.ViewModels private bool FilterProperty(object arg) { - if (!string.IsNullOrWhiteSpace(TreePage.PropertyFilter) && arg is PropertyViewModel property) - { - if (TreePage.UseRegexFilter) - { - return TreePage.FilterRegex?.IsMatch(property.Name) ?? true; - } - - return property.Name.IndexOf(TreePage.PropertyFilter, StringComparison.OrdinalIgnoreCase) != -1; - } - - return true; + return !(arg is PropertyViewModel property) || TreePage.PropertiesFilter.Filter(property.Name); } private class PropertyComparer : IComparer diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/FilterViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/FilterViewModel.cs new file mode 100644 index 0000000000..2d7d2a769c --- /dev/null +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/FilterViewModel.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Text.RegularExpressions; + +namespace Avalonia.Diagnostics.ViewModels +{ + internal class FilterViewModel : ViewModelBase, INotifyDataErrorInfo + { + private readonly Dictionary _errors = new Dictionary(); + private string _propertyFilter = string.Empty; + private bool _useRegexFilter, _useCaseSensitiveFilter, _useWholeWordFilter; + private string _processedFilter; + private Regex _filterRegex; + + public event EventHandler RefreshFilter; + + public bool Filter(string input) + { + return _filterRegex?.IsMatch(input) ?? true; + } + + private void UpdateFilterRegex() + { + void ClearError() + { + if (_errors.Remove(nameof(PropertyFilter))) + { + ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(nameof(PropertyFilter))); + } + } + + _processedFilter = PropertyFilter.Trim(); + + try + { + var options = RegexOptions.Compiled; + var pattern = UseRegexFilter + ? _processedFilter : Regex.Escape(_processedFilter); + if (!UseCaseSensitiveFilter) + { + options |= RegexOptions.IgnoreCase; + } + if (UseWholeWordFilter) + { + pattern = $"\\b(?:{pattern})\\b"; + } + + _filterRegex = new Regex(pattern, options); + ClearError(); + } + catch (Exception exception) + { + _errors[nameof(PropertyFilter)] = exception.Message; + ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(nameof(PropertyFilter))); + } + } + + public string PropertyFilter + { + get => _propertyFilter; + set + { + if (RaiseAndSetIfChanged(ref _propertyFilter, value)) + { + UpdateFilterRegex(); + RefreshFilter?.Invoke(this, EventArgs.Empty); + } + } + } + + public bool UseRegexFilter + { + get => _useRegexFilter; + set + { + if (RaiseAndSetIfChanged(ref _useRegexFilter, value)) + { + UpdateFilterRegex(); + RefreshFilter?.Invoke(this, EventArgs.Empty); + } + } + } + + public bool UseCaseSensitiveFilter + { + get => _useCaseSensitiveFilter; + set + { + if (RaiseAndSetIfChanged(ref _useCaseSensitiveFilter, value)) + { + UpdateFilterRegex(); + RefreshFilter?.Invoke(this, EventArgs.Empty); + } + } + } + + public bool UseWholeWordFilter + { + get => _useWholeWordFilter; + set + { + if (RaiseAndSetIfChanged(ref _useWholeWordFilter, value)) + { + UpdateFilterRegex(); + RefreshFilter?.Invoke(this, EventArgs.Empty); + } + } + } + + public IEnumerable GetErrors(string propertyName) + { + if (_errors.TryGetValue(propertyName, out var error)) + { + yield return error; + } + } + + public bool HasErrors => _errors.Count > 0; + + public event EventHandler ErrorsChanged; + } +} diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs index 6b779cd6ac..28df4d06f0 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs @@ -1,29 +1,27 @@ using System; -using System.Collections; -using System.Collections.Generic; -using System.ComponentModel; -using System.Text.RegularExpressions; using Avalonia.Controls; using Avalonia.VisualTree; namespace Avalonia.Diagnostics.ViewModels { - internal class TreePageViewModel : ViewModelBase, IDisposable, INotifyDataErrorInfo + internal class TreePageViewModel : ViewModelBase, IDisposable { - private readonly Dictionary _errors = new Dictionary(); private TreeNode _selectedNode; private ControlDetailsViewModel _details; - private string _propertyFilter = string.Empty; - private bool _useRegexFilter; public TreePageViewModel(MainViewModel mainView, TreeNode[] nodes) { MainView = mainView; Nodes = nodes; + + PropertiesFilter = new FilterViewModel(); + PropertiesFilter.RefreshFilter += (s, e) => Details?.PropertiesView.Refresh(); } public MainViewModel MainView { get; } + public FilterViewModel PropertiesFilter { get; } + public TreeNode[] Nodes { get; protected set; } public TreeNode SelectedNode @@ -61,63 +59,6 @@ namespace Avalonia.Diagnostics.ViewModels } } - public Regex FilterRegex { get; set; } - - private void UpdateFilterRegex() - { - void ClearError() - { - if (_errors.Remove(nameof(PropertyFilter))) - { - ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(nameof(PropertyFilter))); - } - } - - if (UseRegexFilter) - { - try - { - FilterRegex = new Regex(PropertyFilter, RegexOptions.Compiled); - ClearError(); - } - catch (Exception exception) - { - _errors[nameof(PropertyFilter)] = exception.Message; - ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(nameof(PropertyFilter))); - } - } - else - { - ClearError(); - } - } - - public string PropertyFilter - { - get => _propertyFilter; - set - { - if (RaiseAndSetIfChanged(ref _propertyFilter, value)) - { - UpdateFilterRegex(); - Details.PropertiesView.Refresh(); - } - } - } - - public bool UseRegexFilter - { - get => _useRegexFilter; - set - { - if (RaiseAndSetIfChanged(ref _useRegexFilter, value)) - { - UpdateFilterRegex(); - Details.PropertiesView.Refresh(); - } - } - } - public void Dispose() { foreach (var node in Nodes) @@ -194,17 +135,5 @@ namespace Avalonia.Diagnostics.ViewModels return null; } - - public IEnumerable GetErrors(string propertyName) - { - if (_errors.TryGetValue(propertyName, out var error)) - { - yield return error; - } - } - - public bool HasErrors => _errors.Count > 0; - - public event EventHandler ErrorsChanged; } }