A cross-platform UI framework for .NET
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

215 lines
7.1 KiB

using System;
using Avalonia.Input;
using Avalonia.Input.Raw;
using Avalonia.MicroCom;
using Avalonia.Platform;
using Avalonia.Win32.Interop;
using DropEffect = Avalonia.Win32.Win32Com.DropEffect;
namespace Avalonia.Win32
{
internal class OleDropTarget : CallbackBase, Win32Com.IDropTarget
{
private readonly IInputRoot _target;
private readonly ITopLevelImpl _tl;
private readonly IDragDropDevice _dragDevice;
private IDataObject _currentDrag = null;
public OleDropTarget(ITopLevelImpl tl, IInputRoot target)
{
_dragDevice = AvaloniaLocator.Current.GetService<IDragDropDevice>();
_tl = tl;
_target = target;
}
public static DropEffect ConvertDropEffect(DragDropEffects operation)
{
DropEffect result = DropEffect.None;
if (operation.HasAllFlags(DragDropEffects.Copy))
result |= DropEffect.Copy;
if (operation.HasAllFlags(DragDropEffects.Move))
result |= DropEffect.Move;
if (operation.HasAllFlags(DragDropEffects.Link))
result |= DropEffect.Link;
return result;
}
public static DragDropEffects ConvertDropEffect(DropEffect effect)
{
DragDropEffects result = DragDropEffects.None;
if (effect.HasAllFlags(DropEffect.Copy))
result |= DragDropEffects.Copy;
if (effect.HasAllFlags(DropEffect.Move))
result |= DragDropEffects.Move;
if (effect.HasAllFlags(DropEffect.Link))
result |= DragDropEffects.Link;
return result;
}
private static RawInputModifiers ConvertKeyState(int grfKeyState)
{
var modifiers = RawInputModifiers.None;
var state = (UnmanagedMethods.ModifierKeys)grfKeyState;
if (state.HasAllFlags(UnmanagedMethods.ModifierKeys.MK_LBUTTON))
modifiers |= RawInputModifiers.LeftMouseButton;
if (state.HasAllFlags(UnmanagedMethods.ModifierKeys.MK_MBUTTON))
modifiers |= RawInputModifiers.MiddleMouseButton;
if (state.HasAllFlags(UnmanagedMethods.ModifierKeys.MK_RBUTTON))
modifiers |= RawInputModifiers.RightMouseButton;
if (state.HasAllFlags(UnmanagedMethods.ModifierKeys.MK_SHIFT))
modifiers |= RawInputModifiers.Shift;
if (state.HasAllFlags(UnmanagedMethods.ModifierKeys.MK_CONTROL))
modifiers |= RawInputModifiers.Control;
if (state.HasAllFlags(UnmanagedMethods.ModifierKeys.MK_ALT))
modifiers |= RawInputModifiers.Alt;
return modifiers;
}
unsafe void Win32Com.IDropTarget.DragEnter(Win32Com.IDataObject pDataObj, int grfKeyState, UnmanagedMethods.POINT pt, DropEffect* pdwEffect)
{
var dispatch = _tl?.Input;
if (dispatch == null)
{
*pdwEffect= (int)DropEffect.None;
}
SetDataObject(pDataObj);
var args = new RawDragEvent(
_dragDevice,
RawDragEventType.DragEnter,
_target,
GetDragLocation(pt),
_currentDrag,
ConvertDropEffect(*pdwEffect),
ConvertKeyState(grfKeyState)
);
dispatch(args);
*pdwEffect = ConvertDropEffect(args.Effects);
}
unsafe void Win32Com.IDropTarget.DragOver(int grfKeyState, UnmanagedMethods.POINT pt, DropEffect* pdwEffect)
{
var dispatch = _tl?.Input;
if (dispatch == null)
{
*pdwEffect = (int)DropEffect.None;
}
var args = new RawDragEvent(
_dragDevice,
RawDragEventType.DragOver,
_target,
GetDragLocation(pt),
_currentDrag,
ConvertDropEffect(*pdwEffect),
ConvertKeyState(grfKeyState)
);
dispatch(args);
*pdwEffect = ConvertDropEffect(args.Effects);
}
void Win32Com.IDropTarget.DragLeave()
{
try
{
_tl?.Input(new RawDragEvent(
_dragDevice,
RawDragEventType.DragLeave,
_target,
default(Point),
null,
DragDropEffects.None,
RawInputModifiers.None
));
}
finally
{
ReleaseDataObject();
}
}
unsafe void Win32Com.IDropTarget.Drop(Win32Com.IDataObject pDataObj, int grfKeyState, UnmanagedMethods.POINT pt, DropEffect* pdwEffect)
{
try
{
var dispatch = _tl?.Input;
if (dispatch == null)
{
*pdwEffect = (int)DropEffect.None;
}
SetDataObject(pDataObj);
var args = new RawDragEvent(
_dragDevice,
RawDragEventType.Drop,
_target,
GetDragLocation(pt),
_currentDrag,
ConvertDropEffect(*pdwEffect),
ConvertKeyState(grfKeyState)
);
dispatch(args);
*pdwEffect = ConvertDropEffect(args.Effects);
}
finally
{
ReleaseDataObject();
}
}
private void SetDataObject(Win32Com.IDataObject pDataObj)
{
var newDrag = GetAvaloniaObjectFromCOM(pDataObj);
if (_currentDrag != newDrag)
{
ReleaseDataObject();
_currentDrag = newDrag;
}
}
private void ReleaseDataObject()
{
// OleDataObject keeps COM reference, so it should be disposed.
if (_currentDrag is OleDataObject oleDragSource)
{
oleDragSource?.Dispose();
_currentDrag = null;
}
}
private Point GetDragLocation(UnmanagedMethods.POINT dragPoint)
{
var screenPt = new PixelPoint(dragPoint.X, dragPoint.Y);
return _target.PointToClient(screenPt);
}
protected override void Destroyed()
{
ReleaseDataObject();
}
public static unsafe IDataObject GetAvaloniaObjectFromCOM(Win32Com.IDataObject pDataObj)
{
if (pDataObj is null)
{
throw new ArgumentNullException(nameof(pDataObj));
}
if (pDataObj is IDataObject disposableDataObject)
{
return disposableDataObject;
}
var dataObject = MicroComRuntime.TryUnwrapManagedObject(pDataObj) as DataObject;
if (dataObject is not null)
{
return dataObject;
}
return new OleDataObject(pDataObj);
}
}
}