1# Keyboard and Mouse Event
2
3
4Keyboard and mouse events refer to the input events of the peripheral keyboard and mouse.
5
6
7## Mouse Event
8
9The supported mouse events include the events triggered by the peripheral mouse and touchpad.
10
11Mouse events can trigger the following callbacks.
12
13| Name                                      | Description                                      |
14| ---------------------------------------- | ---------------------------------------- |
15| onHover(event: (isHover: boolean) =&gt; 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) =&gt; void) | Triggered when the component is clicked by a mouse button or the mouse pointer moves on 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![en-us_image_0000001511900504](figures/en-us_image_0000001511900504.png)
25
26
27When ArkUI receives the mouse event, it checks whether the mouse event concerns pressing, lifting, or moving of the left mouse button, and then responds accordingly.
28
29
30- Yes: The mouse event is first converted into a touch event in the same position, and a collision test, gesture judgment, and callback response of the touch event are performed. The collision test and callback response of the mouse event are then performed.
31
32- No: Only the collision test and callback response of the mouse event are performed.
33
34
35>**NOTE**
36>
37>All touch events and gesture events that can be responded to by a single finger may be triggered and responded to by using the left mouse button. For example, to implement page redirection invoked by clicking a button with support for finger touches and left-clicks, you just need to bind one click event (**onClick**). 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.
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 bubbles up to its parent element.
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) => { // Use the onHover API to 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 the initial background color of gray and the content of **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 to inside the button, the callback is invoked, the value of **isHover** changes to **true**, the value of **isHovered** changes to **true**, the background color of the component changes to **Color.Green**, and the content changes to **Hovered!**.
94
95
96When the mouse pointer moves from inside the button to outside the button, the callback is invoked, the value of **isHover** changes to **false**, and the component restores to its initial style.
97
98
99![onHover](figures/onHover.gif)
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 customized mouse behavior logic processing.
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** is used to set whether the current event blocks bubbling.
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 based on the **onHover** example. In the callback, the values of the callback parameters, such as **button** and **action**, are displayed. The same settings are performed on 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 is moved from outside the button to inside the button, only the **onMouse** callback of the **Column** is triggered. When the mouse pointer is moved to the button, as the **onMouse** event bubbles up by default, both the **onMouse** callbacks of the **Column** and **Button** components are invoked. In this process, the mouse pointer moves, but no mouse button is clicked. Therefore, in the displayed information, the value of **button** is 0 (enumerated value of **MouseButton.None**) and the value of **action** is **3** (enumerated value of **MouseAction.Move**).
185
1862. Clicking the mouse button: After the mouse pointer enters the **Button** component, the **Button** component is clicked twice, namely, left-click and right-click.
187   Left-clicked: 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![onMouse1](figures/onMouse1.gif)
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![hoverEffect](figures/hoverEffect.gif)
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![en-us_image_0000001511580944](figures/en-us_image_0000001511580944.png)
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
335onKeyPreIme(event: Callback<KeyEvent, boolean>): T
336```
337
338
339The 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**.
340
341
342The 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**.
343
344
345
346```ts
347// xxx.ets
348@Entry
349@Component
350struct KeyEventExample {
351  @State buttonText: string = '';
352  @State buttonType: string = '';
353  @State columnText: string = '';
354  @State columnType: string = '';
355
356  build() {
357    Column() {
358      Button('onKeyEvent')
359        .defaultFocus(true)
360        .width(140).height(70)
361        .onKeyEvent((event?: KeyEvent) => { // Set the onKeyEvent event for the button.
362          if(event){
363            if (event.type === KeyType.Down) {
364              this.buttonType = 'Down';
365            }
366            if (event.type === KeyType.Up) {
367              this.buttonType = 'Up';
368            }
369            this.buttonText = 'Button: \n' +
370            'KeyType:' + this.buttonType + '\n' +
371            'KeyCode:' + event.keyCode + '\n' +
372            'KeyText:' + event.keyText;
373          }
374        })
375
376      Divider()
377      Text(this.buttonText).fontColor(Color.Green)
378
379      Divider()
380      Text(this.columnText).fontColor(Color.Red)
381    }.width('100%').height('100%').justifyContent(FlexAlign.Center)
382    .onKeyEvent((event?: KeyEvent) => { // Set the onKeyEvent event for the parent container Column.
383      if(event){
384        if (event.type === KeyType.Down) {
385          this.columnType = 'Down';
386        }
387        if (event.type === KeyType.Up) {
388          this.columnType = 'Up';
389        }
390        this.columnText = 'Column: \n' +
391        'KeyType:' + this.buttonType + '\n' +
392        'KeyCode:' + event.keyCode + '\n' +
393        'KeyText:' + event.keyText;
394      }
395    })
396  }
397}
398```
399
400
401In 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).
402
403
404![en-us_image_0000001511421324](figures/en-us_image_0000001511421324.gif)
405
406
407After the application is opened, press the following keys in sequence: Space, Enter, Left Ctrl, Left Shift, Letter A, and Letter Z.
408
409
4101. Because the **onKeyEvent** event bubbles by default, the **onKeyEvent** callbacks of both **Button** and **Column** are invoked.
411
4122. Each key has two callbacks, which correspond to **KeyType.Down** and **KeyType.Up** respectively, indicating that the key is pressed and then lifted.
413
414
415To 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**.
416
417
418
419```ts
420@Entry
421@Component
422struct KeyEventExample {
423  @State buttonText: string = '';
424  @State buttonType: string = '';
425  @State columnText: string = '';
426  @State columnType: string = '';
427
428  build() {
429    Column() {
430      Button('onKeyEvent')
431        .defaultFocus(true)
432        .width(140).height(70)
433        .onKeyEvent((event?: KeyEvent) => {
434          // Use stopPropagation to prevent the key event from bubbling up.
435          if(event){
436            if(event.stopPropagation){
437              event.stopPropagation();
438            }
439            if (event.type === KeyType.Down) {
440              this.buttonType = 'Down';
441            }
442            if (event.type === KeyType.Up) {
443              this.buttonType = 'Up';
444            }
445            this.buttonText = 'Button: \n' +
446              'KeyType:' + this.buttonType + '\n' +
447              'KeyCode:' + event.keyCode + '\n' +
448              'KeyText:' + event.keyText;
449          }
450        })
451
452      Divider()
453      Text(this.buttonText).fontColor(Color.Green)
454
455      Divider()
456      Text(this.columnText).fontColor(Color.Red)
457    }.width('100%').height('100%').justifyContent(FlexAlign.Center)
458    .onKeyEvent((event?: KeyEvent) => { // Set the onKeyEvent event for the parent container Column.
459      if(event){
460        if (event.type === KeyType.Down) {
461          this.columnType = 'Down';
462        }
463        if (event.type === KeyType.Up) {
464          this.columnType = 'Up';
465        }
466        this.columnText = 'Column: \n' +
467          'KeyType:' + this.buttonType + '\n' +
468          'KeyCode:' + event.keyCode + '\n' +
469          'KeyText:' + event.keyText;
470      }
471    })
472  }
473}
474```
475
476
477![en-us_image_0000001511900508](figures/en-us_image_0000001511900508.gif)
478
479Use **OnKeyPreIme** to block the left arrow key input in the text box.
480```ts
481import { KeyCode } from '@kit.InputKit';
482
483@Entry
484@Component
485struct PreImeEventExample {
486  @State buttonText: string = '';
487  @State buttonType: string = '';
488  @State columnText: string = '';
489  @State columnType: string = '';
490
491  build() {
492    Column() {
493      Search({
494        placeholder: "Search..."
495      })
496        .width("80%")
497        .height("40vp")
498        .border({ radius:"20vp" })
499        .onKeyPreIme((event:KeyEvent) => {
500          if (event.keyCode == KeyCode.KEYCODE_DPAD_LEFT) {
501            return true;
502          }
503          return false;
504        })
505    }
506  }
507}
508```
509