The AWT Focus Subsystem

Prior to Java 2 Standard Edition, JDK 1.4, the AWT focus subsystem was inadequate. It suffered from major design and API problems, as well as over a hundred open bugs. Many of these bugs were caused by platform inconsistencies, or incompatibilities between the native focus system for heavyweights and the Java focus system for lightweights.

The single worst problem with the AWT focus implementation was the inability to query for the currently focused Component. Not only was there no API for such a query, but also, because of an insufficient architecture, such information was not even maintained by the code.

Almost as bad was the inability of lightweight children of a Window (not a Frame or a Dialog) to receive keyboard input. This problem existed because Windows never received WINDOW_ACTIVATED events and thus could never be activated, and only active Windows could contain focused Components.

In addition, many developers noted that the APIs for FocusEvent and WindowEvent were insufficient because they did not provide a way for determining the "opposite" Component involved in the focus or activation change. For example, when a Component received a FOCUS_LOST event, it had no way of knowing which Component was gaining focus. Since Microsoft Windows provides this functionality for free, developers migrating from Microsoft Windows C/C++ or Visual Basic to Java had been frustrated by the omission.

To address these and other deficiencies, we have designed a new focus model for the AWT in JDK 1.4. The primary design changes were the construction of a new centralized KeyboardFocusManager class, and a lightweight focus architecture. The amount of focus-related, platform-dependent code has been minimized and replaced by fully pluggable and extensible public APIs in the AWT. While we have attempted to remain backward compatible with the existing implementation, we were forced to make minor incompatible changes in order to reach an elegant and workable conclusion. We anticipate that these incompatibilities will have only a trivial impact on existing applications.

This document is a formal specification both of the new APIs and of existing APIs which remain relevant in the new model. Combined with the javadoc for focus-related classes and methods, this document should enable developers to create substantial AWT and Swing applications with a focus behavior that is customized yet consistent across platforms. This document has the following sections:

Overview of KeyboardFocusManager

The focus model is centralized around a single class, KeyboardFocusManager, that provides a set of APIs for client code to inquire about the current focus state, initiate focus changes, and replace default focus event dispatching with a custom dispatcher. Clients can inquire about the focus state directly, or can register a PropertyChangeListener that will receive PropertyChangeEvents when a change to the focus state occurs.

KeyboardFocusManager introduces the following main concepts and their terminology:

  1. The "focus owner" -- the Component which typically receives keyboard input.
  2. The "permanent focus owner" -- the last Component to receive focus permanently. The "focus owner" and the "permanent focus owner" are equivalent unless a temporary focus change is currently in effect. In such a situation, the "permanent focus owner" will again be the "focus owner" when the temporary focus change ends.
  3. The "focused Window" -- the Window which contains the "focus owner".
  4. The "active Window" -- the Frame or Dialog that is either the "focused Window", or the first Frame or Dialog that is an owner of the "focused Window".
  5. "Focus traversal" -- the user's ability to change the "focus owner" without moving the cursor. Typically, this is done using the keyboard (for example, by using the TAB key), or an equivalent device in an accessible environment. Client code can also initiate traversal programmatically. Normal focus traversal can be either "forward" to the "next" Component, or "backward" to the "previous" Component.
  6. "Focus traversal cycle" -- a portion of the Component hierarchy, such that normal focus traversal "forward" (or "backward") will traverse through all of the Components in the focus cycle, but no other Components. This cycle provides a mapping from an arbitrary Component in the cycle to its "next" (forward traversal) and "previous" (backward traversal) Components.
  7. "Traversable Component" -- Component that is in the focus traversal cycle.
  8. "Non-traversable Component" -- Component that is not in the focus traversal cycle. Note that a non-traversable Component can nevertheless be focused in other way (e.g. by direct focus request).
  9. "Focus cycle root" -- Container that is the root of the Component hierarchy for a particular "focus traversal cycle". When the "focus owner" is a Component inside a particular cycle, normal forward and backward focus traversal cannot move the "focus owner" above the focus cycle root in the Component hierarchy. Instead, two additional traversal operations, "up cycle" and "down cycle", are defined to allow keyboard and programmatic navigation up and down the focus traversal cycle hierarchy.
  10. "Focus traversal policy provider" - Container which has "FocusTraversalPolicyProvider" property as true. This Container will be used to acquire focus traversal policy. This container doesn't define new focus cycle but only modifies the order by which its children are traversed "forward" and "backward". Focus traversal policy provider can be set using setFocusTraversalPolicyProvider on the Container.

Every Window and JInternalFrame is, by default, a "focus cycle root". If it's the only focus cycle root, then all of its focusable descendants should be in its focus cycle, and its focus traversal policy should enforce that they are by making sure that all will be reached during normal forward (or backward) traversal. If, on the other hand, the Window or JInternalFrame has descendants that are also focus cycle roots, then each such descendant is a member of two focus cycles: the one that it is the root of, and the one of its nearest focus-cycle-root ancestor. In order to traverse the focusable components belonging to the focus cycle of such a "descendant" focus cycle root, one first traverses (forward or backward) to reach the descendant, and then uses the "down cycle" operation to reach, in turn, its descendants.

Here is an example:
Three groups as described below: ABCF BDE and DGH.

Assume the following:

There are a total of three focus cycle roots in this example:
  1. A is a root, and A, B, C, and F are members of A's cycle.
  2. B is a root, and B, D, and E are members of B's cycle.
  3. D is a root, and D, G, and H are members of D's cycle.
Windows are the only Containers which, by default, are focus cycle roots. KeyboardFocusManager is an abstract class. AWT provides a default implementation in the DefaultKeyboardFocusManager class.

KeyboardFocusManager and Browser Contexts

Some browsers partition applets in different code bases into separate contexts, and establish walls between these contexts. Each thread and each Component is associated with a particular context and cannot interfere with threads or access Components in other contexts. In such a scenario, there will be one KeyboardFocusManager per context. Other browsers place all applets into the same context, implying that there will be only a single, global KeyboardFocusManager for all applets. This behavior is implementation-dependent. Consult your browser's documentation for more information. No matter how many contexts there may be, however, there can never be more than one focus owner, focused Window, or active Window, per ClassLoader.

KeyEventDispatcher and KeyEventPostProcessor

While the user's KeyEvents should generally be delivered to the focus owner, there are rare cases where this is not desirable. An input method is an example of a specialized Component that should receive KeyEvents even though its associated text Component is and should remain the focus owner.

A KeyEventDispatcher is a lightweight interface that allows client code to pre-listen to all KeyEvents in a particular context. Instances of classes that implement the interface and are registered with the current KeyboardFocusManager will receive KeyEvents before they are dispatched to the focus owner, allowing the KeyEventDispatcher to retarget the event, consume it, dispatch it itself, or make other changes.

For consistency, KeyboardFocusManager itself is a KeyEventDispatcher. By default, the current KeyboardFocusManager will be the sink for all KeyEvents not dispatched by the registered KeyEventDispatchers. The current KeyboardFocusManager cannot be completely deregistered as a KeyEventDispatcher. However, if a KeyEventDispatcher reports that it dispatched the KeyEvent, regardless of whether it actually did so, the KeyboardFocusManager will take no further action with regard to the KeyEvent. (While it is possible for client code to register the current KeyboardFocusManager as a KeyEventDispatcher one or more times, there is no obvious reason why this would be necessary, and therefore it is not recommended.)

Client-code may also post-listen to KeyEvents in a particular context using the KeyEventPostProcessor interface. KeyEventPostProcessors registered with the current KeyboardFocusManager will receive KeyEvents after the KeyEvents have been dispatched to and handled by the focus owner. The KeyEventPostProcessors will also receive KeyEvents that would have been otherwise discarded because no Component in the application currently owns the focus. This will allow applications to implement features that require global KeyEvent post- handling, such as menu shortcuts.

Like KeyEventDispatcher, KeyboardFocusManager also implements KeyEventPostProcessor, and similar restrictions apply to its use in that capacity.

FocusEvent and WindowEvent

The AWT defines the following six event types central to the focus model in two different java.awt.event classes:

  1. WindowEvent.WINDOW_ACTIVATED: This event is dispatched to a Frame or Dialog (but never a Window which is not a Frame or Dialog) when it becomes the active Window.
  2. WindowEvent.WINDOW_GAINED_FOCUS: This event is dispatched to a Window when it becomes the focused Window. Only focusable Windows can receive this event.
  3. FocusEvent.FOCUS_GAINED: This event is dispatched to a Component when it becomes the focus owner. Only focusable Components can receive this event.
  4. FocusEvent.FOCUS_LOST: This event is dispatched to a Component when it is no longer the focus owner.
  5. WindowEvent.WINDOW_LOST_FOCUS: This event is dispatched to a Window when it is no longer the focused Window.
  6. WindowEvent.WINDOW_DEACTIVATED: This event is dispatched to a Frame or Dialog (but never a Window which is not a Frame or Dialog) when it is no longer the active Window.

Event Delivery

If the focus is not in java application and the user clicks on a focusable child Componenta of an inactive Frame b, the following events will be dispatched and handled in order:

  1. b will receive a WINDOW_ACTIVATED event.
  2. Next, b will receive a WINDOW_GAINED_FOCUS event.
  3. Finally, a will receive a FOCUS_GAINED event.
If the user later clicks on a focusable child Component c of another Frame d, the following events will be dispatched and handled in order:
  1. a will receive a FOCUS_LOST event.
  2. b will receive a WINDOW_LOST_FOCUS event.
  3. b will receive a WINDOW_DEACTIVATED event.
  4. d will receive a WINDOW_ACTIVATED event.
  5. d will receive a WINDOW_GAINED_FOCUS event.
  6. c will receive a FOCUS_GAINED event.
Note that each event will be fully handled before the next event is dispatched. This restriction will be enforced even if the Components are in different contexts and are handled on different event dispatching threads.

In addition, each event type will be dispatched in 1-to-1 correspondence with its opposite event type. For example, if a Component receives a FOCUS_GAINED event, under no circumstances can it ever receive another FOCUS_GAINED event without an intervening FOCUS_LOST event.

Finally, it is important to note that these events are delivered for informational purposes only. It is impossible, for example, to prevent the delivery of a pending FOCUS_GAINED event by requesting focus back to the Component losing focus while handling the preceding FOCUS_LOST event. While client code may make such a request, the pending FOCUS_GAINED will still be delivered, followed later by the events transferring focus back to the original focus owner.

If it is absolutely necessary to suppress the FOCUS_GAINED event, client code can install a VetoableChangeListener which rejects the focus change. See Focus and VetoableChangeListener.

Opposite Components and Windows

Each event includes information about the "opposite" Component or Window involved in the focus or activation change. For example, for a FOCUS_GAINED event, the opposite Component is the Component that lost focus. If the focus or activation change occurs with a native application, with a Java application in a different VM or context, or with no other Component, then the opposite Component or Window is null. This information is accessible using FocusEvent.getOppositeComponent or WindowEvent.getOppositeWindow.

On some platforms, it is not possible to discern the opposite Component or Window when the focus or activation change occurs between two different heavyweight Components. In these cases, the opposite Component or Window may be set to null on some platforms, and to a valid non-null value on other platforms. However, for a focus change between two lightweight Components which share the same heavyweight Container, the opposite Component will always be set correctly. Thus, a pure Swing application can ignore this platform restriction when using the opposite Component of a focus change that occurred within a top-level Window.

Temporary FocusEvents

FOCUS_GAINED and FOCUS_LOST events are marked as either temporary or permanent.

Temporary FOCUS_LOST events are sent when a Component is losing the focus, but will regain the focus shortly. These events can be useful when focus changes are used as triggers for validation of data. For instance, a text Component may want to commit its contents when the user begins interacting with another Component, and can accomplish this by responding to FOCUS_LOST events. However, if the FocusEvent received is temporary, the commit should not be done, since the text field will be receiving the focus again shortly.

A permanent focus transfer typically occurs as the result of a user clicking on a selectable, heavyweight Component, focus traversal with the keyboard or an equivalent input device, or from a call to requestFocus() or requestFocusInWindow().

A temporary focus transfer typically occurs as the result of showing a Menu or PopupMenu, clicking or dragging a Scrollbar, moving a Window by dragging the title bar, or making another Window the focused Window. Note that on some platforms, these actions may not generate any FocusEvents at all. On others, temporary focus transfers will occur.

When a Component receives a temporary FOCUS_LOST event, the event's opposite Component (if any) may receive a temporary FOCUS_GAINED event, but could also receive a permanent FOCUS_GAINED event. Showing a Menu or PopupMenu, or clicking or dragging a Scrollbar, should generate a temporary FOCUS_GAINED event. Changing the focused Window, however, will yield a permanent FOCUS_GAINED event for the new focus owner.

The Component class includes variants of requestFocus and requestFocusInWindow which take a desired temporary state as a parameter. However, because specifying an arbitrary temporary state may not be implementable on all native windowing systems, correct behavior for this method can be guaranteed only for lightweight Components. This method is not intended for general use, but exists instead as a hook for lightweight Component libraries, such as Swing.

Focus Traversal

Each Component defines its own Set of focus traversal keys for a given focus traversal operation. Components support separate Sets of keys for forward and backward traversal, and also for traversal up one focus traversal cycle. Containers which are focus cycle roots also support a Set of keys for traversal down one focus traversal cycle. If a Set is not explicitly defined for a Component, that Component recursively inherits a Set from its parent, and ultimately from a context-wide default set on the current KeyboardFocusManager.

Using the AWTKeyStroke API, client code can specify on which of two specific KeyEvents, KEY_PRESSED or KEY_RELEASED, the focus traversal operation will occur. Regardless of which KeyEvent is specified, however, all KeyEvents related to the focus traversal key, including the associated KEY_TYPED event, will be consumed, and will not be dispatched to any Component. It is a runtime error to specify a KEY_TYPED event as mapping to a focus traversal operation, or to map the same event to multiple focus traversal operations for any particular Component or for a KeyboardFocusManager's defaults.

The default focus traversal keys are implementation-dependent. Sun recommends that the all implementations for a particular native platform use the same keys. For Windows and Unix, the recommendations are:

Components can enable and disable all of their focus traversal keys en masse using Component.setFocusTraversalKeysEnabled. When focus traversal keys are disabled, the Component receives all KeyEvents for those keys. When focus traversal keys are enabled, the Component never receives KeyEvents for traversal keys; instead, the KeyEvents are automatically mapped to focus traversal operations.

For normal forward and backward traversal, the AWT focus implementation determines which Component to focus next based on the FocusTraversalPolicy of the focus owner's focus cycle root or focus traversal policy provider. If the focus owner is a focus cycle root, then it may be ambiguous as to which Components represent the next and previous Components to focus during normal focus traversal. Thus, the current KeyboardFocusManager maintains a reference to the "current" focus cycle root, which is global across all contexts. The current focus cycle root is used to resolve the ambiguity.

For up-cycle traversal, the focus owner is set to the current focus owner's focus cycle root, and the current focus cycle root is set to the new focus owner's focus cycle root. If, however, the current focus owner's focus cycle root is a top-level window, then the focus owner is set to the focus cycle root's default component to focus, and the current focus cycle root is unchanged.

For down-cycle traversal, if the current focus owner is a focus cycle root, then the focus owner is set to the current focus owner's default component to focus, and the current focus cycle root is set to the current focus owner. If the current focus owner is not a focus cycle root, then no focus traversal operation occurs.

FocusTraversalPolicy

A FocusTraversalPolicy defines the order in which Components within a particular focus cycle root or focus traversal policy provider are traversed. Instances of FocusTraversalPolicy can be shared across Containers, allowing those Containers to implement the same traversal policy. FocusTraversalPolicies do not need to be reinitialized when the focus-traversal-cycle hierarchy changes.

Each FocusTraversalPolicy must define the following five algorithms:

  1. Given a focus cycle root and a Component a in that cycle, the next Component after a.
  2. Given a focus cycle root and a Component a in that cycle, the previous Component before a.
  3. Given a focus cycle root, the "first" Component in that cycle. The "first" Component is the Component to focus when traversal wraps in the forward direction.
  4. Given a focus cycle root, the "last" Component in that cycle. The "last" Component is the Component to focus when traversal wraps in the reverse direction.
  5. Given a focus cycle root, the "default" Component in that cycle. The "default" Component will be the first to receive focus when traversing down into a new focus traversal cycle. This may be the same as the "first" Component, but need not be.

A FocusTraversalPolicy may optionally provide an algorithm for the following:

Given a Window, the "initial" Component in that Window. The initial Component will be the first to receive focus when the Window is first made visible. By default, this is the same as the "default" Component.
In addition, Swing provides a subclass of FocusTraversalPolicy, InternalFrameFocusTraversalPolicy, which allows developers to provide an algorithm for the following:
Given a JInternalFrame, the "initial" Component in that JInternalFrame. The initial Component is the first to receive focus when the JInternalFrame is first selected. By default, this is the same as the JInternalFrame's default Component to focus.
A FocusTraversalPolicy is installed on a Container using Container.setFocusTraversalPolicy. If a policy is not explicitly set, then a Container inherits its policy from its nearest focus-cycle-root ancestor. Top-levels initialize their focus traversal policies using the context default policy. The context default policy is established by using KeyboardFocusManager. setDefaultFocusTraversalPolicy.

AWT provides two standard FocusTraversalPolicy implementations for use by client code.

  1. ContainerOrderFocusTraversalPolicy: Iterates across the Components in a focus traversal cycle in the order they were added to their Containers. Each Component is tested for fitness using the accept(Component) method. By default, a Component is fit only if it is visible, displayable, enabled, and focusable.
  2. By default, ContainerOrderFocusTraversalPolicy implicitly transfers focus down-cycle. That is, during normal forward focus traversal, the Component traversed after a focus cycle root will be the focus-cycle-root's default Component to focus, regardless of whether the focus cycle root is a traversable or non-traversable Container (see the pic.1,2 below). Such behavior provides backward compatibility with applications designed without the concepts of up- and down-cycle traversal.
  3. DefaultFocusTraversalPolicy: A subclass of ContainerOrderFocusTraversalPolicy which redefines the fitness test. If client code has explicitly set the focusability of a Component by either overriding Component.isFocusTraversable() or Component.isFocusable(), or by calling Component.setFocusable(boolean), then a DefaultFocusTraversalPolicy behaves exactly like a ContainerOrderFocusTraversalPolicy. If, however, the Component is relying on default focusability, then a DefaultFocusTraversalPolicy will reject all Components with non-focusable peers.
    The focusability of a peer is implementation-dependent. Sun recommends that all implementations for a particular native platform construct peers with the same focusability. The recommendations for Windows and Unix are that Canvases, Labels, Panels, Scrollbars, ScrollPanes, Windows, and lightweight Components have non-focusable peers, and all other Components have focusable peers. These recommendations are used in the Sun AWT implementations. Note that the focusability of a Component's peer is different from, and does not impact, the focusability of the Component itself.

Swing provides two additional, standard FocusTraversalPolicy implementations for use by client code. Each implementation is an InternalFrameFocusTraversalPolicy.

  1. SortingFocusTraversalPolicy: Determines traversal order by sorting the Components of a focus traversal cycle based on a given Comparator. Each Component is tested for fitness using the accept(Component) method. By default, a Component is fit only if it is visible, displayable, enabled, and focusable.
  2. By default, SortingFocusTraversalPolicy implicitly transfers focus down-cycle. That is, during normal forward focus traversal, the Component traversed after a focus cycle root will be the focus-cycle-root's default Component to focus, regardless of whether the focus cycle root is a traversable or non-traversable Container (see the pic.1,2 below). Such behavior provides backward compatibility with applications designed without the concepts of up- and down-cycle traversal.
  3. LayoutFocusTraversalPolicy: A subclass of SortingFocusTraversalPolicy which sorts Components based on their size, position, and orientation. Based on their size and position, Components are roughly categorized into rows and columns. For a Container with horizontal orientation, columns run left-to-right or right-to-left, and rows run top-to-bottom. For a Container with vertical orientation, columns run top-to-bottom and rows run left-to-right or right-to-left. All columns in a row are fully traversed before proceeding to the next row.
    In addition, the fitness test is extended to exclude JComponents that have or inherit empty InputMaps.

The figure below shows an implicit focus transfer:
Implicit focus transfer.
Assume the following:

Swing applications, or mixed Swing/AWT applications, that use one of the standard look and feels, or any other look and feel derived from BasicLookAndFeel, will use LayoutFocusTraversalPolicy for all Containers by default.

All other applications, including pure AWT applications, will use DefaultFocusTraversalPolicy by default.

Focus Traversal Policy Providers

A Container that isn't a focus cycle root has an option to provide a FocusTraversalPolicy of its own. To do so, one needs to set Container's focus traversal policy provider property to true with the call to

Container.setFocusTraversalPolicyProvider(boolean)
To determine whether a Container is a focus traversal policy provider, the following method should be used:
Container.isFocusTraversalPolicyProvider()
If focus traversal policy provider property is set on a focus cycle root, it isn't considered a focus traversal policy provider and behaves just like any other focus cycle root.

The main difference between focus cycle roots and focus traversal policy providers is that the latter allow focus to enter and leave them just as all other Containers. However, children inside focus traversal policy provider are traversed in the order determined by provider's FocusTraversalPolicy. In order to enable focus traversal policy providers to behave this way, FocusTraversalPolicies treat them in the following manner: