Guardian667
Guardian667

Reputation: 452

Suppress child node from being informed about an event without consuming the same one

Situation

Let's say you have a Scene which contains a single complex control that itself contains a TextArea. The complex control is composed in a way, that when it gains the focus the TextArea gains the focus.

The ComplexControl has the ability to make the TextArea non-editable. Then a key input can has another semantic than operating a text. The new semantic can be defined by the ComplexControl or any node soemwhat higher in the scene graph.

The Scene has a global shortcut CTRL+N for opening a new Tab/View. In context of the ComplexControl CRTL+N semantic is creating a new Text document.

------------------------
| Scene                |
|  ------------------  |
|  | ComplexControl |  |
|  |  ------------  |  |
|  |  | TextArea |  |  |
|  |  ------------  |  |
|  ------------------  |
------------------------

Objective

React globally or by a node somewhat higher in the scene graph on KeyEvents / KeyCombinations. Any control somewhat lower can take over the events, so that in a more special context an occurred event has more relevance as globally defined.

Suggestion

Setting a KeyHandler onto the Scene or a higher Node. Any EventHandler closer to the Source/Target of the EventDispatchChain" can consume the event. This way the KeyHandler can prevent the Scene or any higher Node from reacting on the key input, which is what the user intends in the special context of a control. Therefore an EventFilter at the higher place is not suitable.

Trouble

The TextArea consumes always any key event, even if it's not the intention of the design. No EventHandler higher in the EventDispatchChain gets informed.

Problem

  1. How can it be forced to give the event back from the TextArea without consuming it and then let it bubble up the EventDispatchChain?
  2. How can delivering the Event down to the TextArea be prevented without consuming it, so that it not even knows anything about the event.

Upvotes: 2

Views: 84

Answers (1)

Guardian667
Guardian667

Reputation: 452

Thanx to sillyfly, who showed me the way. As an answer to question 2, here's the resulting source. I decided to place the functionality at a central point for reuse and give the opportunity to make the suppression condition based. Please don't blame me for the class name, that's not the point ;-)

public class EventUtil {

  /**
   * Prevents Events of the given <b>eventTypes</b> to be passed down during the
   * event capturing phase, if the <b>condition</b> is met.
   * 
   * @param node
   *          The Node, whose descendants should not be informed about the Event
   * @param eventTypes
   *          The types of Events for which the prevention should be valid
   * @param condition
   *          The condition that must be met to prevent passing the Event down
   */
  public static void preventPassDown(Node node, Supplier<Boolean> condition, EventType<?>... eventTypes) {
    for (EventType<?> eventType : eventTypes) {
      if (eventTypes == null) {
        return;
      }
      node.addEventFilter(eventType, event -> {
        if (condition.get()) {
          event.consume();
          Parent parent = node.getParent();
          if (parent != null) {
            Event.fireEvent(parent, event);
          }
        }
      });
    }
  }

  /**
   * Prevents Events of the given <b>eventTypes</b> to be passed down during the
   * event capturing phase.
   * 
   * @param node
   *          The Node, whose descendants should not be informed about the Event
   * @param eventTypes
   *          The types of Events for which the prevention should be valid
   */
  public static void preventPassDown(Node node, EventType<?>... eventTypes) {
    preventPassDown(node, () -> true, eventTypes);
  }

If someone finds an answer to question 1, please feel free to jump in.

Upvotes: 1

Related Questions