Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 39 additions & 7 deletions src/ResXManager.View/Behaviors/EntityFilter.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace ResXManager.View.Behaviors;
namespace ResXManager.View.Behaviors;

using System;
using System.Text.RegularExpressions;
Expand All @@ -8,6 +8,7 @@
using Microsoft.Xaml.Behaviors;

using ResXManager.Infrastructure;
using ResXManager.Model;

using Throttle;

Expand All @@ -26,21 +27,49 @@ public string? FilterText
/// </summary>
public static readonly DependencyProperty FilterTextProperty =
DependencyProperty.Register(nameof(FilterText), typeof(string), typeof(EntityFilter),
new FrameworkPropertyMetadata(null, (sender, _) => ((EntityFilter)sender).FilterText_Changed()));
new FrameworkPropertyMetadata(null, (sender, _) => ((EntityFilter)sender).Filter_Changed()));

public string? ProjectFilter
{
get => (string)GetValue(ProjectFilterProperty);
set => SetValue(ProjectFilterProperty, value);
}
/// <summary>
/// Identifies the <see cref="ProjectFilter"/> dependency property
/// </summary>
public static readonly DependencyProperty ProjectFilterProperty =
DependencyProperty.Register(nameof(ProjectFilter), typeof(string), typeof(EntityFilter),
new FrameworkPropertyMetadata(null, (sender, _) => ((EntityFilter)sender).Filter_Changed()));

[Throttled(typeof(Throttle), 300)]
private void FilterText_Changed()
private void Filter_Changed()
{
var listBox = AssociatedObject;
if (listBox == null)
return;

var value = FilterText;
var textFilter = BuildTextFilter(FilterText);
var projectFilter = ProjectFilter;

listBox.Items.Filter = BuildFilter(value);
if (textFilter == null && projectFilter == null)
{
listBox.Items.Filter = null;
return;
}

listBox.Items.Filter = item =>
{
if (projectFilter != null && item is ResourceEntity entity && !string.Equals(entity.ProjectName, projectFilter, StringComparison.OrdinalIgnoreCase))
return false;

if (textFilter != null && !textFilter(item))
return false;

return true;
};
}

public static Predicate<object>? BuildFilter(string? value)
public static Predicate<object>? BuildTextFilter(string? value)
{
value = value?.Trim();

Expand Down Expand Up @@ -68,10 +97,13 @@ private void FilterText_Changed()
return null;
}

// Keep for backward compatibility with any external callers
public static Predicate<object>? BuildFilter(string? value) => BuildTextFilter(value);

protected override void OnAttached()
{
base.OnAttached();

FilterText_Changed();
Filter_Changed();
}
}
26 changes: 26 additions & 0 deletions src/ResXManager.View/Properties/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions src/ResXManager.View/Properties/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,12 @@ Your ResXManager team.
<data name="AllLanguages" xml:space="preserve">
<value>All languages</value>
</data>
<data name="AllProjects" xml:space="preserve">
<value>All Projects</value>
</data>
<data name="ProjectFilterToolTip" xml:space="preserve">
<value>Filter resource files by project</value>
</data>
<data name="AllComments" xml:space="preserve">
<value>All comments</value>
</data>
Expand Down
30 changes: 28 additions & 2 deletions src/ResXManager.View/Visuals/ResourceView.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@
<Grid.RowDefinitions>
<RowDefinition Height="24" MinHeight="24" />
<RowDefinition Height="4" />
<RowDefinition Height="Auto" />
<RowDefinition Height="4" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<DockPanel Grid.Row="0">
Expand All @@ -113,7 +115,31 @@
</Border>
</DockPanel>

<ListBox x:Name="ListBox" Grid.Row="2"
<ComboBox x:Name="ProjectFilterComboBox" Grid.Row="2"
ItemsSource="{Binding ProjectNames}"
SelectedItem="{Binding SelectedProject}"
IsEditable="False"
ToolTip="{x:Static properties:Resources.ProjectFilterToolTip}"
Style="{DynamicResource {x:Static styles:ResourceKeys.ComboBoxStyle}}">
<ComboBox.ItemTemplate>
<DataTemplate DataType="{x:Type visuals:ProjectFilterItem}">
<TextBlock>
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Text" Value="{Binding ProjectName}" />
<Style.Triggers>
<DataTrigger Binding="{Binding ProjectName}" Value="{x:Null}">
<Setter Property="Text" Value="{x:Static properties:Resources.AllProjects}" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>

<ListBox x:Name="ListBox" Grid.Row="4"
ItemsSource="{Binding Source={StaticResource ResourceEntitiesSource}}"
Padding="5,0" BorderThickness="1" SelectionMode="Extended"
toms:MultiSelectorExtensions.SelectionBinding="{Binding SelectedEntities}"
Expand Down Expand Up @@ -165,7 +191,7 @@
</ContextMenu>
</ListBox.ContextMenu>
<interactions:Interaction.Behaviors>
<behaviors:EntityFilter FilterText="{Binding Text, ElementName=EntityFilter}" />
<behaviors:EntityFilter FilterText="{Binding Text, ElementName=EntityFilter}" ProjectFilter="{Binding EffectiveProjectFilter}" />
<behaviors:SelectAllBehavior x:Name="SelectAllBehavior" />
</interactions:Interaction.Behaviors>
</ListBox>
Expand Down
38 changes: 38 additions & 0 deletions src/ResXManager.View/Visuals/ResourceViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

using DataGridExtensions;

using PropertyChanged;

using ResXManager.Infrastructure;
using ResXManager.Model;
using ResXManager.View.ColumnHeaders;
Expand Down Expand Up @@ -58,6 +60,7 @@ public ResourceViewModel(ResourceManager resourceManager, IConfiguration configu
ResourceTableEntries.CollectionChanged += (_, __) => ResourceTableEntries_CollectionChanged();

resourceManager.LanguageChanged += ResourceManager_LanguageChanged;
resourceManager.ResourceEntities.CollectionChanged += (_, __) => UpdateProjectNames();
}

internal event EventHandler<ResourceTableEntryEventArgs>? ClearFiltersRequest;
Expand All @@ -70,6 +73,34 @@ public ResourceViewModel(ResourceManager resourceManager, IConfiguration configu

public ObservableCollection<ResourceTableEntry> SelectedTableEntries { get; } = [];

public ObservableCollection<ProjectFilterItem> ProjectNames { get; } = [];

private void UpdateProjectNames()
{
var selectedProjectName = SelectedProject?.ProjectName;

var names = ResourceManager.ResourceEntities
.Select(e => e.ProjectName)
.Distinct()
.OrderBy(n => n, StringComparer.CurrentCultureIgnoreCase)
.ToList();

ProjectNames.Clear();
ProjectNames.Add(ProjectFilterItem.AllProjects);
foreach (var name in names)
ProjectNames.Add(new ProjectFilterItem(name));

// Restore selection if the project still exists, otherwise reset to All Projects
SelectedProject = (selectedProjectName != null
? ProjectNames.FirstOrDefault(p => p.ProjectName == selectedProjectName)
: null) ?? ProjectNames[0];
}

public ProjectFilterItem? SelectedProject { get; set; }

[DependsOn(nameof(SelectedProject))]
public string? EffectiveProjectFilter => SelectedProject?.ProjectName;

public bool IsLoading { get; private set; }

// Do not use CollectionViewSource in XAML, has huge performance impact when there are many items in the list.
Expand Down Expand Up @@ -628,3 +659,10 @@ public void Dispose()
Interlocked.Exchange(ref _loadingCancellationTokenSource, null)?.Dispose();
}
}

public sealed class ProjectFilterItem(string? projectName)
{
public static readonly ProjectFilterItem AllProjects = new(null);

public string? ProjectName { get; } = projectName;
}