Thanks, this write-up is really helpful. It sounds like we have two use cases.
- In JavaFX ComboBox and Spinner both need to redirect focus from a sub-component upward to the delegating control. For simple controls like buttons they capture events that might change focus (like mouse clicks). The TextField is more complex so there they override requestFocus. They don’t need to deal with traversal; they simply ensure that there’s no way to traverse to a sub-component. - In your library it sounds like you’re overriding requestFocus to make up for your lack of control over the traversal machinery. You have no control over how the traversal engine chooses a new node but by overriding requestFocus you can redirect focus after the traversal engine has made its choice. Conversations about opening up the traversal engine tend to fall apart because it all gets too complex. I don’t think we ever discussed a much simpler but more limited approach: after the traversal engine has made its choice it could call on the node to fine-tune it. A crude solution would be to allow nodes to override requestFocusVisible but we could probably craft a better API. (There’s another issue in the traversal engine that’s affecting ComboBox and Spinner. The engine begins traversal starting at the KeyEvent’s target and for these controls that target might be the inner TextField. I think that’s the wrong starting point and the traversal engine should start at the scene’s focus owner instead.) Martin > On Jul 30, 2026, at 4:57 AM, Florian Kirmaier <[email protected]> > wrote: > > Yes, it was never specified that it worked that way. > Still, it worked from JavaFX 8 through 18, and the change hit silently. > Now there is basically no reason anymore to override it at all. > > > Typical Use Cases > > Usually it's done for focus delegation. > Similar to the FakeFocusTextField - just without faking it. > Most often, it's used to delegate the focus to an inner subcomponent. > > That's also what's done in the codebase I'm working with. > > Overriding requestFocus is not useful anymore > > At the moment, requestFocus works reliably for mouse input but not for > keyboard events. > A mechanism that only works reliably in one case but not in another > automatically leads to > corner cases when used. > So there is basically no point anymore in overriding it. > If we don't improve the API, this should at least be documented. > > How common is it > > I scanned 44 JavaFX libraries and applications and found 9 genuine overrides > in three > codebases: OpenJFX (FakeFocusTextField), JFoenix (copied it almost verbatim as > FakeFocusJFXTextField), and JabRef (five layout panes forwarding focus > inward). > > But in the codebase I'm working with, I see about 50 cases. > So it's rare in libraries, but it might be more common in application code. > > Documenting that overriding is unsupported would help > > We probably can't make the method final, because it would be a > source-incompatible change. > But we could document that it's no longer supposed to be overridden (if we > don't improve > the API). > > Maybe something like the following: > > * @apiNote Overriding this method is not supported, as not all focus changes > are > * routed through it; focus acquired by keyboard traversal is set directly. > * To observe focus changes, listen to {@link Scene#focusOwnerProperty()}. > > Other focus-related methods aren't overridable either: > Node.requestFocusTraversal is final, > and Node.setFocused is protected final. > So documenting this would just make the existing direction explicit. > > Need for better API exists > > The recurring discussions regarding focus are probably an indication that all > this is > important. > > Spinner and ComboBoxPopupControl (used by ComboBox and DatePicker) both block > traversal > into their own subtree by installing a custom Algorithm for the > ParentTraversalEngine > which returns null. > > But that is an internal API, not accessible to application developers without > --add-exports. > > The project I currently work on uses it too, accessed that way. > > Andy's draft PR covers it: > https://github.com/andy-goryachev-oracle/jfx/pull/14 > It adds a public javafx.scene.TraversalPolicy and Parent.traversalPolicy, and > it already > replaces the Algorithm in SpinnerSkin with a single setTraversalPolicy(...) > call. > That looks like exactly the right direction. > > > Greetings > > Florian Kirmaier > > On Thu, 30 Jul 2026 at 02:34, Martin Fox <[email protected] > <mailto:[email protected]>> wrote: >> requestFocus() is called *before* a node receives focus. Given the way it’s >> named it certainly looks like a way of initiating focus negotiation, not >> finalizing it. And it’s been possible to override this routine and re-direct >> focus since at least JavaFX 8. There’s one instance in Controls that uses it >> for exactly this purpose (the dreaded FakeFocusTextField). >> >> With that said it’s a very weak framework for focus negotiation. For example >> it doesn’t allow ComboBox and Spinner to re-direct focus away from >> sub-nodes. I can think of a few message-based ways of negotiating focus that >> would cover a lot more ground. And for API compatibility I would probably >> initiate those negotiations from requestFocus(). It is the beginning of >> establishing focus, not the end. The end of focus negotiation (such as it >> is) occurs when the node becomes the scene’s focus owner. There’s no direct >> way for a node to determine when that happens beyond monitoring the scene’s >> focusOwner property. >> >> Florian, why are you overriding requestFocus() to begin with? >> >> Martin >> >> > On Jul 29, 2026, at 5:14 PM, Kevin Rushforth <[email protected] >> > <mailto:[email protected]>> wrote: >> > >> > I said something similar in the JBS issue about this being previously >> > undocumented behavior. I agree with Michael on this issue. >> > >> > -- Kevin >> > >> > >> > On 7/29/2026 12:19 PM, Michael Strauß wrote: >> >> requestFocus() was never specified to be called when focus changed, >> >> which is also reflected in its name: it's not called onFocusChanged() >> >> or something like it, it suggests a request only. >> >> >> >> You point out that previously, an application could "decide what to do >> >> when a node receives focus. For example, a node could pass the focus >> >> on to another one." >> >> >> >> I don't think that we should be supporting any attempt to subvert the >> >> focus system, it's just not worth it. The Node.setFocused() situation >> >> is bad enough as it is. Most legitimate use cases for hacks like that >> >> can be solved by a focus delegation system, which seems to be due for >> >> our annual discussion round. >> >> >> >> So I'm in favor of doing nothing here, except maybe documenting that >> >> you shouldn't override requestFocus(). >> > >>
