Keystroke Handling"

From Documentation
m
 
(36 intermediate revisions by 9 users not shown)
Line 7: Line 7:
 
=ENTER and ESC=
 
=ENTER and ESC=
  
To handle ENTER, you could listen to the onOK event (notice O and K are both in upper case). To handle ESC, you could listen to the onCancel event. For example,
+
To handle ENTER key pressing, you can listen to the event:
 +
* '''onOK''' (notice O and K are both in upper case).  
  
<source lang="xml">
+
To handle ESC key pressing, you can listen to the event:
<grid id="form" apply="foo.Login">
+
* '''onCancel'''
    <rows>
+
 
        <row>Username: <textbox id="username"/></row>
+
For example:
        <row>Password: <textbox id="password" type="password"/></row>
+
 
        <row><button label="Login" forward="form.onOK"/><button label="Reset" forward="form.onCancel"/></row>
+
<source lang="xml" highlight='10, 11'>
    </rows>
+
    <grid id="form" apply="org.zkoss.reference.developer.uipattern.KeystrokeComposer">
</grid>
+
        <rows>
 +
            <row>Username:
 +
                <textbox id="username"/>
 +
            </row>
 +
            <row>Password:
 +
                <textbox id="password" type="password"/>
 +
            </row>
 +
            <row>
 +
                <button label="Login" forward="form.onOK"/>
 +
                <button label="Reset" forward="form.onCancel"/>
 +
            </row>
 +
        </rows>
 +
    </grid>
 
</source>
 
</source>
  
 
Then, you could implement [[ZK Developer's Reference/MVC/Controller/Composer|a composer]] as follows.
 
Then, you could implement [[ZK Developer's Reference/MVC/Controller/Composer|a composer]] as follows.
  
<source lang="java">
+
 
package foo;
+
<source lang="java" highlight='15, 21'>
import org.zkoss.zul.*;
+
package org.zkoss.reference.developer.uipattern;
public class Login extends org.zkoss.zk.ui.util.GenericForwardComposer {
+
 
     Textbox username;
+
import org.zkoss.zk.ui.Component;
     Textbox password;
+
import org.zkoss.zk.ui.select.SelectorComposer;
 +
import org.zkoss.zk.ui.select.annotation.*;
 +
import org.zkoss.zul.Textbox;
 +
 
 +
public class KeystrokeComposer extends SelectorComposer<Component> {
 +
 
 +
    @Wire
 +
     private Textbox username;
 +
     @Wire
 +
    private Textbox password;
 +
 
 +
    @Listen("onOK = #form")
 
     public void onOK() {
 
     public void onOK() {
 
         //handle login
 
         //handle login
 +
        System.out.println("ok");
 
     }
 
     }
 +
 +
    @Listen("onCancel = #form")
 
     public void onCancel() {
 
     public void onCancel() {
 
         username.setValue("");
 
         username.setValue("");
Line 37: Line 64:
 
</source>
 
</source>
  
Notice that the <tt>onOK</tt> and <tt>onCancel</tt> events are sent to the nearest ancestor of the component that has the focus. In other words, if you press ENTER in a textbox, then ZK will look up the textbox, its parent, its parent's parent and so on to see if any of them has been registered a listener for <tt>onOK</tt>. If found, the event is sent to it. If not found, nothing happens.
 
  
Also notice that, if a button gains the focus, ENTER will be intercepted by the browser and interpreted as pressed. For example, if you move the focus to the Reset button and press ENTER, you will receive <tt>onCancel</tt> rather than <tt>onOK</tt> (since <tt>onClick</tt> will be fired and it is converted to <tt>onCancel</tt> because of [[ZUML Reference/ZUML/Attributes/forward|the forward attribute]] specified).
+
Notice that the <code>onOK</code> and <code>onCancel</code> events are sent to the nearest ancestor of the component that has the focus. In other words, if you press ENTER in a textbox, then ZK will look up the textbox, its parent, its parent's parent and so on to see if any of them has been registered as a listener for <code>onOK</code>. If found, the event is sent to it. If not found, nothing will happen.
 +
 
 +
Also notice that, if a button gains the focus, ENTER will be intercepted by the browser and interpreted as pressed. For example, if you move the focus to the Reset button and press ENTER, you will receive <code>onCancel</code> rather than <code>onOK</code> (since <code>onClick</code> will be fired and it is converted to <code>onCancel</code> because of [[ZUML Reference/ZUML/Attributes/forward|the forward attribute]] specified).
 +
 
 
=Control Keys=
 
=Control Keys=
  
To handle the control keys, you have to specify the keystrokes you want to handle with <javadoc method="setCtrlKeys(java.lang.String)">org.zkoss.zul.impl.XulElement</javadoc>. Then, if any child component gains the focus and the user presses a keystroke matches the combination, then the <tt>onCtrlKey</tt> will be sent to the component with an instance of <javadoc>org.zkoss.zk.ui.event.KeyEvent</javadoc>.
+
To handle the control keys, you have to specify the keystrokes you want to handle with <javadoc method="setCtrlKeys(java.lang.String)">org.zkoss.zul.impl.XulElement</javadoc>. Then, if any child component gains the focus and the user presses a keystroke that matches the combination, the <code>onCtrlKey</code> will be sent to the component with an instance of <javadoc>org.zkoss.zk.ui.event.KeyEvent</javadoc>.
  
Like ENTER and ESC, you could specify the listener and the <tt>ctrlKeys</tt> property in one of the ancestor. ZK will search the component having the focus, its parent, its parent's parent and so on to find if any of them specifies the <tt>ctrlKeys</tt> property that matches the keystroke.
+
Like ENTER and ESC, you could specify the listener and the <code>ctrlKeys</code> property in one of the ancestors. ZK will search the component having the focus, its parent, its parent's parent and so on to find if any of them specifies the <code>ctrlKeys</code> property that matches the keystroke.
  
 
For example,
 
For example,
  
 
<source lang="xml">
 
<source lang="xml">
<vlayout ctrlKeys="@c^a#f10^#f3" onCtrlKey="doSomething(event.getKeyCode())">
+
<vbox ctrlKeys="@c^a#f10^#f3" onCtrlKey="doSomething(event.getKeyCode())">
 
     <textbox/>
 
     <textbox/>
 
     <datebox/>
 
     <datebox/>
</vlayout>
+
</vbox>
 
</source>
 
</source>
  
Line 59: Line 88:
 
== Allowed Control Keys ==
 
== Allowed Control Keys ==
  
{| border="1" | width="100%"
+
{| class="wikitable" | width="100%"
| <center>'''Key'''</center>
+
!  <center>Key</center>
| <center>'''Description'''</center>
+
! <center>Syntax</center>
 +
! <center>Description</center>
 +
 
 +
|-
 +
| Control
 +
| <center>^[?]</center>
 +
| <code>[?]</code> can be '''a~z, 0~9, #[?]''',
 +
e.g. <code>^k</code> represents <code>Ctrl+k</code>
 +
 
 +
|-
 +
| Alt
 +
| <center>@[?]</center>
 +
| <code>[?]</code> can be '''a~z, 0~9, #[?]''',
 +
e.g. <code>@k</code> represents <code>Alt+k</code>
  
 
|-
 
|-
| <center>^k</center>
+
| Shift
| The control key, i.e., <tt>Ctrl+k</tt>, where <tt>k</tt> can be a~z, 0~9, #n and ~n.
+
| <center>$[?]</center>
 +
| <code>[?]</code> can be '''#[?]'''. Note: $a ~ $z are not supported.
 +
e.g. <code>$#down</code> represents <code>Shift+↓</code>
  
 
|-
 
|-
| <center>@k</center>
+
| Mac command(⌘)
| The alt key, i.e., <tt>Alt+k</tt>, where <tt>k</tt> can be a~z, 0~9, #n and ~n.
+
| <center>%[?]</center>
 
+
|  
 +
{{versionSince| 8.5.0}}
 +
<code>[?]</code> can be ''' a~z, 0~9, #[?]'''.
 +
e.g. <code>%k</code> represents <code>command+k</code>
 
|-
 
|-
| <center>$k</center>
+
| '''Navigation key'''
| The shift key, i.e., <tt>Shift+k</tt>, can <tt>k</tt> could be #n and ~n.
+
| <center>#[?]</center>
 
+
| the supported value of <code>[?]</code> are listed below:
 
|-
 
|-
| <center>#n</center>
+
| Home
| A special key as follows.
 
 
 
 
 
{| border="1" | width="100%"
 
 
| <center>#home</center>
 
| <center>#home</center>
| <center>Home</center>
+
|
 +
|-
 +
| End
 
| <center>#end</center>
 
| <center>#end</center>
| <center>End</center>
+
|
 +
|-
 +
| Insert
 
| <center>#ins</center>
 
| <center>#ins</center>
| <center>Insert</center>
+
|
 
 
 
|-
 
|-
 +
| Delete
 
| <center>#del</center>
 
| <center>#del</center>
| <center>Delete</center>
+
|
 +
|-
 +
| ←
 
| <center>#left</center>
 
| <center>#left</center>
| <center>←</center>
+
|
 +
|-
 +
| →
 
| <center>#right</center>
 
| <center>#right</center>
| <center>→</center>
+
|
 
 
 
|-
 
|-
 +
| ↑
 
| <center>#up</center>
 
| <center>#up</center>
| <center>↑</center>
+
|
 +
|-
 +
| ↓
 
| <center>#down</center>
 
| <center>#down</center>
| <center>↓</center>
+
|
 +
|-
 +
| PgUp
 
| <center>#pgup</center>
 
| <center>#pgup</center>
| <center>PgUp</center>
+
|
 
 
 
|-
 
|-
 +
| PgDn
 
| <center>#pgdn</center>
 
| <center>#pgdn</center>
| <center>PgDn</center>
+
|
|  
+
|-
|  
+
| Backspace
|  
+
| <center>#bak</center>
|  
+
|
 
+
|-
 +
| function key (F1, F2,... F12)
 +
| <center>#f1, #f2, ... #f12</center>
 +
|
 +
|-
 +
| Tab
 +
| <center>#tab</center>
 +
|
 +
{{versionSince| 9.5.1}}
 
|-
 
|-
| <center>#f''n''</center>
+
| Space
| colspan="5" | A function key. #f1, #f2, ... #f12 for F1, F2,... F12.
+
| <center>#space</center>
 
+
|
|}
+
{{versionSince| 10.0.0}}
 
|}
 
|}
  
 
=Document-level Keystrokes=
 
=Document-level Keystrokes=
If there is no widget gaining a focus when the end user presses a keystroke, ZK will try to locate the first root component and then forward the event to it. For example, when visiting the following page, the <tt>div</tt> component will receive the <code>onOK</code> event.
+
 
 +
{{versionSince| 5.0.6}}
 +
 
 +
If you set the library property [https://www.zkoss.org/wiki/ZK_Configuration_Reference/zk.xml/The_Library_Properties/org.zkoss.zk.ui.invokeFirstRootForAfterKeyDown.enabled org.zkoss.zk.ui.invokeFirstRootForAfterKeyDown.enabled] to <code>true</code> and there is no widget gaining a focus when an end-user presses a keystroke, ZK will fire a key event to '''the first root component that has an onCtrlKey listener'''.  
 +
For example, when visiting the following page, the <code>div</code> component will receive the <code>onOK</code> event.
 +
 
 +
<source lang="xml">
 +
<div onOK="doSomething(event)" ctrlKeys="^K" onCtrlKey="doSomething(event)" >
 +
press enter key or ctrl+k.
 +
<zscript><![CDATA[
 +
public void doSomething(KeyEvent e){
 +
Clients.showNotification(e.getKeyCode()+"");
 +
}
 +
]]></zscript>
 +
</div>
 +
</source>
 +
 
 +
In other words, <code>doSomething()</code> will be called if a user presses ENTER, even though no widget ever gains the focus.
 +
 
 +
=Nested Components=
 +
 
 +
Keystrokes are propagated up from the widget gaining the focus to the first ancestor widget that handles the keystroke. For example,
  
 
<source lang="xml">
 
<source lang="xml">
<div onOK="doSomething()">
+
<div onOK="doFirst()">
...
+
  <textbox id="t1"/>
 +
  <div onOK="doSecond()">
 +
      <textbox id="t2"/>
 +
  </div>
 
</div>
 
</div>
 
</source>
 
</source>
  
In other words, <code>doSomething()</code> will be called if the user presses ENTER, even though no widget ever gains the focus.
+
Then, <code>doSecond()</code> is called if <code>t2</code> is the current focus, and <code>doFirst()</code> is called if <code>t1</code> has the focus.
 +
 
 +
=Key handling and onChange event=
 +
When an onChange listener alone is registered on a component, onChange will be triggered by blur events exclusively.
 +
 
 +
However, some key events will cause a check for change value and will fire a change event if necessary.
 +
 
 +
These key events are: onOK, onCancel, and onCtrlkeys. If a listener for any of these events is registered and triggered, an onChange event calculation will be triggered, and an onChange event will be fired if the value of the control has changed.
  
 
=Version History=
 
=Version History=
{{LastUpdated}}
+
 
{| border='1px' | width="100%"
+
{| class='wikitable' | width="100%"
 
! Version !! Date !! Content
 
! Version !! Date !! Content
 
|-
 
|-
Line 138: Line 232:
 
| January 2011
 
| January 2011
 
| Document-level keystroke handling was introduced.
 
| Document-level keystroke handling was introduced.
 +
|-
 +
| 9.5.1
 +
| November 2020
 +
| Add Tab key support
 +
|-
 +
| 10.0.0
 +
| December 2023
 +
| Add Space key support
 
|}
 
|}
  
 
{{ZKDevelopersReferencePageFooter}}
 
{{ZKDevelopersReferencePageFooter}}

Latest revision as of 09:17, 30 January 2024


Keystroke Handling


Keystroke handling is generic. Any component inherited from XulElement can handle the key event in the same way.

ENTER and ESC

To handle ENTER key pressing, you can listen to the event:

  • onOK (notice O and K are both in upper case).

To handle ESC key pressing, you can listen to the event:

  • onCancel

For example:

    <grid id="form" apply="org.zkoss.reference.developer.uipattern.KeystrokeComposer">
        <rows>
            <row>Username:
                <textbox id="username"/>
            </row>
            <row>Password:
                <textbox id="password" type="password"/>
            </row>
            <row>
                <button label="Login" forward="form.onOK"/>
                <button label="Reset" forward="form.onCancel"/>
            </row>
        </rows>
    </grid>

Then, you could implement a composer as follows.


package org.zkoss.reference.developer.uipattern;

import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.select.SelectorComposer;
import org.zkoss.zk.ui.select.annotation.*;
import org.zkoss.zul.Textbox;

public class KeystrokeComposer extends SelectorComposer<Component> {

    @Wire
    private Textbox username;
    @Wire
    private Textbox password;

    @Listen("onOK = #form")
    public void onOK() {
        //handle login
        System.out.println("ok");
    }

    @Listen("onCancel = #form")
    public void onCancel() {
        username.setValue("");
        password.setValue("");
    }
}


Notice that the onOK and onCancel events are sent to the nearest ancestor of the component that has the focus. In other words, if you press ENTER in a textbox, then ZK will look up the textbox, its parent, its parent's parent and so on to see if any of them has been registered as a listener for onOK. If found, the event is sent to it. If not found, nothing will happen.

Also notice that, if a button gains the focus, ENTER will be intercepted by the browser and interpreted as pressed. For example, if you move the focus to the Reset button and press ENTER, you will receive onCancel rather than onOK (since onClick will be fired and it is converted to onCancel because of the forward attribute specified).

Control Keys

To handle the control keys, you have to specify the keystrokes you want to handle with XulElement.setCtrlKeys(String). Then, if any child component gains the focus and the user presses a keystroke that matches the combination, the onCtrlKey will be sent to the component with an instance of KeyEvent.

Like ENTER and ESC, you could specify the listener and the ctrlKeys property in one of the ancestors. ZK will search the component having the focus, its parent, its parent's parent and so on to find if any of them specifies the ctrlKeys property that matches the keystroke.

For example,

<vbox ctrlKeys="@c^a#f10^#f3" onCtrlKey="doSomething(event.getKeyCode())">
    <textbox/>
    <datebox/>
</vbox>

As shown, you could use KeyEvent.getKeyCode() to know which key was pressed.

Allowed Control Keys

Key
Syntax
Description
Control
^[?]
[?] can be a~z, 0~9, #[?],

e.g. ^k represents Ctrl+k

Alt
@[?]
[?] can be a~z, 0~9, #[?],

e.g. @k represents Alt+k

Shift
$[?]
[?] can be #[?]. Note: $a ~ $z are not supported.

e.g. $#down represents Shift+↓

Mac command(⌘)
%[?]

Since 8.5.0 [?] can be a~z, 0~9, #[?]. e.g. %k represents command+k

Navigation key
#[?]
the supported value of [?] are listed below:
Home
#home
End
#end
Insert
#ins
Delete
#del
#left
#right
#up
#down
PgUp
#pgup
PgDn
#pgdn
Backspace
#bak
function key (F1, F2,... F12)
#f1, #f2, ... #f12
Tab
#tab

Since 9.5.1

Space
#space

Since 10.0.0

Document-level Keystrokes

Since 5.0.6

If you set the library property org.zkoss.zk.ui.invokeFirstRootForAfterKeyDown.enabled to true and there is no widget gaining a focus when an end-user presses a keystroke, ZK will fire a key event to the first root component that has an onCtrlKey listener. For example, when visiting the following page, the div component will receive the onOK event.

<div onOK="doSomething(event)" ctrlKeys="^K" onCtrlKey="doSomething(event)" >
press enter key or ctrl+k.
<zscript><![CDATA[
public void doSomething(KeyEvent e){
	Clients.showNotification(e.getKeyCode()+"");
}
]]></zscript>
</div>

In other words, doSomething() will be called if a user presses ENTER, even though no widget ever gains the focus.

Nested Components

Keystrokes are propagated up from the widget gaining the focus to the first ancestor widget that handles the keystroke. For example,

<div onOK="doFirst()">
   <textbox id="t1"/>
   <div onOK="doSecond()">
       <textbox id="t2"/>
   </div>
</div>

Then, doSecond() is called if t2 is the current focus, and doFirst() is called if t1 has the focus.

Key handling and onChange event

When an onChange listener alone is registered on a component, onChange will be triggered by blur events exclusively.

However, some key events will cause a check for change value and will fire a change event if necessary.

These key events are: onOK, onCancel, and onCtrlkeys. If a listener for any of these events is registered and triggered, an onChange event calculation will be triggered, and an onChange event will be fired if the value of the control has changed.

Version History

Version Date Content
5.0.6 January 2011 Document-level keystroke handling was introduced.
9.5.1 November 2020 Add Tab key support
10.0.0 December 2023 Add Space key support



Last Update : 2024/01/30

Copyright © Potix Corporation. This article is licensed under GNU Free Documentation License.