Merge pull request #3259 from Jack251970/remove_duplicated_programs

Support removing duplicated programs
This commit is contained in:
Jeremy Wu 2025-02-22 23:03:29 +11:00 committed by GitHub
commit e028ed399f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 69 additions and 25 deletions

View file

@ -36,6 +36,8 @@
<system:String x:Key="flowlauncher_plugin_program_enable_hideuninstallers_tooltip">Hides programs with common uninstaller names, such as unins000.exe</system:String> <system:String x:Key="flowlauncher_plugin_program_enable_hideuninstallers_tooltip">Hides programs with common uninstaller names, such as unins000.exe</system:String>
<system:String x:Key="flowlauncher_plugin_program_enable_description">Search in Program Description</system:String> <system:String x:Key="flowlauncher_plugin_program_enable_description">Search in Program Description</system:String>
<system:String x:Key="flowlauncher_plugin_program_enable_description_tooltip">Flow will search program's description</system:String> <system:String x:Key="flowlauncher_plugin_program_enable_description_tooltip">Flow will search program's description</system:String>
<system:String x:Key="flowlauncher_plugin_program_enable_hideduplicatedwindowsapp">Hide duplicated apps</system:String>
<system:String x:Key="flowlauncher_plugin_program_enable_hideduplicatedwindowsapp_tooltip">Hide duplicated Win32 programs that are already in the UWP list</system:String>
<system:String x:Key="flowlauncher_plugin_program_suffixes_header">Suffixes</system:String> <system:String x:Key="flowlauncher_plugin_program_suffixes_header">Suffixes</system:String>
<system:String x:Key="flowlauncher_plugin_program_max_depth_header">Max Depth</system:String> <system:String x:Key="flowlauncher_plugin_program_max_depth_header">Max Depth</system:String>

View file

@ -72,6 +72,8 @@ namespace Flow.Launcher.Plugin.Program
private const string ExeUninstallerSuffix = ".exe"; private const string ExeUninstallerSuffix = ".exe";
private const string InkUninstallerSuffix = ".lnk"; private const string InkUninstallerSuffix = ".lnk";
private static readonly string WindowsAppPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "WindowsApps");
static Main() static Main()
{ {
} }
@ -90,11 +92,20 @@ namespace Flow.Launcher.Plugin.Program
{ {
try try
{ {
// Collect all UWP Windows app directories
var uwpsDirectories = _settings.HideDuplicatedWindowsApp ? _uwps
.Where(uwp => !string.IsNullOrEmpty(uwp.Location)) // Exclude invalid paths
.Where(uwp => uwp.Location.StartsWith(WindowsAppPath, StringComparison.OrdinalIgnoreCase)) // Keep system apps
.Select(uwp => uwp.Location.TrimEnd('\\')) // Remove trailing slash
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToArray() : null;
return _win32s.Cast<IProgram>() return _win32s.Cast<IProgram>()
.Concat(_uwps) .Concat(_uwps)
.AsParallel() .AsParallel()
.WithCancellation(token) .WithCancellation(token)
.Where(HideUninstallersFilter) .Where(HideUninstallersFilter)
.Where(p => HideDuplicatedWindowsAppFilter(p, uwpsDirectories))
.Where(p => p.Enabled) .Where(p => p.Enabled)
.Select(p => p.Result(query.Search, Context.API)) .Select(p => p.Result(query.Search, Context.API))
.Where(r => r?.Score > 0) .Where(r => r?.Score > 0)
@ -152,6 +163,23 @@ namespace Flow.Launcher.Plugin.Program
return true; return true;
} }
private static bool HideDuplicatedWindowsAppFilter(IProgram program, string[] uwpsDirectories)
{
if (uwpsDirectories == null || uwpsDirectories.Length == 0) return true;
if (program is UWPApp) return true;
var location = program.Location.TrimEnd('\\'); // Ensure trailing slash
if (string.IsNullOrEmpty(location))
return true; // Keep if location is invalid
if (!location.StartsWith(WindowsAppPath, StringComparison.OrdinalIgnoreCase))
return true; // Keep if not a Windows app
// Check if the any Win32 executable directory contains UWP Windows app location matches
return !uwpsDirectories.Any(uwpDirectory =>
location.StartsWith(uwpDirectory, StringComparison.OrdinalIgnoreCase));
}
public async Task InitAsync(PluginInitContext context) public async Task InitAsync(PluginInitContext context)
{ {
Context = context; Context = context;

View file

@ -121,6 +121,7 @@ namespace Flow.Launcher.Plugin.Program
public bool EnableRegistrySource { get; set; } = true; public bool EnableRegistrySource { get; set; } = true;
public bool EnablePathSource { get; set; } = false; public bool EnablePathSource { get; set; } = false;
public bool EnableUWP { get; set; } = true; public bool EnableUWP { get; set; } = true;
public bool HideDuplicatedWindowsApp { get; set; } = false;
internal const char SuffixSeparator = ';'; internal const char SuffixSeparator = ';';
} }

View file

@ -8,7 +8,7 @@
DataContext="{Binding RelativeSource={RelativeSource Self}}" DataContext="{Binding RelativeSource={RelativeSource Self}}"
mc:Ignorable="d"> mc:Ignorable="d">
<UserControl.Resources> <UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/> <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</UserControl.Resources> </UserControl.Resources>
<Grid Margin="0"> <Grid Margin="0">
<Grid.RowDefinitions> <Grid.RowDefinitions>
@ -18,40 +18,40 @@
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<DockPanel <DockPanel
Margin="70,10,0,8" Margin="70 10 0 8"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
LastChildFill="True"> LastChildFill="True">
<TextBlock <TextBlock
MinWidth="120" MinWidth="120"
Margin="0,5,10,0" Margin="0 5 10 0"
Text="{DynamicResource flowlauncher_plugin_program_index_source}" /> Text="{DynamicResource flowlauncher_plugin_program_index_source}" />
<WrapPanel <WrapPanel
Width="Auto" Width="Auto"
Margin="0,0,14,0" Margin="0 0 14 0"
HorizontalAlignment="Right" HorizontalAlignment="Right"
DockPanel.Dock="Right"> DockPanel.Dock="Right">
<CheckBox <CheckBox
Name="UWPEnabled" Name="UWPEnabled"
Margin="12,0,12,0" Margin="12 0 12 0"
Visibility="{Binding ShowUWPCheckbox, Converter={StaticResource BooleanToVisibilityConverter}}"
Content="{DynamicResource flowlauncher_plugin_program_index_uwp}" Content="{DynamicResource flowlauncher_plugin_program_index_uwp}"
IsChecked="{Binding EnableUWP}" IsChecked="{Binding EnableUWP}"
ToolTip="{DynamicResource flowlauncher_plugin_program_index_uwp_tooltip}" /> ToolTip="{DynamicResource flowlauncher_plugin_program_index_uwp_tooltip}"
Visibility="{Binding ShowUWPCheckbox, Converter={StaticResource BooleanToVisibilityConverter}}" />
<CheckBox <CheckBox
Name="StartMenuEnabled" Name="StartMenuEnabled"
Margin="12,0,12,0" Margin="12 0 12 0"
Content="{DynamicResource flowlauncher_plugin_program_index_start}" Content="{DynamicResource flowlauncher_plugin_program_index_start}"
IsChecked="{Binding EnableStartMenuSource}" IsChecked="{Binding EnableStartMenuSource}"
ToolTip="{DynamicResource flowlauncher_plugin_program_index_start_tooltip}" /> ToolTip="{DynamicResource flowlauncher_plugin_program_index_start_tooltip}" />
<CheckBox <CheckBox
Name="RegistryEnabled" Name="RegistryEnabled"
Margin="12,0,12,0" Margin="12 0 12 0"
Content="{DynamicResource flowlauncher_plugin_program_index_registry}" Content="{DynamicResource flowlauncher_plugin_program_index_registry}"
IsChecked="{Binding EnableRegistrySource}" IsChecked="{Binding EnableRegistrySource}"
ToolTip="{DynamicResource flowlauncher_plugin_program_index_registry_tooltip}" /> ToolTip="{DynamicResource flowlauncher_plugin_program_index_registry_tooltip}" />
<CheckBox <CheckBox
Name="PATHEnabled" Name="PATHEnabled"
Margin="12,0,12,0" Margin="12 0 12 0"
Content="{DynamicResource flowlauncher_plugin_program_index_PATH}" Content="{DynamicResource flowlauncher_plugin_program_index_PATH}"
IsChecked="{Binding EnablePATHSource}" IsChecked="{Binding EnablePATHSource}"
ToolTip="{DynamicResource flowlauncher_plugin_program_index_PATH_tooltip}" /> ToolTip="{DynamicResource flowlauncher_plugin_program_index_PATH_tooltip}" />
@ -67,21 +67,20 @@
BorderBrush="{DynamicResource Color03B}" BorderBrush="{DynamicResource Color03B}"
BorderThickness="1" /> BorderThickness="1" />
<DockPanel <DockPanel
Margin="70,10,0,8" Margin="70 10 0 8"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
LastChildFill="True"> LastChildFill="True">
<TextBlock <TextBlock
MinWidth="120" MinWidth="120"
Margin="0,5,10,0" Margin="0 5 10 0"
Text="{DynamicResource flowlauncher_plugin_program_index_option}" /> Text="{DynamicResource flowlauncher_plugin_program_index_option}" />
<WrapPanel <WrapPanel
Width="Auto" Width="Auto"
Margin="0,0,14,0" Margin="0 0 14 0"
HorizontalAlignment="Right" HorizontalAlignment="Right"
DockPanel.Dock="Right"> DockPanel.Dock="Right">
<CheckBox <CheckBox
Name="HideLnkEnabled" Margin="12 0 12 0"
Margin="12,0,12,0"
Content="{DynamicResource flowlauncher_plugin_program_enable_hidelnkpath}" Content="{DynamicResource flowlauncher_plugin_program_enable_hidelnkpath}"
IsChecked="{Binding HideAppsPath}" IsChecked="{Binding HideAppsPath}"
ToolTip="{DynamicResource flowlauncher_plugin_program_enable_hidelnkpath_tooltip}" /> ToolTip="{DynamicResource flowlauncher_plugin_program_enable_hidelnkpath_tooltip}" />
@ -91,11 +90,15 @@
IsChecked="{Binding HideUninstallers}" IsChecked="{Binding HideUninstallers}"
ToolTip="{DynamicResource flowlauncher_plugin_program_enable_hideuninstallers_tooltip}" /> ToolTip="{DynamicResource flowlauncher_plugin_program_enable_hideuninstallers_tooltip}" />
<CheckBox <CheckBox
Name="DescriptionEnabled" Margin="12 0 12 0"
Margin="12,0,12,0"
Content="{DynamicResource flowlauncher_plugin_program_enable_description}" Content="{DynamicResource flowlauncher_plugin_program_enable_description}"
IsChecked="{Binding EnableDescription}" IsChecked="{Binding EnableDescription}"
ToolTip="{DynamicResource flowlauncher_plugin_program_enable_description_tooltip}" /> ToolTip="{DynamicResource flowlauncher_plugin_program_enable_description_tooltip}" />
<CheckBox
Margin="12 0 12 0"
Content="{DynamicResource flowlauncher_plugin_program_enable_hideduplicatedwindowsapp}"
IsChecked="{Binding HideDuplicatedWindowsApp}"
ToolTip="{DynamicResource flowlauncher_plugin_program_enable_hideduplicatedwindowsapp_tooltip}" />
</WrapPanel> </WrapPanel>
</DockPanel> </DockPanel>
<Separator <Separator
@ -103,28 +106,28 @@
BorderBrush="{DynamicResource Color03B}" BorderBrush="{DynamicResource Color03B}"
BorderThickness="1" /> BorderThickness="1" />
<StackPanel <StackPanel
Margin="60,0,0,2" Margin="60 0 0 2"
HorizontalAlignment="Left" HorizontalAlignment="Left"
VerticalAlignment="Center" VerticalAlignment="Center"
Orientation="Horizontal"> Orientation="Horizontal">
<Button <Button
x:Name="btnLoadAllProgramSource" x:Name="btnLoadAllProgramSource"
MinWidth="120" MinWidth="120"
Margin="10,10,5,10" Margin="10 10 5 10"
HorizontalAlignment="Right" HorizontalAlignment="Right"
Click="btnLoadAllProgramSource_OnClick" Click="btnLoadAllProgramSource_OnClick"
Content="{DynamicResource flowlauncher_plugin_program_all_programs}" /> Content="{DynamicResource flowlauncher_plugin_program_all_programs}" />
<Button <Button
x:Name="btnProgramSuffixes" x:Name="btnProgramSuffixes"
MinWidth="120" MinWidth="120"
Margin="5,10,5,10" Margin="5 10 5 10"
HorizontalAlignment="Right" HorizontalAlignment="Right"
Click="BtnProgramSuffixes_OnClick" Click="BtnProgramSuffixes_OnClick"
Content="{DynamicResource flowlauncher_plugin_program_suffixes}" /> Content="{DynamicResource flowlauncher_plugin_program_suffixes}" />
<Button <Button
x:Name="btnReindex" x:Name="btnReindex"
MinWidth="120" MinWidth="120"
Margin="5,10,5,10" Margin="5 10 5 10"
HorizontalAlignment="Right" HorizontalAlignment="Right"
Click="btnReindex_Click" Click="btnReindex_Click"
Content="{DynamicResource flowlauncher_plugin_program_reindex}" /> Content="{DynamicResource flowlauncher_plugin_program_reindex}" />
@ -142,7 +145,7 @@
Minimum="0" /> Minimum="0" />
<TextBlock <TextBlock
Height="20" Height="20"
Margin="10,0,0,0" Margin="10 0 0 0"
HorizontalAlignment="Center" HorizontalAlignment="Center"
Text="{DynamicResource flowlauncher_plugin_program_indexing}" /> Text="{DynamicResource flowlauncher_plugin_program_indexing}" />
</StackPanel> </StackPanel>
@ -151,7 +154,7 @@
<ListView <ListView
x:Name="programSourceView" x:Name="programSourceView"
Grid.Row="2" Grid.Row="2"
Margin="70,0,20,0" Margin="70 0 20 0"
AllowDrop="True" AllowDrop="True"
BorderBrush="DarkGray" BorderBrush="DarkGray"
BorderThickness="1" BorderThickness="1"
@ -203,7 +206,7 @@
<DockPanel <DockPanel
Grid.Row="3" Grid.Row="3"
Grid.RowSpan="1" Grid.RowSpan="1"
Margin="0,0,20,0"> Margin="0 0 20 0">
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal"> <StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
<Button <Button
x:Name="btnProgramSourceStatus" x:Name="btnProgramSourceStatus"
@ -220,7 +223,7 @@
<Button <Button
x:Name="btnAddProgramSource" x:Name="btnAddProgramSource"
MinWidth="100" MinWidth="100"
Margin="10,10,0,10" Margin="10 10 0 10"
Click="btnAddProgramSource_OnClick" Click="btnAddProgramSource_OnClick"
Content="{DynamicResource flowlauncher_plugin_program_add}" /> Content="{DynamicResource flowlauncher_plugin_program_add}" />
</StackPanel> </StackPanel>

View file

@ -57,6 +57,16 @@ namespace Flow.Launcher.Plugin.Program.Views
} }
} }
public bool HideDuplicatedWindowsApp
{
get => _settings.HideDuplicatedWindowsApp;
set
{
Main.ResetCache();
_settings.HideDuplicatedWindowsApp = value;
}
}
public bool EnableRegistrySource public bool EnableRegistrySource
{ {
get => _settings.EnableRegistrySource; get => _settings.EnableRegistrySource;