Package com.badlogic.gdx.scenes.scene2d

Source Code of com.badlogic.gdx.scenes.scene2d.Group

/*******************************************************************************
* 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;

import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.math.Matrix3;
import com.badlogic.gdx.math.Matrix4;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.scenes.scene2d.utils.Cullable;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.SnapshotArray;

/** 2D scene graph node that may contain other actors.
* <p>
* Actors have a z-order equal to the order they were inserted into the group. Actors inserted later will be drawn on top of
* actors added earlier. Touch events that hit more than one actor are distributed to topmost actors first.
* @author mzechner
* @author Nathan Sweet */
public class Group extends Actor implements Cullable {
  private final SnapshotArray<Actor> children = new SnapshotArray(true, 4, Actor.class);
  private final Matrix3 localTransform = new Matrix3();
  private final Matrix3 worldTransform = new Matrix3();
  private final Matrix4 batchTransform = new Matrix4();
  private final Matrix4 oldBatchTransform = new Matrix4();
  private boolean transform = true;
  private Rectangle cullingArea;
  private final Vector2 point = new Vector2();

  public void act (float delta) {
    super.act(delta);
    Actor[] actors = children.begin();
    for (int i = 0, n = children.size; i < n; i++)
      actors[i].act(delta);
    children.end();
  }

  /** Draws the group and its children. The default implementation calls {@link #applyTransform(Batch, Matrix4)} if needed, then
   * {@link #drawChildren(Batch, float)}, then {@link #resetTransform(Batch)} if needed. */
  public void draw (Batch batch, float parentAlpha) {
    if (transform) applyTransform(batch, computeTransform());
    drawChildren(batch, parentAlpha);
    if (transform) resetTransform(batch);
  }

  /** Draws all children. {@link #applyTransform(Batch, Matrix4)} should be called before and {@link #resetTransform(Batch)} after
   * this method if {@link #setTransform(boolean) transform} is true. If {@link #setTransform(boolean) transform} is false these
   * methods don't need to be called, children positions are temporarily offset by the group position when drawn. This method
   * avoids drawing children completely outside the {@link #setCullingArea(Rectangle) culling area}, if set. */
  protected void drawChildren (Batch batch, float parentAlpha) {
    parentAlpha *= this.color.a;
    SnapshotArray<Actor> children = this.children;
    Actor[] actors = children.begin();
    Rectangle cullingArea = this.cullingArea;
    if (cullingArea != null) {
      // Draw children only if inside culling area.
      float cullLeft = cullingArea.x;
      float cullRight = cullLeft + cullingArea.width;
      float cullBottom = cullingArea.y;
      float cullTop = cullBottom + cullingArea.height;
      if (transform) {
        for (int i = 0, n = children.size; i < n; i++) {
          Actor child = actors[i];
          if (!child.isVisible()) continue;
          float cx = child.x, cy = child.y;
          if (cx <= cullRight && cy <= cullTop && cx + child.width >= cullLeft && cy + child.height >= cullBottom)
            child.draw(batch, parentAlpha);
        }
        batch.flush();
      } else {
        // No transform for this group, offset each child.
        float offsetX = x, offsetY = y;
        x = 0;
        y = 0;
        for (int i = 0, n = children.size; i < n; i++) {
          Actor child = actors[i];
          if (!child.isVisible()) continue;
          float cx = child.x, cy = child.y;
          if (cx <= cullRight && cy <= cullTop && cx + child.width >= cullLeft && cy + child.height >= cullBottom) {
            child.x = cx + offsetX;
            child.y = cy + offsetY;
            child.draw(batch, parentAlpha);
            child.x = cx;
            child.y = cy;
          }
        }
        x = offsetX;
        y = offsetY;
      }
    } else {
      // No culling, draw all children.
      if (transform) {
        for (int i = 0, n = children.size; i < n; i++) {
          Actor child = actors[i];
          if (!child.isVisible()) continue;
          child.draw(batch, parentAlpha);
        }
        batch.flush();
      } else {
        // No transform for this group, offset each child.
        float offsetX = x, offsetY = y;
        x = 0;
        y = 0;
        for (int i = 0, n = children.size; i < n; i++) {
          Actor child = actors[i];
          if (!child.isVisible()) continue;
          float cx = child.x, cy = child.y;
          child.x = cx + offsetX;
          child.y = cy + offsetY;
          child.draw(batch, parentAlpha);
          child.x = cx;
          child.y = cy;
        }
        x = offsetX;
        y = offsetY;
      }
    }
    children.end();
  }

  /** Set the Batch's transformation matrix, often with the result of {@link #computeTransform()}. Note this causes the batch to
   * be flushed. {@link #resetTransform(Batch)} will restore the transform to what it was before this call. */
  protected void applyTransform (Batch batch, Matrix4 transform) {
    oldBatchTransform.set(batch.getTransformMatrix());
    batch.setTransformMatrix(transform);
  }

  /** Returns the transform for this group's coordinate system. */
  protected Matrix4 computeTransform () {
    Matrix3 temp = worldTransform;

    float originX = this.originX;
    float originY = this.originY;
    float rotation = this.rotation;
    float scaleX = this.scaleX;
    float scaleY = this.scaleY;

    if (originX != 0 || originY != 0)
      localTransform.setToTranslation(originX, originY);
    else
      localTransform.idt();
    if (rotation != 0) localTransform.rotate(rotation);
    if (scaleX != 1 || scaleY != 1) localTransform.scale(scaleX, scaleY);
    if (originX != 0 || originY != 0) localTransform.translate(-originX, -originY);
    localTransform.trn(x, y);

    // Find the first parent that transforms.
    Group parentGroup = getParent();
    while (parentGroup != null) {
      if (parentGroup.transform) break;
      parentGroup = parentGroup.getParent();
    }

    if (parentGroup != null) {
      worldTransform.set(parentGroup.worldTransform);
      worldTransform.mul(localTransform);
    } else {
      worldTransform.set(localTransform);
    }

    batchTransform.set(worldTransform);
    return batchTransform;
  }

  /** Restores the Batch transform to what it was before {@link #applyTransform(Batch, Matrix4)}. Note this causes the batch to be
   * flushed. */
  protected void resetTransform (Batch batch) {
    batch.setTransformMatrix(oldBatchTransform);
  }

  /** Children completely outside of this rectangle will not be drawn. This is only valid for use with unrotated and unscaled
   * actors! */
  public void setCullingArea (Rectangle cullingArea) {
    this.cullingArea = cullingArea;
  }

  public Actor hit (float x, float y, boolean touchable) {
    if (touchable && getTouchable() == Touchable.disabled) return null;
    Array<Actor> children = this.children;
    for (int i = children.size - 1; i >= 0; i--) {
      Actor child = children.get(i);
      if (!child.isVisible()) continue;
      child.parentToLocalCoordinates(point.set(x, y));
      Actor hit = child.hit(point.x, point.y, touchable);
      if (hit != null) return hit;
    }
    return super.hit(x, y, touchable);
  }

  /** Called when actors are added to or removed from the group. */
  protected void childrenChanged () {
  }

  /** Adds an actor as a child of this group. The actor is first removed from its parent group, if any.
   * @see #remove() */
  public void addActor (Actor actor) {
    actor.remove();
    children.add(actor);
    actor.setParent(this);
    actor.setStage(getStage());
    childrenChanged();
  }

  /** Adds an actor as a child of this group, at a specific index. The actor is first removed from its parent group, if any.
   * @param index May be greater than the number of children. */
  public void addActorAt (int index, Actor actor) {
    actor.remove();
    if (index >= children.size)
      children.add(actor);
    else
      children.insert(index, actor);
    actor.setParent(this);
    actor.setStage(getStage());
    childrenChanged();
  }

  /** Adds an actor as a child of this group, immediately before another child actor. The actor is first removed from its parent
   * group, if any. */
  public void addActorBefore (Actor actorBefore, Actor actor) {
    actor.remove();
    int index = children.indexOf(actorBefore, true);
    children.insert(index, actor);
    actor.setParent(this);
    actor.setStage(getStage());
    childrenChanged();
  }

  /** Adds an actor as a child of this group, immediately after another child actor. The actor is first removed from its parent
   * group, if any. */
  public void addActorAfter (Actor actorAfter, Actor actor) {
    actor.remove();
    int index = children.indexOf(actorAfter, true);
    if (index == children.size)
      children.add(actor);
    else
      children.insert(index + 1, actor);
    actor.setParent(this);
    actor.setStage(getStage());
    childrenChanged();
  }

  /** Removes an actor from this group. If the actor will not be used again and has actions, they should be
   * {@link Actor#clearActions() cleared} so the actions will be returned to their
   * {@link Action#setPool(com.badlogic.gdx.utils.Pool) pool}, if any. This is not done automatically. */
  public boolean removeActor (Actor actor) {
    if (!children.removeValue(actor, true)) return false;
    Stage stage = getStage();
    if (stage != null) stage.unfocus(actor);
    actor.setParent(null);
    actor.setStage(null);
    childrenChanged();
    return true;
  }

  /** Removes all actors from this group. */
  public void clearChildren () {
    Actor[] actors = children.begin();
    for (int i = 0, n = children.size; i < n; i++) {
      Actor child = actors[i];
      child.setStage(null);
      child.setParent(null);
    }
    children.end();
    children.clear();
    childrenChanged();
  }

  /** Removes all children, actions, and listeners from this group. */
  public void clear () {
    super.clear();
    clearChildren();
  }

  /** Returns the first actor found with the specified name. Note this recursively compares the name of every actor in the group. */
  public <T extends Actor> T findActor (String name) {
    Array<Actor> children = this.children;
    for (int i = 0, n = children.size; i < n; i++)
      if (name.equals(children.get(i).getName())) return (T) children.get(i);
    for (int i = 0, n = children.size; i < n; i++) {
      Actor child = children.get(i);
      if (child instanceof Group) {
        Actor actor = ((Group)child).findActor(name);
        if (actor != null) return (T) actor;
      }
    }
    return null;
  }

  protected void setStage (Stage stage) {
    super.setStage(stage);
    Array<Actor> children = this.children;
    for (int i = 0, n = children.size; i < n; i++)
      children.get(i).setStage(stage);
  }

  /** Swaps two actors by index. Returns false if the swap did not occur because the indexes were out of bounds. */
  public boolean swapActor (int first, int second) {
    int maxIndex = children.size;
    if (first < 0 || first >= maxIndex) return false;
    if (second < 0 || second >= maxIndex) return false;
    children.swap(first, second);
    return true;
  }

  /** Swaps two actors. Returns false if the swap did not occur because the actors are not children of this group. */
  public boolean swapActor (Actor first, Actor second) {
    int firstIndex = children.indexOf(first, true);
    int secondIndex = children.indexOf(second, true);
    if (firstIndex == -1 || secondIndex == -1) return false;
    children.swap(firstIndex, secondIndex);
    return true;
  }

  /** Returns an ordered list of child actors in this group. */
  public SnapshotArray<Actor> getChildren () {
    return children;
  }

  public boolean hasChildren () {
    return children.size > 0;
  }

  /** When true (the default), the Batch is transformed so children are drawn in their parent's coordinate system. This has a
   * performance impact because {@link Batch#flush()} must be done before and after the transform. If the actors in a group are
   * not rotated or scaled, then the transform for the group can be set to false. In this case, each child's position will be
   * offset by the group's position for drawing, causing the children to appear in the correct location even though the Batch has
   * not been transformed. */
  public void setTransform (boolean transform) {
    this.transform = transform;
  }

  public boolean isTransform () {
    return transform;
  }

  /** Converts coordinates for this group to those of a descendant actor. The descendant does not need to be a direct child.
   * @throws IllegalArgumentException if the specified actor is not a descendant of this group. */
  public Vector2 localToDescendantCoordinates (Actor descendant, Vector2 localCoords) {
    Group parent = descendant.getParent();
    if (parent == null) throw new IllegalArgumentException("Child is not a descendant: " + descendant);
    // First convert to the actor's parent coordinates.
    if (parent != this) localToDescendantCoordinates(parent, localCoords);
    // Then from each parent down to the descendant.
    descendant.parentToLocalCoordinates(localCoords);
    return localCoords;
  }

  /** Prints the actor hierarchy recursively for debugging purposes. */
  public void print () {
    print("");
  }

  private void print (String indent) {
    Actor[] actors = children.begin();
    for (int i = 0, n = children.size; i < n; i++) {
      System.out.println(indent + actors[i]);
      if (actors[i] instanceof Group) ((Group)actors[i]).print(indent + "|  ");
    }
    children.end();
  }
}
TOP

Related Classes of com.badlogic.gdx.scenes.scene2d.Group

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.