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

org.fxmisc.wellbehaved.event.template.InputMapTemplate Maven / Gradle / Ivy

There is a newer version: 1.11
Show newest version
package org.fxmisc.wellbehaved.event.template;

import java.util.Arrays;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;

import javafx.event.Event;
import javafx.event.EventType;
import javafx.scene.Node;

import org.fxmisc.wellbehaved.event.EventPattern;
import org.fxmisc.wellbehaved.event.InputHandler;
import org.fxmisc.wellbehaved.event.InputHandler.Result;
import org.fxmisc.wellbehaved.event.InputMap;
import org.fxmisc.wellbehaved.event.Nodes;

public abstract class InputMapTemplate {

    @FunctionalInterface
    public static interface HandlerTemplateConsumer {

         void accept(
                EventType t,
                InputHandlerTemplate h);


        static  HandlerTemplateConsumer from(
                InputMap.HandlerConsumer hc, S target) {
            return new HandlerTemplateConsumer() {
                @Override
                public  void accept(
                        EventType t, InputHandlerTemplate h) {
                    hc.accept(t, evt -> h.process(target, evt));

                }
            };
        }
    }

    private InputHandlerTemplateMap inputHandlerTemplates = null;

    public final void forEachEventType(HandlerTemplateConsumer f) {
        if(inputHandlerTemplates == null) {
            inputHandlerTemplates = getInputHandlerTemplateMap();
        }
        inputHandlerTemplates.forEach(f);
    }

    public final InputMapTemplate orElse(InputMapTemplate that) {
        return sequence(this, that);
    }

    public final InputMap instantiate(S target) {
        return new InputMapTemplateInstance<>(this, target);
    }

    protected abstract InputHandlerTemplateMap getInputHandlerTemplateMap();


    static  InputMapTemplate upCast(InputMapTemplate imt) {
        @SuppressWarnings("unchecked")
        InputMapTemplate res = (InputMapTemplate) imt;
        return res;
    }

    @SafeVarargs
    public static  InputMapTemplate sequence(InputMapTemplate... templates) {
        return new TemplateChain<>(templates);
    }

    public static  InputMapTemplate process(
            EventPattern eventPattern,
            BiFunction action) {
        return new PatternActionTemplate<>(eventPattern, action);
    }

    public static  InputMapTemplate process(
            EventType eventType,
            BiFunction action) {
        return process(EventPattern.eventType(eventType), action);
    }

    public InputMapTemplate ifConsumed(BiConsumer postConsumption) {
        return new InputMapTemplate() {
            @Override
            protected InputHandlerTemplateMap getInputHandlerTemplateMap() {
                return InputMapTemplate.this.getInputHandlerTemplateMap().map(iht -> {
                    return (s, evt) -> {
                        Result res = iht.process(s, evt);
                        if (res == Result.CONSUME) {
                            postConsumption.accept(s, evt);
                        }
                        return res;
                    };
                });
            }
        };
    }

    public static  InputMapTemplate consume(
            EventPattern eventPattern,
            BiConsumer action) {
        return process(eventPattern, (s, u) -> {
            action.accept(s, u);
            return Result.CONSUME;
        });
    }

    public static  InputMapTemplate consume(
            EventType eventType,
            BiConsumer action) {
        return consume(EventPattern.eventType(eventType), action);
    }

    public static  InputMapTemplate consume(
            EventPattern eventPattern) {
        return process(eventPattern, (s, u) -> Result.CONSUME);
    }

    public static  InputMapTemplate consume(
            EventType eventType) {
        return consume(EventPattern.eventType(eventType));
    }

    public static  InputMapTemplate consumeWhen(
            EventPattern eventPattern,
            Predicate condition,
            BiConsumer action) {
        return process(eventPattern, (s, u) -> {
            if(condition.test(s)) {
                action.accept(s, u);
                return Result.CONSUME;
            } else {
                return Result.PROCEED;
            }
        });
    }

    public static  InputMapTemplate consumeWhen(
            EventType eventType,
            Predicate condition,
            BiConsumer action) {
        return consumeWhen(EventPattern.eventType(eventType), condition, action);
    }

    public static  InputMapTemplate consumeUnless(
            EventPattern eventPattern,
            Predicate condition,
            BiConsumer action) {
        return consumeWhen(eventPattern, condition.negate(), action);
    }

    public static  InputMapTemplate consumeUnless(
            EventType eventType,
            Predicate condition,
            BiConsumer action) {
        return consumeUnless(EventPattern.eventType(eventType), condition, action);
    }

    public static  InputMapTemplate ignore(
            EventPattern eventPattern) {
        return new PatternActionTemplate<>(eventPattern, PatternActionTemplate.CONST_IGNORE);
    }

    public static  InputMapTemplate ignore(
            EventType eventType) {
        return ignore(EventPattern.eventType(eventType));
    }

    public static  InputMapTemplate when(
            Predicate condition, InputMapTemplate imt) {

        return new InputMapTemplate() {
            @Override
            protected InputHandlerTemplateMap getInputHandlerTemplateMap() {
                return imt.getInputHandlerTemplateMap().map(
                        h -> (s, evt) -> condition.test(s) ? h.process(s, evt) : Result.PROCEED);
            }
        };
    }

    public static  InputMapTemplate unless(
            Predicate condition, InputMapTemplate imt) {
        return when(condition.negate(), imt);
    }

    public static  InputMapTemplate lift(
            InputMapTemplate imt,
            Function f) {

        return new InputMapTemplate() {
            @Override
            protected InputHandlerTemplateMap getInputHandlerTemplateMap() {
                return imt.getInputHandlerTemplateMap().map(
                        h -> (s, evt) -> h.process(f.apply(s), evt));
            }
        };
    }

    public static  void installOverride(InputMapTemplate imt, S node) {
        Nodes.addInputMap(node, imt.instantiate(node));
    }

    public static  void installOverride(InputMapTemplate imt, S target, Function getNode) {
        Nodes.addInputMap(getNode.apply(target), imt.instantiate(target));
    }

    public static  void installFallback(InputMapTemplate imt, S node) {
        Nodes.addFallbackInputMap(node, imt.instantiate(node));
    }

    public static  void installFallback(InputMapTemplate imt, S target, Function getNode) {
        Nodes.addFallbackInputMap(getNode.apply(target), imt.instantiate(target));
    }

    public static  void uninstall(InputMapTemplate imt, S node) {
        Nodes.removeInputMap(node, imt.instantiate(node));
    }

    public static  void uninstall(InputMapTemplate imt, S target, Function getNode) {
        Nodes.removeInputMap(getNode.apply(target), imt.instantiate(target));
    }
}

class PatternActionTemplate extends InputMapTemplate {
    static final BiFunction CONST_IGNORE = (x, y) -> Result.IGNORE;

    private final EventPattern pattern;
    private final BiFunction action;

    PatternActionTemplate(EventPattern pattern, BiFunction action) {
        this.pattern = pattern;
        this.action  = action;
    }

    @Override
    protected InputHandlerTemplateMap getInputHandlerTemplateMap() {
        InputHandlerTemplateMap ihtm = new InputHandlerTemplateMap<>();
        InputHandlerTemplate iht = (s, t) -> pattern.match(t).map(u -> action.apply(s, u)).orElse(Result.PROCEED);
        pattern.getEventTypes().forEach(et -> ihtm.insertAfter(et, iht));
        return ihtm;
    }

    @Override
    public boolean equals(Object other) {
        if(other instanceof PatternActionTemplate) {
            PatternActionTemplate that = (PatternActionTemplate) other;
            return Objects.equals(this.pattern, that.pattern)
                && Objects.equals(this.action,  that.action);
        } else {
            return false;
        }
    }

    @Override
    public int hashCode() {
        return Objects.hash(pattern, action);
    }
}

class TemplateChain extends InputMapTemplate {
    private final InputMapTemplate[] templates;

    @SafeVarargs
    TemplateChain(InputMapTemplate... templates) {
        this.templates = templates;
    }

    @Override
    protected InputHandlerTemplateMap getInputHandlerTemplateMap() {
        InputHandlerTemplateMap ihtm = new InputHandlerTemplateMap<>();
        for(InputMapTemplate imt: templates) {
            imt.getInputHandlerTemplateMap().forEach(ihtm::insertAfter);
        }
        return ihtm;
    }

    @Override
    public boolean equals(Object other) {
        if(other instanceof TemplateChain) {
            TemplateChain that = (TemplateChain) other;
            return Arrays.equals(this.templates, that.templates);
        } else {
            return false;
        }
    }

    @Override
    public int hashCode() {
        return Arrays.hashCode(templates);
    }
}

class InputMapTemplateInstance implements InputMap {
    private final InputMapTemplate template;
    private final S target;

    InputMapTemplateInstance(InputMapTemplate template, S target) {
        this.template = template;
        this.target = target;
    }

    @Override
    public void forEachEventType(HandlerConsumer hc) {
        template.forEachEventType(
                InputMapTemplate.HandlerTemplateConsumer.from(hc, target));
    }

    @Override
    public boolean equals(Object other) {
        if(other instanceof InputMapTemplateInstance) {
            InputMapTemplateInstance that = (InputMapTemplateInstance) other;
            return Objects.equals(this.template, that.template)
                && Objects.equals(this.target, that.target);
        } else {
            return false;
        }
    }

    @Override
    public int hashCode() {
        return Objects.hash(template, target);
    }
}