Skip to content

Commit b2e3288

Browse files
authored
Fix: Fixed quick access widgets when there are few items (#11169)
1 parent e0737bb commit b2e3288

File tree

7 files changed

+62
-70
lines changed

7 files changed

+62
-70
lines changed

src/Files.App/DataModels/SidebarPinnedModel.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@ public async Task UpdateItemsWithExplorer()
5151

5252
try
5353
{
54-
FavoriteItems = await QuickAccessService.GetPinnedFoldersAsync();
54+
FavoriteItems = (await QuickAccessService.GetPinnedFoldersAsync())
55+
.Where(link => (bool?)link.Properties["System.Home.IsPinned"] ?? false)
56+
.Select(link => link.FilePath).ToList();
5557
RemoveStaleSidebarItems();
5658
await AddAllItemsToSidebar();
5759
}

src/Files.App/Filesystem/RecentItems.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,9 +206,7 @@ public Task<bool> UnpinFromRecentFiles(RecentItem item)
206206
using var shellItem = ShellItem.Open(pidl);
207207
using var cMenu = await ContextMenu.GetContextMenuForFiles(new[] { shellItem }, Shell32.CMF.CMF_NORMAL);
208208
if (cMenu is not null)
209-
{
210209
return await cMenu.InvokeVerb("remove");
211-
}
212210
return false;
213211
}));
214212
}

src/Files.App/ServicesImplementation/QuickAccessService.cs

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,31 +16,26 @@ internal class QuickAccessService : IQuickAccessService
1616
{
1717
private readonly static string guid = "::{679f85cb-0220-4080-b29b-5540cc05aab6}";
1818

19-
public async Task<List<string>> GetPinnedFoldersAsync(bool getRecentItems = false)
19+
public async Task<IEnumerable<ShellFileItem>> GetPinnedFoldersAsync()
2020
{
21-
var sidebarItems = (await Win32Shell.GetShellFolderAsync(guid, "Enumerate", 0, 10000)).Enumerate
22-
.Where(link => link.IsFolder)
23-
.Select(link => link.FilePath).ToList();
24-
25-
if (sidebarItems.Count > 4 && !getRecentItems) // Avoid first opening crash #11139
26-
sidebarItems.RemoveRange(sidebarItems.Count - 4, 4); // 4 is the number of recent items shown in explorer sidebar
27-
28-
return sidebarItems;
21+
return (await Win32Shell.GetShellFolderAsync(guid, "Enumerate", 0, int.MaxValue, "System.Home.IsPinned")).Enumerate
22+
.Where(link => link.IsFolder);
2923
}
30-
31-
public async Task PinToSidebar(string folderPath)
32-
=> await PinToSidebar(new[] { folderPath });
24+
25+
public Task PinToSidebar(string folderPath)
26+
=> PinToSidebar(new[] { folderPath });
3327

3428
public async Task PinToSidebar(string[] folderPaths)
3529
{
3630
await ContextMenu.InvokeVerb("pintohome", folderPaths);
31+
3732
await App.QuickAccessManager.Model.LoadAsync();
3833

3934
App.QuickAccessManager.UpdateQuickAccessWidget?.Invoke(this, new ModifyQuickAccessEventArgs(folderPaths, true));
4035
}
4136

42-
public async Task UnpinFromSidebar(string folderPath)
43-
=> await UnpinFromSidebar(new[] { folderPath });
37+
public Task UnpinFromSidebar(string folderPath)
38+
=> UnpinFromSidebar(new[] { folderPath });
4439

4540
public async Task UnpinFromSidebar(string[] folderPaths)
4641
{
@@ -50,7 +45,7 @@ public async Task UnpinFromSidebar(string[] folderPaths)
5045

5146
foreach (dynamic? fi in f2.Items())
5247
if (folderPaths.Contains((string)fi.Path))
53-
await SafetyExtensions.IgnoreExceptions(async () => {
48+
await SafetyExtensions.IgnoreExceptions(async () => {
5449
await fi.InvokeVerb("unpinfromhome");
5550
});
5651

src/Files.App/Shell/Win32Shell.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Files.Shared;
2+
using Files.Shared.Extensions;
23
using System;
34
using System.Collections.Generic;
45
using System.IO;
@@ -20,7 +21,7 @@ static Win32Shell()
2021
controlPanelCategoryView = new ShellFolder("::{26EE0668-A00A-44D7-9371-BEB064C98683}");
2122
}
2223

23-
public static async Task<(ShellFileItem Folder, List<ShellFileItem> Enumerate)> GetShellFolderAsync(string path, string action, int from, int count)
24+
public static async Task<(ShellFileItem Folder, List<ShellFileItem> Enumerate)> GetShellFolderAsync(string path, string action, int from, int count, params string[] properties)
2425
{
2526
if (path.StartsWith("::{", StringComparison.Ordinal))
2627
{
@@ -51,9 +52,11 @@ static Win32Shell()
5152
var shellFileItem = folderItem is ShellLink link ?
5253
ShellFolderExtensions.GetShellLinkItem(link) :
5354
ShellFolderExtensions.GetShellFileItem(folderItem);
55+
foreach (var prop in properties)
56+
shellFileItem.Properties[prop] = SafetyExtensions.IgnoreExceptions(() => folderItem.Properties[prop]);
5457
flc.Add(shellFileItem);
5558
}
56-
catch (FileNotFoundException)
59+
catch (Exception ex) when (ex is FileNotFoundException || ex is DirectoryNotFoundException)
5760
{
5861
// Happens if files are being deleted
5962
}

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

Lines changed: 37 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -68,27 +68,21 @@ public BitmapImage Thumbnail
6868
get => thumbnail;
6969
set => SetProperty(ref thumbnail, value);
7070
}
71-
public bool IsLibrary => Item is LibraryLocationItem;
72-
public bool IsUserCreatedLibrary => IsLibrary && !LibraryManager.IsDefaultLibrary(Item.Path);
7371
public LocationItem Item { get; private set; }
7472
public string Path { get; set; }
7573
public ICommand SelectCommand { get; set; }
7674
public string Text { get; set; }
7775
public bool IsPinned { get; set; }
7876

79-
public FolderCardItem(LocationItem item = null, string text = null, bool isPinned = true) : this(text, isPinned)
80-
{
81-
Item = item;
82-
}
83-
84-
public FolderCardItem(string text, bool isPinned)
77+
public FolderCardItem(LocationItem item, string text, bool isPinned)
8578
{
8679
if (!string.IsNullOrWhiteSpace(text))
8780
{
8881
Text = text;
8982
AutomationProperties = Text;
90-
IsPinned = isPinned;
9183
}
84+
IsPinned = isPinned;
85+
Item = item;
9286
}
9387

9488
public async Task LoadCardThumbnailAsync()
@@ -177,46 +171,44 @@ private async void ModifyItem(object? sender, ModifyQuickAccessEventArgs? e)
177171
if (e is null)
178172
return;
179173

180-
if (e.Add)
174+
await DispatcherQueue.EnqueueAsync(async () =>
181175
{
182-
var locationItems = new List<LocationItem>();
183-
foreach (var item in e.Paths)
184-
locationItems.Add(await App.QuickAccessManager.Model.CreateLocationItemFromPathAsync(item));
185-
186-
foreach (var item in locationItems)
187-
ItemsAdded.Insert(e.Pin ? ItemsAdded.Count - 4 : ItemsAdded.Count, new FolderCardItem(Path.GetFileName(item.Text), e.Pin) // Add just after the Recent Folders
176+
if (e.Add)
177+
{
178+
foreach (var itemToAdd in e.Paths)
188179
{
189-
Path = item.Path,
190-
SelectCommand = QuickAccessCardCommand
191-
});
192-
193-
var cardLoadTasks = ItemsAdded.Select(cardItem => cardItem.LoadCardThumbnailAsync());
194-
await Task.WhenAll(cardLoadTasks);
195-
}
196-
else
197-
foreach (var itemToRemove in ItemsAdded.Where(x => e.Paths.Contains(x.Path)).ToList())
198-
ItemsAdded.Remove(itemToRemove);
180+
var item = await App.QuickAccessManager.Model.CreateLocationItemFromPathAsync(itemToAdd);
181+
var lastIndex = ItemsAdded.IndexOf(ItemsAdded.FirstOrDefault(x => !x.IsPinned));
182+
ItemsAdded.Insert(e.Pin && lastIndex >= 0 ? lastIndex : ItemsAdded.Count, new FolderCardItem(item, Path.GetFileName(item.Text), e.Pin) // Add just after the Recent Folders
183+
{
184+
Path = item.Path,
185+
SelectCommand = QuickAccessCardCommand
186+
});
187+
}
188+
189+
var cardLoadTasks = ItemsAdded.Select(cardItem => cardItem.LoadCardThumbnailAsync());
190+
await Task.WhenAll(cardLoadTasks);
191+
}
192+
else
193+
foreach (var itemToRemove in ItemsAdded.Where(x => e.Paths.Contains(x.Path)).ToList())
194+
ItemsAdded.Remove(itemToRemove);
195+
});
199196
}
200197

201198
private async void QuickAccessWidget_Loaded(object sender, RoutedEventArgs e)
202199
{
203200
Loaded -= QuickAccessWidget_Loaded;
204201

205-
var itemsToAdd = await QuickAccessService.GetPinnedFoldersAsync(true);
206-
207-
var locationItems = new List<LocationItem>();
208-
foreach (var item in itemsToAdd)
209-
locationItems.Add(await App.QuickAccessManager.Model.CreateLocationItemFromPathAsync(item));
202+
var itemsToAdd = await QuickAccessService.GetPinnedFoldersAsync();
210203

211-
int idx = 0;
212-
foreach (var item in locationItems)
204+
foreach (var itemToAdd in itemsToAdd)
213205
{
214-
ItemsAdded.Add(new FolderCardItem(item, Path.GetFileName(item.Text), idx < locationItems.Count - 4)
206+
var item = await App.QuickAccessManager.Model.CreateLocationItemFromPathAsync(itemToAdd.FilePath);
207+
ItemsAdded.Add(new FolderCardItem(item, Path.GetFileName(item.Text), (bool?)itemToAdd.Properties["System.Home.IsPinned"] ?? false)
215208
{
216209
Path = item.Path,
217210
SelectCommand = QuickAccessCardCommand
218211
});
219-
idx++;
220212
}
221213

222214
App.QuickAccessManager.UpdateQuickAccessWidget += ModifyItem;
@@ -228,7 +220,7 @@ private async void QuickAccessWidget_Loaded(object sender, RoutedEventArgs e)
228220
private void QuickAccessWidget_Unloaded(object sender, RoutedEventArgs e)
229221
{
230222
Unloaded -= QuickAccessWidget_Unloaded;
231-
App.QuickAccessManager.UpdateQuickAccessWidget += ModifyItem;
223+
App.QuickAccessManager.UpdateQuickAccessWidget -= ModifyItem;
232224
}
233225

234226
private void MenuFlyout_Opening(object sender, object e)
@@ -301,13 +293,16 @@ private async void PinToFavorites_Click(object sender, RoutedEventArgs e)
301293
var item = ((MenuFlyoutItem)sender).DataContext as FolderCardItem;
302294
await QuickAccessService.PinToSidebar(item.Path);
303295
ModifyItem(this, new ModifyQuickAccessEventArgs(new[] { item.Path }, false));
304-
var items = await QuickAccessService.GetPinnedFoldersAsync(true);
305-
items.RemoveRange(0, items.Count - 4);
306-
var recentItem = items.Where(x => !ItemsAdded.Select(y => y.Path).Contains(x)).FirstOrDefault();
307-
ModifyItem(this, new ModifyQuickAccessEventArgs(new[] { recentItem }, true)
296+
var items = (await QuickAccessService.GetPinnedFoldersAsync())
297+
.Where(link => !((bool?)link.Properties["System.Home.IsPinned"] ?? false));
298+
var recentItem = items.Where(x => !ItemsAdded.Select(y => y.Path).Contains(x.FilePath)).FirstOrDefault();
299+
if (recentItem is not null)
308300
{
309-
Pin = false
310-
});
301+
ModifyItem(this, new ModifyQuickAccessEventArgs(new[] { recentItem.FilePath }, true)
302+
{
303+
Pin = false
304+
});
305+
}
311306
}
312307

313308
private void UnpinFromFavorites_Click(object sender, RoutedEventArgs e)
@@ -322,11 +317,6 @@ private Task OpenCard(FolderCardItem item)
322317
{
323318
return Task.CompletedTask;
324319
}
325-
if (item.Item is LibraryLocationItem lli && lli.IsEmpty)
326-
{
327-
// TODO: show message?
328-
return Task.CompletedTask;
329-
}
330320

331321
var ctrlPressed = Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Control).HasFlag(CoreVirtualKeyStates.Down);
332322
if (ctrlPressed)

src/Files.Backend/Services/IQuickAccessService.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Collections.Generic;
1+
using Files.Shared;
2+
using System.Collections.Generic;
23
using System.Threading.Tasks;
34

45
namespace Files.App.ServicesImplementation
@@ -9,7 +10,7 @@ public interface IQuickAccessService
910
/// Gets the list of quick access items
1011
/// </summary>
1112
/// <returns></returns>
12-
Task<List<string>> GetPinnedFoldersAsync(bool getRecentItems = false);
13+
Task<IEnumerable<ShellFileItem>> GetPinnedFoldersAsync();
1314

1415
/// <summary>
1516
/// Pins a folder to the quick access list

src/Files.Shared/ShellFileItem.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Generic;
23

34
namespace Files.Shared
45
{
@@ -15,14 +16,16 @@ public class ShellFileItem
1516
public ulong FileSizeBytes { get; set; }
1617
public string FileType { get; set; }
1718
public byte[] PIDL { get; set; } // Low level shell item identifier
19+
public Dictionary<string, object?> Properties { get; set; }
1820

1921
public ShellFileItem()
2022
{
23+
Properties = new Dictionary<string, object?>();
2124
}
2225

2326
public ShellFileItem(
2427
bool isFolder, string recyclePath, string fileName, string filePath,
25-
DateTime recycleDate, DateTime modifiedDate, DateTime createdDate, string fileSize, ulong fileSizeBytes, string fileType, byte[] pidl)
28+
DateTime recycleDate, DateTime modifiedDate, DateTime createdDate, string fileSize, ulong fileSizeBytes, string fileType, byte[] pidl) : this()
2629
{
2730
IsFolder = isFolder;
2831
RecyclePath = recyclePath;

0 commit comments

Comments
 (0)