> Consider the following scenario: a compound control such as ComboBox, which > has an editable TextField control as a part of its skin. It might be > expected that the ComboBox shows the focused border instead of its TextField > even when the latter has the input focus. > > When the user presses and releases a key, the key events are first dispatched > to the input focus owner which is the TextField. This part is correct.
The focus owner in your example is the ComboBox, not the TextField. The fact that the ComboBox skin uses a TextField in its implementation is not a part of the user model. From the user's perspective, it is a monolithic control with an unknown internal structure. Now, if you click on a ComboBox, the only logical thing that can happen is that the ComboBox receive the input focus. More precisely, the following things must be true: <ComboBox>.focused == true Scene.focusOwner == <ComboBox> This is how JavaFX is currently implemented. However, since JavaFX doesn't provide the necessary tools to support these semantics, the actual implementations are decidedly ad-hoc and held together by hacks. > The issue we seem to be struggling with is that, unlike the case when > TextField is used as a top level control, now it is a part of a ComboBox. > Which means it should not handle some keys/events, instead delegating them to > the top level control - there should be a single "controller" (in MVC > parlance), instead of two fighting each other. This statement applies to > built-in controls as well as the custom controls. > > One way to accomplish that is to register a bunch of event filters with the > top level control, to prevent the events to arrive at the inner control, > forwarding these events to the top level controller, which in JavaFX case is > the behavior. Since the FakeFocusTextField that is contained in the ComboBox is not the focus owner, it is not the target of input events. Delivering events to the text field in order to make it work currently requires the skin to fire a new event that is targeted at the text field. This can be observed externally, and is an obvious defect (user presses a single key, observers will see two key presses). > The other way which I think will be easier, is to use the proposed InputMap > to remove the undesired mappings from the inner control. Doing so does not > involve subclassing of the inner controls, since the input map and the > mappings will be a public API. With the mappings disabled (or remapped to > the functions provided by the top level control), there is no contention > between the two anymore. The top level control's controller is in full > control, it can do anything that's required to the inner controls - setting > pseudo styles, inserting text, etc. As an example - the right arrow key in > the combo box's text field can be remapped to a function which checks if the > caret is at the last position and then move the focus to the button, if so > desired. And if the caret is not at the last position, the default function > of text area is invoked - all that enabled by the InputMap. InputMap is only tangentially related to focus handling. Focus delegation solves a whole array of problems that appear when you try to implement a multi-level focus system, such as is the case in JavaFX. In addition to that, focus delegation and InputMap are different levels of abstraction: Focus delegation is a core feature of the framework (just like focus is a core feature), while InputMap is one feature of one implementation of controls. There is nothing special about javafx.controls or its particular flavor of controls, skins, and behaviors. Missing building blocks in the core framework can't be solved in a controls library.