mid merge -- doesn't build

This commit is contained in:
Jordi Adoumie
2024-11-24 16:34:24 -08:00
parent 3ce188d47a
commit 7f026592bf
15 changed files with 1744 additions and 2 deletions

View File

@@ -0,0 +1,35 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.CmdPal.Ext.WindowWalker.Components;
using Microsoft.CmdPal.Extensions;
using Microsoft.CmdPal.Extensions.Helpers;
namespace Microsoft.CmdPal.Ext.WindowWalker.Commands;
internal sealed partial class CloseWindowCommand : InvokableCommand
{
private readonly Window _window;
public CloseWindowCommand(Window window)
{
_window = window;
}
public override ICommandResult Invoke()
{
if (!_window.IsWindow)
{
ExtensionHost.LogMessage(new LogMessage() { Message = $"Can not close the window '{_window.Title}' ({_window.Hwnd}), because it doesn't exist." });
}
_window.CloseThisWindow();
return CommandResult.Dismiss();
}
}

View File

@@ -0,0 +1,91 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Input;
using Microsoft.CmdPal.Ext.WindowWalker.Commands;
using Microsoft.CmdPal.Ext.WindowWalker.Helpers;
using Microsoft.CmdPal.Ext.WindowWalker.Properties;
using Microsoft.CmdPal.Extensions.Helpers;
namespace Microsoft.CmdPal.Ext.WindowWalker.Components;
internal class ContextMenuHelper
{
internal static List<CommandContextItem> GetContextMenuResults(in WindowWalkerListItem listItem)
{
if (!(listItem?.Window is Window windowData))
{
return new List<CommandContextItem>(0);
}
var contextMenu = new List<CommandContextItem>()
{
new(new CloseWindowCommand(windowData))
{
// AcceleratorKey = Key.F4,
// AcceleratorModifiers = ModifierKeys.Control,
Icon = new("\xE8BB"),
Title = $"{Resources.wox_plugin_windowwalker_Close} (Ctrl+F4)",
},
};
// Hide menu if Explorer.exe is the shell process or the process name is ApplicationFrameHost.exe
// In the first case we would crash the windows ui and in the second case we would kill the generic process for uwp apps.
if (!windowData.Process.IsShellProcess && !(windowData.Process.IsUwpApp && string.Equals(windowData.Process.Name, "ApplicationFrameHost.exe", StringComparison.OrdinalIgnoreCase))
&& !(windowData.Process.IsFullAccessDenied && SettingsManager.Instance.HideKillProcessOnElevatedProcesses))
{
contextMenu.Add(new ContextMenuResult
{
AcceleratorKey = Key.Delete,
AcceleratorModifiers = ModifierKeys.Control,
FontFamily = "Segoe Fluent Icons,Segoe MDL2 Assets",
Glyph = "\xE74D", // E74D => Symbol: Delete
Title = $"{Resources.wox_plugin_windowwalker_Kill} (Ctrl+Delete)",
Action = _ => KillProcessCommand(windowData),
});
}
return contextMenu;
}
/// <summary>
/// Method to initiate killing the process of a window
/// </summary>
/// <param name="window">Window data</param>
/// <returns>True if the PT Run window should close, otherwise false.</returns>
private static bool KillProcessCommand(Window window)
{
// Validate process
if (!window.IsWindow || !window.Process.DoesExist || !window.Process.Name.Equals(WindowProcess.GetProcessNameFromProcessID(window.Process.ProcessID), StringComparison.Ordinal))
{
Log.Debug($"Can not kill process '{window.Process.Name}' ({window.Process.ProcessID}) of the window '{window.Title}' ({window.Hwnd}), because it doesn't exist.", typeof(ContextMenuHelper));
return false;
}
// Request user confirmation
if (WindowWalkerSettings.Instance.ConfirmKillProcess)
{
string messageBody = $"{Resources.wox_plugin_windowwalker_KillMessage}\n"
+ $"{window.Process.Name} ({window.Process.ProcessID})\n\n"
+ $"{(window.Process.IsUwpApp ? Resources.wox_plugin_windowwalker_KillMessageUwp : Resources.wox_plugin_windowwalker_KillMessageQuestion)}";
MessageBoxResult messageBoxResult = MessageBox.Show(
messageBody,
Resources.wox_plugin_windowwalker_plugin_name + " - " + Resources.wox_plugin_windowwalker_KillMessageTitle,
MessageBoxButton.YesNo,
MessageBoxImage.Warning);
if (messageBoxResult == MessageBoxResult.No)
{
return false;
}
}
// Kill process
window.Process.KillThisProcess(WindowWalkerSettings.Instance.KillProcessTree);
return !WindowWalkerSettings.Instance.OpenAfterKillAndClose;
}
}

View File

@@ -0,0 +1,135 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
// Code forked from Betsegaw Tadele's https://github.com/betsegaw/windowwalker/
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
namespace Microsoft.Plugin.WindowWalker.Components
{
/// <summary>
/// Class housing fuzzy matching methods
/// </summary>
internal static class FuzzyMatching
{
/// <summary>
/// Finds the best match (the one with the most
/// number of letters adjacent to each other) and
/// returns the index location of each of the letters
/// of the matches
/// </summary>
/// <param name="text">The text to search inside of</param>
/// <param name="searchText">the text to search for</param>
/// <returns>returns the index location of each of the letters of the matches</returns>
internal static List<int> FindBestFuzzyMatch(string text, string searchText)
{
ArgumentNullException.ThrowIfNull(searchText);
ArgumentNullException.ThrowIfNull(text);
// Using CurrentCulture since this is user facing
searchText = searchText.ToLower(CultureInfo.CurrentCulture);
text = text.ToLower(CultureInfo.CurrentCulture);
// Create a grid to march matches like
// eg.
// a b c a d e c f g
// a x x
// c x x
bool[,] matches = new bool[text.Length, searchText.Length];
for (int firstIndex = 0; firstIndex < text.Length; firstIndex++)
{
for (int secondIndex = 0; secondIndex < searchText.Length; secondIndex++)
{
matches[firstIndex, secondIndex] =
searchText[secondIndex] == text[firstIndex] ?
true :
false;
}
}
// use this table to get all the possible matches
List<List<int>> allMatches = GetAllMatchIndexes(matches);
// return the score that is the max
int maxScore = allMatches.Count > 0 ? CalculateScoreForMatches(allMatches[0]) : 0;
List<int> bestMatch = allMatches.Count > 0 ? allMatches[0] : new List<int>();
foreach (var match in allMatches)
{
int score = CalculateScoreForMatches(match);
if (score > maxScore)
{
bestMatch = match;
maxScore = score;
}
}
return bestMatch;
}
/// <summary>
/// Gets all the possible matches to the search string with in the text
/// </summary>
/// <param name="matches"> a table showing the matches as generated by
/// a two dimensional array with the first dimension the text and the second
/// one the search string and each cell marked as an intersection between the two</param>
/// <returns>a list of the possible combinations that match the search text</returns>
internal static List<List<int>> GetAllMatchIndexes(bool[,] matches)
{
ArgumentNullException.ThrowIfNull(matches);
List<List<int>> results = new List<List<int>>();
for (int secondIndex = 0; secondIndex < matches.GetLength(1); secondIndex++)
{
for (int firstIndex = 0; firstIndex < matches.GetLength(0); firstIndex++)
{
if (secondIndex == 0 && matches[firstIndex, secondIndex])
{
results.Add(new List<int> { firstIndex });
}
else if (matches[firstIndex, secondIndex])
{
var tempList = results.Where(x => x.Count == secondIndex && x[x.Count - 1] < firstIndex).Select(x => x.ToList()).ToList();
foreach (var pathSofar in tempList)
{
pathSofar.Add(firstIndex);
}
results.AddRange(tempList);
}
}
results = results.Where(x => x.Count == secondIndex + 1).ToList();
}
return results.Where(x => x.Count == matches.GetLength(1)).ToList();
}
/// <summary>
/// Calculates the score for a string
/// </summary>
/// <param name="matches">the index of the matches</param>
/// <returns>an integer representing the score</returns>
internal static int CalculateScoreForMatches(List<int> matches)
{
ArgumentNullException.ThrowIfNull(matches);
var score = 0;
for (int currentIndex = 1; currentIndex < matches.Count; currentIndex++)
{
var previousIndex = currentIndex - 1;
score -= matches[currentIndex] - matches[previousIndex];
}
return score == 0 ? -10000 : score;
}
}
}

View File

@@ -0,0 +1,60 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
// Code forked from Betsegaw Tadele's https://github.com/betsegaw/windowwalker/
using System;
using Wox.Plugin.Common.Win32;
namespace Microsoft.Plugin.WindowWalker.Components
{
/// <summary>
/// Class containing methods to control the live preview
/// </summary>
internal class LivePreview
{
/// <summary>
/// Makes sure that a window is excluded from the live preview
/// </summary>
/// <param name="hwnd">handle to the window to exclude</param>
public static void SetWindowExclusionFromLivePreview(IntPtr hwnd)
{
uint renderPolicy = (uint)DwmNCRenderingPolicies.Enabled;
_ = NativeMethods.DwmSetWindowAttribute(
hwnd,
12,
ref renderPolicy,
sizeof(uint));
}
/// <summary>
/// Activates the live preview
/// </summary>
/// <param name="targetWindow">the window to show by making all other windows transparent</param>
/// <param name="windowToSpare">the window which should not be transparent but is not the target window</param>
public static void ActivateLivePreview(IntPtr targetWindow, IntPtr windowToSpare)
{
_ = NativeMethods.DwmpActivateLivePreview(
true,
targetWindow,
windowToSpare,
LivePreviewTrigger.Superbar,
IntPtr.Zero);
}
/// <summary>
/// Deactivates the live preview
/// </summary>
public static void DeactivateLivePreview()
{
_ = NativeMethods.DwmpActivateLivePreview(
false,
IntPtr.Zero,
IntPtr.Zero,
LivePreviewTrigger.AltTab,
IntPtr.Zero);
}
}
}

View File

@@ -0,0 +1,133 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
// Code forked from Betsegaw Tadele's https://github.com/betsegaw/windowwalker/
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using Wox.Plugin.Common.Win32;
namespace Microsoft.Plugin.WindowWalker.Components
{
/// <summary>
/// Class that represents the state of the desktops windows
/// </summary>
internal class OpenWindows
{
/// <summary>
/// Used to enforce single execution of EnumWindows
/// </summary>
private static readonly object _enumWindowsLock = new();
/// <summary>
/// PowerLauncher main executable
/// </summary>
private static readonly string _powerLauncherExe = Path.GetFileName(Environment.ProcessPath);
/// <summary>
/// List of all the open windows
/// </summary>
private readonly List<Window> windows = new List<Window>();
/// <summary>
/// An instance of the class OpenWindows
/// </summary>
private static OpenWindows instance;
/// <summary>
/// Gets the list of all open windows
/// </summary>
internal List<Window> Windows => new List<Window>(windows);
/// <summary>
/// Gets an instance property of this class that makes sure that
/// the first instance gets created and that all the requests
/// end up at that one instance
/// </summary>
internal static OpenWindows Instance
{
get
{
if (instance == null)
{
instance = new OpenWindows();
}
return instance;
}
}
/// <summary>
/// Initializes a new instance of the <see cref="OpenWindows"/> class.
/// Private constructor to make sure there is never
/// more than one instance of this class
/// </summary>
private OpenWindows()
{
}
/// <summary>
/// Updates the list of open windows
/// </summary>
internal void UpdateOpenWindowsList(CancellationToken cancellationToken)
{
var tokenHandle = GCHandle.Alloc(cancellationToken);
try
{
var tokenHandleParam = GCHandle.ToIntPtr(tokenHandle);
lock (_enumWindowsLock)
{
windows.Clear();
EnumWindowsProc callbackptr = new EnumWindowsProc(WindowEnumerationCallBack);
_ = NativeMethods.EnumWindows(callbackptr, tokenHandleParam);
}
}
finally
{
if (tokenHandle.IsAllocated)
{
tokenHandle.Free();
}
}
}
/// <summary>
/// Call back method for window enumeration
/// </summary>
/// <param name="hwnd">The handle to the current window being enumerated</param>
/// <param name="lParam">Value being passed from the caller (we don't use this but might come in handy
/// in the future</param>
/// <returns>true to make sure to continue enumeration</returns>
internal bool WindowEnumerationCallBack(IntPtr hwnd, IntPtr lParam)
{
var tokenHandle = GCHandle.FromIntPtr(lParam);
var cancellationToken = (CancellationToken)tokenHandle.Target;
if (cancellationToken.IsCancellationRequested)
{
// Stop enumeration
return false;
}
Window newWindow = new Window(hwnd);
if (newWindow.IsWindow && newWindow.Visible && newWindow.IsOwner &&
(!newWindow.IsToolWindow || newWindow.IsAppWindow) && !newWindow.TaskListDeleted &&
(newWindow.Desktop.IsVisible || !WindowWalkerSettings.Instance.ResultsFromVisibleDesktopOnly || Main.VirtualDesktopHelperInstance.GetDesktopCount() < 2) &&
newWindow.ClassName != "Windows.UI.Core.CoreWindow" && newWindow.Process.Name != _powerLauncherExe)
{
// To hide (not add) preloaded uwp app windows that are invisible to the user and other cloaked windows, we check the cloak state. (Issue #13637.)
// (If user asking to see cloaked uwp app windows again we can add an optional plugin setting in the future.)
if (!newWindow.IsCloaked || newWindow.GetWindowCloakState() == Window.WindowCloakState.OtherDesktop)
{
windows.Add(newWindow);
}
}
return true;
}
}
}

View File

@@ -0,0 +1,185 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Plugin.WindowWalker.Properties;
using Wox.Infrastructure;
using Wox.Plugin;
namespace Microsoft.Plugin.WindowWalker.Components
{
/// <summary>
/// Helper class to work with results
/// </summary>
internal static class ResultHelper
{
/// <summary>
/// Returns a list of all results for the query.
/// </summary>
/// <param name="searchControllerResults">List with all search controller matches</param>
/// <param name="icon">The path to the result icon</param>
/// <returns>List of results</returns>
internal static List<Result> GetResultList(List<SearchResult> searchControllerResults, bool isKeywordSearch, string icon, string infoIcon)
{
if (searchControllerResults == null || searchControllerResults.Count == 0)
{
return new List<Result>();
}
List<Result> resultsList = new List<Result>(searchControllerResults.Count);
bool addExplorerInfo = searchControllerResults.Any(x =>
string.Equals(x.Result.Process.Name, "explorer.exe", StringComparison.OrdinalIgnoreCase) &&
x.Result.Process.IsShellProcess);
// Process each SearchResult to convert it into a Result.
// Using parallel processing if the operation is CPU-bound and the list is large.
resultsList = searchControllerResults
.AsParallel()
.Select(x => CreateResultFromSearchResult(x, icon))
.ToList();
if (addExplorerInfo && isKeywordSearch && !WindowWalkerSettings.Instance.HideExplorerSettingInfo)
{
resultsList.Add(GetExplorerInfoResult(infoIcon));
}
return resultsList;
}
/// <summary>
/// Creates a Result object from a given SearchResult.
/// </summary>
/// <param name="searchResult">The SearchResult object to convert.</param>
/// <param name="icon">The path to the icon that should be used for the Result.</param>
/// <returns>A Result object populated with data from the SearchResult.</returns>
private static Result CreateResultFromSearchResult(SearchResult searchResult, string icon)
{
return new Result
{
Title = searchResult.Result.Title,
IcoPath = icon,
SubTitle = GetSubtitle(searchResult.Result),
ContextData = searchResult.Result,
Action = c =>
{
searchResult.Result.SwitchToWindow();
return true;
},
// For debugging you can set the second parameter to true to see more information.
ToolTipData = GetToolTip(searchResult.Result, false),
};
}
/// <summary>
/// Returns the subtitle for a result
/// </summary>
/// <param name="window">The window properties of the result</param>
/// <returns>String with the subtitle</returns>
private static string GetSubtitle(Window window)
{
if (window == null || !(window is Window))
{
return string.Empty;
}
string subtitleText = Resources.wox_plugin_windowwalker_Running + ": " + window.Process.Name;
if (WindowWalkerSettings.Instance.SubtitleShowPid)
{
subtitleText += $" ({window.Process.ProcessID})";
}
if (!window.Process.IsResponding)
{
subtitleText += $" [{Resources.wox_plugin_windowwalker_NotResponding}]";
}
if (WindowWalkerSettings.Instance.SubtitleShowDesktopName && Main.VirtualDesktopHelperInstance.GetDesktopCount() > 1)
{
subtitleText += $" - {Resources.wox_plugin_windowwalker_Desktop}: {window.Desktop.Name}";
}
return subtitleText;
}
/// <summary>
/// Returns the tool tip for a result
/// </summary>
/// <param name="window">The window properties of the result</param>
/// <param name="debugToolTip">Value indicating if a detailed debug tooltip should be returned</param>
/// <returns>Tooltip for the result or null of failure</returns>
private static ToolTipData GetToolTip(Window window, bool debugToolTip)
{
if (window == null || !(window is Window))
{
return null;
}
if (!debugToolTip)
{
string text = $"{Resources.wox_plugin_windowwalker_Process}: {window.Process.Name}";
text += $"\n{Resources.wox_plugin_windowwalker_ProcessId}: {window.Process.ProcessID}";
if (Main.VirtualDesktopHelperInstance.GetDesktopCount() > 1)
{
text += $"\n{Resources.wox_plugin_windowwalker_Desktop}: {window.Desktop.Name}";
if (!window.Desktop.IsAllDesktopsView)
{
text += $" ({Resources.wox_plugin_windowwalker_Number} {window.Desktop.Number})";
}
}
return new ToolTipData(window.Title, text);
}
else
{
string text = $"hWnd: {window.Hwnd}\n" +
$"Window class: {window.ClassName}\n" +
$"Process ID: {window.Process.ProcessID}\n" +
$"Thread ID: {window.Process.ThreadID}\n" +
$"Process: {window.Process.Name}\n" +
$"Process exists: {window.Process.DoesExist}\n" +
$"Is full access denied: {window.Process.IsFullAccessDenied}\n" +
$"Is uwp app: {window.Process.IsUwpApp}\n" +
$"Is ShellProcess: {window.Process.IsShellProcess}\n" +
$"Is window cloaked: {window.IsCloaked}\n" +
$"Window cloak state: {window.GetWindowCloakState()}\n" +
$"Desktop id: {window.Desktop.Id}\n" +
$"Desktop name: {window.Desktop.Name}\n" +
$"Desktop number: {window.Desktop.Number}\n" +
$"Desktop is visible: {window.Desktop.IsVisible}\n" +
$"Desktop position: {window.Desktop.Position}\n" +
$"Is AllDesktops view: {window.Desktop.IsAllDesktopsView}\n" +
$"Responding: {window.Process.IsResponding}";
return new ToolTipData(window.Title, text);
}
}
/// <summary>
/// Returns an information result about the explorer setting
/// </summary>
/// <param name="iIcon">The path to the info icon.</param>
/// <returns>An object of the type <see cref="Result"/> with the information.</returns>
private static Result GetExplorerInfoResult(string iIcon)
{
return new Result()
{
Title = Resources.wox_plugin_windowwalker_ExplorerInfoTitle,
IcoPath = iIcon,
SubTitle = Resources.wox_plugin_windowwalker_ExplorerInfoSubTitle,
Action = c =>
{
Helper.OpenInShell("rundll32.exe", "shell32.dll,Options_RunDLL 7"); // "shell32.dll,Options_RunDLL 7" opens the view tab in folder options of explorer.
return true;
},
Score = 100_000,
};
}
}
}

View File

@@ -0,0 +1,165 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
// Code forked from Betsegaw Tadele's https://github.com/betsegaw/windowwalker/
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
namespace Microsoft.Plugin.WindowWalker.Components
{
/// <summary>
/// Responsible for searching and finding matches for the strings provided.
/// Essentially the UI independent model of the application
/// </summary>
internal class SearchController
{
/// <summary>
/// the current search text
/// </summary>
private string searchText;
/// <summary>
/// Open window search results
/// </summary>
private List<SearchResult> searchMatches;
/// <summary>
/// Singleton pattern
/// </summary>
private static SearchController instance;
/// <summary>
/// Gets or sets the current search text
/// </summary>
internal string SearchText
{
get
{
return searchText;
}
set
{
// Using CurrentCulture since this is user facing
searchText = value.ToLower(CultureInfo.CurrentCulture).Trim();
}
}
/// <summary>
/// Gets the open window search results
/// </summary>
internal List<SearchResult> SearchMatches
{
get { return new List<SearchResult>(searchMatches).OrderByDescending(x => x.Score).ToList(); }
}
/// <summary>
/// Gets singleton Pattern
/// </summary>
internal static SearchController Instance
{
get
{
if (instance == null)
{
instance = new SearchController();
}
return instance;
}
}
/// <summary>
/// Initializes a new instance of the <see cref="SearchController"/> class.
/// Initializes the search controller object
/// </summary>
private SearchController()
{
searchText = string.Empty;
}
/// <summary>
/// Event handler for when the search text has been updated
/// </summary>
internal void UpdateSearchText(string searchText)
{
SearchText = searchText;
SyncOpenWindowsWithModel();
}
/// <summary>
/// Syncs the open windows with the OpenWindows Model
/// </summary>
internal void SyncOpenWindowsWithModel()
{
System.Diagnostics.Debug.Print("Syncing WindowSearch result with OpenWindows Model");
List<Window> snapshotOfOpenWindows = OpenWindows.Instance.Windows;
if (string.IsNullOrWhiteSpace(SearchText))
{
searchMatches = AllOpenWindows(snapshotOfOpenWindows);
}
else
{
searchMatches = FuzzySearchOpenWindows(snapshotOfOpenWindows);
}
}
/// <summary>
/// Search method that matches the title of windows with the user search text
/// </summary>
/// <param name="openWindows">what windows are open</param>
/// <returns>Returns search results</returns>
private List<SearchResult> FuzzySearchOpenWindows(List<Window> openWindows)
{
List<SearchResult> result = new List<SearchResult>();
var searchStrings = new SearchString(searchText, SearchResult.SearchType.Fuzzy);
foreach (var window in openWindows)
{
var titleMatch = FuzzyMatching.FindBestFuzzyMatch(window.Title, searchStrings.SearchText);
var processMatch = FuzzyMatching.FindBestFuzzyMatch(window.Process.Name, searchStrings.SearchText);
if ((titleMatch.Count != 0 || processMatch.Count != 0) && window.Title.Length != 0)
{
result.Add(new SearchResult(window, titleMatch, processMatch, searchStrings.SearchType));
}
}
System.Diagnostics.Debug.Print("Found " + result.Count + " windows that match the search text");
return result;
}
/// <summary>
/// Search method that matches all the windows with a title
/// </summary>
/// <param name="openWindows">what windows are open</param>
/// <returns>Returns search results</returns>
private List<SearchResult> AllOpenWindows(List<Window> openWindows)
{
List<SearchResult> result = new List<SearchResult>();
foreach (var window in openWindows)
{
if (window.Title.Length != 0)
{
result.Add(new SearchResult(window));
}
}
return result.OrderBy(w => w.Result.Title).ToList();
}
/// <summary>
/// Event args for a window list update event
/// </summary>
internal class SearchResultUpdateEventArgs : EventArgs
{
}
}
}

View File

@@ -0,0 +1,148 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
// Code forked from Betsegaw Tadele's https://github.com/betsegaw/windowwalker/
using System.Collections.Generic;
namespace Microsoft.Plugin.WindowWalker.Components
{
/// <summary>
/// Contains search result windows with each window including the reason why the result was included
/// </summary>
internal class SearchResult
{
/// <summary>
/// Gets the actual window reference for the search result
/// </summary>
internal Window Result
{
get;
private set;
}
/// <summary>
/// Gets the list of indexes of the matching characters for the search in the title window
/// </summary>
internal List<int> SearchMatchesInTitle
{
get;
private set;
}
/// <summary>
/// Gets the list of indexes of the matching characters for the search in the
/// name of the process
/// </summary>
internal List<int> SearchMatchesInProcessName
{
get;
private set;
}
/// <summary>
/// Gets the type of match (shortcut, fuzzy or nothing)
/// </summary>
internal SearchType SearchResultMatchType
{
get;
private set;
}
/// <summary>
/// Gets a score indicating how well this matches what we are looking for
/// </summary>
internal int Score
{
get;
private set;
}
/// <summary>
/// Gets the source of where the best score was found
/// </summary>
internal TextType BestScoreSource
{
get;
private set;
}
/// <summary>
/// Initializes a new instance of the <see cref="SearchResult"/> class.
/// Constructor
/// </summary>
internal SearchResult(Window window, List<int> matchesInTitle, List<int> matchesInProcessName, SearchType matchType)
{
Result = window;
SearchMatchesInTitle = matchesInTitle;
SearchMatchesInProcessName = matchesInProcessName;
SearchResultMatchType = matchType;
CalculateScore();
}
/// <summary>
/// Initializes a new instance of the <see cref="SearchResult"/> class.
/// </summary>
internal SearchResult(Window window)
{
Result = window;
SearchMatchesInTitle = new List<int>();
SearchMatchesInProcessName = new List<int>();
SearchResultMatchType = SearchType.Empty;
CalculateScore();
}
/// <summary>
/// Calculates the score for how closely this window matches the search string
/// </summary>
/// <remarks>
/// Higher Score is better
/// </remarks>
private void CalculateScore()
{
if (FuzzyMatching.CalculateScoreForMatches(SearchMatchesInProcessName) >
FuzzyMatching.CalculateScoreForMatches(SearchMatchesInTitle))
{
Score = FuzzyMatching.CalculateScoreForMatches(SearchMatchesInProcessName);
BestScoreSource = TextType.ProcessName;
}
else
{
Score = FuzzyMatching.CalculateScoreForMatches(SearchMatchesInTitle);
BestScoreSource = TextType.WindowTitle;
}
}
/// <summary>
/// The type of text that a string represents
/// </summary>
internal enum TextType
{
ProcessName,
WindowTitle,
}
/// <summary>
/// The type of search
/// </summary>
internal enum SearchType
{
/// <summary>
/// the search string is empty, which means all open windows are
/// going to be returned
/// </summary>
Empty,
/// <summary>
/// Regular fuzzy match search
/// </summary>
Fuzzy,
/// <summary>
/// The user has entered text that has been matched to a shortcut
/// and the shortcut is now being searched
/// </summary>
Shortcut,
}
}
}

View File

@@ -0,0 +1,46 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
// Code forked from Betsegaw Tadele's https://github.com/betsegaw/windowwalker/
namespace Microsoft.Plugin.WindowWalker.Components
{
/// <summary>
/// A class to represent a search string
/// </summary>
/// <remarks>Class was added inorder to be able to attach various context data to
/// a search string</remarks>
internal class SearchString
{
/// <summary>
/// Gets where is the search string coming from (is it a shortcut
/// or direct string, etc...)
/// </summary>
internal SearchResult.SearchType SearchType
{
get;
private set;
}
/// <summary>
/// Gets the actual text we are searching for
/// </summary>
internal string SearchText
{
get;
private set;
}
/// <summary>
/// Initializes a new instance of the <see cref="SearchString"/> class.
/// Constructor
/// </summary>
/// <param name="searchText">text from search</param>
/// <param name="searchType">type of search</param>
internal SearchString(string searchText, SearchResult.SearchType searchType)
{
SearchText = searchText;
SearchType = searchType;
}
}
}

View File

@@ -0,0 +1,422 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
// Code forked from Betsegaw Tadele's https://github.com/betsegaw/windowwalker/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Wox.Plugin.Common.VirtualDesktop.Helper;
using Wox.Plugin.Common.Win32;
using Wox.Plugin.Logger;
namespace Microsoft.CmdPal.Ext.WindowWalker.Components;
/// <summary>
/// Represents a specific open window
/// </summary>
internal class Window
{
/// <summary>
/// The handle to the window
/// </summary>
private readonly IntPtr hwnd;
/// <summary>
/// A static cache for the process data of all known windows
/// that we don't have to query the data every time
/// </summary>
private static readonly Dictionary<IntPtr, WindowProcess> _handlesToProcessCache = new Dictionary<IntPtr, WindowProcess>();
/// <summary>
/// An instance of <see cref="WindowProcess"/> that contains the process information for the window
/// </summary>
private readonly WindowProcess processInfo;
/// <summary>
/// An instance of <see cref="VDesktop"/> that contains the desktop information for the window
/// </summary>
private readonly VDesktop desktopInfo;
/// <summary>
/// Gets the title of the window (the string displayed at the top of the window)
/// </summary>
internal string Title
{
get
{
int sizeOfTitle = NativeMethods.GetWindowTextLength(hwnd);
if (sizeOfTitle++ > 0)
{
StringBuilder titleBuffer = new StringBuilder(sizeOfTitle);
var numCharactersWritten = NativeMethods.GetWindowText(hwnd, titleBuffer, sizeOfTitle);
if (numCharactersWritten == 0)
{
return string.Empty;
}
return titleBuffer.ToString();
}
else
{
return string.Empty;
}
}
}
/// <summary>
/// Gets the handle to the window
/// </summary>
internal IntPtr Hwnd
{
get { return hwnd; }
}
/// <summary>
/// Gets the object of with the process information of the window
/// </summary>
internal WindowProcess Process
{
get { return processInfo; }
}
/// <summary>
/// Gets the object of with the desktop information of the window
/// </summary>
internal VDesktop Desktop
{
get { return desktopInfo; }
}
/// <summary>
/// Gets the name of the class for the window represented
/// </summary>
internal string ClassName
{
get
{
return GetWindowClassName(Hwnd);
}
}
/// <summary>
/// Gets a value indicating whether the window is visible (might return false if it is a hidden IE tab)
/// </summary>
internal bool Visible
{
get
{
return NativeMethods.IsWindowVisible(Hwnd);
}
}
/// <summary>
/// Gets a value indicating whether the window is cloaked (true) or not (false).
/// (A cloaked window is not visible to the user. But the window is still composed by DWM.)
/// </summary>
internal bool IsCloaked
{
get
{
return GetWindowCloakState() != WindowCloakState.None;
}
}
/// <summary>
/// Gets a value indicating whether the specified window handle identifies an existing window.
/// </summary>
internal bool IsWindow
{
get
{
return NativeMethods.IsWindow(Hwnd);
}
}
/// <summary>
/// Gets a value indicating whether the window is a toolwindow
/// </summary>
internal bool IsToolWindow
{
get
{
return (NativeMethods.GetWindowLong(Hwnd, Win32Constants.GWL_EXSTYLE) &
(uint)ExtendedWindowStyles.WS_EX_TOOLWINDOW) ==
(uint)ExtendedWindowStyles.WS_EX_TOOLWINDOW;
}
}
/// <summary>
/// Gets a value indicating whether the window is an appwindow
/// </summary>
internal bool IsAppWindow
{
get
{
return (NativeMethods.GetWindowLong(Hwnd, Win32Constants.GWL_EXSTYLE) &
(uint)ExtendedWindowStyles.WS_EX_APPWINDOW) ==
(uint)ExtendedWindowStyles.WS_EX_APPWINDOW;
}
}
/// <summary>
/// Gets a value indicating whether the window has ITaskList_Deleted property
/// </summary>
internal bool TaskListDeleted
{
get
{
return NativeMethods.GetProp(Hwnd, "ITaskList_Deleted") != IntPtr.Zero;
}
}
/// <summary>
/// Gets a value indicating whether the specified windows is the owner (i.e. doesn't have an owner)
/// </summary>
internal bool IsOwner
{
get
{
return NativeMethods.GetWindow(Hwnd, GetWindowCmd.GW_OWNER) == IntPtr.Zero;
}
}
/// <summary>
/// Gets a value indicating whether the window is minimized
/// </summary>
internal bool Minimized
{
get
{
return GetWindowSizeState() == WindowSizeState.Minimized;
}
}
/// <summary>
/// Initializes a new instance of the <see cref="Window"/> class.
/// Initializes a new Window representation
/// </summary>
/// <param name="hwnd">the handle to the window we are representing</param>
internal Window(IntPtr hwnd)
{
// TODO: Add verification as to whether the window handle is valid
this.hwnd = hwnd;
processInfo = CreateWindowProcessInstance(hwnd);
desktopInfo = Main.VirtualDesktopHelperInstance.GetWindowDesktop(hwnd);
}
/// <summary>
/// Switches desktop focus to the window
/// </summary>
internal void SwitchToWindow()
{
// The following block is necessary because
// 1) There is a weird flashing behavior when trying
// to use ShowWindow for switching tabs in IE
// 2) SetForegroundWindow fails on minimized windows
// Using Ordinal since this is internal
if (processInfo.Name.ToUpperInvariant().Equals("IEXPLORE.EXE", StringComparison.Ordinal) || !Minimized)
{
NativeMethods.SetForegroundWindow(Hwnd);
}
else
{
if (!NativeMethods.ShowWindow(Hwnd, ShowWindowCommand.Restore))
{
// ShowWindow doesn't work if the process is running elevated: fallback to SendMessage
_ = NativeMethods.SendMessage(Hwnd, Win32Constants.WM_SYSCOMMAND, Win32Constants.SC_RESTORE);
}
}
NativeMethods.FlashWindow(Hwnd, true);
}
/// <summary>
/// Helper function to close the window
/// </summary>
internal void CloseThisWindowHelper()
{
_ = NativeMethods.SendMessageTimeout(Hwnd, Win32Constants.WM_SYSCOMMAND, Win32Constants.SC_CLOSE, 0, 0x0000, 5000, out _);
}
/// <summary>
/// Closes the window
/// </summary>
internal void CloseThisWindow()
{
Thread thread = new(new ThreadStart(CloseThisWindowHelper));
thread.Start();
}
/// <summary>
/// Converts the window name to string along with the process name
/// </summary>
/// <returns>The title of the window</returns>
public override string ToString()
{
// Using CurrentCulture since this is user facing
return Title + " (" + processInfo.Name.ToUpper(CultureInfo.CurrentCulture) + ")";
}
/// <summary>
/// Returns what the window size is
/// </summary>
/// <returns>The state (minimized, maximized, etc..) of the window</returns>
internal WindowSizeState GetWindowSizeState()
{
NativeMethods.GetWindowPlacement(Hwnd, out WINDOWPLACEMENT placement);
switch (placement.ShowCmd)
{
case ShowWindowCommand.Normal:
return WindowSizeState.Normal;
case ShowWindowCommand.Minimize:
case ShowWindowCommand.ShowMinimized:
return WindowSizeState.Minimized;
case ShowWindowCommand.Maximize: // No need for ShowMaximized here since its also of value 3
return WindowSizeState.Maximized;
default:
// throw new Exception("Don't know how to handle window state = " + placement.ShowCmd);
return WindowSizeState.Unknown;
}
}
/// <summary>
/// Enum to simplify the state of the window
/// </summary>
internal enum WindowSizeState
{
Normal,
Minimized,
Maximized,
Unknown,
}
/// <summary>
/// Returns the window cloak state from DWM
/// (A cloaked window is not visible to the user. But the window is still composed by DWM.)
/// </summary>
/// <returns>The state (none, app, ...) of the window</returns>
internal WindowCloakState GetWindowCloakState()
{
_ = NativeMethods.DwmGetWindowAttribute(Hwnd, (int)DwmWindowAttributes.Cloaked, out int isCloakedState, sizeof(uint));
switch (isCloakedState)
{
case (int)DwmWindowCloakStates.None:
return WindowCloakState.None;
case (int)DwmWindowCloakStates.CloakedApp:
return WindowCloakState.App;
case (int)DwmWindowCloakStates.CloakedShell:
return Main.VirtualDesktopHelperInstance.IsWindowCloakedByVirtualDesktopManager(hwnd, Desktop.Id) ? WindowCloakState.OtherDesktop : WindowCloakState.Shell;
case (int)DwmWindowCloakStates.CloakedInherited:
return WindowCloakState.Inherited;
default:
return WindowCloakState.Unknown;
}
}
/// <summary>
/// Enum to simplify the cloak state of the window
/// </summary>
internal enum WindowCloakState
{
None,
App,
Shell,
Inherited,
OtherDesktop,
Unknown,
}
/// <summary>
/// Returns the class name of a window.
/// </summary>
/// <param name="hwnd">Handle to the window.</param>
/// <returns>Class name</returns>
private static string GetWindowClassName(IntPtr hwnd)
{
StringBuilder windowClassName = new StringBuilder(300);
var numCharactersWritten = NativeMethods.GetClassName(hwnd, windowClassName, windowClassName.MaxCapacity);
if (numCharactersWritten == 0)
{
return string.Empty;
}
return windowClassName.ToString();
}
/// <summary>
/// Gets an instance of <see cref="WindowProcess"/> form process cache or creates a new one. A new one will be added to the cache.
/// </summary>
/// <param name="hWindow">The handle to the window</param>
/// <returns>A new Instance of type <see cref="WindowProcess"/></returns>
private static WindowProcess CreateWindowProcessInstance(IntPtr hWindow)
{
lock (_handlesToProcessCache)
{
if (_handlesToProcessCache.Count > 7000)
{
Debug.Print("Clearing Process Cache because it's size is " + _handlesToProcessCache.Count);
_handlesToProcessCache.Clear();
}
// Add window's process to cache if missing
if (!_handlesToProcessCache.ContainsKey(hWindow))
{
// Get process ID and name
var processId = WindowProcess.GetProcessIDFromWindowHandle(hWindow);
var threadId = WindowProcess.GetThreadIDFromWindowHandle(hWindow);
var processName = WindowProcess.GetProcessNameFromProcessID(processId);
if (processName.Length != 0)
{
_handlesToProcessCache.Add(hWindow, new WindowProcess(processId, threadId, processName));
}
else
{
// For the dwm process we can not receive the name. This is no problem because the window isn't part of result list.
Log.Debug($"Invalid process {processId} ({processName}) for window handle {hWindow}.", typeof(Window));
_handlesToProcessCache.Add(hWindow, new WindowProcess(0, 0, string.Empty));
}
}
// Correct the process data if the window belongs to a uwp app hosted by 'ApplicationFrameHost.exe'
// (This only works if the window isn't minimized. For minimized windows the required child window isn't assigned.)
if (string.Equals(_handlesToProcessCache[hWindow].Name, "ApplicationFrameHost.exe", StringComparison.OrdinalIgnoreCase))
{
new Task(() =>
{
EnumWindowsProc callbackptr = new EnumWindowsProc((IntPtr hwnd, IntPtr lParam) =>
{
// Every uwp app main window has at least three child windows. Only the one we are interested in has a class starting with "Windows.UI.Core." and is assigned to the real app process.
// (The other ones have a class name that begins with the string "ApplicationFrame".)
if (GetWindowClassName(hwnd).StartsWith("Windows.UI.Core.", StringComparison.OrdinalIgnoreCase))
{
var childProcessId = WindowProcess.GetProcessIDFromWindowHandle(hwnd);
var childThreadId = WindowProcess.GetThreadIDFromWindowHandle(hwnd);
var childProcessName = WindowProcess.GetProcessNameFromProcessID(childProcessId);
// Update process info in cache
_handlesToProcessCache[hWindow].UpdateProcessInfo(childProcessId, childThreadId, childProcessName);
return false;
}
else
{
return true;
}
});
_ = NativeMethods.EnumChildWindows(hWindow, callbackptr, 0);
}).Start();
}
return _handlesToProcessCache[hWindow];
}
}
}

View File

@@ -0,0 +1,244 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Wox.Infrastructure;
using Wox.Plugin.Common.Win32;
namespace Microsoft.Plugin.WindowWalker.Components
{
/// <summary>
/// Represents the process data of an open window. This class is used in the process cache and for the process object of the open window
/// </summary>
internal class WindowProcess
{
/// <summary>
/// Maximum size of a file name
/// </summary>
private const int MaximumFileNameLength = 1000;
/// <summary>
/// An indicator if the window belongs to an 'Universal Windows Platform (UWP)' process
/// </summary>
private readonly bool _isUwpApp;
/// <summary>
/// Gets the id of the process
/// </summary>
internal uint ProcessID
{
get; private set;
}
/// <summary>
/// Gets a value indicating whether the process is responding or not
/// </summary>
internal bool IsResponding
{
get
{
try
{
return Process.GetProcessById((int)ProcessID).Responding;
}
catch (InvalidOperationException)
{
// Thrown when process not exist.
return true;
}
catch (NotSupportedException)
{
// Thrown when process is not running locally.
return true;
}
}
}
/// <summary>
/// Gets the id of the thread
/// </summary>
internal uint ThreadID
{
get; private set;
}
/// <summary>
/// Gets the name of the process
/// </summary>
internal string Name
{
get; private set;
}
/// <summary>
/// Gets a value indicating whether the window belongs to an 'Universal Windows Platform (UWP)' process
/// </summary>
internal bool IsUwpApp
{
get { return _isUwpApp; }
}
/// <summary>
/// Gets a value indicating whether this is the shell process or not
/// The shell process (like explorer.exe) hosts parts of the user interface (like taskbar, start menu, ...)
/// </summary>
internal bool IsShellProcess
{
get
{
IntPtr hShellWindow = NativeMethods.GetShellWindow();
return GetProcessIDFromWindowHandle(hShellWindow) == ProcessID;
}
}
/// <summary>
/// Gets a value indicating whether the process exists on the machine
/// </summary>
internal bool DoesExist
{
get
{
try
{
var p = Process.GetProcessById((int)ProcessID);
p.Dispose();
return true;
}
catch (InvalidOperationException)
{
// Thrown when process not exist.
return false;
}
catch (ArgumentException)
{
// Thrown when process not exist.
return false;
}
}
}
/// <summary>
/// Gets a value indicating whether full access to the process is denied or not
/// </summary>
internal bool IsFullAccessDenied
{
get; private set;
}
/// <summary>
/// Initializes a new instance of the <see cref="WindowProcess"/> class.
/// </summary>
/// <param name="pid">New process id.</param>
/// <param name="tid">New thread id.</param>
/// <param name="name">New process name.</param>
internal WindowProcess(uint pid, uint tid, string name)
{
UpdateProcessInfo(pid, tid, name);
_isUwpApp = string.Equals(Name, "ApplicationFrameHost.exe", StringComparison.OrdinalIgnoreCase);
}
/// <summary>
/// Updates the process information of the <see cref="WindowProcess"/> instance.
/// </summary>
/// <param name="pid">New process id.</param>
/// <param name="tid">New thread id.</param>
/// <param name="name">New process name.</param>
internal void UpdateProcessInfo(uint pid, uint tid, string name)
{
// TODO: Add verification as to whether the process id and thread id is valid
ProcessID = pid;
ThreadID = tid;
Name = name;
// Process can be elevated only if process id is not 0 (Dummy value on error)
IsFullAccessDenied = (pid != 0) ? TestProcessAccessUsingAllAccessFlag(pid) : false;
}
/// <summary>
/// Gets the process ID for the window handle
/// </summary>
/// <param name="hwnd">The handle to the window</param>
/// <returns>The process ID</returns>
internal static uint GetProcessIDFromWindowHandle(IntPtr hwnd)
{
_ = NativeMethods.GetWindowThreadProcessId(hwnd, out uint processId);
return processId;
}
/// <summary>
/// Gets the thread ID for the window handle
/// </summary>
/// <param name="hwnd">The handle to the window</param>
/// <returns>The thread ID</returns>
internal static uint GetThreadIDFromWindowHandle(IntPtr hwnd)
{
uint threadId = NativeMethods.GetWindowThreadProcessId(hwnd, out _);
return threadId;
}
/// <summary>
/// Gets the process name for the process ID
/// </summary>
/// <param name="pid">The id of the process/param>
/// <returns>A string representing the process name or an empty string if the function fails</returns>
internal static string GetProcessNameFromProcessID(uint pid)
{
IntPtr processHandle = NativeMethods.OpenProcess(ProcessAccessFlags.QueryLimitedInformation, true, (int)pid);
StringBuilder processName = new StringBuilder(MaximumFileNameLength);
if (NativeMethods.GetProcessImageFileName(processHandle, processName, MaximumFileNameLength) != 0)
{
_ = Win32Helpers.CloseHandleIfNotNull(processHandle);
return processName.ToString().Split('\\').Reverse().ToArray()[0];
}
else
{
_ = Win32Helpers.CloseHandleIfNotNull(processHandle);
return string.Empty;
}
}
/// <summary>
/// Kills the process by it's id. If permissions are required, they will be requested.
/// </summary>
/// <param name="killProcessTree">Kill process and sub processes.</param>
internal void KillThisProcess(bool killProcessTree)
{
if (IsFullAccessDenied)
{
string killTree = killProcessTree ? " /t" : string.Empty;
Helper.OpenInShell("taskkill.exe", $"/pid {(int)ProcessID} /f{killTree}", null, Helper.ShellRunAsType.Administrator, true);
}
else
{
Process.GetProcessById((int)ProcessID).Kill(killProcessTree);
}
}
/// <summary>
/// Gets a boolean value indicating whether the access to a process using the AllAccess flag is denied or not.
/// </summary>
/// <param name="pid">The process ID of the process</param>
/// <returns>True if denied and false if not.</returns>
private static bool TestProcessAccessUsingAllAccessFlag(uint pid)
{
IntPtr processHandle = NativeMethods.OpenProcess(ProcessAccessFlags.AllAccess, true, (int)pid);
if (Win32Helpers.GetLastError() == 5)
{
// Error 5 = ERROR_ACCESS_DENIED
_ = Win32Helpers.CloseHandleIfNotNull(processHandle);
return true;
}
else
{
_ = Win32Helpers.CloseHandleIfNotNull(processHandle);
return false;
}
}
}
}

View File

@@ -19,13 +19,15 @@ public class SettingsManager
private readonly string _filePath;
private readonly Settings _settings = new();
private static SettingsManager? instance;
private readonly ToggleSetting _resultsFromVisibleDesktopOnly = new(nameof(ResultsFromVisibleDesktopOnly), Resources.wox_plugin_windowwalker_SettingResultsVisibleDesktop, Resources.wox_plugin_windowwalker_SettingResultsVisibleDesktop, false);
private readonly ToggleSetting _subtitleShowPid = new(nameof(SubtitleShowPid), Resources.wox_plugin_windowwalker_SettingSubtitlePid, Resources.wox_plugin_windowwalker_SettingSubtitlePid, false);
private readonly ToggleSetting _subtitleShowDesktopName = new(nameof(SubtitleShowDesktopName), Resources.wox_plugin_windowwalker_SettingSubtitleDesktopName, Resources.wox_plugin_windowwalker_SettingSubtitleDesktopName_Description, true);
private readonly ToggleSetting _confirmKillProcess = new(nameof(ConfirmKillProcess), Resources.wox_plugin_windowwalker_SettingConfirmKillProcess, Resources.wox_plugin_windowwalker_SettingConfirmKillProcess, true);
private readonly ToggleSetting _killProcessTree = new(nameof(KillProcessTree), Resources.wox_plugin_windowwalker_SettingKillProcessTree, Resources.wox_plugin_windowwalker_SettingKillProcessTree_Description, false);
private readonly ToggleSetting _openAfterKillAndClose = new(nameof(OpenAfterKillAndClose), Resources.wox_plugin_windowwalker_SettingOpenAfterKillAndClose, Resources.wox_plugin_windowwalker_SettingOpenAfterKillAndClose_Description, false);
private readonly ToggleSetting _hideKillProcessOnElevatedProcess = new(nameof(HideKillProcessOnElevatedProcess), Resources.wox_plugin_windowwalker_SettingHideKillProcess, Resources.wox_plugin_windowwalker_SettingHideKillProcess, false);
private readonly ToggleSetting _hideKillProcessOnElevatedProcesses = new(nameof(HideKillProcessOnElevatedProcesses), Resources.wox_plugin_windowwalker_SettingHideKillProcess, Resources.wox_plugin_windowwalker_SettingHideKillProcess, false);
private readonly ToggleSetting _hideExplorerSettingInfo = new(nameof(HideExplorerSettingInfo), Resources.wox_plugin_windowwalker_SettingExplorerSettingInfo, Resources.wox_plugin_windowwalker_SettingExplorerSettingInfo_Description, false);
public bool ResultsFromVisibleDesktopOnly => _resultsFromVisibleDesktopOnly.Value;
@@ -40,7 +42,7 @@ public class SettingsManager
public bool OpenAfterKillAndClose => _openAfterKillAndClose.Value;
public bool HideKillProcessOnElevatedProcess => _hideKillProcessOnElevatedProcess.Value;
public bool HideKillProcessOnElevatedProcesses => _hideKillProcessOnElevatedProcesses.Value;
public bool HideExplorerSettingInfo => _hideExplorerSettingInfo.Value;
@@ -79,6 +81,15 @@ public class SettingsManager
LoadSettings();
}
internal static SettingsManager Instance
{
get
{
instance ??= new SettingsManager();
return instance;
}
}
public Settings GetSettings()
{
return _settings;

View File

@@ -0,0 +1,37 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.CmdPal.Ext.WindowWalker.Helpers;
using Microsoft.CmdPal.Ext.WindowWalker.Properties;
using Microsoft.CmdPal.Extensions;
using Microsoft.CmdPal.Extensions.Helpers;
namespace Microsoft.CmdPal.Ext.WindowWalker.Pages;
internal sealed partial class SettingsPage : FormPage
{
private readonly Settings _settings;
private readonly SettingsManager _settingsManager;
public override IForm[] Forms()
{
var s = _settings.ToForms();
return s;
}
public SettingsPage(SettingsManager settingsManager)
{
Name = "Settings Page NEED TO MAKE RESOURCES FOR THIS";
Icon = new("\uE713"); // Settings icon
_settings = settingsManager.GetSettings();
_settingsManager = settingsManager;
_settings.SettingsChanged += SettingsChanged;
}
private void SettingsChanged(object sender, Settings args)
{
_settingsManager.SaveSettings();
}
}

View File

@@ -6,6 +6,7 @@ using System;
using System.Data;
using System.IO;
using Microsoft.CmdPal.Ext.WindowWalker.Helpers;
using Microsoft.CmdPal.Ext.WindowWalker.Pages;
using Microsoft.CmdPal.Ext.WindowWalker.Properties;
using Microsoft.CmdPal.Extensions;
using Microsoft.CmdPal.Extensions.Helpers;
@@ -19,5 +20,8 @@ public partial class WalkerTopLevelCommandItem : CommandItem
{
Title = Resources.window_walker_top_level_command_title;
Subtitle = Resources.wox_plugin_windowwalker_plugin_name;
MoreCommands = [
new CommandContextItem(new SettingsPage(settingsManager)),
];
}
}

View File

@@ -0,0 +1,26 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.CmdPal.Ext.WindowWalker.Components;
using Microsoft.CmdPal.Extensions.Helpers;
namespace Microsoft.CmdPal.Ext.WindowWalker;
internal partial class WindowWalkerListItem : ListItem
{
private readonly Window _window;
public Window Window => _window;
public WindowWalkerListItem(Window window)
: base(new NoOpCommand())
{
_window = window;
}
}