diff --git a/src/Files.App/DataModels/NavigationControlItems/LocationItem.cs b/src/Files.App/DataModels/NavigationControlItems/LocationItem.cs
index 2ad2908b9cee..d08d0849b50a 100644
--- a/src/Files.App/DataModels/NavigationControlItems/LocationItem.cs
+++ b/src/Files.App/DataModels/NavigationControlItems/LocationItem.cs
@@ -56,6 +56,8 @@ public bool IsExpanded
public bool IsInvalid { get; set; } = false;
+ public bool IsPinned => App.QuickAccessManager.Model.FavoriteItems.Contains(path);
+
public SectionType Section { get; set; }
public ContextMenuOptions MenuOptions { get; set; }
diff --git a/src/Files.App/DataModels/SidebarPinnedModel.cs b/src/Files.App/DataModels/SidebarPinnedModel.cs
index 3694ecf4e95c..a707da12502c 100644
--- a/src/Files.App/DataModels/SidebarPinnedModel.cs
+++ b/src/Files.App/DataModels/SidebarPinnedModel.cs
@@ -75,12 +75,7 @@ public int IndexOfItem(INavigationControlItem locationItem)
}
}
- ///
- /// Adds the item (from a path) to the navigation sidebar
- ///
- /// The path which to save
- /// Task
- public async Task AddItemToSidebarAsync(string path)
+ public async Task CreateLocationItemFromPathAsync(string path)
{
var item = await FilesystemTasks.Wrap(() => DrivesManager.GetRootFromPathAsync(path));
var res = await FilesystemTasks.Wrap(() => StorageFileExtensions.DangerousGetFolderFromPathAsync(path, item));
@@ -130,6 +125,18 @@ public async Task AddItemToSidebarAsync(string path)
Debug.WriteLine($"Pinned item was invalid {res.ErrorCode}, item: {path}");
}
+ return locationItem;
+ }
+
+ ///
+ /// Adds the item (from a path) to the navigation sidebar
+ ///
+ /// The path which to save
+ /// Task
+ public async Task AddItemToSidebarAsync(string path)
+ {
+ var locationItem = await CreateLocationItemFromPathAsync(path);
+
AddLocationItemToSidebar(locationItem);
}
diff --git a/src/Files.App/Filesystem/QuickAccessManager.cs b/src/Files.App/Filesystem/QuickAccessManager.cs
index 543a01e4608e..d1a54d00bcb7 100644
--- a/src/Files.App/Filesystem/QuickAccessManager.cs
+++ b/src/Files.App/Filesystem/QuickAccessManager.cs
@@ -1,6 +1,7 @@
using CommunityToolkit.Mvvm.DependencyInjection;
using Files.App.DataModels;
using Files.App.ServicesImplementation;
+using Files.App.UserControls.Widgets;
using Files.Shared.Extensions;
using System;
using System.Collections.Generic;
@@ -16,7 +17,11 @@ namespace Files.App.Filesystem
public sealed class QuickAccessManager
{
public FileSystemWatcher? PinnedItemsWatcher;
+
public event FileSystemEventHandler? PinnedItemsModified;
+
+ public EventHandler? UpdateQuickAccessWidget;
+
public IQuickAccessService QuickAccessService { get; } = Ioc.Default.GetRequiredService();
public SidebarPinnedModel Model;
diff --git a/src/Files.App/Helpers/WidgetsHelpers.cs b/src/Files.App/Helpers/WidgetsHelpers.cs
index 60a13691c0ef..6b2a40a75f29 100644
--- a/src/Files.App/Helpers/WidgetsHelpers.cs
+++ b/src/Files.App/Helpers/WidgetsHelpers.cs
@@ -37,9 +37,9 @@ public static class WidgetsHelpers
public static bool TryGetIsWidgetSettingEnabled(IPreferencesSettingsService preferencesSettingsService) where TWidget : IWidgetItemModel
{
- if (typeof(TWidget) == typeof(FolderWidget))
+ if (typeof(TWidget) == typeof(QuickAccessWidget))
{
- return preferencesSettingsService.ShowFoldersWidget;
+ return preferencesSettingsService.ShowQuickAccessWidget;
}
if (typeof(TWidget) == typeof(DrivesWidget))
{
diff --git a/src/Files.App/ServicesImplementation/QuickAccessService.cs b/src/Files.App/ServicesImplementation/QuickAccessService.cs
index 5b9e48ecda4a..9b9b42e11d3a 100644
--- a/src/Files.App/ServicesImplementation/QuickAccessService.cs
+++ b/src/Files.App/ServicesImplementation/QuickAccessService.cs
@@ -1,8 +1,13 @@
-using Files.App.Shell;
+using Files.App.DataModels.NavigationControlItems;
+using Files.App.Filesystem;
+using Files.App.Shell;
+using Files.App.UserControls.Widgets;
+using Files.Shared;
using Files.Shared.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Security.Permissions;
using System.Threading.Tasks;
namespace Files.App.ServicesImplementation
@@ -10,19 +15,19 @@ namespace Files.App.ServicesImplementation
internal class QuickAccessService : IQuickAccessService
{
private readonly static string guid = "::{679f85cb-0220-4080-b29b-5540cc05aab6}";
-
- public async Task> GetPinnedFoldersAsync()
+
+ public async Task> GetPinnedFoldersAsync(bool getRecentItems = false)
{
var sidebarItems = (await Win32Shell.GetShellFolderAsync(guid, "Enumerate", 0, 10000)).Enumerate
.Where(link => link.IsFolder)
.Select(link => link.FilePath).ToList();
- if (sidebarItems.Count > 4) // Avoid first opening crash #11139
+ 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;
}
-
+
public async Task PinToSidebar(string folderPath)
=> await PinToSidebar(new[] { folderPath });
@@ -30,8 +35,10 @@ 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 });
@@ -48,6 +55,8 @@ await SafetyExtensions.IgnoreExceptions(async () => {
});
await App.QuickAccessManager.Model.LoadAsync();
+
+ App.QuickAccessManager.UpdateQuickAccessWidget?.Invoke(this, new ModifyQuickAccessEventArgs(folderPaths, false));
}
}
}
diff --git a/src/Files.App/ServicesImplementation/Settings/PreferencesSettingsService.cs b/src/Files.App/ServicesImplementation/Settings/PreferencesSettingsService.cs
index 50b78ecfff26..54612ddd3a86 100644
--- a/src/Files.App/ServicesImplementation/Settings/PreferencesSettingsService.cs
+++ b/src/Files.App/ServicesImplementation/Settings/PreferencesSettingsService.cs
@@ -81,7 +81,7 @@ public bool AlwaysOpenDualPaneInNewTab
set => Set(value);
}
- public bool ShowFoldersWidget
+ public bool ShowQuickAccessWidget
{
get => Get(true);
set => Set(value);
@@ -194,7 +194,7 @@ protected override void RaiseOnSettingChangedEvent(object sender, SettingChanged
case nameof(AlwaysOpenNewInstance):
case nameof(IsDualPaneEnabled):
case nameof(AlwaysOpenDualPaneInNewTab):
- case nameof(ShowFoldersWidget):
+ case nameof(ShowQuickAccessWidget):
case nameof(ShowRecentFilesWidget):
case nameof(ShowDrivesWidget):
case nameof(ShowBundlesWidget):
diff --git a/src/Files.App/Strings/en-US/Resources.resw b/src/Files.App/Strings/en-US/Resources.resw
index 64bae7e48b8a..1764cda41b9a 100644
--- a/src/Files.App/Strings/en-US/Resources.resw
+++ b/src/Files.App/Strings/en-US/Resources.resw
@@ -2289,9 +2289,6 @@
Drives Widget
-
- Folders Widget
-
Recent Files Widget
@@ -2900,6 +2897,9 @@
Display the edit tags flyout
+
+
+ Quick access
Enter your credentials to connect to: {0}
diff --git a/src/Files.App/UserControls/Widgets/FolderWidget.xaml b/src/Files.App/UserControls/Widgets/QuickAccessWidget.xaml
similarity index 78%
rename from src/Files.App/UserControls/Widgets/FolderWidget.xaml
rename to src/Files.App/UserControls/Widgets/QuickAccessWidget.xaml
index 7da6d737f348..7faeb5718242 100644
--- a/src/Files.App/UserControls/Widgets/FolderWidget.xaml
+++ b/src/Files.App/UserControls/Widgets/QuickAccessWidget.xaml
@@ -1,5 +1,5 @@
+
+
+
+
+
+
+
+
+
+
@@ -85,7 +104,6 @@
-
+
{
private BitmapImage thumbnail;
@@ -56,18 +74,20 @@ public BitmapImage Thumbnail
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) : this(text)
+ public FolderCardItem(LocationItem item = null, string text = null, bool isPinned = true) : this(text, isPinned)
{
Item = item;
}
- public FolderCardItem(string text)
+ public FolderCardItem(string text, bool isPinned)
{
if (!string.IsNullOrWhiteSpace(text))
{
Text = text;
AutomationProperties = Text;
+ IsPinned = isPinned;
}
}
@@ -84,43 +104,45 @@ public async Task LoadCardThumbnailAsync()
}
}
- public sealed partial class FolderWidget : UserControl, IWidgetItemModel, INotifyPropertyChanged
+ public sealed partial class QuickAccessWidget : UserControl, IWidgetItemModel, INotifyPropertyChanged
{
private IUserSettingsService UserSettingsService { get; } = Ioc.Default.GetRequiredService();
+ private readonly IQuickAccessService QuickAccessService = Ioc.Default.GetRequiredService();
+
public ObservableCollection ItemsAdded = new();
private bool showMultiPaneControls;
- public FolderWidget()
+ public QuickAccessWidget()
{
InitializeComponent();
- LibraryCardCommand = new AsyncRelayCommand(OpenLibraryCard);
+ QuickAccessCardCommand = new AsyncRelayCommand(OpenCard);
- Loaded += FolderWidget_Loaded;
- Unloaded += FolderWidget_Unloaded;
+ Loaded += QuickAccessWidget_Loaded;
+ Unloaded += QuickAccessWidget_Unloaded;
}
- public delegate void LibraryCardInvokedEventHandler(object sender, LibraryCardInvokedEventArgs e);
+ public delegate void LibraryCardInvokedEventHandler(object sender, QuickAccessCardInvokedEventArgs e);
- public delegate void LibraryCardNewPaneInvokedEventHandler(object sender, LibraryCardInvokedEventArgs e);
+ public delegate void LibraryCardNewPaneInvokedEventHandler(object sender, QuickAccessCardInvokedEventArgs e);
- public delegate void LibraryCardPropertiesInvokedEventHandler(object sender, LibraryCardEventArgs e);
+ public delegate void LibraryCardPropertiesInvokedEventHandler(object sender, QuickAccessCardEventArgs e);
- public event LibraryCardInvokedEventHandler LibraryCardInvoked;
+ public event LibraryCardInvokedEventHandler CardInvoked;
- public event LibraryCardNewPaneInvokedEventHandler LibraryCardNewPaneInvoked;
+ public event LibraryCardNewPaneInvokedEventHandler CardNewPaneInvoked;
- public event LibraryCardPropertiesInvokedEventHandler LibraryCardPropertiesInvoked;
+ public event LibraryCardPropertiesInvokedEventHandler CardPropertiesInvoked;
- public event EventHandler FolderWidgethowMultiPaneControlsInvoked;
+ public event EventHandler QuickAccessWidgetShowMultiPaneControlsInvoked;
public event PropertyChangedEventHandler PropertyChanged;
- public bool IsWidgetSettingEnabled => UserSettingsService.PreferencesSettingsService.ShowFoldersWidget;
+ public bool IsWidgetSettingEnabled => UserSettingsService.PreferencesSettingsService.ShowQuickAccessWidget;
- public ICommand LibraryCardCommand { get; }
+ public ICommand QuickAccessCardCommand { get; }
public ICommand ShowCreateNewLibraryDialogCommand { get; } = new RelayCommand(LibraryManager.ShowCreateNewLibraryDialog);
@@ -130,7 +152,7 @@ public bool ShowMultiPaneControls
{
get
{
- FolderWidgethowMultiPaneControlsInvoked?.Invoke(this, EventArgs.Empty);
+ QuickAccessWidgetShowMultiPaneControlsInvoked?.Invoke(this, EventArgs.Empty);
return showMultiPaneControls;
}
@@ -144,54 +166,69 @@ public bool ShowMultiPaneControls
}
}
- public string WidgetName => nameof(FolderWidget);
+ public string WidgetName => nameof(QuickAccessWidget);
- public string AutomationProperties => "FolderWidgetAutomationProperties/Name".GetLocalizedResource();
+ public string AutomationProperties => "QuickAccess".GetLocalizedResource();
- public string WidgetHeader => "Folders".GetLocalizedResource();
+ public string WidgetHeader => "QuickAccess".GetLocalizedResource();
- private async void FolderWidget_Loaded(object sender, RoutedEventArgs e)
+ private async void ModifyItem(object? sender, ModifyQuickAccessEventArgs? e)
{
- Loaded -= FolderWidget_Loaded;
+ if (e is null)
+ return;
- ItemsAdded.Add(new FolderCardItem("Desktop".GetLocalizedResource())
- {
- Path = UserDataPaths.GetDefault().Desktop,
- SelectCommand = LibraryCardCommand
- });
- ItemsAdded.Add(new FolderCardItem("Documents".GetLocalizedResource())
+ if (e.Add)
{
- Path = UserDataPaths.GetDefault().Documents,
- SelectCommand = LibraryCardCommand
- });
- ItemsAdded.Add(new FolderCardItem("Downloads".GetLocalizedResource())
- {
- Path = UserDataPaths.GetDefault().Downloads,
- SelectCommand = LibraryCardCommand
- });
- ItemsAdded.Add(new FolderCardItem("Music".GetLocalizedResource())
- {
- Path = UserDataPaths.GetDefault().Music,
- SelectCommand = LibraryCardCommand
- });
- ItemsAdded.Add(new FolderCardItem("Pictures".GetLocalizedResource())
- {
- Path = UserDataPaths.GetDefault().Pictures,
- SelectCommand = LibraryCardCommand
- });
- ItemsAdded.Add(new FolderCardItem("Videos".GetLocalizedResource())
+ var locationItems = new List();
+ 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
+ {
+ 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();
+ foreach (var item in itemsToAdd)
+ locationItems.Add(await App.QuickAccessManager.Model.CreateLocationItemFromPathAsync(item));
+
+ int idx = 0;
+ foreach (var item in locationItems)
{
- Path = UserDataPaths.GetDefault().Videos,
- SelectCommand = LibraryCardCommand
- });
+ ItemsAdded.Add(new FolderCardItem(item, Path.GetFileName(item.Text), idx < locationItems.Count - 4)
+ {
+ Path = item.Path,
+ SelectCommand = QuickAccessCardCommand
+ });
+ idx++;
+ }
+
+ App.QuickAccessManager.UpdateQuickAccessWidget += ModifyItem;
var cardLoadTasks = ItemsAdded.Select(cardItem => cardItem.LoadCardThumbnailAsync());
await Task.WhenAll(cardLoadTasks);
}
- private void FolderWidget_Unloaded(object sender, RoutedEventArgs e)
+ private void QuickAccessWidget_Unloaded(object sender, RoutedEventArgs e)
{
- Unloaded -= FolderWidget_Unloaded;
+ Unloaded -= QuickAccessWidget_Unloaded;
+ App.QuickAccessManager.UpdateQuickAccessWidget += ModifyItem;
}
private void MenuFlyout_Opening(object sender, object e)
@@ -199,9 +236,15 @@ private void MenuFlyout_Opening(object sender, object e)
var newPaneMenuItem = (sender as MenuFlyout).Items.SingleOrDefault(x => x.Name == "OpenInNewPane");
// eg. an empty library doesn't have OpenInNewPane context menu item
if (newPaneMenuItem is not null)
- {
newPaneMenuItem.Visibility = ShowMultiPaneControls ? Visibility.Visible : Visibility.Collapsed;
- }
+
+ var pinToFavoritesItem = (sender as MenuFlyout).Items.SingleOrDefault(x => x.Name == "PinToFavorites");
+ if (pinToFavoritesItem is not null)
+ pinToFavoritesItem.Visibility = (pinToFavoritesItem.DataContext as FolderCardItem).IsPinned ? Visibility.Collapsed : Visibility.Visible;
+
+ var unpinFromFavoritesItem = (sender as MenuFlyout).Items.SingleOrDefault(x => x.Name == "UnpinFromFavorites");
+ if (unpinFromFavoritesItem is not null)
+ unpinFromFavoritesItem.Visibility = (unpinFromFavoritesItem.DataContext as FolderCardItem).IsPinned ? Visibility.Visible : Visibility.Collapsed;
}
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
@@ -212,7 +255,7 @@ private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
private void OpenInNewPane_Click(object sender, RoutedEventArgs e)
{
var item = ((MenuFlyoutItem)sender).DataContext as FolderCardItem;
- LibraryCardNewPaneInvoked?.Invoke(this, new LibraryCardInvokedEventArgs { Path = item.Path });
+ CardNewPaneInvoked?.Invoke(this, new QuickAccessCardInvokedEventArgs { Path = item.Path });
}
private async void OpenInNewTab_Click(object sender, RoutedEventArgs e)
@@ -236,24 +279,44 @@ private async void OpenInNewWindow_Click(object sender, RoutedEventArgs e)
await NavigationHelpers.OpenPathInNewWindowAsync(item.Path);
}
- private void OpenLibraryProperties_Click(object sender, RoutedEventArgs e)
+ private void OpenProperties_Click(object sender, RoutedEventArgs e)
{
var presenter = DependencyObjectHelpers.FindParent((MenuFlyoutItem)sender);
var flyoutParent = presenter?.Parent as Popup;
var propertiesItem = ((MenuFlyoutItem)sender).DataContext as FolderCardItem;
- if (propertiesItem is null || !propertiesItem.IsLibrary || flyoutParent is null)
+ if (propertiesItem is null || flyoutParent is null)
return;
EventHandler