Õ¾ÄÚËÑË÷: ÇëÊäÈëËÑË÷¹Ø¼ü´Ê
µ±Ç°Ò³Ãæ: ͼÊéÊ×Ò³ > SWT: The Standard Widget Toolkit

2.2 Keyboard Events and the Focus Control - SWT: The Standard Widget Toolkit

Previous Section  < Day Day Up >  Next Section

2.2 Keyboard Events and the Focus Control

Keyboard focus is the property that determines the target for keys that the user types. Normally, the control that has focus, called the focus control, receives all keyboard events until focus is assigned to another control. When a control has focus, it often draws in a different manner to give the user a visual cue. For example, a text widget might show a flashing I-beam caret; a list widget might draw a dotted rectangle; and so forth.

On some platforms, focus follows the mouse as it moves around the desktop from control to control. Keys go to the control that is under the mouse. On other platforms, the user must explicitly click in (or otherwise select) a control to give it focus. This policy is governed by the window manager and is not something that SWT programs need to worry about. That is, unless you deliberately override the behavior in your application, it will follow whatever the default is for the platform.

2.2.1 Setting the Focus Control

Focus is assigned to a control using Control.setFocus().

setFocus() Attempts to make this control be the one that receives keyboard events. If this control or a descendant takes focus, true is returned, indicating that the operation was successful. Composites attempt to assign focus to their children before taking focus themselves. Some controls, such as labels, do not normally take focus. A control will not take focus if it is disabled or hidden, or when input is blocked due to modality. If focus is not assigned for any reason, false is returned.

isFocusControl() Returns true if the control has focus, otherwise returns false.

You can find out which control has keyboard focus using Display.getFocusControl() (see Getting the Focus Control in the Display chapter).[1]

[1] Despite the fact that the focus control is a property of the Display, as of R3.0, SWT does not provide a Display.setFocusControl(Control control) method. The Control.setFocus() method is provided instead.

2.2.2 Forcing Focus to a Control

Under rare circumstances, you may want to force the keyboard focus to go to a control that does not normally take it. You can do this by calling forceFocus().

forceFocus() Forces the control to receive keyboard events. Controls that do not normally accept keyboard input will take focus using this method. If focus cannot be assigned for any reason, false is returned.

Most Programs Should Not Use forceFocus()

Generally speaking, forcing focus is something that you never want to do. For example, forcing focus to a label is not very useful because labels don't draw in a manner that indicates they can accept input. Forcing focus to a control that does not expect it can confuse users because they have no idea where their keystrokes are going. Application programs should always use setFocus() to assign focus.


One occasion where forceFocus() might be necessary occurs when implementing your own controls. Normally, focus is not assigned to a composite that has children. For example, when focus is in an OK button in a dialog, if the user unintentionally clicks on the parent of the button, focus remains in the button. However, some composites have children and need to take focus themselves. For example, a "notebook" (a control that provides a strip of selectable tabs that are used to switch between pages) might need to take focus when the user clicks on a tab. Notebooks normally have children, one per page. If you were to implement a notebook, instead of using the native control provided by SWT, you would need to force focus to the notebook when the mouse was pressed over a tab.

2.2.3 Focus Events

Table 2.1 shows the focus-related events that are provided by SWT.

Table 2.1. Focus Events

Untyped Event

Description

SWT.FocusIn

The control is now the focus widget

SWT.FocusOut

The control is no longer the focus widget

Typed Event

Listener

Methods

FocusEvent

FocusListener (and FocusAdapter)

focusGained(FocusEvent)

focusLost(FocusEvent)


The SWT.FocusIn (typed event FocusEvent) and SWT.FocusOut (typed event FocusEvent) events can be used to track focus within a program. Focus events contain meaningful values in only the display, widget, and type fields.

Using SWT.FocusIn and SWT.FocusOut to Indicate Focus

The following example uses SWT.FocusIn and SWT.FocusOut to ensure that the class MyControl draws a focus indicator. The implementation of both events simply redraws the control. The SWT.Paint listener checks to see whether the MyControl has focus and draws a focus rectangle.[2] Figure 2.1 shows MyControl with focus in a Shell.

[2] Note that the seemingly obvious approach of drawing and clearing the focus indicator inside the focus events will cause the widget to display incorrectly when there are outstanding paint events (see Painting in a Control in the Controls chapter). No matter where the focus indicator is drawn, paint listeners must always check for focus and draw accordingly.






static class MyControl extends Canvas implements Listener {

    String string = "";

    public MyControl(Composite parent, int style) {

        super(parent, style);

        addListener(SWT.Paint, this);

        addListener(SWT.FocusIn, this);

        addListener(SWT.FocusOut, this);

        addListener(SWT.KeyDown, this);

    }

    public void setText(String string) {

        checkWidget();

        this.string = string == null ? "" : string;

    }

    public void handleEvent(Event event) {

        switch (event.type) {

            case SWT.Paint:

                GC gc = event.gc;

                Rectangle rect = getClientArea();

                Point extent = gc.textExtent(string);

                int x = (rect.width - extent.x) / 2;

                int y = (rect.height - extent.y) / 2;

                gc.drawText(string, x, y);

                if (isFocusControl()) {

                    x -= 2; y -=2;

                    extent.x +=3; extent.y +=3;

                    gc.drawFocus(x, y, extent.x, extent.y);

                }

                break;

            case SWT.FocusIn:

            case SWT.FocusOut:

                redraw();

                break;

            case SWT.KeyDown:

                if (event.character == ' ') {

                    notifyListeners(SWT.Selection, null);

                }

                break;

        }

    }

}


Figure 2.1. The CH1a_MyControl example.

graphics/02fig01.gif


2.2.4 Key Events

When a key is pressed or released, a key event is normally created and delivered to your application. However, depending on the platform, the locale, and the keystroke, there are circumstances when this does not happen. For example, if the key is an accent character, the operating system key classification engine may temporarily consume it. When a caret character (^) is typed in a German locale followed by an e, the result is the accented character ê. Similarly, key events are not created for the intermediate keystrokes consumed by the IME when preprocessing sequences of keys into single Japanese characters. The reason for this is that these kinds of low-level key events are highly platform-specific and generally not useful to most programs.[3] SWT hides the underlying operating system events and issues a single key event when the operating system has finished classifying the keystroke.[4]

[3] The obvious exception would be programs that are trying to implement an IME. Because the appearance and behavior of the native IME are significantly different between operating systems and even between different releases of the same operating system, a nonnative implementation is guaranteed to look out of place.

[4] On Windows, a single keystroke can result in multiple low-level Windows messages, such as WM_KEYDOWN, WM_SYSKEYDOWN, WM_CHAR, WM_SYSCHAR, WM_DEADCHAR, WM_SYSDEADCHAR, WM_IME_CHAR, and WM_IME_COMPOSITION. This makes the SWT keyboard implementation on Windows particularly troublesome.

Table 2.2 shows the key events that are provided by SWT.

Table 2.2. Key Events

Untyped Event

Description

SWT.KeyDown

A key was pressed

SWT.KeyUp

A key was released

Typed Event

Listener

Methods

KeyEvent

KeyListener (and KeyAdapter)

keyPressed(KeyEvent)

keyReleased(KeyEvent)


The SWT.KeyDown (typed event KeyEvent) and SWT.KeyUp (typed event KeyEvent) events represent high-level key presses and key releases. They can be used to implement and intercept key processing. The relevant event fields during SWT.KeyDown and SWT.KeyUp are shown in Table 2.3.

Table 2.3. Public Fields of Class Event Valid during SWT.KeyDown and SWT.KeyUp

Field

Description

character

The Unicode value of the character (e.g., \u0041 for A)

keyCode

A constant indicating which key was pressed (e.g., SWT.PAGE_UP)

stateMask

The "state mask" representing keyboard modifiers (e.g., SWT.SHIFT)

doit

A boolean that can be used to cancel the key operation


2.2.5 Characters, Key Codes, and State Masks

For key events that represent characters found in the Unicode character set, the character field is set to the matching (Unicode) char value. For example, when the user presses the <a> key, an SWT.KeyDown event is issued. The character field in this event has the (char) value a.

Despite the fact that SWT characters are just Java char values, convenience constants are provided for most common control characters, even those that have standard escape sequences (such as '\n' for linefeed). Programmers are free to use the constants (found in Table 2.4) or their equivalents.

Table 2.4. Character Convenience Constants

Character Constant

Description

SWT.BS

The Backspace character ('\b')

SWT.CR

The Return character ('\r')

SWT.DEL

The Delete character ('\u007F')

SWT.ESC

The Escape character ('\u001B')

SWT.LF

The Linefeed character ('\n')

SWT.TAB

The Tab character ('\t')


The character field in a key event represents the final character value for the keystroke. Any modifier keys, such as <Shift> or <Ctrl>, that were held down when the key was pressed have been applied to the character. For example, when the user presses <Shift> and the <a> key, the character field contains A. Less obvious is the result of pressing <Ctrl> and the <a> key. This gives a Control+A character (with value '\u0001', known as SOH in the Unicode specification). Holding down <Ctrl> and pressing @, a to z, [, \, ], ^, or _ results in the corresponding Unicode control characters in the range '\u0000' to '\u001F'.

Why Does SWT Generate Control Characters?

Programmers who are new to SWT sometimes find the fact that the character field contains the processed control character confusing. They expect to create the control character themselves based on the values in the character and stateMask (see below) fields. Doing this is a source of programming errors on keyboards where the <Ctrl> key, in combination with other modifiers, is used to enter international characters. For example, on German Windows, the @ character is entered by holding down a special modifier key, <AltGr> (which is equivalent to pressing <Ctrl> and <Alt>), and typing the <q> key. SWT correctly creates a key event with the character field set to '@'. Application code that attempted to handle control characters itself by detecting when the <Ctrl> key was pressed would turn this sequence into the control character '\u0000' (called NUL in the Unicode spec), which is not the character that the user typed.


The keyCode field in the event is used to represent keystrokes that are not Unicode values. For example, pressing <F4> or <PageUp> causes the keyCode field to contain a corresponding key code value. All possible key code values are defined in the class SWT. For example, the key code value for the <PageUp> key is SWT.PAGE_UP. (There are simply too many constants to list here, see the Javadoc for class SWT for the complete list.)

Although, strictly speaking, there are Unicode values to represent almost every kind of key,[5] the character field in the key event is not always assigned. Normally, a key such as <PageUp> is intended to cause an action, not insert a character. Instead of attempting to draw a character, programs use the fact that the character field is unassigned to classify the keystroke.

[5] Most notably, there are no standard Unicode characters for the function keys.

The keyCode field has one further purpose. If the character field is assigned, the keyCode field is used to represent the key code of the "unaffected" character. For example, the character field will contain the plus character (+) when the <Add> key is pressed. It will also contain the plus when the numeric keypad <Add> key is pressed. Most programs process either <Add> key in the same manner, using the character field. However, some programs might want to differentiate between the two. In this case, the keyCode field will contain SWT.KEYPAD_ADD when the plus character originated from the numeric keypad.

The stateMask field is an integer bit-mask that captures that state of the keyboard and mouse immediately prior to the event.[6] This is an important point to understand. For example, at the time the <Shift> key is pressed, the <Shift> key is not down because you are in the process of pressing it. In this case, the stateMask field does not contain the SWT.SHIFT modifier. However, the stateMask will contain the SWT.SHIFT modifier when the next key is pressed because the <Shift> key is down at that time.

[6] For those readers who are familiar with X Windows, the SWT stateMask field works the same way.

Table 2.5 shows the four[7] specific modifier masks supported by SWT.

[7] The convenience constant SWT.CTRL is also provided that is identical to SWT.CONTROL. Applications can use these two interchangeably, although SWT.CTRL tends to be used to specify accelerators whereas SWT.CONTROL is used for listeners.

Table 2.5. Specific Modifier Key State Masks

Modifier Mask

Description

SWT.CONTROL

The <Ctrl> key was down (same as SWT.CTRL)

SWT.SHIFT

The <Shift> key was down

SWT.ALT

The <Alt> key was down

SWT.COMMAND

The <Command> key was down


Modifier masks are also used to represent keystrokes when the modifier keys themselves are pressed. For example, when the user presses the <Shift> key and nothing else, a key down event is generated with the character field set to '\u0000', the keyCode field set to SWT.SHIFT, and the stateMask field set to zero. If the user were now to press the <a> key, another key event would be generated. This new event would have the character field set to A and the stateMask field set to SWT.SHIFT. This can be a little bit confusing at first because the same constant (SWT.SHIFT) is used to serve two different purposes in the two fields.

The specific modifier keys and state masks can be tricky to use portably. Most keyboards contain at least the <Shift>, <Ctrl>, and <Alt> keys but some platforms use more than just these three. To make things more complicated, platform-specific conventions exist that dictate which modifier keys should be used. For example, on the Macintosh, users expect to type Command+C to copy text to the clipboard. On Windows, the equivalent key combination is Control+C. Although it is possible to write code on a Macintosh that deliberately uses Control+C to copy text, any application that does this would be frustrating to use and would definitely not match the Macintosh user interface guidelines.

To solve this problem, SWT defines the generic modifier masks shown in Table 2.6.

Table 2.6. Generic Modifier Key State Masks

Modifier Mask

Description

SWT.MOD1

The first modifier was down (often SWT.CONTROL)

SWT.MOD2

The second modifier was down (often SWT.SHIFT)

SWT.MOD3

The third modifier was down (often SWT.ALT)

SWT.MOD4

The fourth modifier was down (often zero)

SWT.MODIFIER_MASK

Bitwise-OR of all valid modifiers


These generic modifier masks are initialized when SWT starts up on each platform. For example, SWT.MOD1 is set to SWT.CONTROL on Windows and to SWT.COMMAND on the Macintosh.

It is important to note that expecting to process a modifier key on a platform where the modifier does not exist is futile. For example, the SWT.COMMAND key can never be pressed on Windows because it does not exist on the keyboard.

The constant SWT.MODIFIER_MASK contains every valid modifier. It is generally used to test for an unmodified key. This constant ensures that when new modifier keys are added to SWT, code that tests for unmodified keys (or a key that is modified in any way) will continue to work. The following code fragment illustrates this point.






//WRONG – broken when new modifier masks are added

int bits = SWT.CONTROL | SWT.ALT | SWT.SHIFT | SWT.COMMAND;

if ((event.stateMask & bits) == 0) {

    System.out.println("No modifiers are down");

}



//CORRECT – works when new modifier masks are added

if ((event.stateMask & SWT.MODIFIER_MASK) == 0) {

    System.out.println("No modifiers are down");

}


Which Modifier Constants Should I Use?

Use the generic modifiers if you want the application to use the "expected" modifier keys for common actions on each platform.[8] Use the specific modifier constants if your application needs to handle the exact (named) modifiers the same way everywhere. For example, you would do this if you were building a terminal emulator that needed to pass the modifiers on to a remote host.

Eclipse uses the specific modifiers in its implementation but includes its own multiplatform, user-configurable key bindings mechanism to deal with them. The generic modifiers are definitely a simpler answer for most applications.


[8] Note that this mechanism maps only the modifier keys. It does not map all platform-specific key sequences. For example, it does not map the Macintosh Command+W (window close) key to the Windows equivalent, ALT+F4.

2.2.6 Using SWT.KeyDown to Print Key Events

The following program listens for key up and key down events and prints the stateMask, keyCode, and character values as keys are typed.






public static void main(String[] args) {

    Display display = new Display();

    Shell shell = new Shell(display);

    Listener listener = new Listener() {

        public void handleEvent(Event event) {

            String string =

                event.type == SWT.KeyDown ? "DOWN": "UP  ";

            string += ": stateMask=0x"

                + Integer.toHexString(event.stateMask);

            if ((event.stateMask & SWT.CTRL) != 0)

                string += " CTRL";

            if ((event.stateMask & SWT.ALT) != 0)

                string += " ALT";

            if ((event.stateMask & SWT.SHIFT) != 0)

                string += " SHIFT";

            if ((event.stateMask & SWT.COMMAND) != 0)

                string += " COMMAND";

            string += ", keyCode=0x"

                + Integer.toHexString(event.keyCode);

            string += ", character=0x"

                + Integer.toHexString(event.character);

            switch (event.character) {

                case 0: string += " '\\0'"; break;

                case SWT.BS: string += " '\\b'"; break;

                case SWT.CR: string += " '\\r'"; break;

                case SWT.DEL: string += " DEL"; break;

                case SWT.ESC: string += " ESC"; break;

                case SWT.LF: string += " '\\n'"; break;

                case SWT.TAB: string += " '\\t'";

                    break;

                default:

                    string += " '" + event.character + "'";

                    break;

            }

            System.out.println(string);

        }

    };

    shell.addListener(SWT.KeyDown, listener);

    shell.addListener(SWT.KeyUp, listener);

    shell.setSize(200, 200);

    shell.open();

    while (!shell.isDisposed()) {

        if (!display.readAndDispatch()) display.sleep();

    }

    display.dispose();

}


Using SWT.KeyDown to Consume Keys

Occasionally under rare circumstances, it is necessary to consume key events before they are processed by a control. Because SWT is implemented using native controls, most of the processing behind each keystroke occurs in the operating system. For example, when the user types in a text control, the code that inserts and draws the characters runs in the operating system after the SWT.KeyDown listeners return. Using the doit field, you can consume the keystroke and stop the operating system from processing the character. The following example prevents the user from entering characters in a native text control by setting the doit field to false for every SWT.KeyDown event.






public static void main(String[] args) {

    Display display = new Display();

    Shell shell = new Shell(display);

    Text text = new Text(shell, SWT.SINGLE | SWT.BORDER);

    text.addListener(SWT.KeyDown, new Listener() {

        public void handleEvent(Event event) {

            event.doit = false;

        }

    });

    text.pack();

    shell.pack();

    shell.open();

    while (!shell.isDisposed()) {

        if (!display.readAndDispatch()) display.sleep();

    }

    display.dispose();

}


Sometimes programmers mistakenly attempt to use this feature to implement input validation for text controls. This is unnecessary and prone to error.[9] Input validation is provided in a platform-independent and portable manner for text controls using the SWT.Verify event (see Class Text in the Basic Controls chapter).

[9] Here are a few of the things that can go wrong when attempting to do input validation using the doit field. On Windows, the Control+V key sequence does not insert a Control V character. Instead, it pastes text from the clipboard. Programs that attempt to work around this either by consuming Control+V or validating the contents of the clipboard will fail on the Macintosh. The key sequence used to paste text on that platform is Command+V. Sometimes characters can be entered into a text control without typing, using a built-in context menu or some other input mechanism. These characters will bypass key listeners because a key was not pressed.

Note that setting the doit field to false will not stop the processing of keystrokes when this happens outside of the operating system, for example, in another SWT.KeyDown listener on the same control. In this case, because nothing is done with the keystroke in the operating system, there is nothing to cancel (although it is not harmful to set doit to false). A further problem arises when your listener is added after the other SWT.KeyDown listeners for the control. It runs after the processing has already happened. To solve this problem and intercept and cancel all key processing, no matter where and how the actual work is done, event filters can be used (see Event Filters in the Display chapter).

    Previous Section  < Day Day Up >  Next Section