mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
Order search result by window title
This commit is contained in:
parent
e7e825e930
commit
b2532fc88e
2 changed files with 61 additions and 13 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Flow.Launcher.Infrastructure;
|
using Flow.Launcher.Infrastructure;
|
||||||
|
|
||||||
|
|
@ -60,30 +60,33 @@ namespace Flow.Launcher.Plugin.ProcessKiller
|
||||||
return menuOptions;
|
return menuOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private record RunningProcessInfo(string ProcessName, string MainWindowTitle);
|
||||||
|
|
||||||
private List<Result> CreateResultsFromQuery(Query query)
|
private List<Result> CreateResultsFromQuery(Query query)
|
||||||
{
|
{
|
||||||
string termToSearch = query.Search;
|
string termToSearch = query.Search;
|
||||||
var processlist = processHelper.GetMatchingProcesses(termToSearch);
|
var processList = processHelper.GetMatchingProcesses(termToSearch);
|
||||||
|
var processWithNonEmptyMainWindowTitleList = processHelper.GetProcessesWithNonEmptyWindowTitle();
|
||||||
if (!processlist.Any())
|
|
||||||
|
if (!processList.Any())
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var results = new List<Result>();
|
var results = new List<Result>();
|
||||||
|
|
||||||
foreach (var pr in processlist)
|
foreach (var pr in processList)
|
||||||
{
|
{
|
||||||
var p = pr.Process;
|
var p = pr.Process;
|
||||||
var path = processHelper.TryGetProcessFilename(p);
|
var path = processHelper.TryGetProcessFilename(p);
|
||||||
results.Add(new Result()
|
results.Add(new Result()
|
||||||
{
|
{
|
||||||
IcoPath = path,
|
IcoPath = path,
|
||||||
Title = p.ProcessName + " - " + p.Id,
|
Title = processWithNonEmptyMainWindowTitleList.TryGetValue(p.Id, out var mainWindowTitle) ? mainWindowTitle : p.ProcessName + " - " + p.Id,
|
||||||
SubTitle = path,
|
SubTitle = path,
|
||||||
TitleHighlightData = StringMatcher.FuzzySearch(termToSearch, p.ProcessName).MatchData,
|
TitleHighlightData = StringMatcher.FuzzySearch(termToSearch, p.ProcessName).MatchData,
|
||||||
Score = pr.Score,
|
Score = pr.Score,
|
||||||
ContextData = p.ProcessName,
|
ContextData = new RunningProcessInfo(p.ProcessName, mainWindowTitle),
|
||||||
AutoCompleteText = $"{_context.CurrentPluginMetadata.ActionKeyword}{Plugin.Query.TermSeparator}{p.ProcessName}",
|
AutoCompleteText = $"{_context.CurrentPluginMetadata.ActionKeyword}{Plugin.Query.TermSeparator}{p.ProcessName}",
|
||||||
Action = (c) =>
|
Action = (c) =>
|
||||||
{
|
{
|
||||||
|
|
@ -95,22 +98,25 @@ namespace Flow.Launcher.Plugin.ProcessKiller
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var sortedResults = results.OrderBy(x => x.Title).ToList();
|
var sortedResults = results
|
||||||
|
.OrderBy(x => string.IsNullOrEmpty(((RunningProcessInfo)x.ContextData).MainWindowTitle))
|
||||||
|
.ThenBy(x => x.Title)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
// When there are multiple results AND all of them are instances of the same executable
|
// When there are multiple results AND all of them are instances of the same executable
|
||||||
// add a quick option to kill them all at the top of the results.
|
// add a quick option to kill them all at the top of the results.
|
||||||
var firstResult = sortedResults.FirstOrDefault(x => !string.IsNullOrEmpty(x.SubTitle));
|
var firstResult = sortedResults.FirstOrDefault(x => !string.IsNullOrEmpty(x.SubTitle));
|
||||||
if (processlist.Count > 1 && !string.IsNullOrEmpty(termToSearch) && sortedResults.All(r => r.SubTitle == firstResult?.SubTitle))
|
if (processList.Count > 1 && !string.IsNullOrEmpty(termToSearch) && sortedResults.All(r => r.SubTitle == firstResult?.SubTitle))
|
||||||
{
|
{
|
||||||
sortedResults.Insert(1, new Result()
|
sortedResults.Insert(1, new Result()
|
||||||
{
|
{
|
||||||
IcoPath = firstResult?.IcoPath,
|
IcoPath = firstResult?.IcoPath,
|
||||||
Title = string.Format(_context.API.GetTranslation("flowlauncher_plugin_processkiller_kill_all"), firstResult?.ContextData),
|
Title = string.Format(_context.API.GetTranslation("flowlauncher_plugin_processkiller_kill_all"), ((RunningProcessInfo)firstResult?.ContextData).ProcessName),
|
||||||
SubTitle = string.Format(_context.API.GetTranslation("flowlauncher_plugin_processkiller_kill_all_count"), processlist.Count),
|
SubTitle = string.Format(_context.API.GetTranslation("flowlauncher_plugin_processkiller_kill_all_count"), processList.Count),
|
||||||
Score = 200,
|
Score = 200,
|
||||||
Action = (c) =>
|
Action = (c) =>
|
||||||
{
|
{
|
||||||
foreach (var p in processlist)
|
foreach (var p in processList)
|
||||||
{
|
{
|
||||||
processHelper.TryKill(p.Process);
|
processHelper.TryKill(p.Process);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using Flow.Launcher.Infrastructure;
|
using Flow.Launcher.Infrastructure;
|
||||||
using Flow.Launcher.Infrastructure.Logger;
|
using Flow.Launcher.Infrastructure.Logger;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
@ -11,6 +11,20 @@ namespace Flow.Launcher.Plugin.ProcessKiller
|
||||||
{
|
{
|
||||||
internal class ProcessHelper
|
internal class ProcessHelper
|
||||||
{
|
{
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
private static extern bool EnumWindows(EnumWindowsProc enumProc, IntPtr lParam);
|
||||||
|
|
||||||
|
private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
|
||||||
|
|
||||||
|
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
|
||||||
|
private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
private static extern bool IsWindowVisible(IntPtr hWnd);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
|
||||||
|
|
||||||
private readonly HashSet<string> _systemProcessList = new HashSet<string>()
|
private readonly HashSet<string> _systemProcessList = new HashSet<string>()
|
||||||
{
|
{
|
||||||
"conhost",
|
"conhost",
|
||||||
|
|
@ -60,6 +74,34 @@ namespace Flow.Launcher.Plugin.ProcessKiller
|
||||||
return processlist;
|
return processlist;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a dictionary of process IDs and their window titles for processes that have a visible main window with a non-empty title.
|
||||||
|
/// </summary>
|
||||||
|
public Dictionary<int, string> GetProcessesWithNonEmptyWindowTitle()
|
||||||
|
{
|
||||||
|
var processDict = new Dictionary<int, string>();
|
||||||
|
EnumWindows((hWnd, lParam) =>
|
||||||
|
{
|
||||||
|
StringBuilder windowTitle = new StringBuilder();
|
||||||
|
GetWindowText(hWnd, windowTitle, windowTitle.Capacity);
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(windowTitle.ToString()) && IsWindowVisible(hWnd))
|
||||||
|
{
|
||||||
|
GetWindowThreadProcessId(hWnd, out var processId);
|
||||||
|
var process = Process.GetProcessById((int)processId);
|
||||||
|
|
||||||
|
if (!processDict.ContainsKey((int)processId))
|
||||||
|
{
|
||||||
|
processDict.Add((int)processId, windowTitle.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}, IntPtr.Zero);
|
||||||
|
|
||||||
|
return processDict;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns all non-system processes whose file path matches the given processPath
|
/// Returns all non-system processes whose file path matches the given processPath
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue