|
| 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: Your app may already be forwarding mouse messages to WebView2, if so the bulk |
| 21 | +of the code below is likely already in your app. One thing you need to do specifically |
| 22 | +for this feature is to add the check for w_param == HTCAPTION as a clause for capturing |
| 23 | +the mouse and forwarding messages. It is necessary for the system menu to show. If you |
| 24 | +choose not to include this you would have to handle the logic for showing the system menu |
| 25 | +yourself. |
| 26 | + |
| 27 | +NOTE: This API is not currently supported for environments that use a CoreWindow like UWP |
| 28 | + |
| 29 | +#### Win32 C++ |
| 30 | +```cpp |
| 31 | +LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) |
| 32 | +{ |
| 33 | +.... |
| 34 | +// The following code forwards mouse messages to the WebView2 - Start |
| 35 | + |
| 36 | +// handle the range of mouse messages & WM_NCRBUTTONDOWN |
| 37 | +if (message >= WM_MOUSEFIRST && message <= WM_MOUSELAST |
| 38 | + || message == WM_NCRBUTTONDOWN || message == WM_NCRBUTTONUP) |
| 39 | +{ |
| 40 | + POINT point {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)}; |
| 41 | + // IsPointInWebview is an example of a function which your app can implement that checks if |
| 42 | + // the mouse message point lies inside the rect of the visual that hosts the WebView2 |
| 43 | + if (IsPointInWebView(point) || message == WM_MOUSELEAVE) |
| 44 | + { |
| 45 | + // forward mouse messages to WebView2 |
| 46 | + CHECK_FAILURE(m_compositionController->SendMouseInput( |
| 47 | + static_cast<COREWEBVIEW2_MOUSE_EVENT_KIND>(message), |
| 48 | + static_cast<COREWEBVIEW2_MOUSE_EVENT_VIRTUAL_KEYS>(GET_KEYSTATE_WPARAM(wParam)), |
| 49 | + mouseData, point)); |
| 50 | + return 0; |
| 51 | + } |
| 52 | + |
| 53 | +} |
| 54 | +// The following code forwards mouse messages to the WebView2 - End |
| 55 | + |
| 56 | +switch(message) |
| 57 | +{ |
| 58 | + .... |
| 59 | + // Handling this message is important for enabling dragging |
| 60 | + case WM_NCHITTEST: |
| 61 | + POINT point {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)}; |
| 62 | + COREWEBVIEW2_HIT_TEST_RESULT result = NULL; |
| 63 | + CHECK_FAILURE(m_compositionController->GetHitTestResultAtPoint(&point, &result)); |
| 64 | + |
| 65 | + if (result == COREWEBVIEW2_HIT_TEST_RESULT_HTCAPTION) |
| 66 | + { |
| 67 | + return HTCAPTION; |
| 68 | + } |
| 69 | + |
| 70 | + break; |
| 71 | + .... |
| 72 | +} |
| 73 | +.... |
| 74 | +} |
| 75 | +``` |
| 76 | +#### C#/WinRT |
| 77 | +```c# |
| 78 | +protected override void WndProc(ref Message m) |
| 79 | +{ |
| 80 | +.... |
| 81 | +
|
| 82 | +if (m.Msg == WM_NCHITTEST) |
| 83 | +{ |
| 84 | + Point point = new Point(GET_X_LPARAM(m.lParam), GET_Y_LPARAM(m.lParam)); |
| 85 | + if (m_compositionController.GetHitTestResultAtPoint(point) == |
| 86 | + CoreWebView2HitTestResult.Htcaption) |
| 87 | + { |
| 88 | + m.Result = (IntPtr)HTCAPTION; |
| 89 | + } |
| 90 | +} |
| 91 | +// The following code forwards mouse messages to the WebView2 - Start |
| 92 | +else if (m.msg >= WM_MOUSEFIRST && m.msg <= WM_MOUSELAST |
| 93 | + || m.msg == WM_NCRBUTTONDOWN || m.msg == WM_NCRBUTTONUP) |
| 94 | +{ |
| 95 | + |
| 96 | + Point point = new Point(GET_X_LPARAM(m.lParam), GET_Y_LPARAM(m.lParam)); |
| 97 | + // IsPointInWebview is an example of a function which your app can implement that |
| 98 | + // checks if the mouse message point lies inside the rect of the visual that hosts |
| 99 | + // the WebView. |
| 100 | + if (IsPointInWebView(point) || m.msg == WM_MOUSELEAVE) |
| 101 | + { |
| 102 | + |
| 103 | + // forward mouse messages to WebView2 |
| 104 | + m_compositionController.SendMouseInput( |
| 105 | + (CoreWebView2MouseEventKind)message, |
| 106 | + (CoreWebView2MouseEventVirtualKeys)GET_KEYSTATE_WPARAM(wParam), |
| 107 | + mouseData, |
| 108 | + point); |
| 109 | +
|
| 110 | + m.Result = 0 |
| 111 | + return; |
| 112 | + } |
| 113 | + |
| 114 | +} |
| 115 | +// The following code forwards mouse messages to the WebView2 - Start |
| 116 | + .... |
| 117 | +} |
| 118 | +``` |
| 119 | +# API Notes |
| 120 | +## Marking regions In Your App |
| 121 | +Draggable regions in WebView2 are HTML elements marked with the CSS style |
| 122 | +`-webkit-app-region: drag`. WebView2 will treat any element with this style as a |
| 123 | +drag handle for the window. |
| 124 | + |
| 125 | +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) |
| 126 | + |
| 127 | +# API Details |
| 128 | +## Win32 C++ |
| 129 | +```cpp |
| 130 | +/// This enum contains values representing possible regions a given |
| 131 | +/// point lies within |
| 132 | +typedef enum COREWEBVIEW2_HIT_TEST_RESULT { |
| 133 | + /// A hit test region in the WebView2 which has the CSS style |
| 134 | + /// `-webkit-app-region: drag` set. Web content should use this CSS |
| 135 | + /// style to identify regions that should be treated like the app |
| 136 | + /// window's title bar. This has the same value as the Win32 HTCAPTION |
| 137 | + /// constant. |
| 138 | + COREWEBVIEW2_HIT_TEST_RESULT_CAPTION = 2, |
| 139 | + /// A hit test region in the WebView2 which does not have the CSS style |
| 140 | + /// `-webkit-app-region: drag` set. This is normal web content that should not be |
| 141 | + /// considered part of the app window's title bar. This has the same value |
| 142 | + /// as the Win32 HTCLIENT constant. |
| 143 | + COREWEBVIEW2_HIT_TEST_RESULT_CLIENT = 1, |
| 144 | + /// A hit test region out of bounds of the WebView2. |
| 145 | + /// This has the same value as the Win32 HTNOWHERE |
| 146 | + COREWEBVIEW2_HIT_TEST_RESULT_NOWHERE = 0 |
| 147 | +} COREWEBVIEW2_HIT_TEST_RESULT; |
| 148 | + |
| 149 | +/// This interface includes the new API for enabling WebView2 support for hit-testing regions |
| 150 | +[uuid(42BF7BA5-917A-4C27-ADA1-EA6969854C16), object, pointer_default(unique)] |
| 151 | +interface ICoreWebView2CompositionController4 : ICoreWebView2CompositionController3 { |
| 152 | + /// If you are hosting a WebView2 using CoreWebView2CompositionController, you can call |
| 153 | + /// this method in your Win32 WndProc to determine if the mouse is moving over or |
| 154 | + /// clicking on WebView2 web content that should be considered part of the app window's |
| 155 | + /// title bar. |
| 156 | + |
| 157 | + /// [in] Point is expected to be in the client coordinate space of WebView2. |
| 158 | + /// [out, retval] The method returns: |
| 159 | + /// - COREWEBVIEW2_HIT_TEST_RESULT_CAPTION when point corresponds to |
| 160 | + /// a region (HTML element) within the WebView2 with |
| 161 | + /// `-webkit-app-region: drag` CSS style set |
| 162 | + /// - COREWEBVIEW2_HIT_TEST_RESULT_CLIENT when point corresponds to |
| 163 | + /// a region (HTML element) within the WebView2 without |
| 164 | + /// `-webkit-app-region: drag` CSS style set |
| 165 | + /// - COREWEBVIEW2_HIT_TEST_RESULT_NOWHERE when point is not within the WebView2 |
| 166 | + |
| 167 | + HRESULT GetHitTestResultAtPoint( |
| 168 | + [in] POINT point, |
| 169 | + [out, retval] COREWEBVIEW2_HIT_TEST_RESULT* val); |
| 170 | +} |
| 171 | + |
| 172 | +/// Mouse event type used by SendMouseInput to convey the type of mouse event |
| 173 | +/// being sent to WebView. The values of this enum align with the matching |
| 174 | +/// WM_* window messages. |
| 175 | +typedef enum COREWEBVIEW2_MOUSE_EVENT_KIND { |
| 176 | + .... |
| 177 | + /// Mouse Right Button Down event over a nonclient area, WM_NCRBUTTONDOWN. |
| 178 | + COREWEBVIEW2_MOUSE_EVENT_KIND_NON_CLIENT_RIGHT_BUTTON_DOWN = 0x00A4, |
| 179 | + .... |
| 180 | + /// Mouse Right Button up event over a nonclient area, WM_NCRBUTTONUP. |
| 181 | + COREWEBVIEW2_MOUSE_EVENT_KIND_NON_CLIENT_RIGHT_BUTTON_UP = 0x00A5, |
| 182 | + .... |
| 183 | +} COREWEBVIEW2_MOUSE_EVENT_KIND; |
| 184 | +``` |
| 185 | +## .Net/ WinRT |
| 186 | +```c# |
| 187 | +namespace Microsoft.Web.WebView2.Core { |
| 188 | + enum CoreWebView2HitTestResult |
| 189 | + { |
| 190 | + Caption = 2, |
| 191 | + Client = 1, |
| 192 | + Nowhere = 0, |
| 193 | + } |
| 194 | + enum CoreWebView2MouseEventKind |
| 195 | + { |
| 196 | + ... |
| 197 | + NonClientRightButtonDown = 0x00A4, |
| 198 | + NonClientRightButtonUp = 0x00A5, |
| 199 | + ... |
| 200 | + } |
| 201 | + runtimeclass CoreWebView2CompositionController { |
| 202 | + |
| 203 | + [interface_name("Microsoft.Web.WebView2.Core.ICoreWebView2CompositionController4")] |
| 204 | + { |
| 205 | + CoreWebView2HitTestResult GetHitTestResultAtPoint(Windows.Foundation.Point point); |
| 206 | + } |
| 207 | + } |
| 208 | +} |
| 209 | +``` |
0 commit comments