|
| 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