Class KeyboardFocusManager

java.lang.Object
vaadinx.awt.KeyboardFocusManager
All Implemented Interfaces:
Serializable
Direct Known Subclasses:
FocusManager

public class KeyboardFocusManager extends Object implements Serializable
Emulator for KeyboardFocusManager — who has focus, and moving it on (D_focus_managers). Concrete, where the JDK's is abstract, so the statics have something to hand out. The auto-advance idiom, from inside a Document that has no back-reference to its own component:
Component owner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
if (owner instanceof JTextComponent text && text.getDocument() == this) {
    owner.transferFocus();          // → focusNextComponent(owner)
}

Real: the focus-owner and window queries, and single-step traversal. WARNing (R_match_swing_errors sub-bucket (b)): key-event dispatch, focus cycle roots, FocusTraversalPolicy as a pluggable object, the protected setGlobal* mutators, and the vetoable-listener registry — the browser owns key routing and Tab order.

Stateless facade; the state is per UI. Every method resolves UI.getCurrent() at call time and reads FocusTracker's per-UI pointer, so this process-wide instance holds nothing UI-bound — which is what makes static final FocusManager FM = FocusManager.getCurrentManager(); safe to write. Per-UI instances would turn that idiom into a cross-tab bug the static-field sweep cannot catch, since a focus manager is not a Component.

Bound properties are per UI, and only the four the getters can back. The listener registry lives in the current UI's data rather than on this process-wide facade — a PropertyChangeSupport field here would pool every session's listeners on one list and deliver every tab's focus change to all of them, the same cross-tab hazard the paragraph above rules out for state:

FocusManager.getCurrentManager().addPropertyChangeListener("focusOwner",
        e -> log.info("focus: {} -> {}", e.getOldValue(), e.getNewValue()));

focusOwner, permanentFocusOwner, focusedWindow and activeWindow fire off FocusTracker's pointer move — the place the real transition happens, so what the event announces is what getFocusOwner() answers a line later. defaultFocusTraversalPolicy fires from its own setter. The remaining three JDK properties stay silent because their getters would deny the transition, the reasoning D_reverse_fanout_rows applied to JPopupMenu.setVisible: currentFocusCycleRoot and the traversal-key sets have no state behind them (tier 3 below), and setCurrentKeyboardFocusManager does not swap the manager, so a managingFocus event would report a handover that did not happen.

Vetoable properties are not honoured — addVetoableChangeListener(VetoableChangeListener) still WARNs and registers nothing. The JDK vetoes before the change; here the browser has already moved focus by the time the server hears about it, so a veto could only be implemented as a focus-restoring effect, which is a different thing from the notification R_decline_effect_only owes.

See Also:
  • Field Details

    • FORWARD_TRAVERSAL_KEYS

      public static final int FORWARD_TRAVERSAL_KEYS
      See Also:
    • BACKWARD_TRAVERSAL_KEYS

      public static final int BACKWARD_TRAVERSAL_KEYS
      See Also:
    • UP_CYCLE_TRAVERSAL_KEYS

      public static final int UP_CYCLE_TRAVERSAL_KEYS
      See Also:
    • DOWN_CYCLE_TRAVERSAL_KEYS

      public static final int DOWN_CYCLE_TRAVERSAL_KEYS
      See Also:
  • Constructor Details

    • KeyboardFocusManager

      public KeyboardFocusManager()
  • Method Details

    • getCurrentKeyboardFocusManager

      public static KeyboardFocusManager getCurrentKeyboardFocusManager()
      The shared stateless facade; never null.
    • setCurrentKeyboardFocusManager

      public static void setCurrentKeyboardFocusManager(KeyboardFocusManager manager)
      WARN and ignore. A replacement manager's only overridable hooks are the ones this class WARNs on anyway (key dispatch, traversal policy), so honouring the swap would advertise more working behaviour than exists. Revisit if a real migrated app turns up subclassing a focus manager.
    • getFocusOwner

      public Component getFocusOwner()
      The emulator that owns browser focus in the current UI, or null when focus sits outside our component tree (on <body>, or on a Vaadin component the app added outside vaadinx).

      Resolved through EHelper.emulatorFor(Component), so focus landing on the inner field of a composite peer answers with the emulator that owns the composite.

    • getPermanentFocusOwner

      public Component getPermanentFocusOwner()
      Same answer as getFocusOwner(). The JDK's split exists for temporary focus loss (a scrollbar drag, a native popup stealing focus and giving it back); the browser has no such concept, so every focus change we see is permanent.
    • getFocusedWindow

      public Window getFocusedWindow()
      The Window containing the focus owner — found by walking the emulator parent chain, so a JDialog, JWindow or JInternalFrame overlay wins over the frame behind it.

      The walk needs no special case for the @MainWindow frame: that frame peers on an SJPanel rather than a dialog (D_frame_strategy), but it is still a JFrame and therefore still a Window emulator in the parent chain, so the walk finds it like any other. null means nothing is focused, or the focus owner is not parented under a Window yet — the same answer the JDK gives.

    • getActiveWindow

      public Window getActiveWindow()
      Same answer as getFocusedWindow(): a browser tab has exactly one active window by construction, so the JDK's focused-vs-active distinction (which separates a focused owner-less Window from the Frame that activates with it) has nothing to distinguish here.
    • clearFocusOwner

      public void clearFocusOwner()
      Drops focus entirely: clears the server-side pointer and blurs the browser's activeElement, so the two stay in agreement.
    • clearGlobalFocusOwner

      public void clearGlobalFocusOwner()
      Same as clearFocusOwner(). The JDK's split is about which AppContext may clear whose focus owner — a distinction with no analogue in a single browser tab.
    • focusNextComponent

      public void focusNextComponent(Component component)
      Moves focus to the next focusable component after component, wrapping at the end; silently stays put when there is no other candidate (as the JDK does). The engine behind Component.transferFocus().

      Order comes from the emulator tree, not the browser's Tab order — a depth-first pre-order walk from the top-level ancestor. The two diverge only where DOM order or an explicit tabindex disagrees with our child order, which R_layouts_close_enough's close-enough licence covers; a browser round trip for "next tabbable element" would agree by construction but be async and untestable browserless.

    • focusPreviousComponent

      public void focusPreviousComponent(Component component)
      Backward counterpart of focusNextComponent(Component).
    • focusNextComponent

      public final void focusNextComponent()
      Operates on the current focus owner; no-op when nothing is focused.
    • focusPreviousComponent

      public final void focusPreviousComponent()
      Operates on the current focus owner; no-op when nothing is focused.
    • upFocusCycle

      public void upFocusCycle(Component component)
    • downFocusCycle

      public void downFocusCycle(Container container)
    • upFocusCycle

      public final void upFocusCycle()
    • downFocusCycle

      public final void downFocusCycle()
    • getCurrentFocusCycleRoot

      public Container getCurrentFocusCycleRoot()
    • setGlobalCurrentFocusCycleRoot

      public void setGlobalCurrentFocusCycleRoot(Container container)
    • getGlobalCurrentFocusCycleRoot

      protected Container getGlobalCurrentFocusCycleRoot()
    • getDefaultFocusTraversalPolicy

      public FocusTraversalPolicy getDefaultFocusTraversalPolicy()
      The installed default policy, or null when none was set — never a fabricated one, since there is no traversal engine whose behaviour a default would describe.
    • setDefaultFocusTraversalPolicy

      public void setDefaultFocusTraversalPolicy(FocusTraversalPolicy policy)
      Stores the policy; nothing dispatches on it (D_focus_managers tier 3). Safe to hold on this process-wide facade where per-UI state would not be — the JDK scopes this default per AppContext, i.e. per application, and a policy is a stateless strategy object rather than anything tab-bound.

      Fires "defaultFocusTraversalPolicy" to the current UI's listeners, which is narrower than the value it announces: the field is process-wide, so another tab's listeners miss a change they can read back. Per-UI is the widest delivery a tab-scoped registry can offer, and the alternative — reaching into every session's listener list from one request thread — is the cross-tab hazard this class exists to avoid.

      Throws:
      IllegalArgumentException - if policy is null, as the JDK does
    • getDefaultFocusTraversalKeys

      public Set<AWTKeyStroke> getDefaultFocusTraversalKeys(int id)
      Empty set, no WARN — the same honest answer Component.getFocusTraversalKeys(int) gives: the browser owns the Tab key, so there are no server-side traversal keystrokes to report.
    • setDefaultFocusTraversalKeys

      public void setDefaultFocusTraversalKeys(int id, Set<? extends AWTKeyStroke> keystrokes)
    • getGlobalFocusOwner

      protected Component getGlobalFocusOwner()
    • setGlobalFocusOwner

      protected void setGlobalFocusOwner(Component component)
    • getGlobalPermanentFocusOwner

      protected Component getGlobalPermanentFocusOwner()
    • setGlobalPermanentFocusOwner

      protected void setGlobalPermanentFocusOwner(Component component)
    • getGlobalFocusedWindow

      protected Window getGlobalFocusedWindow()
    • setGlobalFocusedWindow

      protected void setGlobalFocusedWindow(Window window)
    • getGlobalActiveWindow

      protected Window getGlobalActiveWindow()
    • setGlobalActiveWindow

      protected void setGlobalActiveWindow(Window window)
    • dispatchEvent

      public boolean dispatchEvent(AWTEvent event)
    • redispatchEvent

      public final void redispatchEvent(Component target, AWTEvent event)
    • dispatchKeyEvent

      public boolean dispatchKeyEvent(KeyEvent event)
    • postProcessKeyEvent

      public boolean postProcessKeyEvent(KeyEvent event)
    • processKeyEvent

      public void processKeyEvent(Component focusedComponent, KeyEvent event)
    • enqueueKeyEvents

      protected void enqueueKeyEvents(long after, Component untilFocused)
    • dequeueKeyEvents

      protected void dequeueKeyEvents(long after, Component untilFocused)
    • discardKeyEvents

      protected void discardKeyEvents(Component component)
    • addPropertyChangeListener

      public void addPropertyChangeListener(PropertyChangeListener listener)
      Registers for every bound property of this manager, in the current UI:
      FocusManager.getCurrentManager().addPropertyChangeListener("focusOwner",
              e -> statusBar.setText("focus: " + e.getNewValue()));
      

      Per UI, not process-wide. The JDK scopes these listeners per AppContext; a browser tab is the closest thing SB-Emulators has, so a listener registered from one tab never hears another's focus changes — the same call FocusTracker makes for the pointer itself. A registration made with no current UI is dropped with a WARN, since there is no tab to scope it to.

    • addPropertyChangeListener

      public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener)
      See Also:
    • removePropertyChangeListener

      public void removePropertyChangeListener(PropertyChangeListener listener)
    • removePropertyChangeListener

      public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener)
    • getPropertyChangeListeners

      public PropertyChangeListener[] getPropertyChangeListeners()
    • getPropertyChangeListeners

      public PropertyChangeListener[] getPropertyChangeListeners(String propertyName)
    • firePropertyChange

      protected void firePropertyChange(String propertyName, Object oldValue, Object newValue)
      Delivers to the current UI's listeners, or nowhere when none registered in this tab.

      The oldValue == newValue short-circuit is the JDK's own, and is reference identity — a stricter filter than the equals dedupe PropertyChangeSupport then applies, and the reason a hand-written (null, null) defeats both.

    • addVetoableChangeListener

      public void addVetoableChangeListener(VetoableChangeListener listener)
    • addVetoableChangeListener

      public void addVetoableChangeListener(String propertyName, VetoableChangeListener listener)
    • removeVetoableChangeListener

      public void removeVetoableChangeListener(VetoableChangeListener listener)
    • removeVetoableChangeListener

      public void removeVetoableChangeListener(String propertyName, VetoableChangeListener listener)
    • getVetoableChangeListeners

      public VetoableChangeListener[] getVetoableChangeListeners()
    • getVetoableChangeListeners

      public VetoableChangeListener[] getVetoableChangeListeners(String propertyName)
    • fireVetoableChange

      protected void fireVetoableChange(String propertyName, Object oldValue, Object newValue) throws PropertyVetoException
      Throws:
      PropertyVetoException