From 31aa589f82294989469ba116c37afb7fbe6d48f3 Mon Sep 17 00:00:00 2001 From: d2dyno006 <53011783+d2dyno006@users.noreply.github.com> Date: Thu, 23 Jan 2025 00:34:43 +0100 Subject: [PATCH 1/8] Begin work on dropping items --- .../UserControls/Pane/ShelfPane.xaml | 2 + .../UserControls/Pane/ShelfPane.xaml.cs | 46 +++++++++++-------- src/Files.App/Views/Layouts/BaseLayoutPage.cs | 25 ++++++---- 3 files changed, 44 insertions(+), 29 deletions(-) diff --git a/src/Files.App/UserControls/Pane/ShelfPane.xaml b/src/Files.App/UserControls/Pane/ShelfPane.xaml index de9399576fb9..987dedee0c29 100644 --- a/src/Files.App/UserControls/Pane/ShelfPane.xaml +++ b/src/Files.App/UserControls/Pane/ShelfPane.xaml @@ -50,8 +50,10 @@ x.Inner.Id == item.Path)) + continue; + var storable = item switch { StorageFileWithPath => (IStorable?)await storageService.TryGetFileAsync(item.Path), @@ -60,28 +67,27 @@ private async void Shelf_Drop(object sender, DragEventArgs e) } } - private void ListView_DragItemsStarting(object sender, DragItemsStartingEventArgs e) - { - if (ItemsSource is null) - return; + private void ListView_DragItemsStarting(object sender, DragItemsStartingEventArgs e) + { + var apidl = SafetyExtensions.IgnoreExceptions(() => e.Items + .Cast() + .Select(x => new ShellItem(x.Inner.Id).PIDL) + .ToArray()); - 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; + if (apidl 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); - } + if (!Shell32.SHCreateDataObject(null, apidl, null, out var ppDataObject).Succeeded) + return; + + e.Data.Properties["Files_ActionBinder"] = "Files_ShelfBinder"; + ppDataObject.SetData(StandardDataFormats.StorageItems, apidl); + var dataObjectProvider = e.Data.As(); + dataObjectProvider.SetDataObject(ppDataObject); + + + //var obj = new ShellDataObject(); + //ppDataObject.SetData(StandardDataFormats.StorageItems, obj); } public IList? ItemsSource diff --git a/src/Files.App/Views/Layouts/BaseLayoutPage.cs b/src/Files.App/Views/Layouts/BaseLayoutPage.cs index c38181d24653..8793ff682f73 100644 --- a/src/Files.App/Views/Layouts/BaseLayoutPage.cs +++ b/src/Files.App/Views/Layouts/BaseLayoutPage.cs @@ -1137,17 +1137,24 @@ private async void Item_DragOver(object sender, DragEventArgs e) protected virtual async void Item_Drop(object sender, DragEventArgs e) { var deferral = e.GetDeferral(); + try + { + e.Handled = true; + _ = e.Data.Properties; + var exists = e.Data.Properties.TryGetValue("Files_ActionBinder", out var val); + _ = val; - e.Handled = true; - - // Reset dragged over item - dragOverItem = null; - - var item = GetItemFromElement(sender); - if (item is not null) - await ParentShellPageInstance!.FilesystemHelpers.PerformOperationTypeAsync(e.AcceptedOperation, e.DataView, (item as ShortcutItem)?.TargetPath ?? item.ItemPath, false, true, item.IsExecutable, item.IsScriptFile); + // Reset dragged over item + dragOverItem = null; - deferral.Complete(); + var item = GetItemFromElement(sender); + if (item is not null) + await ParentShellPageInstance!.FilesystemHelpers.PerformOperationTypeAsync(e.AcceptedOperation, e.DataView, (item as ShortcutItem)?.TargetPath ?? item.ItemPath, false, true, item.IsExecutable, item.IsScriptFile); + } + finally + { + deferral.Complete(); + } } protected void FileList_ContainerContentChanging(ListViewBase sender, ContainerContentChangingEventArgs args) From 7610dab8c322024409ce49152959a72d5275c453 Mon Sep 17 00:00:00 2001 From: d2dyno006 <53011783+d2dyno006@users.noreply.github.com> Date: Thu, 23 Jan 2025 00:39:42 +0100 Subject: [PATCH 2/8] Update ShelfPane.xaml.cs --- src/Files.App/UserControls/Pane/ShelfPane.xaml.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Files.App/UserControls/Pane/ShelfPane.xaml.cs b/src/Files.App/UserControls/Pane/ShelfPane.xaml.cs index 2bcc2a71a6a4..27d05acaa236 100644 --- a/src/Files.App/UserControls/Pane/ShelfPane.xaml.cs +++ b/src/Files.App/UserControls/Pane/ShelfPane.xaml.cs @@ -81,7 +81,10 @@ private void ListView_DragItemsStarting(object sender, DragItemsStartingEventArg return; e.Data.Properties["Files_ActionBinder"] = "Files_ShelfBinder"; + + // TODO: Format is set correctly, but no items are present ppDataObject.SetData(StandardDataFormats.StorageItems, apidl); + var dataObjectProvider = e.Data.As(); dataObjectProvider.SetDataObject(ppDataObject); From f325868ae4e77b21508cf2a5fa7188ac09ad02f8 Mon Sep 17 00:00:00 2001 From: d2dyno006 <53011783+d2dyno006@users.noreply.github.com> Date: Thu, 23 Jan 2025 22:35:38 +0100 Subject: [PATCH 3/8] Update ShelfPane.xaml.cs --- .../UserControls/Pane/ShelfPane.xaml.cs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/Files.App/UserControls/Pane/ShelfPane.xaml.cs b/src/Files.App/UserControls/Pane/ShelfPane.xaml.cs index 27d05acaa236..6ce2d3cb6b66 100644 --- a/src/Files.App/UserControls/Pane/ShelfPane.xaml.cs +++ b/src/Files.App/UserControls/Pane/ShelfPane.xaml.cs @@ -77,20 +77,18 @@ private void ListView_DragItemsStarting(object sender, DragItemsStartingEventArg if (apidl is null) return; - if (!Shell32.SHCreateDataObject(null, apidl, null, out var ppDataObject).Succeeded) - return; + if (!Shell32.SHGetDesktopFolder(out var pDesktop).Succeeded) + return; - e.Data.Properties["Files_ActionBinder"] = "Files_ShelfBinder"; + if (!Shell32.SHGetIDListFromObject(pDesktop, out var pDesktopPidl).Succeeded) + return; - // TODO: Format is set correctly, but no items are present - ppDataObject.SetData(StandardDataFormats.StorageItems, apidl); + e.Data.Properties["Files_ActionBinder"] = "Files_ShelfBinder"; + if (!Shell32.SHCreateDataObject(pDesktopPidl, apidl, null, out var ppDataObject).Succeeded) + return; var dataObjectProvider = e.Data.As(); dataObjectProvider.SetDataObject(ppDataObject); - - - //var obj = new ShellDataObject(); - //ppDataObject.SetData(StandardDataFormats.StorageItems, obj); } public IList? ItemsSource From 11e55ce5ef470e48859530101adcb4621ff33fa2 Mon Sep 17 00:00:00 2001 From: d2dyno006 <53011783+d2dyno006@users.noreply.github.com> Date: Thu, 30 Jan 2025 22:54:20 +0100 Subject: [PATCH 4/8] WIP: Open flyout when dropping from Shelf --- .../ViewModels/Layouts/BaseLayoutViewModel.cs | 162 +++++++++++------- 1 file changed, 97 insertions(+), 65 deletions(-) diff --git a/src/Files.App/ViewModels/Layouts/BaseLayoutViewModel.cs b/src/Files.App/ViewModels/Layouts/BaseLayoutViewModel.cs index 9e8c4e2472d2..76c77d3eca21 100644 --- a/src/Files.App/ViewModels/Layouts/BaseLayoutViewModel.cs +++ b/src/Files.App/ViewModels/Layouts/BaseLayoutViewModel.cs @@ -11,6 +11,7 @@ using Windows.ApplicationModel.DataTransfer.DragDrop; using Windows.Storage; using Windows.System; +using Microsoft.UI.Xaml.Controls; namespace Files.App.ViewModels.Layouts { @@ -100,17 +101,22 @@ public async Task DragOverAsync(DragEventArgs e) return; } - if (FilesystemHelpers.HasDraggedStorageItems(e.DataView)) + if (!FilesystemHelpers.HasDraggedStorageItems(e.DataView)) { - e.Handled = true; - - var draggedItems = await FilesystemHelpers.GetDraggedStorageItems(e.DataView); - - var pwd = _associatedInstance.ShellViewModel.WorkingDirectory.TrimPath(); - var folderName = Path.IsPathRooted(pwd) && Path.GetPathRoot(pwd) == pwd ? Path.GetPathRoot(pwd) : Path.GetFileName(pwd); + deferral.Complete(); + return; + } + + e.Handled = true; + var draggedItems = await FilesystemHelpers.GetDraggedStorageItems(e.DataView); + var pwd = _associatedInstance.ShellViewModel.WorkingDirectory.TrimPath(); + var folderName = Path.IsPathRooted(pwd) && Path.GetPathRoot(pwd) == pwd ? Path.GetPathRoot(pwd) : Path.GetFileName(pwd); + try + { // As long as one file doesn't already belong to this folder - if (_associatedInstance.InstanceViewModel.IsPageTypeSearchResults || draggedItems.Any() && draggedItems.AreItemsAlreadyInFolder(_associatedInstance.ShellViewModel.WorkingDirectory)) + if (_associatedInstance.InstanceViewModel.IsPageTypeSearchResults || draggedItems.Any() && + draggedItems.AreItemsAlreadyInFolder(_associatedInstance.ShellViewModel.WorkingDirectory)) { e.AcceptedOperation = DataPackageOperation.None; } @@ -120,80 +126,106 @@ public async Task DragOverAsync(DragEventArgs e) } else { - try + e.DragUIOverride.IsCaptionVisible = true; + if (e.DataView.Properties.TryGetValue("Files_ActionBinder", out var actionBinder) && actionBinder is "Files_ShelfBinder") { - e.DragUIOverride.IsCaptionVisible = true; - if (pwd.StartsWith(Constants.UserEnvironmentPaths.RecycleBinPath, StringComparison.Ordinal)) - { - e.DragUIOverride.Caption = string.Format("MoveToFolderCaptionText".GetLocalizedResource(), folderName); - // Some applications such as Edge can't raise the drop event by the Move flag (#14008), so we set the Copy flag as well. - e.AcceptedOperation = DataPackageOperation.Move | DataPackageOperation.Copy; - } - else if (e.Modifiers.HasFlag(DragDropModifiers.Alt) || e.Modifiers.HasFlag(DragDropModifiers.Control | DragDropModifiers.Shift)) - { - e.DragUIOverride.Caption = string.Format("LinkToFolderCaptionText".GetLocalizedResource(), folderName); - e.AcceptedOperation = DataPackageOperation.Link; - } - else if (e.Modifiers.HasFlag(DragDropModifiers.Control)) - { - e.DragUIOverride.Caption = string.Format("CopyToFolderCaptionText".GetLocalizedResource(), folderName); - e.AcceptedOperation = DataPackageOperation.Copy; - } - else if (e.Modifiers.HasFlag(DragDropModifiers.Shift)) - { - e.DragUIOverride.Caption = string.Format("MoveToFolderCaptionText".GetLocalizedResource(), folderName); - // Some applications such as Edge can't raise the drop event by the Move flag (#14008), so we set the Copy flag as well. - e.AcceptedOperation = DataPackageOperation.Move | DataPackageOperation.Copy; - } - else if (draggedItems.Any(x => - x.Item is ZipStorageFile || - x.Item is ZipStorageFolder) || - ZipStorageFolder.IsZipPath(pwd)) - { - e.DragUIOverride.Caption = string.Format("CopyToFolderCaptionText".GetLocalizedResource(), folderName); - e.AcceptedOperation = DataPackageOperation.Copy; - } - else if (draggedItems.AreItemsInSameDrive(_associatedInstance.ShellViewModel.WorkingDirectory)) - { - e.DragUIOverride.Caption = string.Format("MoveToFolderCaptionText".GetLocalizedResource(), folderName); - // Some applications such as Edge can't raise the drop event by the Move flag (#14008), so we set the Copy flag as well. - e.AcceptedOperation = DataPackageOperation.Move | DataPackageOperation.Copy; - } - else - { - e.DragUIOverride.Caption = string.Format("CopyToFolderCaptionText".GetLocalizedResource(), folderName); - e.AcceptedOperation = DataPackageOperation.Copy; - } + e.DragUIOverride.Caption = string.Format("LinkToFolderCaptionText".GetLocalizedResource(), folderName); + e.AcceptedOperation = DataPackageOperation.Link; + } + else if (pwd.StartsWith(Constants.UserEnvironmentPaths.RecycleBinPath, StringComparison.Ordinal)) + { + e.DragUIOverride.Caption = string.Format("MoveToFolderCaptionText".GetLocalizedResource(), folderName); - _itemManipulationModel.ClearSelection(); + // Some applications such as Edge can't raise the drop event by the Move flag (#14008), so we set the Copy flag as well. + e.AcceptedOperation = DataPackageOperation.Move | DataPackageOperation.Copy; } - catch (COMException ex) when (ex.Message.Contains("RPC server is unavailable")) + else if (e.Modifiers.HasFlag(DragDropModifiers.Alt) || e.Modifiers.HasFlag(DragDropModifiers.Control | DragDropModifiers.Shift)) { - Logger?.LogDebug(ex, ex.Message); + e.DragUIOverride.Caption = string.Format("LinkToFolderCaptionText".GetLocalizedResource(), folderName); + e.AcceptedOperation = DataPackageOperation.Link; } + else if (e.Modifiers.HasFlag(DragDropModifiers.Control)) + { + e.DragUIOverride.Caption = string.Format("CopyToFolderCaptionText".GetLocalizedResource(), folderName); + e.AcceptedOperation = DataPackageOperation.Copy; + } + else if (e.Modifiers.HasFlag(DragDropModifiers.Shift)) + { + e.DragUIOverride.Caption = string.Format("MoveToFolderCaptionText".GetLocalizedResource(), folderName); + + // Some applications such as Edge can't raise the drop event by the Move flag (#14008), so we set the Copy flag as well. + e.AcceptedOperation = DataPackageOperation.Move | DataPackageOperation.Copy; + } + else if (draggedItems.Any(x => + x.Item is ZipStorageFile || + x.Item is ZipStorageFolder) || + ZipStorageFolder.IsZipPath(pwd)) + { + e.DragUIOverride.Caption = string.Format("CopyToFolderCaptionText".GetLocalizedResource(), folderName); + e.AcceptedOperation = DataPackageOperation.Copy; + } + else if (draggedItems.AreItemsInSameDrive(_associatedInstance.ShellViewModel.WorkingDirectory)) + { + e.DragUIOverride.Caption = string.Format("MoveToFolderCaptionText".GetLocalizedResource(), folderName); + + // Some applications such as Edge can't raise the drop event by the Move flag (#14008), so we set the Copy flag as well. + e.AcceptedOperation = DataPackageOperation.Move | DataPackageOperation.Copy; + } + else + { + e.DragUIOverride.Caption = string.Format("CopyToFolderCaptionText".GetLocalizedResource(), folderName); + e.AcceptedOperation = DataPackageOperation.Copy; + } + + _itemManipulationModel.ClearSelection(); } } - - deferral.Complete(); + catch (COMException ex) when (ex.Message.Contains("RPC server is unavailable")) + { + Logger?.LogDebug(ex, ex.Message); + } + finally + { + deferral.Complete(); + } } public async Task DropAsync(DragEventArgs e) { e.Handled = true; + var deferral = e.GetDeferral(); - if (FilesystemHelpers.HasDraggedStorageItems(e.DataView)) + try { - var deferral = e.GetDeferral(); + if (!FilesystemHelpers.HasDraggedStorageItems(e.DataView)) + return; - try - { - await _associatedInstance.FilesystemHelpers.PerformOperationTypeAsync(e.AcceptedOperation, e.DataView, _associatedInstance.ShellViewModel.WorkingDirectory, false, true); - await _associatedInstance.RefreshIfNoWatcherExistsAsync(); - } - finally + if (e.DataView.Properties.TryGetValue("Files_ActionBinder", out var actionBinder) && actionBinder is "Files_ShelfBinder") { - deferral.Complete(); + if (e.OriginalSource is not UIElement uiElement) + return; + + var pwd = _associatedInstance.ShellViewModel.WorkingDirectory.TrimPath(); + var folderName = Path.IsPathRooted(pwd) && Path.GetPathRoot(pwd) == pwd ? Path.GetPathRoot(pwd) : Path.GetFileName(pwd); + var menuFlyout = new MenuFlyout() + { + Items = + { + new MenuFlyoutItem() { Text = string.Format("CopyToFolderCaptionText".GetLocalizedResource(), folderName) }, + new MenuFlyoutItem() { Text = string.Format("MoveToFolderCaptionText".GetLocalizedResource(), folderName) }, + } + }; + + menuFlyout.ShowAt(uiElement, e.GetPosition(uiElement)); + return; } + + await _associatedInstance.FilesystemHelpers.PerformOperationTypeAsync(e.AcceptedOperation, e.DataView, _associatedInstance.ShellViewModel.WorkingDirectory, false, true); + await _associatedInstance.RefreshIfNoWatcherExistsAsync(); + } + finally + { + deferral.Complete(); } } From 3bea105c266f3237fdb038e376b5a2746bb9c8c8 Mon Sep 17 00:00:00 2001 From: d2dyno006 <53011783+d2dyno006@users.noreply.github.com> Date: Sat, 15 Feb 2025 11:39:25 +0100 Subject: [PATCH 5/8] Implement Copy and Move operations --- src/Files.App/ViewModels/Layouts/BaseLayoutViewModel.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Files.App/ViewModels/Layouts/BaseLayoutViewModel.cs b/src/Files.App/ViewModels/Layouts/BaseLayoutViewModel.cs index 76c77d3eca21..e6236ed434c9 100644 --- a/src/Files.App/ViewModels/Layouts/BaseLayoutViewModel.cs +++ b/src/Files.App/ViewModels/Layouts/BaseLayoutViewModel.cs @@ -211,8 +211,10 @@ public async Task DropAsync(DragEventArgs e) { Items = { - new MenuFlyoutItem() { Text = string.Format("CopyToFolderCaptionText".GetLocalizedResource(), folderName) }, - new MenuFlyoutItem() { Text = string.Format("MoveToFolderCaptionText".GetLocalizedResource(), folderName) }, + new MenuFlyoutItem() { Text = string.Format("CopyToFolderCaptionText".GetLocalizedResource(), folderName), Command = new AsyncRelayCommand(async ct => + await _associatedInstance.FilesystemHelpers.PerformOperationTypeAsync(DataPackageOperation.Copy, e.DataView, _associatedInstance.ShellViewModel.WorkingDirectory, false, true))}, + new MenuFlyoutItem() { Text = string.Format("MoveToFolderCaptionText".GetLocalizedResource(), folderName), Command = new AsyncRelayCommand(async ct => + await _associatedInstance.FilesystemHelpers.PerformOperationTypeAsync(DataPackageOperation.Move, e.DataView, _associatedInstance.ShellViewModel.WorkingDirectory, false, true))} } }; From 17d1b5c9490f31cf1e090e34eeb089cc5153ce45 Mon Sep 17 00:00:00 2001 From: d2dyno006 <53011783+d2dyno006@users.noreply.github.com> Date: Sat, 15 Feb 2025 11:46:35 +0100 Subject: [PATCH 6/8] Change to INestedStorable --- src/Files.App/Data/Items/ShelfItem.cs | 8 ++++---- src/Files.App/UserControls/Pane/ShelfPane.xaml.cs | 8 +++----- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/Files.App/Data/Items/ShelfItem.cs b/src/Files.App/Data/Items/ShelfItem.cs index fc8b554c333d..461902fd2337 100644 --- a/src/Files.App/Data/Items/ShelfItem.cs +++ b/src/Files.App/Data/Items/ShelfItem.cs @@ -6,7 +6,7 @@ namespace Files.App.Data.Items { [Bindable(true)] - public sealed partial class ShelfItem : ObservableObject, IWrapper, IAsyncInitialize + public sealed partial class ShelfItem : ObservableObject, IWrapper, IAsyncInitialize { private readonly IImageService _imageService; private readonly ICollection _sourceCollection; @@ -16,9 +16,9 @@ public sealed partial class ShelfItem : ObservableObject, IWrapper, I [ObservableProperty] private string? _Path; /// - public IStorable Inner { get; } + public INestedStorable Inner { get; } - public ShelfItem(IStorable storable, ICollection sourceCollection, IImage? icon = null) + public ShelfItem(INestedStorable storable, ICollection sourceCollection, IImage? icon = null) { _imageService = Ioc.Default.GetRequiredService(); _sourceCollection = sourceCollection; @@ -35,7 +35,7 @@ public async Task InitAsync(CancellationToken cancellationToken = default) } [RelayCommand] - private void Remove() + public void Remove() { _sourceCollection.Remove(this); } diff --git a/src/Files.App/UserControls/Pane/ShelfPane.xaml.cs b/src/Files.App/UserControls/Pane/ShelfPane.xaml.cs index 6ce2d3cb6b66..838ac36dd63a 100644 --- a/src/Files.App/UserControls/Pane/ShelfPane.xaml.cs +++ b/src/Files.App/UserControls/Pane/ShelfPane.xaml.cs @@ -1,14 +1,12 @@ // Copyright (c) Files Community // Licensed under the MIT License. -using System.Runtime.InteropServices; 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 Vanara.Windows.Shell; +using Windows.ApplicationModel.DataTransfer; using WinRT; using DragEventArgs = Microsoft.UI.Xaml.DragEventArgs; @@ -52,8 +50,8 @@ private async void Shelf_Drop(object sender, DragEventArgs e) var storable = item switch { - StorageFileWithPath => (IStorable?)await storageService.TryGetFileAsync(item.Path), - StorageFolderWithPath => (IStorable?)await storageService.TryGetFolderAsync(item.Path), + StorageFileWithPath => (INestedStorable?)await storageService.TryGetFileAsync(item.Path), + StorageFolderWithPath => (INestedStorable?)await storageService.TryGetFolderAsync(item.Path), _ => null }; From 6875bdb6957217207bc4e50561429198d06fdd27 Mon Sep 17 00:00:00 2001 From: d2dyno006 <53011783+d2dyno006@users.noreply.github.com> Date: Sat, 15 Feb 2025 11:49:48 +0100 Subject: [PATCH 7/8] Formatting --- .../UserControls/Pane/ShelfPane.xaml.cs | 22 +++++++++---------- .../ViewModels/Layouts/BaseLayoutViewModel.cs | 8 +++---- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/Files.App/UserControls/Pane/ShelfPane.xaml.cs b/src/Files.App/UserControls/Pane/ShelfPane.xaml.cs index 838ac36dd63a..21a1aff1ff47 100644 --- a/src/Files.App/UserControls/Pane/ShelfPane.xaml.cs +++ b/src/Files.App/UserControls/Pane/ShelfPane.xaml.cs @@ -65,23 +65,23 @@ private async void Shelf_Drop(object sender, DragEventArgs e) } } - private void ListView_DragItemsStarting(object sender, DragItemsStartingEventArgs e) - { - var apidl = SafetyExtensions.IgnoreExceptions(() => e.Items - .Cast() - .Select(x => new ShellItem(x.Inner.Id).PIDL) - .ToArray()); + private void ListView_DragItemsStarting(object sender, DragItemsStartingEventArgs e) + { + var apidl = SafetyExtensions.IgnoreExceptions(() => e.Items + .Cast() + .Select(x => new ShellItem(x.Inner.Id).PIDL) + .ToArray()); - if (apidl is null) - return; + if (apidl is null) + return; - if (!Shell32.SHGetDesktopFolder(out var pDesktop).Succeeded) + if (!Shell32.SHGetDesktopFolder(out var pDesktop).Succeeded) return; - if (!Shell32.SHGetIDListFromObject(pDesktop, out var pDesktopPidl).Succeeded) + if (!Shell32.SHGetIDListFromObject(pDesktop, out var pDesktopPidl).Succeeded) return; - e.Data.Properties["Files_ActionBinder"] = "Files_ShelfBinder"; + e.Data.Properties["Files_ActionBinder"] = "Files_ShelfBinder"; if (!Shell32.SHCreateDataObject(pDesktopPidl, apidl, null, out var ppDataObject).Succeeded) return; diff --git a/src/Files.App/ViewModels/Layouts/BaseLayoutViewModel.cs b/src/Files.App/ViewModels/Layouts/BaseLayoutViewModel.cs index e6236ed434c9..5896e423930e 100644 --- a/src/Files.App/ViewModels/Layouts/BaseLayoutViewModel.cs +++ b/src/Files.App/ViewModels/Layouts/BaseLayoutViewModel.cs @@ -116,7 +116,7 @@ public async Task DragOverAsync(DragEventArgs e) { // As long as one file doesn't already belong to this folder if (_associatedInstance.InstanceViewModel.IsPageTypeSearchResults || draggedItems.Any() && - draggedItems.AreItemsAlreadyInFolder(_associatedInstance.ShellViewModel.WorkingDirectory)) + draggedItems.AreItemsAlreadyInFolder(_associatedInstance.ShellViewModel.WorkingDirectory)) { e.AcceptedOperation = DataPackageOperation.None; } @@ -157,9 +157,9 @@ public async Task DragOverAsync(DragEventArgs e) e.AcceptedOperation = DataPackageOperation.Move | DataPackageOperation.Copy; } else if (draggedItems.Any(x => - x.Item is ZipStorageFile || - x.Item is ZipStorageFolder) || - ZipStorageFolder.IsZipPath(pwd)) + x.Item is ZipStorageFile || + x.Item is ZipStorageFolder) || + ZipStorageFolder.IsZipPath(pwd)) { e.DragUIOverride.Caption = string.Format("CopyToFolderCaptionText".GetLocalizedResource(), folderName); e.AcceptedOperation = DataPackageOperation.Copy; From 33dc46e0a6ac0142bb29948f0f3d32dc7fa6b9f7 Mon Sep 17 00:00:00 2001 From: d2dyno006 <53011783+d2dyno006@users.noreply.github.com> Date: Sat, 15 Feb 2025 11:59:05 +0100 Subject: [PATCH 8/8] Create ShelfViewModel.cs --- .../ViewModels/UserControls/ShelfViewModel.cs | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 src/Files.App/ViewModels/UserControls/ShelfViewModel.cs diff --git a/src/Files.App/ViewModels/UserControls/ShelfViewModel.cs b/src/Files.App/ViewModels/UserControls/ShelfViewModel.cs new file mode 100644 index 000000000000..c753bcb6be54 --- /dev/null +++ b/src/Files.App/ViewModels/UserControls/ShelfViewModel.cs @@ -0,0 +1,57 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using System.Collections.Specialized; +using Files.Shared.Utils; + +namespace Files.App.ViewModels.UserControls +{ + [Bindable(true)] + public sealed partial class ShelfViewModel : ObservableObject, IAsyncInitialize + { + private readonly Dictionary _watchers; + + public ObservableCollection Items { get; } + + public ShelfViewModel() + { + _watchers = new(); + Items = new(); + Items.CollectionChanged += Items_CollectionChanged; + } + + /// + public Task InitAsync(CancellationToken cancellationToken = default) + { + // TODO: Load persisted shelf items + return Task.CompletedTask; + } + + private async void Items_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) + { + switch (e.Action) + { + case NotifyCollectionChangedAction.Add when e.NewItems is not null: + { + if (e.NewItems[0] is not INestedStorable nestedStorable) + return; + + var parentPath = SystemIO.Path.GetDirectoryName(nestedStorable.Id) ?? string.Empty; + if (_watchers.ContainsKey(parentPath)) + return; + + if (await nestedStorable.GetParentAsync() is not IMutableFolder mutableFolder) + return; + + // TODO: Register IFolderWatcher + + break; + } + + case NotifyCollectionChangedAction.Remove: + + break; + } + } + } +}