com.badlogic.gdx.scenes.scene2d.utils.DragAndDrop.java Source code

Java tutorial

Introduction

Here is the source code for com.badlogic.gdx.scenes.scene2d.utils.DragAndDrop.java

Source

/*******************************************************************************
 * Copyright 2011 See AUTHORS file.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/

package com.badlogic.gdx.scenes.scene2d.utils;

import com.badlogic.gdx.Input.Buttons;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.Touchable;
import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.ObjectMap;
import com.badlogic.gdx.utils.ObjectMap.Entry;

/** Manages drag and drop operations through registered drag sources and drop targets.
 * @author Nathan Sweet */
public class DragAndDrop {
    static final Vector2 tmpVector = new Vector2();

    Payload payload;
    Actor dragActor;
    Target target;
    boolean isValidTarget;
    Array<Target> targets = new Array();
    ObjectMap<Source, DragListener> sourceListeners = new ObjectMap();
    private float tapSquareSize = 8;
    private int button;
    float dragActorX = 14, dragActorY = -20;
    float touchOffsetX, touchOffsetY;
    long dragStartTime;
    int dragTime = 250;
    int activePointer = -1;
    boolean cancelTouchFocus = true;

    public void addSource(final Source source) {
        DragListener listener = new DragListener() {
            public void dragStart(InputEvent event, float x, float y, int pointer) {
                if (activePointer != -1) {
                    event.stop();
                    return;
                }

                activePointer = pointer;

                dragStartTime = System.currentTimeMillis();
                payload = source.dragStart(event, getTouchDownX(), getTouchDownY(), pointer);
                event.stop();

                if (cancelTouchFocus && payload != null)
                    source.getActor().getStage().cancelTouchFocusExcept(this, source.getActor());
            }

            public void drag(InputEvent event, float x, float y, int pointer) {
                if (payload == null)
                    return;
                if (pointer != activePointer)
                    return;

                Stage stage = event.getStage();

                Touchable dragActorTouchable = null;
                if (dragActor != null) {
                    dragActorTouchable = dragActor.getTouchable();
                    dragActor.setTouchable(Touchable.disabled);
                }

                // Find target.
                Target newTarget = null;
                isValidTarget = false;
                float stageX = event.getStageX() + touchOffsetX, stageY = event.getStageY() + touchOffsetY;
                Actor hit = event.getStage().hit(stageX, stageY, true); // Prefer touchable actors.
                if (hit == null)
                    hit = event.getStage().hit(stageX, stageY, false);
                if (hit != null) {
                    for (int i = 0, n = targets.size; i < n; i++) {
                        Target target = targets.get(i);
                        if (!target.actor.isAscendantOf(hit))
                            continue;
                        newTarget = target;
                        target.actor.stageToLocalCoordinates(tmpVector.set(stageX, stageY));
                        isValidTarget = target.drag(source, payload, tmpVector.x, tmpVector.y, pointer);
                        break;
                    }
                }
                if (newTarget != target) {
                    if (target != null)
                        target.reset(source, payload);
                    target = newTarget;
                }

                if (dragActor != null)
                    dragActor.setTouchable(dragActorTouchable);

                // Add/remove and position the drag actor.
                Actor actor = null;
                if (target != null)
                    actor = isValidTarget ? payload.validDragActor : payload.invalidDragActor;
                if (actor == null)
                    actor = payload.dragActor;
                if (actor == null)
                    return;
                if (dragActor != actor) {
                    if (dragActor != null)
                        dragActor.remove();
                    dragActor = actor;
                    stage.addActor(actor);
                }
                float actorX = event.getStageX() + dragActorX;
                float actorY = event.getStageY() + dragActorY - actor.getHeight();
                if (actorX < 0)
                    actorX = 0;
                if (actorY < 0)
                    actorY = 0;
                if (actorX + actor.getWidth() > stage.getWidth())
                    actorX = stage.getWidth() - actor.getWidth();
                if (actorY + actor.getHeight() > stage.getHeight())
                    actorY = stage.getHeight() - actor.getHeight();
                actor.setPosition(actorX, actorY);
            }

            public void dragStop(InputEvent event, float x, float y, int pointer) {
                if (pointer != activePointer)
                    return;
                activePointer = -1;
                if (payload == null)
                    return;

                if (System.currentTimeMillis() - dragStartTime < dragTime)
                    isValidTarget = false;
                if (dragActor != null)
                    dragActor.remove();
                if (isValidTarget) {
                    float stageX = event.getStageX() + touchOffsetX, stageY = event.getStageY() + touchOffsetY;
                    target.actor.stageToLocalCoordinates(tmpVector.set(stageX, stageY));
                    target.drop(source, payload, tmpVector.x, tmpVector.y, pointer);
                }
                source.dragStop(event, x, y, pointer, payload, isValidTarget ? target : null);
                if (target != null)
                    target.reset(source, payload);
                payload = null;
                target = null;
                isValidTarget = false;
                dragActor = null;
            }
        };
        listener.setTapSquareSize(tapSquareSize);
        listener.setButton(button);
        source.actor.addCaptureListener(listener);
        sourceListeners.put(source, listener);
    }

    public void removeSource(Source source) {
        DragListener dragListener = sourceListeners.remove(source);
        source.actor.removeCaptureListener(dragListener);
    }

    public void addTarget(Target target) {
        targets.add(target);
    }

    public void removeTarget(Target target) {
        targets.removeValue(target, true);
    }

    /** Removes all targets and sources. */
    public void clear() {
        targets.clear();
        for (Entry<Source, DragListener> entry : sourceListeners.entries())
            entry.key.actor.removeCaptureListener(entry.value);
        sourceListeners.clear();
    }

    /** Sets the distance a touch must travel before being considered a drag. */
    public void setTapSquareSize(float halfTapSquareSize) {
        tapSquareSize = halfTapSquareSize;
    }

    /** Sets the button to listen for, all other buttons are ignored. Default is {@link Buttons#LEFT}. Use -1 for any button. */
    public void setButton(int button) {
        this.button = button;
    }

    public void setDragActorPosition(float dragActorX, float dragActorY) {
        this.dragActorX = dragActorX;
        this.dragActorY = dragActorY;
    }

    /** Sets an offset in stage coordinates from the touch position which is used to determine the drop location. Default is 0,0. */
    public void setTouchOffset(float touchOffsetX, float touchOffsetY) {
        this.touchOffsetX = touchOffsetX;
        this.touchOffsetY = touchOffsetY;
    }

    public boolean isDragging() {
        return payload != null;
    }

    /** Returns the current drag actor, or null. */
    public Actor getDragActor() {
        return dragActor;
    }

    /** Time in milliseconds that a drag must take before a drop will be considered valid. This ignores an accidental drag and drop
     * that was meant to be a click. Default is 250. */
    public void setDragTime(int dragMillis) {
        this.dragTime = dragMillis;
    }

    /** When true (default), the {@link Stage#cancelTouchFocus()} touch focus} is cancelled if
     * {@link Source#dragStart(InputEvent, float, float, int) dragStart} returns non-null. This ensures the DragAndDrop is the only
     * touch focus listener, eg when the source is inside a {@link ScrollPane} with flick scroll enabled. */
    public void setCancelTouchFocus(boolean cancelTouchFocus) {
        this.cancelTouchFocus = cancelTouchFocus;
    }

    /** A target where a payload can be dragged from.
     * @author Nathan Sweet */
    static abstract public class Source {
        final Actor actor;

        public Source(Actor actor) {
            if (actor == null)
                throw new IllegalArgumentException("actor cannot be null.");
            this.actor = actor;
        }

        /** @return May be null. */
        abstract public Payload dragStart(InputEvent event, float x, float y, int pointer);

        /** @param payload null if dragStart returned null.
         * @param target null if not dropped on a valid target. */
        public void dragStop(InputEvent event, float x, float y, int pointer, Payload payload, Target target) {
        }

        public Actor getActor() {
            return actor;
        }
    }

    /** A target where a payload can be dropped to.
     * @author Nathan Sweet */
    static abstract public class Target {
        final Actor actor;

        public Target(Actor actor) {
            if (actor == null)
                throw new IllegalArgumentException("actor cannot be null.");
            this.actor = actor;
            Stage stage = actor.getStage();
            if (stage != null && actor == stage.getRoot())
                throw new IllegalArgumentException("The stage root cannot be a drag and drop target.");
        }

        /** Called when the object is dragged over the target. The coordinates are in the target's local coordinate system.
         * @return true if this is a valid target for the object. */
        abstract public boolean drag(Source source, Payload payload, float x, float y, int pointer);

        /** Called when the object is no longer over the target, whether because the touch was moved or a drop occurred. */
        public void reset(Source source, Payload payload) {
        }

        abstract public void drop(Source source, Payload payload, float x, float y, int pointer);

        public Actor getActor() {
            return actor;
        }
    }

    /** The payload of a drag and drop operation. Actors can be optionally provided to follow the cursor and change when over a
     * target. */
    static public class Payload {
        Actor dragActor, validDragActor, invalidDragActor;
        Object object;

        public void setDragActor(Actor dragActor) {
            this.dragActor = dragActor;
        }

        public Actor getDragActor() {
            return dragActor;
        }

        public void setValidDragActor(Actor validDragActor) {
            this.validDragActor = validDragActor;
        }

        public Actor getValidDragActor() {
            return validDragActor;
        }

        public void setInvalidDragActor(Actor invalidDragActor) {
            this.invalidDragActor = invalidDragActor;
        }

        public Actor getInvalidDragActor() {
            return invalidDragActor;
        }

        public Object getObject() {
            return object;
        }

        public void setObject(Object object) {
            this.object = object;
        }
    }
}