mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-07-04 17:39:57 +02:00
Compare commits
5 Commits
shawn/Pyth
...
dev/jpolas
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
76058650a9 | ||
|
|
795ecd79e9 | ||
|
|
78b67c3f58 | ||
|
|
d5f68760db | ||
|
|
26a4ab0d6a |
@@ -29,6 +29,11 @@ public class MockAppCache : IAppCache
|
||||
/// </summary>
|
||||
public IList<IUWPApplication> UWPs => _uwps.AsReadOnly();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether a background re-index is in progress.
|
||||
/// </summary>
|
||||
public bool IsIndexing => false;
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the cache should be reloaded.
|
||||
/// </summary>
|
||||
|
||||
@@ -13,6 +13,8 @@ public class Settings : ISettingsInterface
|
||||
private readonly bool enableDesktopSource;
|
||||
private readonly bool enableRegistrySource;
|
||||
private readonly bool enablePathEnvironmentVariableSource;
|
||||
private readonly bool includeNonAppsOnDesktop;
|
||||
private readonly bool includeNonAppsInStartMenu;
|
||||
private readonly List<string> programSuffixes;
|
||||
private readonly List<string> runCommandSuffixes;
|
||||
|
||||
@@ -21,6 +23,8 @@ public class Settings : ISettingsInterface
|
||||
bool enableDesktopSource = true,
|
||||
bool enableRegistrySource = true,
|
||||
bool enablePathEnvironmentVariableSource = true,
|
||||
bool includeNonAppsOnDesktop = false,
|
||||
bool includeNonAppsInStartMenu = true,
|
||||
List<string> programSuffixes = null,
|
||||
List<string> runCommandSuffixes = null)
|
||||
{
|
||||
@@ -28,6 +32,8 @@ public class Settings : ISettingsInterface
|
||||
this.enableDesktopSource = enableDesktopSource;
|
||||
this.enableRegistrySource = enableRegistrySource;
|
||||
this.enablePathEnvironmentVariableSource = enablePathEnvironmentVariableSource;
|
||||
this.includeNonAppsOnDesktop = includeNonAppsOnDesktop;
|
||||
this.includeNonAppsInStartMenu = includeNonAppsInStartMenu;
|
||||
this.programSuffixes = programSuffixes ?? new List<string> { "bat", "appref-ms", "exe", "lnk", "url" };
|
||||
this.runCommandSuffixes = runCommandSuffixes ?? new List<string> { "bat", "appref-ms", "exe", "lnk", "url", "cpl", "msc" };
|
||||
}
|
||||
@@ -40,6 +46,10 @@ public class Settings : ISettingsInterface
|
||||
|
||||
public bool EnablePathEnvironmentVariableSource => enablePathEnvironmentVariableSource;
|
||||
|
||||
public bool IncludeNonAppsOnDesktop => includeNonAppsOnDesktop;
|
||||
|
||||
public bool IncludeNonAppsInStartMenu => includeNonAppsInStartMenu;
|
||||
|
||||
public List<string> ProgramSuffixes => programSuffixes;
|
||||
|
||||
public List<string> RunCommandSuffixes => runCommandSuffixes;
|
||||
|
||||
@@ -21,13 +21,19 @@ public sealed partial class AllAppsPage : ListPage
|
||||
private readonly IAppCache _appCache;
|
||||
|
||||
private AppListItem[] allAppListItems = [];
|
||||
private volatile bool _isRebuilding;
|
||||
|
||||
public AllAppsPage()
|
||||
: this(AppCache.Instance.Value)
|
||||
: this(AppCache.Instance.Value, AllAppsSettings.Instance.Settings)
|
||||
{
|
||||
}
|
||||
|
||||
public AllAppsPage(IAppCache appCache)
|
||||
: this(appCache, new Settings())
|
||||
{
|
||||
}
|
||||
|
||||
public AllAppsPage(IAppCache appCache, Settings settings)
|
||||
{
|
||||
_appCache = appCache ?? throw new ArgumentNullException(nameof(appCache));
|
||||
this.Name = Resources.all_apps;
|
||||
@@ -43,12 +49,65 @@ public sealed partial class AllAppsPage : ListPage
|
||||
BuildListItems();
|
||||
}
|
||||
});
|
||||
|
||||
settings.SettingsChanged += (s, a) =>
|
||||
{
|
||||
if (_appCache.IsIndexing && !_isRebuilding)
|
||||
{
|
||||
// A background re-index is in progress. Show a loading
|
||||
// indicator and banner, then wait for it to finish.
|
||||
_isRebuilding = true;
|
||||
this.IsLoading = true;
|
||||
RaiseItemsChanged();
|
||||
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
while (_appCache.IsIndexing)
|
||||
{
|
||||
await Task.Delay(200).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
lock (_listLock)
|
||||
{
|
||||
allAppListItems = GetPrograms();
|
||||
_appCache.ResetReloadFlag();
|
||||
}
|
||||
|
||||
_isRebuilding = false;
|
||||
this.IsLoading = false;
|
||||
RaiseItemsChanged();
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
RaiseItemsChanged();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public override IListItem[] GetItems()
|
||||
{
|
||||
// Build or update the list if needed
|
||||
BuildListItems();
|
||||
if (!_isRebuilding)
|
||||
{
|
||||
// Build or update the list if needed
|
||||
BuildListItems();
|
||||
}
|
||||
|
||||
if (_isRebuilding && allAppListItems.Length > 0)
|
||||
{
|
||||
var banner = new ListItem(new NoOpCommand())
|
||||
{
|
||||
Title = Resources.refreshing_app_list,
|
||||
Icon = Icons.Reloading,
|
||||
};
|
||||
|
||||
var result = new IListItem[allAppListItems.Length + 3];
|
||||
result[0] = new Separator(Resources.section_status);
|
||||
result[1] = banner;
|
||||
result[2] = new Separator(Resources.section_all_apps);
|
||||
allAppListItems.CopyTo(result, 3);
|
||||
return result;
|
||||
}
|
||||
|
||||
return allAppListItems;
|
||||
}
|
||||
@@ -84,7 +143,7 @@ public sealed partial class AllAppsPage : ListPage
|
||||
{
|
||||
if (uwpApp.Enabled)
|
||||
{
|
||||
items.Add(new AppListItem(uwpApp.ToAppItem(), true));
|
||||
items.Add(new AppListItem(uwpApp.ToAppItem(), useThumbnails: true));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +151,7 @@ public sealed partial class AllAppsPage : ListPage
|
||||
{
|
||||
if (win32App.Enabled && win32App.Valid)
|
||||
{
|
||||
items.Add(new AppListItem(win32App.ToAppItem(), true));
|
||||
items.Add(new AppListItem(win32App.ToAppItem(), useThumbnails: true));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -62,6 +62,10 @@ public class AllAppsSettings : JsonSettingsManager, ISettingsInterface
|
||||
|
||||
public bool EnablePathEnvironmentVariableSource => _enablePathEnvironmentVariableSource.Value;
|
||||
|
||||
public bool IncludeNonAppsOnDesktop => _includeNonAppsOnDesktop.Value;
|
||||
|
||||
public bool IncludeNonAppsInStartMenu => _includeNonAppsInStartMenu.Value;
|
||||
|
||||
private readonly ChoiceSetSetting _searchResultLimitSource = new(
|
||||
Namespaced(nameof(SearchResultLimit)),
|
||||
Resources.limit_fallback_results_source,
|
||||
@@ -121,6 +125,18 @@ public class AllAppsSettings : JsonSettingsManager, ISettingsInterface
|
||||
string.Empty,
|
||||
false); // this one is very VERY noisy
|
||||
|
||||
private readonly ToggleSetting _includeNonAppsOnDesktop = new(
|
||||
Namespaced(nameof(IncludeNonAppsOnDesktop)),
|
||||
Resources.include_non_apps_on_desktop,
|
||||
string.Empty,
|
||||
false);
|
||||
|
||||
private readonly ToggleSetting _includeNonAppsInStartMenu = new(
|
||||
Namespaced(nameof(IncludeNonAppsInStartMenu)),
|
||||
Resources.include_non_apps_in_start_menu,
|
||||
string.Empty,
|
||||
true);
|
||||
|
||||
public double MinScoreThreshold { get; set; } = 0.75;
|
||||
|
||||
internal const char SuffixSeparator = ';';
|
||||
@@ -139,7 +155,9 @@ public class AllAppsSettings : JsonSettingsManager, ISettingsInterface
|
||||
FilePath = SettingsJsonPath();
|
||||
|
||||
Settings.Add(_enableStartMenuSource);
|
||||
Settings.Add(_includeNonAppsInStartMenu);
|
||||
Settings.Add(_enableDesktopSource);
|
||||
Settings.Add(_includeNonAppsOnDesktop);
|
||||
Settings.Add(_enableRegistrySource);
|
||||
Settings.Add(_enablePathEnvironmentVariableSource);
|
||||
Settings.Add(_searchResultLimitSource);
|
||||
|
||||
@@ -83,6 +83,8 @@ public sealed partial class AppCache : IAppCache, IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsIndexing => _win32ProgramRepository.IsIndexing;
|
||||
|
||||
public bool ShouldReload() => _packageRepository.ShouldReload() || _win32ProgramRepository.ShouldReload();
|
||||
|
||||
public void ResetReloadFlag()
|
||||
|
||||
@@ -19,4 +19,8 @@ public interface ISettingsInterface
|
||||
public List<string> ProgramSuffixes { get; }
|
||||
|
||||
public List<string> RunCommandSuffixes { get; }
|
||||
|
||||
public bool IncludeNonAppsOnDesktop { get; }
|
||||
|
||||
public bool IncludeNonAppsInStartMenu { get; }
|
||||
}
|
||||
|
||||
@@ -33,4 +33,9 @@ public interface IAppCache : IDisposable
|
||||
/// Resets the reload flag.
|
||||
/// </summary>
|
||||
void ResetReloadFlag();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether a background re-index is in progress.
|
||||
/// </summary>
|
||||
bool IsIndexing { get; }
|
||||
}
|
||||
|
||||
@@ -25,4 +25,6 @@ internal static class Icons
|
||||
public static IconInfo UninstallApplicationIcon { get; } = new("\uE74D"); // Uninstall icon
|
||||
|
||||
public static IconInfo GenericAppIcon { get; } = new("\uE737"); // Favicon
|
||||
|
||||
internal static IconInfo Reloading { get; } = new("\uF16A"); // ProgressRing
|
||||
}
|
||||
|
||||
@@ -67,6 +67,10 @@ public class Win32Program : IProgram
|
||||
|
||||
public bool Enabled { get; set; }
|
||||
|
||||
public bool IsInDesktop { get; set; }
|
||||
|
||||
public bool IsInStartMenu { get; set; }
|
||||
|
||||
public bool HasArguments => !string.IsNullOrEmpty(Arguments);
|
||||
|
||||
public string Arguments { get; set; } = string.Empty;
|
||||
@@ -382,6 +386,8 @@ public class Win32Program : IProgram
|
||||
}
|
||||
|
||||
program.LnkFilePath = program.FullPath;
|
||||
program.IsInDesktop = StartsWithAnyFolder(program.LnkFilePath, Environment.SpecialFolder.Desktop, Environment.SpecialFolder.CommonDesktopDirectory);
|
||||
program.IsInStartMenu = StartsWithAnyFolder(program.LnkFilePath, Environment.SpecialFolder.StartMenu, Environment.SpecialFolder.CommonStartMenu);
|
||||
program.LnkResolvedExecutableName = Path.GetFileName(target);
|
||||
program.LnkResolvedExecutableNameLocalized = Path.GetFileName(ShellLocalization.Instance.GetLocalizedPath(target));
|
||||
|
||||
@@ -803,24 +809,6 @@ public class Win32Program : IProgram
|
||||
public override bool Equals(object? obj)
|
||||
=> obj is Win32Program win32Program && Win32ProgramEqualityComparer.Default.Equals(this, win32Program);
|
||||
|
||||
private sealed class Win32ProgramEqualityComparer : IEqualityComparer<Win32Program>
|
||||
{
|
||||
public static readonly Win32ProgramEqualityComparer Default = new();
|
||||
|
||||
public bool Equals(Win32Program? app1, Win32Program? app2)
|
||||
{
|
||||
return app1 is null && app2 is null
|
||||
? true
|
||||
: app1 is not null
|
||||
&& app2 is not null
|
||||
&& (app1.Name?.ToUpperInvariant(), app1.ExecutableName?.ToUpperInvariant(), app1.FullPath?.ToUpperInvariant())
|
||||
.Equals((app2.Name?.ToUpperInvariant(), app2.ExecutableName?.ToUpperInvariant(), app2.FullPath?.ToUpperInvariant()));
|
||||
}
|
||||
|
||||
public int GetHashCode(Win32Program obj)
|
||||
=> (obj.Name?.ToUpperInvariant(), obj.ExecutableName?.ToUpperInvariant(), obj.FullPath?.ToUpperInvariant()).GetHashCode();
|
||||
}
|
||||
|
||||
public static List<Win32Program> DeduplicatePrograms(IEnumerable<Win32Program> programs)
|
||||
{
|
||||
// Create a HashSet with the custom equality comparer to automatically deduplicate programs
|
||||
@@ -1065,4 +1053,44 @@ public class Win32Program : IProgram
|
||||
FullExecutablePath = app.FullPath,
|
||||
};
|
||||
}
|
||||
|
||||
private static bool StartsWithAnyFolder(string path, params Environment.SpecialFolder[] folders)
|
||||
{
|
||||
foreach (var folder in folders)
|
||||
{
|
||||
var folderPath = Environment.GetFolderPath(folder, Environment.SpecialFolderOption.DoNotVerify);
|
||||
if (!string.IsNullOrEmpty(folderPath) && path.StartsWith(folderPath, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private sealed class Win32ProgramEqualityComparer : IEqualityComparer<Win32Program>
|
||||
{
|
||||
public static readonly Win32ProgramEqualityComparer Default = new();
|
||||
|
||||
public bool Equals(Win32Program? app1, Win32Program? app2)
|
||||
{
|
||||
if (app1 is null && app2 is null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (app1 is null || app2 is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return string.Equals(app1.Name, app2.Name, StringComparison.OrdinalIgnoreCase)
|
||||
&& string.Equals(app1.ExecutableName, app2.ExecutableName, StringComparison.OrdinalIgnoreCase)
|
||||
&& string.Equals(app1.FullPath, app2.FullPath, StringComparison.OrdinalIgnoreCase)
|
||||
&& string.Equals(app1.Arguments, app2.Arguments, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
public int GetHashCode(Win32Program app)
|
||||
=> (app.Name?.ToUpperInvariant(), app.ExecutableName?.ToUpperInvariant(), app.FullPath?.ToUpperInvariant(), app.Arguments).GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ namespace Microsoft.CmdPal.Ext.Apps.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Include apps registered in the Registry.
|
||||
/// Looks up a localized string similar to Include apps registered in the Windows Registry.
|
||||
/// </summary>
|
||||
internal static string enable_registry_source {
|
||||
get {
|
||||
@@ -115,7 +115,7 @@ namespace Microsoft.CmdPal.Ext.Apps.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Include apps found in the Start Menu.
|
||||
/// Looks up a localized string similar to Include apps found in the Start menu.
|
||||
/// </summary>
|
||||
internal static string enable_start_menu_source {
|
||||
get {
|
||||
@@ -141,6 +141,42 @@ namespace Microsoft.CmdPal.Ext.Apps.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Also include non-app shortcuts from the Start menu.
|
||||
/// </summary>
|
||||
internal static string include_non_apps_in_start_menu {
|
||||
get {
|
||||
return ResourceManager.GetString("include_non_apps_in_start_menu", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Also include non-app shortcuts from the desktop.
|
||||
/// </summary>
|
||||
internal static string include_non_apps_on_desktop {
|
||||
get {
|
||||
return ResourceManager.GetString("include_non_apps_on_desktop", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Status.
|
||||
/// </summary>
|
||||
internal static string section_status {
|
||||
get {
|
||||
return ResourceManager.GetString("section_status", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to All apps.
|
||||
/// </summary>
|
||||
internal static string section_all_apps {
|
||||
get {
|
||||
return ResourceManager.GetString("section_all_apps", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Installed apps.
|
||||
/// </summary>
|
||||
@@ -285,6 +321,15 @@ namespace Microsoft.CmdPal.Ext.Apps.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Refreshing app list....
|
||||
/// </summary>
|
||||
internal static string refreshing_app_list {
|
||||
get {
|
||||
return ResourceManager.GetString("refreshing_app_list", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Run as administrator.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
@@ -26,36 +26,36 @@
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
@@ -170,13 +170,13 @@
|
||||
<value>Run as different user</value>
|
||||
</data>
|
||||
<data name="enable_start_menu_source" xml:space="preserve">
|
||||
<value>Include apps found in the Start Menu</value>
|
||||
<value>Include apps found in the Start menu</value>
|
||||
</data>
|
||||
<data name="enable_desktop_source" xml:space="preserve">
|
||||
<value>Include apps found on the desktop</value>
|
||||
</data>
|
||||
<data name="enable_registry_source" xml:space="preserve">
|
||||
<value>Include apps registered in the Registry</value>
|
||||
<value>Include apps registered in the Windows Registry</value>
|
||||
</data>
|
||||
<data name="enable_path_environment_variable_source" xml:space="preserve">
|
||||
<value>Include apps anywhere on the %PATH%</value>
|
||||
@@ -238,4 +238,19 @@
|
||||
<value>Default ({0})</value>
|
||||
<comment>default option; {0} = number of items</comment>
|
||||
</data>
|
||||
<data name="refreshing_app_list" xml:space="preserve">
|
||||
<value>Refreshing app list...</value>
|
||||
</data>
|
||||
<data name="section_status" xml:space="preserve">
|
||||
<value>Status</value>
|
||||
</data>
|
||||
<data name="section_all_apps" xml:space="preserve">
|
||||
<value>All apps</value>
|
||||
</data>
|
||||
<data name="include_non_apps_on_desktop" xml:space="preserve">
|
||||
<value>Also include non-app shortcuts from the desktop</value>
|
||||
</data>
|
||||
<data name="include_non_apps_in_start_menu" xml:space="preserve">
|
||||
<value>Also include non-app shortcuts from the Start menu</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -8,27 +8,38 @@ using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ManagedCommon;
|
||||
using Win32Program = Microsoft.CmdPal.Ext.Apps.Programs.Win32Program;
|
||||
using Microsoft.CmdPal.Ext.Apps.Programs;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.Apps.Storage;
|
||||
|
||||
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
|
||||
internal sealed partial class Win32ProgramRepository : ListRepository<Programs.Win32Program>, IProgramRepository
|
||||
internal sealed partial class Win32ProgramRepository : ListRepository<Programs.Win32Program>, IProgramRepository, IDisposable
|
||||
{
|
||||
private const string LnkExtension = ".lnk";
|
||||
private const string UrlExtension = ".url";
|
||||
private static readonly Collection<string> ExtensionsToWatch = ["*.exe", $"*{LnkExtension}", "*.appref-ms", $"*{UrlExtension}"];
|
||||
|
||||
private AllAppsSettings _settings;
|
||||
private IList<IFileSystemWatcherWrapper> _fileSystemWatcherHelpers;
|
||||
private string[] _pathsToWatch;
|
||||
private int _numberOfPathsToWatch;
|
||||
private Collection<string> extensionsToWatch = new Collection<string> { "*.exe", $"*{LnkExtension}", "*.appref-ms", $"*{UrlExtension}" };
|
||||
|
||||
// Snapshot of source-toggle values so we can detect when a re-index is needed.
|
||||
private bool _lastEnableStartMenu;
|
||||
private bool _lastEnableDesktop;
|
||||
private bool _lastEnableRegistry;
|
||||
private bool _lastEnablePath;
|
||||
|
||||
private bool _isDirty;
|
||||
private volatile bool _isIndexing;
|
||||
private CancellationTokenSource? _indexCts;
|
||||
|
||||
private static ConcurrentQueue<string> commonEventHandlingQueue = new ConcurrentQueue<string>();
|
||||
public bool IsIndexing => _isIndexing;
|
||||
|
||||
private static ConcurrentQueue<string> commonEventHandlingQueue = [];
|
||||
|
||||
public Win32ProgramRepository(IList<IFileSystemWatcherWrapper> fileSystemWatcherHelpers, AllAppsSettings settings, string[] pathsToWatch)
|
||||
{
|
||||
@@ -36,8 +47,41 @@ internal sealed partial class Win32ProgramRepository : ListRepository<Programs.W
|
||||
_settings = settings ?? throw new ArgumentNullException(nameof(settings), "Win32ProgramRepository requires an initialized settings object");
|
||||
_pathsToWatch = pathsToWatch;
|
||||
_numberOfPathsToWatch = pathsToWatch.Length;
|
||||
SnapshotSourceSettings();
|
||||
InitializeFileSystemWatchers();
|
||||
|
||||
_settings.Settings.SettingsChanged += (s, a) =>
|
||||
{
|
||||
if (HaveSourceSettingsChanged())
|
||||
{
|
||||
SnapshotSourceSettings();
|
||||
_indexCts?.Cancel();
|
||||
_indexCts?.Dispose();
|
||||
_indexCts = new CancellationTokenSource();
|
||||
var token = _indexCts.Token;
|
||||
_isIndexing = true;
|
||||
_isDirty = true;
|
||||
_ = Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
IndexPrograms(token);
|
||||
_isIndexing = false;
|
||||
_isDirty = true;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// Superseded by a newer indexing request.
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
ApplyNonAppFilter();
|
||||
_isDirty = true;
|
||||
}
|
||||
};
|
||||
|
||||
// This task would always run in the background trying to dequeue file paths from the queue at regular intervals.
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
@@ -83,7 +127,7 @@ internal sealed partial class Win32ProgramRepository : ListRepository<Programs.W
|
||||
_fileSystemWatcherHelpers[index].NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite;
|
||||
|
||||
// filtering the app types that we want to monitor
|
||||
_fileSystemWatcherHelpers[index].Filters = extensionsToWatch;
|
||||
_fileSystemWatcherHelpers[index].Filters = ExtensionsToWatch;
|
||||
|
||||
// Registering the event handlers
|
||||
_fileSystemWatcherHelpers[index].Created += OnAppCreated;
|
||||
@@ -264,9 +308,50 @@ internal sealed partial class Win32ProgramRepository : ListRepository<Programs.W
|
||||
}
|
||||
}
|
||||
|
||||
public void IndexPrograms()
|
||||
public void IndexPrograms() => IndexPrograms(CancellationToken.None);
|
||||
|
||||
public void IndexPrograms(CancellationToken cancellationToken)
|
||||
{
|
||||
var applications = Win32Program.All(_settings);
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
SetList(applications);
|
||||
ApplyNonAppFilter();
|
||||
}
|
||||
|
||||
private void ApplyNonAppFilter()
|
||||
{
|
||||
var includeNonAppsOnDesktop = _settings.IncludeNonAppsOnDesktop;
|
||||
var includeNonAppsInStartMenu = _settings.IncludeNonAppsInStartMenu;
|
||||
|
||||
foreach (var app in this)
|
||||
{
|
||||
if (app.AppType is Win32Program.ApplicationType.GenericFile or Win32Program.ApplicationType.Folder
|
||||
&& !string.IsNullOrEmpty(app.LnkFilePath))
|
||||
{
|
||||
app.Enabled = (includeNonAppsOnDesktop || !app.IsInDesktop)
|
||||
&& (includeNonAppsInStartMenu || !app.IsInStartMenu);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SnapshotSourceSettings()
|
||||
{
|
||||
_lastEnableStartMenu = _settings.EnableStartMenuSource;
|
||||
_lastEnableDesktop = _settings.EnableDesktopSource;
|
||||
_lastEnableRegistry = _settings.EnableRegistrySource;
|
||||
_lastEnablePath = _settings.EnablePathEnvironmentVariableSource;
|
||||
}
|
||||
|
||||
private bool HaveSourceSettingsChanged()
|
||||
{
|
||||
return _settings.EnableStartMenuSource != _lastEnableStartMenu
|
||||
|| _settings.EnableDesktopSource != _lastEnableDesktop
|
||||
|| _settings.EnableRegistrySource != _lastEnableRegistry
|
||||
|| _settings.EnablePathEnvironmentVariableSource != _lastEnablePath;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_indexCts?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user