Skip to content

Commit bdf6a81

Browse files
authored
Feature: Add pinned items to taskbar jumplist (#11632)
1 parent ec17cd8 commit bdf6a81

File tree

4 files changed

+85
-19
lines changed

4 files changed

+85
-19
lines changed

src/Files.App/DataModels/SidebarPinnedModel.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using Files.App.Filesystem;
55
using Files.App.Helpers;
66
using Files.App.ServicesImplementation;
7+
using Files.App.UserControls.Widgets;
78
using Files.Backend.Services.Settings;
89
using System;
910
using System.Collections.Generic;
@@ -193,7 +194,15 @@ public void RemoveStaleSidebarItems()
193194
}
194195

195196
public async void LoadAsync(object? sender, FileSystemEventArgs e)
196-
=> await LoadAsync();
197+
{
198+
App.QuickAccessManager.PinnedItemsWatcher.EnableRaisingEvents = false;
199+
await LoadAsync();
200+
App.QuickAccessManager.UpdateQuickAccessWidget?.Invoke(null, new ModifyQuickAccessEventArgs((await QuickAccessService.GetPinnedFoldersAsync()).Select(x => x.FilePath).ToArray(), true)
201+
{
202+
Reset = true
203+
});
204+
App.QuickAccessManager.PinnedItemsWatcher.EnableRaisingEvents = true;
205+
}
197206

198207
public async Task LoadAsync()
199208
=> await UpdateItemsWithExplorer();

src/Files.App/Helpers/JumpListManager.cs

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Files.App.Filesystem;
2+
using Files.App.UserControls.Widgets;
23
using Files.Shared.Extensions;
34
using System;
45
using System.Collections.Generic;
@@ -14,6 +15,8 @@ public sealed class JumpListManager
1415
{
1516
private JumpList instance = null;
1617
private List<string> JumpListItemPaths { get; set; }
18+
private readonly string JumpListRecentGroupHeader = "ms-resource:///Resources/JumpListRecentGroupHeader";
19+
private readonly string JumpListPinnedGroupHeader = "ms-resource:///Resources/JumpListPinnedGroupHeader";
1720

1821
public JumpListManager()
1922
{
@@ -27,6 +30,9 @@ public async Task InitializeAsync()
2730
if (JumpList.IsSupported())
2831
{
2932
instance = await JumpList.LoadCurrentAsync();
33+
App.QuickAccessManager.UpdateQuickAccessWidget += QuickAccessManager_DataChanged;
34+
35+
QuickAccessManager_DataChanged(null, null);
3036

3137
// Disable automatic jumplist. It doesn't work with Files UWP.
3238
instance.SystemGroupKind = JumpListSystemGroupKind.None;
@@ -48,14 +54,14 @@ public async void AddFolderToJumpList(string path)
4854
{
4955
if (instance is not null)
5056
{
51-
AddFolder(path);
57+
AddFolder(path, JumpListRecentGroupHeader);
5258
await instance.SaveAsync();
5359
}
5460
}
5561
catch { }
5662
}
5763

58-
private void AddFolder(string path)
64+
private void AddFolder(string path, string group)
5965
{
6066
if (instance is not null)
6167
{
@@ -65,9 +71,7 @@ private void AddFolder(string path)
6571
// Jumplist item argument can't end with a slash so append a character that can't exist in a directory name to support listing drives.
6672
var drive = App.DrivesManager.Drives.Where(drive => drive.Path == path).FirstOrDefault();
6773
if (drive is null)
68-
{
6974
return;
70-
}
7175

7276
displayName = drive.Text;
7377
path += '?';
@@ -76,13 +80,9 @@ private void AddFolder(string path)
7680
if (displayName is null)
7781
{
7882
if (path.Equals(CommonPaths.DesktopPath, StringComparison.OrdinalIgnoreCase))
79-
{
8083
displayName = "ms-resource:///Resources/Desktop";
81-
}
8284
else if (path.Equals(CommonPaths.DownloadsPath, StringComparison.OrdinalIgnoreCase))
83-
{
8485
displayName = "ms-resource:///Resources/Downloads";
85-
}
8686
else if (path.Equals(CommonPaths.RecycleBinPath, StringComparison.OrdinalIgnoreCase))
8787
{
8888
var localSettings = ApplicationData.Current.LocalSettings;
@@ -108,21 +108,28 @@ private void AddFolder(string path)
108108
}
109109
}
110110
else
111-
{
112111
displayName = Path.GetFileName(path);
113-
}
114112
}
115113

116114
var jumplistItem = JumpListItem.CreateWithArguments(path, displayName);
117115
jumplistItem.Description = jumplistItem.Arguments;
118-
jumplistItem.GroupName = "ms-resource:///Resources/JumpListRecentGroupHeader";
116+
jumplistItem.GroupName = group;
119117
jumplistItem.Logo = new Uri("ms-appx:///Assets/FolderIcon.png");
120118

121-
// Keep newer items at the top.
122-
instance.Items.Remove(instance.Items.FirstOrDefault(x => x.Arguments.Equals(path, StringComparison.OrdinalIgnoreCase)));
123-
instance.Items.Insert(0, jumplistItem);
124-
JumpListItemPaths.Remove(JumpListItemPaths.FirstOrDefault(x => x.Equals(path, StringComparison.OrdinalIgnoreCase)));
125-
JumpListItemPaths.Add(path);
119+
if (string.Equals(group, JumpListRecentGroupHeader, StringComparison.OrdinalIgnoreCase))
120+
{
121+
// Keep newer items at the top.
122+
instance.Items.Remove(instance.Items.FirstOrDefault(x => x.Arguments.Equals(path, StringComparison.OrdinalIgnoreCase)));
123+
instance.Items.Insert(0, jumplistItem);
124+
125+
JumpListItemPaths.Remove(JumpListItemPaths.FirstOrDefault(x => x.Equals(path, StringComparison.OrdinalIgnoreCase)));
126+
JumpListItemPaths.Add(path);
127+
}
128+
else
129+
{
130+
var pinnedItemsCount = instance.Items.Where(x => x.GroupName == JumpListPinnedGroupHeader).Count();
131+
instance.Items.Insert(pinnedItemsCount, jumplistItem);
132+
}
126133
}
127134
}
128135

@@ -133,9 +140,7 @@ public async void RemoveFolder(string path)
133140
try
134141
{
135142
if (instance is null)
136-
{
137143
return;
138-
}
139144

140145
if (JumpListItemPaths.Remove(path))
141146
{
@@ -146,5 +151,16 @@ public async void RemoveFolder(string path)
146151
}
147152
catch { }
148153
}
154+
155+
private async void QuickAccessManager_DataChanged(object sender, ModifyQuickAccessEventArgs e)
156+
{
157+
if (instance is null)
158+
return;
159+
160+
var itemsToRemove = instance.Items.Where(x => string.Equals(x.GroupName, JumpListPinnedGroupHeader, StringComparison.OrdinalIgnoreCase)).ToList();
161+
itemsToRemove.ForEach(x => instance.Items.Remove(x));
162+
App.QuickAccessManager.Model.FavoriteItems.ForEach(x => AddFolder(x, JumpListPinnedGroupHeader));
163+
await instance.SaveAsync();
164+
}
149165
}
150166
}

src/Files.App/Strings/en-US/Resources.resw

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2610,4 +2610,7 @@
26102610
<data name="UnableToCalcHashes" xml:space="preserve">
26112611
<value>Unable to calculate hashes due to a system error</value>
26122612
</data>
2613+
<data name="JumpListPinnedGroupHeader" xml:space="preserve">
2614+
<value>Pinned items</value>
2615+
</data>
26132616
</root>

src/Files.App/UserControls/Widgets/QuickAccessWidget.xaml.cs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using Files.App.ViewModels;
99
using Files.App.ViewModels.Widgets;
1010
using Files.Backend.Services.Settings;
11+
using Files.Shared;
1112
using Microsoft.UI.Xaml;
1213
using Microsoft.UI.Xaml.Controls;
1314
using Microsoft.UI.Xaml.Input;
@@ -39,14 +40,23 @@ public class QuickAccessCardInvokedEventArgs : EventArgs
3940
public class ModifyQuickAccessEventArgs : EventArgs
4041
{
4142
public string[] Paths { get; set; }
43+
public ShellFileItem[] Items { get; set; }
4244
public bool Add;
4345
public bool Pin = true;
46+
public bool Reset = false;
4447

4548
public ModifyQuickAccessEventArgs(string[] paths, bool add)
4649
{
4750
Paths = paths;
4851
Add = add;
4952
}
53+
54+
public ModifyQuickAccessEventArgs(ShellFileItem[] items, bool add)
55+
{
56+
Paths = items.Select(x => x.FilePath).ToArray();
57+
Items = items;
58+
Add = add;
59+
}
5060
}
5161

5262
public class FolderCardItem : WidgetCardItem, IWidgetCardItem<LocationItem>
@@ -237,6 +247,34 @@ private async void ModifyItem(object? sender, ModifyQuickAccessEventArgs? e)
237247

238248
await DispatcherQueue.EnqueueAsync(async () =>
239249
{
250+
if (e.Reset)
251+
{
252+
// Find the intersection between the two lists and determine whether to remove or add
253+
var itemsToRemove = ItemsAdded.Where(x => !e.Paths.Contains(x.Path)).ToList();
254+
var itemsToAdd = e.Paths.Where(x => !ItemsAdded.Any(y => y.Path == x)).ToList();
255+
256+
// Remove items
257+
foreach (var itemToRemove in itemsToRemove)
258+
ItemsAdded.Remove(itemToRemove);
259+
260+
// Add items
261+
foreach (var itemToAdd in itemsToAdd)
262+
{
263+
var item = await App.QuickAccessManager.Model.CreateLocationItemFromPathAsync(itemToAdd);
264+
var lastIndex = ItemsAdded.IndexOf(ItemsAdded.FirstOrDefault(x => !x.IsPinned));
265+
var isPinned = (bool?)e.Items.Where(x => x.FilePath == itemToAdd).FirstOrDefault().Properties["System.Home.IsPinned"] ?? false;
266+
267+
ItemsAdded.Insert(isPinned && lastIndex >= 0 ? lastIndex : ItemsAdded.Count, new FolderCardItem(item, Path.GetFileName(item.Text), isPinned)
268+
{
269+
Path = item.Path,
270+
SelectCommand = QuickAccessCardCommand
271+
});
272+
}
273+
var cardLoadTasks = ItemsAdded.Select(cardItem => cardItem.LoadCardThumbnailAsync());
274+
await Task.WhenAll(cardLoadTasks);
275+
276+
return;
277+
}
240278
if (e.Add)
241279
{
242280
foreach (var itemToAdd in e.Paths)

0 commit comments

Comments
 (0)