/*******************************************************************************
* Copyright (c) 2011, Nathan Sweet <nathan.sweet@gmail.com> All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
* following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation and/or other materials provided with the
* distribution. * Neither the name of the <organization> nor the names of its contributors may be used to endorse or
* promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package com.badlogic.gdx.scenes.scene2d.ui;
import com.esotericsoftware.tablelayout.BaseTableLayout.Debug;
import com.esotericsoftware.tablelayout.Cell;
import com.esotericsoftware.tablelayout.Toolkit;
import com.esotericsoftware.tablelayout.Value;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.Group;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.Touchable;
import com.badlogic.gdx.scenes.scene2d.ui.Label.LabelStyle;
import com.badlogic.gdx.scenes.scene2d.utils.Align;
import com.badlogic.gdx.scenes.scene2d.utils.Drawable;
import com.badlogic.gdx.utils.Array;
import java.util.List;
/**
* A group that sizes and positions children using table constraints. By default, {@link #getTouchable()} is
* {@link Touchable#childrenOnly}.
* <p>
* The preferred and minimum sizes are that of the children when laid out in columns and rows.
*
* @author Nathan Sweet
*/
public class Table extends WidgetGroup {
static {
Toolkit.instance = new TableToolkit();
}
private final TableLayout layout;
private Drawable background;
private boolean clip;
private Skin skin;
public Table() {
this(null);
}
/**
* Creates a table with a skin, which enables the {@link #add(String)} and {@link #add(String, String)} methods to
* be used.
*/
public Table(Skin skin) {
this.skin = skin;
layout = new TableLayout();
layout.setTable(this);
setTransform(false);
setTouchable(Touchable.childrenOnly);
}
public void draw(SpriteBatch batch, float parentAlpha) {
validate();
drawBackground(batch, parentAlpha);
if (isTransform()) {
applyTransform(batch, computeTransform());
if (clip) {
boolean draw = background == null ? clipBegin(0, 0, getWidth(), getHeight()) : clipBegin(
layout.getPadLeft(), layout.getPadBottom(),
getWidth() - layout.getPadLeft() - layout.getPadRight(), getHeight() - layout.getPadBottom()
- layout.getPadTop());
if (draw) {
drawChildren(batch, parentAlpha);
clipEnd();
}
} else
drawChildren(batch, parentAlpha);
resetTransform(batch);
} else
super.draw(batch, parentAlpha);
}
/**
* Called to draw the background, before clipping is applied (if enabled). Default implementation draws the
* background drawable.
*/
protected void drawBackground(SpriteBatch batch, float parentAlpha) {
if (background != null) {
Color color = getColor();
batch.setColor(color.r, color.g, color.b, color.a * parentAlpha);
background.draw(batch, getX(), getY(), getWidth(), getHeight());
}
}
public void invalidate() {
layout.invalidate();
super.invalidate();
}
public float getPrefWidth() {
if (background != null)
return Math.max(layout.getPrefWidth(), background.getMinWidth());
return layout.getPrefWidth();
}
public float getPrefHeight() {
if (background != null)
return Math.max(layout.getPrefHeight(), background.getMinHeight());
return layout.getPrefHeight();
}
public float getMinWidth() {
return layout.getMinWidth();
}
public float getMinHeight() {
return layout.getMinHeight();
}
/**
* Sets the background drawable from the skin. This may only be called if {@link Table#Table(Skin)} or
* {@link #setSkin(Skin)} was used.
*/
public void setBackground(String drawableName) {
setBackground(skin.getDrawable(drawableName));
}
/**
* Sets the background drawable and sets the table's padding to {@link Drawable#getBottomHeight()} ,
* {@link Drawable#getTopHeight()}, {@link Drawable#getLeftWidth()}, and {@link Drawable#getRightWidth()}.
*
* @param background
* If null, the background will be cleared and all padding is removed.
*/
public void setBackground(Drawable background) {
if (this.background == background)
return;
this.background = background;
if (background == null)
pad(null);
else {
padBottom(background.getBottomHeight());
padTop(background.getTopHeight());
padLeft(background.getLeftWidth());
padRight(background.getRightWidth());
invalidate();
}
}
public Drawable getBackground() {
return background;
}
public Actor hit(float x, float y, boolean touchable) {
if (clip) {
if (touchable && getTouchable() == Touchable.disabled)
return null;
if (x < 0 || x >= getWidth() || y < 0 || y >= getHeight())
return null;
}
return super.hit(x, y, touchable);
}
/**
* Causes the contents to be clipped if they exceed the table widget bounds. Enabling clipping will set
* {@link #setTransform(boolean)} to true.
*/
public void setClip(boolean enabled) {
clip = enabled;
setTransform(enabled);
invalidate();
}
/** Returns the row index for the y coordinate. */
public int getRow(float y) {
return layout.getRow(y);
}
/** Removes all actors and cells from the table. */
public void clear() {
super.clear();
layout.clear();
invalidate();
}
/**
* Adds a new cell with a label. This may only be called if {@link Table#Table(Skin)} or {@link #setSkin(Skin)} was
* used.
*/
public Cell add(String text) {
if (skin == null)
throw new IllegalStateException("Table must have a skin set to use this method.");
return add(new Label(text, skin));
}
/**
* Adds a new cell with a label. This may only be called if {@link Table#Table(Skin)} or {@link #setSkin(Skin)} was
* used.
*/
public Cell add(String text, String labelStyleName) {
if (skin == null)
throw new IllegalStateException("Table must have a skin set to use this method.");
return add(new Label(text, skin.get(labelStyleName, LabelStyle.class)));
}
/**
* Adds a new cell with a label. This may only be called if {@link Table#Table(Skin)} or {@link #setSkin(Skin)} was
* used.
*/
public Cell add(String text, String fontName, Color color) {
if (skin == null)
throw new IllegalStateException("Table must have a skin set to use this method.");
return add(new Label(text, new LabelStyle(skin.getFont(fontName), color)));
}
/**
* Adds a new cell with a label. This may only be called if {@link Table#Table(Skin)} or {@link #setSkin(Skin)} was
* used.
*/
public Cell add(String text, String fontName, String colorName) {
if (skin == null)
throw new IllegalStateException("Table must have a skin set to use this method.");
return add(new Label(text, new LabelStyle(skin.getFont(fontName), skin.getColor(colorName))));
}
/** Adds a cell without a widget. */
public Cell add() {
return layout.add(null);
}
/**
* Adds a new cell to the table with the specified actor.
*
* @param actor
* May be null to add a cell without an actor.
*/
public Cell add(Actor actor) {
return layout.add(actor);
}
/**
* Adds a new cell to the table with the specified actors in a {@link Stack}.
*
* @param actors
* May be null to add a stack without any actors.
*/
public Cell stack(Actor... actors) {
Stack stack = new Stack();
if (actors != null) {
for (int i = 0, n = actors.length; i < n; i++)
stack.addActor(actors[i]);
}
return add(stack);
}
/**
* Indicates that subsequent cells should be added to a new row and returns the cell values that will be used as the
* defaults for all cells in the new row.
*/
public Cell row() {
return layout.row();
}
/**
* Gets the cell values that will be used as the defaults for all cells in the specified column. Columns are indexed
* starting at 0.
*/
public Cell columnDefaults(int column) {
return layout.columnDefaults(column);
}
/** The cell values that will be used as the defaults for all cells. */
public Cell defaults() {
return layout.defaults();
}
public void layout() {
layout.layout();
}
/**
* Removes all actors and cells from the table (same as {@link #clear()}) and additionally resets all table
* properties and cell, column, and row defaults.
*/
public void reset() {
layout.reset();
}
/** Returns the cell for the specified widget in this table, or null. */
public Cell getCell(Actor actor) {
return layout.getCell(actor);
}
/** Returns the cells for this table. */
public List<Cell> getCells() {
return layout.getCells();
}
/** Sets the padTop, padLeft, padBottom, and padRight around the table to the specified value. */
public Table pad(Value pad) {
layout.pad(pad);
return this;
}
public Table pad(Value top, Value left, Value bottom, Value right) {
layout.pad(top, left, bottom, right);
return this;
}
/** Padding at the top edge of the table. */
public Table padTop(Value padTop) {
layout.padTop(padTop);
return this;
}
/** Padding at the left edge of the table. */
public Table padLeft(Value padLeft) {
layout.padLeft(padLeft);
return this;
}
/** Padding at the bottom edge of the table. */
public Table padBottom(Value padBottom) {
layout.padBottom(padBottom);
return this;
}
/** Padding at the right edge of the table. */
public Table padRight(Value padRight) {
layout.padRight(padRight);
return this;
}
/** Sets the padTop, padLeft, padBottom, and padRight around the table to the specified value. */
public Table pad(float pad) {
layout.pad(pad);
return this;
}
public Table pad(float top, float left, float bottom, float right) {
layout.pad(top, left, bottom, right);
return this;
}
/** Padding at the top edge of the table. */
public Table padTop(float padTop) {
layout.padTop(padTop);
return this;
}
/** Padding at the left edge of the table. */
public Table padLeft(float padLeft) {
layout.padLeft(padLeft);
return this;
}
/** Padding at the bottom edge of the table. */
public Table padBottom(float padBottom) {
layout.padBottom(padBottom);
return this;
}
/** Padding at the right edge of the table. */
public Table padRight(float padRight) {
layout.padRight(padRight);
return this;
}
/**
* Sets the alignment of the logical table within the table widget. Set to {@link Align#center}, {@link Align#top},
* {@link Align#bottom} , {@link Align#left} , {@link Align#right}, or any combination of those.
*/
public Table align(int align) {
layout.align(align);
return this;
}
/**
* Sets the alignment of the logical table within the table widget to {@link Align#center}. This clears any other
* alignment.
*/
public Table center() {
layout.center();
return this;
}
/**
* Adds {@link Align#top} and clears {@link Align#bottom} for the alignment of the logical table within the table
* widget.
*/
public Table top() {
layout.top();
return this;
}
/**
* Adds {@link Align#left} and clears {@link Align#right} for the alignment of the logical table within the table
* widget.
*/
public Table left() {
layout.left();
return this;
}
/**
* Adds {@link Align#bottom} and clears {@link Align#top} for the alignment of the logical table within the table
* widget.
*/
public Table bottom() {
layout.bottom();
return this;
}
/**
* Adds {@link Align#right} and clears {@link Align#left} for the alignment of the logical table within the table
* widget.
*/
public Table right() {
layout.right();
return this;
}
/** Turns on all debug lines. */
public Table debug() {
layout.debug();
return this;
}
/** Turns on table debug lines. */
public Table debugTable() {
layout.debugTable();
return this;
}
/** Turns on cell debug lines. */
public Table debugCell() {
layout.debugCell();
return this;
}
/** Turns on widget debug lines. */
public Table debugWidget() {
layout.debugWidget();
return this;
}
/** Turns on debug lines. */
public Table debug(Debug debug) {
layout.debug(debug);
return this;
}
public Debug getDebug() {
return layout.getDebug();
}
public Value getPadTopValue() {
return layout.getPadTopValue();
}
public float getPadTop() {
return layout.getPadTop();
}
public Value getPadLeftValue() {
return layout.getPadLeftValue();
}
public float getPadLeft() {
return layout.getPadLeft();
}
public Value getPadBottomValue() {
return layout.getPadBottomValue();
}
public float getPadBottom() {
return layout.getPadBottom();
}
public Value getPadRightValue() {
return layout.getPadRightValue();
}
public float getPadRight() {
return layout.getPadRight();
}
/** Returns {@link #getPadLeft()} plus {@link #getPadRight()}. */
public float getPadX() {
return layout.getPadLeft() + layout.getPadRight();
}
/** Returns {@link #getPadTop()} plus {@link #getPadBottom()}. */
public float getPadY() {
return layout.getPadTop() + layout.getPadBottom();
}
public int getAlign() {
return layout.getAlign();
}
public void setSkin(Skin skin) {
this.skin = skin;
}
/** If true (the default), positions and sizes are rounded to integers. */
public void setRound(boolean round) {
layout.round = round;
}
/**
* Draws the debug lines for all tables in the stage. If this method is not called each frame, no debug lines will
* be drawn. If debug is never turned on for any table in the application, calling this method will have no effect.
* If a table has ever had debug set, calling this method causes an expensive traversal of all actors in the stage.
*/
static public void drawDebug(Stage stage) {
if (!TableToolkit.drawDebug)
return;
drawDebug(stage.getActors(), stage.getSpriteBatch());
}
static private void drawDebug(Array<Actor> actors, SpriteBatch batch) {
for (int i = 0, n = actors.size; i < n; i++) {
Actor actor = actors.get(i);
if (!actor.isVisible())
continue;
if (actor instanceof Table)
((Table) actor).layout.drawDebug(batch);
if (actor instanceof Group)
drawDebug(((Group) actor).getChildren(), batch);
}
}
}