Skip to content

Commit

Permalink
Code Quality: Added design and basic functionality for the upcoming S…
Browse files Browse the repository at this point in the history
…helf feature (#16673)
  • Loading branch information
d2dyno1 authored Jan 6, 2025
1 parent 7ca7ffd commit 1024760
Show file tree
Hide file tree
Showing 8 changed files with 244 additions and 12 deletions.
7 changes: 2 additions & 5 deletions src/Files.App/Data/Contracts/ImagingService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,12 @@ internal sealed class ImagingService : IImageService
/// <inheritdoc/>
public async Task<IImage?> 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<IImage?> GetImageModelFromDataAsync(byte[] rawData)
Expand Down
40 changes: 40 additions & 0 deletions src/Files.App/Data/Items/ShelfItem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using Files.Shared.Utils;

namespace Files.App.Data.Items
{
[Bindable(true)]
public sealed partial class ShelfItem : ObservableObject, IWrapper<IStorable>, IAsyncInitialize
{
private readonly IImageService _imageService;
private readonly ICollection<ShelfItem> _sourceCollection;

[ObservableProperty] private IImage? _Icon;
[ObservableProperty] private string? _Name;
[ObservableProperty] private string? _Path;

/// <inheritdoc/>
public IStorable Inner { get; }

public ShelfItem(IStorable storable, ICollection<ShelfItem> sourceCollection, IImage? icon = null)
{
_imageService = Ioc.Default.GetRequiredService<IImageService>();
_sourceCollection = sourceCollection;
Inner = storable;
Icon = icon;
Name = storable.Name;
Path = storable.Id;
}

/// <inheritdoc/>
public async Task InitAsync(CancellationToken cancellationToken = default)
{
Icon = await _imageService.GetIconAsync(Inner, cancellationToken);
}

[RelayCommand]
private void Remove()
{
_sourceCollection.Remove(this);
}
}
}
14 changes: 14 additions & 0 deletions src/Files.App/Strings/en-US/Resources.resw
Original file line number Diff line number Diff line change
Expand Up @@ -4022,4 +4022,18 @@
<data name="ShowShelfPane" xml:space="preserve">
<value>Show Shelf Pane</value>
</data>
<data name="Shelf" xml:space="preserve">
<value>Shelf</value>
<comment>'Shelf' refers to the Shelf Pane feature, where users can conveniently drag and drop files for quick access and perform bulk actions with ease.</comment>
</data>
<data name="ClearItems" xml:space="preserve">
<value>Clear items</value>
</data>
<data name="RemoveFromShelf" xml:space="preserve">
<value>Remove from shelf</value>
</data>
<data name="AddToShelf" xml:space="preserve">
<value>Add to Shelf</value>
<comment>Tooltip that displays when dragging items to the Shelf Pane</comment>
</data>
</root>
87 changes: 84 additions & 3 deletions src/Files.App/UserControls/Pane/ShelfPane.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -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">

<UserControl.Resources>
<converters:ImageModelToImageConverter x:Key="ImageModelToImageConverter" />
</UserControl.Resources>

<Grid
Width="240"
Padding="12"
AllowDrop="True"
Background="{ThemeResource App.Theme.InfoPane.BackgroundBrush}"
BackgroundSizing="InnerBorderEdge"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
BorderThickness="1"
CornerRadius="8" />
CornerRadius="8"
DragOver="Shelf_DragOver"
Drop="Shelf_Drop"
RowSpacing="8">

<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>

<StackPanel Grid.Row="0" Spacing="8">

<!-- Title -->
<TextBlock
HorizontalAlignment="Center"
Foreground="{ThemeResource TextFillColorTertiaryBrush}"
Style="{StaticResource App.Theme.BodyTextBlockStyle}"
Text="{helpers:ResourceString Name=Shelf}" />

<!-- (Divider) -->
<Border Height="1" Background="{ThemeResource DividerStrokeColorDefaultBrush}" />

</StackPanel>

<!-- Items List -->
<ListView
Grid.Row="1"
DragItemsStarting="ListView_DragItemsStarting"
ItemsSource="{x:Bind ItemsSource, Mode=OneWay}"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollMode="Auto"
SelectionMode="Extended">
<ListView.ItemTemplate>
<DataTemplate x:DataType="data:ShelfItem">
<StackPanel Orientation="Horizontal" Spacing="8">
<Image
Width="16"
Height="16"
Source="{x:Bind Icon, Mode=OneWay, Converter={StaticResource ImageModelToImageConverter}}" />
<TextBlock Text="{x:Bind Name, Mode=OneWay}" />

<StackPanel.ContextFlyout>
<MenuFlyout>
<MenuFlyoutItem Command="{x:Bind RemoveCommand}" Text="{helpers:ResourceString Name=RemoveFromShelf}">
<MenuFlyoutItem.Icon>
<FontIcon Glyph="&#xE738;" />
</MenuFlyoutItem.Icon>
</MenuFlyoutItem>
</MenuFlyout>
</StackPanel.ContextFlyout>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>

<ListView.ItemContainerStyle>
<Style BasedOn="{StaticResource DefaultListViewItemStyle}" TargetType="ListViewItem">
<Setter Property="Margin" Value="-4,0,-4,0" />
<Setter Property="MinHeight" Value="36" />
</Style>
</ListView.ItemContainerStyle>
</ListView>


<StackPanel Grid.Row="2" Spacing="4">

<!-- (Divider) -->
<Border Height="1" Background="{ThemeResource DividerStrokeColorDefaultBrush}" />

<!-- Bottom Actions -->
<HyperlinkButton
HorizontalAlignment="Center"
VerticalAlignment="Center"
Command="{x:Bind ClearCommand, Mode=OneWay}"
Content="{helpers:ResourceString Name=ClearItems}" />

</StackPanel>
</Grid>
</UserControl>
88 changes: 88 additions & 0 deletions src/Files.App/UserControls/Pane/ShelfPane.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,103 @@
// 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
{
public sealed partial class ShelfPane : UserControl
{
public ShelfPane()
{
// TODO: [Shelf] Remove once view model is connected
ItemsSource = new ObservableCollection<ShelfItem>();

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<IStorageService>();
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<IDataObject>(HWND.NULL, shellItemList);
if (iddo is null)
return;

shellItemList.ForEach(x => x.Dispose());
var dataObjectProvider = e.Data.As<Shell32.IDataObjectProvider>();
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<ShelfItem>? ItemsSource
{
get => (IList<ShelfItem>?)GetValue(ItemsSourceProperty);
set => SetValue(ItemsSourceProperty, value);
}
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register(nameof(ItemsSource), typeof(IList<ShelfItem>), 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));
}
}
1 change: 1 addition & 0 deletions src/Files.App/Views/MainPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@
ShowInfoText="{x:Bind SidebarAdaptiveViewModel.PaneHolder.ActivePaneOrColumn.InstanceViewModel.IsPageTypeNotHome, Mode=OneWay}"
Visibility="{x:Bind SidebarAdaptiveViewModel.PaneHolder.ActivePaneOrColumn.InstanceViewModel.IsPageTypeNotHome, Mode=OneWay}" />

<!-- Shelf Pane -->
<uc:ShelfPane
x:Name="ShelfPane"
Grid.Row="0"
Expand Down
5 changes: 1 addition & 4 deletions src/Files.Shared/Utils/IAsyncInitialize.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
// Copyright (c) Files Community
// Licensed under the MIT License.

using System.Threading;
using System.Threading;
using System.Threading.Tasks;

namespace Files.Shared.Utils
Expand Down
14 changes: 14 additions & 0 deletions src/Files.Shared/Utils/IWrapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace Files.Shared.Utils
{
/// <summary>
/// Wraps and exposes <typeparamref name="T"/> implementation for access.
/// </summary>
/// <typeparam name="T">The wrapped type.</typeparam>
public interface IWrapper<out T>
{
/// <summary>
/// Gets the inner member wrapped by the implementation.
/// </summary>
T Inner { get; }
}
}

0 comments on commit 1024760

Please sign in to comment.