-
Bug
-
Resolution: Unresolved
-
P3
-
jfx11
The problem arises when we have some entity that fires an actionEvent and wants to fork further processing based on the consumed flag of the event. If the action that it fires is not consumed, further processing is incorrect.
The example demonstrates this on a textField:
- as sender, it has a keyPressed handler, fires an actionEvent and prints out the consumed flag
- as receiver it has an action handler that consumes the event
To reproduce:
- compile and run the example as-is
- expected and actual: the fired event is consumed
A:
- uncomment the lines that add an eventFilter to the stage
- expected: fired event is consumed
- actual: fired event it not consumed
B:
- also uncomment the line that removes the eventFilter immediately
- expected: fired event is consumed
- actual: fired event it not consumed
Technically, the reason is that the fired and the received actionEvent are different instances if an eventFilter (for eventType action or above) is present in the dispatch chain: EventHandlerManager.dispatchCapturingEvent dispatches a copy of the fired events, the receiver consumes the copy which - obviously - has no effect on the originally fired action.
Not entirely certain how to fix that
A: as of JDK-8091151, the event dispatch across the hierarchy might have to be handled differently for some - here action - events
B: due toJDK-8092352, the event is copied even if an eventFilter is removed again - the compositeEventHandler only nulls its records (and handler) and eventHandlerManager doesn't check if there are any filters:
private Event dispatchCapturingEvent(
final EventType<? extends Event> handlerType, Event event) {
final CompositeEventHandler<? extends Event> compositeEventHandler =
eventHandlerMap.get(handlerType);
if (compositeEventHandler != null) {
// TODO: skip when no filters are registered in the
// CompositeEventHandler (RT-23952) now JDK-8092352
event = fixEventSource(event, eventSource);
compositeEventHandler.dispatchCapturingEvent(event);
}
return event;
}
P3 because there are some bugs around textField with/out formatter where the incorrect consumed flag leads to problems, f.i.JDK-8207774 and its related issues
The example:
public class ActionDispatchBug extends Application {
public Parent createContent() {
TextField field = new TextField();
// some handler to fire an actionEvent
field.addEventHandler(KeyEvent.KEY_PRESSED, e -> {
if (e.getCode() == KeyCode.A) {
ActionEvent action = new ActionEvent(field, field);
field.fireEvent(action);
LOG.info("action fired " + action.isConsumed() + " @" + action.hashCode());
}
});
// another handler to consume the fired action
field.addEventHandler(ActionEvent.ACTION, action -> {
action.consume();
LOG.info("action consumed: " + " @" + action.hashCode() );
});
VBox actionUI = new VBox(field);
return actionUI;
}
@Override
public void start(Stage stage) throws Exception {
Scene scene = new Scene(createContent());
stage.setScene(scene);
// add/remove an eventFilter
// EventHandler filter = e -> {};
// stage.addEventFilter(EventType.ROOT, filter);
// stage.removeEventFilter(EventType.ROOT, filter);
//stage.setTitle(FXUtils.version());
stage.setX(100);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
@SuppressWarnings("unused")
private static final Logger LOG = Logger
.getLogger(ActionDispatchBug.class.getName());
}
The example demonstrates this on a textField:
- as sender, it has a keyPressed handler, fires an actionEvent and prints out the consumed flag
- as receiver it has an action handler that consumes the event
To reproduce:
- compile and run the example as-is
- expected and actual: the fired event is consumed
A:
- uncomment the lines that add an eventFilter to the stage
- expected: fired event is consumed
- actual: fired event it not consumed
B:
- also uncomment the line that removes the eventFilter immediately
- expected: fired event is consumed
- actual: fired event it not consumed
Technically, the reason is that the fired and the received actionEvent are different instances if an eventFilter (for eventType action or above) is present in the dispatch chain: EventHandlerManager.dispatchCapturingEvent dispatches a copy of the fired events, the receiver consumes the copy which - obviously - has no effect on the originally fired action.
Not entirely certain how to fix that
A: as of JDK-8091151, the event dispatch across the hierarchy might have to be handled differently for some - here action - events
B: due to
private Event dispatchCapturingEvent(
final EventType<? extends Event> handlerType, Event event) {
final CompositeEventHandler<? extends Event> compositeEventHandler =
eventHandlerMap.get(handlerType);
if (compositeEventHandler != null) {
// TODO: skip when no filters are registered in the
// CompositeEventHandler (
event = fixEventSource(event, eventSource);
compositeEventHandler.dispatchCapturingEvent(event);
}
return event;
}
P3 because there are some bugs around textField with/out formatter where the incorrect consumed flag leads to problems, f.i.
The example:
public class ActionDispatchBug extends Application {
public Parent createContent() {
TextField field = new TextField();
// some handler to fire an actionEvent
field.addEventHandler(KeyEvent.KEY_PRESSED, e -> {
if (e.getCode() == KeyCode.A) {
ActionEvent action = new ActionEvent(field, field);
field.fireEvent(action);
LOG.info("action fired " + action.isConsumed() + " @" + action.hashCode());
}
});
// another handler to consume the fired action
field.addEventHandler(ActionEvent.ACTION, action -> {
action.consume();
LOG.info("action consumed: " + " @" + action.hashCode() );
});
VBox actionUI = new VBox(field);
return actionUI;
}
@Override
public void start(Stage stage) throws Exception {
Scene scene = new Scene(createContent());
stage.setScene(scene);
// add/remove an eventFilter
// EventHandler filter = e -> {};
// stage.addEventFilter(EventType.ROOT, filter);
// stage.removeEventFilter(EventType.ROOT, filter);
//stage.setTitle(FXUtils.version());
stage.setX(100);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
@SuppressWarnings("unused")
private static final Logger LOG = Logger
.getLogger(ActionDispatchBug.class.getName());
}
- relates to
-
JDK-8207774 TextField: must not forward ENTER if actionHandler consumed the actionEvent
- Resolved
-
JDK-8091151 Some Events shouldn't go through the EventTarget hierarchy when fired
- Open
-
JDK-8092352 Skip event dispatch if there are no handlers/filters
- Resolved