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