Package tripleplay.util

Source Code of tripleplay.util.Input$LayerRegion

//
// Triple Play - utilities for use in PlayN-based games
// Copyright (c) 2011-2014, Three Rings Design, Inc. - All rights reserved.
// http://github.com/threerings/tripleplay/blob/master/LICENSE

package tripleplay.util;

import java.util.ArrayList;
import java.util.List;

import playn.core.Events;
import playn.core.Layer;

import pythagoras.f.IPoint;
import pythagoras.f.IRectangle;
import pythagoras.f.Point;

/**
* Dispatches user input from a particular source.
*/
public abstract class Input<L>
{
    /** Provides a handle on a listener or action registration. */
    public interface Registration {
        /** Unregisters the action associated with this handle. */
        void cancel ();
    }

    /** Encapsulates enabledness, expiry, and hit testing. */
    public static abstract class Region {
        /** Returns true if this region can be triggered, false if it's currently invisible. */
        public boolean canTrigger () {
            return true;
        }

        /** Returns true if this region is no longer relevant and should be removed. */
        public boolean hasExpired () {
            return false;
        }

        /** Returns true if the (screen-coordinates) point triggers falls in this region. */
        public abstract boolean hitTest (IPoint p);
    }

    /** A region that encompasses the entire screen. */
    public static class ScreenRegion extends Region {
        @Override public boolean hitTest (IPoint p) {
            return true;
        }
    }

    /** A region that encompasses the supplied (screen) bounds. */
    public static class BoundsRegion extends Region {
        public BoundsRegion (IRectangle bounds) {
            _bounds = bounds;
        }

        @Override public boolean hitTest (IPoint p) {
            return _bounds.contains(p);
        }

        protected IRectangle _bounds;
    }

    /** A region that encompasses supplied bounds, as transformed by the supplied layer's
     * transform. While the layer in question is not visible, the region will match clicks. If a
     * reaction using this region is considered for processing and its layer has been removed from
     * the view hierarchy, it will automatically be canceled. */
    public static class LayerRegion extends Region {
        public LayerRegion (Layer layer, IRectangle bounds) {
            _layer = layer;
            _bounds = bounds;
        }

        @Override public boolean canTrigger () {
            return _layer.visible();
        }
        @Override public boolean hasExpired () {
            return _layer.parent() == null;
        }
        @Override public boolean hitTest (IPoint p) {
            // convert the screen coordinates into layer-relative coordinates and check that the
            // point falls within the (layer-transform-relative) bounds
            return _bounds.contains(Layer.Util.screenToLayer(_layer, p, new Point()));
        }

        protected Layer _layer;
        protected IRectangle _bounds;
    }

    /** A region that encompasses the supplied layer's (transformed) bounds. While the layer in
     * question is not visible, the region will not be match clicks. If a reaction using this
     * region is considered for processing and its layer has been removed from the view hierarchy,
     * it will automatically be canceled. */
    public static class SizedLayerRegion extends Region {
        public SizedLayerRegion (Layer.HasSize layer) {
            _layer = layer;
        }

        @Override public boolean canTrigger () {
            return _layer.visible();
        }
        @Override public boolean hasExpired () {
            return _layer.parent() == null;
        }
        @Override public boolean hitTest (IPoint p) {
            // convert the screen coordinates into layer-relative coordinates
            Point lp = Layer.Util.screenToLayer(_layer, p, new Point());
            float x = lp.x, y = lp.y;
            return (x > 0 && y > 0 && x < _layer.scaledWidth() && y < _layer.scaledHeight());
        }

        protected Layer.HasSize _layer;
    }

    /**
     * Configures a reaction to be notified of pointer activity. On pointer start, reactions will be
     * scanned from most-recently-registered to least-recently-registered and hit-tested. Thus more
     * recently registered reactions that overlap previously registered reactions will take
     * precedence. TODO: use layer depth information to hit test based on depth.
     *
     * <p> Subsequent pointer drag and end events will be dispatched to the reaction that
     * successfully hit-tested the pointer start. </p>
     *
     * @return a handle that can be used to clear this registration.
     */
    public abstract Registration register (Region region, L listener);

    /**
     * Registers a reaction using {@link ScreenRegion} and the supplied listener.
     * @return a handle that can be used to clear this registration.
     */
    public Registration register (L listener) {
        return register(new ScreenRegion(), listener);
    }

    /**
     * Registers a reaction using {@link BoundsRegion} and the supplied listener.
     * @return a handle that can be used to clear this registration.
     */
    public Registration register (IRectangle bounds, L listener) {
        return register(new BoundsRegion(bounds), listener);
    }

    /**
     * Registers a reaction using {@link LayerRegion} and the supplied listener.
     * @return a handle that can be used to clear this registration.
     */
    public Registration register (Layer layer, IRectangle bounds, L listener) {
        return register(new LayerRegion(layer, bounds), listener);
    }

    /**
     * Registers a reaction using {@link SizedLayerRegion} and the supplied listener.
     * @return a handle that can be used to clear this registration.
     */
    public Registration register (Layer.HasSize layer, L listener) {
        return register(new SizedLayerRegion(layer), listener);
    }

    protected abstract static class Reactor<L> {
        public L hitTest (Events.Position event) {
            // take a snapshot of the regions list to avoid concurrent modification if reactions
            // are added or removed during processing
            List<Reaction<L>> snapshot = new ArrayList<Reaction<L>>(_reactions);
            Point p = new Point(event.x(), event.y());
            for (int ii = snapshot.size() - 1; ii >= 0; ii--) {
                Reaction<L> r = snapshot.get(ii);
                if (r.region.hasExpired()) {
                    _reactions.remove(r);
                } else if (r.region.canTrigger() && r.region.hitTest(p)) {
                    return r.listener;
                }
            }
            return null;
        }

        public Registration register (Region region, L listener) {
            final Reaction<L> reaction = new Reaction<L>(region, listener);
            _reactions.add(reaction);
            return new Registration() {
                @Override public void cancel () {
                    _reactions.remove(reaction);
                }
            };
        }

        /** A list of all registered reactions. */
        protected List<Reaction<L>> _reactions = new ArrayList<Reaction<L>>();
    }

    protected static final class Reaction<T> {
        public final Region region;
        public final T listener;

        public Reaction (Region region, T listener) {
            this.region = region;
            this.listener = listener;
        }
    }
}
TOP

Related Classes of tripleplay.util.Input$LayerRegion

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.