Skip to content

Commit 0b37a79

Browse files
Merge pull request #3367 from MicrosoftEdge/api-NCHitTestPoint
Api For Hit Testing
2 parents 184b0ee + 7c9bbbc commit 0b37a79

File tree

1 file changed

+273
-0
lines changed

1 file changed

+273
-0
lines changed

specs/NonClientHitTestKind.md

Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
Hit Test Kind
2+
===
3+
4+
# Background
5+
The ability to ask WebView2 for information on a hit test is an important feature for
6+
visually hosted apps. It can be used to support features related to mouse activity in
7+
regions that are traditionally Non-Client areas. Features like Draggable Regions,
8+
Resize, Minimize/Maximize functionality, and more.
9+
10+
11+
# Examples
12+
## Draggable regions (HWND)
13+
Draggable regions are marked regions on a webpage that will move the app window when the
14+
user clicks and drags on them. It also opens up the system menu if right-clicked. This
15+
API is designed to provide draggable regions support for apps that are directly hosting
16+
the CoreWebView2 via visual hosting and for UI framework WebView2 controls that use visual
17+
hosting, like the WinUI2 and WinUI3 WebView2 control.If your app uses windowed hosting,
18+
draggable regions are supported by default, no need to follow this guide.
19+
20+
NOTE: This API is not currently supported for environments that use a CoreWindow like UWP.
21+
22+
#### Win32 C++
23+
```cpp
24+
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
25+
{
26+
....
27+
// We need to forward relevant mouse messages to the webview
28+
29+
// handle the range of mouse messages & WM_NCRBUTTONDOWN
30+
if (message == WM_NCRBUTTONDOWN || message == WM_NCRBUTTONUP)
31+
{
32+
POINT point {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
33+
// WM_NCRBUTTONDOWN/UP have poitns in screen coordinates we need
34+
// to convert the point to client coordinates to match the others
35+
ScreenToClient(hwnd, &point);
36+
37+
// IsPointInWebview is an example of a function which your app can implement that checks if
38+
// the mouse message point lies inside the rect of the visual that hosts the WebView2
39+
if (IsPointInWebView(point))
40+
{
41+
// Adjust the point from app client coordinates to webview client coordinates.
42+
// m_webViewBounds is a rect that represents the bounds that you app has set for
43+
// the webveiw
44+
point.x -= m_webViewBounds.left;
45+
point.y -= m_webViewBounds.top;
46+
47+
// forward mouse messages to WebView2
48+
CHECK_FAILURE(m_compositionController->SendMouseInput(
49+
static_cast<COREWEBVIEW2_MOUSE_EVENT_KIND>(message),
50+
static_cast<COREWEBVIEW2_MOUSE_EVENT_VIRTUAL_KEYS>(GET_KEYSTATE_WPARAM(wParam)),
51+
0, point));
52+
return 0;
53+
}
54+
55+
}
56+
57+
switch(message)
58+
{
59+
....
60+
// Handling this message is important for enabling dragging
61+
case WM_NCHITTEST:
62+
POINT point {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
63+
COREWEBVIEW2_NON_CLIENT_REGION_KIND result = COREWEBVIEW2_NON_CLIENT_REGION_KIND_NOWHERE;
64+
CHECK_FAILURE(m_compositionController->GetNonClientRegionAtPoint(&point, &result));
65+
return result;
66+
....
67+
}
68+
....
69+
}
70+
```
71+
#### C#
72+
```c#
73+
protected override void WndProc(ref Message m)
74+
{
75+
....
76+
77+
if (m.Msg == WM_NCHITTEST)
78+
{
79+
Point point = new Point(GET_X_LPARAM(m.lParam), GET_Y_LPARAM(m.lParam));
80+
if (m_compositionController.GetNonClientRegionAtPoint(point) ==
81+
CoreWebView2NonClientRegionKind.Caption)
82+
{
83+
m.Result = (IntPtr)HTCAPTION;
84+
}
85+
}
86+
else if (m.msg == WM_NCRBUTTONDOWN || m.msg == WM_NCRBUTTONUP)
87+
{
88+
89+
Point point = new Point(GET_X_LPARAM(m.lParam), GET_Y_LPARAM(m.lParam));
90+
91+
// WM_NCRBUTTONDOWN/UP have poitns in screen coordinates we need
92+
// to convert the point to client coordinates to match the others
93+
ScreenToClient(m.hwnd, ref point);
94+
95+
96+
// IsPointInWebview is an example of a function which your app can implement that
97+
// checks if the mouse message point lies inside the rect of the visual that hosts
98+
// the WebView.
99+
if (IsPointInWebView(point))
100+
{
101+
// Adjust the point from app client coordinates to webview client coordinates.
102+
// m_webViewBounds is a rect that represents the bounds that you app has set for
103+
// the webveiw
104+
point.x -= m_webViewBounds.left;
105+
point.y -= m_webViewBounds.top;
106+
107+
// forward mouse messages to WebView2
108+
m_compositionController.SendMouseInput(
109+
(CoreWebView2MouseEventKind)m.msg,
110+
(CoreWebView2MouseEventVirtualKeys)GET_KEYSTATE_WPARAM(wParam),
111+
0,
112+
point);
113+
114+
m.Result = 0;
115+
return;
116+
}
117+
118+
}
119+
// The following code forwards mouse messages to the WebView2 - Start
120+
....
121+
}
122+
```
123+
#### C#/WinRT
124+
125+
```c#
126+
void DraggableRegionSample() {
127+
128+
m_compositionController.NonClientRegionChanged += (object sender, CoreWebView2NonClientRegionChangedEventArgs arg) => {
129+
CoreWebView2NonClientRegionKind kind = arg.Region;
130+
IVector<Windows.Foundation.Rect> region_rects = m_compositionController.QueryNonClientRegion(kind);
131+
// use kind & region_rects to ConfigureRegion on the InputNonClientPointerSource
132+
}
133+
134+
}
135+
```
136+
# API Notes
137+
## Marking regions In Your App
138+
Draggable regions in WebView2 are HTML elements marked with the CSS style
139+
`-webkit-app-region: drag`. WebView2 will treat any element with this style as a
140+
drag handle for the window.
141+
142+
See Documentation [here](https://learn.microsoft.com/en-us/microsoft-edge/progressive-web-apps-chromium/how-to/window-controls-overlay#make-regions-of-your-app-drag-handlers-for-the-window)
143+
144+
# API Details
145+
## Win32 C++
146+
```cpp
147+
/// This enum contains values representing possible regions a given
148+
/// point lies within
149+
typedef enum COREWEBVIEW2_NON_CLIENT_REGION_KIND {
150+
/// A hit test region in the WebView2 which has the CSS style
151+
/// `-webkit-app-region: drag` set. Web content should use this CSS
152+
/// style to identify regions that should be treated like the app
153+
/// window's title bar. This has the same value as the Win32 HTCAPTION
154+
/// constant.
155+
COREWEBVIEW2_NON_CLIENT_REGION_KIND_CAPTION = 2,
156+
/// A hit test region in the WebView2 which does not have the CSS style
157+
/// `-webkit-app-region: drag` set. This is normal web content that should not be
158+
/// considered part of the app window's title bar. This has the same value
159+
/// as the Win32 HTCLIENT constant.
160+
COREWEBVIEW2_NON_CLIENT_REGION_KIND_CLIENT = 1,
161+
/// A hit test region out of bounds of the WebView2.
162+
/// This has the same value as the Win32 HTNOWHERE
163+
COREWEBVIEW2_NON_CLIENT_REGION_KIND_NOWHERE = 0
164+
} COREWEBVIEW2_NON_CLIENT_REGION_KIND;
165+
166+
[uuid(0BEA1283-39DC-48D1-9893-16275505CBBC), object, pointer_default(unique)]
167+
interface ICoreWebView2NonClientRegionChangedEventHandler : IUnknown {
168+
/// Called to provide the implementer with the event args for the
169+
/// corresponding event.
170+
HRESULT Invoke(
171+
[in] ICoreWebView2StagingCompositionController* sender,
172+
[in] ICoreWebView2NonClientRegionChangedEventArgs* args);
173+
}
174+
175+
/// Interface for status bar text change event args
176+
[uuid(E404B00C-669E-43EC-BE43-FB8CD7DBB480), object, pointer_default(unique)]
177+
interface ICoreWebView2NonClientRegionChangedEventArgs : IUnknown {
178+
[propget] HRESULT Region(
179+
[out, retval] COREWEBVIEW2_NON_CLIENT_REGION_KIND* value);
180+
}
181+
// Interface that Represents a Collection of Regoin Rects
182+
[uuid(A990DA9D-4243-4FCD-B895-2E7E87EAAB14), object, pointer_default(unique)]
183+
interface ICoreWebView2RegionRectCollection : IUnknown {
184+
/// Gets the number of `RegionRect` objects contained in the `RegionRectCollection`.
185+
[propget] HRESULT Count([out, retval] UINT32* value);
186+
187+
/// Gets the `RegionRect` at the specified index.
188+
HRESULT GetValueAtIndex([in] UINT32 index,
189+
[out, retval] RECT* value);
190+
191+
}
192+
193+
/// This interface includes the new API for enabling WebView2 support for hit-testing regions
194+
[uuid(42BF7BA5-917A-4C27-ADA1-EA6969854C16), object, pointer_default(unique)]
195+
interface ICoreWebView2CompositionController4 : ICoreWebView2CompositionController3 {
196+
/// If you are hosting a WebView2 using CoreWebView2CompositionController, you can call
197+
/// this method in your Win32 WndProc to determine if the mouse is moving over or
198+
/// clicking on WebView2 web content that should be considered part of the app window's
199+
/// title bar.
200+
201+
/// The point parameter is expected to be in the client coordinate space of WebView2.
202+
/// The method sets the out parameter value as follows:
203+
/// - COREWEBVIEW2_NON_CLIENT_REGION_KIND_CAPTION when point corresponds to
204+
/// a region (HTML element) within the WebView2 with
205+
/// `-webkit-app-region: drag` CSS style set
206+
/// - COREWEBVIEW2_NON_CLIENT_REGION_KIND_CLIENT when point corresponds to
207+
/// a region (HTML element) within the WebView2 without
208+
/// `-webkit-app-region: drag` CSS style set
209+
/// - COREWEBVIEW2_NON_CLIENT_REGION_KIND_NOWHERE when point is not within the WebView2
210+
HRESULT GetNonClientRegionAtPoint(
211+
[in] POINT point,
212+
[out, retval] COREWEBVIEW2_NON_CLIENT_REGION_KIND* value);
213+
214+
HRESULT QueryNonClientRegion(
215+
[in] COREWEBVIEW2_NON_CLIENT_REGION_KIND kind,
216+
[out, retval] ICoreWebView2RegionRectCollection** rects);
217+
218+
/// Use to add a listener to be notified when NonClientRegion change
219+
HRESULT add_NonClientRegionChanged(
220+
[in] ICoreWebView2NonClientRegionChangedEventHandler* eventHandler,
221+
[out] EventRegistrationToken* token);
222+
223+
/// Removing an event handler for `NonClientRegionChanged` event
224+
HRESULT remove_NonClientRegionChanged(
225+
[in] EventRegistrationToken token);
226+
}
227+
228+
/// Mouse event type used by SendMouseInput to convey the type of mouse event
229+
/// being sent to WebView. The values of this enum align with the matching
230+
/// WM_* window messages.
231+
typedef enum COREWEBVIEW2_MOUSE_EVENT_KIND {
232+
....
233+
/// Mouse Right Button Down event over a nonclient area, WM_NCRBUTTONDOWN.
234+
COREWEBVIEW2_MOUSE_EVENT_KIND_NON_CLIENT_RIGHT_BUTTON_DOWN = 0x00A4,
235+
/// Mouse Right Button up event over a nonclient area, WM_NCRBUTTONUP.
236+
COREWEBVIEW2_MOUSE_EVENT_KIND_NON_CLIENT_RIGHT_BUTTON_UP = 0x00A5,
237+
....
238+
} COREWEBVIEW2_MOUSE_EVENT_KIND;
239+
```
240+
## .Net/ WinRT
241+
```c#
242+
namespace Microsoft.Web.WebView2.Core {
243+
enum CoreWebView2NonClientRegionKind
244+
{
245+
Caption = 2,
246+
Client = 1,
247+
Nowhere = 0,
248+
}
249+
enum CoreWebView2MouseEventKind
250+
{
251+
...
252+
NonClientRightButtonDown = 0x00A4,
253+
NonClientRightButtonUp = 0x00A5,
254+
...
255+
}
256+
runtimeclass CoreWebView2NonClientRegionChangedEventArgs
257+
{
258+
// ICoreWebView2NonClientRegionChangedEventArgs members
259+
CoreWebView2NonClientRegionKind Region { get; };
260+
261+
}
262+
runtimeclass CoreWebView2CompositionController {
263+
264+
[interface_name("Microsoft.Web.WebView2.Core.ICoreWebView2CompositionController4")]
265+
{
266+
/// ICoreWebView2CompositionController4 members
267+
event Windows.Foundation.TypedEventHandler<CoreWebView2CompositionController, CoreWebView2NonClientRegionChangedEventArgs> NonClientRegionChanged;
268+
CoreWebView2NonClientRegionKind GetNonClientRegionAtPoint(Windows.Foundation.Point point);
269+
IVector<Windows.Foundation.Rect> QueryNonClientRegion(CoreWebView2NonClientRegionKind Kind);
270+
}
271+
}
272+
}
273+
```

0 commit comments

Comments
 (0)