Skip to content

Commit efbe164

Browse files
committed
Init impl
1 parent 1ee9de3 commit efbe164

File tree

3 files changed

+408
-10
lines changed

3 files changed

+408
-10
lines changed
Lines changed: 344 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,344 @@
1+
using Microsoft.UI.Input;
2+
using Microsoft.UI.Xaml;
3+
using Microsoft.UI.Xaml.Input;
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
using System.Text;
8+
using System.Text.RegularExpressions;
9+
using System.Threading.Tasks;
10+
using Windows.ApplicationModel.DataTransfer;
11+
using Windows.Foundation;
12+
using Windows.Win32;
13+
14+
namespace Files.App.Helpers
15+
{
16+
public class AutomaticDragHelper : DependencyObject
17+
{
18+
public static AutomaticDragHelper GetDragHelper(DependencyObject obj)
19+
{
20+
return (AutomaticDragHelper)obj.GetValue(DragHelperProperty);
21+
}
22+
23+
public static void SetDragHelper(DependencyObject obj, AutomaticDragHelper value)
24+
{
25+
obj.SetValue(DragHelperProperty, value);
26+
}
27+
28+
public static readonly DependencyProperty DragHelperProperty =
29+
DependencyProperty.RegisterAttached("DragHelper", typeof(AutomaticDragHelper), typeof(AutomaticDragHelper), new PropertyMetadata(null, OnDragHelperChanged));
30+
31+
private static void OnDragHelperChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
32+
{
33+
if (e.OldValue is AutomaticDragHelper old)
34+
old.StopDetectingDrag();
35+
}
36+
37+
// The standard Windows mouse drag box size is defined by SM_CXDRAG and SM_CYDRAG.
38+
// UIElement uses the standard box size with dimensions multiplied by this constant.
39+
// This arrangement is in place as accidentally triggering a drag was deemed too easy while
40+
// selecting several items with the mouse in quick succession.
41+
private const double UIELEMENT_MOUSE_DRAG_THRESHOLD_MULTIPLIER = 2.0;
42+
private readonly UIElement m_pOwnerNoRef;
43+
private readonly bool m_shouldAddInputHandlers;
44+
private bool m_isCheckingForMouseDrag;
45+
private Point m_lastMouseRightButtonDownPosition;
46+
private bool m_dragDropPointerPressedToken, m_dragDropPointerMovedToken, m_dragDropPointerReleasedToken, m_dragDropPointerCaptureLostToken, m_dragDropHoldingToken;
47+
private PointerPoint m_spPointerPoint;
48+
private Pointer m_spPointer;
49+
private bool m_isHoldingCompleted, m_isRightButtonPressed;
50+
51+
public AutomaticDragHelper(UIElement pUIElement, bool shouldAddInputHandlers)
52+
{
53+
m_pOwnerNoRef = pUIElement;
54+
m_shouldAddInputHandlers = shouldAddInputHandlers;
55+
}
56+
57+
// Begin tracking the mouse cursor in order to fire a drag start if the pointer
58+
// moves a certain distance away from m_lastMouseRightButtonDownPosition.
59+
public void BeginCheckingForMouseDrag(Pointer pPointer)
60+
{
61+
62+
bool captured = m_pOwnerNoRef.CapturePointer(pPointer);
63+
64+
m_isCheckingForMouseDrag = !!captured;
65+
}
66+
67+
// Stop tracking the mouse cursor.
68+
public void StopCheckingForMouseDrag(Pointer pPointer)
69+
{
70+
// Do not call ReleasePointerCapture() more times than we called CapturePointer()
71+
if (m_isCheckingForMouseDrag)
72+
{
73+
m_isCheckingForMouseDrag = false;
74+
75+
m_pOwnerNoRef.ReleasePointerCapture(pPointer);
76+
}
77+
}
78+
79+
// Return true if we're tracking the mouse and newMousePosition is outside the drag
80+
// rectangle centered at m_lastMouseRightButtonDownPosition (see IsOutsideDragRectangle).
81+
bool ShouldStartMouseDrag(Point newMousePosition)
82+
{
83+
return m_isCheckingForMouseDrag && IsOutsideDragRectangle(newMousePosition, m_lastMouseRightButtonDownPosition);
84+
}
85+
86+
87+
// Returns true if testPoint is outside of the rectangle
88+
// defined by the SM_CXDRAG and SM_CYDRAG system metrics and
89+
// dragRectangleCenter.
90+
bool IsOutsideDragRectangle(Point testPoint, Point dragRectangleCenter)
91+
{
92+
93+
double dx = Math.Abs(testPoint.X - dragRectangleCenter.X);
94+
double dy = Math.Abs(testPoint.Y - dragRectangleCenter.Y);
95+
96+
double maxDx = PInvoke.GetSystemMetrics(Windows.Win32.UI.WindowsAndMessaging.SYSTEM_METRICS_INDEX.SM_CXDRAG);
97+
double maxDy = PInvoke.GetSystemMetrics(Windows.Win32.UI.WindowsAndMessaging.SYSTEM_METRICS_INDEX.SM_CYDRAG);
98+
99+
maxDx *= UIELEMENT_MOUSE_DRAG_THRESHOLD_MULTIPLIER;
100+
maxDy *= UIELEMENT_MOUSE_DRAG_THRESHOLD_MULTIPLIER;
101+
102+
return (dx > maxDx || dy > maxDy);
103+
}
104+
105+
public void StartDetectingDrag()
106+
{
107+
if (m_shouldAddInputHandlers && !m_dragDropPointerPressedToken)
108+
{
109+
m_pOwnerNoRef.PointerPressed += HandlePointerPressedEventArgs;
110+
m_dragDropPointerPressedToken = true;
111+
}
112+
}
113+
114+
public void StopDetectingDrag()
115+
{
116+
if (m_dragDropPointerPressedToken)
117+
{
118+
m_pOwnerNoRef.PointerPressed -= HandlePointerPressedEventArgs;
119+
// zero out the token;
120+
m_dragDropPointerPressedToken = false;
121+
}
122+
}
123+
124+
public void RegisterDragPointerEvents()
125+
{
126+
if (m_shouldAddInputHandlers)
127+
{
128+
PointerEventHandler spDragDropPointerMovedHandler;
129+
PointerEventHandler spDragDropPointerReleasedHandler;
130+
PointerEventHandler spDragDropPointerCaptureLostHandler;
131+
132+
// Hookup pointer events so we can catch and handle it for drag and drop.
133+
if (!m_dragDropPointerMovedToken)
134+
{
135+
m_pOwnerNoRef.PointerMoved += HandlePointerMovedEventArgs;
136+
m_dragDropPointerMovedToken = true;
137+
}
138+
139+
if (!m_dragDropPointerReleasedToken)
140+
{
141+
m_pOwnerNoRef.PointerReleased += HandlePointerReleasedEventArgs;
142+
m_dragDropPointerReleasedToken = true;
143+
}
144+
145+
if (!m_dragDropPointerCaptureLostToken)
146+
{
147+
m_pOwnerNoRef.PointerCaptureLost += HandlePointerCaptureLostEventArgs;
148+
m_dragDropPointerCaptureLostToken = true;
149+
}
150+
}
151+
}
152+
153+
public void HandlePointerPressedEventArgs(object sender, PointerRoutedEventArgs pArgs)
154+
{
155+
Pointer spPointer;
156+
PointerDeviceType pointerDeviceType = PointerDeviceType.Touch;
157+
PointerPoint spPointerPoint;
158+
159+
m_spPointerPoint = null;
160+
m_spPointer = null;
161+
//m_isHoldingCompleted = false;
162+
163+
spPointer = pArgs.Pointer;
164+
pointerDeviceType = spPointer.PointerDeviceType;
165+
166+
spPointerPoint = pArgs.GetCurrentPoint(m_pOwnerNoRef);
167+
168+
// Check if this is a mouse button down.
169+
if (pointerDeviceType == PointerDeviceType.Mouse || pointerDeviceType == PointerDeviceType.Pen)
170+
{
171+
// Mouse button down.
172+
PointerPointProperties spPointerProperties;
173+
bool isRightButtonPressed = false;
174+
175+
spPointerProperties = spPointerPoint.Properties;
176+
isRightButtonPressed = spPointerProperties.IsRightButtonPressed;
177+
178+
// If the left mouse button was the one pressed...
179+
if (!m_isRightButtonPressed && isRightButtonPressed)
180+
{
181+
m_isRightButtonPressed = true;
182+
// Start listening for a mouse drag gesture
183+
m_lastMouseRightButtonDownPosition = spPointerPoint.Position;
184+
BeginCheckingForMouseDrag(spPointer);
185+
186+
RegisterDragPointerEvents();
187+
}
188+
}
189+
/*else
190+
{
191+
m_spPointerPoint = spPointerPoint;
192+
m_spPointer = spPointer;
193+
194+
if (m_shouldAddInputHandlers && !m_dragDropHoldingToken)
195+
{
196+
// Touch input occurs, subscribe to holding
197+
m_pOwnerNoRef.Holding += HandleHoldingEventArgs;
198+
}
199+
200+
RegisterDragPointerEvents();
201+
}*/
202+
}
203+
204+
public void HandlePointerMovedEventArgs(object sender, PointerRoutedEventArgs pArgs)
205+
{
206+
Pointer spPointer;
207+
PointerDeviceType pointerDeviceType = PointerDeviceType.Touch;
208+
209+
spPointer = pArgs.Pointer;
210+
pointerDeviceType = spPointer.PointerDeviceType;
211+
212+
// Our behavior is different between mouse and touch.
213+
// It's up to us to detect mouse drag gestures - if we
214+
// detect one here, start a drag drop.
215+
if (pointerDeviceType == PointerDeviceType.Mouse || pointerDeviceType == PointerDeviceType.Pen)
216+
{
217+
PointerPoint spPointerPoint;
218+
Point newMousePosition;
219+
220+
spPointerPoint = pArgs.GetCurrentPoint(m_pOwnerNoRef);
221+
222+
newMousePosition = spPointerPoint.Position;
223+
if (ShouldStartMouseDrag(newMousePosition))
224+
{
225+
IAsyncOperation<DataPackageOperation> spAsyncOperation;
226+
StopCheckingForMouseDrag(spPointer);
227+
228+
spAsyncOperation = m_pOwnerNoRef.StartDragAsync(spPointerPoint);
229+
}
230+
}
231+
}
232+
233+
234+
public void HandlePointerReleasedEventArgs(object sender, PointerRoutedEventArgs pArgs)
235+
{
236+
Pointer spPointer;
237+
PointerDeviceType pointerDeviceType = PointerDeviceType.Touch;
238+
239+
spPointer = pArgs.Pointer;
240+
pointerDeviceType = spPointer.PointerDeviceType;
241+
242+
// Check if this is a mouse button up
243+
if (pointerDeviceType == PointerDeviceType.Mouse || pointerDeviceType == PointerDeviceType.Pen)
244+
{
245+
bool isRightButtonPressed = false;
246+
PointerPoint spPointerPoint;
247+
PointerPointProperties spPointerProperties;
248+
249+
spPointerPoint = pArgs.GetCurrentPoint(m_pOwnerNoRef);
250+
spPointerProperties = spPointerPoint.Properties;
251+
isRightButtonPressed = spPointerProperties.IsRightButtonPressed;
252+
253+
// if the mouse left button was the one released...
254+
if (m_isRightButtonPressed && !isRightButtonPressed)
255+
{
256+
m_isRightButtonPressed = false;
257+
UnregisterEvents();
258+
// Terminate any mouse drag gesture tracking.
259+
StopCheckingForMouseDrag(spPointer);
260+
}
261+
}
262+
else
263+
{
264+
UnregisterEvents();
265+
}
266+
}
267+
268+
public void HandlePointerCaptureLostEventArgs(object sender, PointerRoutedEventArgs pArgs)
269+
{
270+
Pointer spPointer;
271+
PointerDeviceType pointerDeviceType = PointerDeviceType.Touch;
272+
273+
spPointer = pArgs.Pointer;
274+
275+
pointerDeviceType = spPointer.PointerDeviceType;
276+
if (pointerDeviceType == PointerDeviceType.Mouse || pointerDeviceType == PointerDeviceType.Pen)
277+
{
278+
// We're not necessarily going to get a PointerReleased on capture lost, so reset this flag here.
279+
m_isRightButtonPressed = false;
280+
}
281+
282+
UnregisterEvents();
283+
}
284+
285+
public void UnregisterEvents()
286+
{
287+
// Unregister events handlers
288+
if (m_dragDropPointerMovedToken)
289+
{
290+
m_pOwnerNoRef.PointerMoved -= HandlePointerMovedEventArgs;
291+
m_dragDropPointerMovedToken = false;
292+
}
293+
294+
if (m_dragDropPointerReleasedToken)
295+
{
296+
m_pOwnerNoRef.PointerReleased -= HandlePointerReleasedEventArgs;
297+
m_dragDropPointerReleasedToken = false;
298+
}
299+
300+
if (m_dragDropPointerCaptureLostToken)
301+
{
302+
m_pOwnerNoRef.PointerCaptureLost -= HandlePointerCaptureLostEventArgs;
303+
m_dragDropPointerCaptureLostToken = false;
304+
}
305+
306+
/*if (m_dragDropHoldingToken)
307+
{
308+
m_pOwnerNoRef.Holding -= HandleHoldingEventArgs;
309+
m_dragDropHoldingToken = false;
310+
}*/
311+
}
312+
313+
/*public void HandleHoldingEventArgs(object sender, HoldingRoutedEventArgs pArgs)
314+
{
315+
PointerDeviceType pointerDeviceType = PointerDeviceType.Touch;
316+
317+
pointerDeviceType = pArgs.PointerDeviceType;
318+
319+
if (pointerDeviceType == PointerDeviceType.Touch)
320+
{
321+
HoldingState holdingState = HoldingState.Started;
322+
holdingState = pArgs.HoldingState;
323+
324+
if (holdingState == HoldingState.Started)
325+
{
326+
m_isHoldingCompleted = true;
327+
}
328+
}
329+
}*/
330+
331+
/*public void HandleDirectManipulationDraggingStarted()
332+
{
333+
// Release cross-slide viewport now
334+
m_pOwnerNoRef.DirectManipulationCrossSlideContainerCompleted();
335+
if (m_isHoldingCompleted)
336+
{
337+
m_pOwnerNoRef.OnTouchDragStarted(m_spPointerPoint, m_spPointer);
338+
}
339+
340+
m_spPointerPoint = null;
341+
m_spPointer = null;
342+
}*/
343+
}
344+
}

src/Files.App/ViewModels/Layouts/BaseLayoutViewModel.cs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@
77
using System.IO;
88
using System.Runtime.InteropServices;
99
using System.Windows.Input;
10+
using Vanara.PInvoke;
1011
using Windows.ApplicationModel.DataTransfer;
1112
using Windows.ApplicationModel.DataTransfer.DragDrop;
1213
using Windows.Storage;
1314
using Windows.System;
15+
using WinRT;
1416

1517
namespace Files.App.ViewModels.Layouts
1618
{
@@ -187,7 +189,20 @@ public async Task DropAsync(DragEventArgs e)
187189

188190
try
189191
{
190-
await _associatedInstance.FilesystemHelpers.PerformOperationTypeAsync(e.AcceptedOperation, e.DataView, _associatedInstance.ShellViewModel.WorkingDirectory, false, true);
192+
if ((bool)e.DataView.Properties.GetValueOrDefault("dragRightButton", false))
193+
{
194+
Windows.Win32.PInvoke.GetCursorPos(out var dropPoint);
195+
using var sf = new Vanara.Windows.Shell.ShellFolder(_associatedInstance.ShellViewModel.WorkingDirectory);
196+
var dataObjectProvider = e.DataView.As<Shell32.IDataObjectProvider>();
197+
var iddo = dataObjectProvider.GetDataObject();
198+
var dropTarget = sf.GetViewObject<Ole32.IDropTarget>(HWND.NULL);
199+
dropTarget.DragEnter(iddo, Vanara.PInvoke.MouseButtonState.MK_RBUTTON, new() { X=dropPoint.X, Y=dropPoint.Y }, (Ole32.DROPEFFECT)e.AcceptedOperation);
200+
dropTarget.Drop(iddo, Vanara.PInvoke.MouseButtonState.MK_RBUTTON, new() { X=dropPoint.X, Y=dropPoint.Y }, (Ole32.DROPEFFECT)e.AcceptedOperation);
201+
}
202+
else
203+
{
204+
await _associatedInstance.FilesystemHelpers.PerformOperationTypeAsync(e.AcceptedOperation, e.DataView, _associatedInstance.ShellViewModel.WorkingDirectory, false, true);
205+
}
191206
await _associatedInstance.RefreshIfNoWatcherExistsAsync();
192207
}
193208
finally

0 commit comments

Comments
 (0)