1# Keyboard and Mouse Event 2 3 4Keyboard and mouse events refer to the input events of external keyboards and mouse devices. 5 6 7## Mouse Event 8 9The supported mouse events include the events triggered by external mouse devices and touchpads. 10 11Mouse events can trigger the following callbacks. 12 13| Name | Description | 14| ---------------------------------------- | ---------------------------------------- | 15| onHover(event: (isHover: boolean) => void) | Triggered when the mouse pointer enters or leaves the component.<br>**isHover**: whether the mouse pointer hovers over the component. The value **true** means that the mouse pointer enters the component, and the value **false** means that the mouse pointer leaves the component.| 16| onMouse(event: (event?: MouseEvent) => void) | Triggered when the component is clicked by a mouse button or the mouse pointer moves over the component. The **event** parameter indicates the timestamp, mouse button, action, coordinates of the clicked point on the entire screen, and coordinates of the clicked point relative to the component when the event is triggered.| 17 18When the component is bound to the **onHover** callback, you can use the [hoverEffect](../reference/apis-arkui/arkui-ts/ts-universal-attributes-hover-effect.md#hovereffect) attribute to set the hover effect of the component in hover state. 19 20 21 **Figure 1** Mouse event data flow 22 23 24 25 26 27After a mouse event is passed to ArkUI, it is processed as follows: 28 29 30- If the event is a left-click (pressing, releasing, or moving): The mouse event is first converted into a touch event in the same position. Then the touch event undergoes hit testing, gesture recognition, and callback responses. Finally the mouse event undergoes its own hit testing and callback responses. 31 32- If the event is not a left-click: The event is only used for mouse-specific hit testing and callback responses. 33 34 35>**NOTE** 36> 37>All single-finger touch events and gesture events may be triggered and responded to using the left-click. For example, to implement page redirection invoked by clicking a button with support for finger touches and left-clicks, you just need to bind an **onClick** event. If you want to implement different effects for the finger touch and the left-click, you can use the **source** parameter in the **onClick** callback to determine whether the current event is triggered by a finger or a mouse device. 38 39 40### onHover 41 42 43```ts 44onHover(event: (isHover: boolean) => void) 45``` 46 47 48Triggered when the mouse pointer enters or leaves the component. The **isHover** parameter indicates whether the mouse pointer hovers over the component. This event does not support custom bubbling settings. By default, event bubbling occurs between parent and child components. 49 50 51If this API is bound to a component, it is triggered when the mouse pointer enters the component from outside and the value of **isHover** is **true**, or when the mouse pointer leaves the component and the value of **isHover** is **false**. 52 53 54>**NOTE** 55> 56>Event bubbling is an event propagation in the document object model (DOM) when an event is first handled by an element and then passed to its parent element for further processing. 57 58 59 60 61```ts 62// xxx.ets 63@Entry 64@Component 65struct MouseExample { 66 @State hoverText: string = 'Not Hover'; 67 @State Color: Color = Color.Gray; 68 69 build() { 70 Column() { 71 Button(this.hoverText) 72 .width(200).height(100) 73 .backgroundColor(this.Color) 74 .onHover((isHover?: boolean) => { // Listen for whether the mouse cursor is hovered over the button. 75 if (isHover) { 76 this.hoverText = 'Hovered!'; 77 this.Color = Color.Green; 78 } 79 else { 80 this.hoverText = 'Not Hover'; 81 this.Color = Color.Gray; 82 } 83 }) 84 }.width('100%').height('100%').justifyContent(FlexAlign.Center) 85 } 86} 87``` 88 89 90In this example, a **Button** component is created, with an initial gray background color and the content **Not Hover**. The component is bound to the **onHover** callback. In the callback, **this.isHovered** is set to the callback parameter **isHover**. 91 92 93When the mouse pointer moves from outside the **Button** component to inside, the callback is invoked, setting the value of **isHover** to **true**. As a result, the background color of the component changes to **Color.Green**, and the content is updated to **Hovered!**. 94 95 96When the mouse pointer moves from inside the **Button** component to outside, the callback is invoked again, setting the value of **isHover** to **false**. The component then reverts to its initial style. 97 98 99 100 101 102### onMouse 103 104 105```ts 106onMouse(event: (event?: MouseEvent) => void) 107``` 108 109 110Triggered when a mouse event occurs. It is triggered each time a mouse pointer action (**MouseAction**) is detected in the component. The parameter is a [MouseEvent](../reference/apis-arkui/arkui-ts/ts-universal-mouse-key.md#mouseevent) object, which indicates the mouse event that triggers the callback. This event supports custom bubbling settings. By default, event bubbling occurs between parent and child components. It is commonly used for custom mouse behavior logic. 111 112 113You can use the **MouseEvent** object in the callback to obtain information about the triggered event, including the coordinates (**displayX**, **displayY**, **windowX**, **windowY**, **x**, and **y**), button ([MouseButton](../reference/apis-arkui/arkui-ts/ts-appendix-enums.md#mousebutton8)), action ([MouseAction](../reference/apis-arkui/arkui-ts/ts-appendix-enums.md#mouseaction8)), timestamp (**timestamp**), display area of the object that triggers the event ([EventTarget](../reference/apis-arkui/arkui-ts/ts-universal-events-click.md#eventtarget8)), and event source ([SourceType](../reference/apis-arkui/arkui-ts/ts-gesture-settings.md#sourcetype)). The **stopPropagation** callback of **MouseEvent** can be used to prevent the event from bubbling up. 114 115 116>**NOTE** 117> 118>**MouseButton** indicates the physical mouse button being pressed or released that triggers the mouse event. The values are **Left**, **Right**, **Middle**, **Back**, **Forward**, and **None**. **None** indicates that no button is pressed or released, which means that the event is triggered by the mouse pointer moving on the component. 119 120 121 122```ts 123// xxx.ets 124@Entry 125@Component 126struct MouseExample { 127 @State buttonText: string = ''; 128 @State columnText: string = ''; 129 @State hoverText: string = 'Not Hover'; 130 @State Color: Color = Color.Gray; 131 132 build() { 133 Column() { 134 Button(this.hoverText) 135 .width(200) 136 .height(100) 137 .backgroundColor(this.Color) 138 .onHover((isHover?: boolean) => { 139 if (isHover) { 140 this.hoverText = 'Hovered!'; 141 this.Color = Color.Green; 142 } 143 else { 144 this.hoverText = 'Not Hover'; 145 this.Color = Color.Gray; 146 } 147 }) 148 .onMouse((event?: MouseEvent) => { // Set the onMouse callback for the button. 149 if (event) { 150 this.buttonText = 'Button onMouse:\n' + '' + 151 'button = ' + event.button + '\n' + 152 'action = ' + event.action + '\n' + 153 'x,y = (' + event.x + ',' + event.y + ')' + '\n' + 154 'windowXY=(' + event.windowX + ',' + event.windowY + ')'; 155 } 156 }) 157 Divider() 158 Text(this.buttonText).fontColor(Color.Green) 159 Divider() 160 Text(this.columnText).fontColor(Color.Red) 161 } 162 .width('100%') 163 .height('100%') 164 .justifyContent(FlexAlign.Center) 165 .borderWidth(2) 166 .borderColor(Color.Red) 167 .onMouse((event?: MouseEvent) => { // Set the onMouse callback for the column. 168 if (event) { 169 this.columnText = 'Column onMouse:\n' + '' + 170 'button = ' + event.button + '\n' + 171 'action = ' + event.action + '\n' + 172 'x,y = (' + event.x + ',' + event.y + ')' + '\n' + 173 'windowXY=(' + event.windowX + ',' + event.windowY + ')'; 174 } 175 }) 176 } 177} 178``` 179 180 181Bind the **onMouse** API to the **Button** component based on the **onHover** example, and display the values of the callback parameters, such as **button** and **action**. Apply the same settings to the outer **Column** container. The entire process can be divided into the following two actions: 182 183 1841. Moving the mouse pointer: When the mouse pointer moves from outside the **Button** component to inside, only the **onMouse** callback of the **Column** component is triggered. When the mouse pointer enters the button, as the **onMouse** event bubbles up by default, both the **onMouse** callbacks of the **Column** and **Button** components are invoked. Because no mouse button is clicked during this process, the displayed information shows **button** as **0** (enumerated value of **MouseButton.None**) and **action** as **3** (enumerated value of **MouseAction.Move**). 185 1862. Clicking the mouse button: After the mouse pointer enters the **Button** component, clicking the component twice (left-click and right-click) produces the following results: 187 Left-click: button = 1 (enumerated value of **MouseButton.Left**); action = 1 (enumerated value of **MouseAction.Press**); action = 2 (enumerated value of **MouseAction.Release**). 188 189 Right-click: button = 2 (enumerated value of **MouseButton.Right**); action = 1 (enumerated value of **MouseAction.Press**); action = 2 (enumerated value of **MouseAction.Release**) 190 191 192 193 194 195To prevent the mouse event from bubbling, call the **stopPropagation()** API. 196 197 198 199```ts 200class ish{ 201 isHovered:boolean = false 202 set(val:boolean){ 203 this.isHovered = val; 204 } 205} 206class butf{ 207 buttonText:string = '' 208 set(val:string){ 209 this.buttonText = val 210 } 211} 212@Entry 213@Component 214struct MouseExample { 215 @State isHovered:ish = new ish() 216 build(){ 217 Column(){ 218 Button(this.isHovered ? 'Hovered!' : 'Not Hover') 219 .width(200) 220 .height(100) 221 .backgroundColor(this.isHovered ? Color.Green : Color.Gray) 222 .onHover((isHover?: boolean) => { 223 if(isHover) { 224 let ishset = new ish() 225 ishset.set(isHover) 226 } 227 }) 228 .onMouse((event?: MouseEvent) => { 229 if (event) { 230 if (event.stopPropagation) { 231 event.stopPropagation(); // Prevent the onMouse event from bubbling. 232 } 233 let butset = new butf() 234 butset.set('Button onMouse:\n' + '' + 235 'button = ' + event.button + '\n' + 236 'action = ' + event.action + '\n' + 237 'x,y = (' + event.x + ',' + event.y + ')' + '\n' + 238 'windowXY=(' + event.windowX + ',' + event.windowY + ')'); 239 } 240 }) 241 } 242 } 243} 244``` 245 246 247To prevent the mouse event of the child component (**Button**) from bubbling up to its parent component (**Column**), use the **event** parameter in the **onMouse** callback of **Button** to call the **stopPropagation** API. 248 249 250 251```ts 252event.stopPropagation() 253``` 254 255 256With bubbling prevented, the mouse event on the **Button** component will trigger the **onMouse** callback of the **Button** component, but not the **onMouse** callback of the **Column** component. 257 258 259### hoverEffect 260 261 262```ts 263hoverEffect(value: HoverEffect) 264``` 265 266 267Sets the hover effect of the component in hover state. The parameter value type is **HoverEffect**. The **Auto**, **Scale**, and **Highlight** effects are preset and do not support customization. 268 269 270 **Table 1** HoverEffect 271 272| Value| Description | 273| -------------- | ---------------------------------------- | 274| Auto | Default hover effect, which varies by component. | 275| Scale | Scale effect. When the mouse pointer is placed over the component, the component is scaled up from 100% to 105%. When the mouse pointer is moved away, the component is scaled down from 105% to 100%.| 276| Highlight | Background fade-in and fade-out effect. When the mouse pointer is placed over the component, a white layer with 5% opacity is applied to the background color of the component, resulting in a dimmed background. When the mouse pointer is moved away, the background color of the component is restored to the original style.| 277| None | No effect. | 278 279 280 281```ts 282// xxx.ets 283@Entry 284@Component 285struct HoverExample { 286 build() { 287 Column({ space: 10 }) { 288 Button('Auto') 289 .width(170).height(70) 290 Button('Scale') 291 .width(170).height(70) 292 .hoverEffect(HoverEffect.Scale) 293 Button('Highlight') 294 .width(170).height(70) 295 .hoverEffect(HoverEffect.Highlight) 296 Button('None') 297 .width(170).height(70) 298 .hoverEffect(HoverEffect.None) 299 }.width('100%').height('100%').justifyContent(FlexAlign.Center) 300 } 301} 302``` 303 304 305 306 307 308The default hover effect for the button is the **Highlight** effect. Therefore, the effects of **Auto** and **Highlight** are the same. The **Highlight** effect darkens the background color, **Scale** causes the component to scale, and **None** disables the hover effect. 309 310 311## Key Event 312 313### Key Event Data Flow 314 315 316 317 318After being triggered by a device such as a peripheral keyboard, a key event has its data processed and converted by the driver and multimodal input modules, and then is sent to the currently focused window. The window dispatches the received event, following the sequence below. The dispatch stops once the event is consumed. 319 3201. The window first dispatches the event to the ArkUI framework for invoking the **onKeyPreIme** callback bound to the component in focus as well as the page keyboard shortcuts. 3212. If the ArkUI framework does not consume the event, the window dispatches the event to the input method for key input. 3223. If the input method does not consume the event, the window dispatches the event to the ArkUI framework again for responding to the system default key event (for example, focus navigation) and for invoking the **onKeyEvent** callback bound to the component in focus. 323 324When a text box has focus and the input method is enabled, most key events are consumed by the input method. For example, a letter key is used by the input method to enter a letter in the text box, and an arrow key is used by the input method to switch to the desired candidate word. Yet, if a keyboard shortcut is bound to the text box, the shortcut responds to the event first, and the event will not be consumed by the input method. 325 326After the key event is sent to the ArkUI framework, it first identifies the complete focus chain, and then sends the key event from one node to the next, following the leaf-to-root path. 327 328The key event process for the **Web** component differs from the aforementioned process. The **Web** component does not match keyboard shortcuts when **onKeyPreIme** returns **false**. The unconsumed key event will be re-dispatched back to ArkUI through **ReDispatch** during the third key press dispatch. It is within this **ReDispatch** that operations such as matching keyboard shortcuts are performed again. 329 330### onKeyEvent & onKeyPreIme 331 332 333```ts 334onKeyEvent(event: (event: KeyEvent) => void): T 335onKeyEvent(event: Callback<KeyEvent, boolean>): T 336onKeyPreIme(event: Callback<KeyEvent, boolean>): T 337onKeyEventDispatch(event: Callback<KeyEvent, boolean>): T 338``` 339 340 341The difference between the preceding two methods lies only in the triggering time. For details, see [Key Event Data Flow](#key-event-data-flow). The return value of **onKeyPreIme** determines whether the key event will be dispatched for the page keyboard shortcut, input method, and **onKeyEvent**. 342 343 344The methods are triggered when the bound component has focus and a key event occurs on the component. The callback parameter [KeyEvent](../reference/apis-arkui/arkui-ts/ts-universal-events-key.md#keyevent) can be used to obtain the information about the key event, including [KeyType](../reference/apis-arkui/arkui-ts/ts-appendix-enums.md#keytype), [keyCode](../reference/apis-input-kit/js-apis-keycode.md#keycode), **keyText**, [KeySource](../reference/apis-arkui/arkui-ts/ts-appendix-enums.md#keysource), **deviceId**, **metaKey**, **timestamp**, and **stopPropagation**. 345 346 347 348```ts 349// xxx.ets 350@Entry 351@Component 352struct KeyEventExample { 353 @State buttonText: string = ''; 354 @State buttonType: string = ''; 355 @State columnText: string = ''; 356 @State columnType: string = ''; 357 358 build() { 359 Column() { 360 Button('onKeyEvent') 361 .defaultFocus(true) 362 .width(140).height(70) 363 .onKeyEvent((event?: KeyEvent) => { // Set the onKeyEvent event for the button. 364 if(event){ 365 if (event.type === KeyType.Down) { 366 this.buttonType = 'Down'; 367 } 368 if (event.type === KeyType.Up) { 369 this.buttonType = 'Up'; 370 } 371 this.buttonText = 'Button: \n' + 372 'KeyType:' + this.buttonType + '\n' + 373 'KeyCode:' + event.keyCode + '\n' + 374 'KeyText:' + event.keyText; 375 } 376 }) 377 378 Divider() 379 Text(this.buttonText).fontColor(Color.Green) 380 381 Divider() 382 Text(this.columnText).fontColor(Color.Red) 383 }.width('100%').height('100%').justifyContent(FlexAlign.Center) 384 .onKeyEvent((event?: KeyEvent) => { // Set the onKeyEvent event for the parent container Column. 385 if(event){ 386 if (event.type === KeyType.Down) { 387 this.columnType = 'Down'; 388 } 389 if (event.type === KeyType.Up) { 390 this.columnType = 'Up'; 391 } 392 this.columnText = 'Column: \n' + 393 'KeyType:' + this.buttonType + '\n' + 394 'KeyCode:' + event.keyCode + '\n' + 395 'KeyText:' + event.keyText; 396 } 397 }) 398 } 399} 400``` 401 402 403In the preceding example, **onKeyEvent** is bound to the **Button** component and its parent container **Column**. After the application opens and loads a page, the first focusable non-container component in the component tree automatically obtains focus. Set the **Button** component as the default focus of the current page. Because the **Button** component is a child node of the **Column** component, the **Column** component also obtains focus. For details about the focus obtaining mechanism, see [Focus Event](arkts-common-events-focus-event.md). 404 405 406 407 408 409After the application is opened, press the following keys in sequence: Space, Enter, Left Ctrl, Left Shift, Letter A, and Letter Z. 410 411 4121. Because the **onKeyEvent** event bubbles by default, the **onKeyEvent** callbacks of both **Button** and **Column** are invoked. 413 4142. Each key has two callbacks, which correspond to **KeyType.Down** and **KeyType.Up** respectively, indicating that the key is pressed and then lifted. 415 416 417To prevent the key event of the **Button** component from bubbling up to its parent container **Column**, add the **event.stopPropagation()** API to the **onKeyEvent** callback of **Button**. 418 419 420 421```ts 422@Entry 423@Component 424struct KeyEventExample { 425 @State buttonText: string = ''; 426 @State buttonType: string = ''; 427 @State columnText: string = ''; 428 @State columnType: string = ''; 429 430 build() { 431 Column() { 432 Button('onKeyEvent') 433 .defaultFocus(true) 434 .width(140).height(70) 435 .onKeyEvent((event?: KeyEvent) => { 436 // Use stopPropagation to prevent the key event from bubbling up. 437 if(event){ 438 if(event.stopPropagation){ 439 event.stopPropagation(); 440 } 441 if (event.type === KeyType.Down) { 442 this.buttonType = 'Down'; 443 } 444 if (event.type === KeyType.Up) { 445 this.buttonType = 'Up'; 446 } 447 this.buttonText = 'Button: \n' + 448 'KeyType:' + this.buttonType + '\n' + 449 'KeyCode:' + event.keyCode + '\n' + 450 'KeyText:' + event.keyText; 451 } 452 }) 453 454 Divider() 455 Text(this.buttonText).fontColor(Color.Green) 456 457 Divider() 458 Text(this.columnText).fontColor(Color.Red) 459 }.width('100%').height('100%').justifyContent(FlexAlign.Center) 460 .onKeyEvent((event?: KeyEvent) => { // Set the onKeyEvent event for the parent container Column. 461 if(event){ 462 if (event.type === KeyType.Down) { 463 this.columnType = 'Down'; 464 } 465 if (event.type === KeyType.Up) { 466 this.columnType = 'Up'; 467 } 468 this.columnText = 'Column: \n' + 469 'KeyType:' + this.buttonType + '\n' + 470 'KeyCode:' + event.keyCode + '\n' + 471 'KeyText:' + event.keyText; 472 } 473 }) 474 } 475} 476``` 477 478 479 480 481This example shows how to use **OnKeyPreIme** to block the left arrow key input in the text box. 482```ts 483import { KeyCode } from '@kit.InputKit'; 484 485@Entry 486@Component 487struct PreImeEventExample { 488 @State buttonText: string = ''; 489 @State buttonType: string = ''; 490 @State columnText: string = ''; 491 @State columnType: string = ''; 492 493 build() { 494 Column() { 495 Search({ 496 placeholder: "Search..." 497 }) 498 .width("80%") 499 .height("40vp") 500 .border({ radius:"20vp" }) 501 .onKeyPreIme((event:KeyEvent) => { 502 if (event.keyCode == KeyCode.KEYCODE_DPAD_LEFT) { 503 return true; 504 } 505 return false; 506 }) 507 } 508 } 509} 510``` 511 512This example demonstrates how to use **onKeyEventDispatch** to distribute key events to child components, which handle the events using **onKeyEvent**. 513 514```ts 515@Entry 516@Component 517struct Index { 518 build() { 519 Row() { 520 Row() { 521 Button('button1').id('button1').onKeyEvent((event) => { 522 console.log("button1"); 523 return true 524 }) 525 Button('button1').id('button2').onKeyEvent((event) => { 526 console.log("button2"); 527 return true 528 }) 529 } 530 .width('100%') 531 .height('100%') 532 .id('Row1') 533 .onKeyEventDispatch((event) => { 534 let context = this.getUIContext(); 535 context.getFocusController().requestFocus('button1'); 536 return context.dispatchKeyEvent('button1', event); 537 }) 538 539 } 540 .height('100%') 541 .width('100%') 542 .onKeyEventDispatch((event) => { 543 if (event.type == KeyType.Down) { 544 let context = this.getUIContext(); 545 context.getFocusController().requestFocus('Row1'); 546 return context.dispatchKeyEvent('Row1', event); 547 } 548 return true; 549 }) 550 } 551} 552``` 553