*muttering to myself ..
Meanwhile, my understanding evolved a bit - there are actually two
refiring scenarios, both wrecking the event dispatch sequence, the
first more evil than the second
1. getParent.fireEvent(receivedKeyEvent) which leads to serious
misbehaviour (as reported f.i. in
https://bugs.openjdk.java.net/browse/JDK-8207759 ) - fix understood,
implemented and waiting for review
2. child.fireEvent(receivedKeyEvent) which leads to misbehavior (as
reported in https://bugs.openjdk.java.net/browse/JDK-8229924 ) - still
don't know how to fix it: the child will never get a keyEvent
"naturally" because it's never focusOwner. So the parent must deliver
it somehow .. Currently experimenting with custom implementation of
buildEventDispatchChain (append the editor's chain? or a custom
dispatcher doing so?)
More questions than answers ;)
-- Jeanette
Zitat von Jeanette Winzenburg <faste...@swingempire.de>:
While fixing https://bugs.openjdk.java.net/browse/JDK-8207759 it
turned out that the underlying reason for the bug was a broken event
dispatch sequence introduced by behavior.forwardToParent. Which is a
call to parent.fireEvent with the event that was received. This
builds a nested chain and delivers the event to all handlers in
that new chain - down and up again - _before_ the current chain is
completed. Consequently, the consuming singleton handler for the
same event is notified _after_ the scene-level handlers (in
particular the accelerators) have seen and handled it.
Looks like it happens for any control (not only for a TextField as
in the referenced issue, nor only for controls with a
FakeFocusTextField which refire while processing keyEvents), as the
example below demonstrates.
My current understanding of event dispatch is, that a chain has a
life-cycle consisting of separate (?) states:
- building the chain from eventTargets
- delivering the event along the dispatchers in the chain
There's a contract for dispatch sequence (like capturing/bubbling
phase, dispatch from specialized to super event types and other
rules). That can be guaranteed as long as chain building and event
delivering are separate phases, it seems to break down if they are
mixed (there are other issues with a similar/same underlying reason,
f.i. in all controls with a FakeFocusTextField).
Now the questions:
- is there any specification about not mixing the life-cycle states?
if so, where?
- or is there a way to safely re-fire an event at the moment of receiving it?
- or maybe I got it all wrong, if so please guide me :)
-- Cheers, Jeanette
The example:
public class NestedEventDispatchChain extends Application {
private KeyCode key = DIGIT1;
private Parent createContent() {
Button button = new Button("the evil button!");
// re-firing handler
button.addEventHandler(KEY_PRESSED, e -> {
if (key == e.getCode()) {
System.out.println("before refire " + e);
button.getParent().fireEvent(e);
System.out.println("after refire " + e);
}
});
// consuming singleton handler
button.setOnKeyPressed(e -> {
if (key == e.getCode()) {
e.consume();
System.out.println("consumed in singleton " + e.getCode());
}
});
BorderPane content = new BorderPane(button);
return content;
}
@Override
public void start(Stage stage) throws Exception {
Scene scene = new Scene(createContent());
// accelerator that shouldn't be triggered because singleton
handler consumed
scene.getAccelerators().put(KeyCombination.keyCombination(key.getName()), ()
-> {
System.out.println("accelerator triggered for " + key);
});
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}