From efbe16485bbcfa9064ce15de870e4cfa66126c86 Mon Sep 17 00:00:00 2001 From: Marco Gavelli Date: Fri, 27 Dec 2024 20:03:57 +0100 Subject: [PATCH 1/8] Init impl --- src/Files.App/Helpers/AutomaticDragHelper.cs | 344 ++++++++++++++++++ .../ViewModels/Layouts/BaseLayoutViewModel.cs | 17 +- src/Files.App/Views/Layouts/BaseLayoutPage.cs | 57 ++- 3 files changed, 408 insertions(+), 10 deletions(-) create mode 100644 src/Files.App/Helpers/AutomaticDragHelper.cs diff --git a/src/Files.App/Helpers/AutomaticDragHelper.cs b/src/Files.App/Helpers/AutomaticDragHelper.cs new file mode 100644 index 000000000000..26ee1bcfa71c --- /dev/null +++ b/src/Files.App/Helpers/AutomaticDragHelper.cs @@ -0,0 +1,344 @@ +using Microsoft.UI.Input; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Input; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using Windows.ApplicationModel.DataTransfer; +using Windows.Foundation; +using Windows.Win32; + +namespace Files.App.Helpers +{ + public class AutomaticDragHelper : DependencyObject + { + public static AutomaticDragHelper GetDragHelper(DependencyObject obj) + { + return (AutomaticDragHelper)obj.GetValue(DragHelperProperty); + } + + public static void SetDragHelper(DependencyObject obj, AutomaticDragHelper value) + { + obj.SetValue(DragHelperProperty, value); + } + + public static readonly DependencyProperty DragHelperProperty = + DependencyProperty.RegisterAttached("DragHelper", typeof(AutomaticDragHelper), typeof(AutomaticDragHelper), new PropertyMetadata(null, OnDragHelperChanged)); + + private static void OnDragHelperChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (e.OldValue is AutomaticDragHelper old) + old.StopDetectingDrag(); + } + + // The standard Windows mouse drag box size is defined by SM_CXDRAG and SM_CYDRAG. + // UIElement uses the standard box size with dimensions multiplied by this constant. + // This arrangement is in place as accidentally triggering a drag was deemed too easy while + // selecting several items with the mouse in quick succession. + private const double UIELEMENT_MOUSE_DRAG_THRESHOLD_MULTIPLIER = 2.0; + private readonly UIElement m_pOwnerNoRef; + private readonly bool m_shouldAddInputHandlers; + private bool m_isCheckingForMouseDrag; + private Point m_lastMouseRightButtonDownPosition; + private bool m_dragDropPointerPressedToken, m_dragDropPointerMovedToken, m_dragDropPointerReleasedToken, m_dragDropPointerCaptureLostToken, m_dragDropHoldingToken; + private PointerPoint m_spPointerPoint; + private Pointer m_spPointer; + private bool m_isHoldingCompleted, m_isRightButtonPressed; + + public AutomaticDragHelper(UIElement pUIElement, bool shouldAddInputHandlers) + { + m_pOwnerNoRef = pUIElement; + m_shouldAddInputHandlers = shouldAddInputHandlers; + } + + // Begin tracking the mouse cursor in order to fire a drag start if the pointer + // moves a certain distance away from m_lastMouseRightButtonDownPosition. + public void BeginCheckingForMouseDrag(Pointer pPointer) + { + + bool captured = m_pOwnerNoRef.CapturePointer(pPointer); + + m_isCheckingForMouseDrag = !!captured; + } + + // Stop tracking the mouse cursor. + public void StopCheckingForMouseDrag(Pointer pPointer) + { + // Do not call ReleasePointerCapture() more times than we called CapturePointer() + if (m_isCheckingForMouseDrag) + { + m_isCheckingForMouseDrag = false; + + m_pOwnerNoRef.ReleasePointerCapture(pPointer); + } + } + + // Return true if we're tracking the mouse and newMousePosition is outside the drag + // rectangle centered at m_lastMouseRightButtonDownPosition (see IsOutsideDragRectangle). + bool ShouldStartMouseDrag(Point newMousePosition) + { + return m_isCheckingForMouseDrag && IsOutsideDragRectangle(newMousePosition, m_lastMouseRightButtonDownPosition); + } + + + // Returns true if testPoint is outside of the rectangle + // defined by the SM_CXDRAG and SM_CYDRAG system metrics and + // dragRectangleCenter. + bool IsOutsideDragRectangle(Point testPoint, Point dragRectangleCenter) + { + + double dx = Math.Abs(testPoint.X - dragRectangleCenter.X); + double dy = Math.Abs(testPoint.Y - dragRectangleCenter.Y); + + double maxDx = PInvoke.GetSystemMetrics(Windows.Win32.UI.WindowsAndMessaging.SYSTEM_METRICS_INDEX.SM_CXDRAG); + double maxDy = PInvoke.GetSystemMetrics(Windows.Win32.UI.WindowsAndMessaging.SYSTEM_METRICS_INDEX.SM_CYDRAG); + + maxDx *= UIELEMENT_MOUSE_DRAG_THRESHOLD_MULTIPLIER; + maxDy *= UIELEMENT_MOUSE_DRAG_THRESHOLD_MULTIPLIER; + + return (dx > maxDx || dy > maxDy); + } + + public void StartDetectingDrag() + { + if (m_shouldAddInputHandlers && !m_dragDropPointerPressedToken) + { + m_pOwnerNoRef.PointerPressed += HandlePointerPressedEventArgs; + m_dragDropPointerPressedToken = true; + } + } + + public void StopDetectingDrag() + { + if (m_dragDropPointerPressedToken) + { + m_pOwnerNoRef.PointerPressed -= HandlePointerPressedEventArgs; + // zero out the token; + m_dragDropPointerPressedToken = false; + } + } + + public void RegisterDragPointerEvents() + { + if (m_shouldAddInputHandlers) + { + PointerEventHandler spDragDropPointerMovedHandler; + PointerEventHandler spDragDropPointerReleasedHandler; + PointerEventHandler spDragDropPointerCaptureLostHandler; + + // Hookup pointer events so we can catch and handle it for drag and drop. + if (!m_dragDropPointerMovedToken) + { + m_pOwnerNoRef.PointerMoved += HandlePointerMovedEventArgs; + m_dragDropPointerMovedToken = true; + } + + if (!m_dragDropPointerReleasedToken) + { + m_pOwnerNoRef.PointerReleased += HandlePointerReleasedEventArgs; + m_dragDropPointerReleasedToken = true; + } + + if (!m_dragDropPointerCaptureLostToken) + { + m_pOwnerNoRef.PointerCaptureLost += HandlePointerCaptureLostEventArgs; + m_dragDropPointerCaptureLostToken = true; + } + } + } + + public void HandlePointerPressedEventArgs(object sender, PointerRoutedEventArgs pArgs) + { + Pointer spPointer; + PointerDeviceType pointerDeviceType = PointerDeviceType.Touch; + PointerPoint spPointerPoint; + + m_spPointerPoint = null; + m_spPointer = null; + //m_isHoldingCompleted = false; + + spPointer = pArgs.Pointer; + pointerDeviceType = spPointer.PointerDeviceType; + + spPointerPoint = pArgs.GetCurrentPoint(m_pOwnerNoRef); + + // Check if this is a mouse button down. + if (pointerDeviceType == PointerDeviceType.Mouse || pointerDeviceType == PointerDeviceType.Pen) + { + // Mouse button down. + PointerPointProperties spPointerProperties; + bool isRightButtonPressed = false; + + spPointerProperties = spPointerPoint.Properties; + isRightButtonPressed = spPointerProperties.IsRightButtonPressed; + + // If the left mouse button was the one pressed... + if (!m_isRightButtonPressed && isRightButtonPressed) + { + m_isRightButtonPressed = true; + // Start listening for a mouse drag gesture + m_lastMouseRightButtonDownPosition = spPointerPoint.Position; + BeginCheckingForMouseDrag(spPointer); + + RegisterDragPointerEvents(); + } + } + /*else + { + m_spPointerPoint = spPointerPoint; + m_spPointer = spPointer; + + if (m_shouldAddInputHandlers && !m_dragDropHoldingToken) + { + // Touch input occurs, subscribe to holding + m_pOwnerNoRef.Holding += HandleHoldingEventArgs; + } + + RegisterDragPointerEvents(); + }*/ + } + + public void HandlePointerMovedEventArgs(object sender, PointerRoutedEventArgs pArgs) + { + Pointer spPointer; + PointerDeviceType pointerDeviceType = PointerDeviceType.Touch; + + spPointer = pArgs.Pointer; + pointerDeviceType = spPointer.PointerDeviceType; + + // Our behavior is different between mouse and touch. + // It's up to us to detect mouse drag gestures - if we + // detect one here, start a drag drop. + if (pointerDeviceType == PointerDeviceType.Mouse || pointerDeviceType == PointerDeviceType.Pen) + { + PointerPoint spPointerPoint; + Point newMousePosition; + + spPointerPoint = pArgs.GetCurrentPoint(m_pOwnerNoRef); + + newMousePosition = spPointerPoint.Position; + if (ShouldStartMouseDrag(newMousePosition)) + { + IAsyncOperation spAsyncOperation; + StopCheckingForMouseDrag(spPointer); + + spAsyncOperation = m_pOwnerNoRef.StartDragAsync(spPointerPoint); + } + } + } + + + public void HandlePointerReleasedEventArgs(object sender, PointerRoutedEventArgs pArgs) + { + Pointer spPointer; + PointerDeviceType pointerDeviceType = PointerDeviceType.Touch; + + spPointer = pArgs.Pointer; + pointerDeviceType = spPointer.PointerDeviceType; + + // Check if this is a mouse button up + if (pointerDeviceType == PointerDeviceType.Mouse || pointerDeviceType == PointerDeviceType.Pen) + { + bool isRightButtonPressed = false; + PointerPoint spPointerPoint; + PointerPointProperties spPointerProperties; + + spPointerPoint = pArgs.GetCurrentPoint(m_pOwnerNoRef); + spPointerProperties = spPointerPoint.Properties; + isRightButtonPressed = spPointerProperties.IsRightButtonPressed; + + // if the mouse left button was the one released... + if (m_isRightButtonPressed && !isRightButtonPressed) + { + m_isRightButtonPressed = false; + UnregisterEvents(); + // Terminate any mouse drag gesture tracking. + StopCheckingForMouseDrag(spPointer); + } + } + else + { + UnregisterEvents(); + } + } + + public void HandlePointerCaptureLostEventArgs(object sender, PointerRoutedEventArgs pArgs) + { + Pointer spPointer; + PointerDeviceType pointerDeviceType = PointerDeviceType.Touch; + + spPointer = pArgs.Pointer; + + pointerDeviceType = spPointer.PointerDeviceType; + if (pointerDeviceType == PointerDeviceType.Mouse || pointerDeviceType == PointerDeviceType.Pen) + { + // We're not necessarily going to get a PointerReleased on capture lost, so reset this flag here. + m_isRightButtonPressed = false; + } + + UnregisterEvents(); + } + + public void UnregisterEvents() + { + // Unregister events handlers + if (m_dragDropPointerMovedToken) + { + m_pOwnerNoRef.PointerMoved -= HandlePointerMovedEventArgs; + m_dragDropPointerMovedToken = false; + } + + if (m_dragDropPointerReleasedToken) + { + m_pOwnerNoRef.PointerReleased -= HandlePointerReleasedEventArgs; + m_dragDropPointerReleasedToken = false; + } + + if (m_dragDropPointerCaptureLostToken) + { + m_pOwnerNoRef.PointerCaptureLost -= HandlePointerCaptureLostEventArgs; + m_dragDropPointerCaptureLostToken = false; + } + + /*if (m_dragDropHoldingToken) + { + m_pOwnerNoRef.Holding -= HandleHoldingEventArgs; + m_dragDropHoldingToken = false; + }*/ + } + + /*public void HandleHoldingEventArgs(object sender, HoldingRoutedEventArgs pArgs) + { + PointerDeviceType pointerDeviceType = PointerDeviceType.Touch; + + pointerDeviceType = pArgs.PointerDeviceType; + + if (pointerDeviceType == PointerDeviceType.Touch) + { + HoldingState holdingState = HoldingState.Started; + holdingState = pArgs.HoldingState; + + if (holdingState == HoldingState.Started) + { + m_isHoldingCompleted = true; + } + } + }*/ + + /*public void HandleDirectManipulationDraggingStarted() + { + // Release cross-slide viewport now + m_pOwnerNoRef.DirectManipulationCrossSlideContainerCompleted(); + if (m_isHoldingCompleted) + { + m_pOwnerNoRef.OnTouchDragStarted(m_spPointerPoint, m_spPointer); + } + + m_spPointerPoint = null; + m_spPointer = null; + }*/ + } +} diff --git a/src/Files.App/ViewModels/Layouts/BaseLayoutViewModel.cs b/src/Files.App/ViewModels/Layouts/BaseLayoutViewModel.cs index ec787ddd73f6..0a31aba6b33a 100644 --- a/src/Files.App/ViewModels/Layouts/BaseLayoutViewModel.cs +++ b/src/Files.App/ViewModels/Layouts/BaseLayoutViewModel.cs @@ -7,10 +7,12 @@ using System.IO; using System.Runtime.InteropServices; using System.Windows.Input; +using Vanara.PInvoke; using Windows.ApplicationModel.DataTransfer; using Windows.ApplicationModel.DataTransfer.DragDrop; using Windows.Storage; using Windows.System; +using WinRT; namespace Files.App.ViewModels.Layouts { @@ -187,7 +189,20 @@ public async Task DropAsync(DragEventArgs e) try { - await _associatedInstance.FilesystemHelpers.PerformOperationTypeAsync(e.AcceptedOperation, e.DataView, _associatedInstance.ShellViewModel.WorkingDirectory, false, true); + if ((bool)e.DataView.Properties.GetValueOrDefault("dragRightButton", false)) + { + Windows.Win32.PInvoke.GetCursorPos(out var dropPoint); + using var sf = new Vanara.Windows.Shell.ShellFolder(_associatedInstance.ShellViewModel.WorkingDirectory); + var dataObjectProvider = e.DataView.As(); + var iddo = dataObjectProvider.GetDataObject(); + var dropTarget = sf.GetViewObject(HWND.NULL); + dropTarget.DragEnter(iddo, Vanara.PInvoke.MouseButtonState.MK_RBUTTON, new() { X=dropPoint.X, Y=dropPoint.Y }, (Ole32.DROPEFFECT)e.AcceptedOperation); + dropTarget.Drop(iddo, Vanara.PInvoke.MouseButtonState.MK_RBUTTON, new() { X=dropPoint.X, Y=dropPoint.Y }, (Ole32.DROPEFFECT)e.AcceptedOperation); + } + else + { + await _associatedInstance.FilesystemHelpers.PerformOperationTypeAsync(e.AcceptedOperation, e.DataView, _associatedInstance.ShellViewModel.WorkingDirectory, false, true); + } await _associatedInstance.RefreshIfNoWatcherExistsAsync(); } finally diff --git a/src/Files.App/Views/Layouts/BaseLayoutPage.cs b/src/Files.App/Views/Layouts/BaseLayoutPage.cs index 7dd162f74ba6..7afef444d5d6 100644 --- a/src/Files.App/Views/Layouts/BaseLayoutPage.cs +++ b/src/Files.App/Views/Layouts/BaseLayoutPage.cs @@ -6,6 +6,7 @@ using Files.App.Helpers.ContextFlyouts; using Files.App.UserControls.Menus; using Files.App.ViewModels.Layouts; +using Microsoft.UI.Input; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Controls.Primitives; @@ -22,6 +23,7 @@ using Windows.Foundation.Collections; using Windows.Storage; using Windows.System; +using Windows.UI.Core; using WinRT; using static Files.App.Helpers.PathNormalization; using DispatcherQueueTimer = Microsoft.UI.Dispatching.DispatcherQueueTimer; @@ -921,7 +923,7 @@ private async Task AddShellMenuItemsAsync(List s // Filter mainShellMenuItems that have a non-null LoadSubMenuAction var mainItemsWithSubMenu = mainShellMenuItems.Where(x => x.LoadSubMenuAction is not null); - + var mainSubMenuTasks = mainItemsWithSubMenu.Select(async item => { await item.LoadSubMenuAction(); @@ -936,7 +938,7 @@ private async Task AddShellMenuItemsAsync(List s await item.LoadSubMenuAction(); ShellContextFlyoutFactory.AddItemsToOverflowMenu(overflowItem, item); }); - + itemsControl?.Items.OfType().ForEach(item => { // Enable CharacterEllipsis text trimming for menu items @@ -962,7 +964,7 @@ private async Task AddShellMenuItemsAsync(List s clickAction(flyout.Items); } }); - + await Task.WhenAll(mainSubMenuTasks.Concat(overflowSubMenuTasks)); } @@ -987,10 +989,24 @@ protected virtual void Page_CharacterReceived(UIElement sender, CharacterReceive } protected virtual void FileList_DragItemsStarting(object sender, DragItemsStartingEventArgs e) + => e.Cancel = DragItemsStarting(sender, e.Data, e.Items); + + private void Item_DragStarting(UIElement sender, DragStartingEventArgs args) + { + if (GetItemFromElement(sender) is not ListedItem item) + return; + + var selectedItems = SelectedItems?.ToList() ?? new(); + selectedItems.AddIfNotPresent(item); + args.Data.Properties["dragRightButton"] = InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.RightButton).HasFlag(CoreVirtualKeyStates.Down); + args.Cancel = DragItemsStarting(ItemsControl, args.Data, selectedItems); + } + + protected bool DragItemsStarting(object sender, DataPackage data, IList items) { try { - var itemList = e.Items.OfType().ToList(); + var itemList = items.OfType().ToList(); var firstItem = itemList.FirstOrDefault(); var sortedItems = SortingHelper.OrderFileList(itemList, FolderSettings.DirectorySortOption, FolderSettings.DirectorySortDirection, FolderSettings.SortDirectoriesAlongsideFiles, FolderSettings.SortFilesFirst).ToList(); var orderedItems = sortedItems.SkipWhile(x => x != firstItem).Concat(sortedItems.TakeWhile(x => x != firstItem)).ToList(); @@ -1000,14 +1016,14 @@ protected virtual void FileList_DragItemsStarting(object sender, DragItemsStarti { var iddo = shellItemList[0].Parent.GetChildrenUIObjects(HWND.NULL, shellItemList); shellItemList.ForEach(x => x.Dispose()); - var dataObjectProvider = e.Data.As(); + var dataObjectProvider = data.As(); dataObjectProvider.SetDataObject(iddo); } else { // Only support IStorageItem capable paths var storageItemList = orderedItems.Where(x => !(x.IsHiddenItem && x.IsLinkItem && x.IsRecycleBinItem && x.IsShortcut)).Select(x => VirtualStorageItem.FromListedItem(x)); - e.Data.SetStorageItems(storageItemList, false); + data.SetStorageItems(storageItemList, false); } // Set can window to front (#13255) @@ -1016,8 +1032,9 @@ protected virtual void FileList_DragItemsStarting(object sender, DragItemsStarti } catch (Exception) { - e.Cancel = true; + return true; } + return false; } protected virtual void FileList_DragItemsCompleted(ListViewBase sender, DragItemsCompletedEventArgs args) @@ -1146,8 +1163,22 @@ protected virtual async void Item_Drop(object sender, DragEventArgs e) 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); - + { + if ((bool)e.DataView.Properties.GetValueOrDefault("dragRightButton", false) && item.IsFolder) + { + Windows.Win32.PInvoke.GetCursorPos(out var dropPoint); + using var sf = new Vanara.Windows.Shell.ShellFolder(item.ItemPath); + var dataObjectProvider = e.DataView.As(); + var iddo = dataObjectProvider.GetDataObject(); + var dropTarget = sf.GetViewObject(HWND.NULL); + dropTarget.DragEnter(iddo, MouseButtonState.MK_RBUTTON, new() { X=dropPoint.X, Y=dropPoint.Y }, (Ole32.DROPEFFECT)e.AcceptedOperation); + dropTarget.Drop(iddo, MouseButtonState.MK_RBUTTON, new() { X=dropPoint.X, Y=dropPoint.Y }, (Ole32.DROPEFFECT)e.AcceptedOperation); + } + else + { + await ParentShellPageInstance!.FilesystemHelpers.PerformOperationTypeAsync(e.AcceptedOperation, e.DataView, (item as ShortcutItem)?.TargetPath ?? item.ItemPath, false, true, item.IsExecutable, item.IsScriptFile); + } + } deferral.Complete(); } @@ -1344,6 +1375,11 @@ protected void InitializeDrag(UIElement container, ListedItem item) container.DragLeave += Item_DragLeave; container.Drop += Item_Drop; } + + var dragHelper = new AutomaticDragHelper(container, true); + dragHelper.StartDetectingDrag(); + AutomaticDragHelper.SetDragHelper(container, dragHelper); + container.DragStarting += Item_DragStarting; } protected void UninitializeDrag(UIElement element) @@ -1352,6 +1388,9 @@ protected void UninitializeDrag(UIElement element) element.RemoveHandler(UIElement.DragOverEvent, Item_DragOverEventHandler); element.DragLeave -= Item_DragLeave; element.Drop -= Item_Drop; + + AutomaticDragHelper.SetDragHelper(element, null!); + element.DragStarting -= Item_DragStarting; } public virtual void Dispose() From b29f042409cf02e718cba84e1520ee7426453a5f Mon Sep 17 00:00:00 2001 From: Marco Gavelli Date: Fri, 27 Dec 2024 20:19:11 +0100 Subject: [PATCH 2/8] Handle drop to sidebar and pathbar --- src/Files.App/Helpers/AutomaticDragHelper.cs | 5 +--- .../UserControls/SidebarViewModel.cs | 28 ++++++++++++++++++- src/Files.App/Views/Shells/BaseShellPage.cs | 17 ++++++++++- 3 files changed, 44 insertions(+), 6 deletions(-) diff --git a/src/Files.App/Helpers/AutomaticDragHelper.cs b/src/Files.App/Helpers/AutomaticDragHelper.cs index 26ee1bcfa71c..ac25ec6b190d 100644 --- a/src/Files.App/Helpers/AutomaticDragHelper.cs +++ b/src/Files.App/Helpers/AutomaticDragHelper.cs @@ -13,6 +13,7 @@ namespace Files.App.Helpers { + // https://github.com/microsoft/microsoft-ui-xaml/blob/winui3/release/1.6.3/src/dxaml/xcp/dxaml/lib/AutomaticDragHelper.cpp public class AutomaticDragHelper : DependencyObject { public static AutomaticDragHelper GetDragHelper(DependencyObject obj) @@ -125,10 +126,6 @@ public void RegisterDragPointerEvents() { if (m_shouldAddInputHandlers) { - PointerEventHandler spDragDropPointerMovedHandler; - PointerEventHandler spDragDropPointerReleasedHandler; - PointerEventHandler spDragDropPointerCaptureLostHandler; - // Hookup pointer events so we can catch and handle it for drag and drop. if (!m_dragDropPointerMovedToken) { diff --git a/src/Files.App/ViewModels/UserControls/SidebarViewModel.cs b/src/Files.App/ViewModels/UserControls/SidebarViewModel.cs index b59fb8ac3172..51bb908e724f 100644 --- a/src/Files.App/ViewModels/UserControls/SidebarViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/SidebarViewModel.cs @@ -11,11 +11,13 @@ using System.Collections.Specialized; using System.IO; using System.Windows.Input; +using Vanara.PInvoke; using Windows.ApplicationModel.DataTransfer; using Windows.ApplicationModel.DataTransfer.DragDrop; using Windows.Storage; using Windows.System; using Windows.UI.Core; +using WinRT; namespace Files.App.ViewModels.UserControls { @@ -1251,6 +1253,16 @@ private async Task HandleLocationItemDroppedAsync(LocationItem locationItem, Ite await QuickAccessService.PinToSidebarAsync(item.Path); } } + else if ((bool)args.DroppedItem.Properties.GetValueOrDefault("dragRightButton", false)) + { + Windows.Win32.PInvoke.GetCursorPos(out var dropPoint); + using var sf = new Vanara.Windows.Shell.ShellFolder(locationItem.Path); + var dataObjectProvider = args.DroppedItem.As(); + var iddo = dataObjectProvider.GetDataObject(); + var dropTarget = sf.GetViewObject(HWND.NULL); + dropTarget.DragEnter(iddo, Vanara.PInvoke.MouseButtonState.MK_RBUTTON, new() { X=dropPoint.X, Y=dropPoint.Y }, (Ole32.DROPEFFECT)args.RawEvent.AcceptedOperation); + dropTarget.Drop(iddo, Vanara.PInvoke.MouseButtonState.MK_RBUTTON, new() { X=dropPoint.X, Y=dropPoint.Y }, (Ole32.DROPEFFECT)args.RawEvent.AcceptedOperation); + } else { await FilesystemHelpers.PerformOperationTypeAsync(args.RawEvent.AcceptedOperation, args.DroppedItem, locationItem.Path, false, true); @@ -1260,7 +1272,21 @@ private async Task HandleLocationItemDroppedAsync(LocationItem locationItem, Ite private Task HandleDriveItemDroppedAsync(DriveItem driveItem, ItemDroppedEventArgs args) { - return FilesystemHelpers.PerformOperationTypeAsync(args.RawEvent.AcceptedOperation, args.RawEvent.DataView, driveItem.Path, false, true); + if ((bool)args.DroppedItem.Properties.GetValueOrDefault("dragRightButton", false)) + { + Windows.Win32.PInvoke.GetCursorPos(out var dropPoint); + using var sf = new Vanara.Windows.Shell.ShellFolder(driveItem.Path); + var dataObjectProvider = args.DroppedItem.As(); + var iddo = dataObjectProvider.GetDataObject(); + var dropTarget = sf.GetViewObject(HWND.NULL); + dropTarget.DragEnter(iddo, Vanara.PInvoke.MouseButtonState.MK_RBUTTON, new() { X=dropPoint.X, Y=dropPoint.Y }, (Ole32.DROPEFFECT)args.RawEvent.AcceptedOperation); + dropTarget.Drop(iddo, Vanara.PInvoke.MouseButtonState.MK_RBUTTON, new() { X=dropPoint.X, Y=dropPoint.Y }, (Ole32.DROPEFFECT)args.RawEvent.AcceptedOperation); + return Task.FromResult(ReturnResult.Success); + } + else + { + return FilesystemHelpers.PerformOperationTypeAsync(args.RawEvent.AcceptedOperation, args.RawEvent.DataView, driveItem.Path, false, true); + } } private async Task HandleTagItemDroppedAsync(FileTagItem fileTagItem, ItemDroppedEventArgs args) diff --git a/src/Files.App/Views/Shells/BaseShellPage.cs b/src/Files.App/Views/Shells/BaseShellPage.cs index 3c9e57ffd48d..953464db1ef4 100644 --- a/src/Files.App/Views/Shells/BaseShellPage.cs +++ b/src/Files.App/Views/Shells/BaseShellPage.cs @@ -9,9 +9,11 @@ using Microsoft.UI.Xaml.Navigation; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using Vanara.PInvoke; using Windows.Foundation.Metadata; using Windows.System; using Windows.UI.Core; +using WinRT; using DispatcherQueueTimer = Microsoft.UI.Dispatching.DispatcherQueueTimer; namespace Files.App.Views.Shells @@ -417,7 +419,20 @@ protected void CoreWindow_PointerPressed(object sender, PointerRoutedEventArgs a protected async void ShellPage_PathBoxItemDropped(object sender, PathBoxItemDroppedEventArgs e) { - await FilesystemHelpers.PerformOperationTypeAsync(e.AcceptedOperation, e.Package, e.Path, false, true); + if ((bool)e.Package.Properties.GetValueOrDefault("dragRightButton", false)) + { + Windows.Win32.PInvoke.GetCursorPos(out var dropPoint); + using var sf = new Vanara.Windows.Shell.ShellFolder(e.Path); + var dataObjectProvider = e.Package.As(); + var iddo = dataObjectProvider.GetDataObject(); + var dropTarget = sf.GetViewObject(HWND.NULL); + dropTarget.DragEnter(iddo, MouseButtonState.MK_RBUTTON, new() { X=dropPoint.X, Y=dropPoint.Y }, (Ole32.DROPEFFECT)e.AcceptedOperation); + dropTarget.Drop(iddo, MouseButtonState.MK_RBUTTON, new() { X=dropPoint.X, Y=dropPoint.Y }, (Ole32.DROPEFFECT)e.AcceptedOperation); + } + else + { + await FilesystemHelpers.PerformOperationTypeAsync(e.AcceptedOperation, e.Package, e.Path, false, true); + } e.SignalEvent?.Set(); } From 63490f5077a88374a317b30c2b51981581cf9e18 Mon Sep 17 00:00:00 2001 From: Marco Gavelli Date: Sat, 28 Dec 2024 21:31:10 +0100 Subject: [PATCH 3/8] Reduce code duplication --- .../Data/Factories/ShellContextFlyoutHelper.cs | 18 ++++++++++++++++++ .../ViewModels/Layouts/BaseLayoutViewModel.cs | 10 +--------- .../UserControls/SidebarViewModel.cs | 18 ++---------------- src/Files.App/Views/Layouts/BaseLayoutPage.cs | 10 ++-------- src/Files.App/Views/Shells/BaseShellPage.cs | 10 +--------- 5 files changed, 24 insertions(+), 42 deletions(-) diff --git a/src/Files.App/Data/Factories/ShellContextFlyoutHelper.cs b/src/Files.App/Data/Factories/ShellContextFlyoutHelper.cs index 7d545c5c76df..aa57afe8c16d 100644 --- a/src/Files.App/Data/Factories/ShellContextFlyoutHelper.cs +++ b/src/Files.App/Data/Factories/ShellContextFlyoutHelper.cs @@ -9,10 +9,13 @@ using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Media.Imaging; using System.IO; +using Vanara.PInvoke; +using Windows.ApplicationModel.DataTransfer; using Windows.System; using Windows.UI.Core; using Windows.Win32; using Windows.Win32.UI.WindowsAndMessaging; +using WinRT; namespace Files.App.Helpers { @@ -416,5 +419,20 @@ public static void AddItemsToOverflowMenu(AppBarButton? overflowItem, ContextMen } } } + + public static void InvokeRightButtonDropMenu(string folderPath, DataPackageView dataView, DataPackageOperation acceptedOperation) + { + using var sf = new Vanara.Windows.Shell.ShellItem(folderPath); + if (!sf.IsFolder) + return; + + PInvoke.GetCursorPos(out var dropPoint); + + var dataObjectProvider = dataView.As(); + var iddo = dataObjectProvider.GetDataObject(); + var dropTarget = sf.GetHandler(Shell32.BHID.BHID_SFViewObject); + dropTarget.DragEnter(iddo, MouseButtonState.MK_RBUTTON, new() { X = dropPoint.X, Y = dropPoint.Y }, (Ole32.DROPEFFECT)acceptedOperation); + dropTarget.Drop(iddo, MouseButtonState.MK_RBUTTON, new() { X = dropPoint.X, Y = dropPoint.Y }, (Ole32.DROPEFFECT)acceptedOperation); + } } } diff --git a/src/Files.App/ViewModels/Layouts/BaseLayoutViewModel.cs b/src/Files.App/ViewModels/Layouts/BaseLayoutViewModel.cs index 0a31aba6b33a..d25c920c7406 100644 --- a/src/Files.App/ViewModels/Layouts/BaseLayoutViewModel.cs +++ b/src/Files.App/ViewModels/Layouts/BaseLayoutViewModel.cs @@ -7,12 +7,10 @@ using System.IO; using System.Runtime.InteropServices; using System.Windows.Input; -using Vanara.PInvoke; using Windows.ApplicationModel.DataTransfer; using Windows.ApplicationModel.DataTransfer.DragDrop; using Windows.Storage; using Windows.System; -using WinRT; namespace Files.App.ViewModels.Layouts { @@ -191,13 +189,7 @@ public async Task DropAsync(DragEventArgs e) { if ((bool)e.DataView.Properties.GetValueOrDefault("dragRightButton", false)) { - Windows.Win32.PInvoke.GetCursorPos(out var dropPoint); - using var sf = new Vanara.Windows.Shell.ShellFolder(_associatedInstance.ShellViewModel.WorkingDirectory); - var dataObjectProvider = e.DataView.As(); - var iddo = dataObjectProvider.GetDataObject(); - var dropTarget = sf.GetViewObject(HWND.NULL); - dropTarget.DragEnter(iddo, Vanara.PInvoke.MouseButtonState.MK_RBUTTON, new() { X=dropPoint.X, Y=dropPoint.Y }, (Ole32.DROPEFFECT)e.AcceptedOperation); - dropTarget.Drop(iddo, Vanara.PInvoke.MouseButtonState.MK_RBUTTON, new() { X=dropPoint.X, Y=dropPoint.Y }, (Ole32.DROPEFFECT)e.AcceptedOperation); + SafetyExtensions.IgnoreExceptions(() => ShellContextFlyoutFactory.InvokeRightButtonDropMenu(_associatedInstance.ShellViewModel.WorkingDirectory, e.DataView, e.AcceptedOperation)); } else { diff --git a/src/Files.App/ViewModels/UserControls/SidebarViewModel.cs b/src/Files.App/ViewModels/UserControls/SidebarViewModel.cs index 51bb908e724f..3449ef8fa71a 100644 --- a/src/Files.App/ViewModels/UserControls/SidebarViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/SidebarViewModel.cs @@ -11,13 +11,11 @@ using System.Collections.Specialized; using System.IO; using System.Windows.Input; -using Vanara.PInvoke; using Windows.ApplicationModel.DataTransfer; using Windows.ApplicationModel.DataTransfer.DragDrop; using Windows.Storage; using Windows.System; using Windows.UI.Core; -using WinRT; namespace Files.App.ViewModels.UserControls { @@ -1255,13 +1253,7 @@ private async Task HandleLocationItemDroppedAsync(LocationItem locationItem, Ite } else if ((bool)args.DroppedItem.Properties.GetValueOrDefault("dragRightButton", false)) { - Windows.Win32.PInvoke.GetCursorPos(out var dropPoint); - using var sf = new Vanara.Windows.Shell.ShellFolder(locationItem.Path); - var dataObjectProvider = args.DroppedItem.As(); - var iddo = dataObjectProvider.GetDataObject(); - var dropTarget = sf.GetViewObject(HWND.NULL); - dropTarget.DragEnter(iddo, Vanara.PInvoke.MouseButtonState.MK_RBUTTON, new() { X=dropPoint.X, Y=dropPoint.Y }, (Ole32.DROPEFFECT)args.RawEvent.AcceptedOperation); - dropTarget.Drop(iddo, Vanara.PInvoke.MouseButtonState.MK_RBUTTON, new() { X=dropPoint.X, Y=dropPoint.Y }, (Ole32.DROPEFFECT)args.RawEvent.AcceptedOperation); + SafetyExtensions.IgnoreExceptions(() => ShellContextFlyoutFactory.InvokeRightButtonDropMenu(locationItem.Path, args.DroppedItem, args.RawEvent.AcceptedOperation)); } else { @@ -1274,13 +1266,7 @@ private Task HandleDriveItemDroppedAsync(DriveItem driveItem, Item { if ((bool)args.DroppedItem.Properties.GetValueOrDefault("dragRightButton", false)) { - Windows.Win32.PInvoke.GetCursorPos(out var dropPoint); - using var sf = new Vanara.Windows.Shell.ShellFolder(driveItem.Path); - var dataObjectProvider = args.DroppedItem.As(); - var iddo = dataObjectProvider.GetDataObject(); - var dropTarget = sf.GetViewObject(HWND.NULL); - dropTarget.DragEnter(iddo, Vanara.PInvoke.MouseButtonState.MK_RBUTTON, new() { X=dropPoint.X, Y=dropPoint.Y }, (Ole32.DROPEFFECT)args.RawEvent.AcceptedOperation); - dropTarget.Drop(iddo, Vanara.PInvoke.MouseButtonState.MK_RBUTTON, new() { X=dropPoint.X, Y=dropPoint.Y }, (Ole32.DROPEFFECT)args.RawEvent.AcceptedOperation); + SafetyExtensions.IgnoreExceptions(() => ShellContextFlyoutFactory.InvokeRightButtonDropMenu(driveItem.Path, args.DroppedItem, args.RawEvent.AcceptedOperation)); return Task.FromResult(ReturnResult.Success); } else diff --git a/src/Files.App/Views/Layouts/BaseLayoutPage.cs b/src/Files.App/Views/Layouts/BaseLayoutPage.cs index 7afef444d5d6..d1173bb0650b 100644 --- a/src/Files.App/Views/Layouts/BaseLayoutPage.cs +++ b/src/Files.App/Views/Layouts/BaseLayoutPage.cs @@ -1164,15 +1164,9 @@ protected virtual async void Item_Drop(object sender, DragEventArgs e) var item = GetItemFromElement(sender); if (item is not null) { - if ((bool)e.DataView.Properties.GetValueOrDefault("dragRightButton", false) && item.IsFolder) + if ((bool)e.DataView.Properties.GetValueOrDefault("dragRightButton", false)) { - Windows.Win32.PInvoke.GetCursorPos(out var dropPoint); - using var sf = new Vanara.Windows.Shell.ShellFolder(item.ItemPath); - var dataObjectProvider = e.DataView.As(); - var iddo = dataObjectProvider.GetDataObject(); - var dropTarget = sf.GetViewObject(HWND.NULL); - dropTarget.DragEnter(iddo, MouseButtonState.MK_RBUTTON, new() { X=dropPoint.X, Y=dropPoint.Y }, (Ole32.DROPEFFECT)e.AcceptedOperation); - dropTarget.Drop(iddo, MouseButtonState.MK_RBUTTON, new() { X=dropPoint.X, Y=dropPoint.Y }, (Ole32.DROPEFFECT)e.AcceptedOperation); + SafetyExtensions.IgnoreExceptions(() => ShellContextFlyoutFactory.InvokeRightButtonDropMenu(item.ItemPath, e.DataView, e.AcceptedOperation)); } else { diff --git a/src/Files.App/Views/Shells/BaseShellPage.cs b/src/Files.App/Views/Shells/BaseShellPage.cs index 953464db1ef4..3f3088a72544 100644 --- a/src/Files.App/Views/Shells/BaseShellPage.cs +++ b/src/Files.App/Views/Shells/BaseShellPage.cs @@ -9,11 +9,9 @@ using Microsoft.UI.Xaml.Navigation; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using Vanara.PInvoke; using Windows.Foundation.Metadata; using Windows.System; using Windows.UI.Core; -using WinRT; using DispatcherQueueTimer = Microsoft.UI.Dispatching.DispatcherQueueTimer; namespace Files.App.Views.Shells @@ -421,13 +419,7 @@ protected async void ShellPage_PathBoxItemDropped(object sender, PathBoxItemDrop { if ((bool)e.Package.Properties.GetValueOrDefault("dragRightButton", false)) { - Windows.Win32.PInvoke.GetCursorPos(out var dropPoint); - using var sf = new Vanara.Windows.Shell.ShellFolder(e.Path); - var dataObjectProvider = e.Package.As(); - var iddo = dataObjectProvider.GetDataObject(); - var dropTarget = sf.GetViewObject(HWND.NULL); - dropTarget.DragEnter(iddo, MouseButtonState.MK_RBUTTON, new() { X=dropPoint.X, Y=dropPoint.Y }, (Ole32.DROPEFFECT)e.AcceptedOperation); - dropTarget.Drop(iddo, MouseButtonState.MK_RBUTTON, new() { X=dropPoint.X, Y=dropPoint.Y }, (Ole32.DROPEFFECT)e.AcceptedOperation); + SafetyExtensions.IgnoreExceptions(() => ShellContextFlyoutFactory.InvokeRightButtonDropMenu(e.Path, e.Package, e.AcceptedOperation)); } else { From 2cdca8954dfe16873d4ab0d7c18a9fafebbf7fdf Mon Sep 17 00:00:00 2001 From: Marco Gavelli Date: Sat, 28 Dec 2024 23:24:25 +0100 Subject: [PATCH 4/8] Minor changes --- src/Files.App/Helpers/AutomaticDragHelper.cs | 28 +++++++------------- 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/src/Files.App/Helpers/AutomaticDragHelper.cs b/src/Files.App/Helpers/AutomaticDragHelper.cs index ac25ec6b190d..13c17d0b3824 100644 --- a/src/Files.App/Helpers/AutomaticDragHelper.cs +++ b/src/Files.App/Helpers/AutomaticDragHelper.cs @@ -1,12 +1,6 @@ using Microsoft.UI.Input; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Input; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading.Tasks; using Windows.ApplicationModel.DataTransfer; using Windows.Foundation; using Windows.Win32; @@ -16,6 +10,7 @@ namespace Files.App.Helpers // https://github.com/microsoft/microsoft-ui-xaml/blob/winui3/release/1.6.3/src/dxaml/xcp/dxaml/lib/AutomaticDragHelper.cpp public class AutomaticDragHelper : DependencyObject { + // Added attached dependency property to unregister event handlers public static AutomaticDragHelper GetDragHelper(DependencyObject obj) { return (AutomaticDragHelper)obj.GetValue(DragHelperProperty); @@ -44,10 +39,11 @@ private static void OnDragHelperChanged(DependencyObject d, DependencyPropertyCh private readonly bool m_shouldAddInputHandlers; private bool m_isCheckingForMouseDrag; private Point m_lastMouseRightButtonDownPosition; - private bool m_dragDropPointerPressedToken, m_dragDropPointerMovedToken, m_dragDropPointerReleasedToken, m_dragDropPointerCaptureLostToken, m_dragDropHoldingToken; - private PointerPoint m_spPointerPoint; - private Pointer m_spPointer; - private bool m_isHoldingCompleted, m_isRightButtonPressed; + private bool m_dragDropPointerPressedToken, m_dragDropPointerMovedToken, m_dragDropPointerReleasedToken, m_dragDropPointerCaptureLostToken/*, m_dragDropHoldingToken*/; + //private PointerPoint m_spPointerPoint; + //private Pointer m_spPointer; + //private bool m_isHoldingCompleted; + private bool m_isRightButtonPressed; public AutomaticDragHelper(UIElement pUIElement, bool shouldAddInputHandlers) { @@ -59,7 +55,6 @@ public AutomaticDragHelper(UIElement pUIElement, bool shouldAddInputHandlers) // moves a certain distance away from m_lastMouseRightButtonDownPosition. public void BeginCheckingForMouseDrag(Pointer pPointer) { - bool captured = m_pOwnerNoRef.CapturePointer(pPointer); m_isCheckingForMouseDrag = !!captured; @@ -84,13 +79,11 @@ bool ShouldStartMouseDrag(Point newMousePosition) return m_isCheckingForMouseDrag && IsOutsideDragRectangle(newMousePosition, m_lastMouseRightButtonDownPosition); } - // Returns true if testPoint is outside of the rectangle // defined by the SM_CXDRAG and SM_CYDRAG system metrics and // dragRectangleCenter. bool IsOutsideDragRectangle(Point testPoint, Point dragRectangleCenter) { - double dx = Math.Abs(testPoint.X - dragRectangleCenter.X); double dy = Math.Abs(testPoint.Y - dragRectangleCenter.Y); @@ -153,8 +146,8 @@ public void HandlePointerPressedEventArgs(object sender, PointerRoutedEventArgs PointerDeviceType pointerDeviceType = PointerDeviceType.Touch; PointerPoint spPointerPoint; - m_spPointerPoint = null; - m_spPointer = null; + //m_spPointerPoint = null; + //m_spPointer = null; //m_isHoldingCompleted = false; spPointer = pArgs.Pointer; @@ -172,7 +165,7 @@ public void HandlePointerPressedEventArgs(object sender, PointerRoutedEventArgs spPointerProperties = spPointerPoint.Properties; isRightButtonPressed = spPointerProperties.IsRightButtonPressed; - // If the left mouse button was the one pressed... + // If the right mouse button was the one pressed... if (!m_isRightButtonPressed && isRightButtonPressed) { m_isRightButtonPressed = true; @@ -227,7 +220,6 @@ public void HandlePointerMovedEventArgs(object sender, PointerRoutedEventArgs pA } } - public void HandlePointerReleasedEventArgs(object sender, PointerRoutedEventArgs pArgs) { Pointer spPointer; @@ -247,7 +239,7 @@ public void HandlePointerReleasedEventArgs(object sender, PointerRoutedEventArgs spPointerProperties = spPointerPoint.Properties; isRightButtonPressed = spPointerProperties.IsRightButtonPressed; - // if the mouse left button was the one released... + // if the mouse right button was the one released... if (m_isRightButtonPressed && !isRightButtonPressed) { m_isRightButtonPressed = false; From 699e466f2eab27ab1018fc150be0cb56c73666b1 Mon Sep 17 00:00:00 2001 From: Marco Gavelli Date: Sun, 29 Dec 2024 00:02:24 +0100 Subject: [PATCH 5/8] TO_TEST: Switch NativeClipboard.CurrentDataObject to IDataObjectProvider --- .../Utils/Storage/Operations/FilesystemHelpers.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Files.App/Utils/Storage/Operations/FilesystemHelpers.cs b/src/Files.App/Utils/Storage/Operations/FilesystemHelpers.cs index 9c2a5d888e1f..c5b1c5b9c183 100644 --- a/src/Files.App/Utils/Storage/Operations/FilesystemHelpers.cs +++ b/src/Files.App/Utils/Storage/Operations/FilesystemHelpers.cs @@ -9,11 +9,11 @@ using System.Runtime.InteropServices; using System.Runtime.InteropServices.ComTypes; using Vanara.PInvoke; -using Vanara.Windows.Shell; using Windows.ApplicationModel.DataTransfer; using Windows.Graphics.Imaging; using Windows.Storage; using Windows.Storage.Streams; +using WinRT; using FileAttributes = System.IO.FileAttributes; namespace Files.App.Utils.Storage @@ -758,12 +758,14 @@ public static async Task> GetDraggedStorageIte { if (hasVirtualItems && packageView.Contains("FileContents")) { - var descriptor = NativeClipboard.CurrentDataObject.GetData("FileGroupDescriptorW"); + var dataObjectProvider = packageView.As(); + var iddo = dataObjectProvider.GetDataObject(); + var descriptor = iddo.GetData("FileGroupDescriptorW"); for (var ii = 0; ii < descriptor.cItems; ii++) { if (descriptor.fgd[ii].dwFileAttributes.HasFlag(FileFlagsAndAttributes.FILE_ATTRIBUTE_DIRECTORY)) itemsList.Add(new VirtualStorageFolder(descriptor.fgd[ii].cFileName).FromStorageItem()); - else if (NativeClipboard.CurrentDataObject.GetData("FileContents", DVASPECT.DVASPECT_CONTENT, ii) is IStream stream) + else if (iddo.GetData("FileContents", DVASPECT.DVASPECT_CONTENT, ii) is IStream stream) { var streamContent = new ComStreamWrapper(stream); itemsList.Add(new VirtualStorageFile(streamContent, descriptor.fgd[ii].cFileName).FromStorageItem()); From 7859cfc9f04cb57cad55b98c8c75b40caba9935e Mon Sep 17 00:00:00 2001 From: Marco Gavelli Date: Sun, 29 Dec 2024 01:04:28 +0100 Subject: [PATCH 6/8] Detect right drag from explorer --- .../ViewModels/Layouts/BaseLayoutViewModel.cs | 10 ++++++- .../UserControls/AddressToolbarViewModel.cs | 7 +++++ .../UserControls/SidebarViewModel.cs | 29 +++++++++++++++---- src/Files.App/Views/Layouts/BaseLayoutPage.cs | 9 ++++-- src/Files.App/Views/Shells/BaseShellPage.cs | 6 +++- 5 files changed, 51 insertions(+), 10 deletions(-) diff --git a/src/Files.App/ViewModels/Layouts/BaseLayoutViewModel.cs b/src/Files.App/ViewModels/Layouts/BaseLayoutViewModel.cs index d25c920c7406..ec2c6586995a 100644 --- a/src/Files.App/ViewModels/Layouts/BaseLayoutViewModel.cs +++ b/src/Files.App/ViewModels/Layouts/BaseLayoutViewModel.cs @@ -7,10 +7,12 @@ using System.IO; using System.Runtime.InteropServices; using System.Windows.Input; +using Vanara.PInvoke; using Windows.ApplicationModel.DataTransfer; using Windows.ApplicationModel.DataTransfer.DragDrop; using Windows.Storage; using Windows.System; +using WinRT; namespace Files.App.ViewModels.Layouts { @@ -100,6 +102,10 @@ public async Task DragOverAsync(DragEventArgs e) return; } + // Check if this is a right-button drag operation and store into the dataobject + if (e.Modifiers.HasFlag(DragDropModifiers.RightButton)) + e.DataView.As().GetDataObject().SetData("dragRightButton", true); + if (FilesystemHelpers.HasDraggedStorageItems(e.DataView)) { e.Handled = true; @@ -187,7 +193,9 @@ public async Task DropAsync(DragEventArgs e) try { - if ((bool)e.DataView.Properties.GetValueOrDefault("dragRightButton", false)) + var isRightButtonDrag = e.DataView.As().GetDataObject().GetData("dragRightButton"); + + if (isRightButtonDrag) { SafetyExtensions.IgnoreExceptions(() => ShellContextFlyoutFactory.InvokeRightButtonDropMenu(_associatedInstance.ShellViewModel.WorkingDirectory, e.DataView, e.AcceptedOperation)); } diff --git a/src/Files.App/ViewModels/UserControls/AddressToolbarViewModel.cs b/src/Files.App/ViewModels/UserControls/AddressToolbarViewModel.cs index 07e096b3da2c..f731279ff002 100644 --- a/src/Files.App/ViewModels/UserControls/AddressToolbarViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/AddressToolbarViewModel.cs @@ -10,8 +10,11 @@ using Microsoft.UI.Xaml.Input; using System.IO; using System.Windows.Input; +using Vanara.PInvoke; using Windows.ApplicationModel.DataTransfer; +using Windows.ApplicationModel.DataTransfer.DragDrop; using Windows.UI.Text; +using WinRT; using FocusManager = Microsoft.UI.Xaml.Input.FocusManager; namespace Files.App.ViewModels.UserControls @@ -354,6 +357,10 @@ public async Task PathBoxItem_DragOver(object sender, DragEventArgs e) } } + // Check if this is a right-button drag operation and store into the dataobject + if (e.Modifiers.HasFlag(DragDropModifiers.RightButton)) + e.DataView.As().GetDataObject().SetData("dragRightButton", true); + // In search page if (!FilesystemHelpers.HasDraggedStorageItems(e.DataView) || string.IsNullOrEmpty(pathBoxItem.Path)) { diff --git a/src/Files.App/ViewModels/UserControls/SidebarViewModel.cs b/src/Files.App/ViewModels/UserControls/SidebarViewModel.cs index 3449ef8fa71a..00b3cd1cbe1f 100644 --- a/src/Files.App/ViewModels/UserControls/SidebarViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/SidebarViewModel.cs @@ -11,11 +11,13 @@ using System.Collections.Specialized; using System.IO; using System.Windows.Input; +using Vanara.PInvoke; using Windows.ApplicationModel.DataTransfer; using Windows.ApplicationModel.DataTransfer.DragDrop; using Windows.Storage; using Windows.System; using Windows.UI.Core; +using WinRT; namespace Files.App.ViewModels.UserControls { @@ -1073,6 +1075,10 @@ private async Task HandleLocationItemDragOverAsync(LocationItem locationItem, It { var rawEvent = args.RawEvent; + // Check if this is a right-button drag operation and store into the dataobject + if (args.RawEvent.Modifiers.HasFlag(DragDropModifiers.RightButton)) + args.DroppedItem.As().GetDataObject().SetData("dragRightButton", true); + if (Utils.Storage.FilesystemHelpers.HasDraggedStorageItems(args.DroppedItem)) { args.RawEvent.Handled = true; @@ -1158,6 +1164,10 @@ private async Task HandleDriveItemDragOverAsync(DriveItem driveItem, ItemDragOve if (!Utils.Storage.FilesystemHelpers.HasDraggedStorageItems(args.DroppedItem)) return; + // Check if this is a right-button drag operation and store into the dataobject + if (args.RawEvent.Modifiers.HasFlag(DragDropModifiers.RightButton)) + args.DroppedItem.As().GetDataObject().SetData("dragRightButton", true); + args.RawEvent.Handled = true; var storageItems = await Utils.Storage.FilesystemHelpers.GetDraggedStorageItems(args.DroppedItem); @@ -1251,20 +1261,27 @@ private async Task HandleLocationItemDroppedAsync(LocationItem locationItem, Ite await QuickAccessService.PinToSidebarAsync(item.Path); } } - else if ((bool)args.DroppedItem.Properties.GetValueOrDefault("dragRightButton", false)) - { - SafetyExtensions.IgnoreExceptions(() => ShellContextFlyoutFactory.InvokeRightButtonDropMenu(locationItem.Path, args.DroppedItem, args.RawEvent.AcceptedOperation)); - } else { - await FilesystemHelpers.PerformOperationTypeAsync(args.RawEvent.AcceptedOperation, args.DroppedItem, locationItem.Path, false, true); + var isRightButtonDrag = args.DroppedItem.As().GetDataObject().GetData("dragRightButton"); + + if (isRightButtonDrag) + { + SafetyExtensions.IgnoreExceptions(() => ShellContextFlyoutFactory.InvokeRightButtonDropMenu(locationItem.Path, args.DroppedItem, args.RawEvent.AcceptedOperation)); + } + else + { + await FilesystemHelpers.PerformOperationTypeAsync(args.RawEvent.AcceptedOperation, args.DroppedItem, locationItem.Path, false, true); + } } } } private Task HandleDriveItemDroppedAsync(DriveItem driveItem, ItemDroppedEventArgs args) { - if ((bool)args.DroppedItem.Properties.GetValueOrDefault("dragRightButton", false)) + var isRightButtonDrag = args.DroppedItem.As().GetDataObject().GetData("dragRightButton"); + + if (isRightButtonDrag) { SafetyExtensions.IgnoreExceptions(() => ShellContextFlyoutFactory.InvokeRightButtonDropMenu(driveItem.Path, args.DroppedItem, args.RawEvent.AcceptedOperation)); return Task.FromResult(ReturnResult.Success); diff --git a/src/Files.App/Views/Layouts/BaseLayoutPage.cs b/src/Files.App/Views/Layouts/BaseLayoutPage.cs index d1173bb0650b..69097cddef60 100644 --- a/src/Files.App/Views/Layouts/BaseLayoutPage.cs +++ b/src/Files.App/Views/Layouts/BaseLayoutPage.cs @@ -998,7 +998,6 @@ private void Item_DragStarting(UIElement sender, DragStartingEventArgs args) var selectedItems = SelectedItems?.ToList() ?? new(); selectedItems.AddIfNotPresent(item); - args.Data.Properties["dragRightButton"] = InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.RightButton).HasFlag(CoreVirtualKeyStates.Down); args.Cancel = DragItemsStarting(ItemsControl, args.Data, selectedItems); } @@ -1061,6 +1060,10 @@ private async void Item_DragOver(object sender, DragEventArgs e) DragOperationDeferral? deferral = null; + // Check if this is a right-button drag operation and store into the dataobject + if (e.Modifiers.HasFlag(DragDropModifiers.RightButton)) + e.DataView.As().GetDataObject().SetData("dragRightButton", true); + try { deferral = e.GetDeferral(); @@ -1164,7 +1167,9 @@ protected virtual async void Item_Drop(object sender, DragEventArgs e) var item = GetItemFromElement(sender); if (item is not null) { - if ((bool)e.DataView.Properties.GetValueOrDefault("dragRightButton", false)) + var isRightButtonDrag = e.DataView.As().GetDataObject().GetData("dragRightButton"); + + if (isRightButtonDrag) { SafetyExtensions.IgnoreExceptions(() => ShellContextFlyoutFactory.InvokeRightButtonDropMenu(item.ItemPath, e.DataView, e.AcceptedOperation)); } diff --git a/src/Files.App/Views/Shells/BaseShellPage.cs b/src/Files.App/Views/Shells/BaseShellPage.cs index 3f3088a72544..b15083e7c094 100644 --- a/src/Files.App/Views/Shells/BaseShellPage.cs +++ b/src/Files.App/Views/Shells/BaseShellPage.cs @@ -9,9 +9,11 @@ using Microsoft.UI.Xaml.Navigation; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using Vanara.PInvoke; using Windows.Foundation.Metadata; using Windows.System; using Windows.UI.Core; +using WinRT; using DispatcherQueueTimer = Microsoft.UI.Dispatching.DispatcherQueueTimer; namespace Files.App.Views.Shells @@ -417,7 +419,9 @@ protected void CoreWindow_PointerPressed(object sender, PointerRoutedEventArgs a protected async void ShellPage_PathBoxItemDropped(object sender, PathBoxItemDroppedEventArgs e) { - if ((bool)e.Package.Properties.GetValueOrDefault("dragRightButton", false)) + var isRightButtonDrag = e.Package.As().GetDataObject().GetData("dragRightButton"); + + if (isRightButtonDrag) { SafetyExtensions.IgnoreExceptions(() => ShellContextFlyoutFactory.InvokeRightButtonDropMenu(e.Path, e.Package, e.AcceptedOperation)); } From b02fefe975b8548f32b37d3e81bfefa89c63dbd7 Mon Sep 17 00:00:00 2001 From: Marco Gavelli Date: Sun, 29 Dec 2024 21:47:37 +0100 Subject: [PATCH 7/8] Fix check for right click drag --- src/Files.App/ViewModels/Layouts/BaseLayoutViewModel.cs | 2 +- src/Files.App/ViewModels/UserControls/SidebarViewModel.cs | 4 ++-- src/Files.App/Views/Layouts/BaseLayoutPage.cs | 2 +- src/Files.App/Views/Shells/BaseShellPage.cs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Files.App/ViewModels/Layouts/BaseLayoutViewModel.cs b/src/Files.App/ViewModels/Layouts/BaseLayoutViewModel.cs index ec2c6586995a..dcd856c6ce9b 100644 --- a/src/Files.App/ViewModels/Layouts/BaseLayoutViewModel.cs +++ b/src/Files.App/ViewModels/Layouts/BaseLayoutViewModel.cs @@ -193,7 +193,7 @@ public async Task DropAsync(DragEventArgs e) try { - var isRightButtonDrag = e.DataView.As().GetDataObject().GetData("dragRightButton"); + e.DataView.As().GetDataObject().TryGetData(User32.RegisterClipboardFormat("dragRightButton"), out var isRightButtonDrag); if (isRightButtonDrag) { diff --git a/src/Files.App/ViewModels/UserControls/SidebarViewModel.cs b/src/Files.App/ViewModels/UserControls/SidebarViewModel.cs index 00b3cd1cbe1f..8ffbb26df175 100644 --- a/src/Files.App/ViewModels/UserControls/SidebarViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/SidebarViewModel.cs @@ -1263,7 +1263,7 @@ private async Task HandleLocationItemDroppedAsync(LocationItem locationItem, Ite } else { - var isRightButtonDrag = args.DroppedItem.As().GetDataObject().GetData("dragRightButton"); + args.DroppedItem.As().GetDataObject().TryGetData(User32.RegisterClipboardFormat("dragRightButton"), out var isRightButtonDrag); if (isRightButtonDrag) { @@ -1279,7 +1279,7 @@ private async Task HandleLocationItemDroppedAsync(LocationItem locationItem, Ite private Task HandleDriveItemDroppedAsync(DriveItem driveItem, ItemDroppedEventArgs args) { - var isRightButtonDrag = args.DroppedItem.As().GetDataObject().GetData("dragRightButton"); + args.DroppedItem.As().GetDataObject().TryGetData(User32.RegisterClipboardFormat("dragRightButton"), out var isRightButtonDrag); if (isRightButtonDrag) { diff --git a/src/Files.App/Views/Layouts/BaseLayoutPage.cs b/src/Files.App/Views/Layouts/BaseLayoutPage.cs index 69097cddef60..0dbdea2db337 100644 --- a/src/Files.App/Views/Layouts/BaseLayoutPage.cs +++ b/src/Files.App/Views/Layouts/BaseLayoutPage.cs @@ -1167,7 +1167,7 @@ protected virtual async void Item_Drop(object sender, DragEventArgs e) var item = GetItemFromElement(sender); if (item is not null) { - var isRightButtonDrag = e.DataView.As().GetDataObject().GetData("dragRightButton"); + e.DataView.As().GetDataObject().TryGetData(User32.RegisterClipboardFormat("dragRightButton"), out var isRightButtonDrag); if (isRightButtonDrag) { diff --git a/src/Files.App/Views/Shells/BaseShellPage.cs b/src/Files.App/Views/Shells/BaseShellPage.cs index b15083e7c094..239a6e28bb03 100644 --- a/src/Files.App/Views/Shells/BaseShellPage.cs +++ b/src/Files.App/Views/Shells/BaseShellPage.cs @@ -419,7 +419,7 @@ protected void CoreWindow_PointerPressed(object sender, PointerRoutedEventArgs a protected async void ShellPage_PathBoxItemDropped(object sender, PathBoxItemDroppedEventArgs e) { - var isRightButtonDrag = e.Package.As().GetDataObject().GetData("dragRightButton"); + e.Package.As().GetDataObject().TryGetData(User32.RegisterClipboardFormat("dragRightButton"), out var isRightButtonDrag); if (isRightButtonDrag) { From fd2ef3927a5649654a33920253d256aed0ace167 Mon Sep 17 00:00:00 2001 From: Marco Gavelli Date: Sun, 29 Dec 2024 21:48:33 +0100 Subject: [PATCH 8/8] Revert "TO_TEST: Switch NativeClipboard.CurrentDataObject to IDataObjectProvider" This reverts commit 699e466f2eab27ab1018fc150be0cb56c73666b1. --- .../Utils/Storage/Operations/FilesystemHelpers.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Files.App/Utils/Storage/Operations/FilesystemHelpers.cs b/src/Files.App/Utils/Storage/Operations/FilesystemHelpers.cs index c5b1c5b9c183..9c2a5d888e1f 100644 --- a/src/Files.App/Utils/Storage/Operations/FilesystemHelpers.cs +++ b/src/Files.App/Utils/Storage/Operations/FilesystemHelpers.cs @@ -9,11 +9,11 @@ using System.Runtime.InteropServices; using System.Runtime.InteropServices.ComTypes; using Vanara.PInvoke; +using Vanara.Windows.Shell; using Windows.ApplicationModel.DataTransfer; using Windows.Graphics.Imaging; using Windows.Storage; using Windows.Storage.Streams; -using WinRT; using FileAttributes = System.IO.FileAttributes; namespace Files.App.Utils.Storage @@ -758,14 +758,12 @@ public static async Task> GetDraggedStorageIte { if (hasVirtualItems && packageView.Contains("FileContents")) { - var dataObjectProvider = packageView.As(); - var iddo = dataObjectProvider.GetDataObject(); - var descriptor = iddo.GetData("FileGroupDescriptorW"); + var descriptor = NativeClipboard.CurrentDataObject.GetData("FileGroupDescriptorW"); for (var ii = 0; ii < descriptor.cItems; ii++) { if (descriptor.fgd[ii].dwFileAttributes.HasFlag(FileFlagsAndAttributes.FILE_ATTRIBUTE_DIRECTORY)) itemsList.Add(new VirtualStorageFolder(descriptor.fgd[ii].cFileName).FromStorageItem()); - else if (iddo.GetData("FileContents", DVASPECT.DVASPECT_CONTENT, ii) is IStream stream) + else if (NativeClipboard.CurrentDataObject.GetData("FileContents", DVASPECT.DVASPECT_CONTENT, ii) is IStream stream) { var streamContent = new ComStreamWrapper(stream); itemsList.Add(new VirtualStorageFile(streamContent, descriptor.fgd[ii].cFileName).FromStorageItem());