All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.javafx.scene.control.inputmap.InputMap Maven / Gradle / Ivy

There is a newer version: 24-ea+15
Show newest version
/*
 * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package com.sun.javafx.scene.control.inputmap;

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.event.EventType;
import javafx.scene.Node;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.util.Pair;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * InputMap is a class that is set on a given {@link Node}. When the Node receives
 * an input event from the system, it passes this event in to the InputMap where
 * the InputMap can check all installed
 * {@link InputMap.Mapping mappings} to see if there is any
 * suitable mapping, and if so, fire the provided {@link EventHandler}.
 *
 * @param  The type of the Node that the InputMap is installed in.
 * @since 9
 */
public class InputMap implements EventHandler {

    /***************************************************************************
     *                                                                         *
     * Private fields                                                          *
     *                                                                         *
     **************************************************************************/

    private final N node;

    private final ObservableList> childInputMaps;

    private final ObservableList> mappings;

//    private final ObservableList> interceptors;

    private final Map, List>> installedEventHandlers;

    private final Map> eventTypeMappings;



    /***************************************************************************
     *                                                                         *
     * Constructors                                                            *
     *                                                                         *
     **************************************************************************/

    /**
     * Creates the new InputMap instance which is related specifically to the
     * given Node.
     * @param node The Node for which this InputMap is attached.
     */
    public InputMap(N node) {
        if (node == null) {
            throw new IllegalArgumentException("Node can not be null");
        }

        this.node = node;
        this.eventTypeMappings = new HashMap<>();
        this.installedEventHandlers = new HashMap<>();
//        this.interceptors = FXCollections.observableArrayList();

        // listeners
        this.mappings = FXCollections.observableArrayList();
        mappings.addListener((ListChangeListener>) c -> {
            while (c.next()) {
                // TODO handle mapping removal
                if (c.wasRemoved()) {
                    for (Mapping mapping : c.getRemoved()) {
                        removeMapping(mapping);
                    }
                }

                if (c.wasAdded()) {
                    List> toRemove = new ArrayList<>();
                    for (Mapping mapping : c.getAddedSubList()) {
                        if (mapping == null) {
                            toRemove.add(null);
                        } else {
                            addMapping(mapping);
                        }
                    }

                    if (!toRemove.isEmpty()) {
                        getMappings().removeAll(toRemove);
                        throw new IllegalArgumentException("Null mappings not permitted");
                    }
                }
            }
        });

        childInputMaps = FXCollections.observableArrayList();
        childInputMaps.addListener((ListChangeListener>) c -> {
            while (c.next()) {
                if (c.wasRemoved()) {
                    for (InputMap map : c.getRemoved()) {
                        map.setParentInputMap(null);
                    }
                }

                if (c.wasAdded()) {
                    List> toRemove = new ArrayList<>();
                    for (InputMap map : c.getAddedSubList()) {
                        // we check that the child input map maps to the same node
                        // as this input map
                        if (map.getNode() != getNode()) {
                            toRemove.add(map);
                        } else {
                            map.setParentInputMap(this);
                        }
                    }

                    if (!toRemove.isEmpty()) {
                        getChildInputMaps().removeAll(toRemove);
                        throw new IllegalArgumentException("Child InputMap intances need to share a common Node object");
                    }
                }
            }
        });
    }



    /***************************************************************************
     *                                                                         *
     * Properties                                                              *
     *                                                                         *
     **************************************************************************/

    // --- parent behavior - for now this is an private property
    private ReadOnlyObjectWrapper> parentInputMap = new ReadOnlyObjectWrapper<>(this, "parentInputMap") {
        @Override protected void invalidated() {
            // whenever the parent InputMap changes, we uninstall all mappings and
            // then reprocess them so that they are installed in the correct root.
            reprocessAllMappings();
        }
    };
    private final void setParentInputMap(InputMap value) { parentInputMap.set(value); }
    private final InputMap getParentInputMap() {return parentInputMap.get(); }
    private final ReadOnlyObjectProperty> parentInputMapProperty() { return parentInputMap.getReadOnlyProperty(); }


    // --- interceptor
    /**
     * The role of the interceptor is to block the InputMap on which it is
     * set from executing any mappings (contained within itself, or within a
     * {@link #getChildInputMaps() child InputMap}, whenever the interceptor
     * returns true. The interceptor is called every time an input event is received,
     * and is allowed to reason on the given input event
     * before returning a boolean value, where boolean true means block
     * execution, and boolean false means to allow execution.
     */
    private ObjectProperty> interceptor = new SimpleObjectProperty<>(this, "interceptor");
    public final Predicate getInterceptor() {
        return interceptor.get();
    }
    public final void setInterceptor(Predicate value) {
        interceptor.set(value);
    }
    public final ObjectProperty> interceptorProperty() {
        return interceptor;
    }



    /***************************************************************************
     *                                                                         *
     * Public API                                                              *
     *                                                                         *
     **************************************************************************/

    /**
     * The Node for which this InputMap is attached.
     */
    public final N getNode() {
        return node;
    }

    /**
     * A mutable list of input mappings. Each will be considered whenever an
     * input event is being looked up, and one of which may be used to handle
     * the input event, based on the specifificity returned by each mapping
     * (that is, the mapping with the highest specificity wins).
     */
    public ObservableList> getMappings() {
        return mappings;
    }

    /**
     * A mutable list of child InputMaps. An InputMap may have child input maps,
     * as this allows for easy addition
     * of mappings that are state-specific. For example, if a Node can be in two
     * different states, and the input mappings are different for each, then it
     * makes sense to have one root (and empty) InputMap, with two children
     * input maps, where each is populated with the specific input mappings for
     * one of the two states. To prevent the wrong input map from being considered,
     * it is simply a matter of setting an appropriate
     * {@link #interceptorProperty() interceptor} on each map, so that they are only
     * considered in one of the two states.
     */
    public ObservableList> getChildInputMaps() {
        return childInputMaps;
    }

    /**
     * Disposes all child InputMaps, removes all event handlers from the Node,
     * and clears the mappings list.
     */
    public void dispose() {
        for (InputMap childInputMap : getChildInputMaps()) {
            childInputMap.dispose();
        }

        // uninstall event handlers
        removeAllEventHandlers();

        // clear out all mappings
        getMappings().clear();
    }

    /** {@inheritDoc} */
    @Override public void handle(Event e) {
        if (e == null || e.isConsumed()) return;

        List> mappings = lookup(e, true);
        for (Mapping mapping : mappings) {
            EventHandler eventHandler = mapping.getEventHandler();
            if (eventHandler != null) {
                eventHandler.handle(e);
            }

            if (mapping.isAutoConsume()) {
                e.consume();
            }

            if (e.isConsumed()) {
                break;
            }

            // If we are here, the event has not been consumed, so we continue
            // looping through our list of matches. Refer to the documentation in
            // lookup(Event) for more details on the list ordering.
        }
    }

    /**
     * Looks up the most specific mapping given the input, ignoring all
     * interceptors. The valid values that can be passed into this method is
     * based on the values returned by the {@link Mapping#getMappingKey()}
     * method. Based on the subclasses of Mapping that ship with JavaFX, the
     * valid values are therefore:
     *
     * 
    *
  • KeyMapping: A valid {@link KeyBinding}.
  • *
  • MouseMapping: A valid {@link MouseEvent} event * type (e.g. {@code MouseEvent.MOUSE_PRESSED}).
  • *
* * For other Mapping subclasses, refer to their javadoc, and specifically * what is returned by {@link Mapping#getMappingKey()}, * * @param mappingKey * @return */ // TODO return all mappings, or just the first one? public Optional> lookupMapping(Object mappingKey) { if (mappingKey == null) { return Optional.empty(); } List> mappings = lookupMappingKey(mappingKey); // descend into our child input maps as well for (int i = 0; i < getChildInputMaps().size(); i++) { InputMap childInputMap = getChildInputMaps().get(i); List> childMappings = childInputMap.lookupMappingKey(mappingKey); mappings.addAll(0, childMappings); } return mappings.size() > 0 ? Optional.of(mappings.get(0)) : Optional.empty(); } /*************************************************************************** * * * Private implementation * * * **************************************************************************/ private List> lookupMappingKey(Object mappingKey) { return getMappings().stream() .filter(mapping -> !mapping.isDisabled()) .filter(mapping -> mappingKey.equals(mapping.getMappingKey())) .collect(Collectors.toList()); } /* * Returns a List of Mapping instances, in priority order (from highest priority * to lowest priority). All mappings in the list have the same value specificity, * so are ranked based on the input map (with the leaf input maps taking * precedence over parent / root input maps). */ private List> lookup(Event event, boolean testInterceptors) { // firstly we look at ourselves to see if we have a mapping, assuming our // interceptors are valid if (testInterceptors) { boolean interceptorsApplies = testInterceptor(event, getInterceptor()); if (interceptorsApplies) { return Collections.emptyList(); } } List> mappings = new ArrayList<>(); int minSpecificity = 0; List>> results = lookupMappingAndSpecificity(event, minSpecificity); if (! results.isEmpty()) { minSpecificity = results.get(0).getKey(); mappings.addAll(results.stream().map(pair -> pair.getValue()).collect(Collectors.toList())); } // but we always descend into our child input maps as well, to see if there // is a more specific mapping there. If there is a mapping of equal // specificity, we take the child mapping over the parent mapping. for (int i = 0; i < getChildInputMaps().size(); i++) { InputMap childInputMap = getChildInputMaps().get(i); minSpecificity = scanRecursively(childInputMap, event, testInterceptors, minSpecificity, mappings); } return mappings; } private int scanRecursively(InputMap inputMap, Event event, boolean testInterceptors, int minSpecificity, List> mappings) { // test if the childInputMap should be considered if (testInterceptors) { boolean interceptorsApplies = testInterceptor(event, inputMap.getInterceptor()); if (interceptorsApplies) { return minSpecificity; } } // look at the given InputMap List>> childResults = inputMap.lookupMappingAndSpecificity(event, minSpecificity); if (!childResults.isEmpty()) { int specificity = childResults.get(0).getKey(); List> childMappings = childResults.stream() .map(pair -> pair.getValue()) .collect(Collectors.toList()); if (specificity == minSpecificity) { mappings.addAll(0, childMappings); } else if (specificity > minSpecificity) { mappings.clear(); minSpecificity = specificity; mappings.addAll(childMappings); } } // now look at the children of this input map, if any exist for (int i = 0; i < inputMap.getChildInputMaps().size(); i++) { minSpecificity = scanRecursively(inputMap.getChildInputMaps().get(i), event, testInterceptors, minSpecificity, mappings); } return minSpecificity; } private InputMap getRootInputMap() { InputMap rootInputMap = this; while (true) { if (rootInputMap == null) break; InputMap parentInputMap = rootInputMap.getParentInputMap(); if (parentInputMap == null) break; rootInputMap = parentInputMap; } return rootInputMap; } private void addMapping(Mapping mapping) { InputMap rootInputMap = getRootInputMap(); // we want to track the event handlers we install, so that we can clean // up in the dispose() method (and also so that we don't duplicate // event handlers for a single event type). Because this is all handled // in the root InputMap, we firstly find it, and then we defer to it. rootInputMap.addEventHandler(mapping.eventType); // we maintain a separate map of all mappings, which maps from the // mapping event type into a list of mappings. This allows for easier // iteration in the lookup methods. EventType et = mapping.getEventType(); List _eventTypeMappings = this.eventTypeMappings.computeIfAbsent(et, f -> new ArrayList<>()); _eventTypeMappings.add(mapping); } private void removeMapping(Mapping mapping) { EventType et = mapping.getEventType(); if (this.eventTypeMappings.containsKey(et)) { List _eventTypeMappings = this.eventTypeMappings.get(et); _eventTypeMappings.remove(mapping); // TODO remove the event handler in the root if there are no more mappings of this type // anywhere in the input map tree } } private void addEventHandler(EventType et) { List> eventHandlers = installedEventHandlers.computeIfAbsent(et, f -> new ArrayList<>()); final EventHandler eventHandler = this::handle; if (eventHandlers.isEmpty()) { // System.out.println("Added event handler for type " + et); node.addEventHandler(et, eventHandler); } // We need to store these event handlers so we can dispose cleanly. eventHandlers.add(eventHandler); } private void removeAllEventHandlers() { for (EventType et : installedEventHandlers.keySet()) { List> handlers = installedEventHandlers.get(et); for (EventHandler handler : handlers) { // System.out.println("Removed event handler for type " + et); node.removeEventHandler(et, handler); } } } private void reprocessAllMappings() { removeAllEventHandlers(); this.mappings.stream().forEach(this::addMapping); // now do the same for all children for (InputMap child : getChildInputMaps()) { child.reprocessAllMappings(); } } private List>> lookupMappingAndSpecificity(final Event event, final int minSpecificity) { int _minSpecificity = minSpecificity; List mappings = this.eventTypeMappings.getOrDefault(event.getEventType(), Collections.emptyList()); List>> result = new ArrayList<>(); for (Mapping mapping : mappings) { if (mapping.isDisabled()) continue; // test if mapping has an interceptor that will block this event. // Interceptors return true if the interception should occur. boolean interceptorsApplies = testInterceptor(event, mapping.getInterceptor()); if (interceptorsApplies) { continue; } int specificity = mapping.getSpecificity(event); if (specificity > 0 && specificity == _minSpecificity) { result.add(new Pair<>(specificity, mapping)); } else if (specificity > _minSpecificity) { result.clear(); result.add(new Pair<>(specificity, mapping)); _minSpecificity = specificity; } } return result; } // Interceptors return true if the interception should occur. private boolean testInterceptor(Event e, Predicate interceptor) { return interceptor != null && interceptor.test(e); } /*************************************************************************** * * * Support classes * * * **************************************************************************/ /** * Abstract base class for all input mappings as used by the * {@link InputMap} class. * * @param The type of {@link Event} the mapping represents. */ public static abstract class Mapping { /*********************************************************************** * * * Private fields * * * **********************************************************************/ private final EventType eventType; private final EventHandler eventHandler; /*********************************************************************** * * * Constructors * * * **********************************************************************/ /** * Creates a new Mapping instance. * * @param eventType The {@link EventType} that is being listened for. * @param eventHandler The {@link EventHandler} to fire when the mapping * is selected as the most-specific mapping. */ public Mapping(final EventType eventType, final EventHandler eventHandler) { this.eventType = eventType; this.eventHandler = eventHandler; } /*********************************************************************** * * * Abstract methods * * * **********************************************************************/ /** * This method must be implemented by all mapping implementations such * that it returns an integer value representing how closely the mapping * matches the given {@link Event}. The higher the number, the greater * the match. This allows the InputMap to determine * which mapping is most specific, and to therefore fire the appropriate * mapping {@link Mapping#getEventHandler() EventHandler}. * * @param event The {@link Event} that needs to be assessed for its * specificity. * @return An integer indicating how close of a match the mapping is to * the given Event. The higher the number, the greater the match. */ public abstract int getSpecificity(Event event); /*********************************************************************** * * * Properties * * * **********************************************************************/ // --- disabled /** * By default all mappings are enabled (so this disabled property is set * to false by default). In some cases it is useful to be able to disable * a mapping until it is applicable. In these cases, users may simply * toggle the disabled property until desired. * *

When the disabled property is true, the mapping will not be * considered when input events are received, even if it is the most * specific mapping available.

*/ private BooleanProperty disabled = new SimpleBooleanProperty(this, "disabled", false); public final void setDisabled(boolean value) { disabled.set(value); } public final boolean isDisabled() {return disabled.get(); } public final BooleanProperty disabledProperty() { return disabled; } // --- auto consume /** * By default mappings are set to 'auto consume' their specified event * handler. This means that the event handler will not propagate further, * but in some cases this is not desirable - sometimes it is preferred * that the event continue to 'bubble up' to parent nodes so that they * may also benefit from receiving this event. In these cases, it is * important that this autoConsume property be changed from the default * boolean true to instead be boolean false. */ private BooleanProperty autoConsume = new SimpleBooleanProperty(this, "autoConsume", true); public final void setAutoConsume(boolean value) { autoConsume.set(value); } public final boolean isAutoConsume() {return autoConsume.get(); } public final BooleanProperty autoConsumeProperty() { return autoConsume; } /*********************************************************************** * * * Public API * * * **********************************************************************/ /** * The {@link EventType} that is being listened for. */ public final EventType getEventType() { return eventType; } /** * The {@link EventHandler} that will be fired should this mapping be * the most-specific mapping for a given input, and should it not be * blocked by an interceptor (either at a * {@link InputMap#interceptorProperty() input map} level or a * {@link Mapping#interceptorProperty() mapping} level). */ public final EventHandler getEventHandler() { return eventHandler; } // --- interceptor /** * The role of the interceptor is to block the mapping on which it is * set from executing, whenever the interceptor returns true. The * interceptor is called every time the mapping is the best match for * a given input event, and is allowed to reason on the given input event * before returning a boolean value, where boolean true means block * execution, and boolean false means to allow execution. */ private ObjectProperty> interceptor = new SimpleObjectProperty<>(this, "interceptor"); public final Predicate getInterceptor() { return interceptor.get(); } public final void setInterceptor(Predicate value) { interceptor.set(value); } public final ObjectProperty> interceptorProperty() { return interceptor; } /** * * @return */ public Object getMappingKey() { return eventType; } /** {@inheritDoc} */ @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Mapping)) return false; Mapping that = (Mapping) o; if (eventType != null ? !eventType.equals(that.getEventType()) : that.getEventType() != null) return false; return true; } /** {@inheritDoc} */ @Override public int hashCode() { return eventType != null ? eventType.hashCode() : 0; } } /** * The KeyMapping class provides API to specify * {@link InputMap.Mapping mappings} related to key input. */ public static class KeyMapping extends Mapping { /*********************************************************************** * * * Private fields * * * **********************************************************************/ private final KeyBinding keyBinding; /*********************************************************************** * * * Constructors * * * **********************************************************************/ /** * Creates a new KeyMapping instance that will fire when the given * {@link KeyCode} is entered into the application by the user, and this * will result in the given {@link EventHandler} being fired. * * @param keyCode The {@link KeyCode} to listen for. * @param eventHandler The {@link EventHandler} to fire when the * {@link KeyCode} is observed. */ public KeyMapping(final KeyCode keyCode, final EventHandler eventHandler) { this(new KeyBinding(keyCode), eventHandler); } /** * Creates a new KeyMapping instance that will fire when the given * {@link KeyCode} is entered into the application by the user, and this * will result in the given {@link EventHandler} being fired. The * eventType argument can be one of the following: * *
    *
  • {@link KeyEvent#ANY}
  • *
  • {@link KeyEvent#KEY_PRESSED}
  • *
  • {@link KeyEvent#KEY_TYPED}
  • *
  • {@link KeyEvent#KEY_RELEASED}
  • *
* * @param keyCode The {@link KeyCode} to listen for. * @param eventType The type of {@link KeyEvent} to listen for. * @param eventHandler The {@link EventHandler} to fire when the * {@link KeyCode} is observed. */ public KeyMapping(final KeyCode keyCode, final EventType eventType, final EventHandler eventHandler) { this(new KeyBinding(keyCode, eventType), eventHandler); } /** * Creates a new KeyMapping instance that will fire when the given * {@link KeyBinding} is entered into the application by the user, and this * will result in the given {@link EventHandler} being fired. * * @param keyBinding The {@link KeyBinding} to listen for. * @param eventHandler The {@link EventHandler} to fire when the * {@link KeyBinding} is observed. */ public KeyMapping(KeyBinding keyBinding, final EventHandler eventHandler) { this(keyBinding, eventHandler, null); } /** * Creates a new KeyMapping instance that will fire when the given * {@link KeyBinding} is entered into the application by the user, and this * will result in the given {@link EventHandler} being fired, as long as the * given interceptor is not true. * * @param keyBinding The {@link KeyBinding} to listen for. * @param eventHandler The {@link EventHandler} to fire when the * {@link KeyBinding} is observed. * @param interceptor A {@link Predicate} that, if true, will prevent the * {@link EventHandler} from being fired. */ public KeyMapping(KeyBinding keyBinding, final EventHandler eventHandler, Predicate interceptor) { super(keyBinding == null ? null : keyBinding.getType(), eventHandler); if (keyBinding == null) { throw new IllegalArgumentException("KeyMapping keyBinding constructor argument can not be null"); } this.keyBinding = keyBinding; setInterceptor(interceptor); } /*********************************************************************** * * * Public API * * * **********************************************************************/ /** {@inheritDoc} */ @Override public Object getMappingKey() { return keyBinding; } /** {@inheritDoc} */ @Override public int getSpecificity(Event e) { if (isDisabled()) return 0; if (!(e instanceof KeyEvent)) return 0; return keyBinding.getSpecificity((KeyEvent)e); } /** {@inheritDoc} */ @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof KeyMapping)) return false; if (!super.equals(o)) return false; KeyMapping that = (KeyMapping) o; // we know keyBinding is non-null here return keyBinding.equals(that.keyBinding); } /** {@inheritDoc} */ @Override public int hashCode() { return Objects.hash(keyBinding); } } /** * The MouseMapping class provides API to specify * {@link InputMap.Mapping mappings} related to mouse input. */ public static class MouseMapping extends Mapping { /*********************************************************************** * * * Constructors * * * **********************************************************************/ /** * Creates a new KeyMapping instance that will fire when the given * {@link KeyCode} is entered into the application by the user, and this * will result in the given {@link EventHandler} being fired. The * eventType argument can be any of the {@link MouseEvent} event types, * but typically it is one of the following: * *
    *
  • {@link MouseEvent#ANY}
  • *
  • {@link MouseEvent#MOUSE_PRESSED}
  • *
  • {@link MouseEvent#MOUSE_CLICKED}
  • *
  • {@link MouseEvent#MOUSE_RELEASED}
  • *
* * @param eventType The type of {@link MouseEvent} to listen for. * @param eventHandler The {@link EventHandler} to fire when the * {@link MouseEvent} is observed. */ public MouseMapping(final EventType eventType, final EventHandler eventHandler) { super(eventType, eventHandler); if (eventType == null) { throw new IllegalArgumentException("MouseMapping eventType constructor argument can not be null"); } } /*********************************************************************** * * * Public API * * * **********************************************************************/ /** {@inheritDoc} */ @Override public int getSpecificity(Event e) { if (isDisabled()) return 0; if (!(e instanceof MouseEvent)) return 0; EventType et = getEventType(); // FIXME naive int s = 0; if (e.getEventType() == MouseEvent.MOUSE_CLICKED && et != MouseEvent.MOUSE_CLICKED) return 0; else s++; if (e.getEventType() == MouseEvent.MOUSE_DRAGGED && et != MouseEvent.MOUSE_DRAGGED) return 0; else s++; if (e.getEventType() == MouseEvent.MOUSE_ENTERED && et != MouseEvent.MOUSE_ENTERED) return 0; else s++; if (e.getEventType() == MouseEvent.MOUSE_ENTERED_TARGET && et != MouseEvent.MOUSE_ENTERED_TARGET) return 0; else s++; if (e.getEventType() == MouseEvent.MOUSE_EXITED && et != MouseEvent.MOUSE_EXITED) return 0; else s++; if (e.getEventType() == MouseEvent.MOUSE_EXITED_TARGET && et != MouseEvent.MOUSE_EXITED_TARGET) return 0; else s++; if (e.getEventType() == MouseEvent.MOUSE_MOVED && et != MouseEvent.MOUSE_MOVED) return 0; else s++; if (e.getEventType() == MouseEvent.MOUSE_PRESSED && et != MouseEvent.MOUSE_PRESSED) return 0; else s++; if (e.getEventType() == MouseEvent.MOUSE_RELEASED && et != MouseEvent.MOUSE_RELEASED) return 0; else s++; // TODO handle further checks return s; } } /** * Convenience class that can act as an keyboard input interceptor, either at a * {@link InputMap#interceptorProperty() input map} level or a * {@link Mapping#interceptorProperty() mapping} level. * * @see InputMap#interceptorProperty() * @see Mapping#interceptorProperty() */ public static class KeyMappingInterceptor implements Predicate { private final KeyBinding keyBinding; /** * Creates a new KeyMappingInterceptor, which will block execution of * event handlers (either at a * {@link InputMap#interceptorProperty() input map} level or a * {@link Mapping#interceptorProperty() mapping} level), where the input * received is equal to the given {@link KeyBinding}. * * @param keyBinding The {@link KeyBinding} for which mapping execution * should be blocked. */ public KeyMappingInterceptor(KeyBinding keyBinding) { this.keyBinding = keyBinding; } /** {@inheritDoc} */ @Override public boolean test(Event event) { if (!(event instanceof KeyEvent)) return false; return KeyBinding.toKeyBinding((KeyEvent)event).equals(keyBinding); } } /** * Convenience class that can act as a mouse input interceptor, either at a * {@link InputMap#interceptorProperty() input map} level or a * {@link Mapping#interceptorProperty() mapping} level. * * @see InputMap#interceptorProperty() * @see Mapping#interceptorProperty() */ public static class MouseMappingInterceptor implements Predicate { private final EventType eventType; /** * Creates a new MouseMappingInterceptor, which will block execution of * event handlers (either at a * {@link InputMap#interceptorProperty() input map} level or a * {@link Mapping#interceptorProperty() mapping} level), where the input * received is equal to the given {@link EventType}. * * @param eventType The {@link EventType} for which mapping execution * should be blocked (typically one of * {@link MouseEvent#MOUSE_PRESSED}, * {@link MouseEvent#MOUSE_CLICKED}, or * {@link MouseEvent#MOUSE_RELEASED}). */ public MouseMappingInterceptor(EventType eventType) { this.eventType = eventType; } /** {@inheritDoc} */ @Override public boolean test(Event event) { if (!(event instanceof MouseEvent)) return false; return event.getEventType() == this.eventType; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy