From 1024760cef6db5a83ed3adc00ba2bc653b0db072 Mon Sep 17 00:00:00 2001
From: d2dyno <53011783+d2dyno1@users.noreply.github.com>
Date: Mon, 6 Jan 2025 03:50:22 +0100
Subject: [PATCH] Code Quality: Added design and basic functionality for the
upcoming Shelf feature (#16673)
---
.../Data/Contracts/ImagingService.cs | 7 +-
src/Files.App/Data/Items/ShelfItem.cs | 40 +++++++++
src/Files.App/Strings/en-US/Resources.resw | 14 +++
.../UserControls/Pane/ShelfPane.xaml | 87 +++++++++++++++++-
.../UserControls/Pane/ShelfPane.xaml.cs | 88 +++++++++++++++++++
src/Files.App/Views/MainPage.xaml | 1 +
src/Files.Shared/Utils/IAsyncInitialize.cs | 5 +-
src/Files.Shared/Utils/IWrapper.cs | 14 +++
8 files changed, 244 insertions(+), 12 deletions(-)
create mode 100644 src/Files.App/Data/Items/ShelfItem.cs
create mode 100644 src/Files.Shared/Utils/IWrapper.cs
diff --git a/src/Files.App/Data/Contracts/ImagingService.cs b/src/Files.App/Data/Contracts/ImagingService.cs
index 390824ac9d93..30d3dcd0a427 100644
--- a/src/Files.App/Data/Contracts/ImagingService.cs
+++ b/src/Files.App/Data/Contracts/ImagingService.cs
@@ -13,15 +13,12 @@ internal sealed class ImagingService : IImageService
///
public async Task GetIconAsync(IStorable storable, CancellationToken cancellationToken)
{
- if (storable is not ILocatableStorable locatableStorable)
- return null;
-
- var iconData = await FileThumbnailHelper.LoadIconFromPathAsync(locatableStorable.Path, 24u, ThumbnailMode.ListView, ThumbnailOptions.ResizeThumbnail);
+ var iconData = await FileThumbnailHelper.GetIconAsync(storable.Id, Constants.ShellIconSizes.Small, storable is IFolder, IconOptions.ReturnIconOnly | IconOptions.UseCurrentScale);
if (iconData is null)
return null;
var bitmapImage = await iconData.ToBitmapAsync();
- return new BitmapImageModel(bitmapImage);
+ return bitmapImage is null ? null : new BitmapImageModel(bitmapImage);
}
public async Task GetImageModelFromDataAsync(byte[] rawData)
diff --git a/src/Files.App/Data/Items/ShelfItem.cs b/src/Files.App/Data/Items/ShelfItem.cs
new file mode 100644
index 000000000000..7facfabce5b9
--- /dev/null
+++ b/src/Files.App/Data/Items/ShelfItem.cs
@@ -0,0 +1,40 @@
+using Files.Shared.Utils;
+
+namespace Files.App.Data.Items
+{
+ [Bindable(true)]
+ public sealed partial class ShelfItem : ObservableObject, IWrapper, IAsyncInitialize
+ {
+ private readonly IImageService _imageService;
+ private readonly ICollection _sourceCollection;
+
+ [ObservableProperty] private IImage? _Icon;
+ [ObservableProperty] private string? _Name;
+ [ObservableProperty] private string? _Path;
+
+ ///
+ public IStorable Inner { get; }
+
+ public ShelfItem(IStorable storable, ICollection sourceCollection, IImage? icon = null)
+ {
+ _imageService = Ioc.Default.GetRequiredService();
+ _sourceCollection = sourceCollection;
+ Inner = storable;
+ Icon = icon;
+ Name = storable.Name;
+ Path = storable.Id;
+ }
+
+ ///
+ public async Task InitAsync(CancellationToken cancellationToken = default)
+ {
+ Icon = await _imageService.GetIconAsync(Inner, cancellationToken);
+ }
+
+ [RelayCommand]
+ private void Remove()
+ {
+ _sourceCollection.Remove(this);
+ }
+ }
+}
diff --git a/src/Files.App/Strings/en-US/Resources.resw b/src/Files.App/Strings/en-US/Resources.resw
index 02dbe67ff39d..a219e91f76cc 100644
--- a/src/Files.App/Strings/en-US/Resources.resw
+++ b/src/Files.App/Strings/en-US/Resources.resw
@@ -4022,4 +4022,18 @@
Show Shelf Pane
+
+ Shelf
+ 'Shelf' refers to the Shelf Pane feature, where users can conveniently drag and drop files for quick access and perform bulk actions with ease.
+
+
+ Clear items
+
+
+ Remove from shelf
+
+
+ Add to Shelf
+ Tooltip that displays when dragging items to the Shelf Pane
+
\ No newline at end of file
diff --git a/src/Files.App/UserControls/Pane/ShelfPane.xaml b/src/Files.App/UserControls/Pane/ShelfPane.xaml
index 1804873e84d6..952b0ca4861b 100644
--- a/src/Files.App/UserControls/Pane/ShelfPane.xaml
+++ b/src/Files.App/UserControls/Pane/ShelfPane.xaml
@@ -3,20 +3,101 @@
x:Class="Files.App.UserControls.ShelfPane"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:controls="using:Files.App.Controls"
xmlns:converters="using:Files.App.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:data="using:Files.App.Data.Items"
xmlns:helpers="using:Files.App.Helpers"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
- xmlns:usercontrols="using:Files.App.UserControls"
mc:Ignorable="d">
+
+
+
+
+ CornerRadius="8"
+ DragOver="Shelf_DragOver"
+ Drop="Shelf_Drop"
+ RowSpacing="8">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Files.App/UserControls/Pane/ShelfPane.xaml.cs b/src/Files.App/UserControls/Pane/ShelfPane.xaml.cs
index 25946f599056..baf685530c70 100644
--- a/src/Files.App/UserControls/Pane/ShelfPane.xaml.cs
+++ b/src/Files.App/UserControls/Pane/ShelfPane.xaml.cs
@@ -1,7 +1,13 @@
// Copyright (c) Files Community
// Licensed under the MIT License.
+using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
+using System.Runtime.InteropServices.ComTypes;
+using System.Windows.Input;
+using Vanara.PInvoke;
+using Windows.ApplicationModel.DataTransfer;
+using WinRT;
namespace Files.App.UserControls
{
@@ -9,7 +15,89 @@ public sealed partial class ShelfPane : UserControl
{
public ShelfPane()
{
+ // TODO: [Shelf] Remove once view model is connected
+ ItemsSource = new ObservableCollection();
+
InitializeComponent();
}
+
+ private void Shelf_DragOver(object sender, DragEventArgs e)
+ {
+ if (!FilesystemHelpers.HasDraggedStorageItems(e.DataView))
+ return;
+
+ e.Handled = true;
+ e.DragUIOverride.Caption = Strings.AddToShelf.GetLocalizedResource();
+ e.AcceptedOperation = DataPackageOperation.Link;
+ }
+
+ private async void Shelf_Drop(object sender, DragEventArgs e)
+ {
+ if (ItemsSource is null)
+ return;
+
+ // Get items
+ var storageService = Ioc.Default.GetRequiredService();
+ var storageItems = (await FilesystemHelpers.GetDraggedStorageItems(e.DataView)).ToArray();
+
+ // Add to list
+ foreach (var item in storageItems)
+ {
+ var storable = item switch
+ {
+ StorageFileWithPath => (IStorable?)await storageService.TryGetFileAsync(item.Path),
+ StorageFolderWithPath => (IStorable?)await storageService.TryGetFolderAsync(item.Path),
+ _ => null
+ };
+
+ if (storable is null)
+ continue;
+
+ var shelfItem = new ShelfItem(storable, ItemsSource);
+ _ = shelfItem.InitAsync();
+
+ ItemsSource.Add(shelfItem);
+ }
+ }
+
+ private void ListView_DragItemsStarting(object sender, DragItemsStartingEventArgs e)
+ {
+ if (ItemsSource is null)
+ return;
+
+ var shellItemList = SafetyExtensions.IgnoreExceptions(() => ItemsSource.Select(x => new Vanara.Windows.Shell.ShellItem(x.Inner.Id)).ToArray());
+ if (shellItemList?[0].FileSystemPath is not null)
+ {
+ var iddo = shellItemList[0].Parent?.GetChildrenUIObjects(HWND.NULL, shellItemList);
+ if (iddo is null)
+ return;
+
+ shellItemList.ForEach(x => x.Dispose());
+ var dataObjectProvider = e.Data.As();
+ dataObjectProvider.SetDataObject(iddo);
+ }
+ else
+ {
+ // Only support IStorageItem capable paths
+ var storageItems = ItemsSource.Select(x => VirtualStorageItem.FromPath(x.Inner.Id));
+ e.Data.SetStorageItems(storageItems, false);
+ }
+ }
+
+ public IList? ItemsSource
+ {
+ get => (IList?)GetValue(ItemsSourceProperty);
+ set => SetValue(ItemsSourceProperty, value);
+ }
+ public static readonly DependencyProperty ItemsSourceProperty =
+ DependencyProperty.Register(nameof(ItemsSource), typeof(IList), typeof(ShelfPane), new PropertyMetadata(null));
+
+ public ICommand? ClearCommand
+ {
+ get => (ICommand?)GetValue(ClearCommandProperty);
+ set => SetValue(ClearCommandProperty, value);
+ }
+ public static readonly DependencyProperty ClearCommandProperty =
+ DependencyProperty.Register(nameof(ClearCommand), typeof(ICommand), typeof(ShelfPane), new PropertyMetadata(null));
}
}
diff --git a/src/Files.App/Views/MainPage.xaml b/src/Files.App/Views/MainPage.xaml
index 0c9da874c568..74aec3c6d77c 100644
--- a/src/Files.App/Views/MainPage.xaml
+++ b/src/Files.App/Views/MainPage.xaml
@@ -248,6 +248,7 @@
ShowInfoText="{x:Bind SidebarAdaptiveViewModel.PaneHolder.ActivePaneOrColumn.InstanceViewModel.IsPageTypeNotHome, Mode=OneWay}"
Visibility="{x:Bind SidebarAdaptiveViewModel.PaneHolder.ActivePaneOrColumn.InstanceViewModel.IsPageTypeNotHome, Mode=OneWay}" />
+
+ /// Wraps and exposes implementation for access.
+ ///
+ /// The wrapped type.
+ public interface IWrapper
+ {
+ ///
+ /// Gets the inner member wrapped by the implementation.
+ ///
+ T Inner { get; }
+ }
+}