Skip to content

Fix: Fixed quick access widgets when there are few items #11169

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Feb 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/Files.App/DataModels/SidebarPinnedModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ public async Task UpdateItemsWithExplorer()

try
{
FavoriteItems = await QuickAccessService.GetPinnedFoldersAsync();
FavoriteItems = (await QuickAccessService.GetPinnedFoldersAsync())
.Where(link => (bool?)link.Properties["System.Home.IsPinned"] ?? false)
.Select(link => link.FilePath).ToList();
RemoveStaleSidebarItems();
await AddAllItemsToSidebar();
}
Expand Down
2 changes: 0 additions & 2 deletions src/Files.App/Filesystem/RecentItems.cs
Original file line number Diff line number Diff line change
Expand Up @@ -206,9 +206,7 @@ public Task<bool> UnpinFromRecentFiles(RecentItem item)
using var shellItem = ShellItem.Open(pidl);
using var cMenu = await ContextMenu.GetContextMenuForFiles(new[] { shellItem }, Shell32.CMF.CMF_NORMAL);
if (cMenu is not null)
{
return await cMenu.InvokeVerb("remove");
}
return false;
}));
}
Expand Down
25 changes: 10 additions & 15 deletions src/Files.App/ServicesImplementation/QuickAccessService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,31 +16,26 @@ internal class QuickAccessService : IQuickAccessService
{
private readonly static string guid = "::{679f85cb-0220-4080-b29b-5540cc05aab6}";

public async Task<List<string>> GetPinnedFoldersAsync(bool getRecentItems = false)
public async Task<IEnumerable<ShellFileItem>> GetPinnedFoldersAsync()
{
var sidebarItems = (await Win32Shell.GetShellFolderAsync(guid, "Enumerate", 0, 10000)).Enumerate
.Where(link => link.IsFolder)
.Select(link => link.FilePath).ToList();

if (sidebarItems.Count > 4 && !getRecentItems) // Avoid first opening crash #11139
sidebarItems.RemoveRange(sidebarItems.Count - 4, 4); // 4 is the number of recent items shown in explorer sidebar

return sidebarItems;
return (await Win32Shell.GetShellFolderAsync(guid, "Enumerate", 0, int.MaxValue, "System.Home.IsPinned")).Enumerate
.Where(link => link.IsFolder);
}
public async Task PinToSidebar(string folderPath)
=> await PinToSidebar(new[] { folderPath });

public Task PinToSidebar(string folderPath)
=> PinToSidebar(new[] { folderPath });

public async Task PinToSidebar(string[] folderPaths)
{
await ContextMenu.InvokeVerb("pintohome", folderPaths);

await App.QuickAccessManager.Model.LoadAsync();

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

public async Task UnpinFromSidebar(string folderPath)
=> await UnpinFromSidebar(new[] { folderPath });
public Task UnpinFromSidebar(string folderPath)
=> UnpinFromSidebar(new[] { folderPath });

public async Task UnpinFromSidebar(string[] folderPaths)
{
Expand All @@ -50,7 +45,7 @@ public async Task UnpinFromSidebar(string[] folderPaths)

foreach (dynamic? fi in f2.Items())
if (folderPaths.Contains((string)fi.Path))
await SafetyExtensions.IgnoreExceptions(async () => {
await SafetyExtensions.IgnoreExceptions(async () => {
await fi.InvokeVerb("unpinfromhome");
});

Expand Down
7 changes: 5 additions & 2 deletions src/Files.App/Shell/Win32Shell.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Files.Shared;
using Files.Shared.Extensions;
using System;
using System.Collections.Generic;
using System.IO;
Expand All @@ -20,7 +21,7 @@ static Win32Shell()
controlPanelCategoryView = new ShellFolder("::{26EE0668-A00A-44D7-9371-BEB064C98683}");
}

public static async Task<(ShellFileItem Folder, List<ShellFileItem> Enumerate)> GetShellFolderAsync(string path, string action, int from, int count)
public static async Task<(ShellFileItem Folder, List<ShellFileItem> Enumerate)> GetShellFolderAsync(string path, string action, int from, int count, params string[] properties)
{
if (path.StartsWith("::{", StringComparison.Ordinal))
{
Expand Down Expand Up @@ -51,9 +52,11 @@ static Win32Shell()
var shellFileItem = folderItem is ShellLink link ?
ShellFolderExtensions.GetShellLinkItem(link) :
ShellFolderExtensions.GetShellFileItem(folderItem);
foreach (var prop in properties)
shellFileItem.Properties[prop] = SafetyExtensions.IgnoreExceptions(() => folderItem.Properties[prop]);
flc.Add(shellFileItem);
}
catch (FileNotFoundException)
catch (Exception ex) when (ex is FileNotFoundException || ex is DirectoryNotFoundException)
{
// Happens if files are being deleted
}
Expand Down
84 changes: 37 additions & 47 deletions src/Files.App/UserControls/Widgets/QuickAccessWidget.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,27 +68,21 @@ public BitmapImage Thumbnail
get => thumbnail;
set => SetProperty(ref thumbnail, value);
}
public bool IsLibrary => Item is LibraryLocationItem;
public bool IsUserCreatedLibrary => IsLibrary && !LibraryManager.IsDefaultLibrary(Item.Path);
public LocationItem Item { get; private set; }
public string Path { get; set; }
public ICommand SelectCommand { get; set; }
public string Text { get; set; }
public bool IsPinned { get; set; }

public FolderCardItem(LocationItem item = null, string text = null, bool isPinned = true) : this(text, isPinned)
{
Item = item;
}

public FolderCardItem(string text, bool isPinned)
public FolderCardItem(LocationItem item, string text, bool isPinned)
{
if (!string.IsNullOrWhiteSpace(text))
{
Text = text;
AutomationProperties = Text;
IsPinned = isPinned;
}
IsPinned = isPinned;
Item = item;
}

public async Task LoadCardThumbnailAsync()
Expand Down Expand Up @@ -177,46 +171,44 @@ private async void ModifyItem(object? sender, ModifyQuickAccessEventArgs? e)
if (e is null)
return;

if (e.Add)
await DispatcherQueue.EnqueueAsync(async () =>
{
var locationItems = new List<LocationItem>();
foreach (var item in e.Paths)
locationItems.Add(await App.QuickAccessManager.Model.CreateLocationItemFromPathAsync(item));

foreach (var item in locationItems)
ItemsAdded.Insert(e.Pin ? ItemsAdded.Count - 4 : ItemsAdded.Count, new FolderCardItem(Path.GetFileName(item.Text), e.Pin) // Add just after the Recent Folders
if (e.Add)
{
foreach (var itemToAdd in e.Paths)
{
Path = item.Path,
SelectCommand = QuickAccessCardCommand
});

var cardLoadTasks = ItemsAdded.Select(cardItem => cardItem.LoadCardThumbnailAsync());
await Task.WhenAll(cardLoadTasks);
}
else
foreach (var itemToRemove in ItemsAdded.Where(x => e.Paths.Contains(x.Path)).ToList())
ItemsAdded.Remove(itemToRemove);
var item = await App.QuickAccessManager.Model.CreateLocationItemFromPathAsync(itemToAdd);
var lastIndex = ItemsAdded.IndexOf(ItemsAdded.FirstOrDefault(x => !x.IsPinned));
ItemsAdded.Insert(e.Pin && lastIndex >= 0 ? lastIndex : ItemsAdded.Count, new FolderCardItem(item, Path.GetFileName(item.Text), e.Pin) // Add just after the Recent Folders
{
Path = item.Path,
SelectCommand = QuickAccessCardCommand
});
}

var cardLoadTasks = ItemsAdded.Select(cardItem => cardItem.LoadCardThumbnailAsync());
await Task.WhenAll(cardLoadTasks);
}
else
foreach (var itemToRemove in ItemsAdded.Where(x => e.Paths.Contains(x.Path)).ToList())
ItemsAdded.Remove(itemToRemove);
});
}

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

var itemsToAdd = await QuickAccessService.GetPinnedFoldersAsync(true);

var locationItems = new List<LocationItem>();
foreach (var item in itemsToAdd)
locationItems.Add(await App.QuickAccessManager.Model.CreateLocationItemFromPathAsync(item));
var itemsToAdd = await QuickAccessService.GetPinnedFoldersAsync();

int idx = 0;
foreach (var item in locationItems)
foreach (var itemToAdd in itemsToAdd)
{
ItemsAdded.Add(new FolderCardItem(item, Path.GetFileName(item.Text), idx < locationItems.Count - 4)
var item = await App.QuickAccessManager.Model.CreateLocationItemFromPathAsync(itemToAdd.FilePath);
ItemsAdded.Add(new FolderCardItem(item, Path.GetFileName(item.Text), (bool?)itemToAdd.Properties["System.Home.IsPinned"] ?? false)
{
Path = item.Path,
SelectCommand = QuickAccessCardCommand
});
idx++;
}

App.QuickAccessManager.UpdateQuickAccessWidget += ModifyItem;
Expand All @@ -228,7 +220,7 @@ private async void QuickAccessWidget_Loaded(object sender, RoutedEventArgs e)
private void QuickAccessWidget_Unloaded(object sender, RoutedEventArgs e)
{
Unloaded -= QuickAccessWidget_Unloaded;
App.QuickAccessManager.UpdateQuickAccessWidget += ModifyItem;
App.QuickAccessManager.UpdateQuickAccessWidget -= ModifyItem;
}

private void MenuFlyout_Opening(object sender, object e)
Expand Down Expand Up @@ -301,13 +293,16 @@ private async void PinToFavorites_Click(object sender, RoutedEventArgs e)
var item = ((MenuFlyoutItem)sender).DataContext as FolderCardItem;
await QuickAccessService.PinToSidebar(item.Path);
ModifyItem(this, new ModifyQuickAccessEventArgs(new[] { item.Path }, false));
var items = await QuickAccessService.GetPinnedFoldersAsync(true);
items.RemoveRange(0, items.Count - 4);
var recentItem = items.Where(x => !ItemsAdded.Select(y => y.Path).Contains(x)).FirstOrDefault();
ModifyItem(this, new ModifyQuickAccessEventArgs(new[] { recentItem }, true)
var items = (await QuickAccessService.GetPinnedFoldersAsync())
.Where(link => !((bool?)link.Properties["System.Home.IsPinned"] ?? false));
var recentItem = items.Where(x => !ItemsAdded.Select(y => y.Path).Contains(x.FilePath)).FirstOrDefault();
if (recentItem is not null)
{
Pin = false
});
ModifyItem(this, new ModifyQuickAccessEventArgs(new[] { recentItem.FilePath }, true)
{
Pin = false
});
}
}

private void UnpinFromFavorites_Click(object sender, RoutedEventArgs e)
Expand All @@ -322,11 +317,6 @@ private Task OpenCard(FolderCardItem item)
{
return Task.CompletedTask;
}
if (item.Item is LibraryLocationItem lli && lli.IsEmpty)
{
// TODO: show message?
return Task.CompletedTask;
}

var ctrlPressed = Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Control).HasFlag(CoreVirtualKeyStates.Down);
if (ctrlPressed)
Expand Down
5 changes: 3 additions & 2 deletions src/Files.Backend/Services/IQuickAccessService.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using Files.Shared;
using System.Collections.Generic;
using System.Threading.Tasks;

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

/// <summary>
/// Pins a folder to the quick access list
Expand Down
5 changes: 4 additions & 1 deletion src/Files.Shared/ShellFileItem.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;

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

public ShellFileItem()
{
Properties = new Dictionary<string, object?>();
}

public ShellFileItem(
bool isFolder, string recyclePath, string fileName, string filePath,
DateTime recycleDate, DateTime modifiedDate, DateTime createdDate, string fileSize, ulong fileSizeBytes, string fileType, byte[] pidl)
DateTime recycleDate, DateTime modifiedDate, DateTime createdDate, string fileSize, ulong fileSizeBytes, string fileType, byte[] pidl) : this()
{
IsFolder = isFolder;
RecyclePath = recyclePath;
Expand Down