Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8229467

Fired ActionEvent not consumed if (unrelated!) EventFilter in dispatch chain

    Details

      Description

      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 to JDK-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());

      }

        Attachments

          Issue Links

            Activity

              People

              • Assignee:
                aghaisas Ajit Ghaisas
                Reporter:
                fastegal Jeanette Winzenburg
              • Votes:
                0 Vote for this issue
                Watchers:
                1 Start watching this issue

                Dates

                • Created:
                  Updated: