How Keyboard Focus is Handled

Responsibility for keyboard focus (the determination of which window receives events from the keyboard) is distributed among the toolkit peers, the window system, and AWT.

Event delivery

The routing decision for keyboard events is in the domain of the window system. It keeps track of which window is supposed to receive the keyboard events, and this is the window to which they are actually directed when they occur.

Assignment of focus

The assignment of the window system's focus is handled primarily by AWT. Every top-level window has an associated FocusManager whose job it is to keep track of which component within that window should have the keyboard focus when this top level window is active. For example, if the system allows the user to advance the focus through a list of components using the TAB key, the FocusManager is consulted to actually advance the focus. It does this by searching through the list of components in the container for the next (or previous) component which is visible, enabled, and is capable of taking the focus.

The assignment of focus is accomplished by calling the peer's requestFocus() method. This in turn tells the window system that the window associated with this component should receive the focus.

Focus change events

Whenever the focus is transferred, the AWT expects to receive events describing the loss and gain of focus. In some cases there may be only a loss or a gain; this happens if the focus is being transferred to or from a window that is not under AWT's control. But in the case of a focus traversal like the "TAB advance" mechanism described above, a FOCUS_LOST event will be sent, followed by a FOCUS_GAINED event.

Window activation

The FocusManager keeps track of which child should have the focus when the window is active. But within the grander scheme of the window system, what determines which top-level window is the active one? This is system-dependent behavior; in a Motif implementation, for example, the X window manager will set the policy for how activation happens and will actually assign the window activation when necessary.

Regardless of who is implementing and enforcing the policy, the toolkit gets involved in this game. Whenever a top-level window is activated or deactivated, events describing the focus change must be sent to AWT. Both a WINDOW_DEACTIVATE, for the window losing the active focus, and a WINDOW_ACTIVATE, for the window gaining the active focus, must be sent.

It should be becoming clear that there are two distinct "levels" of focus: the activation state, which is an indication of which top-level window is receiving attention, and the input focus, which indicates which child window (e.g. a TextField) is actually supposed to receive the events. It is important to keep the distinction clear. There are two sets of events associated with these different levels of focus.

General notes

In a typical JDK implementation, the native window system's window activation model is used. Usually the window system will give users explicit control over window activation, and will assign the focus based on a combination of user directives and defined policy: e.g. default behaviors, implicit deactivation of one window before another is activated, etc.

The java FocusEvent class is a subclass of ComponentEvent. This implies that focus events do not get sent to AWT Menu-related objects. This does not mean that a menu component can't receive the keyboard focus, but it does mean that the assignment of this focus may not be under AWT control.

Lightweight components

It should be noted that as far as the window system is concerned, both the active window and the focus owner are windows. How, then, does a "lightweight" component--for which there is no guaranteed one-to-one mapping from a window (peer) to a component--receive keyboard events? The answer is that the FocusManager assigns focus to a Component, without regard to whether it has a window associated with it or not. When the FocusManager assigns focus to a component whose peer is a LightweightPeer, special "proxy" code is invoked. The proxy code assigns the window system's focus to the window which contains the lightweight. Then it tells a special event dispatcher, used especially for distributing events to lightweights, that it must redirect the key events to the appropriate lightweight component. (The dispatcher is required to synthesize FOCUS_LOST and FOCUS_GAINED events, as appropriate, if the request causes focus to be reassigned.)

Personal Java Peers

As can be inferred from the general notes, the window activation policy is system-dependent. In the current implementation of the Personal Java peers, window activation state is maintained as a LIFO (stack). This simple scheme is feasible because in a minimal implementation of Personal Java, there can be only one frame, and dialogs must be modal; taken together, these two things mean that windows can be expected to stack and unstack in a fairly predictable fashion. Activate and deactivate events are simply associated with the visibility changes of the windows: when show() is called on a new window (a dialog, unless this is the one and only top-level window) the old active window is deactivated, the new window is pushed on the stack, and it is activated. When the window on top is hidden, the reverse operation takes place. If a window being hidden is not the top level window, it is simply removed from the stack; no activation event is needed because the window is going away, and a deactivation event would be wrong because the operation was not on the active window.

If the optional functionality for multiple top-level windows were to be implemented, a window management policy, such as the one alluded to in the general notes, would have to be put in place. Assignment of activation, probably including the generation of the activate and deactivate events, would then be the window manager's responsiblity.


Last modified: Fri May 15 16:32:43 PDT