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