Skip to content

Commit c3fe5df

Browse files
authored
Merge pull request #590 from MicrosoftEdge/api-notifydroptargetaction-draft
Create NotifyDropTargetAction.md
2 parents e7087aa + 419a3a3 commit c3fe5df

File tree

1 file changed

+313
-0
lines changed

1 file changed

+313
-0
lines changed

specs/NotifyDropTargetAction.md

Lines changed: 313 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,313 @@
1+
<!-- TEMPLATE
2+
The purpose of this spec is to describe a new WebView2 feature and its APIs.
3+
4+
There are two audiences for the spec. The first are people
5+
that want to evaluate and give feedback on the API, as part of
6+
the submission process. When it's complete
7+
it will be incorporated into the public documentation at
8+
docs.microsoft.com (https://docs.microsoft.com/en-us/microsoft-edge/webview2/).
9+
Hopefully we'll be able to copy it mostly verbatim.
10+
So the second audience is everyone that reads there to learn how
11+
and why to use this API.
12+
-->
13+
14+
15+
# Background
16+
These APIs will allow users to drop things such as images, text, and links into the WebView as part of a drag/drop operation.
17+
The reason that we need separate APIs for this with composition hosting is because we don't have an HWND to call RegisterDragDrop on.
18+
The hosting app needs to call RegisterDragDrop on the HWND that contains the WebView and implement IDropTarget so it can forward the
19+
calls (IDropTarget::DragEnter, DragMove, DragLeave, and Drop) to the WebView. Other UI frameworks have their own ways to register.
20+
For example, Xaml require setting event handler for DragEnter, DragOver, DragLeave, and Drop on the corresponding UIElement.
21+
22+
Additionally, for win32, the hosting app also needs to call into IDropTargetHelper before forwarding those calls to us as documented here:
23+
https://docs.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-idroptargethelper
24+
25+
For API reviewers, We want a unified API surface between COM and WinRT that works in both UWP and Win32.
26+
27+
We could:
28+
1. Have one API focusing on Win32 types and require the end dev to convert from UWP types to Win32 (which we've done).
29+
2. Have one API focusing on UWP types and require the end dev to convert Win32 to UWP.
30+
3. Have two sets of methods one for Win32 and one for UWP types.
31+
Or we could do (1) or (2) and provide a conversion function.
32+
Because the conversion is simple and we have a large Win32 user base we chose (1) and provided a conversion function.
33+
34+
35+
# Description
36+
DragEnter, DragOver, DragLeave, and Drop are functions meant to provide a way for composition hosted WebViews to receive drop events as part of a drag/drop operation.
37+
It is the hosting application's responsibility to call RegisterDragDrop (https://docs.microsoft.com/en-us/windows/win32/api/ole2/nf-ole2-registerdragdrop)
38+
on the HWND that contains any composition hosted WebViews and to implement IDropTarget(https://docs.microsoft.com/en-us/windows/win32/api/oleidl/nn-oleidl-idroptarget)
39+
to receive the corresponding drop events from Ole32:
40+
41+
- IDropTarget::DragEnter
42+
- IDropTarget::DragMove
43+
- IDropTarget::DragLeave
44+
- IDropTarget::Drop
45+
46+
For other UI frameworks such as Xaml, the hosting application would set event handlers for DragEnter, DragOver, DragLeave, and Drop
47+
on the UIElement that contains the WebView2 element.
48+
49+
# Examples
50+
## Win32
51+
```c++
52+
// Win32 Sample
53+
54+
// Implementation for IDropTarget
55+
HRESULT DropTarget::DragEnter(IDataObject* dataObject,
56+
DWORD keyState,
57+
POINTL point,
58+
DWORD* effect)
59+
{
60+
POINT point = { cursorPosition.x, cursorPosition.y };
61+
// Tell the helper that we entered so it can update the drag image and get
62+
// the correct effect.
63+
wil::com_ptr<IDropTargetHelper> dropHelper = DropHelper();
64+
if (dropHelper)
65+
{
66+
dropHelper->DragEnter(GetHWND(), dataObject, &point, *effect);
67+
}
68+
69+
// Convert the screen point to client coordinates add the WebView's offset.
70+
m_viewComponent->OffsetPointToWebView(&point);
71+
return m_webViewCompositionController2->DragEnter(
72+
dataObject, keyState, point, effect);
73+
}
74+
75+
HRESULT DropTarget::DragOver(DWORD keyState,
76+
POINTL point,
77+
DWORD* effect)
78+
{
79+
POINT point = { cursorPosition.x, cursorPosition.y };
80+
// Tell the helper that we moved over it so it can update the drag image
81+
// and get the correct effect.
82+
wil::com_ptr<IDropTargetHelper> dropHelper = DropHelper();
83+
if (dropHelper)
84+
{
85+
dropHelper->DragOver(&point, *effect);
86+
}
87+
88+
// Convert the screen point to client coordinates add the WebView's offset.
89+
// This returns whether the resultant point is over the WebView visual.
90+
m_viewComponent->OffsetPointToWebView(&point);
91+
return m_webViewCompositionController2->DragOver(
92+
keyState, point, effect);
93+
}
94+
95+
HRESULT DropTarget::DragLeave()
96+
{
97+
// Tell the helper that we moved out of it so it can update the drag image.
98+
wil::com_ptr<IDropTargetHelper> dropHelper = DropHelper();
99+
if (dropHelper)
100+
{
101+
dropHelper->DragLeave();
102+
}
103+
104+
return m_webViewCompositionController2->DragLeave();
105+
}
106+
107+
HRESULT DropTarget::Drop(IDataObject* dataObject,
108+
DWORD keyState,
109+
POINTL point,
110+
DWORD* effect)
111+
{
112+
POINT point = { cursorPosition.x, cursorPosition.y };
113+
// Tell the helper that we dropped onto it so it can update the drag image
114+
// and get the correct effect.
115+
wil::com_ptr<IDropTargetHelper> dropHelper = DropHelper();
116+
if (dropHelper)
117+
{
118+
dropHelper->Drop(dataObject, &point, *effect);
119+
}
120+
121+
// Convert the screen point to client coordinates add the WebView's offset.
122+
// This returns whether the resultant point is over the WebView visual.
123+
m_viewComponent->OffsetPointToWebView(&point);
124+
return m_webViewCompositionController2->Drop(
125+
dataObject, keyState, point, effect);
126+
}
127+
```
128+
129+
## WinRT
130+
```c#
131+
// WinRT Sample
132+
private void WebView_DragEnter(object sender, DragEventArgs e)
133+
{
134+
uint keyboardState =
135+
ConvertDragDropModifiersToWin32KeyboardState(e.Modifiers);
136+
Point pointerPosition = CoreWindow.GetForCurrentThread().PointerPosition;
137+
DataPackageOperation operation =
138+
(DataPackageOperation)webView2CompositionController.DragEnter(
139+
e.Data, keyboardState, pointerPosition);
140+
e.AcceptedOperation = operation;
141+
}
142+
143+
private void WebView_DragOver(object sender, DragEventArgs e)
144+
{
145+
uint keyboardState =
146+
ConvertDragDropModifiersToWin32KeyboardState(e.Modifiers);
147+
Point pointerPosition = CoreWindow.GetForCurrentThread().PointerPosition;
148+
DataPackageOperation operation =
149+
(DataPackageOperation)webView2CompositionController.DragOver(
150+
keyboardState, pointerPosition);
151+
e.AcceptedOperation = operation;
152+
}
153+
154+
private void WebView_DragLeave(object sender, DragEventArgs e)
155+
{
156+
webView2CompositionController.DragLeave();
157+
}
158+
159+
private void WebView_Drop(object sender, DragEventArgs e)
160+
{
161+
uint keyboardState =
162+
ConvertDragDropModifiersToWin32KeyboardState(e.Modifiers);
163+
Point pointerPosition = CoreWindow.GetForCurrentThread().PointerPosition;
164+
DataPackageOperation operation =
165+
(DataPackageOperation)webView2CompositionController.Drop(
166+
e.Data, keyboardState, pointerPosition);
167+
e.AcceptedOperation = operation;
168+
}
169+
170+
// Win32 keyboard state modifiers that are relevant during drag and drop.
171+
public const uint MK_LBUTTON = 1;
172+
public const uint MK_RBUTTON = 2;
173+
public const uint MK_SHIFT = 4;
174+
public const uint MK_CONTROL = 8;
175+
public const uint MK_MBUTTON = 16;
176+
public const uint MK_ALT = 32;
177+
178+
// Helper function to convert DragDropModifiers to win32 keyboard state
179+
// modifiers that WebView2 uses during drag and drop operation.
180+
private uint ConvertDragDropModifiersToWin32KeyboardState(
181+
Windows.ApplicationModel.DataTransfer.DragDrop.DragDropModifiers modifiers)
182+
{
183+
uint win32DragDropModifiers = 0;
184+
if ((modifiers & DragDropModifiers.Shift) == DragDropModifiers.Shift)
185+
win32DragDropModifiers |= MK_SHIFT;
186+
if ((modifiers & DragDropModifiers.Control) == DragDropModifiers.Control)
187+
win32DragDropModifiers |= MK_CONTROL;
188+
if ((modifiers & DragDropModifiers.Alt) == DragDropModifiers.Alt)
189+
win32DragDropModifiers |= MK_ALT;
190+
if ((modifiers & DragDropModifiers.LeftButton) == DragDropModifiers.LeftButton)
191+
win32DragDropModifiers |= MK_LBUTTON;
192+
if ((modifiers & DragDropModifiers.MiddleButton) == DragDropModifiers.MiddleButton)
193+
win32DragDropModifiers |= MK_MBUTTON;
194+
if ((modifiers & DragDropModifiers.RightButton) == DragDropModifiers.RightButton)
195+
win32DragDropModifiers |= MK_RBUTTON;
196+
return win32DragDropModifiers;
197+
}
198+
```
199+
200+
201+
# API Details
202+
## Win32
203+
```c++
204+
interface ICoreWebView2CompositionController2 : ICoreWebView2CompositionController {
205+
/// This set of APIs (DragEnter, DragLeave, DragOver, and Drop) will allow
206+
/// users to drop things such as images, text, and links into the WebView as
207+
/// part of a drag/drop operation. The reason that we need a separate API for
208+
/// this with composition hosting is because we don't have an HWND to call
209+
/// RegisterDragDrop on. The hosting app needs to call RegisterDragDrop on the
210+
/// HWND that contains the WebView and implement IDropTarget so it can forward
211+
/// the calls (IDropTarget::DragEnter, DragMove, DragLeave, and Drop) to the
212+
/// WebView.
213+
///
214+
/// This function corresponds to IDropTarget::DragEnter
215+
///
216+
/// The hosting application must register as an IDropTarget and implement
217+
/// and forward DragEnter calls to this function.
218+
///
219+
/// In addition, the hosting application needs to create an IDropTargetHelper
220+
/// and call the corresponding IDropTargetHelper::DragEnter function on that
221+
/// object before forwarding the call to WebView.
222+
///
223+
/// point parameter must be modified to include the WebView's offset and be in
224+
/// the WebView's client coordinates (Similar to how SendMouseInput works).
225+
HRESULT DragEnter(
226+
[in] IDataObject* dataObject,
227+
[in] DWORD keyState,
228+
[in] POINT point,
229+
[out, retval] DWORD* effect);
230+
231+
/// Please refer to DragEnter for more information on how Drag/Drop works with
232+
/// WebView2.
233+
///
234+
/// This function corresponds to IDropTarget::DragLeave
235+
///
236+
/// The hosting application must register as an IDropTarget and implement
237+
/// and forward DragLeave calls to this function.
238+
///
239+
/// In addition, the hosting application needs to create an IDropTargetHelper
240+
/// and call the corresponding IDropTargetHelper::DragLeave function on that
241+
/// object before forwarding the call to WebView.
242+
HRESULT DragLeave();
243+
244+
/// Please refer to DragEnter for more information on how Drag/Drop works with
245+
/// WebView2.
246+
///
247+
/// This function corresponds to IDropTarget::DragOver
248+
///
249+
/// The hosting application must register as an IDropTarget and implement
250+
/// and forward DragOver calls to this function.
251+
///
252+
/// In addition, the hosting application needs to create an IDropTargetHelper
253+
/// and call the corresponding IDropTargetHelper::DragOver function on that
254+
/// object before forwarding the call to WebView.
255+
///
256+
/// point parameter must be modified to include the WebView's offset and be in
257+
/// the WebView's client coordinates (Similar to how SendMouseInput works).
258+
HRESULT DragOver(
259+
[in] DWORD keyState,
260+
[in] POINT point,
261+
[out, retval] DWORD* effect);
262+
263+
/// Please refer to DragEnter for more information on how Drag/Drop works with
264+
/// WebView2.
265+
///
266+
/// This function corresponds to IDropTarget::Drop
267+
///
268+
/// The hosting application must register as an IDropTarget and implement
269+
/// and forward Drop calls to this function.
270+
///
271+
/// In addition, the hosting application needs to create an IDropTargetHelper
272+
/// and call the corresponding IDropTargetHelper::Drop function on that
273+
/// object before forwarding the call to WebView.
274+
///
275+
/// point parameter must be modified to include the WebView's offset and be in
276+
/// the WebView's client coordinates (Similar to how SendMouseInput works).
277+
HRESULT Drop(
278+
[in] IDataObject* dataObject,
279+
[in] DWORD keyState,
280+
[in] POINT point,
281+
[out, retval] DWORD* effect);
282+
}
283+
```
284+
285+
## WinRT
286+
```c#
287+
namespace Microsoft.Web.WebView2.Core
288+
{
289+
public sealed class CoreWebView2CompositionController : CoreWebView2Controller, ICoreWebView2CompositionController2
290+
{
291+
// New APIs
292+
uint DragEnter(
293+
Windows.ApplicationModel.DataTransfer.DataPackage dataObject,
294+
uint keyState,
295+
Point point);
296+
297+
void DragLeave();
298+
299+
uint DragOver(
300+
uint keyState,
301+
Windows.Foundation.Point point);
302+
303+
uint Drop(
304+
Windows.ApplicationModel.DataTransfer.DataPackage dataObject,
305+
uint keyState,
306+
Windows.Foundation.Point point);
307+
}
308+
}
309+
```
310+
311+
# Appendix
312+
A good resource to read about the whole Ole drag/drop is located here:
313+
https://docs.microsoft.com/en-us/cpp/mfc/drag-and-drop-ole?view=msvc-160#:~:text=You%20select%20the%20data%20from,than%20the%20copy%2Fpaste%20sequence.

0 commit comments

Comments
 (0)