Skip to content

Commit bc90eee

Browse files
committed
Feature: Profile tags
1 parent a3fb1bc commit bc90eee

File tree

9 files changed

+225
-87
lines changed

9 files changed

+225
-87
lines changed

Source/NETworkManager.Localization/Resources/Strings.Designer.cs

Lines changed: 27 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Source/NETworkManager.Localization/Resources/Strings.resx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3975,4 +3975,13 @@ Right-click for more options.</value>
39753975
<data name="FilterByTags" xml:space="preserve">
39763976
<value>Filter by tags</value>
39773977
</data>
3978+
<data name="NoTagsFound" xml:space="preserve">
3979+
<value>No tags found!</value>
3980+
</data>
3981+
<data name="Match" xml:space="preserve">
3982+
<value>Match</value>
3983+
</data>
3984+
<data name="Any" xml:space="preserve">
3985+
<value>Any</value>
3986+
</data>
39783987
</root>

Source/NETworkManager.Profiles/ProfileFilterInfo.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,9 @@ public class ProfileFilterInfo
1616
/// Tags for filtering profiles.
1717
/// </summary>
1818
public IEnumerable<string> Tags { get; set; } = [];
19+
20+
/// <summary>
21+
/// Indicates whether to match any or all tags in the filter.
22+
/// </summary>
23+
public ProfileTagsFilterMatch TagsFilterMatch { get; set; } = ProfileTagsFilterMatch.Any;
1924
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
using NETworkManager.Utilities;
2+
3+
namespace NETworkManager.Profiles;
4+
5+
/// <summary>
6+
/// Class used to represent a tag filter in the UI (Popup).
7+
/// </summary>
8+
public class ProfileTagsFilterInfo : PropertyChangedBase
9+
{
10+
/// <summary>
11+
/// Private field for <see cref="IsSelected"/>.
12+
/// </summary>
13+
private bool _isSelected;
14+
15+
/// <summary>
16+
/// Indicates whether the tag is selected for filtering.
17+
/// </summary>
18+
public bool IsSelected
19+
{
20+
get => _isSelected;
21+
set
22+
{
23+
if (_isSelected == value)
24+
return;
25+
26+
_isSelected = value;
27+
OnPropertyChanged();
28+
}
29+
}
30+
31+
/// <summary>
32+
/// Tag name.
33+
/// </summary>
34+
public string Name { get; set; }
35+
36+
/// <summary>
37+
/// Creates a new instance of <see cref="ProfileTagsFilterInfo"/> with parameters.
38+
/// </summary>
39+
/// <param name="isSelected">Indicates whether the tag is selected for filtering.</param>
40+
/// <param name="name">Tag name.</param>
41+
public ProfileTagsFilterInfo(bool isSelected, string name)
42+
{
43+
IsSelected = isSelected;
44+
Name = name;
45+
}
46+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
namespace NETworkManager.Profiles;
2+
3+
/// <summary>
4+
/// Enumeration for tag filtering options.
5+
/// </summary>
6+
public enum ProfileTagsFilterMatch
7+
{
8+
/// <summary>
9+
/// Any of the tags can match.
10+
/// </summary>
11+
Any,
12+
13+
/// <summary>
14+
/// All of the tags must match.
15+
/// </summary>
16+
All
17+
}

Source/NETworkManager.Settings/GlobalStaticConfiguration.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ public static class GlobalStaticConfiguration
3131
public static int NetworkChangeDetectionDelay => 5000;
3232

3333
// Profile config
34+
public static bool Profile_TagsMatchAny => true;
3435
public static bool Profile_ExpandProfileView => true;
3536
public static double Profile_WidthCollapsed => 40;
3637
public static double Profile_DefaultWidthExpanded => 250;

Source/NETworkManager.Utilities/FilterTag.cs

Lines changed: 0 additions & 26 deletions
This file was deleted.

Source/NETworkManager/ViewModels/PingMonitorHostViewModel.cs

Lines changed: 69 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -212,36 +212,66 @@ public bool IsSearching
212212
}
213213
}
214214

215-
private bool _filterIsOpen;
215+
private bool _profileFilterIsOpen;
216216

217-
public bool FilterIsOpen
217+
public bool ProfileFilterIsOpen
218218
{
219-
get => _filterIsOpen;
219+
get => _profileFilterIsOpen;
220220
set
221221
{
222-
if (value == _filterIsOpen)
222+
if (value == _profileFilterIsOpen)
223223
return;
224224

225-
_filterIsOpen = value;
225+
_profileFilterIsOpen = value;
226226
OnPropertyChanged();
227227
}
228228
}
229229

230-
public ICollectionView Tags { get; set; }
230+
public ICollectionView ProfileTagsFilterView { get; set; }
231231

232-
public ObservableCollection<FilterTag> FilterTags { get; set; } = [];
232+
public ObservableCollection<ProfileTagsFilterInfo> ProfileTagsFilter { get; set; } = [];
233233

234-
private bool _isFilterSet;
234+
private bool _profileTagsMatchAny = GlobalStaticConfiguration.Profile_TagsMatchAny;
235235

236-
public bool IsFilterSet
236+
public bool ProfileTagsMatchAny
237237
{
238-
get => _isFilterSet;
238+
get => _profileTagsMatchAny;
239239
set
240240
{
241-
if (value == _isFilterSet)
241+
if (value == _profileTagsMatchAny)
242242
return;
243243

244-
_isFilterSet = value;
244+
_profileTagsMatchAny = value;
245+
OnPropertyChanged();
246+
}
247+
}
248+
249+
private bool _profileTagsMatchAll;
250+
251+
public bool ProfileTagsMatchAll
252+
{
253+
get => _profileTagsMatchAll;
254+
set
255+
{
256+
if (value == _profileTagsMatchAll)
257+
return;
258+
259+
_profileTagsMatchAll = value;
260+
OnPropertyChanged();
261+
}
262+
}
263+
264+
private bool _isProfileFilterSet;
265+
266+
public bool IsProfileFilterSet
267+
{
268+
get => _isProfileFilterSet;
269+
set
270+
{
271+
if (value == _isProfileFilterSet)
272+
return;
273+
274+
_isProfileFilterSet = value;
245275
OnPropertyChanged();
246276
}
247277
}
@@ -316,8 +346,8 @@ public PingMonitorHostViewModel(IDialogCoordinator instance)
316346
// Profiles
317347
CreateTags();
318348

319-
Tags = CollectionViewSource.GetDefaultView(FilterTags);
320-
Tags.SortDescriptions.Add(new SortDescription(nameof(FilterTag.Name), ListSortDirection.Ascending));
349+
ProfileTagsFilterView = CollectionViewSource.GetDefaultView(ProfileTagsFilter);
350+
ProfileTagsFilterView.SortDescriptions.Add(new SortDescription(nameof(ProfileTagsFilterInfo.Name), ListSortDirection.Ascending));
321351

322352
SetProfilesView(new ProfileFilterInfo());
323353

@@ -442,34 +472,34 @@ private void ClearSearchAction()
442472
Search = string.Empty;
443473
}
444474

445-
public ICommand OpenFilterCommand => new RelayCommand(_ => OpenFilterAction());
475+
public ICommand OpenProfileFilterCommand => new RelayCommand(_ => OpenProfileFilterAction());
446476

447-
private void OpenFilterAction()
477+
private void OpenProfileFilterAction()
448478
{
449-
FilterIsOpen = true;
479+
ProfileFilterIsOpen = true;
450480
}
451481

452-
public ICommand ApplyFilterCommand => new RelayCommand(_ => ApplyFilterAction());
482+
public ICommand ApplyProfileFilterCommand => new RelayCommand(_ => ApplyProfileFilterAction());
453483

454-
private void ApplyFilterAction()
484+
private void ApplyProfileFilterAction()
455485
{
456486
RefreshProfiles();
457487

458-
IsFilterSet = true;
459-
FilterIsOpen = false;
488+
IsProfileFilterSet = true;
489+
ProfileFilterIsOpen = false;
460490
}
461491

462-
public ICommand ClearFilterCommand => new RelayCommand(_ => ClearFilterAction());
492+
public ICommand ClearProfileFilterCommand => new RelayCommand(_ => ClearProfileFilterAction());
463493

464-
private void ClearFilterAction()
494+
private void ClearProfileFilterAction()
465495
{
466-
foreach (var tag in FilterTags)
496+
foreach (var tag in ProfileTagsFilter)
467497
tag.IsSelected = false;
468498

469499
RefreshProfiles();
470500

471-
IsFilterSet = false;
472-
FilterIsOpen = false;
501+
IsProfileFilterSet = false;
502+
ProfileFilterIsOpen = false;
473503
}
474504

475505
#endregion
@@ -656,18 +686,18 @@ private void CreateTags()
656686

657687
var tagSet = new HashSet<string>(tags);
658688

659-
for (var i = FilterTags.Count - 1; i >= 0; i--)
689+
for (var i = ProfileTagsFilter.Count - 1; i >= 0; i--)
660690
{
661-
if (!tagSet.Contains(FilterTags[i].Name))
662-
FilterTags.RemoveAt(i);
691+
if (!tagSet.Contains(ProfileTagsFilter[i].Name))
692+
ProfileTagsFilter.RemoveAt(i);
663693
}
664694

665-
var existingTagNames = new HashSet<string>(FilterTags.Select(ft => ft.Name));
695+
var existingTagNames = new HashSet<string>(ProfileTagsFilter.Select(ft => ft.Name));
666696

667697
foreach (var tag in tags)
668698
{
669699
if (!existingTagNames.Contains(tag))
670-
FilterTags.Add(new FilterTag(false, tag));
700+
ProfileTagsFilter.Add(new ProfileTagsFilterInfo(false, tag));
671701
}
672702
}
673703

@@ -677,12 +707,15 @@ private void SetProfilesView(ProfileFilterInfo filter, ProfileInfo profile = nul
677707
{
678708
Source = ProfileManager.Groups.SelectMany(x => x.Profiles).Where(x => x.PingMonitor_Enabled && (
679709
string.IsNullOrEmpty(filter.Search) || x.Name.IndexOf(filter.Search) > -1 || x.PingMonitor_Host.IndexOf(filter.Search) > -1) && (
680-
!filter.Tags.Any() || filter.Tags.All(tag => x.TagsCollection.Contains(tag)))
710+
// If no tags are selected, show all profiles
711+
(!filter.Tags.Any()) ||
712+
// Any tag can match
713+
(filter.TagsFilterMatch == ProfileTagsFilterMatch.Any && filter.Tags.Any(tag => x.TagsCollection.Contains(tag))) ||
714+
// All tags must match
715+
(filter.TagsFilterMatch == ProfileTagsFilterMatch.All && filter.Tags.All(tag => x.TagsCollection.Contains(tag))))
681716
).OrderBy(x => x.Group).ThenBy(x => x.Name)
682717
}.View;
683718

684-
// Add "Any" based in selection
685-
686719
Profiles.GroupDescriptions.Add(new PropertyGroupDescription(nameof(ProfileInfo.Group)));
687720

688721
// Set specific profile or first if null
@@ -703,7 +736,8 @@ private void RefreshProfiles()
703736
SetProfilesView(new ProfileFilterInfo
704737
{
705738
Search = Search,
706-
Tags = [.. FilterTags.Where(x => x.IsSelected).Select(x => x.Name)]
739+
Tags = [.. ProfileTagsFilter.Where(x => x.IsSelected).Select(x => x.Name)],
740+
TagsFilterMatch = ProfileTagsMatchAny ? ProfileTagsFilterMatch.Any : ProfileTagsFilterMatch.All
707741
}, SelectedProfile);
708742
}
709743
#endregion

0 commit comments

Comments
 (0)