Skip to content

Commit 773fb8d

Browse files
authored
Merge pull request #3150 from PaulPSta/processkiller_orderby_windowtitle
Put Processes with Visible Window On the Top & Do not kill FL Process
2 parents 350276d + f2e5006 commit 773fb8d

File tree

12 files changed

+252
-58
lines changed

12 files changed

+252
-58
lines changed

Flow.Launcher.Infrastructure/FileExplorerHelper.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,6 @@ private static dynamic GetActiveExplorer()
6767
return explorerWindows.Zip(zOrders).MinBy(x => x.Second).First;
6868
}
6969

70-
private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
71-
7270
/// <summary>
7371
/// Gets the z-order for one or more windows atomically with respect to each other. In Windows, smaller z-order is higher. If the window is not top level, the z order is returned as -1.
7472
/// </summary>
Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-

2-
using Flow.Launcher.Plugin.PluginsManager.ViewModels;
1+
using Flow.Launcher.Plugin.PluginsManager.ViewModels;
32

43
namespace Flow.Launcher.Plugin.PluginsManager.Views
54
{
@@ -8,15 +7,11 @@ namespace Flow.Launcher.Plugin.PluginsManager.Views
87
/// </summary>
98
public partial class PluginsManagerSettings
109
{
11-
private readonly SettingsViewModel viewModel;
12-
1310
internal PluginsManagerSettings(SettingsViewModel viewModel)
1411
{
1512
InitializeComponent();
1613

17-
this.viewModel = viewModel;
18-
19-
this.DataContext = viewModel;
14+
DataContext = viewModel;
2015
}
2116
}
2217
}

Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
1414
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
1515
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
16+
<UseWPF>true</UseWPF>
1617
</PropertyGroup>
1718

1819
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
@@ -58,7 +59,6 @@
5859
</ItemGroup>
5960

6061
<ItemGroup>
61-
<ProjectReference Include="..\..\Flow.Launcher.Infrastructure\Flow.Launcher.Infrastructure.csproj" />
6262
<ProjectReference Include="..\..\Flow.Launcher.Plugin\Flow.Launcher.Plugin.csproj" />
6363
</ItemGroup>
6464

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
2-
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
3-
xmlns:system="clr-namespace:System;assembly=mscorlib">
1+
<ResourceDictionary
2+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4+
xmlns:system="clr-namespace:System;assembly=mscorlib">
45

56
<system:String x:Key="flowlauncher_plugin_processkiller_plugin_name">Process Killer</system:String>
67
<system:String x:Key="flowlauncher_plugin_processkiller_plugin_description">Kill running processes from Flow Launcher</system:String>
@@ -9,4 +10,6 @@
910
<system:String x:Key="flowlauncher_plugin_processkiller_kill_all_count">kill {0} processes</system:String>
1011
<system:String x:Key="flowlauncher_plugin_processkiller_kill_instances">kill all instances</system:String>
1112

13+
<system:String x:Key="flowlauncher_plugin_processkiller_put_visible_window_process_top">Put processes with visible windows on the top</system:String>
14+
1215
</ResourceDictionary>

Plugins/Flow.Launcher.Plugin.ProcessKiller/Main.cs

Lines changed: 90 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,27 @@
1+
using System;
12
using System.Collections.Generic;
23
using System.Linq;
3-
using Flow.Launcher.Infrastructure;
4+
using System.Windows.Controls;
5+
using Flow.Launcher.Plugin.ProcessKiller.ViewModels;
6+
using Flow.Launcher.Plugin.ProcessKiller.Views;
47

58
namespace Flow.Launcher.Plugin.ProcessKiller
69
{
7-
public class Main : IPlugin, IPluginI18n, IContextMenu
10+
public class Main : IPlugin, IPluginI18n, IContextMenu, ISettingProvider
811
{
9-
private ProcessHelper processHelper = new ProcessHelper();
12+
private readonly ProcessHelper processHelper = new();
1013

1114
private static PluginInitContext _context;
1215

16+
internal Settings Settings;
17+
18+
private SettingsViewModel _viewModel;
19+
1320
public void Init(PluginInitContext context)
1421
{
1522
_context = context;
23+
Settings = context.API.LoadSettingJsonStorage<Settings>();
24+
_viewModel = new SettingsViewModel(Settings);
1625
}
1726

1827
public List<Result> Query(Query query)
@@ -48,7 +57,7 @@ public List<Result> LoadContextMenus(Result result)
4857
{
4958
foreach (var p in similarProcesses)
5059
{
51-
processHelper.TryKill(p);
60+
processHelper.TryKill(_context, p);
5261
}
5362

5463
return true;
@@ -62,45 +71,102 @@ public List<Result> LoadContextMenus(Result result)
6271

6372
private List<Result> CreateResultsFromQuery(Query query)
6473
{
65-
string termToSearch = query.Search;
66-
var processlist = processHelper.GetMatchingProcesses(termToSearch);
67-
68-
if (!processlist.Any())
74+
// Get all non-system processes
75+
var allPocessList = processHelper.GetMatchingProcesses();
76+
if (!allPocessList.Any())
6977
{
7078
return null;
7179
}
7280

73-
var results = new List<Result>();
81+
// Filter processes based on search term
82+
var searchTerm = query.Search;
83+
var processlist = new List<ProcessResult>();
84+
var processWindowTitle = ProcessHelper.GetProcessesWithNonEmptyWindowTitle();
85+
if (string.IsNullOrWhiteSpace(searchTerm))
86+
{
87+
foreach (var p in allPocessList)
88+
{
89+
var progressNameIdTitle = ProcessHelper.GetProcessNameIdTitle(p);
7490

91+
if (processWindowTitle.TryGetValue(p.Id, out var windowTitle))
92+
{
93+
// Add score to prioritize processes with visible windows
94+
// And use window title for those processes
95+
processlist.Add(new ProcessResult(p, Settings.PutVisibleWindowProcessesTop ? 200 : 0, windowTitle, null, progressNameIdTitle));
96+
}
97+
else
98+
{
99+
processlist.Add(new ProcessResult(p, 0, progressNameIdTitle, null, progressNameIdTitle));
100+
}
101+
}
102+
}
103+
else
104+
{
105+
foreach (var p in allPocessList)
106+
{
107+
var progressNameIdTitle = ProcessHelper.GetProcessNameIdTitle(p);
108+
109+
if (processWindowTitle.TryGetValue(p.Id, out var windowTitle))
110+
{
111+
// Get max score from searching process name, window title and process id
112+
var windowTitleMatch = _context.API.FuzzySearch(searchTerm, windowTitle);
113+
var processNameIdMatch = _context.API.FuzzySearch(searchTerm, progressNameIdTitle);
114+
var score = Math.Max(windowTitleMatch.Score, processNameIdMatch.Score);
115+
if (score > 0)
116+
{
117+
// Add score to prioritize processes with visible windows
118+
// And use window title for those processes
119+
if (Settings.PutVisibleWindowProcessesTop)
120+
{
121+
score += 200;
122+
}
123+
processlist.Add(new ProcessResult(p, score, windowTitle,
124+
score == windowTitleMatch.Score ? windowTitleMatch : null, progressNameIdTitle));
125+
}
126+
}
127+
else
128+
{
129+
var processNameIdMatch = _context.API.FuzzySearch(searchTerm, progressNameIdTitle);
130+
var score = processNameIdMatch.Score;
131+
if (score > 0)
132+
{
133+
processlist.Add(new ProcessResult(p, score, progressNameIdTitle, processNameIdMatch, progressNameIdTitle));
134+
}
135+
}
136+
}
137+
}
138+
139+
var results = new List<Result>();
75140
foreach (var pr in processlist)
76141
{
77142
var p = pr.Process;
78143
var path = processHelper.TryGetProcessFilename(p);
79144
results.Add(new Result()
80145
{
81146
IcoPath = path,
82-
Title = p.ProcessName + " - " + p.Id,
147+
Title = pr.Title,
148+
TitleToolTip = pr.Tooltip,
83149
SubTitle = path,
84-
TitleHighlightData = StringMatcher.FuzzySearch(termToSearch, p.ProcessName).MatchData,
150+
TitleHighlightData = pr.TitleMatch?.MatchData,
85151
Score = pr.Score,
86152
ContextData = p.ProcessName,
87153
AutoCompleteText = $"{_context.CurrentPluginMetadata.ActionKeyword}{Plugin.Query.TermSeparator}{p.ProcessName}",
88154
Action = (c) =>
89155
{
90-
processHelper.TryKill(p);
91-
// Re-query to refresh process list
92-
_context.API.ChangeQuery(query.RawQuery, true);
93-
return true;
156+
processHelper.TryKill(_context, p);
157+
_context.API.ReQuery();
158+
return false;
94159
}
95160
});
96161
}
97162

163+
// Order results by process name for processes without visible windows
98164
var sortedResults = results.OrderBy(x => x.Title).ToList();
99165

100166
// When there are multiple results AND all of them are instances of the same executable
101167
// add a quick option to kill them all at the top of the results.
102168
var firstResult = sortedResults.FirstOrDefault(x => !string.IsNullOrEmpty(x.SubTitle));
103-
if (processlist.Count > 1 && !string.IsNullOrEmpty(termToSearch) && sortedResults.All(r => r.SubTitle == firstResult?.SubTitle))
169+
if (processlist.Count > 1 && !string.IsNullOrEmpty(searchTerm) && sortedResults.All(r => r.SubTitle == firstResult?.SubTitle))
104170
{
105171
sortedResults.Insert(1, new Result()
106172
{
@@ -112,16 +178,20 @@ private List<Result> CreateResultsFromQuery(Query query)
112178
{
113179
foreach (var p in processlist)
114180
{
115-
processHelper.TryKill(p.Process);
181+
processHelper.TryKill(_context, p.Process);
116182
}
117-
// Re-query to refresh process list
118-
_context.API.ChangeQuery(query.RawQuery, true);
119-
return true;
183+
_context.API.ReQuery();
184+
return false;
120185
}
121186
});
122187
}
123188

124189
return sortedResults;
125190
}
191+
192+
public Control CreateSettingPanel()
193+
{
194+
return new SettingsControl(_viewModel);
195+
}
126196
}
127197
}
Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,7 @@
11
QueryFullProcessImageName
2-
OpenProcess
2+
OpenProcess
3+
EnumWindows
4+
GetWindowTextLength
5+
GetWindowText
6+
IsWindowVisible
7+
GetWindowThreadProcessId

0 commit comments

Comments
 (0)