Twitter Logo Follow us on Twitter
Project Information About this project

Key and Mouse Event Handling in RAP

Most SWT widgets provide distinctive events that are well suited for typical use cases of that widget, like selection events and modify events. These events should, if possible, always be favored over key and mouse events, which are available on all widgets. Not only are the more specific events fired at the appropriate times and provide all relevant informations, but they can also be more optimized by the RAP framework regarding performance, network-traffic and compatibility. Still, key and mouse events are sometimes necessary to use, especially when adding or modifying widget-behavior, or for global event handling.

Due to their diverse nature, differing implementation, and the high frequency in which they may be fired, key and mouse events are a major challenge in a web-environment, especially with a client-server architecture like RAP. While RWT provides a SWT-like API for key and mouse events, some specifics should be noted.

Mouse Events

All mouse event types using in the MouseListener interface are almost fully supported. (They can only detect the first three mouse buttons.) They are:

However, all other event mouse event types, used in MouseTrackListener and MouseWheelListener, are unsupported/missing. Namely:

  • MouseMove
  • MouseEnter
  • MouseExit
  • MouseWheel

They can not be reasonably implemented because the network latency would make them barely usable. They are mostly supported in RAP ClientScripting though.

Mouse events are only sent to the server (and only cause traffic) if they are fired on widgets that have a MouseListener or untyped listener for a mouse event attached. As a side-effect, using a display filter does not work like in SWT. Example:

display.addFilter( SWT.MouseDown, new Listener() { ... } );

This would catch all mouse events in SWT, but in RWT it will only receive those which targets already have a mouse listener.

Traverse Events

Before we discuss key events, it is important to note that there are also traverse events, which are related. Traverse events are fired before a key event, and only for keys that can change the focus. (For example Escape, Tab, Arrows.) The idea is that you can prevent or manipulate the focus change, but that is not supported in RWT. (See "Prevent Default Operation" for a workaround.)

Key Events

Event Order and Latency

There are two type of key events, KeyDown and KeyUp. In SWT, KeyDown is fired after pressing the key, then after a short delay fired repeatedly until the key is released. Then KeyUp is fired.
In RWT, KeyUp is fired immediately after KeyDown, even if the key is not released. After the short delay, both events are fired repeatedly until the key is released. An Example for pressing and holding a key in SWT and RWT (read left to right):

SWT KeyDown delay KeyDown KeyDown KeyUp
RWT KeyDown
KeyUp
delay KeyDown
KeyUp
KeyDown
KeyUp
-

A key event is sent to the server immediately when the key is pressed (not on release). This requests triggers both the KeyDown and KeyUp event. If a key is held down, or when typing very fast, the new key events may be put into a queue on the client until the current key event is finished being processed. This also increases the latency a bit.
If no latency is not acceptable at all, the key events would have to be processed on the client directly, or a custom widget has to be used.

Prevent Default Operation (CANCEL_KEYS)

The KeyEvent object has a doit field (as do untyped events), which is true by default. In SWT, this field can be set to false to prevent the default operation associated with pressing or releasing this key, like inserting a character in a Text widget. RAP supported this in a limited capacity until 1.4, but support was discontinued in 1.5. The reason for this was that it worked unreliably and prevented a number of bugs from being fixed.

As an alternative, the Cancel Keys feature was introduced. It is slightly less flexible, but very powerful and still single-sourcing capable. Instead of reacting to a key event, it allows to attach a list of keys or key-combinations to a widget using the RWT.CANCEL_KEYS constant:

widget.setData( RWT.CANCEL_KEYS, new String[] { "CTRL+1", "CTRL+2" } ); 

When the given key-combination is pressed while the widget is focused, the operation is canceled. It also cancels operations that could only be prevented on traverse events in SWT. Event more importantly, it is possible to suppress most (not all) browser shortcuts that way, which would otherwise be triggered at the same moment that your key listener is executed. For example CTRL+F usually opens and focuses a search-dialog in the browser, but the RAP application might want to use it to focus its own search-field.

Additional Notes:

  • Valid patterns to identify key combinations are documented with the Active Keys feature.
  • Browser-shortcuts of the pattern ALT+[letter] can often not be suppressed.
  • The Cancel Keys do not influence the key event handling. That means it works whether a listener is attached or not, and canceled keys are still fired as an SWT key event.
  • This approach is single-sourcing capable. Since the doit field still exists in RWT, and the list is attached using only setData, both mechanisms can be used in parallel.
  • An alternative to Cancel Keys is ClientScripting, which can take the state of the widget into account.

Reduce Traffic (ACTIVE_KEYS)

As explained above, each keystroke creates at least one HTTP-request. Obviously this can create a lot of traffic. If this is a concern, the Active Keys feature can help you. Exactly like with cancel keys, active keys lets you attach a list of keys or key-combinations to a widget (using RWT.ACTIVE_KEYS). As result, only key events that match a combination on this list will be issued, drastically reducing traffic.

Additional Notes:

  • Valid patterns to identify key combinations are documented on the RWT.ACTIVE_KEYS constant.
  • Unlike Cancel Keys, Active Keys are only relevant if a key listener is attached.
  • Despite the naming, Active Keys and Cancel Keys can be freely combined. They do not influence each other.
  • This approach is also single-sourcing capable, as long as the key listener does not rely on the preselection of the Active Keys feature.

Global Key Event Handling (Keybindings)

Both Active Keys and Cancel Keys can also be used on an instance of Display, which is useful to allow keybindings ("shortcuts"). Active Keys work slightly different in that case. Like with mouse events (see above), a display filter usually catches only key events when their target has a listener attached. However, attaching a list of Active Keys to display will result in all key events that match a list entry to be fired, regardless of the focused widget. Likewise, a list of Cancel Keys attached to the display is always in effect.

To implement global shortcuts, one would first attach a list of all shortcuts as Active Keys to display. The same list can then be used again for Cancel Keys to prevent any undesired operations to be triggered by any of the keys. Then a filter for SWT.KeyDown has to be added, where the pressed key combination has to be determined and handled.

The combinations for global shortcuts should be carefully chosen, so they do not to interfere any of the core functionality of a widget or the browser. For example, SHIFT+[arrow key] is not a good choice, since this is used by many widgets for selection. Similarly, many users might be accustomed to use F5 to reload the page. The combinations CTRL+TAB and ALT+TAB can not be used. They will never be fired and can not be canceled.

Summary:

A key event will be issued if any if this is true:

  • The key combination is in the displays Active Keys list.
  • The focused widget has a key listener attached and no Active Keys list.
  • The focused widget has a key listener attached and the key combination is its Active Keys list.

A key event will cancelled if the browser supports it, and any if this is true:

  • The key combination is in the displays Cancel Keys list.
  • The focused widget has a Cancel Keys list and the key combination is in it.