mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-07 19:57:07 +02:00
Users can now pin/unpin apps from the top of AllApps extension (#40544)
Users can now use Ctrl+P to toggle pinning and unpinning applications from the top of the All Apps extension. This pinned status persists through restarts & reboots. https://github.com/user-attachments/assets/86895a38-7312-438a-9409-b50a85979d12 Reference: #40543 --------- Co-authored-by: Mike Griese <migrie@microsoft.com>
This commit is contained in:
@@ -2,14 +2,20 @@
|
||||
// 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.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ManagedCommon;
|
||||
using Microsoft.CmdPal.Ext.Apps.Commands;
|
||||
using Microsoft.CmdPal.Ext.Apps.Helpers;
|
||||
using Microsoft.CmdPal.Ext.Apps.Programs;
|
||||
using Microsoft.CmdPal.Ext.Apps.Properties;
|
||||
using Microsoft.CmdPal.Ext.Apps.State;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
@@ -18,16 +24,22 @@ namespace Microsoft.CmdPal.Ext.Apps;
|
||||
public sealed partial class AllAppsPage : ListPage
|
||||
{
|
||||
private readonly Lock _listLock = new();
|
||||
private AppListItem[] allAppsSection = [];
|
||||
|
||||
private AppItem[] allApps = [];
|
||||
private AppListItem[] unpinnedApps = [];
|
||||
private AppListItem[] pinnedApps = [];
|
||||
|
||||
public AllAppsPage()
|
||||
{
|
||||
this.Name = Resources.all_apps;
|
||||
this.Icon = IconHelpers.FromRelativePath("Assets\\AllApps.svg");
|
||||
this.Icon = Icons.AllAppsIcon;
|
||||
this.ShowDetails = true;
|
||||
this.IsLoading = true;
|
||||
this.PlaceholderText = Resources.search_installed_apps_placeholder;
|
||||
|
||||
// Subscribe to pin state changes to refresh the command provider
|
||||
PinnedAppsManager.Instance.PinStateChanged += OnPinStateChanged;
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
lock (_listLock)
|
||||
@@ -37,89 +49,139 @@ public sealed partial class AllAppsPage : ListPage
|
||||
});
|
||||
}
|
||||
|
||||
internal AppListItem[] GetPinnedApps()
|
||||
{
|
||||
BuildListItems();
|
||||
return pinnedApps;
|
||||
}
|
||||
|
||||
public override IListItem[] GetItems()
|
||||
{
|
||||
if (allAppsSection.Length == 0 || AppCache.Instance.Value.ShouldReload())
|
||||
{
|
||||
lock (_listLock)
|
||||
{
|
||||
BuildListItems();
|
||||
}
|
||||
}
|
||||
|
||||
return allAppsSection;
|
||||
// Build or update the list if needed
|
||||
BuildListItems();
|
||||
return pinnedApps.Concat(unpinnedApps).ToArray();
|
||||
}
|
||||
|
||||
private void BuildListItems()
|
||||
{
|
||||
this.IsLoading = true;
|
||||
if (allApps.Length == 0 || AppCache.Instance.Value.ShouldReload())
|
||||
{
|
||||
lock (_listLock)
|
||||
{
|
||||
this.IsLoading = true;
|
||||
|
||||
Stopwatch stopwatch = new();
|
||||
stopwatch.Start();
|
||||
Stopwatch stopwatch = new();
|
||||
stopwatch.Start();
|
||||
|
||||
var apps = GetPrograms();
|
||||
var apps = GetPrograms();
|
||||
this.allApps = apps.AllApps;
|
||||
this.pinnedApps = apps.PinnedItems;
|
||||
this.unpinnedApps = apps.UnpinnedItems;
|
||||
|
||||
this.allAppsSection = apps
|
||||
.Select((app) => new AppListItem(app, true))
|
||||
.ToArray();
|
||||
this.IsLoading = false;
|
||||
|
||||
this.IsLoading = false;
|
||||
AppCache.Instance.Value.ResetReloadFlag();
|
||||
|
||||
AppCache.Instance.Value.ResetReloadFlag();
|
||||
|
||||
stopwatch.Stop();
|
||||
Logger.LogTrace($"{nameof(AllAppsPage)}.{nameof(BuildListItems)} took: {stopwatch.ElapsedMilliseconds} ms");
|
||||
stopwatch.Stop();
|
||||
Logger.LogTrace($"{nameof(AllAppsPage)}.{nameof(BuildListItems)} took: {stopwatch.ElapsedMilliseconds} ms");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal List<AppItem> GetPrograms()
|
||||
private AppItem[] GetAllApps()
|
||||
{
|
||||
var uwpResults = AppCache.Instance.Value.UWPs
|
||||
.Where((application) => application.Enabled)
|
||||
.Select(UwpToAppItem);
|
||||
.Where((application) => application.Enabled)
|
||||
.Select(app => app.ToAppItem());
|
||||
|
||||
var win32Results = AppCache.Instance.Value.Win32s
|
||||
.Where((application) => application.Enabled && application.Valid)
|
||||
.Select(app =>
|
||||
{
|
||||
var icoPath = string.IsNullOrEmpty(app.IcoPath) ?
|
||||
(app.AppType == Win32Program.ApplicationType.InternetShortcutApplication ?
|
||||
app.IcoPath :
|
||||
app.FullPath) :
|
||||
app.IcoPath;
|
||||
.Select(app => app.ToAppItem());
|
||||
|
||||
// icoPath = icoPath.EndsWith(".lnk", System.StringComparison.InvariantCultureIgnoreCase) ? (icoPath + ",0") : icoPath;
|
||||
icoPath = icoPath.EndsWith(".lnk", System.StringComparison.InvariantCultureIgnoreCase) ?
|
||||
app.FullPath :
|
||||
icoPath;
|
||||
return new AppItem()
|
||||
{
|
||||
Name = app.Name,
|
||||
Subtitle = app.Description,
|
||||
Type = app.Type(),
|
||||
IcoPath = icoPath,
|
||||
ExePath = !string.IsNullOrEmpty(app.LnkFilePath) ? app.LnkFilePath : app.FullPath,
|
||||
DirPath = app.Location,
|
||||
Commands = app.GetCommands(),
|
||||
};
|
||||
});
|
||||
|
||||
return uwpResults.Concat(win32Results).OrderBy(app => app.Name).ToList();
|
||||
var allApps = uwpResults.Concat(win32Results).ToArray();
|
||||
return allApps;
|
||||
}
|
||||
|
||||
private AppItem UwpToAppItem(UWPApplication app)
|
||||
internal (AppItem[] AllApps, AppListItem[] PinnedItems, AppListItem[] UnpinnedItems) GetPrograms()
|
||||
{
|
||||
var iconPath = app.LogoType != LogoType.Error ? app.LogoPath : string.Empty;
|
||||
var item = new AppItem()
|
||||
var allApps = GetAllApps();
|
||||
var pinned = new List<AppListItem>();
|
||||
var unpinned = new List<AppListItem>();
|
||||
|
||||
foreach (var app in allApps)
|
||||
{
|
||||
Name = app.Name,
|
||||
Subtitle = app.Description,
|
||||
Type = UWPApplication.Type(),
|
||||
IcoPath = iconPath,
|
||||
DirPath = app.Location,
|
||||
UserModelId = app.UserModelId,
|
||||
IsPackaged = true,
|
||||
Commands = app.GetCommands(),
|
||||
};
|
||||
return item;
|
||||
var isPinned = PinnedAppsManager.Instance.IsAppPinned(app.AppIdentifier);
|
||||
var appListItem = new AppListItem(app, true, isPinned);
|
||||
|
||||
if (isPinned)
|
||||
{
|
||||
appListItem.Tags = appListItem.Tags
|
||||
.Concat([new Tag() { Icon = Icons.PinIcon }])
|
||||
.ToArray();
|
||||
pinned.Add(appListItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
unpinned.Add(appListItem);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
allApps
|
||||
.ToArray(),
|
||||
pinned
|
||||
.OrderBy(app => app.Title)
|
||||
.ToArray(),
|
||||
unpinned
|
||||
.OrderBy(app => app.Title)
|
||||
.ToArray());
|
||||
}
|
||||
|
||||
private void OnPinStateChanged(object? sender, PinStateChangedEventArgs e)
|
||||
{
|
||||
/*
|
||||
* Rebuilding all the lists is pretty expensive.
|
||||
* So, instead, we'll just compare pinned items to move existing
|
||||
* items between the two lists.
|
||||
*/
|
||||
var existingAppItem = allApps.FirstOrDefault(f => f.AppIdentifier == e.AppIdentifier);
|
||||
|
||||
if (existingAppItem != null)
|
||||
{
|
||||
var appListItem = new AppListItem(existingAppItem, true, e.IsPinned);
|
||||
|
||||
if (e.IsPinned)
|
||||
{
|
||||
// Remove it from the unpinned apps array
|
||||
this.unpinnedApps = this.unpinnedApps
|
||||
.Where(app => app.AppIdentifier != existingAppItem.AppIdentifier)
|
||||
.OrderBy(app => app.Title)
|
||||
.ToArray();
|
||||
|
||||
var newPinned = this.pinnedApps.ToList();
|
||||
newPinned.Add(appListItem);
|
||||
|
||||
this.pinnedApps = newPinned
|
||||
.OrderBy(app => app.Title)
|
||||
.ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remove it from the pinned apps array
|
||||
this.pinnedApps = this.pinnedApps
|
||||
.Where(app => app.AppIdentifier != existingAppItem.AppIdentifier)
|
||||
.OrderBy(app => app.Title)
|
||||
.ToArray();
|
||||
|
||||
var newUnpinned = this.unpinnedApps.ToList();
|
||||
newUnpinned.Add(appListItem);
|
||||
|
||||
this.unpinnedApps = newUnpinned
|
||||
.OrderBy(app => app.Title)
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
RaiseItemsChanged(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user