dk.sidereal.lumm.components.input.Clickable.java Source code

Java tutorial

Introduction

Here is the source code for dk.sidereal.lumm.components.input.Clickable.java

Source

/*******************************************************************************
 * Copyright 2014 See AUTHORS file.
 * <p>
 * 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
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * 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 dk.sidereal.lumm.components.input;

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

import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector3;
import dk.sidereal.lumm.architecture.Lumm;
import dk.sidereal.lumm.architecture.LummComponent;
import dk.sidereal.lumm.architecture.LummObject;
import dk.sidereal.lumm.architecture.concrete.ConcreteLummComponent;
import dk.sidereal.lumm.architecture.core.Input;
import dk.sidereal.lumm.architecture.core.Input.ActionType;
import dk.sidereal.lumm.architecture.core.input.ActionData;
import dk.sidereal.lumm.architecture.core.input.OnClickListener;
import dk.sidereal.lumm.architecture.core.input.TouchData;
import dk.sidereal.lumm.architecture.core.input.OnTouchListener;
import dk.sidereal.lumm.architecture.listeners.OnDisposeListener;

/**
 * Behavior used for creating events that are to be triggered when the user
 * clicks in a certain area. The area is saved in {@link #area}, which gets
 * updated each frame based on the location of the AbstractObject. The event to
 * run is {@link #eventOnHold}
 *
 * @see {@link Input}
 * @author Claudiu Bele
 */

// TODO handle multiple area types
public class Clickable extends ConcreteLummComponent {

    // region external

    private class ActionEventEntry {

        String inputProcessor;

        OnClickListener event;

        public ActionEventEntry(String inputProcessor, OnClickListener event) {
            this.inputProcessor = inputProcessor;
            this.event = event;
        }
    }

    private class TouchEventEntry {

        String inputProcessor;

        OnTouchListener event;

        public TouchEventEntry(String inputProcessor, OnTouchListener event) {
            this.inputProcessor = inputProcessor;
            this.event = event;
        }
    }

    // endregion external

    // region fields

    /**
     * The amount of time to wait so as to not trigger multiple events in a row
     */
    public float cooldown;

    /** The time remaining until we can record an event again */
    public float timeRemaining;

    /** Sprite used for debugging */
    public static Sprite debugSpriteSource;

    /**
     * Events that were added from an instance of Clickable to the Input, to
     * remove from Input in the dispose method
     */
    private List<ActionEventEntry> registeredActionEvents;

    /**
     * Events that were added from an instance of Clickable to the Input, to
     * remove from Input in the dispose method
     */
    private List<TouchEventEntry> registeredTouchEvents;

    /**
     * The area which if clicked, triggers an event. Set in
     * {@link #setAreaSize(float, float)} or {@link #setArea(Rectangle)}
     */
    private Rectangle area;

    /** Debugging sprite */
    private Sprite debugSprite;

    private float offsetX, offsetY;

    // endregion fields

    // region constructors

    public Clickable(LummObject obj) {

        super(obj);

        if (Lumm.debug.isEnabled())
            this.debugSprite = new Sprite(debugSpriteSource);
        this.cooldown = 1f;
        this.timeRemaining = 0f;

        registeredActionEvents = new ArrayList<Clickable.ActionEventEntry>();
        registeredTouchEvents = new ArrayList<Clickable.TouchEventEntry>();

        setDebugToggleKeys(Keys.CONTROL_LEFT, Keys.Z);

        onDisposeListener = new OnDisposeListener<LummComponent>() {

            @Override
            public void onDispose(LummComponent caller) {

                for (int i = 0; i < registeredActionEvents.size(); i++) {
                    Lumm.input.removeOnClickListener(registeredActionEvents.get(i).inputProcessor,
                            registeredActionEvents.get(i).event);
                }

                for (int i = 0; i < registeredTouchEvents.size(); i++) {
                    Lumm.input.removeOnTouchListener(registeredTouchEvents.get(i).inputProcessor,
                            registeredTouchEvents.get(i).event);
                }
            }
        };

    }

    @Override
    protected void initialiseClass() {

        if (!Lumm.debug.isEnabled())
            return;
        if (debugSpriteSource == null) {
            debugSpriteSource = new Sprite(
                    Lumm.assets.get(Lumm.assets.frameworkAssetsFolder + "White.png", Texture.class));
            debugSpriteSource.setColor(new Color(218 / 255f, 165 / 255f, 32 / 255f, 0.5f));
        }
    }

    // endregion constructors

    // region methods

    @Override
    public void onDebug() {

        if (isEnabled()) {
            debugSprite.setBounds(area.x, area.y, area.width, area.height);
            debugSprite.draw(object.getSceneLayer().spriteBatch);
        }
    }

    @Override
    public void onUpdate() {

        // adapt the area based on the position of the object
        area.x = object.position.getX() + offsetX;
        area.y = object.position.getY() + offsetY;

        timeRemaining -= Lumm.time.getDeltaTime();

    }

    public Rectangle getArea() {

        return area;
    }

    public void setAreaSize(float width, float height) {

        offsetX = -width / 2;
        offsetY = -height / 2;
        this.area = new Rectangle(object.position.getX() - width / 2, object.position.getY() - height / 2, width,
                height);

    }

    public void setAreaSize(float width, float height, float offsetX, float offsetY) {

        this.offsetX = offsetX;
        this.offsetY = offsetY;
        this.area = new Rectangle(object.position.getX() + offsetX, object.position.getY() + offsetY, width,
                height);

    }

    public void setArea(Rectangle area) {

        this.area = area;
    }

    /**
     * Adds an action event to {@link Lumm#input}. To read more on what each
     * parameter does, visit
     * {@link Input#addOnClickListener(String, int, OnClickListener, ActionType)}, as
     * that is the method that will be accessed from this one after some
     * tweaking. For adding events related to individual fingers other than the
     * first one, use
     * {@link #addTouchEvent(String, int, OnTouchListener, ActionType)}
     *
     * @param inputProcessorName
     * @param action
     * @param event
     * @param eventType
     * @param inside
     *            Whether to run the action when the action happens inside or
     *            outside of clickable area
     */
    public void addOnClickListener(String inputProcessorName, int action, final OnClickListener event,
            ActionType eventType, final boolean inside) {

        // make event that encapsulates the passed event
        OnClickListener clickableEvent = new OnClickListener() {

            @Override
            public boolean onClick(ActionData inputData) {
                // event will not run if mouse position is not within bounds.
                if (area.contains(object.getSceneLayer().mousePosition) == inside)
                    return event.onClick(inputData);
                else
                    return false;

            }
        };

        Lumm.input.addOnClickListener(inputProcessorName, action, clickableEvent, eventType);
        registeredActionEvents.add(new ActionEventEntry(inputProcessorName, clickableEvent));

    }

    public void addTouchEvent(String inputProcessorName, int action, final OnTouchListener touchEvent,
            ActionType eventType, final boolean inside) {
        // make event that encapsulates the passed event
        OnTouchListener clickableEvent = new OnTouchListener() {

            @Override
            public boolean run(TouchData inputData) {
                // translate touch input to a mouse position
                Vector3 translatedTouchPosition = new Vector3(inputData.getPosition(), 0);
                object.getSceneLayer().camera.unproject(translatedTouchPosition);

                if (area.contains(translatedTouchPosition.x, translatedTouchPosition.y) == inside)
                    return touchEvent.run(inputData);
                else
                    return false;
            }
        };

        Lumm.input.addOnTouchListener(inputProcessorName, action, clickableEvent, eventType);
        registeredTouchEvents.add(new TouchEventEntry(inputProcessorName, clickableEvent));
    }

    // endregion methods
}